lind пре 3 година
родитељ
комит
a3c95c21c3

+ 226 - 0
src/pages/device/Instance/Detail/Running/Property/Indicators.tsx

@@ -0,0 +1,226 @@
+import { message, Modal } from 'antd';
+import {
+  ArrayItems,
+  Checkbox,
+  DatePicker,
+  Form,
+  FormGrid,
+  FormItem,
+  Input,
+  NumberPicker,
+  Select,
+} from '@formily/antd';
+import { createForm } from '@formily/core';
+import { createSchemaField } from '@formily/react';
+
+import type { PropertyMetadata } from '@/pages/device/Product/typings';
+import { useEffect, useState } from 'react';
+import { InstanceModel, service } from '@/pages/device/Instance';
+
+interface Props {
+  data: Partial<PropertyMetadata>;
+  onCancel: () => void;
+}
+
+const componentMap = {
+  int: 'NumberPicker',
+  long: 'NumberPicker',
+  float: 'NumberPicker',
+  double: 'NumberPicker',
+  number: 'NumberPicker',
+  date: 'DatePicker',
+  boolean: 'Select',
+};
+
+const Indicators = (props: Props) => {
+  const { data } = props;
+
+  const [metrics, setMetrics] = useState<any[]>(data.expands?.metrics || []);
+
+  const form = createForm({
+    validateFirst: true,
+    initialValues: {
+      metrics: metrics,
+    },
+  });
+
+  const SchemaField = createSchemaField({
+    components: {
+      FormItem,
+      Input,
+      Checkbox,
+      Select,
+      DatePicker,
+      ArrayItems,
+      NumberPicker,
+      FormGrid,
+    },
+  });
+
+  const schema = {
+    type: 'object',
+    properties: {
+      metrics: {
+        type: 'array',
+        'x-component': 'ArrayItems',
+        'x-decorator': 'FormItem',
+        items: {
+          type: 'object',
+          properties: {
+            name: {
+              type: 'string',
+              title: '',
+              'x-decorator': 'FormItem',
+              'x-component': 'Input',
+              'x-hidden': true,
+            },
+            space: {
+              type: 'void',
+              title: '指标值',
+              'x-decorator': 'FormItem',
+              'x-component': 'FormGrid',
+              'x-decorator-props': {
+                labelAlign: 'left',
+                layout: 'vertical',
+              },
+              'x-component-props': {
+                maxColumns: 12,
+                minColumns: 12,
+              },
+              'x-reactions': [
+                {
+                  dependencies: ['.name'],
+                  fulfill: {
+                    state: {
+                      title: '{{$deps[0]}}',
+                    },
+                  },
+                },
+              ],
+              properties: {
+                'value[0]': {
+                  'x-decorator': 'FormItem',
+                  'x-component': componentMap[data.valueType?.type || ''] || 'Input',
+                  'x-reactions': [
+                    {
+                      dependencies: ['..range', data.valueType?.type],
+                      fulfill: {
+                        state: {
+                          dataSource:
+                            data.valueType?.type === 'boolean'
+                              ? [
+                                  {
+                                    label: data.valueType?.trueText,
+                                    value: data.valueType?.trueValue,
+                                  },
+                                  {
+                                    label: data.valueType?.falseText,
+                                    value: data.valueType?.falseValue,
+                                  },
+                                ]
+                              : [],
+                          decoratorProps: {
+                            gridSpan: '{{!!$deps[0]?5:$deps[1]==="boolean"?12:10}}',
+                          },
+                        },
+                      },
+                    },
+                  ],
+                  'x-decorator-props': {
+                    gridSpan: 5,
+                  },
+                },
+                'value[1]': {
+                  title: '~',
+                  'x-decorator': 'FormItem',
+                  'x-component': componentMap[data.valueType?.type || ''] || 'Input',
+                  'x-reactions': [
+                    {
+                      dependencies: ['..range'],
+                      fulfill: {
+                        state: {
+                          visible: '{{!!$deps[0]}}',
+                        },
+                      },
+                    },
+                  ],
+                  'x-decorator-props': {
+                    gridSpan: 5,
+                  },
+                },
+                range: {
+                  type: 'boolean',
+                  title: '',
+                  'x-decorator': 'FormItem',
+                  'x-component': 'Checkbox',
+                  'x-hidden': true,
+                },
+              },
+            },
+          },
+        },
+      },
+    },
+  };
+
+  console.log(data);
+  console.log(InstanceModel.detail);
+
+  useEffect(() => {
+    if (InstanceModel.detail.id && data.id) {
+      service.queryMetric(InstanceModel.detail.id || '', data.id || '').then((resp) => {
+        if (resp.status === 200) {
+          if ((resp?.result || []).length > 0) {
+            setMetrics(resp.result);
+          } else {
+            setMetrics(data.expands?.metrics || []);
+          }
+        }
+      });
+    }
+  }, [data.id]);
+
+  return (
+    <Modal
+      maskClosable={false}
+      title="编辑指标"
+      visible
+      width={600}
+      onOk={async () => {
+        const params = (await form.submit()) as any;
+        const resp = await service.saveMetric(
+          InstanceModel.detail.id || '',
+          data.id || '',
+          params.metrics,
+        );
+        if (resp.status === 200) {
+          message.success('操作成功!');
+          props.onCancel();
+        }
+      }}
+      onCancel={() => {
+        props.onCancel();
+      }}
+    >
+      <div
+        style={{
+          width: '100%',
+          height: 40,
+          lineHeight: '40px',
+          color: 'rgba(0, 0, 0, 0.55)',
+          backgroundColor: '#f6f6f6',
+          paddingLeft: 10,
+        }}
+      >
+        场景联动页面可引用指标配置触发条件
+      </div>
+      <div style={{ marginTop: '30px' }}>
+        <Form form={form} layout="vertical">
+          <SchemaField schema={schema} />
+        </Form>
+      </div>
+    </Modal>
+  );
+};
+
+export default Indicators;

+ 19 - 1
src/pages/device/Instance/Detail/Running/Property/PropertyCard.less

@@ -4,8 +4,26 @@
   }
 }
 
+.card-title-box {
+  display: flex;
+  justify-content: space-between;
+  width: 100%;
+  margin-bottom: 10px;
+
+  .card-title {
+    width: 60%;
+    margin-right: 10;
+    overflow: hidden;
+    color: 'rgba(0, 0, 0, .65)';
+    font-weight: 400;
+    font-size: 12;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+}
+
 .value {
-  width: '100%';
+  width: 100%;
   overflow: hidden;
   white-space: nowrap;
   text-overflow: ellipsis;

+ 47 - 30
src/pages/device/Instance/Detail/Running/Property/PropertyCard.tsx

@@ -1,6 +1,10 @@
-import { EditOutlined, SyncOutlined, UnorderedListOutlined } from '@ant-design/icons';
-import { Divider, message, Spin, Tooltip } from 'antd';
-import ProCard from '@ant-design/pro-card';
+import {
+  ClockCircleOutlined,
+  EditOutlined,
+  SyncOutlined,
+  UnorderedListOutlined,
+} from '@ant-design/icons';
+import { Card, message, Space, Spin, Tooltip } from 'antd';
 import type { PropertyMetadata } from '@/pages/device/Product/typings';
 import { useState } from 'react';
 import { service } from '@/pages/device/Instance';
@@ -8,6 +12,7 @@ import { useParams } from 'umi';
 import PropertyLog from '@/pages/device/Instance/Detail/MetadataLog/Property';
 import EditProperty from '@/pages/device/Instance/Detail/Running/Property/EditProperty';
 import moment from 'moment';
+import Indicators from './Indicators';
 import './PropertyCard.less';
 
 interface Props {
@@ -33,36 +38,39 @@ const Property = (props: Props) => {
 
   const [visible, setVisible] = useState<boolean>(false);
   const [editVisible, setEditVisible] = useState<boolean>(false);
+  const [indicatorVisible, setIndicatorVisible] = useState<boolean>(false);
 
   const renderTitle = (title: string) => {
     return (
-      <div className="value" style={{ color: 'rgba(0, 0, 0, .65)', fontSize: 14 }}>
-        {title}
-      </div>
-    );
-  };
-
-  return (
-    <ProCard
-      title={renderTitle(data?.name || '')}
-      extra={
-        <>
+      <div className="card-title-box">
+        <div className="card-title">
+          <Tooltip title={title}>{title}</Tooltip>
+        </div>
+        <Space style={{ fontSize: 12 }}>
           {(data.expands?.readOnly === false || data.expands?.readOnly === 'false') && (
-            <>
-              <Tooltip placement="top" title="设置属性至设备">
-                <EditOutlined
+            <Tooltip placement="top" title="设置属性至设备">
+              <EditOutlined
+                onClick={() => {
+                  setEditVisible(true);
+                }}
+              />
+            </Tooltip>
+          )}
+          {(data.expands?.metrics || []).length > 0 &&
+            ['int', 'long', 'float', 'double', 'string', 'boolean', 'date'].includes(
+              data.valueType?.type || '',
+            ) && (
+              <Tooltip placement="top" title="指标">
+                <ClockCircleOutlined
                   onClick={() => {
-                    setEditVisible(true);
+                    setIndicatorVisible(true);
                   }}
                 />
               </Tooltip>
-              <Divider type="vertical" />
-            </>
-          )}
+            )}
           <Tooltip placement="top" title="获取最新属性值">
             <SyncOutlined onClick={refreshProperty} />
           </Tooltip>
-          <Divider type="vertical" />
           <Tooltip placement="top" title="详情">
             <UnorderedListOutlined
               onClick={() => {
@@ -70,15 +78,16 @@ const Property = (props: Props) => {
               }}
             />
           </Tooltip>
-        </>
-      }
-      bordered
-      hoverable
-      colSpan={{ xs: 12, sm: 8, md: 6, lg: 6, xl: 6 }}
-      style={{ backgroundColor: 'rgba(0, 0, 0, .02)' }}
-    >
+        </Space>
+      </div>
+    );
+  };
+
+  return (
+    <Card bordered hoverable style={{ backgroundColor: 'rgba(0, 0, 0, .02)' }}>
       <Spin spinning={loading}>
         <div>
+          <div>{renderTitle(data?.name || '')}</div>
           <div className="value" style={{ fontWeight: 700, fontSize: '24px', color: '#323130' }}>
             {value?.formatValue || '--'}
           </div>
@@ -98,7 +107,15 @@ const Property = (props: Props) => {
         data={data}
       />
       <PropertyLog data={data} visible={visible} close={() => setVisible(false)} />
-    </ProCard>
+      {indicatorVisible && (
+        <Indicators
+          data={data}
+          onCancel={() => {
+            setIndicatorVisible(false);
+          }}
+        />
+      )}
+    </Card>
   );
 };
 export default Property;

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

@@ -265,6 +265,17 @@ class Service extends BaseService<DeviceInstance> {
         paging: false,
       },
     });
+  // 保存设备的物模型指标
+  public saveMetric = (deviceId: string, propertyId: string, data: any) =>
+    request(`/${SystemConst.API_BASE}/device-instance/${deviceId}/metric/property/${propertyId}`, {
+      method: 'PATCH',
+      data,
+    });
+  // 查询设备的物模型指标
+  public queryMetric = (deviceId: string, propertyId: string) =>
+    request(`/${SystemConst.API_BASE}/device-instance/${deviceId}/metric/property/${propertyId}`, {
+      method: 'GET',
+    });
 }
 
 export default Service;