Bladeren bron

merge(device): device

fix: 设备详情修改
Lind 3 jaren geleden
bovenliggende
commit
23e41386ff

+ 45 - 76
src/pages/device/Instance/Detail/Config/Tags/index.tsx

@@ -1,8 +1,10 @@
-import { createSchemaField, FormProvider, observer } from '@formily/react';
+import { createSchemaField, FormProvider } from '@formily/react';
 import { Editable, FormItem, Input, ArrayTable } from '@formily/antd';
 import { createForm } from '@formily/core';
-import { Card } from 'antd';
+import { Card, message } from 'antd';
 import { useIntl } from '@@/plugin-locale/localeExports';
+import { InstanceModel, service } from '@/pages/device/Instance';
+import { useEffect, useState } from 'react';
 
 const SchemaField = createSchemaField({
   components: {
@@ -12,14 +14,29 @@ const SchemaField = createSchemaField({
     ArrayTable,
   },
 });
-const form = createForm();
 
-const Tags = observer(() => {
+const Tags = () => {
   const intl = useIntl();
+  const [tags, setTags] = useState<any[]>([]);
+
+  const tag = InstanceModel.detail?.tags;
+
+  useEffect(() => {
+    if (tag) {
+      setTags([...tag] || []);
+    }
+  }, [tag]);
+
+  const form = createForm({
+    initialValues: {
+      tags: tags,
+    },
+  });
+
   const schema = {
     type: 'object',
     properties: {
-      array: {
+      tags: {
         type: 'array',
         'x-decorator': 'FormItem',
         'x-component': 'ArrayTable',
@@ -33,34 +50,17 @@ const Tags = observer(() => {
             column1: {
               type: 'void',
               'x-component': 'ArrayTable.Column',
-              'x-component-props': {
-                width: 50,
-                title: intl.formatMessage({
-                  id: 'pages.device.instanceDetail.detail.sort',
-                  defaultMessage: '排序',
-                }),
-                align: 'center',
-              },
-              properties: {
-                sort: {
-                  type: 'void',
-                  'x-component': 'ArrayTable.SortHandle',
-                },
-              },
-            },
-            column3: {
-              type: 'void',
-              'x-component': 'ArrayTable.Column',
               'x-component-props': { width: 200, title: 'ID' },
               properties: {
-                a1: {
+                key: {
                   type: 'string',
-                  'x-decorator': 'Editable',
+                  'x-decorator': 'FormItem',
                   'x-component': 'Input',
+                  'x-disabled': true,
                 },
               },
             },
-            column4: {
+            column2: {
               type: 'void',
               'x-component': 'ArrayTable.Column',
               'x-component-props': {
@@ -71,14 +71,14 @@ const Tags = observer(() => {
                 }),
               },
               properties: {
-                a2: {
+                name: {
                   type: 'string',
-                  'x-decorator': 'FormItem',
+                  'x-decorator': 'Editable',
                   'x-component': 'Input',
                 },
               },
             },
-            column5: {
+            column3: {
               type: 'void',
               'x-component': 'ArrayTable.Column',
               'x-component-props': {
@@ -89,56 +89,13 @@ const Tags = observer(() => {
                 }),
               },
               properties: {
-                a3: {
+                value: {
                   type: 'string',
-                  'x-decorator': 'FormItem',
+                  'x-decorator': 'Editable',
                   'x-component': 'Input',
                 },
               },
             },
-            column6: {
-              type: 'void',
-              'x-component': 'ArrayTable.Column',
-              'x-component-props': {
-                title: intl.formatMessage({
-                  id: 'pages.data.option',
-                  defaultMessage: '操作',
-                }),
-                dataIndex: 'operations',
-                width: 200,
-                fixed: 'right',
-              },
-              properties: {
-                item: {
-                  type: 'void',
-                  'x-component': 'FormItem',
-                  properties: {
-                    remove: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.Remove',
-                    },
-                    moveDown: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.MoveDown',
-                    },
-                    moveUp: {
-                      type: 'void',
-                      'x-component': 'ArrayTable.MoveUp',
-                    },
-                  },
-                },
-              },
-            },
-          },
-        },
-        properties: {
-          add: {
-            type: 'void',
-            'x-component': 'ArrayTable.Addition',
-            title: intl.formatMessage({
-              id: 'pages.device.instanceDetail.detail.value',
-              defaultMessage: '添加标签',
-            }),
           },
         },
       },
@@ -151,7 +108,18 @@ const Tags = observer(() => {
         defaultMessage: '标签',
       })}
       extra={
-        <a>
+        <a
+          onClick={async () => {
+            const values = (await form.submit()) as any;
+            if (values?.tags) {
+              const resp = await service.saveTags(InstanceModel.detail?.id || '', values.tags);
+              if (resp.status === 200) {
+                InstanceModel.detail = { ...InstanceModel.detail, tags: values.tags };
+                message.success('操作成功!');
+              }
+            }
+          }}
+        >
           {intl.formatMessage({
             id: 'pages.device.instanceDetail.save',
             defaultMessage: '保存',
@@ -164,5 +132,6 @@ const Tags = observer(() => {
       </FormProvider>
     </Card>
   );
-});
+};
+
 export default Tags;

+ 79 - 14
src/pages/device/Instance/Detail/Config/index.tsx

@@ -1,4 +1,4 @@
-import { Card, Divider, Empty } from 'antd';
+import { Card, Divider, Empty, message, Popconfirm, Space, Tooltip } from 'antd';
 import { InstanceModel, service } from '@/pages/device/Instance';
 import { useEffect, useState } from 'react';
 import { createSchemaField } from '@formily/react';
@@ -8,6 +8,7 @@ import { Form, FormGrid, FormItem, FormLayout, Input, Password, PreviewText } fr
 import { createForm } from '@formily/core';
 import { history, useParams } from 'umi';
 import Tags from '@/pages/device/Instance/Detail/Config/Tags';
+import Icon from '@ant-design/icons';
 
 const componentMap = {
   string: 'Input',
@@ -36,13 +37,19 @@ const Config = () => {
     initialValues: InstanceModel.detail?.configuration,
   });
 
-  const id = InstanceModel.current?.id;
+  const id = InstanceModel.detail?.id;
 
   useEffect(() => {
     if (id) {
       service.getConfigMetadata(id).then((config) => {
         setMetadata(config?.result);
       });
+      setState(
+        !!(
+          InstanceModel.detail?.configuration &&
+          Object.keys(InstanceModel.detail?.configuration).length > 0
+        ),
+      );
     }
 
     return () => {};
@@ -93,18 +100,76 @@ const Config = () => {
         };
 
         return (
-          <Card
-            title={item.name}
-            extra={<a onClick={() => setState(!state)}>{state ? '编辑' : '保存'}</a>}
-          >
-            <PreviewText.Placeholder value="-">
-              <Form form={form}>
-                <FormLayout labelCol={6} wrapperCol={16}>
-                  <SchemaField schema={itemSchema} />
-                </FormLayout>
-              </Form>
-            </PreviewText.Placeholder>
-          </Card>
+          <>
+            <Divider />
+            <Card
+              title={item.name}
+              extra={
+                <Space>
+                  <a
+                    onClick={async () => {
+                      const values = (await form.submit()) as any;
+                      const resp = await service.modify(id || '', {
+                        id,
+                        configuration: { ...values },
+                      });
+                      if (resp.status === 200) {
+                        InstanceModel.detail = {
+                          ...InstanceModel.detail,
+                          configuration: { ...values },
+                        };
+                        setState(!state);
+                      }
+                    }}
+                  >
+                    {state ? '编辑' : '保存'}
+                  </a>
+                  {InstanceModel.detail.state?.value !== 'notActive' && (
+                    <Popconfirm
+                      title="确认重新应用该配置?"
+                      onConfirm={async () => {
+                        const resp = await service.deployDevice(id || '');
+                        if (resp.status === 200) {
+                          message.success('操作成功');
+                        }
+                      }}
+                    >
+                      <a>应用配置</a>
+                      <Tooltip title="修改配置后需重新应用后才能生效。">
+                        <Icon type="question-circle-o" />
+                      </Tooltip>
+                    </Popconfirm>
+                  )}
+                  {InstanceModel.detail?.aloneConfiguration && (
+                    <Popconfirm
+                      title="确认恢复默认配置?"
+                      onConfirm={async () => {
+                        const resp = await service.configurationReset(id || '');
+                        if (resp.status === 200) {
+                          message.success('恢复默认配置成功');
+                        }
+                      }}
+                    >
+                      <a>恢复默认</a>
+                      <Tooltip
+                        title={`该设备单独编辑过配置信息,点击此将恢复成默认的配置信息,请谨慎操作。`}
+                      >
+                        <Icon type="question-circle-o" />
+                      </Tooltip>
+                    </Popconfirm>
+                  )}
+                </Space>
+              }
+            >
+              <PreviewText.Placeholder value="-">
+                <Form form={form}>
+                  <FormLayout labelCol={6} wrapperCol={16}>
+                    <SchemaField schema={itemSchema} />
+                  </FormLayout>
+                </Form>
+              </PreviewText.Placeholder>
+            </Card>
+          </>
         );
       })
     ) : (

+ 111 - 75
src/pages/device/Instance/Detail/Info/index.tsx

@@ -1,87 +1,123 @@
-import { Descriptions } from 'antd';
+import { Card, Descriptions } from 'antd';
 import { InstanceModel } from '@/pages/device/Instance';
 import moment from 'moment';
 import { observer } from '@formily/react';
 import { useIntl } from '@@/plugin-locale/localeExports';
+import Config from '@/pages/device/Instance/Detail/Config';
+import Save from '../../Save';
+import { useState } from 'react';
+import type { DeviceInstance } from '../../typings';
 
 const Info = observer(() => {
   const intl = useIntl();
+  const [visible, setVisible] = useState<boolean>(false);
+
   return (
     <>
-      <Descriptions size="small" column={3}>
-        <Descriptions.Item
-          label={intl.formatMessage({
-            id: 'pages.table.deviceId',
-            defaultMessage: '设备ID',
-          })}
-        >
-          {InstanceModel.detail?.id}
-        </Descriptions.Item>
-        <Descriptions.Item
-          label={intl.formatMessage({
-            id: 'pages.table.productName',
-            defaultMessage: '产品名称',
-          })}
-        >
-          {InstanceModel.detail?.name}
-        </Descriptions.Item>
-        <Descriptions.Item
-          label={intl.formatMessage({
-            id: 'pages.device.instanceDetail.deviceType',
-            defaultMessage: '设备类型',
-          })}
-        >
-          {InstanceModel.detail?.deviceType?.text}
-        </Descriptions.Item>
-        <Descriptions.Item
-          label={intl.formatMessage({
-            id: 'pages.device.instanceDetail.transportProtocol',
-            defaultMessage: '链接协议',
-          })}
-        >
-          {InstanceModel.detail?.protocolName}
-        </Descriptions.Item>
-        <Descriptions.Item
-          label={intl.formatMessage({
-            id: 'pages.device.instanceDetail.protocolName',
-            defaultMessage: '消息协议',
-          })}
-        >
-          {InstanceModel.detail?.transport}
-        </Descriptions.Item>
-        <Descriptions.Item
-          label={intl.formatMessage({
-            id: 'pages.device.instanceDetail.createTime',
-            defaultMessage: '创建时间',
-          })}
-        >
-          {moment(InstanceModel.detail?.createTime).format('YYYY-MM-DD HH:mm:ss')}
-        </Descriptions.Item>
-        <Descriptions.Item
-          label={intl.formatMessage({
-            id: 'pages.device.instanceDetail.registerTime',
-            defaultMessage: '注册时间',
-          })}
-        >
-          {InstanceModel.detail?.createTime}
-        </Descriptions.Item>
-        <Descriptions.Item
-          label={intl.formatMessage({
-            id: 'pages.device.instanceDetail.lastTimeOnline',
-            defaultMessage: '最后上线时间',
-          })}
-        >
-          {InstanceModel.detail?.createTime}
-        </Descriptions.Item>
-        <Descriptions.Item
-          label={intl.formatMessage({
-            id: 'pages.table.description',
-            defaultMessage: '说明',
-          })}
-        >
-          {InstanceModel.detail?.description}
-        </Descriptions.Item>
-      </Descriptions>
+      <Card
+        title={'设备信息'}
+        extra={
+          <a
+            onClick={() => {
+              setVisible(true);
+            }}
+          >
+            编辑
+          </a>
+        }
+      >
+        <Descriptions size="small" column={3} bordered>
+          <Descriptions.Item
+            label={intl.formatMessage({
+              id: 'pages.table.deviceId',
+              defaultMessage: '设备ID',
+            })}
+          >
+            {InstanceModel.detail?.id}
+          </Descriptions.Item>
+          <Descriptions.Item
+            label={intl.formatMessage({
+              id: 'pages.table.productName',
+              defaultMessage: '产品名称',
+            })}
+          >
+            {InstanceModel.detail?.name}
+          </Descriptions.Item>
+          <Descriptions.Item
+            label={intl.formatMessage({
+              id: 'pages.device.instanceDetail.deviceType',
+              defaultMessage: '设备类型',
+            })}
+          >
+            {InstanceModel.detail?.deviceType?.text}
+          </Descriptions.Item>
+          <Descriptions.Item
+            label={intl.formatMessage({
+              id: 'pages.device.instanceDetail.transportProtocol',
+              defaultMessage: '链接协议',
+            })}
+          >
+            {InstanceModel.detail?.protocolName}
+          </Descriptions.Item>
+          <Descriptions.Item
+            label={intl.formatMessage({
+              id: 'pages.device.instanceDetail.protocolName',
+              defaultMessage: '消息协议',
+            })}
+          >
+            {InstanceModel.detail?.transport}
+          </Descriptions.Item>
+          <Descriptions.Item
+            label={intl.formatMessage({
+              id: 'pages.device.instanceDetail.createTime',
+              defaultMessage: '创建时间',
+            })}
+          >
+            {moment(InstanceModel.detail?.createTime).format('YYYY-MM-DD HH:mm:ss')}
+          </Descriptions.Item>
+          <Descriptions.Item
+            label={intl.formatMessage({
+              id: 'pages.device.instanceDetail.registerTime',
+              defaultMessage: '注册时间',
+            })}
+          >
+            {moment(InstanceModel.detail?.registerTime).format('YYYY-MM-DD HH:mm:ss')}
+          </Descriptions.Item>
+          <Descriptions.Item
+            label={intl.formatMessage({
+              id: 'pages.device.instanceDetail.lastTimeOnline',
+              defaultMessage: '最后上线时间',
+            })}
+          >
+            {InstanceModel.detail?.onlineTime
+              ? moment(InstanceModel.detail?.onlineTime).format('YYYY-MM-DD HH:mm:ss')
+              : '--'}
+          </Descriptions.Item>
+          <Descriptions.Item
+            label={intl.formatMessage({
+              id: 'pages.table.description',
+              defaultMessage: '说明',
+            })}
+          >
+            {InstanceModel.detail?.description}
+          </Descriptions.Item>
+        </Descriptions>
+      </Card>
+      <Config />
+      <Save
+        data={{ ...InstanceModel?.detail, describe: InstanceModel?.detail?.description || '' }}
+        close={(data: DeviceInstance | undefined) => {
+          setVisible(false);
+          if (data) {
+            InstanceModel.detail = {
+              ...InstanceModel.detail,
+              name: data?.name,
+              description: data?.describe,
+            };
+          }
+        }}
+        visible={visible}
+      />
     </>
   );
 });

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

@@ -5,7 +5,6 @@ import { Badge, Button, Card, Divider, message, Tooltip } from 'antd';
 import { useEffect, useState } from 'react';
 import { statusMap } from '@/pages/device/Product';
 import { observer } from '@formily/react';
-import Config from '@/pages/device/Instance/Detail/Config';
 import Log from '@/pages/device/Instance/Detail/Log';
 import Alarm from '@/pages/device/components/Alarm';
 import Info from '@/pages/device/Instance/Detail/Info';
@@ -60,9 +59,9 @@ const InstanceDetail = observer(() => {
       key: 'detail',
       tab: intl.formatMessage({
         id: 'pages.device.instanceDetail.detail',
-        defaultMessage: '配置信息',
+        defaultMessage: '实例信息',
       }),
-      component: <Config />,
+      component: <Info />,
     },
     {
       key: 'running',
@@ -152,7 +151,7 @@ const InstanceDetail = observer(() => {
       onBack={history.goBack}
       onTabChange={setTab}
       tabList={list}
-      content={<Info />}
+      // content={<Info />}
       title={
         <>
           {InstanceModel.detail.name}

+ 1 - 1
src/pages/device/Instance/Import/index.tsx

@@ -16,7 +16,7 @@ import { downloadFile } from '@/utils/util';
 interface Props {
   visible: boolean;
   close: () => void;
-  data?: DeviceInstance;
+  data: Partial<DeviceInstance>;
 }
 
 const FileFormat = (props: any) => {

+ 11 - 15
src/pages/device/Instance/Save/index.tsx

@@ -11,15 +11,21 @@ import { useEffect, useState } from 'react';
 
 interface Props {
   visible: boolean;
-  close: () => void;
-  data?: DeviceInstance;
+  close: (data?: DeviceInstance) => void;
+  data: Partial<DeviceInstance>;
 }
 
 const Save = (props: Props) => {
   const { visible, close, data } = props;
   const [productList, setProductList] = useState<any[]>([]);
   const form = createForm({
-    initialValues: data,
+    initialValues: {
+      id: data?.id,
+      name: data?.name,
+      productName: data?.productName,
+      productId: data?.productId,
+      describe: data?.describe,
+    },
   });
 
   useEffect(() => {
@@ -54,7 +60,7 @@ const Save = (props: Props) => {
     const resp = (await service.update(values)) as any;
     if (resp.status === 200) {
       message.success('保存成功');
-      props.close();
+      props.close(values);
     }
   };
   const schema: ISchema = {
@@ -76,17 +82,6 @@ const Save = (props: Props) => {
             'x-decorator-props': {
               tooltip: <div>若不填写,系统将自动生成唯一ID</div>,
             },
-            // 'x-validator': `{{(value) => {
-            //     return service.isExists(value).then(resp => {
-            //         if(resp.status === 200){
-            //             if(!resp.result){
-            //                 resolve('已存在该设备ID')
-            //             }
-            //         } else {
-            //             resolve('')
-            //         }
-            //     })
-            //   }}}`
           },
           name: {
             title: '名称',
@@ -105,6 +100,7 @@ const Save = (props: Props) => {
           },
           productId: {
             title: '所属产品',
+            'x-disabled': !!data?.id,
             required: true,
             'x-component': 'Select',
             'x-decorator': 'FormItem',

+ 15 - 20
src/pages/device/Instance/index.tsx

@@ -24,7 +24,6 @@ import Save from './Save';
 import Export from './Export';
 import Import from './Import';
 import Process from './Process';
-import encodeQuery from '@/utils/encodeQuery';
 import SearchComponent from '@/components/SearchComponent';
 import SystemConst from '@/utils/const';
 import Token from '@/utils/token';
@@ -38,14 +37,14 @@ statusMap.set('offline', 'error');
 statusMap.set('notActive', 'processing');
 
 export const InstanceModel = model<{
-  current: DeviceInstance | undefined;
+  current: Partial<DeviceInstance>;
   detail: Partial<DeviceInstance>;
   config: any;
   metadataItem: MetadataItem;
   params: Set<string>; // 处理无限循环Card
   active?: string; // 当前编辑的Card
 }>({
-  current: undefined,
+  current: {},
   detail: {},
   config: {},
   metadataItem: {},
@@ -60,7 +59,7 @@ const Instance = () => {
   const [operationVisible, setOperationVisible] = useState<boolean>(false);
   const [type, setType] = useState<'active' | 'sync'>('active');
   const [api, setApi] = useState<string>('');
-  const [current, setCurrent] = useState<DeviceInstance>();
+  const [current, setCurrent] = useState<Partial<DeviceInstance>>({});
   const [searchParams, setSearchParams] = useState<any>({});
   const [bindKeys, setBindKeys] = useState<any[]>([]);
   const intl = useIntl();
@@ -170,20 +169,6 @@ const Instance = () => {
             <EyeOutlined />
           </Tooltip>
         </Link>,
-        // <a key="editable" onClick={() => {
-        //   setVisible(true)
-        //   setCurrent(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({
@@ -363,7 +348,17 @@ const Instance = () => {
         actionRef={actionRef}
         params={searchParams}
         options={{ fullScreen: true }}
-        request={(params) => service.query(encodeQuery({ ...params, sorts: { id: 'ascend' } }))}
+        request={(params) =>
+          service.query({
+            ...params,
+            sorts: [
+              {
+                name: 'id',
+                order: 'ascend',
+              },
+            ],
+          })
+        }
         rowKey="id"
         search={false}
         pagination={{ pageSize: 10 }}
@@ -377,7 +372,7 @@ const Instance = () => {
           <Button
             onClick={() => {
               setVisible(true);
-              setCurrent(undefined);
+              setCurrent({});
             }}
             key="button"
             icon={<PlusOutlined />}

+ 10 - 0
src/pages/device/Instance/service.ts

@@ -149,6 +149,16 @@ class Service extends BaseService<DeviceInstance> {
       method: 'POST',
       data,
     });
+  public configurationReset = (deviceId: string) =>
+    request(`/${SystemConst.API_BASE}/device-instance/${deviceId}/configuration/_reset`, {
+      method: 'PUT',
+    });
+
+  public saveTags = (deviceId: string, data: any) =>
+    request(`/${SystemConst.API_BASE}/device-instance/${deviceId}/tag`, {
+      method: 'PATCH',
+      data,
+    });
 }
 
 export default Service;