Prechádzať zdrojové kódy

feat(datasource): System->DataSource

Lind 4 rokov pred
rodič
commit
921d056180

+ 6 - 0
config/routes.ts

@@ -69,6 +69,12 @@
         icon: 'smile',
         icon: 'smile',
         component: './system/Tenant',
         component: './system/Tenant',
       },
       },
+      {
+        path: '/system/datasource',
+        name: 'datasource',
+        icon: 'smile',
+        component: './system/DataSource',
+      },
     ],
     ],
   },
   },
   {
   {

+ 50 - 27
src/locales/en-US/pages.ts

@@ -1,5 +1,3 @@
-import Firmware from '@/pages/device/Firmware';
-
 export default {
 export default {
   'pages.layouts.userLayout.title': 'Jetlinks is an open source Internet of Things basic platform',
   'pages.layouts.userLayout.title': 'Jetlinks is an open source Internet of Things basic platform',
   'pages.login.accountLogin.tab': 'Account Login',
   'pages.login.accountLogin.tab': 'Account Login',
@@ -396,18 +394,24 @@ export default {
   'pages.simulator.device.add.operationInformation.networkCard.tip':
   'pages.simulator.device.add.operationInformation.networkCard.tip':
     'This is the prompt for binding the network card',
     'This is the prompt for binding the network card',
   'pages.simulator.device.add.operationInformation.total': 'Total number of simulations',
   'pages.simulator.device.add.operationInformation.total': 'Total number of simulations',
-  'pages.simulator.device.add.operationInformation.total.tip': 'This is a hint for the simulated total',
+  'pages.simulator.device.add.operationInformation.total.tip':
+    'This is a hint for the simulated total',
   'pages.simulator.device.add.operationInformation.index': 'Starting index',
   'pages.simulator.device.add.operationInformation.index': 'Starting index',
-  'pages.simulator.device.add.operationInformation.index.tip': 'This is the tip of the starting engine',
+  'pages.simulator.device.add.operationInformation.index.tip':
+    'This is the tip of the starting engine',
   'pages.simulator.device.add.operationInformation.concurrency': 'Concurrency',
   'pages.simulator.device.add.operationInformation.concurrency': 'Concurrency',
-  'pages.simulator.device.add.operationInformation.concurrency.tip': 'This is a reminder of the number of concurrent',
+  'pages.simulator.device.add.operationInformation.concurrency.tip':
+    'This is a reminder of the number of concurrent',
   'pages.simulator.device.add.operationInformation.otherFunctions': 'Other functions',
   'pages.simulator.device.add.operationInformation.otherFunctions': 'Other functions',
-  'pages.simulator.device.add.operationInformation.otherFunctions.tip': 'This is a hint for other functions',
-  'pages.simulator.device.add.operationInformation.otherFunctions.simulationData': 'Simulation data reporting',
+  'pages.simulator.device.add.operationInformation.otherFunctions.tip':
+    'This is a hint for other functions',
+  'pages.simulator.device.add.operationInformation.otherFunctions.simulationData':
+    'Simulation data reporting',
   'pages.simulator.device.add.operationInformation.otherFunctions.simulationData.name': 'Name',
   'pages.simulator.device.add.operationInformation.otherFunctions.simulationData.name': 'Name',
   'pages.simulator.device.add.operationInformation.otherFunctions.simulationData.name.tip':
   'pages.simulator.device.add.operationInformation.otherFunctions.simulationData.name.tip':
     'This is a hint of the name',
     'This is a hint of the name',
-  'pages.simulator.device.add.operationInformation.otherFunctions.simulationData.function': 'function',
+  'pages.simulator.device.add.operationInformation.otherFunctions.simulationData.function':
+    'function',
   'pages.simulator.device.add.operationInformation.otherFunctions.simulationData.function.tip':
   'pages.simulator.device.add.operationInformation.otherFunctions.simulationData.function.tip':
     'This is a hint of the function',
     'This is a hint of the function',
   'pages.simulator.device.add.operationInformation.otherFunctions.simulationData.scriptingLanguage':
   'pages.simulator.device.add.operationInformation.otherFunctions.simulationData.scriptingLanguage':
@@ -443,21 +447,26 @@ export default {
   'pages.cloud.alibabaCloud.add': 'Add product',
   'pages.cloud.alibabaCloud.add': 'Add product',
   'pages.cloud.alibabaCloud.add.codecProtocol': 'Codec protocol',
   'pages.cloud.alibabaCloud.add.codecProtocol': 'Codec protocol',
   'pages.cloud.alibabaCloud.add.explain': 'explain',
   'pages.cloud.alibabaCloud.add.explain': 'explain',
-  'pages.cloud.alibabaCloud.add.certificationInformationConfiguration': 'Certification information configuration',
+  'pages.cloud.alibabaCloud.add.certificationInformationConfiguration':
+    'Certification information configuration',
   'pages.cloud.alibabaCloud.add.certificationInformationConfiguration.zoneID': 'Zone ID',
   'pages.cloud.alibabaCloud.add.certificationInformationConfiguration.zoneID': 'Zone ID',
-  'pages.cloud.alibabaCloud.add.certificationInformationConfiguration.tip': 'Regions and Availability Zones',
+  'pages.cloud.alibabaCloud.add.certificationInformationConfiguration.tip':
+    'Regions and Availability Zones',
   'pages.cloud.alibabaCloud.add.certificationInformationConfiguration.apiInterfaceAddress':
   'pages.cloud.alibabaCloud.add.certificationInformationConfiguration.apiInterfaceAddress':
     'API interface address',
     'API interface address',
   'pages.cloud.alibabaCloud.add.certificationInformationConfiguration.authenticationInterfaceAddress':
   'pages.cloud.alibabaCloud.add.certificationInformationConfiguration.authenticationInterfaceAddress':
     'Authentication interface address',
     'Authentication interface address',
   'pages.cloud.alibabaCloud.add.bridgeConfiguration': 'Bridge configuration',
   'pages.cloud.alibabaCloud.add.bridgeConfiguration': 'Bridge configuration',
   'pages.cloud.alibabaCloud.add.bridgeConfiguration.localServiceID': 'Local service ID',
   'pages.cloud.alibabaCloud.add.bridgeConfiguration.localServiceID': 'Local service ID',
-  'pages.cloud.alibabaCloud.add.bridgeConfiguration.http2InterfaceAddress': 'HTTP2 interface address',
+  'pages.cloud.alibabaCloud.add.bridgeConfiguration.http2InterfaceAddress':
+    'HTTP2 interface address',
 
 
   // 视频网关-基本配置
   // 视频网关-基本配置
   'pages.videoGateway.basicConfiguration': 'basic configuration',
   'pages.videoGateway.basicConfiguration': 'basic configuration',
-  'pages.videoGateway.basicConfiguration.streamingMediaServiceConfiguration': 'Streaming media service configuration',
-  'pages.videoGateway.basicConfiguration.streamingMediaServiceConfiguration.name': 'Streaming media name',
+  'pages.videoGateway.basicConfiguration.streamingMediaServiceConfiguration':
+    'Streaming media service configuration',
+  'pages.videoGateway.basicConfiguration.streamingMediaServiceConfiguration.name':
+    'Streaming media name',
   'pages.videoGateway.basicConfiguration.streamingMediaServiceConfiguration.serviceProvider':
   'pages.videoGateway.basicConfiguration.streamingMediaServiceConfiguration.serviceProvider':
     'Service provider',
     'Service provider',
   'pages.videoGateway.basicConfiguration.streamingMediaServiceConfiguration.publicHost':
   'pages.videoGateway.basicConfiguration.streamingMediaServiceConfiguration.publicHost':
@@ -482,28 +491,36 @@ export default {
   'pages.videoGateway.basicConfiguration.GB.name': 'Signaling name',
   'pages.videoGateway.basicConfiguration.GB.name': 'Signaling name',
   'pages.videoGateway.basicConfiguration.GB.name.placeholder': 'Please enter the signaling name',
   'pages.videoGateway.basicConfiguration.GB.name.placeholder': 'Please enter the signaling name',
   'pages.videoGateway.basicConfiguration.GB.relatedProducts': 'related products',
   'pages.videoGateway.basicConfiguration.GB.relatedProducts': 'related products',
-  'pages.videoGateway.basicConfiguration.GB.relatedProducts.placeholder': 'Please select related products',
-  'pages.videoGateway.basicConfiguration.GB.SIPId.placeholder': 'Please enter the signaling SIP  ID',
+  'pages.videoGateway.basicConfiguration.GB.relatedProducts.placeholder':
+    'Please select related products',
+  'pages.videoGateway.basicConfiguration.GB.SIPId.placeholder':
+    'Please enter the signaling SIP  ID',
   'pages.videoGateway.basicConfiguration.GB.SIPRegion': 'SIP Region',
   'pages.videoGateway.basicConfiguration.GB.SIPRegion': 'SIP Region',
   'pages.videoGateway.basicConfiguration.GB.SIPRegion.placeholder': '',
   'pages.videoGateway.basicConfiguration.GB.SIPRegion.placeholder': '',
-  'pages.videoGateway.basicConfiguration.GB.SIPHost.placeholder': 'Please enter the signaling SIP domain',
+  'pages.videoGateway.basicConfiguration.GB.SIPHost.placeholder':
+    'Please enter the signaling SIP domain',
   'pages.videoGateway.basicConfiguration.GB.publicNetworkHost': 'Public network host',
   'pages.videoGateway.basicConfiguration.GB.publicNetworkHost': 'Public network host',
-  'pages.videoGateway.basicConfiguration.GB.publicNetworkHost.placeholder': 'Please enter the signaling public network Host',
+  'pages.videoGateway.basicConfiguration.GB.publicNetworkHost.placeholder':
+    'Please enter the signaling public network Host',
   'pages.videoGateway.basicConfiguration.GB.accessCode': 'Access code',
   'pages.videoGateway.basicConfiguration.GB.accessCode': 'Access code',
-  'pages.videoGateway.basicConfiguration.GB.accessCode.tip': 'Please enter the signaling access password',
+  'pages.videoGateway.basicConfiguration.GB.accessCode.tip':
+    'Please enter the signaling access password',
   'pages.videoGateway.basicConfiguration.GB.port': 'Port',
   'pages.videoGateway.basicConfiguration.GB.port': 'Port',
   'pages.videoGateway.basicConfiguration.GB.port.placeholder': 'Port',
   'pages.videoGateway.basicConfiguration.GB.port.placeholder': 'Port',
   'pages.videoGateway.basicConfiguration.GB.publicNetworkPort': 'public network port',
   'pages.videoGateway.basicConfiguration.GB.publicNetworkPort': 'public network port',
-  'pages.videoGateway.basicConfiguration.GB.publicNetworkPort.placeholder': 'Please enter the signaling port',
+  'pages.videoGateway.basicConfiguration.GB.publicNetworkPort.placeholder':
+    'Please enter the signaling port',
   'pages.videoGateway.basicConfiguration.GB.characterSet': 'Character Set',
   'pages.videoGateway.basicConfiguration.GB.characterSet': 'Character Set',
   'pages.videoGateway.basicConfiguration.GB.explain': 'explain',
   'pages.videoGateway.basicConfiguration.GB.explain': 'explain',
-  'pages.videoGateway.basicConfiguration.GB.explain.placeholder': 'Please enter at least five characters',
+  'pages.videoGateway.basicConfiguration.GB.explain.placeholder':
+    'Please enter at least five characters',
 
 
   // 视频网关-视频设备
   // 视频网关-视频设备
   'pages.videoGateway.videoEquipment': 'Video equipment',
   'pages.videoGateway.videoEquipment': 'Video equipment',
   'pages.videoGateway.videoEquipment.name': 'Equipment name',
   'pages.videoGateway.videoEquipment.name': 'Equipment name',
   'pages.videoGateway.videoEquipment.name.tip': 'Please enter the device name',
   'pages.videoGateway.videoEquipment.name.tip': 'Please enter the device name',
-  'pages.videoGateway.videoEquipment.nationalStandardEquipmentNumber': 'National standard equipment number',
+  'pages.videoGateway.videoEquipment.nationalStandardEquipmentNumber':
+    'National standard equipment number',
   'pages.videoGateway.videoEquipment.signalingTransmission': 'Signaling transmission',
   'pages.videoGateway.videoEquipment.signalingTransmission': 'Signaling transmission',
   'pages.videoGateway.videoEquipment.streamingMode': 'Streaming mode',
   'pages.videoGateway.videoEquipment.streamingMode': 'Streaming mode',
   'pages.videoGateway.videoEquipment.numberOfChannels': 'Number of channels',
   'pages.videoGateway.videoEquipment.numberOfChannels': 'Number of channels',
@@ -517,7 +534,8 @@ export default {
 
 
   // 视频网关-视频设备-通道列表
   // 视频网关-视频设备-通道列表
   'pages.videoGateway.videoEquipment.channelList': 'Channel list',
   'pages.videoGateway.videoEquipment.channelList': 'Channel list',
-  'pages.videoGateway.videoEquipment.channelList.channelNationalStandard': 'Channel national standard',
+  'pages.videoGateway.videoEquipment.channelList.channelNationalStandard':
+    'Channel national standard',
   'pages.videoGateway.videoEquipment.channelList.name': 'Channel name',
   'pages.videoGateway.videoEquipment.channelList.name': 'Channel name',
   'pages.videoGateway.videoEquipment.channelList.manufacturer': 'Manufacturer',
   'pages.videoGateway.videoEquipment.channelList.manufacturer': 'Manufacturer',
   'pages.videoGateway.videoEquipment.channelList.installationAddress': 'Installation address',
   'pages.videoGateway.videoEquipment.channelList.installationAddress': 'Installation address',
@@ -543,13 +561,16 @@ export default {
   'pages.videoGateway.internationalCascade.explain': 'Explain',
   'pages.videoGateway.internationalCascade.explain': 'Explain',
   'pages.videoGateway.internationalCascade.add': 'Add national standard cascade',
   'pages.videoGateway.internationalCascade.add': 'Add national standard cascade',
   'pages.videoGateway.internationalCascade.add.cascadeId': 'Cascade ID',
   'pages.videoGateway.internationalCascade.add.cascadeId': 'Cascade ID',
-  'pages.videoGateway.internationalCascade.add.cascadeId.placeholder': 'Please enter the cascade ID',
+  'pages.videoGateway.internationalCascade.add.cascadeId.placeholder':
+    'Please enter the cascade ID',
   'pages.videoGateway.internationalCascade.add.cascadeName': 'Cascade name',
   'pages.videoGateway.internationalCascade.add.cascadeName': 'Cascade name',
-  'pages.videoGateway.internationalCascade.add.cascadeName.placeholder': 'Please enter a cascade name',
+  'pages.videoGateway.internationalCascade.add.cascadeName.placeholder':
+    'Please enter a cascade name',
   'pages.videoGateway.internationalCascade.add.streamingService': 'Please enter streaming service',
   'pages.videoGateway.internationalCascade.add.streamingService': 'Please enter streaming service',
   'pages.videoGateway.internationalCascade.add.streamingService.placeholder': '',
   'pages.videoGateway.internationalCascade.add.streamingService.placeholder': '',
   'pages.videoGateway.internationalCascade.add.proxyVideoStreaming': 'Proxy video streaming',
   'pages.videoGateway.internationalCascade.add.proxyVideoStreaming': 'Proxy video streaming',
-  'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration': 'Signaling service configuration',
+  'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration':
+    'Signaling service configuration',
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.clusterNodeId':
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.clusterNodeId':
     'Cluster node ID',
     'Cluster node ID',
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.clusterNodeId.placeholder':
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.clusterNodeId.placeholder':
@@ -559,7 +580,8 @@ export default {
     'Please enter a name',
     'Please enter a name',
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.SIPIp.placeholder':
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.SIPIp.placeholder':
     'Please enter SIP ID',
     'Please enter SIP ID',
-  'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.SIPRegion': 'SIP Region',
+  'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.SIPRegion':
+    'SIP Region',
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.SIPRegion.placeholder':
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.SIPRegion.placeholder':
     'Please enter the SIP domain',
     'Please enter the SIP domain',
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.SIPHost.placeholder':
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.SIPHost.placeholder':
@@ -585,7 +607,8 @@ export default {
     'Access code',
     'Access code',
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.accessCode.placeholder':
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.accessCode.placeholder':
     'Please enter the access code',
     'Please enter the access code',
-  'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.manufacturer': 'Manufacturer',
+  'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.manufacturer':
+    'Manufacturer',
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.manufacturer.placeholder':
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.manufacturer.placeholder':
     'Please enter the manufacturer',
     'Please enter the manufacturer',
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.model': 'Model',
   'pages.videoGateway.internationalCascade.add.signalingServiceConfiguration.model': 'Model',

+ 17 - 17
src/pages/device/Instance/index.tsx

@@ -51,24 +51,24 @@ const Instance = () => {
     },
     },
     {
     {
       title: intl.formatMessage({
       title: intl.formatMessage({
-        id:'pages.device.instance.equipmentName',
-        defaultMessage:'设备名称',
+        id: 'pages.device.instance.equipmentName',
+        defaultMessage: '设备名称',
       }),
       }),
       dataIndex: 'name',
       dataIndex: 'name',
       ellipsis: true,
       ellipsis: true,
     },
     },
     {
     {
       title: intl.formatMessage({
       title: intl.formatMessage({
-        id:'pages.device.instance.productName',
-        defaultMessage:'产品名称',
+        id: 'pages.device.instance.productName',
+        defaultMessage: '产品名称',
       }),
       }),
       dataIndex: 'productName',
       dataIndex: 'productName',
       ellipsis: true,
       ellipsis: true,
     },
     },
     {
     {
       title: intl.formatMessage({
       title: intl.formatMessage({
-        id:'pages.device.instance.registrationTime',
-        defaultMessage:'注册时间',
+        id: 'pages.device.instance.registrationTime',
+        defaultMessage: '注册时间',
       }),
       }),
       dataIndex: 'registryTime',
       dataIndex: 'registryTime',
       width: '200px',
       width: '200px',
@@ -77,8 +77,8 @@ const Instance = () => {
     },
     },
     {
     {
       title: intl.formatMessage({
       title: intl.formatMessage({
-        id:'pages.device.instance.status',
-        defaultMessage:'状态',
+        id: 'pages.device.instance.status',
+        defaultMessage: '状态',
       }),
       }),
       dataIndex: 'state',
       dataIndex: 'state',
       width: '90px',
       width: '90px',
@@ -87,22 +87,22 @@ const Instance = () => {
       filters: [
       filters: [
         {
         {
           text: intl.formatMessage({
           text: intl.formatMessage({
-            id:'pages.device.instance.status.notEnabled',
-            defaultMessage:'未启用',
+            id: 'pages.device.instance.status.notEnabled',
+            defaultMessage: '未启用',
           }),
           }),
           value: 'notActive',
           value: 'notActive',
         },
         },
         {
         {
           text: intl.formatMessage({
           text: intl.formatMessage({
-            id:'pages.device.instance.status.offLine',
-            defaultMessage:'离线',
+            id: 'pages.device.instance.status.offLine',
+            defaultMessage: '离线',
           }),
           }),
           value: 'offline',
           value: 'offline',
         },
         },
         {
         {
           text: intl.formatMessage({
           text: intl.formatMessage({
-            id:'pages.device.instance.status.onLine',
-            defaultMessage:'在线',
+            id: 'pages.device.instance.status.onLine',
+            defaultMessage: '在线',
           }),
           }),
           value: 'online',
           value: 'online',
         },
         },
@@ -110,9 +110,9 @@ const Instance = () => {
       filterMultiple: false,
       filterMultiple: false,
     },
     },
     {
     {
-      title:intl.formatMessage({
-        id:'pages.device.instance.explain',
-        defaultMessage:'说明',
+      title: intl.formatMessage({
+        id: 'pages.device.instance.explain',
+        defaultMessage: '说明',
       }),
       }),
       dataIndex: 'describe',
       dataIndex: 'describe',
       width: '15%',
       width: '15%',

+ 247 - 0
src/pages/system/DataSource/index.tsx

@@ -0,0 +1,247 @@
+import { PageContainer } from '@ant-design/pro-layout';
+import BaseCrud from '@/components/BaseCrud';
+import type { ProColumns } from '@jetlinks/pro-table';
+import { CurdModel } from '@/components/BaseCrud/model';
+import { message, Popconfirm, Tooltip } from 'antd';
+import { CloseCircleOutlined, EditOutlined, PlayCircleOutlined } from '@ant-design/icons';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import type { ActionType } from '@jetlinks/pro-table';
+import { useEffect, useRef } from 'react';
+import type { ISchema } from '@formily/json-schema';
+import { model } from '@formily/reactive';
+import Service from '@/pages/system/DataSource/service';
+import { from, mergeMap, toArray } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+export const service = new Service('datasource/config');
+export const DataSourceModel = model<{
+  types: Partial<DataSourceType>[];
+}>({
+  types: [],
+});
+const DataSource = () => {
+  const intl = useIntl();
+  const actionRef = useRef<ActionType>();
+
+  useEffect(() => {
+    service
+      .getType()
+      .pipe(
+        mergeMap((data: DataSourceType[]) => from(data)),
+        map((i: DataSourceType) => ({ label: i.id, value: i.name })),
+        toArray(),
+      )
+      .subscribe((data: Partial<DataSourceType>[]) => {
+        DataSourceModel.types = data;
+      });
+  }, []);
+
+  const columns: ProColumns<DataSourceItem>[] = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+    },
+    {
+      title: '名称',
+      dataIndex: 'name',
+    },
+    {
+      title: '类型',
+      dataIndex: 'typeId',
+    },
+    {
+      title: '说明',
+      dataIndex: 'description',
+    },
+    {
+      title: '状态',
+      dataIndex: 'state',
+      render: (value: any) => value.text,
+    },
+    {
+      title: '操作',
+      valueType: 'option',
+      align: 'center',
+      width: 200,
+
+      render: (text, record) => [
+        <a key="editable" onClick={() => CurdModel.update(record)}>
+          <Tooltip
+            title={intl.formatMessage({
+              id: 'pages.data.option.edit',
+              defaultMessage: '编辑',
+            })}
+          >
+            <EditOutlined />
+          </Tooltip>
+        </a>,
+        <a href={record.id} target="_blank" rel="noopener noreferrer" key="view">
+          <Popconfirm
+            title={intl.formatMessage({
+              id: 'pages.data.option.disable.tips',
+              defaultMessage: '确认禁用?',
+            })}
+            onConfirm={async () => {
+              await service.update({
+                id: record.id,
+                // state: record.state ? 0 : 1,
+              });
+              message.success(
+                intl.formatMessage({
+                  id: 'pages.data.option.success',
+                  defaultMessage: '操作成功!',
+                }),
+              );
+              actionRef.current?.reload();
+            }}
+          >
+            <Tooltip
+              title={intl.formatMessage({
+                id: `pages.data.option.${record.state ? 'disable' : 'enable'}`,
+                defaultMessage: record.state ? '禁用' : '启用',
+              })}
+            >
+              {record.state ? <CloseCircleOutlined /> : <PlayCircleOutlined />}
+            </Tooltip>
+          </Popconfirm>
+        </a>,
+      ],
+    },
+  ];
+
+  const schema: ISchema = {
+    type: 'object',
+    properties: {
+      grid: {
+        type: 'void',
+        'x-component': 'FormGrid',
+        'x-component-props': {
+          minColumns: [1],
+          maxColumns: [2],
+        },
+        properties: {
+          name: {
+            title: '名称',
+            'x-component': 'Input',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 1,
+              labelCol: 4,
+            },
+          },
+          typeId: {
+            title: '类型',
+            'x-component': 'Select',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 1,
+              labelCol: 4,
+            },
+            enum: [],
+          },
+          'shareConfig.adminUrl': {
+            title: '管理地址',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 1,
+              labelCol: 4,
+            },
+            required: true,
+            default: 'http://localhost:15672',
+            'x-visible': false,
+            'x-component': 'Input',
+          },
+          'shareConfig.addresses': {
+            title: '链接地址',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 1,
+              labelCol: 4,
+            },
+            required: true,
+            default: 'localhost:5672',
+            'x-visible': false,
+            'x-component': 'Input',
+          },
+          'shareConfig.virtualHost': {
+            title: '虚拟域',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 1,
+              labelCol: 4,
+            },
+            required: true,
+            'x-visible': false,
+            default: '/',
+            'x-component': 'Input',
+          },
+          'shareConfig.username': {
+            title: '用户名',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 1,
+              labelCol: 4,
+            },
+            'x-visible': false,
+
+            'x-component': 'Input',
+          },
+          'shareConfig.password': {
+            title: '密码',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 1,
+              labelCol: 4,
+            },
+            'x-visible': false,
+
+            'x-component': 'Input',
+          },
+          'shareConfig.bootstrapServers': {
+            title: '地址',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 2,
+              labelCol: 2,
+              wrapperCol: 20,
+            },
+            'x-visible': false,
+            'x-component': 'Select',
+            'x-component-props': {
+              mode: 'tags',
+            },
+          },
+          description: {
+            title: '说明',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 2,
+              labelCol: 2,
+              wrapperCol: 20,
+            },
+            'x-component': 'Input.TextArea',
+            'x-component-props': {
+              rows: 4,
+            },
+          },
+        },
+      },
+    },
+  };
+
+  return (
+    <PageContainer>
+      <BaseCrud<DataSourceItem>
+        modelConfig={{
+          width: 800,
+        }}
+        columns={columns}
+        service={service}
+        title="数据源管理"
+        schema={schema}
+        actionRef={actionRef}
+      />
+    </PageContainer>
+  );
+};
+export default DataSource;

+ 19 - 0
src/pages/system/DataSource/service.ts

@@ -0,0 +1,19 @@
+import BaseService from '@/utils/BaseService';
+import { request } from '@@/plugin-request/request';
+import { defer, from } from 'rxjs';
+import { filter, map } from 'rxjs/operators';
+
+class Service extends BaseService<DataSourceItem> {
+  changeStatus = (id: string, status: 'disable' | 'enable') =>
+    request(`${this.uri}/${id}/_${status}`, { method: 'PUT' });
+
+  getType = () =>
+    defer(() =>
+      from(request(`${this.uri}/types`, { method: 'GET' })).pipe(
+        filter((resp) => resp.status === 200),
+        map((resp) => resp.result),
+      ),
+    );
+}
+
+export default Service;

+ 22 - 0
src/pages/system/DataSource/typings.d.ts

@@ -0,0 +1,22 @@
+type DataSourceItem = {
+  id: string;
+  name: string;
+  shareCluster: true;
+  shareConfig: Record<string, any>;
+  state: {
+    text: string;
+    value: string;
+  };
+  typeId: string;
+  createTime: number;
+  creatorId: string;
+  creatorName: string;
+  description: string;
+};
+
+type DataSourceType = {
+  label: string;
+  value: string;
+  id: string;
+  name: string;
+};