import fetch from 'dva/fetch';
import {
notification
} from 'antd';
import qs from 'querystring';
const codeMessage = {
200: '请求成功',
201: '新建或修改数据成功',
202: '一个请求已经进入后台队列',
204: '删除数据成功',
400: '请求失败',
401: 'token失效',
403: '禁止访问',
404: '请求失败',
406: '请求方式错误',
500: '服务器错误',
502: '网关错误',
503: '服务不可用',
504: '网关超时',
};
// 检查ajax返回的状态
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
/**
* 暂时没用,服务端返回的 status 始终为200
*
* @type {*|string|string}
*/
const errorText = codeMessage[response.status] || response.statusText;
notification.error({
message: `请求错误 ${response.status}: ${response.url}`,
description: errorText,
});
const error = new Error(response.statusText);
error.name = response.status;
error.response = response;
throw error;
}
// fetch超时处理
const TIMEOUT = 100000;
const timeoutFetch = (url, options) => {
let fetchPromise = fetch(url, options);
let timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('请求超时')), TIMEOUT);
});
return Promise.race([fetchPromise, timeoutPromise]);
};
/**
* 请求url,返回promise 对象
*
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default function request(url, options) {
const defaultOptions = {
credentials: 'include',
method: 'GET',
mode: 'cors',
};
const mergeOptions = {
...defaultOptions,
...options
};
const userInfo = JSON.parse(window.sessionStorage.getItem('userInfo'));
const appKey = window.sessionStorage.getItem('appKey');
mergeOptions.headers = {
accept: 'application/json',
'content-type': 'application/json; charset=utf-8',
...mergeOptions.headers,
};
if (appKey) mergeOptions.headers.uuuappkey = appKey;
if (userInfo) mergeOptions.headers.uuutoken = userInfo.sessionId;
if (mergeOptions.method !== 'GET') {
mergeOptions.body = JSON.stringify(mergeOptions.body);
}
if (mergeOptions.method !== 'POST' && mergeOptions.params) {
url = `${url}${url.indexOf('?') !== -1 ? '&' : '?'}${qs.stringify(mergeOptions.params)}`;
}
if (!mergeOptions.hideTime && !mergeOptions.params) {
url = `${url}?timeStamp=${new Date().getTime()}`;
}
return timeoutFetch(url, mergeOptions)
.then(checkStatus)
.then((response) => {
return response.json();
})
.then((data) => {
if (data.code === 200 || data.success === true) {
return data;
}
if (data.code === 300) { // 接口出错
return data;
}
if (data.code === 401) { // token失效
notification.error({
message: 'token失效',
description: data.msg,
key: 'error'
});
window.g_app._store.dispatch({
type: 'app/logout'
});
return data;
}
if (data.code === 403) { // 没有权限
notification.error({
message: '没有权限',
description: data.msg,
key: 'error'
});
return data;
}
if (data.code >= 404 && data.code < 422) {
notification.error({
message: '请求失败',
description: data.msg,
key: 'error'
});
return data;
}
if (data.code <= 504 && data.code >= 500) {
notification.error({
message: '服务器错误',
description: data.msg,
key: 'error'
});
return data;
}
})
.catch((error) => {
const {
response
} = error;
let msg;
let statusCode;
if (response && response instanceof Object) {
const {
status,
statusText
} = response;
statusCode = status;
msg = statusText;
} else {
statusCode = 600;
msg = 'Network Error';
}
return Promise.reject({
success: false,
code: statusCode,
msg
});
});
}
调用:
import request from '../utils/request';
import { apiPrefix } from '../config/projectConfig';
// 获取 scheme 列表
export function oracleSchema() {
return request(`${apiPrefix}/api_manager/interface/api/fetchOracleSchemas`);
}
// 接口菜单层级树
export function interfaceLevel(pid) {
return request(
`${apiPrefix}/api_manager/interface/api/getAPILevelTree?pid=${pid}`,{
hideTime: true,
});
}
// 获取目录树
export function catalogTree() {
return request(`${apiPrefix}/api_manager/interface/api/getAPICatalogTree`);
}
// 搜索菜单树
export function searchLevelTree(search) {
return request(
`${apiPrefix}/api_manager/interface/api/searchAPILevelTree?search=${search}`, {
hideTime: true,
});
}
// 查询接口上级分类树
export function apiDirectory(apiId) {
return request(`${apiPrefix}/api_manager/interface/api/getAPIDirectory?apiId=${apiId}`, {
hideTime: true,
});
}
// 新增 API 树目录
export function addApiTree(params) {
return request(`${apiPrefix}/api_manager/interface/api/addApiTree`, {
method: 'POST',
body: params,
});
}
// 删除 API 树目录
export function delApiTree(params) {
return request(`${apiPrefix}/api_manager/interface/api/deleteApiTree?apiTreeId=${params.apiTreeId}`, {
method: 'DELETE',
hideTime: true,
});
}
/************************************************************************
* 接口相关
************************************************************************/
// 接口列表
export function apiList(params, pageNum, pageSize) {
return request(`${apiPrefix}/api_manager/interface/api/getApiList`, {
method: 'POST',
body: { ...params, pageNum, pageSize },
});
}
// 新增接口
export function addApi(params) {
return request(`${apiPrefix}/api_manager/interface/api/addApi`, {
method: 'POST',
body: params,
});
}
// 修改接口
export function updateApi(params) {
return request(`${apiPrefix}/api_manager/interface/api/updateApi`, {
method: 'PUT',
body: params,
});
}
// 删除接口
export function delApi(params) {
return request(`${apiPrefix}/api_manager/interface/api/deleteApi?apiId=${params.apiId}`, {
method: 'DELETE',
hideTime: true,
});
}
/************************************************************************
* sql相关
************************************************************************/
// 获取 sql
export function getApiSqlInfo(params) {
return request(`${apiPrefix}/api_manager/interface/api/getApiSqlInfo?apiId=${params.apiId}`, {
hideTime: true,
});
}
// 保存
export function interfaceDefSave(params) {
return request(`${apiPrefix}/api_manager/interface/api/interfaceDefSave`, {
method: 'POST',
body: params,
});
}
// 获取模型列表 - 表名
export function getBizList(params, pageNum, pageSize) {
return request(`${apiPrefix}/api_manager/interface/api/getBizList`, {
method: 'POST',
body: { ...params, pageNum, pageSize },
});
}
// 获取模型列表 - 列名
export function getBizColList(bizId) {
return request(`${apiPrefix}/api_manager/interface/api/getBizColList?bizId=${bizId}`, {
hideTime: true,
});
}
// 解析 sql 获得 select item 列表(出参)
export function selectItemsForSql(params) {
return request(`${apiPrefix}/api_manager/interface/api/getSelectItemsForSql`, {
method: 'POST',
body: params,
});
}
// 获取 procedure info
export function procedureInfo(params) {
return request(`${apiPrefix}/api_manager/interface/api/fetchOracleProcedureInfo`, {
method: 'POST',
body: params,
});
}
// 获取 procedure params
export function procedureParams({ owner, procedureName }) {
return request(`${apiPrefix}/api_manager/interface/api/fetchOracleAllProcedureParams`, {
params: {
owner,
procedureName,
},
});
}
/************************************************************************
* auth相关
************************************************************************/
// 获取系统名
export function appNameApi({ apiId }) {
return request(`${apiPrefix}/menu_manager/menu/api/getAppNameForApi?apiId=${apiId}`, {
method: 'POST',
hideTime: true,
});
}
// 获取指标列表
export function indexAppName(params) {
return request(`${apiPrefix}/menu_manager/menu/api/getIndexForAppName`, {
method: 'POST',
body: params,
});
}
// 保存 auth
export function addIndexAppName(params) {
return request(`${apiPrefix}/menu_manager/menu/api/addIndexForAppName`, {
method: 'POST',
body: [...params],
});
}
dva模型调用:
import { message } from 'antd';
import modelExtend from 'dva-model-extend';
import { pageModel } from '../../../utils/util';
import {
addApiTree,
addApi,
getApiSqlInfo,
interfaceDefSave,
selectItemsForSql,
procedureInfo,
procedureParams,
appNameApi,
indexAppName,
addIndexAppName,
} from '../../../services/modelInterface';
export default modelExtend(pageModel, {
namespace: 'interfaceModal',
state: {
interfaceDef: {},
},
effects: {
// 获取 接口定义
* getApiSqlInfo({ payload }, { call, put }) {
const { success, data } = yield call(getApiSqlInfo, payload);
if (success) {
if (data.apiType === 0) data.apiType = 1;
yield put({
type: 'updateState',
payload: {
interfaceDef: data,
},
});
return true;
} else {
message.error('初始化失败');
}
},
/************************************************************************
* sql 弹窗相关
************************************************************************/
// sql弹窗保存
* interfaceDefSave({ payload }, { call }) {
const { success } = yield call(interfaceDefSave, payload);
if (success) return true;
},
// 解析 sql 获得 select item 列表(出参)
* getSelectItemsForSql({ payload }, { call }) {
const { success, data } = yield call(selectItemsForSql, payload);
if (success) return data;
},
// 获取 oracleChildSchema
* procedureInfo({ payload }, { call }) {
const { success, data } = yield call(procedureInfo, payload);
if (success) return data.list;
},
// 获取 SP 入参字段
* procedureParams({ payload }, { call }) {
const { success, data } = yield call(procedureParams, payload);
if (success) return data;
},
/************************************************************************
* auth 弹窗相关
************************************************************************/
// 获取系统名
* appNameApi({ payload }, { call }) {
const { success, data } = yield call(appNameApi, payload);
if (success) return data;
},
// 获取指标
* indexAppName({ payload }, { call }) {
const { success, data } = yield call(indexAppName, payload);
if (success) return data;
},
// 保存auth
* addIndexAppName({ payload }, { call }) {
const { success } = yield call(addIndexAppName, payload);
if (success) return true;
},
},
});
模块组成:
组件具体使用:
import React, { Component, Fragment } from 'react';
import { connect } from 'dva';
import PropTypes from 'prop-types';
import { Col, Row, Button, Modal, Collapse, Card, Icon, Tooltip, Form, Input } from 'antd';
import { ContextMenu, MenuItem } from 'react-contextmenu';
import { Page, SearchTree, CommonTable, CustomModal, CustomForm, CommonSearch, Breadcrumb } from '@/components';
import { apiList } from '../../services/modelInterface';
import { modelInterfaceColumns, participationColumns } from './config/columns';
import { sqlForm, folderForm, interfaceForm } from './config/form';
import { modelInterfaceSearch } from './config/search';
import { getCrumb } from '../../utils/util';
import ModalView from './components';
import style from './index.less';
const { Panel } = Collapse;
const { TextArea } = Input;
@connect(({ modelInterface, loading }) => ({
...modelInterface,
loading,
}))
class ModelInterface extends Component {
tableRef = React.createRef();
state = {
editable: false,
searchParams: {},
selectedNode: {},
visible: false, // 模态框状态
modalType: '', // 模态框类型
record: {}, // 编辑时原始数据
};
static getDerivedStateFromProps(props, state) {
const { dispatch, selectedNode } = props;
if (props.selectedNode !== state.selectedNode && props.selectedNode) {
const { id, isDirectory } = props.selectedNode;
if (id) {
dispatch({ type: 'modelInterface/getBizDirectory', id });
if (!isDirectory) {
// dispatch({ type: 'bizFormTable/bizInfo', payload: { id } });
}
return { selectedNode };
}
}
return null;
}
// 处理搜索
handleSearch = (values) => {
this.setState({ searchParams: values });
};
// 模态框状态处理
showModal = async ({ record, modalType }) => {
const { dispatch } = this.props;
if (modalType === 'sql') {
const data = await dispatch({ type: 'interfaceModal/getApiSqlInfo', payload: { apiId: record.apiId } });
if (!data) return;
}
this.setState({ visible: true, modalType, record });
};
closeModal = () => this.setState({ visible: false, modalType: '', record: {} });
// 选择接口
handleSelect = async (keys, { node }) => {
const { dispatch, selectedNode } = this.props;
const dataRef = node.props['dataRef'];
const flag = selectedNode.id !== dataRef.id;
if (flag) {
this.setState({ editable: false });
if (dataRef['subNodeNum']) {
await dispatch({ type: 'modelInterface/levelTreeList', pid: dataRef.id });
}
dispatch({ type: 'modelInterface/updateState', payload: { selectedNode: dataRef } });
}
};
// 新增目录
handleAddFolder = (e, data, target) => {
const nodeItem = JSON.parse(target.getAttribute('data-item'));
this.setState({ visible: true, modalType: 'folder', record: { upperApiTreeId: nodeItem.id } });
};
// 删除目录
handleDelFolder = (e, data, target) => {
const nodeItem = JSON.parse(target.getAttribute('data-item'));
Modal.confirm({
title: '提示',
content: '你确定删除该目录嘛?',
onOk: async () => {
const { dispatch } = this.props;
await dispatch({ type: 'modelInterface/delApiTree', payload: { apiTreeId: nodeItem.id } });
dispatch({ type: 'modelInterface/interfaceTree' });
},
onCancel() { },
});
};
// 删除接口
handleDelInterface = (e, data, target) => {
const nodeItem = JSON.parse(target.getAttribute('data-item'));
Modal.confirm({
title: '提示',
content: '你确定删除该接口嘛?',
onOk: async () => {
const { dispatch } = this.props;
await dispatch({ type: 'modelInterface/delApi', payload: { apiId: nodeItem.id } });
this.tableRef.current.handleFirstPage();
},
onCancel() { },
});
};
// 保存
handleSubmit = async (values) => {
const { modalType, record } = this.state;
const { dispatch } = this.props;
if (modalType === 'folder') {
await dispatch({
type: 'modelInterface/addApiTree',
payload: { ...record, ...values },
});
dispatch({ type: 'modelInterface/interfaceTree' });
}
// 新增或编辑接口
if (modalType === 'updateApi' || modalType === 'addApi') {
const params = record && record.apiId ? { ...values, apiId: record.apiId } : values;
await dispatch({
type: `modelInterface/${modalType}`,
payload: params,
});
this.tableRef.current.handleFirstPage();
}
};
// 面包屑点击
handleCrumbClick = async (item) => {
const { dispatch } = this.props;
dispatch({ type: 'modelInterface/levelTreeList', pid: item.id });
};
render() {
const { visible, modalType, record, searchParams } = this.state;
const { selectedNode, interfaceTree, expandedKeys, directoryInfo, catalogTree, dispatch, loading, form } = this.props;
console.log('interfaceTree>>>>>', interfaceTree)
const columns = [
...modelInterfaceColumns,
{
title: '操作',
dataIndex: 'operator',
render: (text, record) => (
<div className="btn-group">
<Button type="primary" size="small" onClick={() => this.showModal({ modalType: 'sql', record })}>SQL</Button>
<Button type="primary" size="small" onClick={() => this.showModal({ modalType: 'updateApi', record })}>修改</Button>
<Button type="primary" size="small" onClick={() => this.showModal({ modalType: 'auth', record })}>授权</Button>
</div>
),
},
];
const paramsColumns = [
...participationColumns,
{
title: '操作',
dataIndex: 'operator',
render: (text, record) => (
<div className="btn-group">
<Button type="primary" size="small" onClick={() => this.showModal({ modalType: 'sql', record })}>SQL</Button>
<Button type="primary" size="small" onClick={() => this.showModal({ modalType: 'updateApi', record })}>修改</Button>
<Button type="primary" size="small" onClick={() => this.showModal({ modalType: 'auth', record })}>授权</Button>
</div>
),
},
];
return (
<Page className="pageContainer" loading={loading.effects['modelInterface/interfaceTree']}>
<Row gutter={12}>
{/* 树列表 */}
<Col xs={{ span: 24 }} lg={{ span: 6 }} className="treeNav">
<SearchTree
dispatch={dispatch}
placeholder='请输入表名称'
config={{ key: 'id', title: 'name' }}
showRightMenu={true}
showRemoteSearch={true}
treeData={interfaceTree}
defaultExpandedKeys={expandedKeys}
iconFlagOptions={{ icon: 'api' }}
handleSelect={this.handleSelect}
modelFunc="modelInterface/searchTreeWithColCount"
/>
<ContextMenu id="parent_unique_identifier">
<MenuItem key="addInterface" onClick={() => this.showModal({ modalType: 'addApi' })}>新增接口</MenuItem>
<MenuItem divider />
<MenuItem key="addFolder" onClick={this.handleAddFolder}>新增目录</MenuItem>
<MenuItem divider />
<MenuItem key="delFolder" onClick={this.handleDelFolder}>删除目录</MenuItem>
</ContextMenu>
<ContextMenu id="child_unique_identifier">
<MenuItem key="delInterface" onClick={this.handleDelInterface}>删除接口</MenuItem>
</ContextMenu>
</Col>
{/* 表格列表 */}
<Col xs={{ span: 24 }} lg={{ span: 18 }} className="tableContainer">
<div className="gutter-box">
<Breadcrumb breadcrumbList={getCrumb(directoryInfo, { title: 'name', click: this.handleCrumbClick })} />
{/* <CommonSearch
style={{ marginTop: 12 }}
columnNumber={2}
showResetBtn={false}
formList={modelInterfaceSearch}
handleSearch={this.handleSearch}
/> */}
<Collapse defaultActiveKey={['1']} style={{ marginBottom: 20 }} >
<Panel header="sql信息" key="1">
<div>1111</div>
</Panel>
</Collapse>
<Collapse defaultActiveKey={['1']} style={{ marginBottom: 20 }} >
<Panel header="入参" key="1">
<CommonTable
rowKey="apiId"
bordered={false}
selectType={false}
ref={this.tableRef}
columns={paramsColumns}
fetchList={apiList}
searchParams={{ upperId: selectedNode.id, ...searchParams }}
/>
</Panel>
</Collapse>
{/* 系统 */}
<Row gutter={24}>
{[{ appName: 'UCM', appComment: '系统内容' }, { appName: 'ACP', appComment: '系统内容' },] > 0 && [{ appName: 'UCM', appComment: '系统内容' }, { appName: 'ACP', appComment: '系统内容' },].map((sys, index) => (
<Col span={6} key={sys.appKey} style={{ marginBottom: 24 }}>
<Card
className={style.sysBox}
bodyStyle={{ padding: '24px', height: 150 }}
>
<Row>
<Col span={20} className='name'>{sys.appName}</Col>
<Col span={4} className='operate-box'>
<Tooltip placement='top' title='编辑'>
<Icon type='edit' style={{ fontSize: '22px', paddingTop: '6px' }} onClick={() => { this.onShowEditModel(sys); }} />
</Tooltip>
</Col>
</Row>
<Row>
<Col span={24} className='desc'>{sys.appComment}</Col>
</Row>
</Card>
</Col>
))}
</Row>
{/* 菜单 */}
<Row gutter={24}>
{[{ appName: 'UCM', appComment: '系统内容' }, { appName: 'ACP', appComment: '系统内容' },].length > 0 && [{ appName: 'UCM', appComment: '系统内容' }, { appName: 'ACP', appComment: '系统内容' },].map((sys, index) => (
<Col span={6} key={sys.appKey} style={{ marginBottom: 24 }}>
<Card
className={style.sysBox}
bodyStyle={{ padding: '24px', height: 150 }}
>
<Row>
<Col span={20} className='name'>{sys.appName}</Col>
<Col span={4} className='operate-box'>
<Tooltip placement='top' title='编辑'>
<Icon type='edit' style={{ fontSize: '22px', paddingTop: '6px' }} onClick={() => { this.onShowEditModel(sys); }} />
</Tooltip>
</Col>
</Row>
<Row>
<Col span={24} className='desc'>{sys.appComment}</Col>
</Row>
</Card>
</Col>
))}
</Row>
<Collapse defaultActiveKey={['1']} >
<Panel header="出参" key="1">
<CommonTable
rowKey="apiId"
bordered={false}
selectType={false}
ref={this.tableRef}
columns={paramsColumns}
fetchList={apiList}
searchParams={{ upperId: selectedNode.id, ...searchParams }}
/>
</Panel>
</Collapse>
</div>
</Col>
</Row>
{/* 目录、接口弹窗 */}
{
['auth', 'sql'].indexOf(modalType) === -1 && (
<CustomModal
visible={visible}
record={record}
onCancel={this.closeModal}
onSubmit={this.handleSubmit}
renderTitle={() => {
if (modalType === 'folder') {
return (record && !record.id) ? '新增目录' : '编辑目录';
}
if (modalType === 'addApi') {
return '新增接口';
}
if (modalType === 'updateApi') {
return '编辑接口';
}
}}
renderContent={(formOpts) => (
<Fragment>
{modalType === 'folder' && <CustomForm formList={folderForm} {...formOpts} />}
{(modalType === 'addApi' || modalType === 'updateApi') && <CustomForm formList={interfaceForm(catalogTree)} {...formOpts} />}
</Fragment>
)}
/>
)
}
{/* 授权、SQL弹窗 */}
<ModalView visible={visible} modalType={modalType} record={record} onCancel={this.closeModal} />
</Page>
);
}
}
export default Form.create()(ModelInterface);