525 lines
16 KiB
JavaScript
525 lines
16 KiB
JavaScript
import React, { PureComponent as Component } from 'react';
|
||
import {
|
||
Upload,
|
||
Icon,
|
||
message,
|
||
Select,
|
||
Tooltip,
|
||
Button,
|
||
Spin,
|
||
Switch,
|
||
Modal,
|
||
Radio,
|
||
Input,
|
||
Checkbox
|
||
} from 'antd';
|
||
import PropTypes from 'prop-types';
|
||
import { connect } from 'react-redux';
|
||
import './ProjectData.scss';
|
||
import axios from 'axios';
|
||
|
||
import URL from 'url';
|
||
|
||
const Dragger = Upload.Dragger;
|
||
import { saveImportData } from '../../../../reducer/modules/interface';
|
||
import { fetchUpdateLogData } from '../../../../reducer/modules/news.js';
|
||
import { handleSwaggerUrlData } from '../../../../reducer/modules/project';
|
||
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');
|
||
function handleExportRouteParams(url, status, isWiki) {
|
||
if (!url) {
|
||
return;
|
||
}
|
||
let urlObj = URL.parse(url, true),
|
||
query = {};
|
||
query = Object.assign(query, urlObj.query, { status, isWiki });
|
||
return URL.format({
|
||
pathname: urlObj.pathname,
|
||
query
|
||
});
|
||
}
|
||
|
||
// exportDataModule.pdf = {
|
||
// name: 'Pdf',
|
||
// route: '/api/interface/download_crx',
|
||
// desc: '导出项目接口文档为 pdf 文件'
|
||
// }
|
||
@connect(
|
||
state => {
|
||
return {
|
||
curCatid: -(-state.inter.curdata.catid),
|
||
basePath: state.project.currProject.basepath,
|
||
updateLogList: state.news.updateLogList,
|
||
swaggerUrlData: state.project.swaggerUrlData
|
||
};
|
||
},
|
||
{
|
||
saveImportData,
|
||
fetchUpdateLogData,
|
||
handleSwaggerUrlData
|
||
}
|
||
)
|
||
class ProjectData extends Component {
|
||
constructor(props) {
|
||
super(props);
|
||
this.state = {
|
||
selectCatid: '',
|
||
menuList: [],
|
||
curImportType: 'swagger',
|
||
curExportType: null,
|
||
showLoading: false,
|
||
dataSync: 'merge',
|
||
exportContent: 'all',
|
||
isSwaggerUrl: false,
|
||
swaggerUrl: '',
|
||
isWiki: false
|
||
};
|
||
}
|
||
static propTypes = {
|
||
match: PropTypes.object,
|
||
curCatid: PropTypes.number,
|
||
basePath: PropTypes.string,
|
||
saveImportData: PropTypes.func,
|
||
fetchUpdateLogData: PropTypes.func,
|
||
updateLogList: PropTypes.array,
|
||
handleSwaggerUrlData: PropTypes.func,
|
||
swaggerUrlData: PropTypes.string
|
||
};
|
||
|
||
UNSAFE_componentWillMount() {
|
||
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,
|
||
selectCatid: menuList[0]._id
|
||
});
|
||
}
|
||
});
|
||
plugin.emitHook('import_data', importDataModule);
|
||
plugin.emitHook('export_data', exportDataModule, this.props.match.params.id);
|
||
}
|
||
|
||
selectChange(value) {
|
||
this.setState({
|
||
selectCatid: +value
|
||
});
|
||
}
|
||
|
||
uploadChange = info => {
|
||
const status = info.file.status;
|
||
if (status !== 'uploading') {
|
||
console.log(info.file, info.fileList);
|
||
}
|
||
if (status === 'done') {
|
||
message.success(`${info.file.name} 文件上传成功`);
|
||
} else if (status === 'error') {
|
||
message.error(`${info.file.name} 文件上传失败`);
|
||
}
|
||
};
|
||
|
||
handleAddInterface = async res => {
|
||
return await HandleImportData(
|
||
res,
|
||
this.props.match.params.id,
|
||
this.state.selectCatid,
|
||
this.state.menuList,
|
||
this.props.basePath,
|
||
this.state.dataSync,
|
||
message.error,
|
||
message.success,
|
||
() => this.setState({ showLoading: false })
|
||
);
|
||
};
|
||
|
||
// 本地文件上传
|
||
handleFile = info => {
|
||
if (!this.state.curImportType) {
|
||
return message.error('请选择导入数据的方式');
|
||
}
|
||
if (this.state.selectCatid) {
|
||
this.setState({ showLoading: true });
|
||
let reader = new FileReader();
|
||
reader.readAsText(info.file);
|
||
reader.onload = async res => {
|
||
res = await importDataModule[this.state.curImportType].run(res.target.result);
|
||
if (this.state.dataSync === 'merge') {
|
||
// 开启同步
|
||
this.showConfirm(res);
|
||
} else {
|
||
// 未开启同步
|
||
await this.handleAddInterface(res);
|
||
}
|
||
};
|
||
} else {
|
||
message.error('请选择上传的默认分类');
|
||
}
|
||
};
|
||
|
||
showConfirm = async res => {
|
||
let that = this;
|
||
let typeid = this.props.match.params.id;
|
||
let apiCollections = res.apis.map(item => {
|
||
return {
|
||
method: item.method,
|
||
path: item.path
|
||
};
|
||
});
|
||
let result = await this.props.fetchUpdateLogData({
|
||
type: 'project',
|
||
typeid,
|
||
apis: apiCollections
|
||
});
|
||
let domainData = result.payload.data.data;
|
||
const ref = confirm({
|
||
title: '您确认要进行数据同步????',
|
||
width: 600,
|
||
okType: 'danger',
|
||
iconType: 'exclamation-circle',
|
||
className: 'dataImport-confirm',
|
||
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 }} />
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
<p className="info">温馨提示: 数据同步后,可能会造成原本的修改数据丢失</p>
|
||
</div>
|
||
),
|
||
async onOk() {
|
||
await that.handleAddInterface(res);
|
||
},
|
||
onCancel() {
|
||
that.setState({ showLoading: false, dataSync: 'normal' });
|
||
ref.destroy();
|
||
}
|
||
});
|
||
};
|
||
|
||
handleImportType = val => {
|
||
this.setState({
|
||
curImportType: val,
|
||
isSwaggerUrl: false
|
||
});
|
||
};
|
||
|
||
handleExportType = val => {
|
||
this.setState({
|
||
curExportType: val,
|
||
isWiki: false
|
||
});
|
||
};
|
||
|
||
// 处理导入信息同步
|
||
onChange = checked => {
|
||
this.setState({
|
||
dataSync: checked
|
||
});
|
||
};
|
||
|
||
// 处理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 {
|
||
// 处理swagger url 导入
|
||
await this.props.handleSwaggerUrlData(this.state.swaggerUrl);
|
||
// let result = json5_parse(this.props.swaggerUrlData)
|
||
let res = await importDataModule[this.state.curImportType].run(this.props.swaggerUrlData);
|
||
if (this.state.dataSync === 'merge') {
|
||
// merge
|
||
this.showConfirm(res);
|
||
} else {
|
||
// 未开启同步
|
||
await this.handleAddInterface(res);
|
||
}
|
||
} catch (e) {
|
||
this.setState({ showLoading: false });
|
||
message.error(e.message);
|
||
}
|
||
} else {
|
||
message.error('请选择上传的默认分类');
|
||
}
|
||
};
|
||
|
||
// 处理导出接口是全部还是公开
|
||
handleChange = e => {
|
||
this.setState({ exportContent: e.target.value });
|
||
};
|
||
|
||
// 处理是否开启wiki导出
|
||
handleWikiChange = e => {
|
||
this.setState({
|
||
isWiki: e.target.checked
|
||
});
|
||
};
|
||
|
||
/**
|
||
*
|
||
*
|
||
* @returns
|
||
* @memberof ProjectData
|
||
*/
|
||
render() {
|
||
const uploadMess = {
|
||
name: 'interfaceData',
|
||
multiple: true,
|
||
showUploadList: false,
|
||
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 exportHref = handleExportRouteParams(
|
||
exportUrl,
|
||
this.state.exportContent,
|
||
this.state.isWiki
|
||
);
|
||
|
||
// console.log('inter', this.state.exportContent);
|
||
return (
|
||
<div className="g-row">
|
||
<div className="m-panel">
|
||
<div className="postman-dataImport">
|
||
<div className="dataImportCon">
|
||
<div>
|
||
<h3>
|
||
数据导入
|
||
<a
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
href="https://hellosean1025.github.io/yapi/documents/data.html"
|
||
>
|
||
<Tooltip title="点击查看文档">
|
||
<Icon type="question-circle-o" />
|
||
</Tooltip>
|
||
</a>
|
||
</h3>
|
||
</div>
|
||
<div className="dataImportTile">
|
||
<Select
|
||
placeholder="请选择导入数据的方式"
|
||
value={this.state.curImportType}
|
||
onChange={this.handleImportType}
|
||
>
|
||
{Object.keys(importDataModule).map(name => {
|
||
return (
|
||
<Option key={name} value={name}>
|
||
{importDataModule[name].name}
|
||
</Option>
|
||
);
|
||
})}
|
||
</Select>
|
||
</div>
|
||
<div className="catidSelect">
|
||
<Select
|
||
value={this.state.selectCatid + ''}
|
||
showSearch
|
||
style={{ width: '100%' }}
|
||
placeholder="请选择数据导入的默认分类"
|
||
optionFilterProp="children"
|
||
onChange={this.selectChange.bind(this)}
|
||
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>
|
||
);
|
||
})}
|
||
</Select>
|
||
</div>
|
||
<div className="dataSync">
|
||
<span className="label">
|
||
数据同步
|
||
<Tooltip
|
||
title={
|
||
<div>
|
||
<h3 style={{ color: 'white' }}>普通模式</h3>
|
||
<p>不导入已存在的接口</p>
|
||
<br />
|
||
<h3 style={{ color: 'white' }}>智能合并</h3>
|
||
<p>
|
||
已存在的接口,将合并返回数据的 response,适用于导入了 swagger
|
||
数据,保留对数据结构的改动
|
||
</p>
|
||
<br />
|
||
<h3 style={{ color: 'white' }}>完全覆盖</h3>
|
||
<p>不保留旧数据,完全使用新数据,适用于接口定义完全交给后端定义</p>
|
||
</div>
|
||
}
|
||
>
|
||
<Icon type="question-circle-o" />
|
||
</Tooltip>{' '}
|
||
</span>
|
||
<Select value={this.state.dataSync} onChange={this.onChange}>
|
||
<Option value="normal">普通模式</Option>
|
||
<Option value="good">智能合并</Option>
|
||
<Option value="merge">完全覆盖</Option>
|
||
</Select>
|
||
|
||
{/* <Switch checked={this.state.dataSync} onChange={this.onChange} /> */}
|
||
</div>
|
||
{this.state.curImportType === 'swagger' && (
|
||
<div className="dataSync">
|
||
<span className="label">
|
||
开启url导入
|
||
<Tooltip title="swagger url 导入">
|
||
<Icon type="question-circle-o" />
|
||
</Tooltip>{' '}
|
||
|
||
</span>
|
||
|
||
<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="dataImportTile">
|
||
<Select placeholder="请选择导出数据的方式" onChange={this.handleExportType}>
|
||
{Object.keys(exportDataModule).map(name => {
|
||
return (
|
||
<Option key={name} value={name}>
|
||
{exportDataModule[name].name}
|
||
</Option>
|
||
);
|
||
})}
|
||
</Select>
|
||
</div>
|
||
|
||
<div className="dataExport">
|
||
<RadioGroup defaultValue="all" onChange={this.handleChange}>
|
||
<Radio value="all">全部接口</Radio>
|
||
<Radio value="open">公开接口</Radio>
|
||
</RadioGroup>
|
||
</div>
|
||
<div className="export-content">
|
||
{this.state.curExportType ? (
|
||
<div>
|
||
<p className="export-desc">{exportDataModule[this.state.curExportType].desc}</p>
|
||
<a
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
href={exportHref}>
|
||
<Button className="export-button" type="primary" size="large">
|
||
{' '}
|
||
导出{' '}
|
||
</Button>
|
||
</a>
|
||
<Checkbox
|
||
checked={this.state.isWiki}
|
||
onChange={this.handleWikiChange}
|
||
className="wiki-btn"
|
||
disabled={this.state.curExportType === 'json'}
|
||
>
|
||
添加wiki
|
||
<Tooltip title="开启后 html 和 markdown 数据导出会带上wiki数据">
|
||
<Icon type="question-circle-o" />
|
||
</Tooltip>{' '}
|
||
</Checkbox>
|
||
</div>
|
||
) : (
|
||
<Button disabled className="export-button" type="primary" size="large">
|
||
{' '}
|
||
导出{' '}
|
||
</Button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
}
|
||
|
||
export default ProjectData;
|