mirror of
https://github.com/YMFE/yapi.git
synced 2024-11-27 04:40:08 +08:00
feat: mock
This commit is contained in:
parent
a3dd8f4a3d
commit
e3c53bfede
@ -185,6 +185,23 @@ function handleMockWord(word) {
|
|||||||
return Mock.mock(word);
|
return Mock.mock(word);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并后新的对象属性与 Obj 一致,nextObj 有对应属性则取 nextObj 属性值,否则取 Obj 属性值
|
||||||
|
* @param {Object} Obj 旧对象
|
||||||
|
* @param {Object} nextObj 新对象
|
||||||
|
* @return {Object} 合并后的对象
|
||||||
|
*/
|
||||||
|
exports.safeAssign = (Obj, nextObj) => {
|
||||||
|
let keys = Object.keys(nextObj);
|
||||||
|
return Object.keys(Obj).reduce((result, value) => {
|
||||||
|
if (keys.indexOf(value) >= 0) {
|
||||||
|
result[value] = nextObj[value];
|
||||||
|
} else {
|
||||||
|
result[value] = Obj[value];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
exports.simpleJsonPathParse = simpleJsonPathParse;
|
exports.simpleJsonPathParse = simpleJsonPathParse;
|
||||||
exports.handleMockWord = handleMockWord;
|
exports.handleMockWord = handleMockWord;
|
||||||
|
30
client/reducer/modules/mockCol.js
Normal file
30
client/reducer/modules/mockCol.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
const FETCH_MOCK_COL = 'yapi/mockCol/FETCH_MOCK_COL';
|
||||||
|
|
||||||
|
// Reducer
|
||||||
|
const initialState = {
|
||||||
|
list: []
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (state = initialState, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case FETCH_MOCK_COL:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
list: action.payload.data.data
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action Creators
|
||||||
|
export async function fetchMockCol(interfaceId) {
|
||||||
|
let result = await axios.get('/api/plugin/advmock/case/list?interface_id=' + interfaceId);
|
||||||
|
return {
|
||||||
|
type: FETCH_MOCK_COL,
|
||||||
|
payload: result.data
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import news from './news.js'
|
|||||||
import addInterface from './addInterface.js'
|
import addInterface from './addInterface.js'
|
||||||
import menu from './menu.js'
|
import menu from './menu.js'
|
||||||
import follow from './follow.js'
|
import follow from './follow.js'
|
||||||
|
import mockCol from './mockCol.js'
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
group,
|
group,
|
||||||
@ -18,5 +19,6 @@ export default combineReducers({
|
|||||||
news,
|
news,
|
||||||
addInterface,
|
addInterface,
|
||||||
menu,
|
menu,
|
||||||
follow
|
follow,
|
||||||
|
mockCol
|
||||||
})
|
})
|
||||||
|
258
exts/yapi-plugin-advanced-mock/MockCol/CaseDesModal.js
Normal file
258
exts/yapi-plugin-advanced-mock/MockCol/CaseDesModal.js
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
// import axios from 'axios'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { Button, Form, Input, Switch, Select, Icon, Modal } from 'antd';
|
||||||
|
import { safeAssign } from '../../../client/common.js';
|
||||||
|
import { httpCodes } from '../index.js';
|
||||||
|
|
||||||
|
const Option = Select.Option;
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 5 }
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 12 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Form.create()
|
||||||
|
export default class CaseDesModal extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
form: PropTypes.object,
|
||||||
|
caseData: PropTypes.object,
|
||||||
|
onCancel: PropTypes.func,
|
||||||
|
onOk: PropTypes.func,
|
||||||
|
isAdd: PropTypes.bool,
|
||||||
|
visible: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
preProcess = caseData => {
|
||||||
|
// const a = {
|
||||||
|
// interface_id: { type: Number, required: true },
|
||||||
|
// project_id: {type: Number, required: true},
|
||||||
|
// ip: {type: String},
|
||||||
|
// ip_enable: {type: Boolean, default: false},
|
||||||
|
// name: {type: String, required: true},
|
||||||
|
// code: {type: Number, default: 200},
|
||||||
|
// deplay: {type: Number, default: 0},
|
||||||
|
// headers: [{
|
||||||
|
// name: {type: String, required: true},
|
||||||
|
// value: {type: String}
|
||||||
|
// }],
|
||||||
|
// params: mongoose.Schema.Types.Mixed,
|
||||||
|
// uid: String,
|
||||||
|
// up_time: Number,
|
||||||
|
// res_body: {type: String, required: true}
|
||||||
|
// }
|
||||||
|
const initCaseData = {
|
||||||
|
ip: '',
|
||||||
|
ip_enable: false,
|
||||||
|
name: '',
|
||||||
|
code: '200',
|
||||||
|
deplay: 0,
|
||||||
|
headers: [{name: '', value: ''}],
|
||||||
|
paramsArr: [{name: '', value: ''}],
|
||||||
|
res_body: ''
|
||||||
|
}
|
||||||
|
caseData.paramsArr = caseData.params && caseData.params.length ? Object.keys(caseData.params).map(key => {
|
||||||
|
return { name: key, value: caseData.params[key] }
|
||||||
|
}) : [{name: '', value: ''}];
|
||||||
|
caseData.headers = caseData.headers && caseData.headers.length ? caseData.headers : [{name: '', value: ''}];
|
||||||
|
caseData = safeAssign(initCaseData, caseData);
|
||||||
|
return caseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
endProcess = caseData => {
|
||||||
|
const headers = [];
|
||||||
|
const params = {};
|
||||||
|
caseData.headers.forEach(item => {
|
||||||
|
if (item.name) {
|
||||||
|
headers.push({
|
||||||
|
name: item.name,
|
||||||
|
value: item.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
caseData.paramsArr.forEach(item => {
|
||||||
|
if (item.name) {
|
||||||
|
params[item.name] = item.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
caseData.headers = headers;
|
||||||
|
caseData.params = params;
|
||||||
|
delete caseData.paramsArr;
|
||||||
|
return caseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.form.setFieldsValue(this.preProcess(this.props.caseData))
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (this.props.caseData !== nextProps.caseData) {
|
||||||
|
this.props.form.setFieldsValue(this.preProcess(this.props.caseData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOk = () => {
|
||||||
|
const form = this.props.form;
|
||||||
|
this.props.onOk(this.endProcess(form.getFieldsValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { getFieldDecorator, getFieldValue } = this.props.form;
|
||||||
|
const { isAdd, visible, onCancel } = this.props;
|
||||||
|
// const { caseData } = this.props;
|
||||||
|
// const headers = caseData.headers || [{name: '', value: ''}];
|
||||||
|
// const params = caseData.params || {'': ''};
|
||||||
|
// const paramsArr = Object.keys(params).map(key => {
|
||||||
|
// return { name: key, value: params[key] }
|
||||||
|
// })
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={isAdd ? '添加期望' : '编辑期望'}
|
||||||
|
visible={visible}
|
||||||
|
maskClosable={false}
|
||||||
|
onOk={this.handleOk}
|
||||||
|
onCancel={() => onCancel()}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<FormItem
|
||||||
|
{...formItemLayout}
|
||||||
|
label="期望名称"
|
||||||
|
>
|
||||||
|
{getFieldDecorator('name', {
|
||||||
|
rules: [{ required: true, message: '请输入期望名称!' }]
|
||||||
|
})(
|
||||||
|
<Input placeholder="请输入期望名称" />
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
<FormItem label="IP 过滤">
|
||||||
|
{getFieldDecorator('ip_enable', {
|
||||||
|
valuePropName: 'checked',
|
||||||
|
rules: [{ type: 'boolean' }]
|
||||||
|
})(
|
||||||
|
<Switch />
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
{...formItemLayout}
|
||||||
|
>
|
||||||
|
{getFieldDecorator('ip')(
|
||||||
|
<Input placeholder="请输入过滤的 IP 地址" />
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
{...formItemLayout}
|
||||||
|
label="HTTP CODE"
|
||||||
|
>
|
||||||
|
{getFieldDecorator('code')(
|
||||||
|
<Select search>
|
||||||
|
{
|
||||||
|
httpCodes.map(code => <Option key={''+code} value={''+code}>{''+code}</Option>)
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
{...formItemLayout}
|
||||||
|
label="延时"
|
||||||
|
>
|
||||||
|
{getFieldDecorator('deplay', {
|
||||||
|
initialValue: 0,
|
||||||
|
rules: [{ required: true, message: '请输入延时时间!', type: 'integer' }]
|
||||||
|
})(
|
||||||
|
<Input placeholder="请输入延时时间" />
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
{
|
||||||
|
getFieldDecorator('headers', { initialValue: [] }) &&
|
||||||
|
getFieldValue('headers').map((item, index) => (
|
||||||
|
<div key={index}>
|
||||||
|
<FormItem
|
||||||
|
{...formItemLayout}
|
||||||
|
label={index ? '' : 'HTTP 头'}
|
||||||
|
>
|
||||||
|
{getFieldDecorator(`headers[${index}].name`)(
|
||||||
|
<Input />
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
{...formItemLayout}
|
||||||
|
>
|
||||||
|
{getFieldDecorator(`headers[${index}].value`)(
|
||||||
|
<Input />
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
<FormItem>
|
||||||
|
<Button type="dashed" onClick={this.add} style={{ width: '60%' }}>
|
||||||
|
<Icon type="plus" /> 添加 HTTP 头
|
||||||
|
</Button>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
{
|
||||||
|
getFieldDecorator('paramsArr', { initialValue: [] }) &&
|
||||||
|
getFieldValue('paramsArr').map((item, index) => (
|
||||||
|
<div key={index}>
|
||||||
|
<FormItem
|
||||||
|
label={index ? '' : '参数'}
|
||||||
|
>
|
||||||
|
{getFieldDecorator(`paramsArr[${index}].name`)(
|
||||||
|
<Input />
|
||||||
|
)}
|
||||||
|
{getFieldValue('paramsArr').length > 1 ? (
|
||||||
|
<Icon
|
||||||
|
className="dynamic-delete-button"
|
||||||
|
type="minus-circle-o"
|
||||||
|
onClick={() => this.remove(index)}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
>
|
||||||
|
{getFieldDecorator(`paramsArr[${index}].value`)(
|
||||||
|
<Input />
|
||||||
|
)}
|
||||||
|
{getFieldValue('paramsArr').length > 1 ? (
|
||||||
|
<Icon
|
||||||
|
className="dynamic-delete-button"
|
||||||
|
type="minus-circle-o"
|
||||||
|
onClick={() => this.remove(index)}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</FormItem>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
<FormItem>
|
||||||
|
<Button type="dashed" onClick={this.add} style={{ width: '60%' }}>
|
||||||
|
<Icon type="plus" /> 添加参数
|
||||||
|
</Button>
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
|
<FormItem
|
||||||
|
{...formItemLayout}
|
||||||
|
label="返回 JSON"
|
||||||
|
>
|
||||||
|
{getFieldDecorator('res_body', {
|
||||||
|
rules: [{ required: true, message: '请输入期望名称!' }]
|
||||||
|
})(
|
||||||
|
<Input placeholder="返回 JSON" />
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
106
exts/yapi-plugin-advanced-mock/MockCol/MockCol.js
Normal file
106
exts/yapi-plugin-advanced-mock/MockCol/MockCol.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
// import axios from 'axios'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
|
import { Table, Button } from 'antd';
|
||||||
|
import { fetchMockCol } from '../../../client/reducer/modules/mockCol'
|
||||||
|
import { formatTime } from '../../../client/common.js';
|
||||||
|
import CaseDesModal from './CaseDesModal';
|
||||||
|
|
||||||
|
@connect(
|
||||||
|
state => {
|
||||||
|
return {
|
||||||
|
list: state.mockCol.list
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fetchMockCol
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@withRouter
|
||||||
|
export default class MockCol extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
list: PropTypes.array,
|
||||||
|
match: PropTypes.object,
|
||||||
|
fetchMockCol: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
caseData: {},
|
||||||
|
caseDesModalVisible: false,
|
||||||
|
isAdd: false
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
const interfaceId = this.props.match.params.actionId
|
||||||
|
this.props.fetchMockCol(interfaceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOk = (caseData) => {
|
||||||
|
if (this.state.isAdd) {
|
||||||
|
caseData = Object.assign({
|
||||||
|
...caseData,
|
||||||
|
interface_id: 0,
|
||||||
|
project_id: 0,
|
||||||
|
uid: 0
|
||||||
|
})
|
||||||
|
// addCase()
|
||||||
|
} else {
|
||||||
|
// saveCase()
|
||||||
|
}
|
||||||
|
console.log(caseData)
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFormRef = (form) => {
|
||||||
|
this.form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const data = this.props.list;
|
||||||
|
const { isAdd, caseData, caseDesModalVisible } = this.state;
|
||||||
|
|
||||||
|
const columns = [{
|
||||||
|
title: '期望名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
render: text => <a href="#">{text}</a>
|
||||||
|
}, {
|
||||||
|
title: 'ip',
|
||||||
|
dataIndex: 'ip',
|
||||||
|
key: 'ip'
|
||||||
|
}, {
|
||||||
|
title: '创建人',
|
||||||
|
dataIndex: 'username',
|
||||||
|
key: 'username'
|
||||||
|
}, {
|
||||||
|
title: '编辑时间',
|
||||||
|
key: 'action',
|
||||||
|
render: text => formatTime(text)
|
||||||
|
}, {
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'address',
|
||||||
|
key: 'address'
|
||||||
|
}];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '20px 10px' }}>
|
||||||
|
<Button type="primary" onClick={() => this.setState({isAdd: true, caseDesModalVisible: true})}>添加期望</Button>
|
||||||
|
<Table columns={columns} dataSource={data} />
|
||||||
|
<CaseDesModal
|
||||||
|
visible={caseDesModalVisible}
|
||||||
|
isAdd={isAdd}
|
||||||
|
caseData={caseData}
|
||||||
|
onOk={this.handleOk}
|
||||||
|
onCancel={() => this.setState({caseDesModalVisible: false})}
|
||||||
|
ref={this.saveFormRef}
|
||||||
|
></CaseDesModal>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import AdvMock from './AdvMock.js'
|
import AdvMock from './MockCol/MockCol.js'
|
||||||
|
|
||||||
module.exports = function(){
|
module.exports = function(){
|
||||||
this.bindHook('interface_tab', function(tabs){
|
this.bindHook('interface_tab', function(tabs){
|
||||||
|
Loading…
Reference in New Issue
Block a user