mirror of
https://github.com/YMFE/yapi.git
synced 2025-01-18 13:04:46 +08:00
feat: 增加swagger URL 导入
This commit is contained in:
parent
9155572a18
commit
a68f26ca6a
@ -1,6 +1,7 @@
|
||||
### v1.3.19
|
||||
|
||||
* 增加项目文档记录wiki
|
||||
* 支持swagger URL 导入
|
||||
|
||||
#### Bug Fixed
|
||||
|
||||
|
@ -1,29 +1,42 @@
|
||||
import React, { PureComponent as Component } from 'react'
|
||||
import { Upload, Icon, message, Select, Tooltip, Button, Spin, Switch, Modal,Radio } from 'antd';
|
||||
import React, { PureComponent as Component } from 'react';
|
||||
import {
|
||||
Upload,
|
||||
Icon,
|
||||
message,
|
||||
Select,
|
||||
Tooltip,
|
||||
Button,
|
||||
Spin,
|
||||
Switch,
|
||||
Modal,
|
||||
Radio,
|
||||
Input
|
||||
} from 'antd';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import './ProjectData.scss';
|
||||
import axios from 'axios';
|
||||
|
||||
import URL from 'url'
|
||||
import URL from 'url';
|
||||
|
||||
const Dragger = Upload.Dragger;
|
||||
import { saveImportData } from '../../../../reducer/modules/interface';
|
||||
import { fetchUpdateLogData } from '../../../../reducer/modules/news.js'
|
||||
import { fetchUpdateLogData } from '../../../../reducer/modules/news.js';
|
||||
const Option = Select.Option;
|
||||
const confirm = Modal.confirm;
|
||||
const plugin = require('client/plugin.js');
|
||||
const RadioGroup = Radio.Group;
|
||||
const importDataModule = {};
|
||||
const exportDataModule = {};
|
||||
const HandleImportData = require('common/HandleImportData')
|
||||
const HandleImportData = require('common/HandleImportData');
|
||||
|
||||
function handleExportRouteParams (url, value) {
|
||||
if(!url) {
|
||||
return
|
||||
function handleExportRouteParams(url, value) {
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
let urlObj = URL.parse(url, true), query = {};
|
||||
query = Object.assign(query, urlObj.query, {status: value});
|
||||
let urlObj = URL.parse(url, true),
|
||||
query = {};
|
||||
query = Object.assign(query, urlObj.query, { status: value });
|
||||
return URL.format({
|
||||
pathname: urlObj.pathname,
|
||||
query
|
||||
@ -37,31 +50,31 @@ function handleExportRouteParams (url, value) {
|
||||
// }
|
||||
@connect(
|
||||
state => {
|
||||
|
||||
return {
|
||||
curCatid: -(-state.inter.curdata.catid),
|
||||
basePath: state.project.currProject.basepath,
|
||||
updateLogList: state.news.updateLogList
|
||||
}
|
||||
}, {
|
||||
};
|
||||
},
|
||||
{
|
||||
saveImportData,
|
||||
fetchUpdateLogData
|
||||
}
|
||||
)
|
||||
|
||||
class ProjectData extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectCatid: "",
|
||||
selectCatid: '',
|
||||
menuList: [],
|
||||
curImportType: null,
|
||||
curExportType: null,
|
||||
showLoading: false,
|
||||
dataSync: false,
|
||||
exportContent: 'all'
|
||||
|
||||
}
|
||||
exportContent: 'all',
|
||||
isSwaggerUrl: false,
|
||||
swaggerUrl: ''
|
||||
};
|
||||
}
|
||||
static propTypes = {
|
||||
match: PropTypes.object,
|
||||
@ -70,30 +83,28 @@ class ProjectData extends Component {
|
||||
saveImportData: PropTypes.func,
|
||||
fetchUpdateLogData: PropTypes.func,
|
||||
updateLogList: PropTypes.array
|
||||
}
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
axios.get(`/api/interface/getCatMenu?project_id=${this.props.match.params.id}`).then((data) => {
|
||||
axios.get(`/api/interface/getCatMenu?project_id=${this.props.match.params.id}`).then(data => {
|
||||
if (data.data.errcode === 0) {
|
||||
let menuList = data.data.data;
|
||||
this.setState({
|
||||
menuList: menuList
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
plugin.emitHook('import_data', importDataModule);
|
||||
plugin.emitHook('export_data', exportDataModule, this.props.match.params.id);
|
||||
}
|
||||
|
||||
|
||||
selectChange(value) {
|
||||
this.setState({
|
||||
selectCatid: +value
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
uploadChange = (info) => {
|
||||
uploadChange = info => {
|
||||
const status = info.file.status;
|
||||
if (status !== 'uploading') {
|
||||
console.log(info.file, info.fileList);
|
||||
@ -103,9 +114,9 @@ class ProjectData extends Component {
|
||||
} else if (status === 'error') {
|
||||
message.error(`${info.file.name} 文件上传失败`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleAddInterface = async (res)=>{
|
||||
handleAddInterface = async res => {
|
||||
return await HandleImportData(
|
||||
res,
|
||||
this.props.match.params.id,
|
||||
@ -113,14 +124,14 @@ class ProjectData extends Component {
|
||||
this.state.menuList,
|
||||
this.props.basePath,
|
||||
this.state.dataSync,
|
||||
message.error,
|
||||
message.success,
|
||||
()=> this.setState({ showLoading: false })
|
||||
)
|
||||
}
|
||||
message.error,
|
||||
message.success,
|
||||
() => this.setState({ showLoading: false })
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
handleFile = (info) => {
|
||||
// 本地文件上传
|
||||
handleFile = info => {
|
||||
if (!this.state.curImportType) {
|
||||
return message.error('请选择导入数据的方式');
|
||||
}
|
||||
@ -135,27 +146,28 @@ class ProjectData extends Component {
|
||||
this.showConfirm(res);
|
||||
} else {
|
||||
// 未开启同步
|
||||
await this.handleAddInterface(res)
|
||||
await this.handleAddInterface(res);
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
message.error("请选择上传的默认分类");
|
||||
message.error('请选择上传的默认分类');
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
showConfirm = async (res) => {
|
||||
|
||||
showConfirm = async res => {
|
||||
let that = this;
|
||||
let typeid = this.props.match.params.id;
|
||||
let apiCollections = res.apis.map(item=>{
|
||||
let apiCollections = res.apis.map(item => {
|
||||
return {
|
||||
method:item.method,
|
||||
method: item.method,
|
||||
path: item.path
|
||||
}
|
||||
})
|
||||
let result = await this.props.fetchUpdateLogData({ type: 'project', typeid, apis: apiCollections })
|
||||
};
|
||||
});
|
||||
let result = await this.props.fetchUpdateLogData({
|
||||
type: 'project',
|
||||
typeid,
|
||||
apis: apiCollections
|
||||
});
|
||||
let domainData = result.payload.data.data;
|
||||
const ref = confirm({
|
||||
title: '您确认要进行数据同步????',
|
||||
@ -163,59 +175,99 @@ class ProjectData extends Component {
|
||||
okType: 'danger',
|
||||
iconType: 'exclamation-circle',
|
||||
className: 'dataImport-confirm',
|
||||
okText: "确认",
|
||||
cancelText: "取消",
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
content: (
|
||||
<div className="postman-dataImport-modal">
|
||||
<div className="postman-dataImport-modal-content">
|
||||
{
|
||||
domainData.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className="postman-dataImport-show-diff">
|
||||
<span className="logcontent" dangerouslySetInnerHTML={{ __html: item.content }}>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
{domainData.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className="postman-dataImport-show-diff">
|
||||
<span className="logcontent" dangerouslySetInnerHTML={{ __html: item.content }} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<p className="info">温馨提示: 数据同步后,可能会造成原本的修改数据丢失</p>
|
||||
</div>
|
||||
),
|
||||
async onOk() {
|
||||
await that.handleAddInterface(res)
|
||||
await that.handleAddInterface(res);
|
||||
},
|
||||
onCancel() {
|
||||
that.setState({ showLoading: false, dataSync: false })
|
||||
ref.destroy()
|
||||
that.setState({ showLoading: false, dataSync: false });
|
||||
ref.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleImportType = (val) => {
|
||||
handleImportType = val => {
|
||||
this.setState({
|
||||
curImportType: val
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleExportType = (val) => {
|
||||
handleExportType = val => {
|
||||
this.setState({
|
||||
curExportType: val
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onChange = (checked) => {
|
||||
// 处理导入信息同步
|
||||
onChange = checked => {
|
||||
this.setState({
|
||||
dataSync: checked
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleChange = (e) => {
|
||||
// 处理swagger URL 导入
|
||||
handleUrlChange = checked => {
|
||||
this.setState({
|
||||
isSwaggerUrl: checked
|
||||
});
|
||||
};
|
||||
|
||||
// 记录输入的url
|
||||
swaggerUrlInput = url => {
|
||||
this.setState({
|
||||
swaggerUrl: url
|
||||
});
|
||||
};
|
||||
|
||||
// url导入上传
|
||||
onUrlUpload = async () => {
|
||||
|
||||
if (!this.state.curImportType) {
|
||||
return message.error('请选择导入数据的方式');
|
||||
}
|
||||
|
||||
if(!this.state.swaggerUrl) {
|
||||
return message.error('url 不能为空');
|
||||
}
|
||||
if (this.state.selectCatid) {
|
||||
this.setState({ showLoading: true });
|
||||
try {
|
||||
let content = await axios(this.state.swaggerUrl);
|
||||
content = content.data;
|
||||
let res = await importDataModule[this.state.curImportType].run(content);
|
||||
if (this.state.dataSync) {
|
||||
// 开启同步
|
||||
this.showConfirm(res);
|
||||
} else {
|
||||
// 未开启同步
|
||||
await this.handleAddInterface(res);
|
||||
}
|
||||
} catch (e) {
|
||||
message.error(e.message);
|
||||
}
|
||||
} else {
|
||||
message.error('请选择上传的默认分类');
|
||||
}
|
||||
};
|
||||
|
||||
handleChange = e => {
|
||||
this.setState({ exportContent: e.target.value });
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
@ -231,9 +283,12 @@ class ProjectData extends Component {
|
||||
action: '/api/interface/interUpload',
|
||||
customRequest: this.handleFile,
|
||||
onChange: this.uploadChange
|
||||
}
|
||||
};
|
||||
|
||||
let exportUrl = this.state.curExportType && exportDataModule[this.state.curExportType] && exportDataModule[this.state.curExportType].route;
|
||||
let exportUrl =
|
||||
this.state.curExportType &&
|
||||
exportDataModule[this.state.curExportType] &&
|
||||
exportDataModule[this.state.curExportType].route;
|
||||
let exportHref = handleExportRouteParams(exportUrl, this.state.exportContent);
|
||||
|
||||
// console.log('inter', this.state.exportContent);
|
||||
@ -242,14 +297,27 @@ 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/documents/data.html" >
|
||||
<Tooltip title="点击查看文档"><Icon type="question-circle-o" /></Tooltip>
|
||||
</a></h3></div>
|
||||
<div>
|
||||
<h3>
|
||||
数据导入 <a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://yapi.ymfe.org/documents/data.html"
|
||||
>
|
||||
<Tooltip title="点击查看文档">
|
||||
<Icon type="question-circle-o" />
|
||||
</Tooltip>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div className="dataImportTile">
|
||||
<Select placeholder="请选择导入数据的方式" onChange={this.handleImportType}>
|
||||
{Object.keys(importDataModule).map((name) => {
|
||||
|
||||
return <Option key={name} value={name}>{importDataModule[name].name}</Option>
|
||||
{Object.keys(importDataModule).map(name => {
|
||||
return (
|
||||
<Option key={name} value={name}>
|
||||
{importDataModule[name].name}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</div>
|
||||
@ -260,40 +328,94 @@ class ProjectData extends Component {
|
||||
placeholder="请选择数据导入的默认分类"
|
||||
optionFilterProp="children"
|
||||
onChange={this.selectChange.bind(this)}
|
||||
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
|
||||
filterOption={(input, option) =>
|
||||
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
>
|
||||
{this.state.menuList.map((item, key) => {
|
||||
return <Option key={key} value={item._id + ""}>{item.name}</Option>;
|
||||
return (
|
||||
<Option key={key} value={item._id + ''}>
|
||||
{item.name}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</div>
|
||||
<div className="dataSync">
|
||||
<span>开启数据同步<Tooltip title="开启数据同步后会覆盖项目中原本的数据"><Icon type="question-circle-o" /></Tooltip> :</span>
|
||||
<span>
|
||||
开启数据同步<Tooltip title="开启数据同步后会覆盖项目中原本的数据">
|
||||
<Icon type="question-circle-o" />
|
||||
</Tooltip>{' '}
|
||||
:
|
||||
</span>
|
||||
|
||||
<Switch checked={this.state.dataSync} onChange={this.onChange} />
|
||||
</div>
|
||||
{this.state.curImportType === 'swagger' && (
|
||||
<div className="dataSync">
|
||||
<span>
|
||||
开启url导入<Tooltip title="swagger url 导入">
|
||||
<Icon type="question-circle-o" />
|
||||
</Tooltip>{' '}
|
||||
:
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<div style={{ marginTop: 16, height: 180 }}>
|
||||
<Spin spinning={this.state.showLoading} tip="上传中...">
|
||||
<Dragger {...uploadMess}>
|
||||
<p className="ant-upload-drag-icon">
|
||||
<Icon type="inbox" />
|
||||
</p>
|
||||
<p className="ant-upload-text">点击或者拖拽文件到上传区域</p>
|
||||
<p className="ant-upload-hint" onClick={(e)=>{
|
||||
e.stopPropagation();
|
||||
}} dangerouslySetInnerHTML={{__html: this.state.curImportType ? importDataModule[this.state.curImportType].desc : null}} ></p>
|
||||
</Dragger>
|
||||
</Spin>
|
||||
</div>
|
||||
<Switch checked={this.state.isSwaggerUrl} onChange={this.handleUrlChange} />
|
||||
</div>
|
||||
)}
|
||||
{this.state.isSwaggerUrl ? (
|
||||
<div className="import-content url-import-content">
|
||||
<Input
|
||||
placeholder="http://demo.swagger.io/v2/swagger.json"
|
||||
onChange={e => this.swaggerUrlInput(e.target.value)}
|
||||
/>
|
||||
<Button type="primary" className="url-btn" onClick={this.onUrlUpload} loading={this.state.showLoading}>
|
||||
上传
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="import-content">
|
||||
<Spin spinning={this.state.showLoading} tip="上传中...">
|
||||
<Dragger {...uploadMess}>
|
||||
<p className="ant-upload-drag-icon">
|
||||
<Icon type="inbox" />
|
||||
</p>
|
||||
<p className="ant-upload-text">点击或者拖拽文件到上传区域</p>
|
||||
<p
|
||||
className="ant-upload-hint"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: this.state.curImportType
|
||||
? importDataModule[this.state.curImportType].desc
|
||||
: null
|
||||
}}
|
||||
/>
|
||||
</Dragger>
|
||||
</Spin>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="dataImportCon" style={{ marginLeft: '20px', display: Object.keys(exportDataModule).length > 0 ? '' : 'none' }}>
|
||||
<div ><h3>数据导出</h3></div>
|
||||
<div
|
||||
className="dataImportCon"
|
||||
style={{
|
||||
marginLeft: '20px',
|
||||
display: Object.keys(exportDataModule).length > 0 ? '' : 'none'
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<h3>数据导出</h3>
|
||||
</div>
|
||||
<div className="dataImportTile">
|
||||
<Select placeholder="请选择导出数据的方式" onChange={this.handleExportType}>
|
||||
{Object.keys(exportDataModule).map((name) => {
|
||||
return <Option key={name} value={name}>{exportDataModule[name].name}</Option>
|
||||
{Object.keys(exportDataModule).map(name => {
|
||||
return (
|
||||
<Option key={name} value={name}>
|
||||
{exportDataModule[name].name}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</div>
|
||||
@ -304,25 +426,28 @@ class ProjectData extends Component {
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<div className="export-content">
|
||||
{this.state.curExportType ?
|
||||
{this.state.curExportType ? (
|
||||
<div>
|
||||
<p className="export-desc">{exportDataModule[this.state.curExportType].desc}</p>
|
||||
<a target="_blank" href={exportHref} >
|
||||
<Button className="export-button" type="primary" size="large"> 导出 </Button>
|
||||
|
||||
<a target="_blank" href={exportHref}>
|
||||
<Button className="export-button" type="primary" size="large">
|
||||
{' '}
|
||||
导出{' '}
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
:
|
||||
<Button disabled className="export-button" type="primary" size="large"> 导出 </Button>
|
||||
}
|
||||
|
||||
|
||||
) : (
|
||||
<Button disabled className="export-button" type="primary" size="large">
|
||||
{' '}
|
||||
导出{' '}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.import-content {
|
||||
margin-top: 16px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.url-import-content {
|
||||
text-align: center;
|
||||
.url-btn{
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.export-content{
|
||||
text-align: center;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user