opti: 接口编辑页面优化

This commit is contained in:
wenbo.dong 2017-09-21 18:44:39 +08:00
parent ecacefc63e
commit 05b55f0b94
8 changed files with 341 additions and 263 deletions

View File

@ -3,25 +3,32 @@ export default {
NAME_LIMIT: 30, // 限制名称的字符长度(中文算两个长度)
HTTP_METHOD: {
'GET': {
request_body: false
request_body: false,
default_tab: 'query'
},
'POST': {
request_body: true
request_body: true,
default_tab: 'body'
},
'PUT': {
request_body: true
request_body: true,
default_tab: 'body'
},
'DELETE': {
request_body: true
request_body: true,
default_tab: 'body'
},
'HEAD': {
request_body: false
request_body: false,
default_tab: 'query'
},
'OPTIONS': {
request_body: false
request_body: false,
default_tab: 'query'
},
'PATCH': {
request_body: true
request_body: true,
default_tab: 'body'
}
},
PROJECT_COLOR: {

View File

@ -91,7 +91,7 @@ class InterfaceEdit extends Component {
}
s.onerror = () => {
s.onerror = () => {
console.error('websocket connect failed.')
}
@ -118,4 +118,4 @@ class InterfaceEdit extends Component {
}
}
export default withRouter(InterfaceEdit);
export default withRouter(InterfaceEdit);

View File

@ -1,10 +1,13 @@
.interface-edit{
padding:10px;
padding: 24px;
.interface-edit-item{
margin-bottom: 10px;
margin-bottom: 16px;
}
.interface-edit-item.hide {
display: none;
}
.interface-edit-item-content{
margin-bottom: 10px;
margin-bottom: 16px;
}
.interface-edit-del-icon{
margin-top: 10px;

View File

@ -25,6 +25,7 @@ import {
const FormItem = Form.Item;
const Option = Select.Option;
const InputGroup = Input.Group;
const RadioButton = Radio.Button;
const RadioGroup = Radio.Group;
const dataTpl = {
req_query: { name: "", required: "1", desc: "" },
@ -61,6 +62,20 @@ class InterfaceEditForm extends Component {
return item
})
}
// 设置标签的展开与折叠
curdata['hideTabs'] = {
req: {
body: 'hide',
query: 'hide',
headers: 'hide'
},
other: {
remark: '',
mail: 'hide'
}
};
curdata['hideTabs']['req'][HTTP_METHOD[curdata.method].default_tab] = '';
console.log(curdata);
this.state = Object.assign({
title: '',
path: '',
@ -94,6 +109,8 @@ class InterfaceEditForm extends Component {
res_body_mock: '',
jsonType: 'tpl',
mockUrl: this.props.mockUrl
}, curdata)
}
@ -252,6 +269,24 @@ class InterfaceEditForm extends Component {
}
// 点击切换radio
changeRadioGroup = (e) => {
const res = e.target.value.split('-');
const obj = {}
// 先全部隐藏
for (let key in this.state.hideTabs[res[0]]) {
obj[key] = 'hide';
}
// 再取消选中项目的隐藏
obj[res[1]] = '';
this.setState({
hideTabs: {
...this.state.hideTabs,
[res[0]]: obj
}
})
}
render() {
const { getFieldDecorator } = this.props.form;
const formItemLayout = {
@ -404,276 +439,287 @@ class InterfaceEditForm extends Component {
const requestBodyList = this.state.req_body_form.map((item, index) => {
return requestBodyTpl(item, index)
})
console.log(this.state.hideTabs);
return (
<Form onSubmit={this.handleSubmit}>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="接口名称"
>
{getFieldDecorator('title', {
initialValue: this.state.title,
rules: nameLengthLimit('接口')
})(
<Input placeholder="接口名称" />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="选择分类"
>
{getFieldDecorator('catid', {
initialValue: this.state.catid + "",
rules: [
{ required: true, message: '请选择一个分类' }
]
})(
<Select placeholder="请选择一个分类">
{this.props.cat.map(item => {
return <Option key={item._id} value={item._id + ""} >{item.name}</Option>
})}
</Select>
)}
</FormItem>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label={(
<span>
接口路径&nbsp;
<Tooltip title="接口路径,支持动态路由,例如:'/api/user/:id'">
<Icon type="question-circle-o" style={{ width: "10px" }} />
</Tooltip>
</span>
)}
>
<InputGroup compact>
<Select value={this.state.method} onChange={val => this.setState({ method: val })} style={{ width: "15%" }}>
{HTTP_METHOD_KEYS.map(item => {
return <Option key={item} value={item}>{item}</Option>
})}
</Select>
<Tooltip title="接口基本路径,可在项目配置里修改" style={{ display: this.props.basepath == '' ? 'block' : 'none' }}>
<Input disabled value={this.props.basepath} readOnly onChange={() => { }} style={{ width: '25%' }} />
</Tooltip>
{getFieldDecorator('path', {
initialValue: this.state.path,
rules: [{
required: true, message: '请输入接口路径!'
}]
})(
<Input onChange={this.handlePath} placeholder="/path" style={{ width: '60%' }} />
)}
</InputGroup>
<Row className="interface-edit-item">
<Col span={18} offset={0}>
{paramsList}
</Col>
</Row>
</FormItem>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="状态"
>
{getFieldDecorator('status', { initialValue: this.state.status })(
<Select>
<Option value="done">已完成</Option>
<Option value="undone">未完成</Option>
</Select>
)}
</FormItem>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="Query"
>
<Button size="small" type="primary" onClick={() => this.addParams('req_query')}>添加Query参数</Button>
</FormItem>
<Row className="interface-edit-item">
<Col span={18} offset={4}>
{QueryList}
</Col>
</Row>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="请求Headers"
>
<Button size="small" type="primary" onClick={() => this.addParams('req_headers')}>添加Header</Button>
</FormItem>
<Row className="interface-edit-item">
<Col span={18} offset={4}>
{headerList}
</Col>
</Row>
{HTTP_METHOD[this.state.method].request_body ? <div >
<h2 className="interface-title" style={{marginTop: 0}}>基本设置</h2>
<div className="panel-sub">
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="请求Body"
label="接口名称"
>
{getFieldDecorator('req_body_type', {
initialValue: this.state.req_body_type
{getFieldDecorator('title', {
initialValue: this.state.title,
rules: nameLengthLimit('接口')
})(
<RadioGroup>
<Radio value="form">form</Radio>
<Radio value="json">json</Radio>
<Radio value="file">file</Radio>
<Radio value="raw">raw</Radio>
</RadioGroup>
<Input placeholder="接口名称" />
)}
</FormItem>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="选择分类"
>
{getFieldDecorator('catid', {
initialValue: this.state.catid + "",
rules: [
{ required: true, message: '请选择一个分类' }
]
})(
<Select placeholder="请选择一个分类">
{this.props.cat.map(item => {
return <Option key={item._id} value={item._id + ""} >{item.name}</Option>
})}
</Select>
)}
</FormItem>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label={(
<span>
接口路径&nbsp;
<Tooltip title="接口路径,支持动态路由,例如:'/api/user/:id'">
<Icon type="question-circle-o" style={{ width: "10px" }} />
</Tooltip>
</span>
)}
>
<InputGroup compact>
<Select value={this.state.method} onChange={val => this.setState({ method: val })} style={{ width: "15%" }}>
{HTTP_METHOD_KEYS.map(item => {
return <Option key={item} value={item}>{item}</Option>
})}
</Select>
<Tooltip title="接口基本路径,可在项目配置里修改" style={{ display: this.props.basepath == '' ? 'block' : 'none' }}>
<Input disabled value={this.props.basepath} readOnly onChange={() => { }} style={{ width: '25%' }} />
</Tooltip>
{getFieldDecorator('path', {
initialValue: this.state.path,
rules: [{
required: true, message: '请输入接口路径!'
}]
})(
<Input onChange={this.handlePath} placeholder="/path" style={{ width: '60%' }} />
)}
</InputGroup>
<Row className="interface-edit-item">
<Col span={18} offset={0}>
{paramsList}
</Col>
</Row>
</FormItem>
<Row className="interface-edit-item" style={{ display: this.props.form.getFieldValue('req_body_type') === 'form' ? 'block' : 'none' }}>
<Col span={18} offset={4} style={{ minHeight: "50px" }}>
<Row>
<Col span="24" className="interface-edit-item">
<Button size="small" type="primary" onClick={() => this.addParams('req_body_form')}>添加form参数</Button>
</Col>
</Row>
{requestBodyList}
</Col>
</Row>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="状态"
>
{getFieldDecorator('status', { initialValue: this.state.status })(
<Select>
<Option value="done">已完成</Option>
<Option value="undone">未完成</Option>
</Select>
)}
</FormItem>
</div>
: null}
<Row className="interface-edit-item" style={{ display: this.props.form.getFieldValue('req_body_type') === 'json' ? 'block' : 'none' }}>
<Col span={18} offset={4} id="req_body_json" style={{ minHeight: "300px" }}>
</Col>
</Row>
<h2 className="interface-title">Request 设置</h2>
{this.props.form.getFieldValue('req_body_type') === 'file' ?
<Row className="interface-edit-item" >
<Col span={18} offset={4}>
{getFieldDecorator('req_body_other', { initialValue: this.state.req_body_other })(
<Input.TextArea placeholder="备注信息" />
)}
</Col>
<div className="container-radiogroup">
<RadioGroup defaultValue={HTTP_METHOD[this.state.method].request_body ? 'req-body' : 'req-query'} size="large" className="radioGroup" onChange={this.changeRadioGroup}>
{HTTP_METHOD[this.state.method].request_body ? <RadioButton value="req-body">Body</RadioButton> : null}
<RadioButton value="req-query">Query</RadioButton>
<RadioButton value="req-headers">Headers</RadioButton>
</RadioGroup>
</div>
<div className="panel-sub">
<FormItem
className={'interface-edit-item ' + this.state.hideTabs.req.query}
>
<Button size="small" type="primary" onClick={() => this.addParams('req_query')}>添加Query参数</Button>
</FormItem>
</Row>
:
null
}
{this.props.form.getFieldValue('req_body_type') === 'raw' ?
<Row>
<Col span={18} offset={4} >
{getFieldDecorator('req_body_other', { initialValue: this.state.req_body_other })(
<Input.TextArea placeholder="备注信息" />
)}
<Row className={'interface-edit-item ' + this.state.hideTabs.req.query}>
<Col>
{QueryList}
</Col>
</Row>
: null
}
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="响应Body"
>
<FormItem
className={'interface-edit-item ' + this.state.hideTabs.req.headers}
>
<Button size="small" type="primary" onClick={() => this.addParams('req_headers')}>添加Header</Button>
</FormItem>
<Row className={'interface-edit-item ' + this.state.hideTabs.req.headers}>
<Col>
{headerList}
</Col>
</Row>
{HTTP_METHOD[this.state.method].request_body ? <div >
<FormItem
className={'interface-edit-item ' + this.state.hideTabs.req.body}
>
{getFieldDecorator('req_body_type', {
initialValue: this.state.req_body_type
})(
<RadioGroup>
<Radio value="form">form</Radio>
<Radio value="json">json</Radio>
<Radio value="file">file</Radio>
<Radio value="raw">raw</Radio>
</RadioGroup>
)}
</FormItem>
<Row className={'interface-edit-item ' + this.state.hideTabs.req.body} style={{ display: this.props.form.getFieldValue('req_body_type') === 'form' ? 'block' : 'none' }}>
<Col style={{ minHeight: "50px" }}>
<Row>
<Col span="24" className="interface-edit-item">
<Button size="small" type="primary" onClick={() => this.addParams('req_body_form')}>添加form参数</Button>
</Col>
</Row>
{requestBodyList}
</Col>
</Row>
</div>
: null}
<Row className={'interface-edit-item ' + (this.props.form.getFieldValue('req_body_type') === 'json' ? this.state.hideTabs.req.body : 'hide')}>
<Col id="req_body_json" style={{ minHeight: "300px" }}>
</Col>
</Row>
{this.props.form.getFieldValue('req_body_type') === 'file' ?
<Row className="interface-edit-item" >
<Col>
{getFieldDecorator('req_body_other', { initialValue: this.state.req_body_other })(
<Input.TextArea placeholder="备注信息" />
)}
</Col>
</Row>
:
null
}
{this.props.form.getFieldValue('req_body_type') === 'raw' ?
<Row>
<Col>
{getFieldDecorator('req_body_other', { initialValue: this.state.req_body_other })(
<Input.TextArea placeholder="备注信息" />
)}
</Col>
</Row>
: null
}
</div>
{/* ----------- Response ------------- */}
<h2 className="interface-title">Response 设置</h2>
<div className="container-radiogroup">
{getFieldDecorator('res_body_type', {
initialValue: this.state.res_body_type
})(
<RadioGroup>
<Radio value="json">json(mock)</Radio>
<Radio value="raw">raw</Radio>
<RadioGroup size="large" className="radioGroup">
<RadioButton value="json">json(mock)</RadioButton>
<RadioButton value="raw">raw</RadioButton>
</RadioGroup>
)}
</div>
<div className="panel-sub">
<Row className="interface-edit-item" style={{ display: this.props.form.getFieldValue('res_body_type') === 'json' ? 'block' : 'none' }}>
<Col>
<Tabs defaultActiveKey="tpl" onChange={this.handleJsonType} >
<TabPane tab="模板" key="tpl">
</FormItem>
</TabPane>
<TabPane tab="预览" key="preview">
</TabPane>
</Tabs>
<div>
<h3 style={{ padding: '10px 0' }}>基于mockjs和json5,可直接写mock模板和注释,具体使用方法请<span onClick={() => window.open('https://ued.qunar.com/yapi/mock.html', '_blank')}>查看文档</span></h3>
<div id="res_body_json" style={{ minHeight: "300px", display: this.state.jsonType === 'tpl' ? 'block' : 'none' }} ></div>
<div id="mock-preview" style={{ backgroundColor: "#eee", lineHeight: "20px", minHeight: "300px", display: this.state.jsonType === 'preview' ? 'block' : 'none' }}></div>
</div>
</Col>
</Row>
<Row className="interface-edit-item" style={{ display: this.props.form.getFieldValue('res_body_type') === 'raw' ? 'block' : 'none' }}>
<Col>
{getFieldDecorator('res_body', { initialValue: this.state.res_body })(
<Input.TextArea style={{ minHeight: "150px" }} placeholder="备注信息" />
)}
</Col>
</Row>
</div>
<Row className="interface-edit-item" style={{ display: this.props.form.getFieldValue('res_body_type') === 'json' ? 'block' : 'none' }}>
<Col span={18} offset={4} >
<Tabs defaultActiveKey="tpl" onChange={this.handleJsonType} >
<TabPane tab="模板" key="tpl">
{/* ----------- other ------------- */}
</TabPane>
<TabPane tab="预览" key="preview">
</TabPane>
</Tabs>
<div>
<h3 style={{ padding: '10px 0' }}>基于mockjs和json5,可直接写mock模板和注释,具体使用方法请查看文档</h3>
<div id="res_body_json" style={{ minHeight: "300px", display: this.state.jsonType === 'tpl' ? 'block' : 'none' }} ></div>
<div id="mock-preview" style={{ backgroundColor: "#eee", lineHeight: "20px", minHeight: "300px", display: this.state.jsonType === 'preview' ? 'block' : 'none' }}></div>
<h2 className="interface-title"> </h2>
<div className="container-radiogroup">
<RadioGroup defaultValue="other-remark" size="large" className="radioGroup" onChange={this.changeRadioGroup}>
<RadioButton value="other-remark"> </RadioButton>
<RadioButton value="other-mail"> </RadioButton>
</RadioGroup>
</div>
<div className="panel-sub">
<FormItem
className={'interface-edit-item ' + this.state.hideTabs.other.remark}
>
<div >
<div id="desc" ></div>
</div>
</Col>
</Row>
<Row className="interface-edit-item" style={{ display: this.props.form.getFieldValue('res_body_type') === 'raw' ? 'block' : 'none' }}>
<Col span={18} offset={4} >
{getFieldDecorator('res_body', { initialValue: this.state.res_body })(
<Input.TextArea style={{ minHeight: "150px" }} placeholder="备注信息" />
</FormItem>
<FormItem
className={'interface-edit-item ' + this.state.hideTabs.other.mail}
{...formItemLayout}
label="是否开启邮件通知"
>
{getFieldDecorator('switch_notice', { valuePropName: 'checked', initialValue: true })(
<Switch checkedChildren="开" unCheckedChildren="关" />
)}
</Col>
</Row>
</FormItem>
<FormItem
className={'interface-edit-item ' + (this.props.form.getFieldValue('switch_notice') === true ? this.state.hideTabs.other.mail : '')}
{...formItemLayout}
label="改动日志"
>
{getFieldDecorator('message', { initialValue: "" })(
<Input.TextArea style={{ minHeight: "150px" }} placeholder="改动日志会通过邮件发送给关注此项目的用户" />
)}
</FormItem>
</div>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="备注"
>
<div >
<div id="desc" ></div>
</div>
</FormItem>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="是否开启邮件通知"
>
{getFieldDecorator('switch_notice', { valuePropName: 'checked', initialValue: true })(
<Switch checkedChildren="开" unCheckedChildren="关" />
)}
</FormItem>
<FormItem
style={{ display: this.props.form.getFieldValue('switch_notice') === true ? 'block' : 'none' }}
className="interface-edit-item"
{...formItemLayout}
label="改动日志"
>
{getFieldDecorator('message', { initialValue: "" })(
<Input.TextArea style={{ minHeight: "150px" }} placeholder="改动日志会通过邮件发送给关注此项目的用户" />
)}
</FormItem>
<FormItem
className="interface-edit-item"
wrapperCol={{ span: 14, offset: 10 }}
style={{textAlign: 'center', marginTop: '16px'}}
>
<Button type="primary" htmlType="submit">保存</Button>
</FormItem>

View File

@ -167,7 +167,7 @@ class InterfaceList extends Component {
});
return (
<div style={{ padding: '24px' }}>
<h2 style={{ display: 'inline-block'}}>{intername?intername:'全部接口'}</h2>
<h2 className="interface-title" style={{ display: 'inline-block', margin: 0}}>{intername?intername:'全部接口'}</h2>
<Button style={{float: 'right'}} type="primary" onClick={() => this.setState({ visible: true })}>添加接口</Button>
<Table className="table-interfacelist" pagination={false} columns={columns} onChange={this.handleChange} dataSource={data} />
<Modal

View File

@ -150,17 +150,39 @@
}
.right-content{
margin:3px;
min-height: 5rem;
background: #fff;
.interface-content{
.ant-tabs-nav{
width:100%;
// background-color: #ddd;
// color: $color-white;
}
.ant-tabs-nav-wrap{
text-align: left;
}
margin:3px;
min-height: 5rem;
background: #fff;
.interface-content{
.ant-tabs-nav{
width:100%;
// background-color: #ddd;
// color: $color-white;
}
.ant-tabs-nav-wrap{
text-align: left;
}
}
.interface-title {
font-weight: normal;
margin-top: .48rem;
margin-bottom: .16rem;
border-left: 3px solid #2395f1;
padding-left: 8px;
}
.container-radiogroup {
text-align: center;
margin-bottom: .16rem;
}
.panel-sub {
background: #eceef1;
padding: .16rem;
}
.ant-radio-button-wrapper-checked {
color: #fff;
background-color: #2395f1;
&:hover {
color: #ddd;
}
}
}

View File

@ -74,7 +74,7 @@ em {
height: auto !important;
height: 100%;
margin-bottom: -2.4rem;
background-color: #ececec;
background-color: #eceef1;
&::after {
content: '';
display: block;

View File

@ -34,7 +34,7 @@
// ---
// Background color for `<body>`
@body-background : #ececec;
@body-background : #eceef1;
// Base background color for most components
@component-background : #fff;
@heading-color : fade(#273848, 85%);
@ -149,7 +149,7 @@
// Layout
// @layout-body-background : #ececec;
@layout-body-background : #eceef1;
@layout-header-background : #32363a;
@layout-header-height : 56px;
@layout-header-padding : 0 0;