mirror of
https://github.com/YMFE/yapi.git
synced 2024-12-21 05:19:42 +08:00
Merge branch 'dev' into doc
This commit is contained in:
commit
2ac3fa76da
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,3 +1,17 @@
|
||||
### v1.2.5
|
||||
#### Bug Fixed
|
||||
1. 成员如果第一次添加成员时选择组长,接着再添加下一个成员,如果select是默认的开发者,这时候会出现与上次select相同的值
|
||||
2. 如果添加了一个不存在的成员还是会提示添加成功,并且发送的数据是原来发送成功的数据,这里需要重置初始值并在未找到对应用户名时对未找到的人名应该提示用户不存在
|
||||
3. 测试集合中的左侧 Menu 拖动的时候右侧的List需要跟随变动
|
||||
4. 在接口开发阶段,多个人并行改接口,如果最后一个人改之前没刷新页面,会把之前的人修改过的都冲掉了
|
||||
5. 修复cross-request,response header字段重复bug
|
||||
6. Fix 接口集自动化测试 header 没有解析 mock 和 变量参数
|
||||
|
||||
#### Feature
|
||||
1. 优化了分组添加,编辑交互
|
||||
2. cross-request 计算了接口请求时间
|
||||
3. 新增接口文档导出 html, markdown 功能
|
||||
|
||||
### v1.2.4
|
||||
#### Bug Fixed
|
||||
1. 期望值输入时候换成字符串,导致diff时因类型不一致匹配不上
|
||||
|
27
README.md
27
README.md
@ -45,29 +45,16 @@ YApi 是<strong>高效</strong>、<strong>易用</strong>、<strong>功能强大
|
||||
|
||||
密码: ymfe.org
|
||||
|
||||
### YApi 的客户
|
||||
* 去哪儿
|
||||
### YApi 的一些客户
|
||||
* 去哪儿
|
||||
* 携程
|
||||
* 艺龙
|
||||
* 京东
|
||||
* 唯品支付
|
||||
* 链家网
|
||||
* 快手
|
||||
* 中山市鲣鸟网络科技有限公司
|
||||
* 广州大象健康科技有限公司
|
||||
* 杭州数慧科技有限公司
|
||||
* 厦门凌拓通信科技有限公司
|
||||
* 北京星度网络科技有限公司
|
||||
* 广州奥睿智能科技有限公司
|
||||
* 厦门彩虹狗科技有限公司
|
||||
* 企智互联科技
|
||||
* 西安市唐朝商贸有限公司
|
||||
* 华生基因
|
||||
* 维拉假日科技有限公司
|
||||
* 小斑马科技(天津)有限公司
|
||||
* 梅州幻麟网络科技有限公司
|
||||
* 安百科技
|
||||
* 好玩友
|
||||
* Nexusguard
|
||||
* 木木工作室
|
||||
* 汇通天下
|
||||
* 便利蜂
|
||||
|
||||
|
||||
### Authors
|
||||
* [suxiaoxin](https://github.com/suxiaoxin)
|
||||
|
@ -87,10 +87,10 @@ Footer.defaultProps = {
|
||||
linkList: [
|
||||
{
|
||||
itemTitle: '版本: ' + packageJson.version,
|
||||
itemLink: 'https://yapi.ymfe.org/releases.html'
|
||||
itemLink: 'http://yapi.qunar.com/releases.html'
|
||||
}, {
|
||||
itemTitle: '使用文档',
|
||||
itemLink: 'https://yapi.ymfe.org/'
|
||||
itemLink: 'http://yapi.qunar.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ const tipAdd = (<div className="title-container">
|
||||
</div>);
|
||||
const tipDoc = (<div className="title-container">
|
||||
<h3 className="title">使用文档 <Tag color="orange">推荐!</Tag></h3>
|
||||
<p>初次使用 YApi,强烈建议你阅读 <a target="_blank" href="https://yapi.ymfe.org/" rel="noopener noreferrer">使用文档</a>
|
||||
<p>初次使用 YApi,强烈建议你阅读 <a target="_blank" href="http://yapi.qunar.com" rel="noopener noreferrer">使用文档</a>
|
||||
,我们为你提供了通俗易懂的快速入门教程,更有详细的使用说明,欢迎阅读! </p>
|
||||
</div>);
|
||||
|
||||
@ -150,7 +150,7 @@ const ToolUser = (props) => {
|
||||
>
|
||||
<Tooltip placement="bottom" title={'使用文档'}>
|
||||
<li className="toolbar-li">
|
||||
<a target="_blank" href="https://yapi.ymfe.org/" rel="noopener noreferrer"><Icon
|
||||
<a target="_blank" href="http://yapi.qunar.com" rel="noopener noreferrer"><Icon
|
||||
className="dropdown-link" style={{ fontSize: 16 }} type="question-circle" /></a>
|
||||
</li>
|
||||
</Tooltip>
|
||||
|
@ -197,23 +197,27 @@ export default class Run extends Component {
|
||||
return;
|
||||
}
|
||||
const { headers, bodyForm, pathParam, bodyOther, caseEnv, domains, method, pathname, query, bodyType } = this.state;
|
||||
const urlObj = URL.parse(_.find(domains, item => item.name === caseEnv).domain);
|
||||
|
||||
let path = pathname;
|
||||
|
||||
|
||||
pathParam.forEach(item => {
|
||||
path = path.replace(`:${item.name}`, handleMockWord(item.value) || `:${item.name}`);
|
||||
});
|
||||
if (urlObj.pathname) {
|
||||
if (urlObj.pathname[urlObj.pathname.length - 1] !== '/') {
|
||||
urlObj.pathname += '/'
|
||||
const urlObj = URL.parse(URL.resolve(_.find(domains, item => item.name === caseEnv).domain, '.' + path));
|
||||
|
||||
let pathQuery = {};
|
||||
urlObj.query && urlObj.query.split('&').forEach(item=>{
|
||||
if(item){
|
||||
item = item.split('=');
|
||||
pathQuery[item[0]] = item[1];
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const href = URL.format({
|
||||
protocol: urlObj.protocol || 'http',
|
||||
host: urlObj.host,
|
||||
pathname: urlObj.pathname ? URL.resolve(urlObj.pathname, '.' + path) : path,
|
||||
query: this.getQueryObj(query)
|
||||
pathname: urlObj.pathname,
|
||||
query: Object.assign(pathQuery, this.getQueryObj(query))
|
||||
});
|
||||
let reqBody;
|
||||
if(bodyType === 'form'){
|
||||
@ -575,7 +579,16 @@ export default class Run extends Component {
|
||||
let val = handleMockWord(item.value);
|
||||
path = path.replace(`:${item.name}`, val || `:${item.name}`);
|
||||
});
|
||||
const search = decodeURIComponent(URL.format({ query: this.getQueryObj(query) }));
|
||||
const pathObj = URL.parse(path);
|
||||
path = pathObj.pathname;
|
||||
let pathQuery = {};
|
||||
pathObj.query && pathObj.query.split('&').forEach(item=>{
|
||||
if(item){
|
||||
item = item.split('=');
|
||||
pathQuery[item[0]] = item[1];
|
||||
}
|
||||
})
|
||||
const search = decodeURIComponent(URL.format({ query: Object.assign(pathQuery, this.getQueryObj(query)) }));
|
||||
|
||||
let validResView;
|
||||
validResView = validRes.map((item, index) => {
|
||||
|
@ -56,7 +56,7 @@ function timeago(timestamp) {
|
||||
// timeago(new Date().getTime() - 40);
|
||||
|
||||
@connect(
|
||||
|
||||
|
||||
state => {
|
||||
return {
|
||||
newsData: state.news.newsData,
|
||||
@ -93,7 +93,7 @@ class TimeTree extends Component {
|
||||
|
||||
getMore() {
|
||||
const that = this;
|
||||
|
||||
|
||||
if (this.props.curpage <= this.props.newsData.total) {
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
@ -167,7 +167,7 @@ export default {
|
||||
],
|
||||
IP_REGEXP: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/,
|
||||
docHref: {
|
||||
adv_mock_case: 'https://yapi.ymfe.org/adv_mock.html#Mock_期望',
|
||||
adv_mock_script: 'https://yapi.ymfe.org/adv_mock.html#自定义_Mock_脚本'
|
||||
adv_mock_case: 'http://yapi.qunar.com/adv_mock.html#Mock_期望',
|
||||
adv_mock_script: 'http://yapi.qunar.com/adv_mock.html#自定义_Mock_脚本'
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import GroupList from './GroupList/GroupList.js';
|
||||
import ProjectList from './ProjectList/ProjectList.js';
|
||||
import MemberList from './MemberList/MemberList.js';
|
||||
import GroupLog from './GroupLog/GroupLog.js';
|
||||
import GroupSetting from './GroupSetting/GroupSetting.js';
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { Route, Switch, Redirect } from 'react-router-dom';
|
||||
@ -54,6 +55,7 @@ export default class Group extends Component {
|
||||
<TabPane tab="项目列表" key="1"><ProjectList/></TabPane>
|
||||
{this.props.currGroup.type === 'public'?<TabPane tab="成员列表" key="2"><MemberList/></TabPane>:null}
|
||||
{["admin","owner","guest","dev"].indexOf(this.props.curUserRoleInGroup)>-1 || this.props.curUserRole === "admin"?<TabPane tab="分组动态" key="3"><GroupLog/></TabPane>:""}
|
||||
{(this.props.curUserRole === "admin" || this.props.curUserRoleInGroup === 'owner') && this.props.currGroup.type !== 'private' ? <TabPane tab="分组设置" key="4"><GroupSetting/></TabPane> : null}
|
||||
</Tabs>
|
||||
</Content>
|
||||
</Layout>
|
||||
|
@ -1,14 +1,12 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { Icon, Modal, Alert, Input, message, Menu, Row, Col, Dropdown, Popover } from 'antd'
|
||||
import { Icon, Modal, Input, message, Row, Menu, Col, Popover, Tooltip } from 'antd'
|
||||
import { autobind } from 'core-decorators';
|
||||
import axios from 'axios';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
const { TextArea } = Input;
|
||||
const Search = Input.Search;
|
||||
const TYPE_EDIT = 'edit';
|
||||
const confirm = Modal.confirm;
|
||||
import UsernameAutoComplete from '../../../components/UsernameAutoComplete/UsernameAutoComplete.js';
|
||||
import GuideBtns from '../../../components/GuideBtns/GuideBtns.js';
|
||||
import { fetchNewsData } from '../../../reducer/modules/news.js';
|
||||
@ -65,7 +63,6 @@ export default class GroupList extends Component {
|
||||
|
||||
state = {
|
||||
addGroupModalVisible: false,
|
||||
editGroupModalVisible: false,
|
||||
newGroupName: '',
|
||||
newGroupDesc: '',
|
||||
currGroupName: '',
|
||||
@ -100,34 +97,19 @@ export default class GroupList extends Component {
|
||||
}
|
||||
|
||||
@autobind
|
||||
showModal(type) {
|
||||
if (type === 'edit') {
|
||||
const { currGroup } = this.props;
|
||||
this.setState({
|
||||
currGroupName: currGroup.group_name,
|
||||
currGroupDesc: currGroup.group_desc,
|
||||
editGroupModalVisible: true
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
addGroupModalVisible: true
|
||||
});
|
||||
}
|
||||
showModal() {
|
||||
this.setState({
|
||||
addGroupModalVisible: true
|
||||
});
|
||||
}
|
||||
@autobind
|
||||
hideModal(type) {
|
||||
if (type === TYPE_EDIT) {
|
||||
this.setState({
|
||||
editGroupModalVisible: false
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
newGroupName: '',
|
||||
group_name: '',
|
||||
owner_uids: [],
|
||||
addGroupModalVisible: false
|
||||
});
|
||||
}
|
||||
hideModal() {
|
||||
this.setState({
|
||||
newGroupName: '',
|
||||
group_name: '',
|
||||
owner_uids: [],
|
||||
addGroupModalVisible: false
|
||||
});
|
||||
}
|
||||
@autobind
|
||||
async addGroup() {
|
||||
@ -159,9 +141,6 @@ export default class GroupList extends Component {
|
||||
if (res.data.errcode) {
|
||||
message.error(res.data.errmsg);
|
||||
} else {
|
||||
this.setState({
|
||||
editGroupModalVisible: false
|
||||
});
|
||||
await this.props.fetchGroupList();
|
||||
|
||||
this.setState({ groupList: this.props.groupList });
|
||||
@ -174,20 +153,12 @@ export default class GroupList extends Component {
|
||||
}
|
||||
}
|
||||
@autobind
|
||||
inputNewGroupName(e, type) {
|
||||
if (type === TYPE_EDIT) {
|
||||
this.setState({ currGroupName: e.target.value })
|
||||
} else {
|
||||
this.setState({ newGroupName: e.target.value });
|
||||
}
|
||||
inputNewGroupName(e) {
|
||||
this.setState({ newGroupName: e.target.value });
|
||||
}
|
||||
@autobind
|
||||
inputNewGroupDesc(e, type) {
|
||||
if (type === TYPE_EDIT) {
|
||||
this.setState({ currGroupDesc: e.target.value })
|
||||
} else {
|
||||
this.setState({ newGroupDesc: e.target.value });
|
||||
}
|
||||
inputNewGroupDesc(e) {
|
||||
this.setState({ newGroupDesc: e.target.value });
|
||||
}
|
||||
|
||||
@autobind
|
||||
@ -207,50 +178,6 @@ export default class GroupList extends Component {
|
||||
})
|
||||
}
|
||||
|
||||
showConfirm = () => {
|
||||
let that = this;
|
||||
confirm({
|
||||
title: "确认删除 " + that.props.currGroup.group_name + " 分组吗?",
|
||||
content: <div style={{ marginTop: '10px', fontSize: '13px', lineHeight: '25px' }}>
|
||||
<Alert message="警告:此操作非常危险,会删除该分组下面所有项目和接口,并且无法恢复!" type="warning" />
|
||||
<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) {
|
||||
message.error('分组名称有误')
|
||||
return new Promise((resolve, reject) => {
|
||||
reject('error')
|
||||
})
|
||||
} else {
|
||||
that.deleteGroup()
|
||||
}
|
||||
|
||||
},
|
||||
iconType: 'delete',
|
||||
onCancel() { }
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
async deleteGroup() {
|
||||
const self = this;
|
||||
const { currGroup } = self.props;
|
||||
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.props.setCurrGroup(currGroup)
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
searchGroup(e, value) {
|
||||
const v = value || e.target.value;
|
||||
@ -262,40 +189,17 @@ export default class GroupList extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// GroupSetting 组件设置的分组信息,通过redux同步到左侧分组菜单中
|
||||
if (this.props.groupList !== nextProps.groupList) {
|
||||
this.setState({
|
||||
groupList: nextProps.groupList
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { currGroup } = this.props;
|
||||
const delmark = <Menu.Item>
|
||||
<span onClick={() => this.showModal(TYPE_EDIT)}>编辑分组</span>
|
||||
</Menu.Item>
|
||||
const editmark = <Menu.Item>
|
||||
<span onClick={() => { this.showConfirm() }}>删除分组</span>
|
||||
</Menu.Item>
|
||||
const addmark = <Menu.Item>
|
||||
<span onClick={this.showModal}>添加分组</span>
|
||||
</Menu.Item>
|
||||
|
||||
let menu = <Menu>
|
||||
{
|
||||
this.props.curUserRole === "admin" && this.props.currGroup.type !== 'private' ? (editmark) : ''
|
||||
}
|
||||
{
|
||||
(this.props.curUserRole === "admin" || this.props.curUserRoleInGroup === 'owner') && this.props.currGroup.type !== 'private' ? (delmark) : ''
|
||||
}
|
||||
{
|
||||
this.props.curUserRole === 'admin' ? (addmark) : ''
|
||||
}
|
||||
</Menu>;
|
||||
menu = (this.props.curUserRoleInGroup === 'owner' && this.props.curUserRole !== 'admin') ? <a className="editSet"><Icon type="setting" onClick={() => this.showModal(TYPE_EDIT)} /></a> : <Dropdown overlay={menu}>
|
||||
<a className="ant-dropdown-link" href="#">
|
||||
<Icon type="setting" />
|
||||
</a>
|
||||
</Dropdown>;
|
||||
// console.log(this.props.currGroup.type,this.props.curUserRoleInGroup,this.props.curUserRole);
|
||||
// if(!(this.props.currGroup.type !=='private') && !(this.props.curUserRoleInGroup === 'owner' || this.props.curUserRole === 'admin')){
|
||||
// menu = null;
|
||||
// }
|
||||
|
||||
|
||||
return (
|
||||
<div className="m-group">
|
||||
{!this.props.study ? <div className="study-mask"></div> : null}
|
||||
@ -303,7 +207,10 @@ export default class GroupList extends Component {
|
||||
<div className="curr-group">
|
||||
<div className="curr-group-name">
|
||||
<span className="name">{currGroup.group_name}</span>
|
||||
{this.props.curUserRole === "admin" || this.props.curUserRoleInGroup === 'owner' ? (menu) : ''}
|
||||
{/* this.props.curUserRole === "admin" || this.props.curUserRoleInGroup === 'owner' ? (menu) : '' */}
|
||||
{ /* 只有超级管理员能添加分组 */
|
||||
this.props.curUserRole === 'admin' ? <Tooltip title="添加分组"><a className="editSet"><Icon className="btn" type="folder-add" onClick={this.showModal} /></a></Tooltip> : ''
|
||||
}
|
||||
</div>
|
||||
<div className="curr-group-desc">简介: {currGroup.group_desc}</div>
|
||||
</div>
|
||||
@ -370,27 +277,6 @@ export default class GroupList extends Component {
|
||||
</Row>
|
||||
</Modal> : ''
|
||||
}
|
||||
|
||||
<Modal
|
||||
title="编辑分组"
|
||||
visible={this.state.editGroupModalVisible}
|
||||
onOk={this.editGroup}
|
||||
onCancel={() => this.hideModal(TYPE_EDIT)}
|
||||
className="add-group-modal"
|
||||
>
|
||||
<Row gutter={6} className="modal-input">
|
||||
<Col span="5"><div className="label">分组名:</div></Col>
|
||||
<Col span="15">
|
||||
<Input placeholder="请输入分组名称" value={this.state.currGroupName} onChange={(e) => this.inputNewGroupName(e, TYPE_EDIT)}></Input>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={6} className="modal-input">
|
||||
<Col span="5"><div className="label">简介:</div></Col>
|
||||
<Col span="15">
|
||||
<TextArea rows={3} placeholder="请输入分组描述" value={this.state.currGroupDesc} onChange={(e) => this.inputNewGroupDesc(e, TYPE_EDIT)}></TextArea>
|
||||
</Col>
|
||||
</Row>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
200
client/containers/Group/GroupSetting/GroupSetting.js
Normal file
200
client/containers/Group/GroupSetting/GroupSetting.js
Normal file
@ -0,0 +1,200 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { autobind } from 'core-decorators';
|
||||
import { Input, Button, message, Icon, Card, Alert, Modal } from 'antd';
|
||||
import { fetchNewsData } from '../../../reducer/modules/news.js';
|
||||
import { changeGroupMsg, fetchGroupList, setCurrGroup, fetchGroupMsg, updateGroupList, deleteGroup } from '../../../reducer/modules/group.js';
|
||||
const { TextArea } = Input;
|
||||
import _ from 'underscore';
|
||||
import './GroupSetting.scss';
|
||||
const confirm = Modal.confirm;
|
||||
|
||||
@connect(
|
||||
state => {
|
||||
return {
|
||||
groupList: state.group.groupList,
|
||||
currGroup: state.group.currGroup,
|
||||
curUserRole: state.user.role
|
||||
}
|
||||
},
|
||||
{
|
||||
changeGroupMsg,
|
||||
fetchGroupList,
|
||||
setCurrGroup,
|
||||
fetchGroupMsg,
|
||||
fetchNewsData,
|
||||
updateGroupList,
|
||||
deleteGroup
|
||||
}
|
||||
)
|
||||
class GroupLog extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
currGroupDesc: '',
|
||||
currGroupName: '',
|
||||
showDangerOptions: false
|
||||
}
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
currGroup: PropTypes.object,
|
||||
curUserRole: PropTypes.string,
|
||||
changeGroupMsg: PropTypes.func,
|
||||
fetchGroupList: PropTypes.func,
|
||||
setCurrGroup: PropTypes.func,
|
||||
fetchGroupMsg: PropTypes.func,
|
||||
fetchNewsData: PropTypes.func,
|
||||
updateGroupList: PropTypes.func,
|
||||
deleteGroup: PropTypes.func,
|
||||
groupList: PropTypes.array
|
||||
}
|
||||
|
||||
// 修改分组名称
|
||||
changeName = (e) => {
|
||||
this.setState({
|
||||
currGroupName: e.target.value
|
||||
})
|
||||
}
|
||||
// 修改分组描述
|
||||
changeDesc = (e) => {
|
||||
this.setState({
|
||||
currGroupDesc: e.target.value
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
currGroupName: this.props.currGroup.group_name,
|
||||
currGroupDesc: this.props.currGroup.group_desc
|
||||
})
|
||||
}
|
||||
|
||||
// 点击“查看危险操作”按钮
|
||||
toggleDangerOptions = () => {
|
||||
// console.log(this.state.showDangerOptions);
|
||||
this.setState({
|
||||
showDangerOptions: !this.state.showDangerOptions
|
||||
});
|
||||
}
|
||||
|
||||
// 编辑分组信息
|
||||
@autobind
|
||||
async editGroup() {
|
||||
const id = this.props.currGroup._id;
|
||||
const res = await this.props.changeGroupMsg({
|
||||
group_name: this.state.currGroupName,
|
||||
group_desc: this.state.currGroupDesc,
|
||||
id: this.props.currGroup._id
|
||||
});
|
||||
if (!res.payload.data.errcode) {
|
||||
message.success('修改成功!');
|
||||
await this.props.fetchGroupList(this.props.groupList);
|
||||
this.props.updateGroupList(this.props.groupList)
|
||||
const currGroup = _.find(this.props.groupList, (group) => { return + group._id === + id });
|
||||
|
||||
this.props.setCurrGroup(currGroup);
|
||||
this.props.fetchGroupMsg(this.props.currGroup._id);
|
||||
this.props.fetchNewsData(this.props.currGroup._id, "group", 1, 10)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除分组
|
||||
@autobind
|
||||
async deleteGroup() {
|
||||
const that = this;
|
||||
const { currGroup } = that.props;
|
||||
const res = await this.props.deleteGroup({ id: currGroup._id });
|
||||
if (!res.payload.data.errcode) {
|
||||
message.success('删除成功')
|
||||
await that.props.fetchGroupList();
|
||||
const currGroup = that.props.groupList[0] || { group_name: '', group_desc: '' };
|
||||
that.setState({ groupList: that.props.groupList });
|
||||
that.props.setCurrGroup(currGroup)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除分组的二次确认
|
||||
showConfirm = () => {
|
||||
const that = this;
|
||||
confirm({
|
||||
title: "确认删除 " + that.props.currGroup.group_name + " 分组吗?",
|
||||
content: <div style={{ marginTop: '10px', fontSize: '13px', lineHeight: '25px' }}>
|
||||
<Alert message="警告:此操作非常危险,会删除该分组下面所有项目和接口,并且无法恢复!" type="warning" />
|
||||
<div style={{ marginTop: '16px' }}>
|
||||
<p><b>请输入分组名称确认此操作:</b></p>
|
||||
<Input id="group_name" />
|
||||
</div>
|
||||
</div>,
|
||||
onOk() {
|
||||
const groupName = document.getElementById('group_name').value;
|
||||
if (that.props.currGroup.group_name !== groupName) {
|
||||
message.error('分组名称有误')
|
||||
return new Promise((resolve, reject) => {
|
||||
reject('error')
|
||||
})
|
||||
} else {
|
||||
that.deleteGroup()
|
||||
}
|
||||
|
||||
},
|
||||
iconType: 'delete',
|
||||
onCancel() { }
|
||||
});
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// 切换分组时,更新分组信息并关闭删除分组操作
|
||||
if (this.props.currGroup._id !== nextProps.currGroup._id) {
|
||||
this.setState({
|
||||
showDangerOptions: false,
|
||||
currGroupName: nextProps.currGroup.group_name,
|
||||
currGroupDesc: nextProps.currGroup.group_desc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className="m-panel card-panel card-panel-s panel-group">
|
||||
<div>
|
||||
<div className="row">
|
||||
<div className="left"><div className="label">分组名:</div></div>
|
||||
<div className="right">
|
||||
<Input size="large" placeholder="请输入分组名称" value={this.state.currGroupName} onChange={this.changeName}></Input>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="left"><div className="label">简介:</div></div>
|
||||
<div className="right">
|
||||
<TextArea size="large" rows={3} placeholder="请输入分组描述" value={this.state.currGroupDesc} onChange={this.changeDesc}></TextArea>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="left"></div>
|
||||
<div className="right"><Button type="primary" onClick={this.editGroup}>保存</Button></div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 只有超级管理员能删除分组 */}
|
||||
{this.props.curUserRole === "admin" ?
|
||||
<div className="danger-container">
|
||||
<div className="title">
|
||||
<h2 className="content"><Icon type="exclamation-circle-o" /> 危险操作</h2>
|
||||
<Button onClick={this.toggleDangerOptions}>查 看<Icon type={this.state.showDangerOptions ? 'up' : 'down'} /></Button>
|
||||
</div>
|
||||
{this.state.showDangerOptions ? <Card noHovering={true} className="card-danger">
|
||||
<div className="card-danger-content">
|
||||
<h3>删除分组</h3>
|
||||
<p>分组一旦删除,将无法恢复数据,请慎重操作!</p>
|
||||
<p>只有超级管理员有权限删除分组。</p>
|
||||
</div>
|
||||
<Button type="danger" ghost className="card-danger-btn" onClick={this.showConfirm}>删除</Button>
|
||||
</Card> : null}
|
||||
</div> : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default GroupLog;
|
14
client/containers/Group/GroupSetting/GroupSetting.scss
Normal file
14
client/containers/Group/GroupSetting/GroupSetting.scss
Normal file
@ -0,0 +1,14 @@
|
||||
.panel-group {
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: .16rem;
|
||||
}
|
||||
.left {
|
||||
flex: 100px 0 1;
|
||||
text-align: right;
|
||||
}
|
||||
.right {
|
||||
flex: 830px 0 1;
|
||||
}
|
||||
}
|
@ -122,9 +122,9 @@ class ProjectList extends Component {
|
||||
<div style={{ paddingTop: '24px' }} 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} 个项目
|
||||
{this.props.currGroup.group_name} 分组共 ({projectData.length}) 个项目
|
||||
</Col>
|
||||
<Col>
|
||||
<Col span={8}>
|
||||
{/(admin)|(owner)|(dev)/.test(this.props.currGroup.role) ?
|
||||
<Link to="/add-project"><Button type="primary">添加项目</Button></Link> :
|
||||
<Tooltip title="您没有权限,请联系该分组组长或管理员">
|
||||
|
@ -18,9 +18,13 @@
|
||||
line-height: 40px;
|
||||
border-radius: 4px;
|
||||
text-align: right;
|
||||
padding: 13px 15px;
|
||||
padding: 0 10px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: rgba(39, 56, 72, 0.85);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ const HomeGuest = () => (
|
||||
<Col span={24}>
|
||||
<div className="home-header">
|
||||
<a href="#" className="item">YAPI</a>
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://yapi.ymfe.org/" className="item">使用文档</a>
|
||||
<a target="_blank" rel="noopener noreferrer" href="http://yapi.qunar.com" className="item">使用文档</a>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
@ -210,7 +210,7 @@ class Home extends Component {
|
||||
<div className="tip-btns">
|
||||
<div className="btn-group">
|
||||
<Link to="/login"><Button type="primary" className="btn-home btn-login">登录 / 注册</Button></Link>
|
||||
<Button className="btn-home btn-home-normal"><a target="_blank" rel="noopener noreferrer" href="https://yapi.ymfe.org/" >使用文档</a></Button>
|
||||
<Button className="btn-home btn-home-normal"><a target="_blank" rel="noopener noreferrer" href="http://yapi.qunar.com" >使用文档</a></Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -14,17 +14,28 @@
|
||||
color: #6b6c6d;
|
||||
.ant-timeline-item{
|
||||
min-height: 60px;
|
||||
.ant-timeline-item-head-custom {
|
||||
padding: 0;
|
||||
width: 0;
|
||||
}
|
||||
.ant-timeline-item-head{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
left: -13px;
|
||||
top:8px;
|
||||
// border-color:#e1e3e4;
|
||||
border:2px solid #e1e3e4;
|
||||
border-radius: 50%;
|
||||
// width: 40px;
|
||||
// height: 40px;
|
||||
// left: -13px;
|
||||
top: 13px;
|
||||
// // border-color:#e1e3e4;
|
||||
// border:2px solid #e1e3e4;
|
||||
// border-radius: 50%;
|
||||
}
|
||||
.ant-timeline-item-tail{
|
||||
top: 30px;
|
||||
// top: 30px;
|
||||
}
|
||||
.ant-avatar {
|
||||
border: 2px solid #e1e3e4;
|
||||
box-sizing: content-box;
|
||||
border-radius: 50%;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
.ant-avatar{
|
||||
|
@ -193,6 +193,7 @@ class InterfaceColContent extends Component {
|
||||
}
|
||||
|
||||
handleTest = async (interfaceData) => {
|
||||
console.log(1)
|
||||
const { currProject } = this.props;
|
||||
let requestParams = {};
|
||||
let { case_env } = interfaceData;
|
||||
@ -207,22 +208,26 @@ class InterfaceColContent extends Component {
|
||||
|
||||
case_env = this.state.currColEnv ? this.state.currColEnv : case_env;
|
||||
|
||||
let pathQuery = {};
|
||||
let currDomain = _.find(domains, item => item.name === case_env);
|
||||
|
||||
if (!currDomain) {
|
||||
currDomain = domains[0];
|
||||
}
|
||||
const urlObj = URL.parse(currDomain.domain);
|
||||
if (urlObj.pathname) {
|
||||
if (urlObj.pathname[urlObj.pathname.length - 1] !== '/') {
|
||||
urlObj.pathname += '/'
|
||||
|
||||
const urlObj = URL.parse(URL.resolve(currDomain.domain, '.' + path));
|
||||
urlObj.query && urlObj.query.split('&').forEach(item=>{
|
||||
if(item){
|
||||
item = item.split('=');
|
||||
pathQuery[item[0]] = item[1];
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const href = URL.format({
|
||||
protocol: urlObj.protocol || 'http',
|
||||
host: urlObj.host,
|
||||
pathname: urlObj.pathname ? URL.resolve(urlObj.pathname, '.' + path) : path,
|
||||
query: this.getQueryObj(interfaceData.req_query, requestParams)
|
||||
pathname: urlObj.pathname,
|
||||
query: Object.assign(pathQuery, this.getQueryObj(interfaceData.req_query, requestParams))
|
||||
});
|
||||
|
||||
let result = { code: 400, msg: '数据异常', validRes: [] };
|
||||
@ -543,7 +548,7 @@ class InterfaceColContent extends Component {
|
||||
header: {
|
||||
label: 'key',
|
||||
formatters: [() => {
|
||||
return <Tooltip title={<span>每个用例都有唯一的key,用于获取所匹配接口的响应数据,例如使用 <a href="https://yapi.ymfe.org/case.html#变量参数" className="link-tooltip" target="blank">变量参数</a> 功能</span>}>
|
||||
return <Tooltip title={<span>每个用例都有唯一的key,用于获取所匹配接口的响应数据,例如使用 <a href="http://yapi.qunar.com/case.html#变量参数" className="link-tooltip" target="blank">变量参数</a> 功能</span>}>
|
||||
Key</Tooltip>
|
||||
}]
|
||||
},
|
||||
@ -649,7 +654,7 @@ class InterfaceColContent extends Component {
|
||||
let colEnv = this.props.currProject.env || [];
|
||||
return (
|
||||
<div className="interface-col">
|
||||
<h2 className="interface-title" style={{ display: 'inline-block', margin: "0 20px", marginBottom: '16px' }}>测试集合 <a target="_blank" rel="noopener noreferrer" href="https://yapi.ymfe.org/case.html" >
|
||||
<h2 className="interface-title" style={{ display: 'inline-block', margin: "0 20px", marginBottom: '16px' }}>测试集合 <a target="_blank" rel="noopener noreferrer" href="http://yapi.qunar.com/case.html" >
|
||||
<Tooltip title="点击查看文档"><Icon type="question-circle-o" /></Tooltip>
|
||||
</a></h2>
|
||||
<div style={{ display: 'inline-block', margin: 0, marginBottom: '16px' }}>
|
||||
|
@ -769,7 +769,7 @@ class InterfaceEditForm extends Component {
|
||||
|
||||
</Tabs>
|
||||
<div>
|
||||
<h3 style={{ padding: '10px 0' }}>基于mockjs和json5,可直接写mock模板和注释,具体使用方法请 <span className="href" onClick={() => window.open('https://yapi.ymfe.org/mock.html', '_blank')}>查看文档</span></h3>
|
||||
<h3 style={{ padding: '10px 0' }}>基于mockjs和json5,可直接写mock模板和注释,具体使用方法请 <span className="href" onClick={() => window.open('http://yapi.qunar.com/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>
|
||||
|
@ -19,7 +19,6 @@ const exportDataModule = {};
|
||||
// }
|
||||
@connect(
|
||||
state => {
|
||||
// console.log(state);
|
||||
return {
|
||||
curCatid: -(-state.inter.curdata.catid),
|
||||
basePath: state.project.currProject.basepath
|
||||
@ -189,7 +188,7 @@ class ProjectData extends Component {
|
||||
<div className="m-panel">
|
||||
<div className="postman-dataImport">
|
||||
<div className="dataImportCon">
|
||||
<div ><h3>数据导入 <a target="_blank" rel="noopener noreferrer" href="https://yapi.ymfe.org/data.html" >
|
||||
<div ><h3>数据导入 <a target="_blank" rel="noopener noreferrer" href="http://yapi.qunar.com/data.html" >
|
||||
<Tooltip title="点击查看文档"><Icon type="question-circle-o" /></Tooltip>
|
||||
</a></h3></div>
|
||||
<div className="dataImportTile">
|
||||
|
@ -8,11 +8,17 @@ const FETCH_GROUP_MSG = 'yapi/group/FETCH_GROUP_MSG';
|
||||
const ADD_GROUP_MEMBER = 'yapi/group/ADD_GROUP_MEMBER';
|
||||
const DEL_GROUP_MEMBER = 'yapi/group/DEL_GROUP_MEMBER';
|
||||
const CHANGE_GROUP_MEMBER = 'yapi/group/CHANGE_GROUP_MEMBER';
|
||||
const CHANGE_GROUP_MESSAGE = 'yapi/group/CHANGE_GROUP_MESSAGE';
|
||||
const UPDATE_GROUP_LIST = 'yapi/group/UPDATE_GROUP_LIST';
|
||||
const DEL_GROUP = 'yapi/group/DEL_GROUP';
|
||||
|
||||
// Reducer
|
||||
const initialState = {
|
||||
groupList: [],
|
||||
currGroup: { group_name: '' },
|
||||
currGroup: {
|
||||
group_name: '',
|
||||
group_desc: ''
|
||||
},
|
||||
member: [],
|
||||
role: ''
|
||||
};
|
||||
@ -25,6 +31,12 @@ export default (state = initialState, action) => {
|
||||
groupList: action.payload.data.data
|
||||
};
|
||||
}
|
||||
case UPDATE_GROUP_LIST: {
|
||||
return {
|
||||
...state,
|
||||
groupList: action.payload
|
||||
}
|
||||
}
|
||||
case SET_CURR_GROUP: {
|
||||
return {
|
||||
...state,
|
||||
@ -83,6 +95,30 @@ export function changeMemberRole(param) {
|
||||
}
|
||||
}
|
||||
|
||||
// 修改分组信息
|
||||
export function changeGroupMsg(param) {
|
||||
return {
|
||||
type: CHANGE_GROUP_MESSAGE,
|
||||
payload: axios.post('/api/group/up', param)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新左侧的分组列表
|
||||
export function updateGroupList(param) {
|
||||
return {
|
||||
type: UPDATE_GROUP_LIST,
|
||||
payload: param
|
||||
}
|
||||
}
|
||||
|
||||
// 删除分组
|
||||
export function deleteGroup(param) {
|
||||
return {
|
||||
type: DEL_GROUP,
|
||||
payload: axios.post('/api/group/del', param)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取分组成员列表
|
||||
export function fetchGroupMemberList(id) {
|
||||
return {
|
||||
|
@ -9,5 +9,7 @@ module.exports = {
|
||||
name: 'import-swagger'
|
||||
},{
|
||||
name: 'statistics'
|
||||
},{
|
||||
name: 'export-data'
|
||||
}]
|
||||
}
|
@ -66,6 +66,10 @@ Your browser does not support the video tag.
|
||||
|
||||
http 状态码
|
||||
|
||||
* params
|
||||
|
||||
http request params, 合并了 query 和 body
|
||||
|
||||
* body
|
||||
|
||||
返回 response body
|
||||
@ -76,7 +80,7 @@ Your browser does not support the video tag.
|
||||
|
||||
* records
|
||||
|
||||
记录的 http 请求信息,假设需要获取 key为555的接口参数或者响应数据,可通过 records[555].params 或 records[555].body 获取
|
||||
记录的 http 请求信息,假设需要获取 key 为 555 的接口参数或者响应数据,可通过 records[555].params 或 records[555].body 获取
|
||||
|
||||
### 示例
|
||||
```
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
可按 Ctrl + c 中断部署操作,然后删除之前部署的文件,重新部署。
|
||||
|
||||
如果还是不行,请参考 文档 -> 内网部署 -> <a href="https://yapi.ymfe.org/devops.html#方式二. 命令行部署" >命令行部署</a> 文档
|
||||
如果还是不行,请参考 文档 -> 内网部署 -> <a href="./devops.html#方式二. 命令行部署" >命令行部署</a> 文档
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
Binary file not shown.
27
exts/yapi-plugin-export-data/client.js
Normal file
27
exts/yapi-plugin-export-data/client.js
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
// import {message} from 'antd'
|
||||
|
||||
|
||||
function exportData(exportDataModule,pid){
|
||||
exportDataModule.html = {
|
||||
name: 'html',
|
||||
route: `/api/plugin/export?type=html&pid=${pid}`,
|
||||
desc: '导出项目接口文档为 html 文件'
|
||||
}
|
||||
exportDataModule.markdown = {
|
||||
name: 'markdown',
|
||||
route: `/api/plugin/export?type=markdown&pid=${pid}`,
|
||||
desc: '导出项目接口文档为 markdown 文件'
|
||||
}
|
||||
// exportDataModule.pdf = {
|
||||
// name: 'pdf',
|
||||
// route: `/api/plugin/export?type=pdf&pid=${pid}`,
|
||||
// desc: '导出项目接口文档为 pdf 文件'
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = function(){
|
||||
this.bindHook('export_data', exportData)
|
||||
}
|
237
exts/yapi-plugin-export-data/controller.js
Normal file
237
exts/yapi-plugin-export-data/controller.js
Normal file
@ -0,0 +1,237 @@
|
||||
const baseController = require('controllers/base.js');
|
||||
const interfaceModel = require('models/interface.js');
|
||||
const projectModel = require('models/project.js');
|
||||
const yapi = require('yapi.js');
|
||||
const markdownIt = require("markdown-it");
|
||||
const markdownItAnchor = require("markdown-it-anchor");
|
||||
const markdownItTableOfContents = require("markdown-it-table-of-contents");
|
||||
const defaultTheme = require("./defaultTheme.js");
|
||||
|
||||
let isMarkdown = false;
|
||||
// const htmlToPdf = require("html-pdf");
|
||||
class exportController extends baseController{
|
||||
constructor(ctx){
|
||||
super(ctx);
|
||||
this.interModel = yapi.getInst(interfaceModel);
|
||||
this.projectModel = yapi.getInst(projectModel);
|
||||
}
|
||||
|
||||
async exportData(ctx){
|
||||
let pid = ctx.request.query.pid;
|
||||
let type = ctx.request.query.type;
|
||||
if(!pid){
|
||||
ctx.body = yapi.commons.resReturn(null, 200, "pid 不为空");
|
||||
}
|
||||
let curProject;
|
||||
let tp = "";
|
||||
try {
|
||||
curProject = await this.projectModel.get(pid);
|
||||
ctx.set("Content-Type", "application/octet-stream");
|
||||
|
||||
switch(type){
|
||||
case "markdown": {
|
||||
isMarkdown = true;
|
||||
tp = await createMarkdown.bind(this)(pid,false);
|
||||
ctx.set("Content-Disposition",`attachment; filename=api.md`);
|
||||
return ctx.body = tp;
|
||||
}
|
||||
// case "pdf": {
|
||||
// tp = await createPdf.bind(this)(pid,false);
|
||||
// // ctx.set("Content-Disposition",'filename="api.pdf"');
|
||||
// return ctx.body = tp;
|
||||
// }
|
||||
default: {//默认为html
|
||||
tp = await createHtml.bind(this)(pid);
|
||||
ctx.set("Content-Disposition",`attachment; filename=api.html`);
|
||||
return ctx.body = tp;
|
||||
}
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
yapi.commons.log(error, 'error');
|
||||
ctx.body = yapi.commons.resReturn(null, 502, "下载出错");
|
||||
}
|
||||
|
||||
// async function createPdf(){
|
||||
// let md = await createMarkdown.bind(this)(pid);
|
||||
// let markdown = new markdownIt();
|
||||
// markdown.use(markdownItAnchor); // Optional, but makes sense as you really want to link to something
|
||||
// markdown.use(markdownItTableOfContents,{
|
||||
// markerPattern: /^\[toc\]/im
|
||||
// });
|
||||
// let tp = defaultTheme + unescape(markdown.render(md));
|
||||
// tp = createHtml5(tp);
|
||||
|
||||
// let htp = htmlToPdf.create(tp);
|
||||
|
||||
// let getPdfBuffer = ()=>{
|
||||
// return new Promise((resolve, reject)=>{
|
||||
// htp.toBuffer(function(err, buffer){
|
||||
// if(err) reject(err);
|
||||
// resolve(buffer)
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// let result = await getPdfBuffer();
|
||||
// return result;
|
||||
// }
|
||||
|
||||
async function createHtml(pid){
|
||||
let md = await createMarkdown.bind(this)(pid,true);
|
||||
let markdown = new markdownIt();
|
||||
markdown.use(markdownItAnchor); // Optional, but makes sense as you really want to link to something
|
||||
markdown.use(markdownItTableOfContents,{
|
||||
markerPattern: /^\[toc\]/im
|
||||
});
|
||||
|
||||
let tp = unescape(markdown.render(md));
|
||||
let left;
|
||||
|
||||
let content = tp.replace(/<div\s+?class="table-of-contents"\s*>[\s\S]*?<\/ul>\s*<\/div>/gi, function(match){
|
||||
left = match;
|
||||
return '';
|
||||
});
|
||||
return createHtml5(left, content);
|
||||
}
|
||||
|
||||
function escapeStr(str){
|
||||
return !isMarkdown ? escape(str) : str;
|
||||
}
|
||||
|
||||
function createHtml5(left, tp){
|
||||
//html5模板
|
||||
let html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>${curProject.name}</title>
|
||||
${defaultTheme}
|
||||
</head>
|
||||
<body>
|
||||
<div class="m-header">
|
||||
<a href="#" style="display: inherit;"><svg class="svg" width="32px" height="32px" viewBox="0 0 64 64" version="1.1"><title>Icon</title><desc>Created with Sketch.</desc><defs><linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1"><stop stop-color="#FFFFFF" offset="0%"></stop><stop stop-color="#F2F2F2" offset="100%"></stop></linearGradient><circle id="path-2" cx="31.9988602" cy="31.9988602" r="2.92886048"></circle><filter x="-85.4%" y="-68.3%" width="270.7%" height="270.7%" filterUnits="objectBoundingBox" id="filter-3"><feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset><feGaussianBlur stdDeviation="1.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.159703351 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix></filter></defs><g id="首页" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="大屏幕"><g id="Icon"><circle id="Oval-1" fill="url(#linearGradient-1)" cx="32" cy="32" r="32"></circle><path d="M36.7078009,31.8054514 L36.7078009,51.7110548 C36.7078009,54.2844537 34.6258634,56.3695395 32.0579205,56.3695395 C29.4899777,56.3695395 27.4099998,54.0704461 27.4099998,51.7941246 L27.4099998,31.8061972 C27.4099998,29.528395 29.4909575,27.218453 32.0589004,27.230043 C34.6268432,27.241633 36.7078009,29.528395 36.7078009,31.8054514 Z" id="blue" fill="#2359F1" fill-rule="nonzero"></path><path d="M45.2586091,17.1026914 C45.2586091,17.1026914 45.5657231,34.0524383 45.2345291,37.01141 C44.9033351,39.9703817 43.1767091,41.6667796 40.6088126,41.6667796 C38.040916,41.6667796 35.9609757,39.3676862 35.9609757,37.0913646 L35.9609757,17.1034372 C35.9609757,14.825635 38.0418959,12.515693 40.6097924,12.527283 C43.177689,12.538873 45.2586091,14.825635 45.2586091,17.1026914 Z" id="green" fill="#57CF27" fill-rule="nonzero" transform="translate(40.674608, 27.097010) rotate(60.000000) translate(-40.674608, -27.097010) "></path><path d="M28.0410158,17.0465598 L28.0410158,36.9521632 C28.0410158,39.525562 25.9591158,41.6106479 23.3912193,41.6106479 C20.8233227,41.6106479 18.7433824,39.3115545 18.7433824,37.035233 L18.7433824,17.0473055 C18.7433824,14.7695034 20.8243026,12.4595614 23.3921991,12.4711513 C25.9600956,12.4827413 28.0410158,14.7695034 28.0410158,17.0465598 Z" id="red" fill="#FF561B" fill-rule="nonzero" transform="translate(23.392199, 27.040878) rotate(-60.000000) translate(-23.392199, -27.040878) "></path><g id="inner-round"><use fill="black" fill-opacity="1" filter="url(#filter-3)" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#path-2"></use><use fill="#F7F7F7" fill-rule="evenodd" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#path-2"></use></g></g></g></g></svg></a>
|
||||
<a href="#"><h1 class="title">YAPI 接口文档</h1></a>
|
||||
<div class="nav">
|
||||
<a href="http://yapi.qunar.com/">YApi</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="g-doc">
|
||||
${left}
|
||||
<div id="right" class="content-right">
|
||||
${tp}
|
||||
<footer class="m-footer">
|
||||
<p>Build by <a href="https://ymfe.org/">YMFE</a>.</p>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
return html;
|
||||
}
|
||||
function createBaseMessage(inter){
|
||||
// 基本信息
|
||||
let baseMessage = `### 基本信息\n\n**Path:** ${curProject.basepath + inter.path}\n\n**Method:** ${inter.method}\n\n**接口描述:**\n${inter.desc?escapeStr(inter.desc):""}\n`;
|
||||
return baseMessage;
|
||||
}
|
||||
function replaceBr(str){
|
||||
return str.replace("\n",escapeStr("<br/>"));
|
||||
}
|
||||
function createReqHeaders(req_headers){
|
||||
// Request-headers
|
||||
if(req_headers&&req_headers.length){
|
||||
let headersTable = `**Headers**\n\n`;
|
||||
headersTable += `| 参数名称 | 参数值 | 是否必须 | 示例 | 备注 |\n| ------------ | ------------ | ------------ | ------------ | ------------ |\n`;
|
||||
for(let j = 0;j<req_headers.length;j++){
|
||||
headersTable += `| ${replaceBr(req_headers[j].name||"")||""} | ${replaceBr(req_headers[j].value||"")||""} | ${req_headers[j].required?"是":"否"} | ${replaceBr(req_headers[j].example||"")||""} | ${replaceBr(req_headers[j].desc||"")||""} |\n`;
|
||||
}
|
||||
return headersTable;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function createPathParams(req_params){
|
||||
if(req_params&&req_params.length){
|
||||
let paramsTable = `**路径参数**\n`;
|
||||
paramsTable += `| 参数名称 | 示例 | 备注 |\n| ------------ | ------------ | ------------ | ------------ | ------------ |\n`;
|
||||
for(let j = 0;j<req_params.length;j++){
|
||||
paramsTable += `| ${replaceBr(req_params[j].name||"") || ""} | ${replaceBr(req_params[j].example||"") || ""} | ${replaceBr(req_params[j].desc||"")||""} |\n`;
|
||||
}
|
||||
return paramsTable;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function createReqQuery(req_query){
|
||||
if(req_query&&req_query.length){
|
||||
let headersTable = `**Query**\n\n`;
|
||||
headersTable += `| 参数名称 | 是否必须 | 示例 | 备注 |\n| ------------ | ------------ | ------------ | ------------ |\n`;
|
||||
for(let j = 0;j<req_query.length;j++){
|
||||
headersTable += `| ${replaceBr(req_query[j].name||"") || ""} | ${req_query[j].required?"是":"否"} | ${replaceBr(req_query[j].example||"") || ""} | ${replaceBr(req_query[j].desc||"")||""} |\n`;
|
||||
}
|
||||
return headersTable;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function createReqBody(req_body_type,req_body_form,req_body_other){
|
||||
if(req_body_type === "form" && req_body_form.length){
|
||||
let bodyTable = `**Body**\n\n`
|
||||
bodyTable += `| 参数名称 | 参数类型 | 是否必须 | 示例 | 备注 |\n| ------------ | ------------ | ------------ | ------------ | ------------ |\n`; let req_body = req_body_form;
|
||||
for(let j = 0;j<req_body.length;j++){
|
||||
bodyTable += `| ${replaceBr(req_body[j].name||"") || ""} | ${req_body[j].type || ""} | ${req_body[j].required?"是":"否"} | ${replaceBr(req_body[j].example||"")||""} | ${replaceBr(req_body[j].desc||"")||""} |\n`;
|
||||
}
|
||||
return `${bodyTable}\n\n`;
|
||||
}else if(req_body_other){//other
|
||||
return `**Body**\n\n`+"```javascript"+`\n${req_body_other || ""}`+"\n```";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function createResponse(res_body){
|
||||
let resTitle = `\n### Reponse\n\n`;
|
||||
if(res_body){
|
||||
let resBody = "```javascript"+`\n${res_body || ""}\n`+"```";
|
||||
return resTitle + resBody;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
async function createMarkdown(pid,isToc){//拼接markdown
|
||||
//模板
|
||||
let mdTemplate = ``;
|
||||
const toc = `[TOC]\n\n`;
|
||||
try{
|
||||
const interList = await this.interModel.listByPid(pid);
|
||||
// 项目名、项目描述
|
||||
let title = escapeStr('<h1>' + curProject.name + '</h1>');
|
||||
mdTemplate += `\n ${title} \n ${curProject.desc||""}\n${escapeStr("<br>")}\n`
|
||||
for(let i = 0;i<interList.length;i++){//循环拼接 接口
|
||||
// 接口名称
|
||||
mdTemplate += `\n## ${escapeStr(`${interList[i].title}\n<a id=${interList[i].title}> </a>`)}\n`;
|
||||
isToc && (mdTemplate += toc)
|
||||
// 基本信息
|
||||
mdTemplate += createBaseMessage(interList[i]);
|
||||
// Request
|
||||
mdTemplate += `\n### Request\n`;
|
||||
// Request-headers
|
||||
mdTemplate += createReqHeaders(interList[i].req_headers);
|
||||
// Request-params
|
||||
mdTemplate += createPathParams(interList[i].req_params);
|
||||
// Request-query
|
||||
mdTemplate += createReqQuery(interList[i].req_query);
|
||||
// Request-body
|
||||
mdTemplate += createReqBody(interList[i].req_body_type,interList[i].req_body_form,interList[i].req_body_other);
|
||||
// Response
|
||||
// Response-body
|
||||
mdTemplate += createResponse(interList[i].res_body);
|
||||
}
|
||||
return mdTemplate;
|
||||
}catch(e){
|
||||
yapi.commons.log(e, 'error');
|
||||
ctx.body = yapi.commons.resReturn(null, 502, "下载出错");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exportController;
|
340
exts/yapi-plugin-export-data/defaultTheme.css
Normal file
340
exts/yapi-plugin-export-data/defaultTheme.css
Normal file
@ -0,0 +1,340 @@
|
||||
@charset "UTF-8";
|
||||
html,
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
blockquote {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* 设置滚动条的样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
/* 外层轨道 */
|
||||
::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset006pxrgba(255, 0, 0, 0.3);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 滚动条滑块 */
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:window-inactive {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", SimSun, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 25px;
|
||||
color: #393838;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 10px 0 15px 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #ddd;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
a, a:link, a:visited {
|
||||
color: #34495e;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover, a:focus {
|
||||
color: #59d69d;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
p {
|
||||
padding-left: 10px;
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: #404040;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #2c3e50;
|
||||
font-weight: 600;
|
||||
margin-top: 35px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 42px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
clear: both;
|
||||
font-weight: 400;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 3px solid #59d69d;
|
||||
padding-left: 8px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 0 0 19px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding: 13px 13px 21px 15px;
|
||||
margin-bottom: 18px;
|
||||
font-family: georgia, serif;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
blockquote:before {
|
||||
font-size: 40px;
|
||||
margin-left: -10px;
|
||||
font-family: georgia, serif;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
blockquote p {
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
margin-bottom: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
code,
|
||||
pre {
|
||||
font-family: Monaco, Andale Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #fee9cc;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
padding: 1px 3px;
|
||||
font-size: 12px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
padding: 14px;
|
||||
margin: 0 0 18px;
|
||||
line-height: 16px;
|
||||
font-size: 11px;
|
||||
border: 1px solid #d9d9d9;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
background: #f6f6f6;
|
||||
}
|
||||
|
||||
pre code {
|
||||
background-color: #f6f6f6;
|
||||
color: #737373;
|
||||
font-size: 11px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
sup {
|
||||
font-size: 0.83em;
|
||||
vertical-align: super;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body,
|
||||
code,
|
||||
pre code,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: black;
|
||||
}
|
||||
|
||||
table,
|
||||
pre {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.table-of-contents {
|
||||
top: 61px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.table-of-contents ul {
|
||||
position: fixed;
|
||||
top: 80px;
|
||||
left: 40px;
|
||||
overflow: auto;
|
||||
margin: 0px;
|
||||
height: 100%;
|
||||
padding: 0px 0px;
|
||||
box-sizing: border-box;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.table-of-contents ul li {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.table-of-contents a {
|
||||
padding: 2px 0px;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.content-right {
|
||||
position: relative;
|
||||
top: -20px;
|
||||
max-width: 700px;
|
||||
margin-left: 290px;
|
||||
padding-left: 70px;
|
||||
}
|
||||
|
||||
body > p {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
body > table {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
body > pre {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.curProject {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
font-size: 25px;
|
||||
color: black;
|
||||
margin-left: -240px;
|
||||
width: 240px;
|
||||
padding: 5px;
|
||||
line-height: 25px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.g-doc {
|
||||
margin-top: 56px;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.m-header {
|
||||
background: #32363a;
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
padding-left: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
z-index: 9;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.m-header .title {
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
font-weight: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
margin: 0;
|
||||
margin-left: 16px;
|
||||
padding: 0;
|
||||
line-height: 56px;
|
||||
border: none;
|
||||
}
|
||||
.m-header .nav {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
position: absolute;
|
||||
right: 32px;
|
||||
}
|
||||
.m-header .nav a {
|
||||
color: #fff;
|
||||
margin-left: 16px;
|
||||
padding: 8px;
|
||||
transition: color .2s;
|
||||
}
|
||||
.m-header .nav a:hover {
|
||||
color: #59d69d;
|
||||
}
|
||||
|
||||
.m-footer {
|
||||
border-top: 1px solid #ddd;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=defaultTheme.css.map */
|
7
exts/yapi-plugin-export-data/defaultTheme.css.map
Normal file
7
exts/yapi-plugin-export-data/defaultTheme.css.map
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"mappings": "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",
|
||||
"sources": ["defaultTheme.scss"],
|
||||
"names": [],
|
||||
"file": "defaultTheme.css"
|
||||
}
|
4
exts/yapi-plugin-export-data/defaultTheme.js
Normal file
4
exts/yapi-plugin-export-data/defaultTheme.js
Normal file
@ -0,0 +1,4 @@
|
||||
const fs = require('fs');
|
||||
const sysPath = require('path');
|
||||
const css = fs.readFileSync(sysPath.join(__dirname, './defaultTheme.css'));
|
||||
module.exports = '<style>' + css + '</style>';
|
334
exts/yapi-plugin-export-data/defaultTheme.scss
Normal file
334
exts/yapi-plugin-export-data/defaultTheme.scss
Normal file
@ -0,0 +1,334 @@
|
||||
html,
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
blockquote {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
/* 设置滚动条的样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
/* 外层轨道 */
|
||||
::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset006pxrgba(255, 0, 0, 0.3);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
/* 滚动条滑块 */
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.5);
|
||||
}
|
||||
::-webkit-scrollbar-thumb:window-inactive {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", SimSun, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 25px;
|
||||
color: #393838;
|
||||
position: relative;
|
||||
// overflow-x: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 10px 0 15px 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #ddd;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
a, a:link, a:visited {
|
||||
color: #34495e;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover, a:focus {
|
||||
color: #59d69d;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
p {
|
||||
padding-left: 10px;
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: #404040;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #2c3e50;
|
||||
font-weight: 600;
|
||||
margin-top: 35px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 42px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
clear: both;
|
||||
font-weight: 400;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 3px solid #59d69d;
|
||||
padding-left: 8px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 0 0 19px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding: 13px 13px 21px 15px;
|
||||
margin-bottom: 18px;
|
||||
font-family: georgia, serif;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
blockquote:before {
|
||||
font-size: 40px;
|
||||
margin-left: -10px;
|
||||
font-family: georgia, serif;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
blockquote p {
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
margin-bottom: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
code,
|
||||
pre {
|
||||
font-family: Monaco, Andale Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #fee9cc;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
padding: 1px 3px;
|
||||
font-size: 12px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
padding: 14px;
|
||||
margin: 0 0 18px;
|
||||
line-height: 16px;
|
||||
font-size: 11px;
|
||||
border: 1px solid #d9d9d9;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
background: #f6f6f6;
|
||||
}
|
||||
|
||||
pre code {
|
||||
background-color: #f6f6f6;
|
||||
color: #737373;
|
||||
font-size: 11px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
sup {
|
||||
font-size: 0.83em;
|
||||
vertical-align: super;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body,
|
||||
code,
|
||||
pre code,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: black;
|
||||
}
|
||||
table,
|
||||
pre {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.table-of-contents {
|
||||
top: 61px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.table-of-contents ul {
|
||||
position: fixed;
|
||||
top: 80px;
|
||||
left: 40px;
|
||||
overflow: auto;
|
||||
margin: 0px;
|
||||
height: 100%;
|
||||
padding: 0px 0px;
|
||||
box-sizing: border-box;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.table-of-contents ul li {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.table-of-contents a {
|
||||
padding: 2px 0px;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.content-right {
|
||||
position: relative;
|
||||
top: -20px;
|
||||
max-width: 700px;
|
||||
margin-left: 290px;
|
||||
padding-left: 70px;
|
||||
}
|
||||
|
||||
body>p {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
body>table {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
body>pre {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.curProject {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
font-size: 25px;
|
||||
color: black;
|
||||
margin-left: -240px;
|
||||
width: 240px;
|
||||
padding: 5px;
|
||||
line-height: 25px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.g-doc {
|
||||
margin-top: 56px;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.m-header {
|
||||
background: #32363a;
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
padding-left: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
z-index: 9;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
.title {
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
font-weight: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
margin: 0;
|
||||
margin-left: 16px;
|
||||
padding: 0;
|
||||
line-height: 56px;
|
||||
border: none;
|
||||
}
|
||||
.nav {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
position: absolute;
|
||||
right: 32px;
|
||||
a {
|
||||
color: #fff;
|
||||
margin-left: 16px;
|
||||
padding: 8px;
|
||||
transition: color .2s;
|
||||
}
|
||||
a:hover {
|
||||
color: #59d69d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.m-footer {
|
||||
border-top: 1px solid #ddd;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
4
exts/yapi-plugin-export-data/index.js
Normal file
4
exts/yapi-plugin-export-data/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
server: true,
|
||||
client: true
|
||||
}
|
16
exts/yapi-plugin-export-data/server.js
Normal file
16
exts/yapi-plugin-export-data/server.js
Normal file
@ -0,0 +1,16 @@
|
||||
const controller = require('./controller');
|
||||
|
||||
// const mongoose = require('mongoose');
|
||||
// const _ = require('underscore');
|
||||
|
||||
module.exports = function(){
|
||||
this.bindHook('add_router', function(addRouter){
|
||||
addRouter({
|
||||
controller: controller,
|
||||
method: 'get',
|
||||
path: 'export',
|
||||
action: 'exportData'
|
||||
})
|
||||
})
|
||||
|
||||
}
|
@ -46,13 +46,13 @@ module.exports = function () {
|
||||
})
|
||||
|
||||
// MockServer生成mock数据后触发
|
||||
this.bindHook('mock_after', async function (context) {
|
||||
this.bindHook('mock_after', function (context) {
|
||||
|
||||
let interfaceId = context.interfaceData._id;
|
||||
let projectId = context.projectData._id;
|
||||
let groupId = context.projectData.group_id;
|
||||
let ip = context.ctx.originalUrl;
|
||||
|
||||
//let ip = context.ctx.originalUrl;
|
||||
let ip = context.ctx.ip.match(/\d+.\d+.\d+.\d+/)[0];
|
||||
let data = {
|
||||
interface_id: interfaceId,
|
||||
project_id: projectId,
|
||||
@ -64,8 +64,7 @@ module.exports = function () {
|
||||
let inst = yapi.getInst(statisModel);
|
||||
|
||||
try {
|
||||
let result = await inst.save(data);
|
||||
result = yapi.commons.fieldSelect(result, ['interface_id', 'project_id', 'group_id', 'time', 'ip', 'date']);
|
||||
inst.save(data).then();
|
||||
|
||||
} catch (e) {
|
||||
yapi.commons.log('mockStatisError', e);
|
||||
|
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yapi",
|
||||
"version": "1.2.4",
|
||||
"version": "1.2.5",
|
||||
"description": "YAPI",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@ -19,8 +19,6 @@
|
||||
"license": "Apache2.0",
|
||||
"dependencies": {
|
||||
"axios": "^0.16.2",
|
||||
"chalk": "^2.0.1",
|
||||
"clipboard": "^1.7.1",
|
||||
"deref": "^0.7.0",
|
||||
"fs-extra": "^3.0.1",
|
||||
"json-schema-ref-parser": "^4.0.0",
|
||||
@ -34,20 +32,20 @@
|
||||
"koa-send": "^3.2.0",
|
||||
"koa-session-minimal": "^3.0.3",
|
||||
"koa-static": "^3.0.0",
|
||||
"koa-views": "^5.2.0",
|
||||
"koa-websocket": "^4.0.0",
|
||||
"markdown-it": "^8.4.0",
|
||||
"markdown-it-anchor": "^4.0.0",
|
||||
"markdown-it-table-of-contents": "^0.3.2",
|
||||
"mockjs": "^1.0.1-beta3",
|
||||
"moment": "^2.18.1",
|
||||
"mongoose": "4.7.0",
|
||||
"mongoose-auto-increment": "^5.0.1",
|
||||
"nodemailer": "^4.0.1",
|
||||
"ora": "^1.3.0",
|
||||
"randexp": "^0.4.6",
|
||||
"request": "^2.81.0",
|
||||
"sha1": "^1.1.1",
|
||||
"tslib": "^1.8.0",
|
||||
"underscore": "^1.8.3",
|
||||
"universal-cookie": "^2.0.8",
|
||||
"url": "^0.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -132,6 +132,14 @@ class interfaceModel extends baseModel {
|
||||
.sort({_id: -1})
|
||||
.exec();
|
||||
}
|
||||
|
||||
listByPid(project_id){
|
||||
return this.model.find({
|
||||
project_id: project_id
|
||||
})
|
||||
.sort({_id: -1})
|
||||
.exec();
|
||||
}
|
||||
|
||||
//获取全部接口信息
|
||||
getInterfaceListCount() {
|
||||
|
@ -73,6 +73,21 @@ var hooks = {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* 导出 markdown 数据
|
||||
* @param context Object
|
||||
* {
|
||||
* projectData: project,
|
||||
interfaceData: interfaceData,
|
||||
ctx: ctx,
|
||||
mockJson: res
|
||||
* }
|
||||
*
|
||||
*/
|
||||
'export_markdown': {
|
||||
type: 'multi',
|
||||
listener: []
|
||||
},
|
||||
/**
|
||||
* MockServer生成mock数据后触发
|
||||
* @param context Object
|
||||
@ -92,13 +107,23 @@ var hooks = {
|
||||
* 增加路由的钩子
|
||||
* type Sync
|
||||
* @param addPluginRouter Function
|
||||
* @info
|
||||
* addPLuginPLugin(config)
|
||||
*
|
||||
* config = {
|
||||
* path, // String
|
||||
* method, // String
|
||||
* path, // String 路由名称
|
||||
* method, // String 请求方法 get post ...
|
||||
* controller // Class 继承baseController的class
|
||||
* action // String controller的Action
|
||||
* }
|
||||
*
|
||||
* 示例:
|
||||
* config = {
|
||||
* path: "export/pdf",
|
||||
* method: "get",
|
||||
* controller: controller,
|
||||
* action: "exportPdf"
|
||||
* }
|
||||
*/
|
||||
add_router: {
|
||||
type: 'multi',
|
||||
|
Binary file not shown.
@ -223,12 +223,14 @@ Your browser does not support the video tag.
|
||||
<p>断言函数,详细 api可查看 <a target="_blank" href="https://nodejs.org/dist/latest-v8.x/docs/api/assert.html">document</a></p>
|
||||
</li><li><p>status</p>
|
||||
<p>http 状态码</p>
|
||||
</li><li><p>params</p>
|
||||
<p>http request params, 合并了 query 和 body</p>
|
||||
</li><li><p>body </p>
|
||||
<p>返回 response body</p>
|
||||
</li><li><p>header </p>
|
||||
<p>返回 response header</p>
|
||||
</li><li><p>records </p>
|
||||
<p>记录的 http 请求信息,假设需要获取 key为555的接口参数或者响应数据,可通过 records[555].params 或 records[555].body 获取 </p>
|
||||
<p>记录的 http 请求信息,假设需要获取 key 为 555 的接口参数或者响应数据,可通过 records[555].params 或 records[555].body 获取 </p>
|
||||
</li></ul>
|
||||
<h3 class="subject" id="示例">示例 <a class="hashlink" href="#示例">#</a></h3><pre><code>assert.equal(body.errcode<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>
|
||||
assert.equal(body.data.group_name<span class="token punctuation">,</span> 'testGroup'<span class="token punctuation">)</span>
|
||||
|
@ -106,7 +106,7 @@
|
||||
|
||||
|
||||
|
||||
<p class="home-version">当前版本:v1.2.4</p>
|
||||
<p class="home-version">当前版本:v1.2.5</p>
|
||||
|
||||
|
||||
</div>
|
||||
|
@ -152,7 +152,7 @@
|
||||
<h2 class="subject" id="Q3_忘记密码怎么办?">Q3 忘记密码怎么办? <a class="hashlink" href="#Q3_忘记密码怎么办?">#</a></h2><p>请联系 <code>超级管理员</code> ,只有超级管理员能重置密码。</p>
|
||||
<h2 class="subject" id="Q4_发现了_Bug_怎么办?">Q4 发现了 Bug 怎么办? <a class="hashlink" href="#Q4_发现了_Bug_怎么办?">#</a></h2><p>请反馈到 <a href="https://github.com/YMFE/yapi/issues" _blank="target">Github</a>,功能性的问题我们会在一周内修复,并在每周一发布新的版本 Tag.</p>
|
||||
<h2 class="subject" id="Q5_可视化部署一直处于_github_压缩包下载状态怎么办?">Q5 可视化部署一直处于 github 压缩包下载状态怎么办? <a class="hashlink" href="#Q5_可视化部署一直处于_github_压缩包下载状态怎么办?">#</a></h2><p>可按 Ctrl + c 中断部署操作,然后删除之前部署的文件,重新部署。</p>
|
||||
<p>如果还是不行,请参考 文档 -> 内网部署 -> <a href="https://yapi.ymfe.org/devops.html#方式二. 命令行部署" >命令行部署</a> 文档</p>
|
||||
<p>如果还是不行,请参考 文档 -> 内网部署 -> <a href="./devops.html#方式二. 命令行部署" >命令行部署</a> 文档</p>
|
||||
<p><br>
|
||||
<br>
|
||||
<br>
|
||||
|
@ -102,6 +102,10 @@
|
||||
<nav class="docs-sidebar hidden-print hidden-xs hidden-sm">
|
||||
<ul class="nav docs-sidenav">
|
||||
|
||||
<li >
|
||||
<a href="#v1.2.5">v1.2.5</a>
|
||||
</li>
|
||||
|
||||
<li >
|
||||
<a href="#v1.2.4">v1.2.4</a>
|
||||
</li>
|
||||
@ -138,7 +142,11 @@
|
||||
|
||||
<div class="content-right markdown-body use-sidebar" role="main">
|
||||
|
||||
<h3 class="subject" id="v1.2.4">v1.2.4 <a class="hashlink" href="#v1.2.4">#</a></h3><h4 class="subject" id="Bug_Fixed">Bug Fixed <a class="hashlink" href="#Bug_Fixed">#</a></h4><ol>
|
||||
<h3 class="subject" id="v1.2.5">v1.2.5 <a class="hashlink" href="#v1.2.5">#</a></h3><h4 class="subject" id="Bug_Fixed">Bug Fixed <a class="hashlink" href="#Bug_Fixed">#</a></h4><ol>
|
||||
<li>成员如果第一次添加成员时选择组长,接着再添加下一个成员,如果select是默认的开发者,这时候会出现与上次select相同的值</li><li>如果添加了一个不存在的成员还是会提示添加成功,并且发送的数据是原来发送成功的数据,这里需要重置初始值并在未找到对应用户名时对未找到的人名应该提示用户不存在</li><li>测试集合中的左侧 Menu 拖动的时候右侧的List需要跟随变动</li><li>在接口开发阶段,多个人并行改接口,如果最后一个人改之前没刷新页面,会把之前的人修改过的都冲掉了</li><li>修复cross-request,response header字段重复bug</li><li>Fix 接口集自动化测试 header 没有解析 mock 和 变量参数</li></ol>
|
||||
<h4 class="subject" id="Feature">Feature <a class="hashlink" href="#Feature">#</a></h4><ol>
|
||||
<li>优化了分组添加,编辑交互</li><li>cross-request 计算了接口请求时间</li><li>新增接口文档导出 html, markdown 功能</li></ol>
|
||||
<h3 class="subject" id="v1.2.4">v1.2.4 <a class="hashlink" href="#v1.2.4">#</a></h3><h4 class="subject" id="Bug_Fixed">Bug Fixed <a class="hashlink" href="#Bug_Fixed">#</a></h4><ol>
|
||||
<li>期望值输入时候换成字符串,导致diff时因类型不一致匹配不上</li><li>swagger 导入数据时出现的 id 未定义bug</li><li>fix: kerberos dependencies 导致安装依赖需要编译的问题</li><li>修复了高级 mock 期望过滤参数为空时匹配不到的 bug</li><li>将接口编辑页的保存按钮变成一直在窗口底部</li><li>修改需求文档中项目操作处修改项目中的接口测试a链接指向的网页错误问题</li><li>添加接口时重名,现在提示“已存在”,并在提示信息中告知用户删改接口的位置</li><li>已添加的成员,再次添加会提示“添加成功”,优化提示为已成功添加人数,和已存在人数</li><li>添加分组和修改分组时有个权限问题没有更新,切换列表才更新,该问题已解决</li><li>解决修改和删除公共分类名称处,在添加接口时,选择接口分类名称没有修改的问题</li></ol>
|
||||
<h4 class="subject" id="Feature">Feature <a class="hashlink" href="#Feature">#</a></h4><ol>
|
||||
<li>接口 path 支持了后面带 /</li><li>cross-request支持了不安全的 header,如 cookie, referer...</li><li>支持了 path 带特殊符号"!"</li><li>请求参数可改变顺序,目前只是对必需和非必需进行自动排序</li><li>用户头像上传问题,txt改成jpg格式上传,用户头像显示空白,然后无法再次上传头像。无法再次上传的问题已经解决</li><li>解决用户头像改变但是header处图片不变的问题。问题描述:用户上传头像成功但是Header处的头像没有改变,并且点击其他页面后再回到个人中心里面的头像又变成没有重新上传时的图片,必须重新刷新才可以将Header处的图片更新</li><li>解决导入 postman 接口动态路由无法导入的 Bug</li></ol>
|
||||
|
@ -1 +1 @@
|
||||
window.WEBPACK_ASSETS = {"index.js":{"js":"index@aeda4564fb6a9b08fa32.js","css":"index@aeda4564fb6a9b08fa32.css"},"lib":{"js":"lib@f6dd47c1f1d5bc6322ad.js"},"lib2":{"js":"lib2@dbcc326d4fe3e4473316.js"},"manifest":{"js":"manifest@b67af9f8b578904e66c5.js"}}
|
||||
window.WEBPACK_ASSETS = {"index.js":{"js":"index@45bbc08f1c61a1014932.js","css":"index@45bbc08f1c61a1014932.css"},"lib":{"js":"lib@9521c6250635e5a8893a.js"},"lib2":{"js":"lib2@dbcc326d4fe3e4473316.js"},"manifest":{"js":"manifest@b67af9f8b578904e66c5.js"}}
|
1
static/prd/index@45bbc08f1c61a1014932.css
Normal file
1
static/prd/index@45bbc08f1c61a1014932.css
Normal file
File diff suppressed because one or more lines are too long
BIN
static/prd/index@45bbc08f1c61a1014932.css.gz
Normal file
BIN
static/prd/index@45bbc08f1c61a1014932.css.gz
Normal file
Binary file not shown.
1
static/prd/index@45bbc08f1c61a1014932.js
Normal file
1
static/prd/index@45bbc08f1c61a1014932.js
Normal file
File diff suppressed because one or more lines are too long
BIN
static/prd/index@45bbc08f1c61a1014932.js.gz
Normal file
BIN
static/prd/index@45bbc08f1c61a1014932.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
static/prd/lib@9521c6250635e5a8893a.js.gz
Normal file
BIN
static/prd/lib@9521c6250635e5a8893a.js.gz
Normal file
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user