wzyyy 3 лет назад
Родитель
Сommit
d6048fc3e9

+ 1 - 1
src/components/ProTableCard/CardItems/applyCard.tsx

@@ -21,7 +21,7 @@ providerType.set('third-party', '第三方应用');
 export default (props: DeviceCardProps) => {
   return (
     <TableCard
-      showMask={false}
+      // showMask={false}
       detail={props.detail}
       actions={props.actions}
       status={props.state}

+ 131 - 0
src/pages/system/Apply/Api/base.tsx

@@ -0,0 +1,131 @@
+import Tree from '@/pages/system/Platforms/Api/leftTree';
+import Table from '@/pages/system/Platforms/Api/basePage';
+import SwaggerUI from '@/pages/system/Platforms/Api/swagger-ui';
+import { useCallback, useEffect, useState } from 'react';
+import { service } from '@/pages/system/Platforms';
+import { model } from '@formily/reactive';
+import { observer } from '@formily/react';
+import './index.less';
+import { useLocation } from 'umi';
+import { useDomFullHeight } from '@/hooks';
+import Home from '../Home';
+
+export const ApiModel = model<{
+  data: any[] | undefined;
+  baseUrl: string;
+  showTable: boolean;
+  components: any;
+  swagger: any;
+  debugger: any;
+}>({
+  data: [],
+  baseUrl: '',
+  showTable: true,
+  components: {},
+  swagger: {},
+  debugger: {},
+});
+
+interface ApiPageProps {
+  showDebugger?: boolean;
+  /**
+   * true 只展示已赋权的接口
+   */
+  isShowGranted?: boolean;
+  /**
+   * false:table暂时所有接口
+   */
+  isOpenGranted?: boolean;
+  type?: 'all' | 'empowerment' | 'authorize';
+  showHome?: boolean;
+}
+
+export default observer((props: ApiPageProps) => {
+  const location = useLocation();
+  const [operations, setOperations] = useState<string[] | undefined>(undefined);
+  const [GrantKeys, setGrantKeys] = useState<string[] | undefined>(undefined);
+  const { minHeight } = useDomFullHeight(`.platforms-api`);
+
+  const initModel = () => {
+    ApiModel.data = [];
+    ApiModel.showTable = true;
+    ApiModel.components = {};
+    ApiModel.swagger = {};
+    ApiModel.debugger = {};
+  };
+
+  /**
+   *  获取能授权的接口ID
+   */
+  const getOperations = () => {
+    service.apiOperations().then((resp: any) => {
+      if (resp.status === 200) {
+        setOperations(resp.result);
+      }
+    });
+  };
+
+  /**
+   * 获取已授权的接口ID
+   */
+  const getApiGrant = useCallback(() => {
+    const param = new URLSearchParams(location.search);
+    const code = param.get('code');
+
+    if (props.isOpenGranted === false) {
+      service.apiOperations().then((resp: any) => {
+        if (resp.status === 200) {
+          setGrantKeys(resp.result);
+        }
+      });
+    } else {
+      service.getApiGranted(code!).then((resp: any) => {
+        if (resp.status === 200) {
+          setGrantKeys(resp.result);
+        }
+      });
+    }
+  }, [location, props.isOpenGranted]);
+
+  useEffect(() => {
+    initModel();
+    getOperations();
+    getApiGrant();
+  }, []);
+
+  return (
+    <div className={'platforms-api'} style={{ minHeight }}>
+      <div className={'platforms-api-tree'}>
+        <Tree
+          isShowGranted={props.isShowGranted}
+          grantKeys={GrantKeys}
+          operations={operations}
+          showHome={props.showHome}
+          type={props.type}
+          onSelect={(data) => {
+            // console.log(data);
+            ApiModel.data = data;
+            ApiModel.showTable = true;
+          }}
+        />
+      </div>
+      {ApiModel.showTable ? (
+        <>
+          {ApiModel.data ? (
+            <Table
+              data={ApiModel.data}
+              operations={operations}
+              isOpenGranted={props.isOpenGranted}
+              isShowGranted={props.isShowGranted}
+              grantKeys={GrantKeys}
+            />
+          ) : (
+            <Home />
+          )}
+        </>
+      ) : (
+        <SwaggerUI showDebugger={props.showDebugger} />
+      )}
+    </div>
+  );
+});

+ 217 - 0
src/pages/system/Apply/Api/basePage.tsx

@@ -0,0 +1,217 @@
+import { Button, Table } from 'antd';
+import { useCallback, useEffect, useRef, useState } from 'react';
+import { useLocation } from 'umi';
+import { service } from '@/pages/system/Platforms';
+import { ApiModel } from '@/pages/system/Platforms/Api/base';
+import { onlyMessage } from '@/utils/util';
+
+interface TableProps {
+  data: any;
+  operations: string[] | undefined;
+  // 是否只展示已授权的接口
+  isShowGranted?: boolean;
+  //
+  isOpenGranted?: boolean;
+  //
+  grantKeys: string[] | undefined;
+}
+
+export default (props: TableProps) => {
+  const [selectKeys, setSelectKeys] = useState<string[]>([]);
+  const [dataSource, setDataSource] = useState<any[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [GrantKeys, setGrantKeys] = useState<string[] | undefined>(undefined);
+
+  const grantCache = useRef<string[]>([]);
+
+  const location = useLocation();
+
+  const getOperations = async (apiData: any[], operations: string[]) => {
+    // 过滤只能授权的接口,当isShowGranted为true时,过滤为已赋权的接口
+    setDataSource(
+      apiData.filter((item) => item && item.operationId && operations.includes(item.operationId)),
+    );
+  };
+
+  const getApiGrant = useCallback(() => {
+    const param = new URLSearchParams(location.search);
+    const code = param.get('code');
+
+    if (props.isOpenGranted === false) {
+      service.apiOperations().then((resp: any) => {
+        if (resp.status === 200) {
+          setGrantKeys(resp.result);
+        }
+      });
+    } else {
+      service.getApiGranted(code!).then((resp: any) => {
+        if (resp.status === 200) {
+          setGrantKeys(resp.result);
+        }
+      });
+    }
+  }, [location, props.isOpenGranted]);
+
+  /**
+   * 获取已授权的接口ID
+   */
+  useEffect(() => {
+    grantCache.current = props.grantKeys || [];
+    setSelectKeys(props.grantKeys || []);
+    setGrantKeys(props.grantKeys);
+  }, [props.grantKeys]);
+
+  useEffect(() => {
+    if (props.isShowGranted) {
+      if (props.data && selectKeys) {
+        getOperations(props.data, selectKeys);
+      } else {
+        setDataSource([]);
+      }
+    }
+  }, [props.isShowGranted, selectKeys, props.data]);
+
+  useEffect(() => {
+    if (props.isOpenGranted === false) {
+      setDataSource(props.data);
+    } else if (!props.isShowGranted) {
+      if (props.data && props.data.length && props.operations) {
+        getOperations(props.data, props.operations);
+      } else {
+        setDataSource([]);
+      }
+    }
+  }, [props.data, props.operations, props.isShowGranted, props.isOpenGranted]);
+
+  const save = useCallback(async () => {
+    const param = new URLSearchParams(location.search);
+    const code = param.get('code');
+    // 和原有已授权数据进行对比
+    const addGrant = selectKeys.filter((key) => {
+      if (grantCache.current.includes(key)) {
+        return false;
+      }
+      return true;
+    });
+
+    // 获取删除的数据
+    const removeGrant = grantCache.current.filter((key) => {
+      if (selectKeys.includes(key)) {
+        return false;
+      }
+      return true;
+    });
+
+    const addOperations = addGrant.map((a: string) => {
+      const item = dataSource.find((b) => b.operationId === a);
+      return {
+        id: a,
+        permissions: item?.security,
+      };
+    });
+
+    const removeOperations = removeGrant.map((a: string) => {
+      const item = dataSource.find((b) => b.operationId === a);
+      return {
+        id: a,
+        permissions: item?.security,
+      };
+    });
+
+    grantCache.current = addGrant;
+
+    setLoading(true);
+    if (props.isOpenGranted === false) {
+      // console.log(props.grantKeys)
+      // console.log(addGrant,'add')
+      // console.log(removeGrant,'del')
+      const resp2 = removeGrant.length ? await service.apiOperationsRemove(removeGrant) : {};
+      const resp = await service.apiOperationsAdd(addGrant);
+      if (resp.status === 200 || resp2.status === 200) {
+        getApiGrant();
+        onlyMessage('操作成功');
+      }
+    } else {
+      const resp2 = await service.removeApiGrant(code!, { operations: removeOperations });
+      const resp = await service.addApiGrant(code!, { operations: addOperations });
+      if (resp.status === 200 || resp2.status === 200) {
+        getApiGrant();
+        onlyMessage('操作成功');
+      }
+    }
+    setLoading(false);
+  }, [selectKeys, location, dataSource, props.isOpenGranted]);
+
+  return (
+    <div className={'platforms-api-table'}>
+      <Table<any>
+        rowKey={'operationId'}
+        columns={[
+          {
+            title: 'API',
+            dataIndex: 'url',
+            render: (text: string, record) => {
+              return (
+                <Button
+                  type={'link'}
+                  style={{ padding: 0, width: '100%', textAlign: 'left' }}
+                  onClick={() => {
+                    console.log(record);
+                    ApiModel.swagger = record;
+                    ApiModel.showTable = false;
+                  }}
+                >
+                  <span className={'ellipsis'}>{text}</span>
+                </Button>
+              );
+            },
+          },
+          {
+            title: '说明',
+            dataIndex: 'summary',
+            ellipsis: true,
+          },
+        ]}
+        pagination={false}
+        dataSource={dataSource}
+        rowSelection={
+          props.isShowGranted !== true
+            ? {
+                selectedRowKeys: selectKeys,
+                onSelect: (record, selected) => {
+                  if (selected) {
+                    const newArr = [...selectKeys, record.operationId];
+                    setSelectKeys(newArr);
+                  } else {
+                    setSelectKeys([...selectKeys.filter((key) => key !== record.operationId)]);
+                  }
+                },
+                onSelectAll: (selected, selectedRows) => {
+                  if (selected) {
+                    // const items = selectedRows.filter((item) => !!item).map((item) => item.operationId).concat(props.grantKeys)
+                    // console.log(items)
+                    setSelectKeys(
+                      selectedRows
+                        .filter((item) => !!item)
+                        .map((item) => item.operationId)
+                        .concat(GrantKeys),
+                    );
+                  } else {
+                    setSelectKeys([]);
+                  }
+                },
+              }
+            : undefined
+        }
+        scroll={{ y: 600 }}
+      />
+      {props.isShowGranted !== true && (
+        <div className={'platforms-api-save'}>
+          <Button type={'primary'} onClick={save} loading={loading}>
+            保存
+          </Button>
+        </div>
+      )}
+    </div>
+  );
+};

+ 80 - 0
src/pages/system/Apply/Api/index.less

@@ -0,0 +1,80 @@
+@import '~antd/es/style/themes/default.less';
+
+.platforms-api {
+  display: flex;
+  padding: 24px;
+  background-color: #fff;
+
+  .platforms-api-tree {
+    width: 320px;
+    padding-right: 12px;
+    border-right: 1px solid #e9e9e9;
+  }
+
+  .platforms-api-table {
+    display: flex;
+    flex-direction: column;
+    flex-grow: 1;
+    width: 0;
+    margin-left: 24px;
+
+    .platforms-api-save {
+      margin-top: 12px;
+    }
+  }
+
+  .platforms-api-swagger {
+    flex: 1;
+    margin-left: 24px;
+
+    .platforms-api-swagger-back {
+      margin-bottom: 24px;
+    }
+
+    .platforms-api-swagger-content {
+      .swagger-content-title {
+        font-weight: bold;
+        font-size: 16px;
+      }
+
+      .swagger-content-item,
+      .swagger-content-url {
+        margin-top: 24px;
+      }
+
+      .swagger-content-url .url-method {
+        color: #fff;
+        border: none;
+
+        &.put {
+          background-color: @orange-6;
+        }
+
+        &.delete {
+          background-color: @red-6;
+        }
+
+        &.post {
+          background-color: @green-6;
+        }
+
+        &.get {
+          background-color: @blue-6;
+        }
+
+        &.patch {
+          background-color: @lime-6;
+        }
+      }
+
+      .swagger-content-request-type {
+        display: flex;
+        justify-content: space-between;
+
+        > span:nth-child(odd) {
+          font-weight: bold;
+        }
+      }
+    }
+  }
+}

+ 10 - 0
src/pages/system/Apply/Api/index.tsx

@@ -0,0 +1,10 @@
+import { PageContainer } from '@ant-design/pro-layout';
+import BasePage from './base';
+
+export default () => {
+  return (
+    <PageContainer>
+      <BasePage type={'empowerment'} showHome={true} />
+    </PageContainer>
+  );
+};

+ 211 - 0
src/pages/system/Apply/Api/leftTree.tsx

@@ -0,0 +1,211 @@
+import { Tree } from 'antd';
+import React, { useCallback, useEffect, useState } from 'react';
+import { service } from '@/pages/system/Platforms';
+import { ApiModel } from '@/pages/system/Platforms/Api/base';
+import { forkJoin, from, defer } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { LoadingOutlined } from '@ant-design/icons';
+
+type LeftTreeType = {
+  onSelect: (data: any) => void;
+  /**
+   * 是否只展示已授权的接口
+   */
+  isShowGranted?: boolean;
+  grantKeys?: string[]; // 已授权的接口
+  type?: 'all' | 'empowerment' | 'authorize'; // 全部、赋权、授权
+  operations?: string[]; // 能赋权的key
+  showHome?: boolean;
+};
+
+interface DataNode {
+  name: string;
+  id: string;
+  isLeaf?: boolean;
+  icon?: React.ReactNode;
+  children?: DataNode[];
+}
+
+export default (props: LeftTreeType) => {
+  const [treeData, setTreeData] = useState<DataNode[]>([]);
+  const [loading, setLoading] = useState(false);
+
+  const updateTreeData = useCallback(
+    (list: DataNode[], parentItem: any): DataNode[] => {
+      let newArray: any[] = list;
+      // console.log(list, props.grantKeys);
+      if (props.type === 'empowerment') {
+        newArray = list.filter(
+          (item: any) =>
+            item.extraData &&
+            item.extraData.some((extraItem: any) =>
+              props.operations?.includes(extraItem.operationId),
+            ),
+        );
+      } else if (props.type === 'authorize') {
+        newArray = list.filter(
+          (item: any) =>
+            item.extraData &&
+            item.extraData.some((extraItem: any) =>
+              props.grantKeys?.includes(extraItem.operationId),
+            ),
+        );
+      }
+      parentItem.children = newArray;
+
+      return parentItem;
+    },
+    [props.isShowGranted, props.grantKeys, props.type, props.operations],
+  );
+
+  const handleTreeData = (data: any) => {
+    if (!data || !(data && Object.keys(data).length)) {
+      return [];
+    }
+    const newArr = data.tags.map((item: any) => ({ id: item.name, name: item.name, isLeaf: true }));
+
+    Object.keys(data.paths).forEach((a: any) => {
+      Object.keys(data.paths[a]).forEach((b) => {
+        const { tags, ...extraData } = data.paths[a][b];
+        const tag = tags[0];
+        const obj = {
+          url: a,
+          method: b,
+          ...extraData,
+        };
+        const item = newArr.find((c: any) => c.id === tag);
+        if (item) {
+          item.extraData = item.extraData ? [...item.extraData, obj] : [obj];
+        }
+      });
+    });
+    return newArr;
+  };
+
+  const getChildrenData = async (data: any[], extraData?: any) => {
+    const ofArray: any[] = [];
+    data.forEach((item: any) => {
+      ofArray.push(
+        defer(() =>
+          from(service.getApiNextLevel(item.name)).pipe(
+            map((resp: any) => {
+              if (resp && resp.components) {
+                ApiModel.components = { ...ApiModel.components, ...resp.components.schemas };
+                return handleTreeData(resp);
+              }
+              return undefined;
+            }),
+            map((resp: any) => resp && updateTreeData(resp, item)),
+          ),
+        ),
+      );
+    });
+
+    forkJoin(ofArray).subscribe((res) => {
+      // console.log(res);
+      setLoading(false);
+      setTreeData(extraData ? [extraData, ...res] : res);
+    });
+  };
+
+  const getLevelOne = async () => {
+    setLoading(true);
+    const resp = await service.getApiFirstLevel();
+    if (resp.urls && resp.urls.length) {
+      if (props.showHome) {
+        const home = {
+          id: 'home',
+          name: '首页',
+          isLeaf: true,
+        };
+        ApiModel.data = undefined;
+        getChildrenData(
+          resp.urls.map((item: any) => ({ ...item, id: item.url })),
+          home,
+        );
+      } else {
+        // setTreeData(resp.urls.map((item: any) => ({ ...item, id: item.url })));
+        getChildrenData(resp.urls.map((item: any) => ({ ...item, id: item.url })));
+      }
+    }
+  };
+
+  // const getChildren = (key: string, name: string): Promise<any> => {
+  //   return new Promise(async (resolve) => {
+  //     const resp = await service.getApiNextLevel(name);
+  //     if (resp && resp.components) {
+  //       ApiModel.components = { ...ApiModel.components, ...resp.components.schemas };
+  //       const handleData = handleTreeData(resp);
+  //       setTreeData((origin) => {
+  //         const data = updateTreeData(origin, key, handleData);
+  //
+  //         return data;
+  //       });
+  //       resolve(resp.result);
+  //     } else {
+  //       resolve([])
+  //     }
+  //   });
+  // };
+
+  // const onLoadData = (node: any): Promise<void> => {
+  //   return new Promise(async (resolve) => {
+  //     if (node.children) {
+  //       resolve();
+  //       return;
+  //     }
+  //     await getChildren(node.key, node.name);
+  //     resolve();
+  //   });
+  // };
+
+  useEffect(() => {
+    if (props.type === 'all') {
+      getLevelOne();
+    } else if (props.type === 'empowerment' && props.grantKeys) {
+      getLevelOne();
+    } else if (props.type === 'authorize' && props.operations) {
+      getLevelOne();
+    }
+  }, [props.operations, props.grantKeys, props.type]);
+
+  // console.log(treeData);
+  return (
+    <div
+      style={{
+        width: '100%',
+        height: '100%',
+      }}
+    >
+      {!loading ? (
+        <Tree
+          showIcon
+          showLine={{ showLeafIcon: false }}
+          height={700}
+          style={{ minWidth: 145 }}
+          fieldNames={{
+            title: 'name',
+            key: 'id',
+          }}
+          onSelect={(_, { node }: any) => {
+            if (node.isLeaf && props.onSelect) {
+              props.onSelect(node.extraData);
+            }
+          }}
+          // onExpand={(_,{node}:any)=>{
+          //   if (node.isLeaf && props.onSelect) {
+          //     props.onSelect(node.extraData);
+          //   }
+          // }}
+          defaultSelectedKeys={['home']}
+          // loadData={onLoadData}
+          treeData={treeData}
+        />
+      ) : (
+        <div style={{ paddingTop: 32, display: 'flex', justifyContent: 'center' }}>
+          <LoadingOutlined style={{ fontSize: 32 }} />
+        </div>
+      )}
+    </div>
+  );
+};

+ 365 - 0
src/pages/system/Apply/Api/swagger-ui/base.tsx

@@ -0,0 +1,365 @@
+import { observer } from '@formily/react';
+import { ApiModel } from '@/pages/system/Platforms/Api/base';
+import { TitleComponent } from '@/components';
+import ReactJson from 'react-json-view';
+import { Button, Input, Table, Tabs } from 'antd';
+import { useCallback, useEffect, useState } from 'react';
+import { cloneDeep, isArray, isObject } from 'lodash';
+import classNames from 'classnames';
+
+export default observer(() => {
+  const [dataSource, setDataSource] = useState<any[]>([]);
+  const [responseData, setResponseData] = useState<any[]>([]);
+
+  const getContent = (data: any) => {
+    return Object.keys(data)[0];
+  };
+
+  const ObjectFindValue = (name: string, obj: any): any => {
+    let value: any = '';
+    if (obj[name]) {
+      value = obj[name];
+    } else {
+      Object.keys(obj).some((key) => {
+        const _value = isObject(obj[key]) ? ObjectFindValue(name, obj[key]) : undefined;
+        if (_value) {
+          value = _value;
+          return true;
+        }
+        return false;
+      });
+    }
+    return value;
+  };
+
+  const titleCase = (value: string) => {
+    return value.slice(0, 1).toLowerCase() + value.slice(1);
+  };
+
+  const handleEntityTable = useCallback(
+    (entityName: string, entityData: any, entityType: string, required: boolean) => {
+      let propertiesData: any[] = [];
+
+      if (entityData) {
+        propertiesData = Object.keys(entityData).map((key) => {
+          return {
+            name: key,
+            description: entityData[key].description,
+            method: '',
+            required: !!entityData[key].required,
+            type: entityData[key].type,
+          };
+        });
+      }
+      // 数组类型,实体名末尾加s
+      const _isArray = entityType === 'array' ? 's' : '';
+
+      setDataSource([
+        ...dataSource,
+        {
+          name: titleCase(entityName) + _isArray,
+          description: entityName,
+          method: 'body',
+          required: required,
+          type: entityType || entityName,
+          children: propertiesData,
+        },
+      ]);
+    },
+    [dataSource],
+  );
+
+  const getEntity = () => {
+    const contentType: any = Object.values(ApiModel.swagger.requestBody.content);
+    if (contentType) {
+      const refUrl = ObjectFindValue('$ref', ApiModel.swagger.requestBody.content);
+      if (refUrl) {
+        const entityName = refUrl.split('/').pop();
+        const entityType = ObjectFindValue('type', ApiModel.swagger.requestBody.content);
+        const entityRequired = ApiModel.swagger.requestBody.required;
+        console.log(entityName, ApiModel.components);
+        const entity: any = ApiModel.components[entityName];
+        const file = ObjectFindValue('file', ApiModel.swagger.requestBody.content);
+        // 是否为文件上传
+        if (file && isObject(file)) {
+          const fileObj = [
+            {
+              name: 'file',
+              description: '',
+              method: 'query',
+              required: true,
+              type: 'file',
+            },
+          ];
+          setDataSource(fileObj);
+          ApiModel.debugger.params = fileObj;
+        } else if (entity) {
+          handleEntityTable(entityName, entity.properties || entity, entityType, !!entityRequired);
+        }
+        return entityType === 'array' ? [entity.properties || entity] : entity.properties || entity;
+      }
+      return '';
+    }
+    return '';
+  };
+
+  const handleEntity = (entityData: any): any => {
+    let newEntity = {};
+    if (isArray(entityData)) {
+      newEntity = [handleEntity(entityData[0])];
+    } else if (isObject(entityData)) {
+      Object.keys(entityData).forEach((key) => {
+        const type = entityData[key].type;
+        if (type) {
+          if (type.includes('integer')) {
+            newEntity[key] = 0;
+          } else if (type === 'boolean') {
+            newEntity[key] = true;
+          } else if (type === 'object') {
+            newEntity[key] = {};
+          } else if (type === 'array') {
+            newEntity[key] = [];
+          } else {
+            newEntity[key] = '';
+          }
+        } else {
+          newEntity[key] = '';
+        }
+      });
+    }
+    return newEntity;
+  };
+
+  const getResult = (name: string, oldName: string = '') => {
+    if (name === oldName) {
+      // 禁止套娃
+      return [];
+    }
+    const entity = cloneDeep(ApiModel.components[name].properties);
+    Object.keys(entity).forEach((key) => {
+      const type = entity[key].type;
+      if ((entity[key].items && entity[key].items.$ref) || entity[key].$ref) {
+        const _ref = entity[key].$ref || entity[key].items.$ref;
+        const refName = _ref.split('/').pop();
+        if (type === 'array') {
+          entity[key] = [getResult(refName, name)];
+        } else {
+          entity[key] = getResult(refName, name);
+        }
+      } else if (type) {
+        if (type.includes('integer')) {
+          entity[key] = 0;
+        } else if (type === 'boolean') {
+          entity[key] = true;
+        } else {
+          entity[key] = '';
+        }
+      }
+    });
+    return entity;
+  };
+
+  const handleResponseParam = (name: any, oldName: string = ''): any[] => {
+    if (!ApiModel.components[name]) {
+      return [];
+    }
+
+    const entity = cloneDeep(ApiModel.components[name].properties);
+
+    const newArr: any[] = [];
+    if (name === oldName) {
+      return newArr;
+    }
+
+    Object.keys(entity).forEach((key) => {
+      const type = entity[key].type;
+      const obj: any = {
+        code: key,
+        description: entity[key].description,
+        type: type,
+      };
+
+      if ((entity[key].items && entity[key].items.$ref) || entity[key].$ref) {
+        const _ref = entity[key].$ref || entity[key].items.$ref;
+        const refName = _ref.split('/').pop();
+        if (refName) {
+          obj.type = refName;
+          obj.children = handleResponseParam(refName, name);
+        }
+      }
+      newArr.push(obj);
+    });
+    return newArr;
+  };
+
+  const handleResponse = () => {
+    const newArr: any[] = [];
+    Object.keys(ApiModel.swagger.responses).forEach((key) => {
+      const refUrl = ObjectFindValue('$ref', ApiModel.swagger.responses[key]);
+      const entityName = refUrl.split('/').pop();
+
+      newArr.push({
+        code: key,
+        description: ApiModel.swagger.responses[key].description,
+        schema: key !== '400' ? entityName : '',
+        entityName: entityName,
+        result: key !== '400' ? getResult(entityName) : {},
+      });
+    });
+    setResponseData(newArr);
+  };
+
+  useEffect(() => {
+    if (ApiModel.swagger.parameters) {
+      const params = ApiModel.swagger.parameters.map((item: any) => {
+        return {
+          name: item.name,
+          required: item.required,
+          type: item.schema.type,
+          description: item.description,
+          method: item.in,
+        };
+      });
+      ApiModel.debugger.params = params;
+      setDataSource(params);
+    }
+    if (ApiModel.swagger.requestBody) {
+      ApiModel.debugger.body = handleEntity(getEntity());
+    }
+
+    if (ApiModel.swagger.responses) {
+      handleResponse();
+    }
+  }, []);
+
+  return (
+    <div className={'platforms-api-swagger-content'}>
+      <div className={'swagger-content-title'}>{ApiModel.swagger.summary}</div>
+      <div className={'swagger-content-url'}>
+        <Input.Group compact>
+          <Button className={classNames('url-method', ApiModel.swagger.method)}>
+            {ApiModel.swagger.method ? ApiModel.swagger.method.toUpperCase() : ''}
+          </Button>
+          <Input
+            style={{
+              width: `calc(100% - ${ApiModel.swagger.method !== 'delete' ? '70px' : '80px'})`,
+            }}
+            value={ApiModel.swagger.url}
+            readOnly
+          />
+        </Input.Group>
+      </div>
+      <div className={'swagger-content-item swagger-content-request-type'}>
+        <span>请求数据类型</span>
+        <span>
+          {ApiModel.swagger.requestBody
+            ? getContent(ApiModel.swagger.requestBody.content)
+            : 'application/x-www-form-urlencoded'}
+        </span>
+        <span>响应数据类型</span>
+        <span>{`["/"]`}</span>
+      </div>
+      {ApiModel.swagger.description && (
+        <div className={'swagger-content-item'}>
+          <TitleComponent data={'接口描述'} />
+          <div> {ApiModel.swagger.description} </div>
+        </div>
+      )}
+      {ApiModel.swagger.requestBody &&
+        ApiModel.debugger.body &&
+        !!Object.keys(ApiModel.debugger.body).length && (
+          <div className={'swagger-content-item'}>
+            <TitleComponent data={'请求示例'} />
+            <div>
+              {
+                // @ts-ignore
+                <ReactJson
+                  displayObjectSize={false}
+                  displayDataTypes={false}
+                  name={false}
+                  src={ApiModel.debugger.body}
+                />
+              }
+            </div>
+          </div>
+        )}
+      <div className={'swagger-content-item'}>
+        <TitleComponent data={'请求参数'} />
+        <Table
+          pagination={false}
+          size={'small'}
+          columns={[
+            { title: '参数名', dataIndex: 'name' },
+            { title: '参数说明', dataIndex: 'description' },
+            { title: '请求类型', dataIndex: 'method' },
+            {
+              title: '是否必须',
+              dataIndex: 'required',
+              render: (text) => <span>{`${!!text}`}</span>,
+            },
+            { title: '参数类型', dataIndex: 'type' },
+          ]}
+          dataSource={dataSource}
+        />
+      </div>
+      <div className={'swagger-content-item'}>
+        <TitleComponent data={'响应状态'} />
+        <Table
+          pagination={false}
+          size={'small'}
+          columns={[
+            { title: '状态码', dataIndex: 'code' },
+            { title: '说明', dataIndex: 'description' },
+            { title: 'schema', dataIndex: 'schema' },
+          ]}
+          dataSource={responseData}
+        />
+      </div>
+      <div className={'swagger-content-item'}>
+        <Tabs>
+          {responseData
+            .filter((item) => item.code !== '400')
+            .map((item) => {
+              return (
+                <Tabs.TabPane key={item.code} tab={item.code}>
+                  <div>
+                    <div>
+                      <TitleComponent data={'响应参数'} style={{ margin: 0 }} />
+                      <Table
+                        pagination={false}
+                        size={'small'}
+                        columns={[
+                          { title: '参数名称', dataIndex: 'code' },
+                          { title: '参数说明', dataIndex: 'description' },
+                          { title: '类型', dataIndex: 'type' },
+                        ]}
+                        dataSource={handleResponseParam(item.entityName)}
+                      />
+                    </div>
+                    <div
+                      style={{
+                        padding: 1,
+                        border: '1px solid #f0f0f0',
+                        borderRadius: 2,
+                        marginTop: 12,
+                      }}
+                    >
+                      {
+                        // @ts-ignore
+                        <ReactJson
+                          displayObjectSize={false}
+                          displayDataTypes={false}
+                          name={false}
+                          src={item.result}
+                        />
+                      }
+                    </div>
+                  </div>
+                </Tabs.TabPane>
+              );
+            })}
+        </Tabs>
+      </div>
+    </div>
+  );
+});

+ 253 - 0
src/pages/system/Apply/Api/swagger-ui/debugging.tsx

@@ -0,0 +1,253 @@
+import { TitleComponent } from '@/components';
+import ReactJson from 'react-json-view';
+import { request } from 'umi';
+import MonacoEditor from 'react-monaco-editor';
+import { Button, Input } from 'antd';
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import { createSchemaField, FormProvider, observer } from '@formily/react';
+import { ApiModel } from '@/pages/system/Platforms/Api/base';
+import { createForm } from '@formily/core';
+import { ArrayTable, Editable, FormItem, Input as FormilyInput } from '@formily/antd';
+import type { ISchema } from '@formily/json-schema';
+import SystemConst from '@/utils/const';
+import classNames from 'classnames';
+
+export default observer(() => {
+  const [result, setResult] = useState({});
+  const [body, setBody] = useState({});
+
+  const editor: any = useRef(null);
+
+  useEffect(() => {
+    if (ApiModel.debugger.body && editor.current) {
+      const { editor: MEditor } = editor.current;
+      MEditor.setValue(JSON.stringify(ApiModel.debugger.body));
+      setTimeout(() => {
+        MEditor.getAction('editor.action.formatDocument').run();
+      }, 300);
+      // MEditor.trigger('anyString', 'editor.action.formatDocument');//自动格式化代码
+      MEditor.setValue(MEditor.getValue());
+    }
+  }, [ApiModel.debugger, editor.current]);
+
+  const SchemaField = createSchemaField({
+    components: {
+      FormItem,
+      Editable,
+      Input: FormilyInput,
+      ArrayTable,
+    },
+  });
+
+  const form = useMemo(
+    () =>
+      createForm({
+        validateFirst: true,
+      }),
+    [],
+  );
+
+  const onSearch = useCallback(async () => {
+    const formData: any = await form.submit();
+    console.log(formData);
+    let newUrl = ApiModel.swagger.url;
+    if (formData && formData.params && formData.params.length) {
+      const params = formData.params;
+      params.forEach((item: any) => {
+        if (newUrl.includes(`{${item.name}}`)) {
+          newUrl = newUrl.replace(`{${item.name}}`, item.values);
+        }
+      });
+      console.log(newUrl);
+    }
+
+    // 判断请求类型
+    const method = ApiModel.swagger.method && ApiModel.swagger.method.toUpperCase();
+    let options = {};
+    if (['POST', 'PUT', 'PATCH'].includes(method)) {
+      options = {
+        method,
+        data: body || {},
+      };
+    } else if (['GET', 'DELETE'].includes(method)) {
+      options = {
+        method,
+        params: body || {},
+      };
+    }
+
+    request(`/${SystemConst.API_BASE}${newUrl}`, options).then((resp) => {
+      if (resp.status === 200) {
+        setResult(resp);
+      } else {
+        resp
+          .clone()
+          .text()
+          .then((res: string) => {
+            if (res) {
+              setResult(JSON.parse(res));
+            } else {
+              resp
+                .clone()
+                .json()
+                .then((res2: any) => {
+                  setResult(res2);
+                });
+            }
+          });
+      }
+    });
+  }, [body]);
+
+  useEffect(() => {
+    if (form && ApiModel.debugger && ApiModel.debugger.params) {
+      const arr = ApiModel.debugger.params.map((item: any) => {
+        return {
+          name: item.name,
+          values: '',
+        };
+      });
+      form.setValues({ params: arr });
+    }
+  }, [form, ApiModel.debugger]);
+
+  const schema: ISchema = {
+    type: 'object',
+    properties: {
+      params: {
+        type: 'array',
+        'x-decorator': 'FormItem',
+        'x-component': 'ArrayTable',
+        'x-component-props': {
+          pagination: { pageSize: 10 },
+          scroll: { x: '100%' },
+        },
+        items: {
+          type: 'object',
+          properties: {
+            column1: {
+              type: 'void',
+              'x-component': 'ArrayTable.Column',
+              'x-component-props': { title: '参数名称' },
+              properties: {
+                name: {
+                  type: 'string',
+                  'x-decorator': 'FormItem',
+                  'x-component': 'Input',
+                  required: true,
+                },
+              },
+            },
+            column2: {
+              type: 'void',
+              'x-component': 'ArrayTable.Column',
+              'x-component-props': { title: '参数值' },
+              properties: {
+                values: {
+                  type: 'string',
+                  'x-decorator': 'FormItem',
+                  'x-component': 'Input',
+                  required: true,
+                },
+              },
+            },
+            column6: {
+              type: 'void',
+              'x-component': 'ArrayTable.Column',
+              'x-component-props': {
+                title: '操作',
+                dataIndex: 'operations',
+                width: 100,
+                fixed: 'right',
+                align: 'center',
+              },
+              properties: {
+                item: {
+                  type: 'void',
+                  'x-component': 'FormItem',
+                  properties: {
+                    remove: {
+                      type: 'void',
+                      'x-component': 'ArrayTable.Remove',
+                    },
+                  },
+                },
+              },
+            },
+          },
+        },
+        properties: {
+          add: {
+            type: 'void',
+            'x-component': 'ArrayTable.Addition',
+            title: '新增',
+          },
+        },
+      },
+    },
+  };
+
+  return (
+    <div className={'platforms-api-swagger-content'}>
+      <div className={'swagger-content-title'}>{ApiModel.swagger.summary}</div>
+      <div className={'swagger-content-url'}>
+        <Input.Group compact>
+          <Button className={classNames('url-method', ApiModel.swagger.method)}>
+            {ApiModel.swagger.method ? ApiModel.swagger.method.toUpperCase() : ''}
+          </Button>
+          <Input
+            style={{
+              width: `calc(100% - ${ApiModel.swagger.method !== 'delete' ? '140px' : '150px'})`,
+            }}
+            value={ApiModel.swagger.url}
+          />
+          <Button type="primary" onClick={onSearch}>
+            发送
+          </Button>
+        </Input.Group>
+      </div>
+      <div className={'swagger-content-item'}>
+        <TitleComponent data={'请求参数'} />
+        <div>
+          {ApiModel.debugger.params && (
+            <FormProvider form={form}>
+              <SchemaField schema={schema} />
+            </FormProvider>
+          )}
+          {ApiModel.debugger.body && (
+            <MonacoEditor
+              height={200}
+              language={'json'}
+              theme={'dark'}
+              ref={editor}
+              onChange={(value) => {
+                try {
+                  setBody(JSON.parse(value));
+                } catch (e) {
+                  console.warn(e);
+                }
+              }}
+              editorDidMount={(_editor) => {
+                _editor.getAction('editor.action.formatDocument').run();
+              }}
+            />
+          )}
+        </div>
+      </div>
+      <div className={'swagger-content-item'}>
+        <TitleComponent data={'响应内容'} />
+        <div style={{ border: '1px solid #f0f0f0', borderRadius: 2, padding: 1 }}>
+          {
+            // @ts-ignore
+            <ReactJson
+              displayObjectSize={false}
+              displayDataTypes={false}
+              name={false}
+              src={result}
+            />
+          }
+        </div>
+      </div>
+    </div>
+  );
+});

+ 33 - 0
src/pages/system/Apply/Api/swagger-ui/index.tsx

@@ -0,0 +1,33 @@
+import { Button, Tabs } from 'antd';
+import { ApiModel } from '@/pages/system/Platforms/Api/base';
+import Base from './base';
+import Debugger from './debugging';
+
+interface SwaggerProps {
+  showDebugger?: boolean;
+}
+
+export default (props: SwaggerProps) => {
+  return (
+    <div className={'platforms-api-swagger'}>
+      <Button
+        onClick={() => {
+          ApiModel.showTable = true;
+        }}
+        className={'platforms-api-swagger-back'}
+      >
+        返回
+      </Button>
+      <Tabs type="card">
+        <Tabs.TabPane tab={'文档'} key={1}>
+          <Base />
+        </Tabs.TabPane>
+        {props.showDebugger === true && (
+          <Tabs.TabPane tab={'调试'} key={2}>
+            <Debugger />
+          </Tabs.TabPane>
+        )}
+      </Tabs>
+    </div>
+  );
+};

+ 38 - 0
src/pages/system/Apply/Home/index.less

@@ -0,0 +1,38 @@
+.home {
+  padding: 20px;
+
+  h1 {
+    font-weight: 600;
+    font-size: 20px;
+  }
+
+  h2 {
+    font-weight: 600;
+    font-size: 18px;
+  }
+
+  .h2-text {
+    color: #999;
+  }
+
+  h3 {
+    margin-top: 10px;
+    font-weight: 600;
+    font-size: 16px;
+  }
+
+  .h3-text {
+    max-width: 530px;
+    margin-top: 3px;
+    color: #999;
+  }
+
+  p {
+    color: #666;
+  }
+
+  .div-border {
+    padding: 10px;
+    border-left: 10px solid #eee;
+  }
+}

Разница между файлами не показана из-за своего большого размера
+ 176 - 0
src/pages/system/Apply/Home/index.tsx


+ 8 - 5
src/pages/system/Apply/Save/index.tsx

@@ -581,7 +581,7 @@ const Save = () => {
       'x-component': 'Input',
       default: 'oauth2',
     },
-    'apiClient.authConfig.oAuth2.authorizationUrl': {
+    'apiClient.authConfig.oauth2.authorizationUrl': {
       type: 'string',
       title: '授权地址',
       'x-decorator': 'FormItem',
@@ -596,7 +596,7 @@ const Save = () => {
         placeholder: '请输入授权地址',
       },
     },
-    'apiClient.authConfig.oAuth2.redirectUri': {
+    'apiClient.authConfig.oauth2.redirectUri': {
       type: 'string',
       title: '回调地址',
       'x-decorator': 'FormItem',
@@ -611,7 +611,7 @@ const Save = () => {
         placeholder: '请输入回调地址',
       },
     },
-    'apiClient.authConfig.oAuth2.clientId': {
+    'apiClient.authConfig.oauth2.clientId': {
       type: 'string',
       title: 'appId',
       'x-decorator': 'FormItem',
@@ -626,7 +626,7 @@ const Save = () => {
         placeholder: '请输入appId',
       },
     },
-    'apiClient.authConfig.oAuth2.clientSecret': {
+    'apiClient.authConfig.oauth2.clientSecret': {
       type: 'string',
       title: 'appKey',
       'x-decorator': 'FormItem',
@@ -1519,10 +1519,13 @@ const Save = () => {
     setView(false);
     const params = new URLSearchParams(location.search);
     const item = params.get('id');
-    console.log(id);
+    // console.log(id);
     if (item) {
       setId(item);
     }
+    if (location && location.state) {
+      setView(location.state.view);
+    }
   }, [location]);
   return (
     <PageContainer>

+ 120 - 0
src/pages/system/Apply/View/index.tsx

@@ -0,0 +1,120 @@
+import { PageContainer } from '@ant-design/pro-layout';
+import { Button, Card, Col, Input, Popover, Row } from 'antd';
+import ApiPage from '../Api/base';
+import { useEffect, useState } from 'react';
+import { useLocation } from 'umi';
+import { service } from '@/pages/system/Platforms';
+import * as moment from 'moment';
+
+const defaultHeight = 50;
+
+export default () => {
+  const location = useLocation();
+
+  const [clientId, setClientId] = useState('');
+  const [secureKey, setSecureKey] = useState('');
+  const [sdkDetail, setSdkDetail] = useState<any>({});
+
+  const getDetail = async (id: string) => {
+    const resp = await service.getDetail(id);
+    if (resp.status === 200) {
+      setClientId(resp.result.id);
+      setSecureKey(resp.result.apiServer.secureKey);
+    }
+  };
+
+  const getSDKDetail = async () => {
+    const resp = await service.getSdk();
+    if (resp.status === 200) {
+      setSdkDetail(resp.result[0]);
+    }
+  };
+
+  const downLoad = (url: string) => {
+    if (url) {
+      const downNode = document.createElement('a');
+      downNode.href = url;
+      downNode.download = `${moment(new Date()).format('YYYY-MM-DD-HH-mm-ss')}.sdk`;
+      downNode.style.display = 'none';
+      downNode.setAttribute('target', '_blank');
+      document.body.appendChild(downNode);
+      downNode.click();
+      document.body.removeChild(downNode);
+    }
+  };
+
+  useEffect(() => {
+    const param = new URLSearchParams(location.search);
+    const code = param.get('code');
+    if (code) {
+      getDetail(code);
+    }
+  }, [location]);
+
+  useEffect(() => {
+    //  请求SDK下载地址
+    getSDKDetail();
+  }, []);
+
+  const downLoadJDK = (
+    <div>
+      <div
+        style={{
+          width: 500,
+          borderRadius: 2,
+          marginBottom: 12,
+        }}
+      >
+        <Input.TextArea value={sdkDetail?.dependency} rows={6} readOnly />
+      </div>
+      <Button
+        type={'primary'}
+        style={{ width: '100%' }}
+        onClick={() => {
+          downLoad(sdkDetail.sdk);
+        }}
+      >
+        jar下载
+      </Button>
+    </div>
+  );
+
+  return (
+    <PageContainer>
+      <Row gutter={[16, 16]}>
+        <Col span={24}>
+          <Row gutter={16}>
+            <Col span={12}>
+              <Card title="基本信息">
+                <div style={{ height: defaultHeight }}>
+                  <div>
+                    <span style={{ fontWeight: 'bold', fontSize: 16 }}>clientId: </span>
+                    {clientId}
+                  </div>
+                  <div>
+                    <span style={{ fontWeight: 'bold', fontSize: 16 }}>secureKey: </span>
+                    {secureKey}
+                  </div>
+                </div>
+              </Card>
+            </Col>
+            <Col span={12}>
+              <Card title="SDK下载">
+                <div style={{ height: defaultHeight }}>
+                  <Popover trigger="click" title={'POM依赖'} content={downLoadJDK}>
+                    <Button> Java </Button>
+                  </Popover>
+                </div>
+              </Card>
+            </Col>
+          </Row>
+        </Col>
+        <Col span={24}>
+          <Card title={'API文档'}>
+            <ApiPage type={'authorize'} showDebugger={true} isShowGranted={true} showHome={true} />
+          </Card>
+        </Col>
+      </Row>
+    </PageContainer>
+  );
+};

+ 91 - 1
src/pages/system/Apply/index.tsx

@@ -1,4 +1,4 @@
-import { PermissionButton, ProTableCard } from '@/components';
+import { AIcon, PermissionButton, ProTableCard } from '@/components';
 import ApplyCard from '@/components/ProTableCard/CardItems/applyCard';
 import SearchComponent from '@/components/SearchComponent';
 import useHistory from '@/hooks/route/useHistory';
@@ -7,6 +7,7 @@ import { onlyMessage } from '@/utils/util';
 import {
   DeleteOutlined,
   EditOutlined,
+  EyeOutlined,
   PlayCircleOutlined,
   PlusOutlined,
   StopOutlined,
@@ -45,6 +46,11 @@ const Apply = () => {
     });
   };
 
+  const isApiService = (params: any[]) => {
+    const res = params?.map((item) => item.value).includes('apiServer');
+    return res;
+  };
+
   const columns: ProColumns<any>[] = [
     {
       dataIndex: 'name',
@@ -129,6 +135,40 @@ const Apply = () => {
         >
           <EditOutlined />
         </PermissionButton>,
+        isApiService(record.integrationModes) ? (
+          <PermissionButton
+            key={'empowerment'}
+            type={'link'}
+            style={{ padding: 0 }}
+            isPermission={permission.empowerment}
+            tooltip={{
+              title: '赋权',
+            }}
+            onClick={() => {
+              const url = getMenuPathByCode('system/Apply/Api');
+              history.push(`${url}?code=${record.id}`);
+            }}
+          >
+            <AIcon type={'icon-fuquan'} />
+          </PermissionButton>
+        ) : null,
+        isApiService(record.integrationModes) ? (
+          <PermissionButton
+            key={'api'}
+            type={'link'}
+            style={{ padding: 0 }}
+            isPermission={permission.api}
+            tooltip={{
+              title: '查看API',
+            }}
+            onClick={() => {
+              const url = getMenuPathByCode('system/Apply/View');
+              history.push(`${url}?code=${record.id}`);
+            }}
+          >
+            <AIcon type={'icon-chakanAPI'} />
+          </PermissionButton>
+        ) : null,
         <PermissionButton
           isPermission={permission.action}
           key="action"
@@ -238,6 +278,20 @@ const Apply = () => {
         cardRender={(record) => (
           <ApplyCard
             {...record}
+            detail={
+              <PermissionButton
+                key={'view'}
+                type={'link'}
+                style={{ padding: 0, fontSize: 24, color: '#fff' }}
+                isPermission={permission.view}
+                onClick={() => {
+                  const url = getMenuPathByCode('system/Apply/Save');
+                  history.push(`${url}?id=${record.id}`, { view: true });
+                }}
+              >
+                <EyeOutlined />
+              </PermissionButton>
+            }
             actions={[
               <PermissionButton
                 isPermission={permission.update}
@@ -258,6 +312,42 @@ const Apply = () => {
                 <EditOutlined />
                 编辑
               </PermissionButton>,
+              isApiService(record.integrationModes) ? (
+                <PermissionButton
+                  key={'empowerment'}
+                  type={'link'}
+                  style={{ padding: 0 }}
+                  isPermission={permission.empowerment}
+                  tooltip={{
+                    title: '赋权',
+                  }}
+                  onClick={() => {
+                    const url = getMenuPathByCode('system/Apply/Api');
+                    history.push(`${url}?code=${record.id}`);
+                  }}
+                >
+                  <AIcon type={'icon-fuquan'} />
+                  赋权
+                </PermissionButton>
+              ) : null,
+              isApiService(record.integrationModes) ? (
+                <PermissionButton
+                  key={'api'}
+                  type={'link'}
+                  style={{ padding: 0 }}
+                  isPermission={permission.api}
+                  tooltip={{
+                    title: '查看API',
+                  }}
+                  onClick={() => {
+                    const url = getMenuPathByCode('system/Apply/View');
+                    history.push(`${url}?code=${record.id}`);
+                  }}
+                >
+                  <AIcon type={'icon-chakanAPI'} />
+                  查看API
+                </PermissionButton>
+              ) : null,
               <PermissionButton
                 isPermission={permission.action}
                 key="action"

+ 1 - 1
src/pages/system/Platforms/index.tsx

@@ -21,7 +21,7 @@ import { getMenuPathByCode } from '@/utils/menu';
 import { useDomFullHeight } from '@/hooks';
 import { onlyMessage } from '@/utils/util';
 
-export const service = new Service('api-client');
+export const service = new Service('application');
 
 export default () => {
   const actionRef = useRef<ActionType>();

+ 8 - 8
src/pages/system/Platforms/service.ts

@@ -9,7 +9,7 @@ class Service extends BaseService<platformsType> {
       params,
     });
 
-  getDetail = (id: string) => request(`${this.uri}/${id}/detail`, { method: 'GET' });
+  getDetail = (id: string) => request(`${this.uri}/${id}`, { method: 'GET' });
 
   edit = (data: any) => request(`${this.uri}/${data.id}`, { method: 'PUT', data });
 
@@ -46,32 +46,32 @@ class Service extends BaseService<platformsType> {
    * @param data
    */
   saveApiGrant = (id: string, data: any) =>
-    request(`/${SystemConst.API_BASE}/api-client/${id}/grant`, { method: 'POST', data });
+    request(`/${SystemConst.API_BASE}/application/${id}/grant`, { method: 'POST', data });
 
   addApiGrant = (id: string, data: any) =>
-    request(`/${SystemConst.API_BASE}/api-client/${id}/grant/_add`, { method: 'POST', data });
+    request(`/${SystemConst.API_BASE}/application/${id}/grant/_add`, { method: 'POST', data });
 
   removeApiGrant = (id: string, data: any) =>
-    request(`/${SystemConst.API_BASE}/api-client/${id}/grant/_delete`, { method: 'POST', data });
+    request(`/${SystemConst.API_BASE}/application/${id}/grant/_delete`, { method: 'POST', data });
 
   /**
    * 获取已授权的接口ID
    * @param id 第三方平台的ID
    */
   getApiGranted = (id: string) =>
-    request(`/${SystemConst.API_BASE}/api-client/${id}/granted`, { method: 'GET' });
+    request(`/${SystemConst.API_BASE}/application/${id}/granted`, { method: 'GET' });
 
   /**
    * 获取可授权的接口ID
    */
   apiOperations = () =>
-    request(`/${SystemConst.API_BASE}/api-client/operations`, { method: 'GET' });
+    request(`/${SystemConst.API_BASE}/application/operations`, { method: 'GET' });
 
   /**
    * 新增可授权的接口ID
    */
   apiOperationsAdd = (data?: any) =>
-    request(`/${SystemConst.API_BASE}/api-client/operations/_batch`, {
+    request(`/${SystemConst.API_BASE}/application/operations/_batch`, {
       method: 'PATCH',
       data: data || [],
     });
@@ -80,7 +80,7 @@ class Service extends BaseService<platformsType> {
    * 删除可授权的接口ID
    */
   apiOperationsRemove = (data?: any) =>
-    request(`/${SystemConst.API_BASE}/api-client/operations/_batch`, {
+    request(`/${SystemConst.API_BASE}/application/operations/_batch`, {
       method: 'DELETE',
       data: data || [],
     });

+ 2 - 0
src/utils/menu/router.ts

@@ -131,6 +131,8 @@ export enum MENUS_CODE {
   'system/Platforms/View' = 'system/Platforms/View',
   'system/Platforms/Setting' = 'system/Platforms/Setting',
   'system/Apply' = 'system/Apply',
+  'system/Apply/Api' = 'system/Apply/Api',
+  'system/Apply/View' = 'system/Apply/View',
 }
 
 export type MENUS_CODE_TYPE = keyof typeof MENUS_CODE | string;