Pārlūkot izejas kodu

fix: 4.18修改bug

sun-chaochao 3 gadi atpakaļ
vecāks
revīzija
f7dbdcfbeb

BIN
public/images/protocol.png


+ 15 - 13
src/components/BaseCrud/index.tsx

@@ -1,5 +1,5 @@
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { useIntl } from '@@/plugin-locale/localeExports';
-import { Button } from 'antd';
+import { Button, Tooltip } from 'antd';
 import type { ActionType, ProColumns, RequestData } from '@jetlinks/pro-table';
 import type { ActionType, ProColumns, RequestData } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 
 
@@ -121,18 +121,20 @@ const BaseCrud = <T extends Record<string, any>>(props: Props<T>) => {
         }
         }
         dateFormatter="string"
         dateFormatter="string"
         headerTitle={
         headerTitle={
-          <Button
-            disabled={props.disableAdd}
-            onClick={CurdModel.add}
-            key="button"
-            icon={<PlusOutlined />}
-            type="primary"
-          >
-            {intl.formatMessage({
-              id: 'pages.data.option.add',
-              defaultMessage: '新增',
-            })}
-          </Button>
+          <Tooltip title={props.disableAdd ? '暂无权限,请联系管理员' : ''}>
+            <Button
+              disabled={props.disableAdd}
+              onClick={CurdModel.add}
+              key="button"
+              icon={<PlusOutlined />}
+              type="primary"
+            >
+              {intl.formatMessage({
+                id: 'pages.data.option.add',
+                defaultMessage: '新增',
+              })}
+            </Button>
+          </Tooltip>
         }
         }
         defaultParams={defaultParams}
         defaultParams={defaultParams}
         // toolBarRender={() =>
         // toolBarRender={() =>

+ 10 - 2
src/components/ProTableCard/CardItems/AccessConfig/index.less

@@ -57,7 +57,7 @@
       .container {
       .container {
         display: flex;
         display: flex;
         width: 100%;
         width: 100%;
-        min-height: 50px;
+        min-height: 60px;
         margin-top: 10px;
         margin-top: 10px;
 
 
         .server,
         .server,
@@ -65,9 +65,15 @@
           width: calc(50% - 20px);
           width: calc(50% - 20px);
           margin-right: 10px;
           margin-right: 10px;
 
 
+          .serverItem {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: center;
+          }
+
           .subTitle {
           .subTitle {
             width: 100%;
             width: 100%;
-            margin-bottom: 5px;
             overflow: hidden;
             overflow: hidden;
             color: rgba(0, 0, 0, 0.75);
             color: rgba(0, 0, 0, 0.75);
             font-size: 12px;
             font-size: 12px;
@@ -79,6 +85,8 @@
             width: 100%;
             width: 100%;
             height: 20px;
             height: 20px;
             overflow: hidden;
             overflow: hidden;
+            color: #666;
+            font-size: 12px;
             white-space: nowrap;
             white-space: nowrap;
             text-overflow: ellipsis;
             text-overflow: ellipsis;
           }
           }

+ 21 - 10
src/components/ProTableCard/CardItems/AccessConfig/index.tsx

@@ -38,23 +38,34 @@ export default (props: AccessConfigCardProps) => {
         </div>
         </div>
         <div className="card">
         <div className="card">
           <div className="header">
           <div className="header">
-            <Tooltip title={props.name}>
-              <div className="title ellipsis">{props.name || '--'}</div>
-            </Tooltip>
-            <div className="desc">{props.description || '--'}</div>
+            <div className="title ellipsis">
+              <Tooltip title={props.name}>{props.name || '--'}</Tooltip>
+            </div>
+            <div className="desc">
+              <Tooltip title={props.description}>{props.description || '--'}</Tooltip>
+            </div>
           </div>
           </div>
           <div className="container">
           <div className="container">
             <div className="server">
             <div className="server">
               <div className="subTitle">{props?.channelInfo?.name || '--'}</div>
               <div className="subTitle">{props?.channelInfo?.name || '--'}</div>
-              {props.channelInfo?.addresses.slice(0, 2).map((i: any, index: number) => (
-                <div className="subItem" key={i.address + `_address${index}`}>
-                  <Badge color={i.health === -1 ? 'red' : 'green'} text={i.address} />
-                </div>
-              ))}
+              <div className="serverItem">
+                {props.channelInfo?.addresses.slice(0, 2).map((i: any, index: number) => (
+                  <div className="subItem" key={i.address + `_address${index}`}>
+                    <Tooltip title={i.address}>
+                      <Badge color={i.health === -1 ? 'red' : 'green'} />
+                      {i.address}
+                    </Tooltip>
+                  </div>
+                ))}
+              </div>
             </div>
             </div>
             <div className="procotol">
             <div className="procotol">
               <div className="subTitle">{props?.protocolDetail?.name || '--'}</div>
               <div className="subTitle">{props?.protocolDetail?.name || '--'}</div>
-              <div className="desc">{props.protocolDetail?.description || '--'}</div>
+              <div className="desc">
+                <Tooltip title={props.protocolDetail?.description}>
+                  {props.protocolDetail?.description || '--'}
+                </Tooltip>
+              </div>
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>

+ 64 - 0
src/components/ProTableCard/CardItems/protocol.tsx

@@ -0,0 +1,64 @@
+import React from 'react';
+import type { ProtocolItem } from '@/pages/link/Protocol/typings';
+import { StatusColorEnum } from '@/components/BadgeStatus';
+import { TableCard } from '@/components';
+import '@/style/common.less';
+import '../index.less';
+import { Col, Row, Tooltip } from 'antd';
+
+export interface ProcotolCardProps extends ProtocolItem {
+  detail?: React.ReactNode;
+  actions?: React.ReactNode[];
+  avatarSize?: number;
+}
+
+const defaultImage = require('/public/images/protocol.png');
+
+export default (props: ProcotolCardProps) => {
+  return (
+    <TableCard
+      showMask={false}
+      actions={props.actions}
+      status={props.state === 1 ? 'enabled' : 'disabled'}
+      statusText={props.state === 1 ? '已发布' : '未发布'}
+      statusNames={{
+        enabled: StatusColorEnum.processing,
+        disabled: StatusColorEnum.error,
+      }}
+    >
+      <div className={'pro-table-card-item'}>
+        <div className={'card-item-avatar'}>
+          <img width={88} height={88} src={defaultImage} alt={''} />
+        </div>
+        <div className={'card-item-body'}>
+          <div className={'card-item-header'}>
+            <span className={'card-item-header-name ellipsis'}>{props.name}</span>
+          </div>
+          <Row gutter={24}>
+            <Col span={12}>
+              <div>
+                <div style={{ color: 'rgba(0, 0, 0, 0.75)', fontSize: 12 }}>ID</div>
+                <div
+                  style={{
+                    width: '100%',
+                    overflow: 'hidden',
+                    whiteSpace: 'nowrap',
+                    textOverflow: 'ellipsis',
+                  }}
+                >
+                  <Tooltip title={props.id}>{props.id}</Tooltip>
+                </div>
+              </div>
+            </Col>
+            <Col span={12}>
+              <div>
+                <div style={{ color: 'rgba(0, 0, 0, 0.75)', fontSize: 12 }}>类型</div>
+                <div>{props.type}</div>
+              </div>
+            </Col>
+          </Row>
+        </div>
+      </div>
+    </TableCard>
+  );
+};

+ 3 - 2
src/pages/device/Instance/Detail/Config/index.tsx

@@ -38,6 +38,7 @@ const Config = () => {
   };
   };
 
 
   useEffect(() => {
   useEffect(() => {
+    console.log(id);
     if (id) {
     if (id) {
       service.getConfigMetadata(id).then((config) => {
       service.getConfigMetadata(id).then((config) => {
         setMetadata(config?.result);
         setMetadata(config?.result);
@@ -78,7 +79,7 @@ const Config = () => {
     }
     }
   };
   };
 
 
-  return (
+  return metadata.length > 0 ? (
     <div style={{ width: '100%', marginTop: '20px' }} className="config">
     <div style={{ width: '100%', marginTop: '20px' }} className="config">
       <div style={{ display: 'flex', marginBottom: 20 }}>
       <div style={{ display: 'flex', marginBottom: 20 }}>
         <div style={{ fontSize: 16, fontWeight: 700 }}>配置</div>
         <div style={{ fontSize: 16, fontWeight: 700 }}>配置</div>
@@ -172,7 +173,7 @@ const Config = () => {
         />
         />
       )}
       )}
     </div>
     </div>
-  );
+  ) : null;
 };
 };
 
 
 export default Config;
 export default Config;

+ 9 - 3
src/pages/device/Instance/Detail/Diagnose/Status/index.tsx

@@ -94,6 +94,7 @@ const Status = observer((props: Props) => {
         datalist.push(network);
         datalist.push(network);
       }
       }
       setList([...datalist]);
       setList([...datalist]);
+      return datalist;
     }
     }
   };
   };
 
 
@@ -460,7 +461,7 @@ const Status = observer((props: Props) => {
     if (deviceConfig?.result?.channelId && deviceConfig?.channel === 'network') {
     if (deviceConfig?.result?.channelId && deviceConfig?.channel === 'network') {
       network = await service.queryNetworkState(deviceConfig?.channelId);
       network = await service.queryNetworkState(deviceConfig?.channelId);
     }
     }
-    initList(proItem.result, configuration.result, deviceConfig.result);
+    const list1 = initList(proItem.result, configuration.result, deviceConfig.result);
 
 
     diagnoseProduct(proItem.result)
     diagnoseProduct(proItem.result)
       .then(() => diagnoseConfig(proItem.result))
       .then(() => diagnoseConfig(proItem.result))
@@ -476,7 +477,10 @@ const Status = observer((props: Props) => {
           }
           }
         });
         });
         if (a) {
         if (a) {
-          Store.set('diagnose-status', DiagnoseStatusModel.status);
+          Store.set('diagnose-status', {
+            list: list1,
+            status: DiagnoseStatusModel.status,
+          });
           props.onChange('success');
           props.onChange('success');
         } else {
         } else {
           props.onChange('error');
           props.onChange('error');
@@ -489,7 +493,9 @@ const Status = observer((props: Props) => {
       handleSearch();
       handleSearch();
     } else {
     } else {
       const dt = Store.get('diagnose-status');
       const dt = Store.get('diagnose-status');
-      DiagnoseStatusModel.status = dt;
+      DiagnoseStatusModel.status = dt?.status;
+      console.log(dt.status);
+      setList(dt?.list || []);
       props.onChange('success');
       props.onChange('success');
     }
     }
   }, []);
   }, []);

+ 9 - 8
src/pages/device/Instance/Detail/Diagnose/index.tsx

@@ -29,14 +29,14 @@ statusColor.set('s-success-active', '#24B276');
 statusColor.set('s-success', '#24B276');
 statusColor.set('s-success', '#24B276');
 statusColor.set('success', '#24B276');
 statusColor.set('success', '#24B276');
 statusColor.set('waiting', '#FF9000');
 statusColor.set('waiting', '#FF9000');
-statusColor.set('diaabled', 'rgba(0, 0, 0, .8)');
+statusColor.set('disabled', 'rgba(0, 0, 0, .8)');
 
 
 const statusText = new Map();
 const statusText = new Map();
 statusText.set('s-error', '连接失败');
 statusText.set('s-error', '连接失败');
 statusText.set('s-success-active', '连接成功');
 statusText.set('s-success-active', '连接成功');
 statusText.set('s-success', '连接成功');
 statusText.set('s-success', '连接成功');
 statusText.set('waiting', '诊断中');
 statusText.set('waiting', '诊断中');
-statusText.set('diaabled', '诊断中');
+statusText.set('disabled', '诊断中');
 
 
 const Diagnose = () => {
 const Diagnose = () => {
   const [current, setCurrent] = useState<string>('status');
   const [current, setCurrent] = useState<string>('status');
@@ -69,7 +69,7 @@ const Diagnose = () => {
       component: (
       component: (
         <div
         <div
           style={
           style={
-            message !== 'diaabled'
+            message !== 'disabled'
               ? {
               ? {
                   backgroundImage: `url(${bImageMap.get(message)})`,
                   backgroundImage: `url(${bImageMap.get(message)})`,
                   backgroundSize: '100% 100%',
                   backgroundSize: '100% 100%',
@@ -77,16 +77,17 @@ const Diagnose = () => {
               : {
               : {
                   backgroundColor: 'rgba(0, 0, 0, .08)',
                   backgroundColor: 'rgba(0, 0, 0, .08)',
                   borderLeft: '2px solid rgba(0, 0, 0, .8)',
                   borderLeft: '2px solid rgba(0, 0, 0, .8)',
+                  cursor: 'not-allowed',
                 }
                 }
           }
           }
           className="item-box"
           className="item-box"
         >
         >
           <div className="item-title">消息通信</div>
           <div className="item-title">消息通信</div>
           <div
           <div
-            className={classNames('item-context', message !== 'diaabled' ? 'item-message' : '')}
+            className={classNames('item-context', message !== 'disabled' ? 'item-message' : '')}
             style={{ fontWeight: 400 }}
             style={{ fontWeight: 400 }}
           >
           >
-            {message === 'diaabled' ? (
+            {message === 'disabled' ? (
               <span style={{ color: statusColor.get(message) }}>
               <span style={{ color: statusColor.get(message) }}>
                 <Badge color={statusColor.get(message)} /> 连接中
                 <Badge color={statusColor.get(message)} /> 连接中
               </span>
               </span>
@@ -159,13 +160,13 @@ const Diagnose = () => {
             onChange={(type: string) => {
             onChange={(type: string) => {
               if (type === 'success') {
               if (type === 'success') {
                 setStatus('s-success-active');
                 setStatus('s-success-active');
-                setMessage('diaabled');
+                setMessage('waiting');
               } else if (type === 'error') {
               } else if (type === 'error') {
                 setStatus('s-error');
                 setStatus('s-error');
-                setMessage('diaabled');
+                setMessage('disabled');
               } else if (type === 'loading') {
               } else if (type === 'loading') {
                 setStatus('waiting');
                 setStatus('waiting');
-                setMessage('diaabled');
+                setMessage('disabled');
               }
               }
             }}
             }}
           />
           />

+ 1 - 2
src/pages/device/Instance/Detail/Info/index.tsx

@@ -110,8 +110,7 @@ const Info = observer(() => {
             {InstanceModel.detail?.description}
             {InstanceModel.detail?.description}
           </Descriptions.Item>
           </Descriptions.Item>
         </Descriptions>
         </Descriptions>
-        {InstanceModel.detail?.configuration &&
-          Object.keys(InstanceModel.detail?.configuration).length > 0 && <Config />}
+        <Config />
         {InstanceModel.detail?.tags && InstanceModel.detail?.tags.length > 0 && <Tags />}
         {InstanceModel.detail?.tags && InstanceModel.detail?.tags.length > 0 && <Tags />}
       </Card>
       </Card>
       <Save
       <Save

+ 14 - 30
src/pages/device/Product/Detail/Access/index.tsx

@@ -73,11 +73,7 @@ const Access = () => {
       key: 'group',
       key: 'group',
       ellipsis: true,
       ellipsis: true,
       align: 'center',
       align: 'center',
-      render: (text: any) => (
-        <Tooltip placement="top" title={text}>
-          {text}
-        </Tooltip>
-      ),
+      width: 100,
       onCell: (record: any, index: number) => {
       onCell: (record: any, index: number) => {
         const list = (config?.routes || []).sort((a: any, b: any) => a - b) || [];
         const list = (config?.routes || []).sort((a: any, b: any) => a - b) || [];
         const arr = list.filter((res: any) => {
         const arr = list.filter((res: any) => {
@@ -98,7 +94,7 @@ const Access = () => {
       ellipsis: true,
       ellipsis: true,
       align: 'center',
       align: 'center',
       render: (text: any) => (
       render: (text: any) => (
-        <Tooltip placement="top" title={text}>
+        <Tooltip placement="topLeft" title={text}>
           {text}
           {text}
         </Tooltip>
         </Tooltip>
       ),
       ),
@@ -109,6 +105,7 @@ const Access = () => {
       key: 'stream',
       key: 'stream',
       ellipsis: true,
       ellipsis: true,
       align: 'center',
       align: 'center',
+      width: 100,
       render: (text: any, record: any) => {
       render: (text: any, record: any) => {
         const list = [];
         const list = [];
         if (record?.upstream) {
         if (record?.upstream) {
@@ -127,7 +124,7 @@ const Access = () => {
       ellipsis: true,
       ellipsis: true,
       align: 'center',
       align: 'center',
       render: (text: any) => (
       render: (text: any) => (
-        <Tooltip placement="top" title={text}>
+        <Tooltip placement="topLeft" title={text}>
           {text}
           {text}
         </Tooltip>
         </Tooltip>
       ),
       ),
@@ -140,12 +137,8 @@ const Access = () => {
       dataIndex: 'group',
       dataIndex: 'group',
       key: 'group',
       key: 'group',
       ellipsis: true,
       ellipsis: true,
+      width: 100,
       align: 'center',
       align: 'center',
-      render: (text: any) => (
-        <Tooltip placement="top" title={text}>
-          {text}
-        </Tooltip>
-      ),
       onCell: (record: any, index: number) => {
       onCell: (record: any, index: number) => {
         const list = (config?.routes || []).sort((a: any, b: any) => a - b) || [];
         const list = (config?.routes || []).sort((a: any, b: any) => a - b) || [];
         const arr = list.filter((res: any) => {
         const arr = list.filter((res: any) => {
@@ -166,7 +159,7 @@ const Access = () => {
       ellipsis: true,
       ellipsis: true,
       align: 'center',
       align: 'center',
       render: (text: any) => (
       render: (text: any) => (
-        <Tooltip placement="top" title={text}>
+        <Tooltip placement="topLeft" title={text}>
           {text}
           {text}
         </Tooltip>
         </Tooltip>
       ),
       ),
@@ -178,7 +171,7 @@ const Access = () => {
       ellipsis: true,
       ellipsis: true,
       align: 'center',
       align: 'center',
       render: (text: any) => (
       render: (text: any) => (
-        <Tooltip placement="top" title={text}>
+        <Tooltip placement="topLeft" title={text}>
           {text}
           {text}
         </Tooltip>
         </Tooltip>
       ),
       ),
@@ -190,7 +183,7 @@ const Access = () => {
       ellipsis: true,
       ellipsis: true,
       align: 'center',
       align: 'center',
       render: (text: any) => (
       render: (text: any) => (
-        <Tooltip placement="top" title={text}>
+        <Tooltip placement="topLeft" title={text}>
           {text}
           {text}
         </Tooltip>
         </Tooltip>
       ),
       ),
@@ -431,9 +424,9 @@ const Access = () => {
               <div className={styles.item}>{renderConfigCard()}</div>
               <div className={styles.item}>{renderConfigCard()}</div>
             </div>
             </div>
           </Col>
           </Col>
-          <Col span={12}>
-            <div className={styles.info}>
-              {config?.routes && config?.routes?.length > 0 ? (
+          {config?.routes && config?.routes?.length > 0 && (
+            <Col span={12}>
+              <div className={styles.info}>
                 <div>
                 <div>
                   <div style={{ fontWeight: '600', marginBottom: 10 }}>
                   <div style={{ fontWeight: '600', marginBottom: 10 }}>
                     {access?.provider === 'mqtt-server-gateway' ||
                     {access?.provider === 'mqtt-server-gateway' ||
@@ -449,18 +442,9 @@ const Access = () => {
                     scroll={{ y: 500 }}
                     scroll={{ y: 500 }}
                   />
                   />
                 </div>
                 </div>
-              ) : (
-                <Empty
-                  description={`暂无${
-                    access?.provider === 'mqtt-server-gateway' ||
-                    access?.provider === 'mqtt-client-gateway'
-                      ? 'topic'
-                      : 'URL信息'
-                  }`}
-                />
-              )}
-            </div>
-          </Col>
+              </div>
+            </Col>
+          )}
         </Row>
         </Row>
       )}
       )}
       {configVisible && (
       {configVisible && (

+ 67 - 44
src/pages/link/AccessConfig/Detail/Access/index.tsx

@@ -21,6 +21,7 @@ import ReactMarkdown from 'react-markdown';
 import { getButtonPermission, getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 import { getButtonPermission, getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 import { ExclamationCircleFilled } from '@ant-design/icons';
 import { ExclamationCircleFilled } from '@ant-design/icons';
 import TitleComponent from '@/components/TitleComponent';
 import TitleComponent from '@/components/TitleComponent';
+import { PermissionButton } from '@/components';
 
 
 interface Props {
 interface Props {
   change: () => void;
   change: () => void;
@@ -39,6 +40,8 @@ const Access = (props: Props) => {
   const [procotolCurrent, setProcotolCurrent] = useState<string>('');
   const [procotolCurrent, setProcotolCurrent] = useState<string>('');
   const [networkCurrent, setNetworkCurrent] = useState<string>('');
   const [networkCurrent, setNetworkCurrent] = useState<string>('');
   const [config, setConfig] = useState<any>();
   const [config, setConfig] = useState<any>();
+  const networkPermission = PermissionButton.usePermission('link/Type').permission;
+  const protocolPermission = PermissionButton.usePermission('link/Protocol').permission;
 
 
   const MetworkTypeMapping = new Map();
   const MetworkTypeMapping = new Map();
   MetworkTypeMapping.set('websocket-server', 'WEB_SOCKET_SERVER');
   MetworkTypeMapping.set('websocket-server', 'WEB_SOCKET_SERVER');
@@ -75,8 +78,10 @@ const Access = (props: Props) => {
   };
   };
 
 
   useEffect(() => {
   useEffect(() => {
-    if (props.provider?.id) {
-      queryNetworkList(props.provider?.id);
+    if (props.provider?.id && !props.data?.id) {
+      queryNetworkList(props.provider?.id, {
+        include: networkCurrent || '',
+      });
       setCurrent(0);
       setCurrent(0);
     }
     }
   }, [props.provider]);
   }, [props.provider]);
@@ -90,7 +95,9 @@ const Access = (props: Props) => {
         description: props.data?.description,
         description: props.data?.description,
       });
       });
       setCurrent(0);
       setCurrent(0);
-      queryNetworkList(props.data?.provider);
+      queryNetworkList(props.data?.provider, {
+        include: props.data?.channelId,
+      });
     }
     }
   }, [props.data]);
   }, [props.data]);
 
 
@@ -142,6 +149,7 @@ const Access = (props: Props) => {
       key: 'group',
       key: 'group',
       ellipsis: true,
       ellipsis: true,
       align: 'center',
       align: 'center',
+      width: 80,
       render: (text: any) => (
       render: (text: any) => (
         <Tooltip placement="top" title={text}>
         <Tooltip placement="top" title={text}>
           {text}
           {text}
@@ -166,6 +174,7 @@ const Access = (props: Props) => {
       key: 'topic',
       key: 'topic',
       ellipsis: true,
       ellipsis: true,
       align: 'center',
       align: 'center',
+      with: '30%',
       render: (text: any) => (
       render: (text: any) => (
         <Tooltip placement="top" title={text}>
         <Tooltip placement="top" title={text}>
           {text}
           {text}
@@ -177,6 +186,7 @@ const Access = (props: Props) => {
       dataIndex: 'stream',
       dataIndex: 'stream',
       key: 'stream',
       key: 'stream',
       ellipsis: true,
       ellipsis: true,
+      width: 80,
       align: 'center',
       align: 'center',
       render: (text: any, record: any) => {
       render: (text: any, record: any) => {
         const list = [];
         const list = [];
@@ -283,6 +293,7 @@ const Access = (props: Props) => {
                   queryNetworkList(
                   queryNetworkList(
                     props.provider?.id,
                     props.provider?.id,
                     encodeQuery({
                     encodeQuery({
+                      include: networkCurrent || '',
                       terms: {
                       terms: {
                         name$LIKE: `%${value}%`,
                         name$LIKE: `%${value}%`,
                       },
                       },
@@ -291,26 +302,29 @@ const Access = (props: Props) => {
                 }}
                 }}
                 style={{ width: 500, margin: '20px 0' }}
                 style={{ width: 500, margin: '20px 0' }}
               />
               />
-              <Button
-                type="primary"
-                disabled={getButtonPermission('link/Type', ['add'])}
+              <PermissionButton
+                isPermission={networkPermission.add}
                 onClick={() => {
                 onClick={() => {
                   const url = getMenuPathByCode(MENUS_CODE['link/Type/Detail']);
                   const url = getMenuPathByCode(MENUS_CODE['link/Type/Detail']);
                   const tab: any = window.open(`${origin}/#${url}`);
                   const tab: any = window.open(`${origin}/#${url}`);
                   tab!.onTabSaveSuccess = (value: any) => {
                   tab!.onTabSaveSuccess = (value: any) => {
                     if (value.status === 200) {
                     if (value.status === 200) {
-                      queryNetworkList(props.provider?.id);
+                      queryNetworkList(props.provider?.id, {
+                        include: networkCurrent || '',
+                      });
                     }
                     }
                   };
                   };
                 }}
                 }}
+                key="button"
+                type="primary"
               >
               >
                 新增
                 新增
-              </Button>
+              </PermissionButton>
             </div>
             </div>
             {networkList.length > 0 ? (
             {networkList.length > 0 ? (
               <Row gutter={[16, 16]}>
               <Row gutter={[16, 16]}>
                 {networkList.map((item) => (
                 {networkList.map((item) => (
-                  <Col key={item.name} span={8}>
+                  <Col key={item.id} span={8}>
                     <Card
                     <Card
                       className={styles.cardRender}
                       className={styles.cardRender}
                       style={{
                       style={{
@@ -352,21 +366,26 @@ const Access = (props: Props) => {
                 description={
                 description={
                   <span>
                   <span>
                     暂无数据
                     暂无数据
-                    <Button
-                      type="link"
-                      disabled={getButtonPermission('link/Type', ['add'])}
-                      onClick={() => {
-                        const url = getMenuPathByCode(MENUS_CODE['link/Type/Detail']);
-                        const tab: any = window.open(`${origin}/#${url}`);
-                        tab!.onTabSaveSuccess = (value: any) => {
-                          if (value.status === 200) {
-                            queryNetworkList(props.provider?.id);
-                          }
-                        };
-                      }}
-                    >
-                      创建接入方式
-                    </Button>
+                    {getButtonPermission('link/Type', ['add']) ? (
+                      '请联系管理员进行配置'
+                    ) : (
+                      <Button
+                        type="link"
+                        onClick={() => {
+                          const url = getMenuPathByCode(MENUS_CODE['link/Type/Detail']);
+                          const tab: any = window.open(`${origin}/#${url}`);
+                          tab!.onTabSaveSuccess = (value: any) => {
+                            if (value.status === 200) {
+                              queryNetworkList(props.provider?.id, {
+                                include: networkCurrent || '',
+                              });
+                            }
+                          };
+                        }}
+                      >
+                        创建接入方式
+                      </Button>
+                    )}
                   </span>
                   </span>
                 }
                 }
               />
               />
@@ -396,9 +415,8 @@ const Access = (props: Props) => {
                 }}
                 }}
                 style={{ width: 500, margin: '20px 0' }}
                 style={{ width: 500, margin: '20px 0' }}
               />
               />
-              <Button
-                type="primary"
-                disabled={getButtonPermission('link/Protocol', ['add'])}
+              <PermissionButton
+                isPermission={protocolPermission.add}
                 onClick={() => {
                 onClick={() => {
                   const url = getMenuPathByCode(MENUS_CODE[`link/Protocol`]);
                   const url = getMenuPathByCode(MENUS_CODE[`link/Protocol`]);
                   const tab: any = window.open(`${origin}/#${url}?save=true`);
                   const tab: any = window.open(`${origin}/#${url}?save=true`);
@@ -408,14 +426,16 @@ const Access = (props: Props) => {
                     }
                     }
                   };
                   };
                 }}
                 }}
+                key="button"
+                type="primary"
               >
               >
                 新增
                 新增
-              </Button>
+              </PermissionButton>
             </div>
             </div>
             {procotolList.length > 0 ? (
             {procotolList.length > 0 ? (
               <Row gutter={[16, 16]}>
               <Row gutter={[16, 16]}>
                 {procotolList.map((item) => (
                 {procotolList.map((item) => (
-                  <Col key={item.name} span={8}>
+                  <Col key={item.id} span={8}>
                     <Card
                     <Card
                       className={styles.cardRender}
                       className={styles.cardRender}
                       style={{
                       style={{
@@ -441,21 +461,24 @@ const Access = (props: Props) => {
                 description={
                 description={
                   <span>
                   <span>
                     暂无数据
                     暂无数据
-                    <Button
-                      type="link"
-                      disabled={getButtonPermission('link/Protocol', ['add'])}
-                      onClick={() => {
-                        const url = getMenuPathByCode(MENUS_CODE[`link/Protocol`]);
-                        const tab: any = window.open(`${origin}/#${url}?save=true`);
-                        tab!.onTabSaveSuccess = (value: any) => {
-                          if (value) {
-                            queryProcotolList(props.provider?.id);
-                          }
-                        };
-                      }}
-                    >
-                      去新增
-                    </Button>
+                    {getButtonPermission('link/Protocol', ['add']) ? (
+                      '请联系管理员进行配置'
+                    ) : (
+                      <Button
+                        type="link"
+                        onClick={() => {
+                          const url = getMenuPathByCode(MENUS_CODE[`link/Protocol`]);
+                          const tab: any = window.open(`${origin}/#${url}?save=true`);
+                          tab!.onTabSaveSuccess = (value: any) => {
+                            if (value) {
+                              queryProcotolList(props.provider?.id);
+                            }
+                          };
+                        }}
+                      >
+                        去新增
+                      </Button>
+                    )}
                   </span>
                   </span>
                 }
                 }
               />
               />

+ 171 - 261
src/pages/link/Protocol/index.tsx

@@ -1,26 +1,31 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import { PageContainer } from '@ant-design/pro-layout';
-import type { ProtocolItem } from '@/pages/link/Protocol/typings';
-import { useEffect, useRef } from 'react';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
-import { Badge, Button, message } from 'antd';
-import { CheckCircleOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons';
-import BaseCrud from '@/components/BaseCrud';
-import { useIntl } from '@@/plugin-locale/localeExports';
-import type { ISchema } from '@formily/json-schema';
-import { CurdModel } from '@/components/BaseCrud/model';
+import type { ProtocolItem } from '@/pages/link/Protocol/typings';
+import { Badge, message } from 'antd';
+import { useRef, useState } from 'react';
+import {
+  CheckCircleOutlined,
+  DeleteOutlined,
+  EditOutlined,
+  PlusOutlined,
+  StopOutlined,
+} from '@ant-design/icons';
 import Service from '@/pages/link/Protocol/service';
 import Service from '@/pages/link/Protocol/service';
-import { onFormValuesChange, registerValidateRules } from '@formily/core';
-import { Store } from 'jetlinks-store';
-import { useLocation } from 'umi';
-import SystemConst from '@/utils/const';
-import { getButtonPermission } from '@/utils/menu';
-import { PermissionButton } from '@/components';
+import { useIntl } from 'umi';
+import SearchComponent from '@/components/SearchComponent';
+import { ProTableCard, PermissionButton } from '@/components';
+import ProcotolCard from '@/components/ProTableCard/CardItems/protocol';
+import Save from './save';
 
 
 export const service = new Service('protocol');
 export const service = new Service('protocol');
+
 const Protocol = () => {
 const Protocol = () => {
-  const intl = useIntl();
   const actionRef = useRef<ActionType>();
   const actionRef = useRef<ActionType>();
+  const [visible, setVisible] = useState<boolean>(false);
+  const [current, setCurrent] = useState<ProtocolItem | undefined>();
+  const [searchParams, setSearchParams] = useState<any>({});
   const { permission } = PermissionButton.usePermission('link/Protocol');
   const { permission } = PermissionButton.usePermission('link/Protocol');
+  const intl = useIntl();
 
 
   const modifyState = async (id: string, type: 'deploy' | 'un-deploy') => {
   const modifyState = async (id: string, type: 'deploy' | 'un-deploy') => {
     const resp = await service.modifyState(id, type);
     const resp = await service.modifyState(id, type);
@@ -71,14 +76,13 @@ const Protocol = () => {
         defaultMessage: '操作',
         defaultMessage: '操作',
       }),
       }),
       valueType: 'option',
       valueType: 'option',
-      width: 200,
       render: (text, record) => [
       render: (text, record) => [
         <PermissionButton
         <PermissionButton
           isPermission={permission.update}
           isPermission={permission.update}
           key="edit"
           key="edit"
           onClick={() => {
           onClick={() => {
-            CurdModel.update(record);
-            CurdModel.model = 'edit';
+            setCurrent(record);
+            setVisible(true);
           }}
           }}
           type={'link'}
           type={'link'}
           style={{ padding: 0 }}
           style={{ padding: 0 }}
@@ -91,43 +95,33 @@ const Protocol = () => {
         >
         >
           <EditOutlined />
           <EditOutlined />
         </PermissionButton>,
         </PermissionButton>,
-        record.state !== 1 && (
-          <PermissionButton
-            isPermission={permission.action}
-            key="publish"
-            onClick={() => {
-              modifyState(record.id, 'deploy');
-            }}
-            type={'link'}
-            style={{ padding: 0 }}
-            tooltip={{
-              title: '发布',
-            }}
-          >
-            <CheckCircleOutlined />
-          </PermissionButton>
-        ),
-        record.state === 1 && (
-          <PermissionButton
-            isPermission={permission.action}
-            key="publish"
-            onClick={() => {
-              modifyState(record.id, 'un-deploy');
-            }}
-            type={'link'}
-            style={{ padding: 0 }}
-            tooltip={{
-              title: '撤销',
-            }}
-          >
-            <CheckCircleOutlined />
-          </PermissionButton>
-        ),
+        <PermissionButton
+          isPermission={permission.action}
+          key="action"
+          type={'link'}
+          style={{ padding: 0 }}
+          tooltip={{
+            title: record.state === 1 ? '撤销' : '发布',
+          }}
+          popConfirm={{
+            title: `确认${record.state === 1 ? '撤销' : '发布'}`,
+            onConfirm: () => {
+              if (record.state === 1) {
+                modifyState(record.id, 'un-deploy');
+              } else {
+                modifyState(record.id, 'deploy');
+              }
+            },
+          }}
+        >
+          {record.state === 1 ? <StopOutlined /> : <CheckCircleOutlined />}
+        </PermissionButton>,
         <PermissionButton
         <PermissionButton
           isPermission={permission.delete}
           isPermission={permission.delete}
           tooltip={{
           tooltip={{
             title: record.state !== 1 ? '删除' : '请先禁用该协议,再删除',
             title: record.state !== 1 ? '删除' : '请先禁用该协议,再删除',
           }}
           }}
+          style={{ padding: 0 }}
           disabled={record.state === 1}
           disabled={record.state === 1}
           popConfirm={{
           popConfirm={{
             title: '确认删除',
             title: '确认删除',
@@ -155,223 +149,139 @@ const Protocol = () => {
     },
     },
   ];
   ];
 
 
-  registerValidateRules({
-    validateId(value) {
-      if (!value) return '';
-      const reg = new RegExp('^[0-9a-zA-Z_\\\\-]+$');
-      return reg.exec(value) ? '' : 'ID只能由数字、26个英文字母或者下划线组成';
-    },
-  });
-
-  const schema: ISchema = {
-    type: 'object',
-    properties: {
-      layout: {
-        type: 'void',
-        'x-component': 'FormGrid',
-        'x-component-props': {
-          maxColumns: 1,
-          minColumns: 1,
-        },
-        properties: {
-          id: {
-            title: 'ID',
-            'x-component': 'Input',
-            'x-decorator': 'FormItem',
-            'x-decorator-props': {
-              gridSpan: 1,
-            },
-            'x-validator': [
-              {
-                required: true,
-                message: '请输入ID',
-              },
-              {
-                max: 64,
-                message: '最多可输入64个字符',
-              },
-              {
-                validateId: true,
-                message: 'ID只能由数字、26个英文字母或者下划线组成',
-              },
-              {
-                triggerType: 'onBlur',
-                validator: (value: string) => {
-                  if (!value) return;
-                  return new Promise((resolve) => {
-                    service
-                      .validator(value)
-                      .then((resp) => {
-                        if (!!resp?.result) {
-                          resolve('ID已存在');
-                        } else {
-                          resolve('');
-                        }
-                      })
-                      .catch(() => '验证失败!');
-                  });
-                },
-              },
-            ],
-            'x-component-props': {
-              placeholder: '请输入ID',
-            },
-          },
-          name: {
-            title: '名称',
-            'x-component': 'Input',
-            'x-decorator': 'FormItem',
-            'x-decorator-props': {
-              gridSpan: 1,
-            },
-            'x-component-props': {
-              placeholder: '请输入名称',
-            },
-            'x-validator': [
-              {
-                required: true,
-                message: '请输入名称',
-              },
-              {
-                max: 64,
-                message: '最多可输入64个字符',
-              },
-            ],
-          },
-          type: {
-            title: '类型',
-            'x-component': 'Select',
-            'x-decorator': 'FormItem',
-            'x-decorator-props': {
-              tooltip: <div>jar:上传协议jar包,文件格式支持.jar或.zip</div>,
-            },
-            'x-component-props': {
-              placeholder: '请选择类型',
-            },
-            'x-validator': [
-              {
-                required: true,
-                message: '请选择类型',
-              },
-            ],
-            enum: [
-              { label: 'jar', value: 'jar' },
-              { label: 'local', value: 'local' },
-              // { label: 'script', value: 'script' },
-            ],
-          },
-          configuration: {
-            type: 'object',
-            properties: {
-              location: {
-                title: '文件地址',
-                'x-decorator': 'FormItem',
-                'x-visible': false,
-                'x-decorator-props': {
-                  tooltip: (
-                    <div>
-                      local:填写本地协议编译目录绝对地址,如:d:/workspace/protocol/target/classes
-                    </div>
-                  ),
-                },
-                'x-validator': [
-                  {
-                    required: true,
-                    message: '请输入文件地址',
-                  },
-                ],
-                'x-reactions': {
-                  dependencies: ['..type'],
-                  fulfill: {
-                    state: {
-                      visible: '{{["jar","local"].includes($deps[0])}}',
-                      componentType: '{{$deps[0]==="jar"?"FileUpload":"Input"}}',
-                      componentProps:
-                        '{{$deps[0]==="jar"?{type:"file", accept: ".jar, .zip"}:{placeholder: "请输入文件地址"}}}',
-                    },
-                  },
-                },
-              },
-            },
-          },
-          description: {
-            title: '说明',
-            'x-component': 'Input.TextArea',
-            'x-decorator': 'FormItem',
-            'x-component-props': {
-              rows: 3,
-              showCount: true,
-              maxLength: 200,
-            },
-          },
-        },
-      },
-    },
-  };
-
-  const location = useLocation();
-
-  useEffect(() => {
-    if ((location as any).query?.save === 'true') {
-      CurdModel.add();
-    }
-    const subscription = Store.subscribe(SystemConst.BASE_UPDATE_DATA, (data) => {
-      if ((window as any).onTabSaveSuccess) {
-        (window as any).onTabSaveSuccess(data);
-        setTimeout(() => window.close(), 300);
-      }
-    });
-    return () => subscription.unsubscribe();
-  }, []);
-
   return (
   return (
     <PageContainer>
     <PageContainer>
-      <BaseCrud
+      <SearchComponent<ProtocolItem>
+        field={columns}
+        target="Protocol"
+        onSearch={(data) => {
+          actionRef.current?.reset?.();
+          setSearchParams(data);
+        }}
+      />
+      <ProTableCard<ProtocolItem>
         columns={columns}
         columns={columns}
-        service={service}
-        title={'插件管理'}
-        search={false}
-        modelConfig={{ width: '550px' }}
-        schema={schema}
         actionRef={actionRef}
         actionRef={actionRef}
-        disableAdd={getButtonPermission('link/Protocol', ['add'])}
-        formEffect={() => {
-          onFormValuesChange((form) => {
-            form.setFieldState('id', (state) => {
-              state.disabled = CurdModel.model === 'edit';
-            });
-            form.setFieldState('type', (state) => {
-              state.disabled = CurdModel.model === 'edit';
-            });
-          });
-        }}
-        footer={
-          <>
-            <Button onClick={CurdModel.close}>取消</Button>
-            <Button
-              type="primary"
-              onClick={() => {
-                Store.set('save-data', true);
-              }}
-            >
-              保存
-            </Button>
-            <Button
-              type="primary"
-              onClick={() => {
-                Store.set('save-data', async (data: any) => {
-                  // 获取到的保存的数据
-                  if (data.id) {
-                    await modifyState(data.id, 'deploy');
-                  }
-                });
-              }}
-            >
-              保存并发布
-            </Button>
-          </>
+        params={searchParams}
+        options={{ fullScreen: true }}
+        request={(params) =>
+          service.query({
+            ...params,
+            sorts: [
+              {
+                name: 'createTime',
+                order: 'desc',
+              },
+            ],
+          })
         }
         }
+        rowKey="id"
+        search={false}
+        pagination={{ pageSize: 10 }}
+        headerTitle={[
+          <PermissionButton
+            onClick={() => {
+              setVisible(true);
+              setCurrent({});
+            }}
+            style={{ marginRight: 12 }}
+            isPermission={permission.add}
+            key="button"
+            icon={<PlusOutlined />}
+            type="primary"
+          >
+            {intl.formatMessage({
+              id: 'pages.data.option.add',
+              defaultMessage: '新增',
+            })}
+          </PermissionButton>,
+        ]}
+        cardRender={(record) => (
+          <ProcotolCard
+            {...record}
+            actions={[
+              <PermissionButton
+                isPermission={permission.update}
+                key="edit"
+                onClick={() => {
+                  setCurrent(record);
+                  setVisible(true);
+                }}
+                type={'link'}
+                style={{ padding: 0 }}
+                tooltip={{
+                  title: intl.formatMessage({
+                    id: 'pages.data.option.edit',
+                    defaultMessage: '编辑',
+                  }),
+                }}
+              >
+                <EditOutlined />
+              </PermissionButton>,
+              <PermissionButton
+                isPermission={permission.action}
+                key="action"
+                type={'link'}
+                style={{ padding: 0 }}
+                tooltip={{
+                  title: record.state === 1 ? '撤销' : '发布',
+                }}
+                popConfirm={{
+                  title: `确认${record.state === 1 ? '撤销' : '发布'}`,
+                  onConfirm: () => {
+                    if (record.state === 1) {
+                      modifyState(record.id, 'un-deploy');
+                    } else {
+                      modifyState(record.id, 'deploy');
+                    }
+                  },
+                }}
+              >
+                {record.state === 1 ? <StopOutlined /> : <CheckCircleOutlined />}
+              </PermissionButton>,
+              <PermissionButton
+                isPermission={permission.delete}
+                tooltip={{
+                  title: record.state !== 1 ? '删除' : '请先禁用该协议,再删除',
+                }}
+                disabled={record.state === 1}
+                popConfirm={{
+                  title: '确认删除',
+                  onConfirm: async () => {
+                    const resp: any = await service.remove(record.id);
+                    if (resp.status === 200) {
+                      message.success(
+                        intl.formatMessage({
+                          id: 'pages.data.option.success',
+                          defaultMessage: '操作成功!',
+                        }),
+                      );
+                      actionRef.current?.reload();
+                    } else {
+                      message.error(resp?.message || '操作失败');
+                    }
+                  },
+                }}
+                key="delete"
+                type="link"
+              >
+                <DeleteOutlined />
+              </PermissionButton>,
+            ]}
+          />
+        )}
       />
       />
+      {visible && (
+        <Save
+          data={current}
+          close={() => {
+            setVisible(false);
+          }}
+          reload={() => {
+            actionRef.current?.reload();
+          }}
+        />
+      )}
     </PageContainer>
     </PageContainer>
   );
   );
 };
 };

+ 256 - 0
src/pages/link/Protocol/save/index.tsx

@@ -0,0 +1,256 @@
+import { Button, message } from 'antd';
+import { createForm, registerValidateRules } from '@formily/core';
+import { createSchemaField } from '@formily/react';
+import React, { useEffect, useState } from 'react';
+import * as ICONS from '@ant-design/icons';
+import { Select, Form, FormItem, Input, FormGrid } from '@formily/antd';
+import type { ISchema } from '@formily/json-schema';
+import { service } from '@/pages/link/Protocol';
+import { Modal } from '@/components';
+import FileUpload from '../FileUpload';
+import type { ProtocolItem } from '@/pages/link/Protocol/typings';
+
+interface Props {
+  data: ProtocolItem | undefined;
+  close: () => void;
+  reload: () => void;
+}
+
+const Save = (props: Props) => {
+  const [data, setData] = useState<ProtocolItem | undefined>(props.data);
+
+  useEffect(() => {
+    setData(props.data);
+  }, [props.data]);
+
+  const form = createForm({
+    validateFirst: true,
+    initialValues: data || {},
+  });
+
+  registerValidateRules({
+    validateId(value) {
+      if (!value) return '';
+      const reg = new RegExp('^[0-9a-zA-Z_\\\\-]+$');
+      return reg.exec(value) ? '' : 'ID只能由数字、26个英文字母或者下划线组成';
+    },
+  });
+
+  const SchemaField = createSchemaField({
+    components: {
+      FormItem,
+      Input,
+      Select,
+      FileUpload,
+      FormGrid,
+    },
+    scope: {
+      icon(name: any) {
+        return React.createElement(ICONS[name]);
+      },
+    },
+  });
+
+  const schema: ISchema = {
+    type: 'object',
+    properties: {
+      layout: {
+        type: 'void',
+        'x-component': 'FormGrid',
+        'x-component-props': {
+          maxColumns: 1,
+          minColumns: 1,
+        },
+        properties: {
+          id: {
+            title: 'ID',
+            'x-component': 'Input',
+            'x-decorator': 'FormItem',
+            'x-disabled': !!props.data?.id,
+            'x-decorator-props': {
+              gridSpan: 1,
+            },
+            'x-validator': [
+              {
+                required: true,
+                message: '请输入ID',
+              },
+              {
+                max: 64,
+                message: '最多可输入64个字符',
+              },
+              {
+                validateId: true,
+                message: 'ID只能由数字、26个英文字母或者下划线组成',
+              },
+              {
+                triggerType: 'onBlur',
+                validator: (value: string) => {
+                  if (!value) return;
+                  return new Promise((resolve) => {
+                    service
+                      .validator(value)
+                      .then((resp) => {
+                        if (!!resp?.result) {
+                          resolve('ID已存在');
+                        } else {
+                          resolve('');
+                        }
+                      })
+                      .catch(() => '验证失败!');
+                  });
+                },
+              },
+            ],
+            'x-component-props': {
+              placeholder: '请输入ID',
+            },
+          },
+          name: {
+            title: '名称',
+            'x-component': 'Input',
+            'x-decorator': 'FormItem',
+            'x-decorator-props': {
+              gridSpan: 1,
+            },
+            'x-component-props': {
+              placeholder: '请输入名称',
+            },
+            'x-validator': [
+              {
+                required: true,
+                message: '请输入名称',
+              },
+              {
+                max: 64,
+                message: '最多可输入64个字符',
+              },
+            ],
+          },
+          type: {
+            title: '类型',
+            'x-component': 'Select',
+            'x-decorator': 'FormItem',
+            'x-disabled': !!props.data?.id,
+            'x-decorator-props': {
+              tooltip: <div>jar:上传协议jar包,文件格式支持.jar或.zip</div>,
+            },
+            'x-component-props': {
+              placeholder: '请选择类型',
+            },
+            'x-validator': [
+              {
+                required: true,
+                message: '请选择类型',
+              },
+            ],
+            enum: [
+              { label: 'jar', value: 'jar' },
+              { label: 'local', value: 'local' },
+              // { label: 'script', value: 'script' },
+            ],
+          },
+          configuration: {
+            type: 'object',
+            properties: {
+              location: {
+                title: '文件地址',
+                'x-decorator': 'FormItem',
+                'x-visible': false,
+                'x-decorator-props': {
+                  tooltip: (
+                    <div>
+                      local:填写本地协议编译目录绝对地址,如:d:/workspace/protocol/target/classes
+                    </div>
+                  ),
+                },
+                'x-validator': [
+                  {
+                    required: true,
+                    message: '请输入文件地址',
+                  },
+                ],
+                'x-reactions': {
+                  dependencies: ['..type'],
+                  fulfill: {
+                    state: {
+                      visible: '{{["jar","local"].includes($deps[0])}}',
+                      componentType: '{{$deps[0]==="jar"?"FileUpload":"Input"}}',
+                      componentProps:
+                        '{{$deps[0]==="jar"?{type:"file", accept: ".jar, .zip"}:{placeholder: "请输入文件地址"}}}',
+                    },
+                  },
+                },
+              },
+            },
+          },
+          description: {
+            title: '说明',
+            'x-component': 'Input.TextArea',
+            'x-decorator': 'FormItem',
+            'x-component-props': {
+              rows: 3,
+              showCount: true,
+              maxLength: 200,
+            },
+          },
+        },
+      },
+    },
+  };
+
+  const save = async (deploy: boolean) => {
+    const value = await form.submit<ProtocolItem>();
+    let response = undefined;
+    if (props.data?.id) {
+      response = await service.save(value);
+    } else {
+      response = await service.update(value);
+    }
+    if (response && response.status === 200) {
+      message.success('操作成功');
+      if (deploy) {
+        await service.modifyState(value.id, 'deploy');
+      }
+      props.reload();
+    }
+  };
+
+  return (
+    <Modal
+      title={props?.data?.id ? '编辑' : '新增'}
+      maskClosable={false}
+      visible
+      onCancel={props.close}
+      width={700}
+      permissionCode={'link/Protocol'}
+      permission={['add', 'edit']}
+      footer={
+        <>
+          <Button onClick={props.close}>取消</Button>
+          <Button
+            type="primary"
+            onClick={() => {
+              save(false);
+            }}
+          >
+            保存
+          </Button>
+          <Button
+            type="primary"
+            onClick={() => {
+              save(true);
+            }}
+          >
+            保存并发布
+          </Button>
+        </>
+      }
+    >
+      <Form form={form} layout="vertical">
+        <SchemaField schema={schema} />
+      </Form>
+    </Modal>
+  );
+};
+export default Save;

+ 38 - 18
src/pages/system/Permission/index.tsx

@@ -7,7 +7,7 @@ import {
   PlayCircleOutlined,
   PlayCircleOutlined,
   PlusOutlined,
   PlusOutlined,
 } from '@ant-design/icons';
 } from '@ant-design/icons';
-import { Badge, Button, Dropdown, Menu, message, Popconfirm, Space, Upload } from 'antd';
+import { Badge, Button, Dropdown, Menu, message, Popconfirm, Space, Tooltip, Upload } from 'antd';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { useIntl } from '@@/plugin-locale/localeExports';
@@ -49,24 +49,34 @@ const Permission: React.FC = observer(() => {
           showUploadList={false}
           showUploadList={false}
           accept=".json"
           accept=".json"
           beforeUpload={(file) => {
           beforeUpload={(file) => {
-            const reader = new FileReader();
-            reader.readAsText(file);
-            reader.onload = (result: any) => {
-              try {
-                const data = JSON.parse(result.target.result);
-                service.batchAdd(data).subscribe((resp) => {
-                  if (resp.status === 200) {
-                    message.success('导入成功');
-                    actionRef.current?.reload();
-                  }
-                });
-              } catch (error) {
-                message.error('导入失败,请重试!');
-              }
-            };
+            if (file.type === 'application/json') {
+              const reader = new FileReader();
+              reader.readAsText(file);
+              reader.onload = (result: any) => {
+                try {
+                  const data = JSON.parse(result.target.result);
+                  service.batchAdd(data).subscribe((resp) => {
+                    if (resp.status === 200) {
+                      message.success('导入成功');
+                      actionRef.current?.reload();
+                    }
+                  });
+                } catch (error) {
+                  message.error('导入失败,请重试!');
+                }
+              };
+            } else {
+              message.error('请上传json格式');
+            }
           }}
           }}
         >
         >
-          <Button disabled={getButtonPermission('system/Permission', ['import'])}>导入</Button>
+          <Tooltip
+            title={
+              getButtonPermission('system/Permission', ['import']) ? '暂无权限,请联系管理员' : ''
+            }
+          >
+            <Button disabled={getButtonPermission('system/Permission', ['import'])}>导入</Button>
+          </Tooltip>
         </Upload>
         </Upload>
       </Menu.Item>
       </Menu.Item>
       <Menu.Item key="export">
       <Menu.Item key="export">
@@ -84,7 +94,13 @@ const Permission: React.FC = observer(() => {
             });
             });
           }}
           }}
         >
         >
-          <Button disabled={getButtonPermission('system/Permission', ['export'])}>导出</Button>
+          <Tooltip
+            title={
+              getButtonPermission('system/Permission', ['export']) ? '暂无权限,请联系管理员' : ''
+            }
+          >
+            <Button disabled={getButtonPermission('system/Permission', ['export'])}>导出</Button>
+          </Tooltip>
         </Popconfirm>
         </Popconfirm>
       </Menu.Item>
       </Menu.Item>
     </Menu>
     </Menu>
@@ -202,7 +218,11 @@ const Permission: React.FC = observer(() => {
           type={'link'}
           type={'link'}
           key={'delete'}
           key={'delete'}
           style={{ padding: 0 }}
           style={{ padding: 0 }}
+          disabled={!!record.status}
           isPermission={permission.delete}
           isPermission={permission.delete}
+          tooltip={{
+            title: !!record.status ? '请先禁用,再删除' : '删除',
+          }}
           popConfirm={{
           popConfirm={{
             title: '确认删除',
             title: '确认删除',
             onConfirm: async () => {
             onConfirm: async () => {

+ 1 - 1
src/utils/menu/router.ts

@@ -121,7 +121,7 @@ export enum BUTTON_PERMISSION_ENUM {
   'sync' = 'sync',
   'sync' = 'sync',
   'channel' = 'channel',
   'channel' = 'channel',
   'debug' = 'debug',
   'debug' = 'debug',
-  'log' = 'log'
+  'log' = 'log',
 }
 }
 
 
 // 调试按钮、通知记录、批量导出、批量导入、选择通道、推送、分配资产、绑定用户对应的ID是啥
 // 调试按钮、通知记录、批量导出、批量导入、选择通道、推送、分配资产、绑定用户对应的ID是啥