This commit is contained in:
System Administrator 2017-09-14 18:19:28 +08:00
commit 8e854e91d0
31 changed files with 436 additions and 292 deletions

View File

@ -10,6 +10,7 @@ import Loading from './components/Loading/Loading';
import { checkLoginState } from './reducer/modules/user';
import { requireAuthentication } from './components/AuthenticatedComponent';
const LOADING_STATUS = 0;
@connect(

View File

@ -58,7 +58,7 @@ class ErrMsg extends Component {
break;
case 'noProject':
title = '该分组还没有项目呢';
desc = <span>请点击右上角 <Icon type="plus-circle" /> 按钮新建项目</span>;
desc = <span>请点击右上角添加项目按钮新建项目</span>;
break;
case 'noData':
title = '暂无数据';

View File

@ -1,4 +1,3 @@
@import '../../styles/common.scss';
@import '../../styles/mixin.scss';
.nav-tooltip {

View File

@ -192,7 +192,7 @@ export default class Run extends Component {
const href = URL.format({
protocol: urlObj.protocol || 'http',
host: urlObj.host,
pathname: path,
pathname: urlObj.pathname? urlObj.pathname + path : path,
query: this.getQueryObj(query)
});

View File

@ -91,15 +91,8 @@ class UsernameAutoComplete extends Component {
dataSource: userList
});
if (userList.length) {
userList.forEach((item) => {
if (item.username === this.state.changeName) {
// 每次取回搜索值后,没选择时默认选择第一位
this.changeState(userList[0].id, userList[0].username);
} else {
// 有候选词但没有对应输入框中的字符串,此时应清空候选 uid 和 username
this.changeState(-1, '');
}
});
// 每次取回搜索值后,没选择时默认选择第一位
this.changeState(userList[0].id, userList[0].username);
} else {
// 如果没有搜索结果,则清空候选 uid 和 username
this.changeState(-1, '');

View File

@ -32,7 +32,8 @@ const formItemLayout = {
@connect(
state => {
return {
groupList: state.group.groupList
groupList: state.group.groupList,
currGroup: state.group.currGroup
}
},
{
@ -46,12 +47,14 @@ class ProjectList extends Component {
constructor(props) {
super(props);
this.state = {
groupList: []
groupList: [],
currGroupId: null
}
}
static propTypes = {
groupList: PropTypes.array,
form: PropTypes.object,
currGroup: PropTypes.object,
addProject: PropTypes.func,
history: PropTypes.object,
setBreadcrumb: PropTypes.func,
@ -88,7 +91,15 @@ class ProjectList extends Component {
async componentWillMount() {
this.props.setBreadcrumb([{name: '新建项目'}]);
await this.props.fetchGroupList();
if(!this.props.currGroup._id){
await this.props.fetchGroupList();
}
if(this.props.groupList.length === 0){
return null;
}
this.setState({
currGroupId: this.props.currGroup._id ? this.props.currGroup._id : this.props.groupList[0]._id
})
this.setState({groupList: this.props.groupList});
}
@ -114,7 +125,7 @@ class ProjectList extends Component {
label="所属分组"
>
{getFieldDecorator('group', {
initialValue: this.state.groupList.length > 0? this.state.groupList[0]._id.toString() : null ,
initialValue: this.state.currGroupId+'' ,
rules: [{
required: true, message: '请选择项目所属的分组!'
}]

View File

@ -1,10 +1,10 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Button, Icon, Modal,Alert, Input, message, Menu, Row, Col } from 'antd'
import { Icon, Modal, Alert, Input, message, Menu, Row, Col } from 'antd'
import { autobind } from 'core-decorators';
import axios from 'axios';
import { withRouter } from 'react-router';
import { withRouter } from 'react-router-dom';
const { TextArea } = Input;
const Search = Input.Search;
const TYPE_EDIT = 'edit';
@ -63,18 +63,18 @@ export default class GroupList extends Component {
const groupId = !isNaN(this.props.match.params.groupId) ? parseInt(this.props.match.params.groupId) : 0;
await this.props.fetchGroupList();
let currGroup = this.props.groupList[0] || { group_name: '', group_desc: '' };
if(this.props.groupList.length && groupId){
for(let i = 0;i<this.props.groupList.length;i++){
if(this.props.groupList[i]._id === groupId){
if (this.props.groupList.length && groupId) {
for (let i = 0; i < this.props.groupList.length; i++) {
if (this.props.groupList[i]._id === groupId) {
currGroup = this.props.groupList[i];
}else{
} else {
this.props.history.replace(`${currGroup._id}`);
}
}
}else if(!groupId && this.props.groupList.length){
} else if (!groupId && this.props.groupList.length) {
this.props.history.push(`/group/${this.props.groupList[0]._id}`);
}
this.setState({groupList: this.props.groupList});
this.setState({ groupList: this.props.groupList });
this.props.setCurrGroup(currGroup)
}
@ -114,9 +114,9 @@ export default class GroupList extends Component {
addGroupModalVisible: false
});
await this.props.fetchGroupList();
this.setState({groupList: this.props.groupList});
this.setState({ groupList: this.props.groupList });
this.props.setCurrGroup(res.data.data)
}else{
} else {
message.error(res.data.errmsg)
}
}
@ -124,7 +124,7 @@ export default class GroupList extends Component {
async editGroup() {
const { currGroupName: group_name, currGroupDesc: group_desc } = this.state;
const id = this.props.currGroup._id;
const res = await axios.post('/api/group/up', { group_name, group_desc, id });
const res = await axios.post('/api/group/up', { group_name, group_desc, id });
if (res.data.errcode) {
message.error(res.data.errmsg);
} else {
@ -137,17 +137,17 @@ export default class GroupList extends Component {
@autobind
inputNewGroupName(e, type) {
if (type === TYPE_EDIT) {
this.setState({ currGroupName: e.target.value})
this.setState({ currGroupName: e.target.value })
} else {
this.setState({newGroupName: e.target.value});
this.setState({ newGroupName: e.target.value });
}
}
@autobind
inputNewGroupDesc(e, type) {
if (type === TYPE_EDIT) {
this.setState({ currGroupDesc: e.target.value})
this.setState({ currGroupDesc: e.target.value })
} else {
this.setState({newGroupDesc: e.target.value});
this.setState({ newGroupDesc: e.target.value });
}
}
@ -166,25 +166,25 @@ export default class GroupList extends Component {
})
}
showConfirm =()=> {
showConfirm = () => {
let that = this;
confirm({
title: "确认删除 "+that.props.currGroup.group_name+" 分组吗?",
content: <div style={{marginTop:'10px', fontSize: '12px', lineHeight: '25px'}}>
title: "确认删除 " + that.props.currGroup.group_name + " 分组吗?",
content: <div style={{ marginTop: '10px', fontSize: '12px', lineHeight: '25px' }}>
<Alert message="警告:此操作非常危险,会删除该分组下面所有项目和接口,并且无法恢复!" type="warning" />
<div style={{marginTop: '16px'}}>
<div style={{ marginTop: '16px' }}>
<p><b>请输入分组名称确认此操作:</b></p>
<Input id="group_name" />
</div>
</div>,
onOk() {
let groupName = document.getElementById('group_name').value;
if(that.props.currGroup.group_name !== groupName){
if (that.props.currGroup.group_name !== groupName) {
message.error('分组名称有误')
return new Promise((resolve, reject)=>{
return new Promise((resolve, reject) => {
reject('error')
})
}else{
} else {
that.deleteGroup()
}
@ -198,14 +198,14 @@ export default class GroupList extends Component {
async deleteGroup() {
const self = this;
const { currGroup } = self.props;
const res = await axios.post('/api/group/del', {id: currGroup._id})
const res = await axios.post('/api/group/del', { id: currGroup._id })
if (res.data.errcode) {
message.error(res.data.errmsg);
} else {
message.success('删除成功')
await self.props.fetchGroupList()
const currGroup = self.props.groupList[0] || { group_name: '', group_desc: '' };
self.setState({groupList: self.props.groupList});
self.setState({ groupList: self.props.groupList });
self.props.setCurrGroup(currGroup)
}
}
@ -215,17 +215,17 @@ export default class GroupList extends Component {
const v = value || e.target.value;
const { groupList } = this.props;
if (v === '') {
this.setState({groupList})
this.setState({ groupList })
} else {
this.setState({groupList: groupList.filter(group => new RegExp(v, 'i').test(group.group_name))})
this.setState({ groupList: groupList.filter(group => new RegExp(v, 'i').test(group.group_name)) })
}
}
render () {
render() {
const { currGroup } = this.props;
const delmark = <Icon className="edit-group" type="edit" title="编辑分组" onClick={() => this.showModal(TYPE_EDIT)}/>
const editmark = <Icon className="delete-group" onClick={()=> {this.showConfirm()}} type="delete" title="删除分组"/>
const delmark = <Icon className="edit-group" type="edit" title="编辑分组" onClick={() => this.showModal(TYPE_EDIT)} />
const editmark = <Icon className="delete-group" onClick={() => { this.showConfirm() }} type="delete" title="删除分组" />
const addmark = <Icon className="edit-group" onClick={this.showModal} type="plus" title="添加分组" />
return (
@ -235,10 +235,13 @@ export default class GroupList extends Component {
<div className="curr-group-name">
<div className="text" title={currGroup.group_name}>{currGroup.group_name}</div>
{
this.props.curUserRole === "admin"?(editmark):''
this.props.curUserRole === "admin" ? (editmark) : ''
}
{
this.props.curUserRole === "admin"?(delmark):''
this.props.curUserRole === "admin" ? (delmark) : ''
}
{
this.props.curUserRole === 'admin' ? (addmark) : ''
}
</div>
@ -246,12 +249,8 @@ export default class GroupList extends Component {
</div>
<div className="group-operate">
<div className="search">
<Search placeholder="Filter by name" onChange={this.searchGroup} onSearch={(v) => this.searchGroup(null, v)}/>
<Search placeholder="Filter by name" onChange={this.searchGroup} onSearch={(v) => this.searchGroup(null, v)} />
</div>
{
this.props.curUserRole === "admin"?(<Button type="primary" onClick={this.showModal}>添加分组</Button>):''
}
</div>
<Menu
className="group-list"
@ -284,7 +283,7 @@ export default class GroupList extends Component {
<Row gutter={6} className="modal-input">
<Col span="5"><div className="label">简介</div></Col>
<Col span="15">
<TextArea rows = {3} placeholder="请输入分组描述" onChange={this.inputNewGroupDesc}></TextArea>
<TextArea rows={3} placeholder="请输入分组描述" onChange={this.inputNewGroupDesc}></TextArea>
</Col>
</Row>
<Row gutter={6} className="modal-input">

View File

@ -1,12 +1,13 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Row, Col } from 'antd';
import { addProject, fetchProjectList, delProject, changeUpdateModal } from '../../../reducer/modules/project';
import { Row, Col, Button, Tooltip } from 'antd';
import { Link } from 'react-router-dom';
import { addProject, fetchProjectList, delProject, changeUpdateModal } from '../../../reducer/modules/project';
import ProjectCard from '../../../components/ProjectCard/ProjectCard.js';
import ErrMsg from '../../../components/ErrMsg/ErrMsg.js';
import { autobind } from 'core-decorators';
import { setBreadcrumb } from '../../../reducer/modules/user';
import { setBreadcrumb } from '../../../reducer/modules/user';
import './ProjectList.scss'
@ -75,7 +76,7 @@ class ProjectList extends Component {
}
componentWillReceiveProps(nextProps) {
this.props.setBreadcrumb([{name: '分组: ' + (nextProps.currGroup.group_name || '')}]);
this.props.setBreadcrumb([{ name: '' + (nextProps.currGroup.group_name || '') }]);
// 切换分组
if (this.props.currGroup !== nextProps.currGroup) {
@ -100,14 +101,29 @@ class ProjectList extends Component {
render() {
const projectData = this.state.projectData;
return (
<div className="m-panel card-panel card-panel-s">
<div style={{ paddingTop: '20px' }} className="m-panel card-panel card-panel-s project-list" >
<Row className="project-list-header">
<Col span={16} style={{ textAlign: 'left' }}>
{this.props.currGroup.group_name}分组 {projectData.length} 个项目
</Col>
<Col>
<Tooltip title="您没有权限,请联系该分组组长或管理员">
{this.props.currGroup.role ?
<Button type="primary" ><Link to="/add-project">添加项目</Link></Button> :
<Button type="primary" disabled >添加项目</Button>}
</Tooltip>
</Col>
</Row>
<Row gutter={16}>
{projectData.length ? projectData.map((item, index) => {
return (
<Col span={8} key={index}>
<ProjectCard projectData={item} callbackResult={this.receiveRes} />
</Col>);
}) : <ErrMsg type="noProject"/>}
}) : <ErrMsg type="noProject" />}
</Row>
</div>
);

View File

@ -3,6 +3,7 @@
margin-bottom: 0;
}
.m-panel{
background-color: #fff;
padding: 24px;
@ -12,6 +13,21 @@
margin-top: 0;
}
.project-list{
.project-list-header{
background: #eee;
height: 64px;
line-height: 40px;
border-radius: 4px;
text-align: right;
padding: 12px 15px;
font-weight: bold;
margin-bottom: 15px;
}
}
.ant-input-group-wrapper {
width: 100%;
}

View File

@ -52,8 +52,11 @@ export default class InterfaceColContent extends Component {
result.payload.data.data.find(item => +item._id === +currColId) && +currColId ||
result.payload.data.data[0]._id;
this.props.history.push('/project/' + params.id + '/interface/col/' + currColId)
this.props.fetchCaseList(currColId);
this.props.setColData({currColId: +currColId, isShowCol: true})
if(currColId && currColId != 0){
this.props.fetchCaseList(currColId);
this.props.setColData({currColId: +currColId, isShowCol: true})
}
}
componentWillReceiveProps(nextProps) {
@ -63,8 +66,11 @@ export default class InterfaceColContent extends Component {
if (!interfaceColList.find(item => +item._id === +newColId)) {
this.props.history.push('/project/' + id + '/interface/col/' + interfaceColList[0]._id)
} else if (oldColId !== newColId) {
this.props.fetchCaseList(newColId);
this.props.setColData({currColId: +newColId, isShowCol: true})
if(newColId && newColId != 0){
this.props.fetchCaseList(newColId);
this.props.setColData({currColId: +newColId, isShowCol: true})
}
}
}

View File

@ -4,13 +4,15 @@ import _ from 'underscore'
import constants from '../../../../constants/variable.js'
import { handlePath, nameLengthLimit } from '../../../../common.js'
import json5 from 'json5'
import {message} from 'antd'
import { message, Tabs } from 'antd'
import Editor from 'wangeditor'
const TabPane = Tabs.TabPane;
const validJson = (json)=>{
try{
const validJson = (json) => {
try {
json5.parse(json);
return true;
}catch(e){
} catch (e) {
return false;
}
}
@ -90,34 +92,33 @@ class InterfaceEditForm extends Component {
res_body: '',
desc: '',
res_body_mock: '',
jsonType: 'tpl',
mockUrl: this.props.mockUrl
}, curdata)
}
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
if (!err) {
values.desc = this.editor.txt.html();
if (values.res_body_type === 'json') {
if(validJson(this.state.res_body) === false){
return message.error('返回json格式有问题请检查')
if (this.state.res_body && validJson(this.state.res_body) === false) {
return message.error('返回body json格式有问题请检查')
}
values.res_body = this.state.res_body
values.res_body = this.state.res_body;
}
if (values.req_body_type === 'json') {
if(validJson(this.state.req_body_other) === false){
return message.error('请求json格式有问题请检查')
if (this.state.req_body_other && validJson(this.state.req_body_other) === false) {
return message.error('响应Body json格式有问题请检查');
}
values.req_body_other = this.state.req_body_other;
}
values.method = this.state.method;
values.req_params = values.req_params || [];
let isfile = false, isHavaContentType = false;
if (values.req_body_type === 'form') {
if (values.req_body_type === 'form') {
values.req_body_form.forEach((item) => {
if (item.type === 'file') {
isfile = true;
@ -179,7 +180,7 @@ class InterfaceEditForm extends Component {
container: 'res_body_json',
data: that.state.res_body,
onChange: function (d) {
if (d.format === true){
if (d.format === true) {
mockPreview.editor.setValue(d.mockText)
}
that.setState({
@ -194,6 +195,9 @@ class InterfaceEditForm extends Component {
data: resBodyEditor.curData.mockText,
readOnly: true
})
let editor = this.editor = new Editor('#desc');
editor.create();
}
addParams = (name, data) => {
@ -214,6 +218,13 @@ class InterfaceEditForm extends Component {
this.setState(newValue)
}
handleJsonType = (key)=>{
key = key || 'tpl';
this.setState({
jsonType: key
})
}
handlePath = (e) => {
let val = e.target.value, queue = [];
val = handlePath(val)
@ -316,7 +327,7 @@ class InterfaceEditForm extends Component {
const requestBodyTpl = (data, index) => {
return <Row key={index} className="interface-edit-item-content">
<Col span="8">
<Col span="4">
{getFieldDecorator('req_body_form[' + index + '].name', {
initialValue: data.name
})(
@ -328,8 +339,18 @@ class InterfaceEditForm extends Component {
initialValue: data.type
})(
<Select>
<Option value="text">文本</Option>
<Option value="file">文件</Option>
<Option value="text">text</Option>
<Option value="file">file</Option>
</Select>
)}
</Col>
<Col span="4" >
{getFieldDecorator('req_body_form[' + index + '].required', {
initialValue: data.required
})(
<Select>
<Option value="1">必需</Option>
<Option value="0">非必需</Option>
</Select>
)}
</Col>
@ -471,16 +492,6 @@ class InterfaceEditForm extends Component {
)}
</FormItem>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="接口描述"
>
{getFieldDecorator('desc', { initialValue: this.state.desc })(
<Input.TextArea placeholder="接口描述" />
)}
</FormItem>
<FormItem
className="interface-edit-item"
{...formItemLayout}
@ -596,35 +607,28 @@ class InterfaceEditForm extends Component {
)}
</FormItem>
<Row className="interface-edit-item" style={{ display: this.props.form.getFieldValue('res_body_type') === 'json' ? 'block' : 'none' }}>
<Col span={17} offset={4} >
<h3>基于mockjs和json5,可直接写mock模板和注释,具体使用方法请查看文档</h3>
<div id="res_body_json" style={{ minHeight: "300px" }} ></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">
</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>
</div>
</Col>
</Row>
<FormItem
style={{ display: this.props.form.getFieldValue('res_body_type') === 'json' ? 'block' : 'none' }}
className="interface-edit-item"
{...formItemLayout}
label="mock地址"
>
<Input disabled onChange={() => { }} value={this.state.mockUrl} />
</FormItem>
<FormItem
style={{ display: this.props.form.getFieldValue('res_body_type') === 'json' ? 'block' : 'none' }}
className="interface-edit-item"
{...formItemLayout}
label="预览"
>
<div id="mock-preview" style={{ backgroundColor: "#eee", lineHeight: "20px", minHeight: "300px" }}>
</div>
</FormItem>
<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 })(
@ -635,12 +639,27 @@ class InterfaceEditForm extends Component {
</Row>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="备注"
>
<div id="desc" ></div>
{/* {getFieldDecorator('desc', { initialValue: this.state.desc })(
<Input.TextArea style={{minHeight: '200px'}} placeholder="接口备注信息" />
)} */}
</FormItem>
<FormItem
className="interface-edit-item"
{...formItemLayout}
label="是否开启邮件通知"
>
{getFieldDecorator('switch_notice', { valuePropName: 'checked', initialValue: false })(
{getFieldDecorator('switch_notice', { valuePropName: 'checked', initialValue: true })(
<Switch checkedChildren="开" unCheckedChildren="关" />
)}
</FormItem>

View File

@ -14,9 +14,9 @@ const TreeNode = Tree.TreeNode;
@connect(
state => {
return {
list: state.inter.list,
inter: state.inter.curdata,
@ -174,12 +174,12 @@ class InterfaceMenu extends Component {
title: '您确认删除此接口',
content: '温馨提示:接口删除后,无法恢复',
async onOk() {
await that.props.deleteInterfaceData(id, that.props.projectId)
await that.getList()
ref.destroy()
that.props.history.push('/project/' + that.props.match.params.id + '/interface/api')
},
async onCancel() {
ref.destroy()
@ -227,8 +227,8 @@ class InterfaceMenu extends Component {
const matchParams = this.props.match.params;
let menuList = this.state.list;
const searchBox = <div className="interface-filter">
<Input onChange={this.onFilter} value={this.state.filter} placeholder="Filter by name" style={{ width: "70%" }} />
<Tag color="#108ee9" onClick={() => this.changeModal('add_cat_modal_visible', true)} style={{ marginLeft: "16px" }} ><Icon type="plus" /></Tag>
<Input onChange={this.onFilter} value={this.state.filter} placeholder="Filter by name" />
<Tag color="#108ee9" onClick={() => this.changeModal('add_cat_modal_visible', true)} className="btn-filter" ><Icon type="plus" /></Tag>
<Modal
title="添加接口"
visible={this.state.visible}
@ -250,7 +250,7 @@ class InterfaceMenu extends Component {
<Modal
title="修改分类"
visible={this.state.change_cat_modal_visible}
onCancel={() => this.changeModal('change_cat_modal_visible', false)}
onCancel={() => this.changeModal('change_cat_modal_visible', false)}
footer={null}
>
<AddInterfaceCatForm catdata={this.state.curCatdata} onCancel={() => this.changeModal('change_cat_modal_visible', false)} onSubmit={this.handleChangeInterfaceCat} />
@ -297,7 +297,7 @@ class InterfaceMenu extends Component {
// case 'DELETE': color = 'red'; break;
// default: color = "yellow";
// }
return <TreeNode
return <TreeNode
title={<div className="aa" onMouseEnter={() => this.enterItem(item._id)} onMouseLeave={this.leaveItem} >
<Link className="interface-item" to={"/project/" + matchParams.id + "/interface/api/" + item._id} >{item.title}</Link>
<Icon type='delete' className="interface-delete-icon" onClick={() => { this.showConfirm(item._id) }} style={{ display: this.state.delIcon == item._id ? 'block' : 'none' }} />
@ -335,7 +335,7 @@ class InterfaceMenu extends Component {
let currentKes = defaultExpandedKeys();
if (this.state.filter) {
let arr = [];
menuList = menuList.filter( (item) => {
@ -343,12 +343,12 @@ class InterfaceMenu extends Component {
if (item.name.indexOf(this.state.filter) === -1) {
item.list = item.list.filter(inter=>{
if(inter.title.indexOf(this.state.filter) === -1 && inter.path.indexOf(this.state.filter)){
return false;
return false;
}
//arr.push('cat_' + inter.catid)
interfaceFilter = true;
return true;
})
return interfaceFilter === true
}
@ -379,7 +379,7 @@ class InterfaceMenu extends Component {
<Icon type='setting' className="interface-delete-icon" />
</Dropdown>
</div>}
key={'cat_' + item._id}
key={'cat_' + item._id}
className={`interface-item-nav ${item.list.length?"":"cat_switch_hidden"}`}
>
{item.list.map(item_interface_create)}

View File

@ -45,7 +45,7 @@ class View extends Component {
key: 'type',
render: (text)=>{
text = text || "";
return text.toLowerCase()==="text"?<span><i className="TextIcon">T</i></span>:<span><Icon type="file" /></span>
return text.toLowerCase()==="text"?<span><i className="query-icon text">T</i></span>:<span><Icon type="file" className="query-icon" /></span>
}
},{
title: '是否必须',
@ -250,7 +250,7 @@ class View extends Component {
if(!methodColor) methodColor = "get";
let res = <div className="caseContainer">
<div className="colName">
<span className="colKey">接口名</span>
<span className="colKey">接口名</span>
<span className="colValue">{this.props.curData.title}</span>
</div>
<div className="colMethod">
@ -262,7 +262,7 @@ class View extends Component {
<span className="colValue">{this.props.currProject.basepath}{this.props.curData.path}</span>
</div>
<div className="colstatus">
<span className="colKey"></span>
<span className="colKey">&emsp;&emsp;</span>
<span className={'tag-status ' + this.props.curData.status}>{status[this.props.curData.status]}</span>
</div>
<div className="colAddTime">
@ -278,8 +278,8 @@ class View extends Component {
<span className="colValue">{location.protocol + '//' + location.hostname + (location.port !== "" ? ":" + location.port : "") + `/mock/${this.props.currProject._id}${this.props.currProject.basepath}${this.props.curData.path}`}</span>
</div>
{this.props.curData.desc?<div className="colDesc">
<span className="colKey">接口描述</span>
<span className="colValue">{this.props.curData.desc}</span>
<span className="colKey">接口备注</span>
<span className="colValue" dangerouslySetInnerHTML={{__html: this.props.curData.desc}}></span>
</div>:""}
{req_dataSource.length?<div className="colHeader">
<span className="colKey">路径参数</span>

View File

@ -21,7 +21,7 @@
margin: 0px;
.colKey{
padding-bottom: 0px;
}
}
.ace_print-margin{
@ -37,12 +37,19 @@
}
}
.colBody{
.ant-table-row{
span{
i{
margin-right: 4px;
}
}
.query-icon {
display: inline-block;
width: .12rem;
margin-right: 4px;
position: relative;
&.text:after {
content: 'T';
display: block;
position: absolute;
right: 2px;
bottom: -2px;
transform: scale(.7);
}
}
}
.colDesc{
@ -108,4 +115,4 @@
// margin-left: 50px;
// overflow:visible;
// }
}
}

View File

@ -42,13 +42,23 @@
height: 40px;
line-height: 31px;
}
.ant-input {
width: 100%;
}
.interface-filter{
padding-top:7px;
padding-left: 10px;
padding-right: 50px;
height:45px;
line-height: 32px;
padding-top:7px;
background-color: #efefef
background-color: #efefef;
position: relative;
}
.btn-filter {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
}
.interface-list{
.cat_switch_hidden{
@ -56,7 +66,7 @@
visibility: hidden;
}
}
a{
color: #333
@ -71,7 +81,6 @@
}
.interface-item{
display: inline-block;
width: 180px;
overflow: hidden;
top: 0px;
line-height: 100%;
@ -94,7 +103,7 @@
}
}
}
.right-content{
margin:3px;
min-height: 5rem;

View File

@ -162,7 +162,7 @@ class ProjectMember extends Component {
render () {
const columns = [{
title: ' 项目成员 ('+this.state.projectMemberList.length + ') 人',
title: this.props.projectMsg.name + ' 项目成员 ('+this.state.projectMemberList.length + ') 人',
dataIndex: 'username',
key: 'username',
render: (text, record) => {

View File

@ -231,14 +231,14 @@ class ProjectMessage extends Component {
validator(rule, value, callback) {
if (value) {
if (value.length === 0) {
callback('请输入环境名');
callback('请输入环境');
} else if (!/\S/.test(value)) {
callback('请输入环境名');
callback('请输入环境');
} else {
return callback();
}
} else {
callback('请输入环境名');
callback('请输入环境');
}
}
}]
@ -266,8 +266,6 @@ class ProjectMessage extends Component {
callback('请输入环境域名!');
} else if (/\s/.test(value)) {
callback('环境域名不允许出现空格!');
} else if (/\//.test(value)) {
callback('环境域名不允许出现‘\/!');
} else {
return callback();
}
@ -328,7 +326,7 @@ class ProjectMessage extends Component {
</Col>
<Col xs={18} sm={15} lg={19} className="setting-intro">
<h2 className="ui-title">{this.state.currGroup + ' / ' + projectMsg.name}</h2>
<p className="ui-desc">{projectMsg.desc}</p>
{/* <p className="ui-desc">{projectMsg.desc}</p> */}
</Col>
</Row>
<hr className="breakline" />
@ -403,10 +401,10 @@ class ProjectMessage extends Component {
{getFieldDecorator('desc', {
initialValue: initFormValues.desc,
rules: [{
required: false, message: '描述不超过100字!', max: 100
required: false
}]
})(
<TextArea rows={4} />
<TextArea rows={8} />
)}
</FormItem>

View File

@ -5,6 +5,7 @@ import App from './Application'
import { Provider } from 'react-redux'
import createStore from './reducer/create';
import './styles/theme.less'
import './styles/common.scss';
const store = createStore();
if (process.env.NODE_ENV === 'production') {
ReactDOM.render(

61
npm-shrinkwrap.json generated
View File

@ -240,7 +240,7 @@
},
"are-we-there-yet": {
"version": "1.1.4",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
"requires": {
"delegates": "1.0.0",
@ -428,7 +428,7 @@
},
"async-foreach": {
"version": "0.1.3",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/async-foreach/-/async-foreach-0.1.3.tgz",
"resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
"integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI="
},
"async-validator": {
@ -1664,7 +1664,7 @@
},
"block-stream": {
"version": "0.0.9",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/block-stream/-/block-stream-0.0.9.tgz",
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
"integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
"requires": {
"inherits": "2.0.3"
@ -2168,7 +2168,7 @@
},
"cli-source-preview": {
"version": "1.1.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/cli-source-preview/-/cli-source-preview-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/cli-source-preview/-/cli-source-preview-1.1.0.tgz",
"integrity": "sha1-BTA6sSeakJPq0aODez7iMfMAZUQ=",
"requires": {
"chalk": "1.1.3"
@ -2176,7 +2176,7 @@
"dependencies": {
"chalk": {
"version": "1.1.3",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/chalk/-/chalk-1.1.3.tgz",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "2.2.1",
@ -2492,7 +2492,7 @@
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/console-control-strings/-/console-control-strings-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"consolidate": {
@ -2707,7 +2707,7 @@
},
"cross-spawn": {
"version": "3.0.1",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/cross-spawn/-/cross-spawn-3.0.1.tgz",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
"integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
"requires": {
"lru-cache": "4.1.1",
@ -4495,7 +4495,7 @@
},
"fs-promise": {
"version": "0.5.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/fs-promise/-/fs-promise-0.5.0.tgz",
"resolved": "https://registry.npmjs.org/fs-promise/-/fs-promise-0.5.0.tgz",
"integrity": "sha1-Q0fWv2JGVacGGkMZITw5MnatPvM=",
"requires": {
"any-promise": "1.3.0",
@ -4506,7 +4506,7 @@
"dependencies": {
"fs-extra": {
"version": "0.26.7",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/fs-extra/-/fs-extra-0.26.7.tgz",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
"integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
"requires": {
"graceful-fs": "4.1.11",
@ -5428,7 +5428,7 @@
},
"fstream": {
"version": "1.0.11",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/fstream/-/fstream-1.0.11.tgz",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
"integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
"requires": {
"graceful-fs": "4.1.11",
@ -5444,7 +5444,7 @@
},
"gauge": {
"version": "2.7.4",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/gauge/-/gauge-2.7.4.tgz",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"requires": {
"aproba": "1.1.2",
@ -6111,7 +6111,7 @@
},
"has-unicode": {
"version": "2.0.1",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/has-unicode/-/has-unicode-2.0.1.tgz",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"hash-base": {
@ -6464,7 +6464,7 @@
},
"in-publish": {
"version": "2.0.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/in-publish/-/in-publish-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
"integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E="
},
"indent-string": {
@ -8190,7 +8190,7 @@
},
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
},
"lodash.cond": {
@ -8312,7 +8312,7 @@
},
"lodash.mergewith": {
"version": "4.6.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz",
"integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU="
},
"lodash.once": {
@ -8948,7 +8948,7 @@
},
"node-gyp": {
"version": "3.6.2",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/node-gyp/-/node-gyp-3.6.2.tgz",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz",
"integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=",
"requires": {
"fstream": "1.0.11",
@ -8968,7 +8968,7 @@
"dependencies": {
"semver": {
"version": "5.3.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/semver/-/semver-5.3.0.tgz",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
}
}
@ -9028,7 +9028,7 @@
},
"node-sass-china": {
"version": "4.5.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/node-sass-china/-/node-sass-china-4.5.0.tgz",
"resolved": "https://registry.npmjs.org/node-sass-china/-/node-sass-china-4.5.0.tgz",
"integrity": "sha1-YnbBcNzqWBz14lwC/sqjUPcyx1Q=",
"requires": {
"async-foreach": "0.1.3",
@ -9053,7 +9053,7 @@
"dependencies": {
"chalk": {
"version": "1.1.3",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/chalk/-/chalk-1.1.3.tgz",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "2.2.1",
@ -9065,7 +9065,7 @@
},
"gaze": {
"version": "1.1.2",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/gaze/-/gaze-1.1.2.tgz",
"resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz",
"integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=",
"requires": {
"globule": "1.2.0"
@ -9073,7 +9073,7 @@
},
"globule": {
"version": "1.2.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/globule/-/globule-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz",
"integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=",
"requires": {
"glob": "7.1.2",
@ -9083,7 +9083,7 @@
},
"lodash.assign": {
"version": "4.2.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/lodash.assign/-/lodash.assign-4.2.0.tgz",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
}
}
@ -9240,7 +9240,7 @@
},
"nopt": {
"version": "3.0.6",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/nopt/-/nopt-3.0.6.tgz",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
"integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
"requires": {
"abbrev": "1.1.0"
@ -12785,7 +12785,7 @@
},
"sass-graph": {
"version": "2.2.4",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/sass-graph/-/sass-graph-2.2.4.tgz",
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
"integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
"requires": {
"glob": "7.1.2",
@ -12827,7 +12827,7 @@
},
"scss-tokenizer": {
"version": "0.2.3",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
"resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
"integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
"requires": {
"js-base64": "2.1.9",
@ -12836,7 +12836,7 @@
"dependencies": {
"source-map": {
"version": "0.4.4",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/source-map/-/source-map-0.4.4.tgz",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
"requires": {
"amdefine": "1.0.1"
@ -13244,7 +13244,7 @@
},
"stdout-stream": {
"version": "1.4.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/stdout-stream/-/stdout-stream-1.4.0.tgz",
"resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz",
"integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=",
"requires": {
"readable-stream": "2.3.3"
@ -13651,7 +13651,7 @@
},
"tar": {
"version": "2.2.1",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/tar/-/tar-2.2.1.tgz",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
"integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
"requires": {
"block-stream": "0.0.9",
@ -14436,6 +14436,11 @@
"makeerror": "1.0.11"
}
},
"wangeditor": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/wangeditor/-/wangeditor-3.0.8.tgz",
"integrity": "sha1-qm5FetXmDiFbwuYE7uIB2TFdAXY="
},
"warning": {
"version": "3.0.0",
"resolved": "https://repo.corp.qunar.com/artifactory/api/npm/npm-qunar/warning/-/warning-3.0.0.tgz",

View File

@ -93,6 +93,7 @@
"universal-cookie": "^2.0.8",
"url": "^0.11.0",
"validate-commit-msg": "^2.12.2",
"wangeditor": "^3.0.8",
"webpack": "^3.5.5",
"webpack-dev-middleware": "^1.12.0",
"ykit-config-antd": "^0.1.3",

View File

@ -1,3 +1,3 @@
module.exports = function(){
}
return null;
};

View File

@ -282,7 +282,20 @@ class groupController extends baseController {
try {
var groupInst = yapi.getInst(groupModel);
let result = await groupInst.list();
ctx.body = yapi.commons.resReturn(result);
let newResult = [];
if(result && result.length > 0){
for(let i=0; i< result.length; i++){
result[i] = result[i].toObject();
result[i].role = await this.checkAuth(result[i]._id, 'group', 'edit');
if(result[i].role){
newResult.unshift(result[i]);
}else{
newResult.push(result[i]);
}
}
}
ctx.body = yapi.commons.resReturn(newResult);
} catch (e) {
ctx.body = yapi.commons.resReturn(null, 402, e.message);
}

View File

@ -76,10 +76,23 @@ class interfaceController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 400, '接口请求路径不能为空');
}
if (!yapi.commons.verifyPath(params.path)) {
let http_path = url.parse(params.path, true);
params.path = http_path.pathname;
if (!yapi.commons.verifyPath(http_path.pathname)) {
return ctx.body = yapi.commons.resReturn(null, 400, '接口path第一位必须是/,最后一位不能为/');
}
if (!params.req_query) {
params.req_query = [];
Object.keys(http_path.query).forEach((item) => {
params.req_query.push({
name: item
})
})
}
let checkRepeat = await this.Model.checkRepeat(params.project_id, params.path, params.method);
if (checkRepeat > 0) {
@ -178,13 +191,19 @@ class interfaceController extends baseController {
*/
async get(ctx) {
let params = ctx.request.query;
if (!params.id) {
return ctx.body = yapi.commons.resReturn(null, 400, '接口id不能为空');
}
try {
let result = await this.Model.get(params.id);
let project = await this.projectModel.getBaseInfo(result.project_id);
if (project.project_type === 'private') {
if (await this.checkAuth(project._id, 'project', 'edit') !== true) {
return ctx.body = yapi.commons.resReturn(null, 406, '没有权限');
}
}
ctx.body = yapi.commons.resReturn(result);
} catch (e) {
ctx.body = yapi.commons.resReturn(null, 402, e.message);
@ -203,7 +222,12 @@ class interfaceController extends baseController {
*/
async list(ctx) {
let project_id = ctx.request.query.project_id;
let project = await this.projectModel.getBaseInfo(project_id);
if (project.project_type === 'private') {
if (await this.checkAuth(project._id, 'project', 'edit') !== true) {
return ctx.body = yapi.commons.resReturn(null, 406, '没有权限');
}
}
if (!project_id) {
return ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空');
}
@ -222,7 +246,15 @@ class interfaceController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 400, 'catid不能为空');
}
try {
let catdata = await this.catModel.get(catid);
let project = await this.projectModel.getBaseInfo(catdata.project_id);
if (project.project_type === 'private') {
if (await this.checkAuth(project._id, 'project', 'edit') !== true) {
return ctx.body = yapi.commons.resReturn(null, 406, '没有权限');
}
}
let result = await this.Model.listByCatid(catid)
ctx.body = yapi.commons.resReturn(result);
} catch (err) {
ctx.body = yapi.commons.resReturn(null, 402, err.message);
@ -235,6 +267,14 @@ class interfaceController extends baseController {
if (!project_id) {
return ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空');
}
let project = await this.projectModel.getBaseInfo(project_id);
if (project.project_type === 'private') {
if (await this.checkAuth(project._id, 'project', 'edit') !== true) {
return ctx.body = yapi.commons.resReturn(null, 406, '没有权限');
}
}
try {
let result = await this.catModel.list(project_id), newResult = [];
for (let i = 0, item, list; i < result.length; i++) {
@ -613,26 +653,32 @@ class interfaceController extends baseController {
}
/**
* 获取分类列表
* @interface /interface/getCatMenu
* @method GET
* @category interface
* @foldnumber 10
* @param {Number} project_id 项目id不能为空
* @returns {Object}
* @example ./api/interface/getCatMenu
*/
/**
* 获取分类列表
* @interface /interface/getCatMenu
* @method GET
* @category interface
* @foldnumber 10
* @param {Number} project_id 项目id不能为空
* @returns {Object}
* @example ./api/interface/getCatMenu
*/
async getCatMenu(ctx) {
let project_id = ctx.request.query.project_id;
if(!project_id){
if (!project_id) {
return ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空');
}
try{
let project = await this.projectModel.getBaseInfo(project_id);
if (project.project_type === 'private') {
if (await this.checkAuth(project._id, 'project', 'edit') !== true) {
return ctx.body = yapi.commons.resReturn(null, 406, '没有权限');
}
}
try {
let res = await this.catModel.list(project_id);
return ctx.body = yapi.commons.resReturn(res);
}catch(e){
} catch (e) {
yapi.commons.resReturn(null, 400, e.message);
}
}

View File

@ -27,6 +27,12 @@ class interfaceColController extends baseController{
async list(ctx){
try {
let id = ctx.query.project_id;
let project = await this.projectModel.getBaseInfo(id);
if (project.project_type === 'private') {
if (await this.checkAuth(project._id, 'project', 'edit') !== true) {
return ctx.body = yapi.commons.resReturn(null, 406, '没有权限');
}
}
let result = await this.colModel.list(id);
for(let i=0; i< result.length;i++){
@ -110,8 +116,19 @@ class interfaceColController extends baseController{
async getCaseList(ctx){
try {
let id = ctx.query.col_id;
let inst = yapi.getInst(interfaceCaseModel);
let result = await inst.list(id, 'all');
if(!id || id == 0){
return ctx.body = yapi.commons.resReturn(null, 407, 'col_id不能为空')
}
let result = await this.caseModel.list(id, 'all');
let colData = await this.colModel.get(id);
let project = await this.projectModel.getBaseInfo(colData.project_id);
if (project.project_type === 'private') {
if (await this.checkAuth(project._id, 'project', 'edit') !== true) {
return ctx.body = yapi.commons.resReturn(null, 406, '没有权限');
}
}
for(let index=0; index< result.length; index++){
result[index] = result[index].toObject();
@ -120,7 +137,7 @@ class interfaceColController extends baseController{
await this.caseModel.del(result[index]._id);
result[index] = undefined;
continue;
}
}
let projectData = await this.projectModel.getBaseInfo(interfaceData.project_id);
result[index].path = projectData.basepath + interfaceData.path;
result[index].method = interfaceData.method;

View File

@ -308,6 +308,11 @@ class projectController extends baseController {
if (!result) {
return ctx.body = yapi.commons.resReturn(null, 400, '不存在的项目');
}
if (result.project_type === 'private') {
if (await this.checkAuth(result._id, 'project', 'edit') !== true) {
return ctx.body = yapi.commons.resReturn(null, 406, '没有权限');
}
}
result = result.toObject();
let catInst = yapi.getInst(interfaceCatModel);
let cat = await catInst.list(params.id);

View File

@ -899,7 +899,7 @@
<p>
<small class="text-muted">源码位置:</small>
<a href="./static/server/controllers/group.js.html#290" target="_blank">./server/controllers/group.js:290</a>
<a href="./static/server/controllers/group.js.html#303" target="_blank">./server/controllers/group.js:303</a>
</p>
@ -971,7 +971,7 @@
<p>
<small class="text-muted">源码位置:</small>
<a href="./static/server/controllers/group.js.html#330" target="_blank">./server/controllers/group.js:330</a>
<a href="./static/server/controllers/group.js.html#343" target="_blank">./server/controllers/group.js:343</a>
</p>
@ -3511,7 +3511,7 @@
<p>
<small class="text-muted">源码位置:</small>
<a href="./static/server/controllers/interface.js.html#168" target="_blank">./server/controllers/interface.js:168</a>
<a href="./static/server/controllers/interface.js.html#181" target="_blank">./server/controllers/interface.js:181</a>
</p>
@ -3576,7 +3576,7 @@
<p>
<small class="text-muted">源码位置:</small>
<a href="./static/server/controllers/interface.js.html#193" target="_blank">./server/controllers/interface.js:193</a>
<a href="./static/server/controllers/interface.js.html#206" target="_blank">./server/controllers/interface.js:206</a>
</p>
@ -3641,7 +3641,7 @@
<p>
<small class="text-muted">源码位置:</small>
<a href="./static/server/controllers/interface.js.html#255" target="_blank">./server/controllers/interface.js:255</a>
<a href="./static/server/controllers/interface.js.html#268" target="_blank">./server/controllers/interface.js:268</a>
</p>
@ -3874,7 +3874,7 @@
<p>
<small class="text-muted">源码位置:</small>
<a href="./static/server/controllers/interface.js.html#427" target="_blank">./server/controllers/interface.js:427</a>
<a href="./static/server/controllers/interface.js.html#440" target="_blank">./server/controllers/interface.js:440</a>
</p>
@ -3946,7 +3946,7 @@
<p>
<small class="text-muted">源码位置:</small>
<a href="./static/server/controllers/interface.js.html#615" target="_blank">./server/controllers/interface.js:615</a>
<a href="./static/server/controllers/interface.js.html#628" target="_blank">./server/controllers/interface.js:628</a>
</p>

View File

@ -144,14 +144,14 @@
<pre><code>mockd地址 http<span class="token operator">:</span>//yapi.corp.qunar.com/mock/<span class="token number">29</span>/api/hackathon/login
</code></pre><p>项目id可以在项目设置里查看到</p>
<h2 class="subject" id="定义mock数据示例">定义mock数据示例 <a class="hashlink" href="#定义mock数据示例">#</a></h2><pre><code><span class="token punctuation">{</span>
<span class="token property">"status|0-1"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> //接口状态
"status|<span class="token number">0</span>-<span class="token number">1</span>"<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> //接口状态
<span class="token property">"message"</span><span class="token operator">:</span> <span class="token string">"请求完成"</span><span class="token punctuation">,</span> //消息提示
<span class="token property">"data"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"counts"</span><span class="token operator">:</span><span class="token string">"@integer"</span><span class="token punctuation">,</span> //统计数量
<span class="token property">"totalSubjectType|4-10"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
"totalSubjectType|<span class="token number">4</span>-<span class="token number">10</span>"<span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"subjectName|regexp"</span><span class="token operator">:</span> <span class="token string">"大数据|机器学习|工具"</span><span class="token punctuation">,</span> //主题名
<span class="token property">"subjectType|+1"</span><span class="token operator">:</span> <span class="token number">1</span> //类型
"subjectName|regexp"<span class="token operator">:</span> "大数据|机器学习|工具<span class="token string">", //主题名
"</span>subjectType|+<span class="token number">1</span>"<span class="token operator">:</span> <span class="token number">1</span> //类型
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"data"</span><span class="token operator">:</span><span class="token punctuation">[</span>
@ -164,8 +164,8 @@
<span class="token punctuation">}</span>
</code></pre><h2 class="subject" id="yapiMock跟mockjs区别">yapiMock跟mockjs区别 <a class="hashlink" href="#yapiMock跟mockjs区别">#</a></h2><p>因为yapi基于json定义mock无法使用mockjs原有的函数功能正则表达式需要基于rule书写示例如下</p>
<pre><code><span class="token punctuation">{</span>
<span class="token property">"name|regexp"</span><span class="token operator">:</span> <span class="token string">"[a-z0-9_]+?"</span><span class="token punctuation">,</span>
<span class="token property">"type|regexp"</span><span class="token operator">:</span> <span class="token string">"json|text|xml"</span> //枚举数据类型可这样实现
"name|regexp"<span class="token operator">:</span> <span class="token string">"[a-z0-9_]+?"</span><span class="token punctuation">,</span>
"type|regexp"<span class="token operator">:</span> "json|text|xml" //枚举数据类型可这样实现
<span class="token punctuation">}</span>
</code></pre><h2 class="subject" id="如何使用Mock?">如何使用Mock? <a class="hashlink" href="#如何使用Mock?">#</a></h2><h3 class="subject" id="1_在js代码直接请求yapi提供的mock地址不用担心跨域问题">1 在js代码直接请求yapi提供的mock地址不用担心跨域问题 <a class="hashlink" href="#1_在js代码直接请求yapi提供的mock地址不用担心跨域问题">#</a></h3><p>在代码直接请求yapi提供的mock地址以jQuery为例</p>
<pre><code class="lang-javascript"><span class="token keyword">let</span> prefix <span class="token operator">=</span> <span class="token string">'http://yapi.local.qunar.com:3000/mock/2817'</span>
@ -196,9 +196,9 @@ $<span class="token punctuation">.</span><span class="token function">post</span
<p><a href="#DPD">2. 数据占位符定义规范Data Placeholder DefinitionDPD</a></p>
<p><span id = "DTD"></span></p>
<h3 class="subject" id="数据模板定义规范Data_Template_DefinitionDTD">数据模板定义规范Data Template DefinitionDTD <a class="hashlink" href="#数据模板定义规范Data_Template_DefinitionDTD">#</a></h3><p>数据模板中的每个属性由 3 部分构成:属性名、生成规则、属性值:</p>
<pre><code>// 属性名 name (与生成规则之间用 <span class="token string">"|"</span> 隔开)
// 生成规则 rule生成规则有<span class="token number">7</span>种详见下面的生成规则)
// 属性值 value可以含有 <span class="token string">"@占位符"</span> 同时也指定了最终值的初始值和类型)
<pre><code>// 属性名 name (与生成规则之间用 "|<span class="token string">" 隔开)
// 生成规则 rule生成规则有7种详见下面的生成规则
// 属性值 value可以含有 "</span>@占位符" 同时也指定了最终值的初始值和类型)
'name|rule'<span class="token operator">:</span> value

View File

@ -8,7 +8,6 @@ $(document).ready(function() {
var $versionSelector = $('.version-selector');
var $versionMask = $('.m-version-mask');
var isPanelHide = true;
var winWidth = $(window).width();
var h2 = $('.content-right').find('h2');
var h3 = $('.content-right').find('h3');
var a = $('.content-left').find('a');

View File

@ -309,7 +309,20 @@ class groupController extends baseController {
try {
var groupInst = yapi.getInst(groupModel);
let result = await groupInst.list();
ctx.body = yapi.commons.resReturn(result);
let newResult = [];
if(result && result.length > 0){
for(let i=0; i< result.length; i++){
result[i] = result[i].toObject();
result[i].role = await this.checkAuth(result[i]._id, 'group', 'edit');
if(result[i].role){
newResult.unshift(result[i]);
}else{
newResult.push(result[i]);
}
}
}
ctx.body = yapi.commons.resReturn(newResult);
} catch (e) {
ctx.body = yapi.commons.resReturn(null, 402, e.message);
}

View File

@ -103,9 +103,22 @@ class interfaceController extends baseController {
return ctx.body = yapi.commons.resReturn(null, 400, '接口请求路径不能为空');
}
if (!yapi.commons.verifyPath(params.path)) {
let http_path = url.parse(params.path, true);
params.path = http_path.pathname;
if (!yapi.commons.verifyPath(http_path.pathname)) {
return ctx.body = yapi.commons.resReturn(null, 400, '接口path第一位必须是/,最后一位不能为/');
}
if(!params.req_query){
params.req_query = [];
Object.keys(http_path.query).forEach((item)=>{
params.req_query.push({
name: item
})
})
}
let checkRepeat = await this.Model.checkRepeat(params.project_id, params.path, params.method);

87
ykit.js
View File

@ -36,69 +36,6 @@ function initPlugins(){
initPlugins();
function handleCommonsChunk(webpackConfig) {
var commonsChunk = {
vendors: {
lib: ['react', 'redux',
'redux-thunk',
'react-dom',
'redux-promise',
'react-router',
'react-router-dom',
'prop-types',
'axios',
'moment'
],
lib2: [
'brace',
'mockjs',
'json5'
]
}
},
chunks = [],
filenameTpl = webpackConfig.output[this.env],
vendors;
if (typeof commonsChunk === 'object' && commonsChunk !== undefined) {
if (typeof commonsChunk.name === 'string' && commonsChunk) {
chunks.push(commonsChunk.name);
}
vendors = commonsChunk.vendors;
if (typeof vendors === 'object' && vendors !== undefined) {
var i = 0;
for (var name in vendors) {
if (vendors.hasOwnProperty(name) && vendors[name]) {
i++;
chunks.push(name);
webpackConfig.entry[name] = Array.isArray(vendors[name]) ? vendors[name] : [vendors[name]];
}
}
if (i > 0) {
chunks.push('manifest');
}
}
if (chunks.length > 0) {
let chunkFilename = filenameTpl.filename
chunkFilename = chunkFilename.replace("[ext]", '.js')
webpackConfig.plugins.push(
new this.webpack.optimize.CommonsChunkPlugin({
name: chunks,
filename: chunkFilename,
minChunks: commonsChunk.minChunks ? commonsChunk.minChunks : 2
})
);
}
}
}
module.exports = {
plugins: [{
name: 'antd',
@ -122,6 +59,26 @@ module.exports = {
exports: [
'./index.js'
],
commonsChunk: {
vendors: {
lib: ['react', 'redux',
'redux-thunk',
'react-dom',
'redux-promise',
'react-router',
'react-router-dom',
'prop-types',
'axios',
'moment'
],
lib2: [
'brace',
'mockjs',
'json5'
]
}
},
modifyWebpackConfig: function (baseConfig) {
var ENV_PARAMS = {};
@ -152,8 +109,8 @@ module.exports = {
baseConfig.output.prd.publicPath = '';
baseConfig.output.prd.filename = '[name]@[chunkhash][ext]'
//commonsChunk
handleCommonsChunk.call(this, baseConfig)
baseConfig.module.loaders.push({
test: /\.less$/,
loader: ykit.ExtractTextPlugin.extract(