feat: mock

This commit is contained in:
zwjamnsss 2017-10-20 16:00:30 +08:00
parent a3dd8f4a3d
commit e3c53bfede
6 changed files with 415 additions and 2 deletions

View File

@ -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;

View 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
}
}

View File

@ -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
}) })

View 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>
)
}
}

View 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>
)
}
}

View File

@ -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){