Browse Source

feat: 物模型指标

sun-chaochao 4 years ago
parent
commit
0c93a6b891

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

@@ -0,0 +1,226 @@
+import { message, Modal } from 'antd';
+import {
+  FormItem,
+  Input,
+  Select,
+  DatePicker,
+  ArrayItems,
+  Form,
+  Checkbox,
+  NumberPicker,
+  FormGrid,
+} 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;

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

@@ -4,8 +4,25 @@
   }
   }
 }
 }
 
 
+.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 {
 .value {
-  width: '100%';
+  width: 100%;
   overflow: hidden;
   overflow: hidden;
   white-space: nowrap;
   white-space: nowrap;
   text-overflow: ellipsis;
   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 { message, Space, Spin, Tooltip, Card } from 'antd';
 import type { PropertyMetadata } from '@/pages/device/Product/typings';
 import type { PropertyMetadata } from '@/pages/device/Product/typings';
 import { useState } from 'react';
 import { useState } from 'react';
 import { service } from '@/pages/device/Instance';
 import { service } from '@/pages/device/Instance';
@@ -8,6 +12,7 @@ import { useParams } from 'umi';
 import PropertyLog from '@/pages/device/Instance/Detail/MetadataLog/Property';
 import PropertyLog from '@/pages/device/Instance/Detail/MetadataLog/Property';
 import EditProperty from '@/pages/device/Instance/Detail/Running/Property/EditProperty';
 import EditProperty from '@/pages/device/Instance/Detail/Running/Property/EditProperty';
 import moment from 'moment';
 import moment from 'moment';
+import Indicators from './Indicators';
 import './PropertyCard.less';
 import './PropertyCard.less';
 
 
 interface Props {
 interface Props {
@@ -33,36 +38,39 @@ const Property = (props: Props) => {
 
 
   const [visible, setVisible] = useState<boolean>(false);
   const [visible, setVisible] = useState<boolean>(false);
   const [editVisible, setEditVisible] = useState<boolean>(false);
   const [editVisible, setEditVisible] = useState<boolean>(false);
+  const [indicatorVisible, setIndicatorVisible] = useState<boolean>(false);
 
 
   const renderTitle = (title: string) => {
   const renderTitle = (title: string) => {
     return (
     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') && (
           {(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={() => {
                   onClick={() => {
-                    setEditVisible(true);
+                    setIndicatorVisible(true);
                   }}
                   }}
                 />
                 />
               </Tooltip>
               </Tooltip>
-              <Divider type="vertical" />
-            </>
-          )}
+            )}
           <Tooltip placement="top" title="获取最新属性值">
           <Tooltip placement="top" title="获取最新属性值">
             <SyncOutlined onClick={refreshProperty} />
             <SyncOutlined onClick={refreshProperty} />
           </Tooltip>
           </Tooltip>
-          <Divider type="vertical" />
           <Tooltip placement="top" title="详情">
           <Tooltip placement="top" title="详情">
             <UnorderedListOutlined
             <UnorderedListOutlined
               onClick={() => {
               onClick={() => {
@@ -70,15 +78,16 @@ const Property = (props: Props) => {
               }}
               }}
             />
             />
           </Tooltip>
           </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}>
       <Spin spinning={loading}>
         <div>
         <div>
+          <div>{renderTitle(data?.name || '')}</div>
           <div className="value" style={{ fontWeight: 700, fontSize: '24px', color: '#323130' }}>
           <div className="value" style={{ fontWeight: 700, fontSize: '24px', color: '#323130' }}>
             {value?.formatValue || '--'}
             {value?.formatValue || '--'}
           </div>
           </div>
@@ -98,7 +107,15 @@ const Property = (props: Props) => {
         data={data}
         data={data}
       />
       />
       <PropertyLog data={data} visible={visible} close={() => setVisible(false)} />
       <PropertyLog data={data} visible={visible} close={() => setVisible(false)} />
-    </ProCard>
+      {indicatorVisible && (
+        <Indicators
+          data={data}
+          onCancel={() => {
+            setIndicatorVisible(false);
+          }}
+        />
+      )}
+    </Card>
   );
   );
 };
 };
 export default Property;
 export default Property;

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

@@ -265,6 +265,17 @@ class Service extends BaseService<DeviceInstance> {
         paging: false,
         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;
 export default Service;

+ 224 - 1
src/pages/device/components/Metadata/Base/Edit/index.tsx

@@ -2,11 +2,14 @@ import { Button, Drawer, Dropdown, Menu, message } from 'antd';
 import { createSchemaField, observer } from '@formily/react';
 import { createSchemaField, observer } from '@formily/react';
 import MetadataModel from '../model';
 import MetadataModel from '../model';
 import type { Field, IFieldState } from '@formily/core';
 import type { Field, IFieldState } from '@formily/core';
-import { createForm, registerValidateRules } from '@formily/core';
+import { createForm, onFieldReact, registerValidateRules } from '@formily/core';
 import {
 import {
   ArrayItems,
   ArrayItems,
+  Checkbox,
+  DatePicker,
   Editable,
   Editable,
   Form,
   Form,
+  FormGrid,
   FormItem,
   FormItem,
   FormLayout,
   FormLayout,
   Input,
   Input,
@@ -54,6 +57,44 @@ const Edit = observer((props: Props) => {
     () =>
     () =>
       createForm({
       createForm({
         initialValues: MetadataModel.item as Record<string, unknown>,
         initialValues: MetadataModel.item as Record<string, unknown>,
+        effects: () => {
+          onFieldReact('expands.metrics.*.*', (field, form1) => {
+            console.log('指标配置');
+            const type = field.query('valueType.type').take() as Field;
+            console.log(type.value, 'value');
+
+            const componentMap = {
+              int: 'NumberPicker',
+              long: 'NumberPicker',
+              float: 'NumberPicker',
+              double: 'NumberPicker',
+              number: 'NumberPicker',
+              date: 'DatePicker',
+              boolean: 'Select',
+            };
+
+            form1.setFieldState('expands.metrics.*.edit.space.value.*', (state) => {
+              state.componentType = componentMap[type.value] || 'Input';
+              if (type.value === 'date') {
+                state.componentProps = {
+                  showTime: true,
+                };
+              } else if (type.value === 'boolean') {
+                state.componentType = 'Select';
+                // 获取 boolean配置的值
+                const values = form1.getValuesIn('valueType');
+                state.dataSource = [
+                  { label: values.trueText, value: values.trueValue },
+                  { label: values.falseText, value: values.falseValue },
+                ];
+              }
+            });
+          });
+          /// 处理Boolean 类型
+          // expands.metrics.0.edit.space.value.0 路径
+          // const metricsPath = field.query('expands.metrics.value.0');
+          // form.setValuesIn('expands.metrics.value.0', 'testtttt')
+        },
       }),
       }),
     [],
     [],
   );
   );
@@ -102,6 +143,9 @@ const Edit = observer((props: Props) => {
       BooleanEnum,
       BooleanEnum,
       ConfigParam,
       ConfigParam,
       FRuleEditor,
       FRuleEditor,
+      Checkbox,
+      FormGrid,
+      DatePicker,
     },
     },
     scope: {
     scope: {
       async asyncOtherConfig(field: Field) {
       async asyncOtherConfig(field: Field) {
@@ -616,6 +660,185 @@ const Edit = observer((props: Props) => {
             'x-component': 'ConfigParam',
             'x-component': 'ConfigParam',
             'x-reactions': '{{asyncOtherConfig}}',
             'x-reactions': '{{asyncOtherConfig}}',
           },
           },
+          // 指标
+          metrics: {
+            type: 'array',
+            'x-component': 'ArrayItems',
+            'x-decorator': 'FormItem',
+            title: '指标配置',
+            items: {
+              type: 'object',
+              'x-decorator': 'ArrayItems.Item',
+              properties: {
+                left: {
+                  type: 'void',
+                  'x-component': 'Space',
+                  properties: {
+                    sort: {
+                      type: 'void',
+                      'x-decorator': 'FormItem',
+                      'x-component': 'ArrayItems.SortHandle',
+                    },
+                    index: {
+                      type: 'void',
+                      'x-decorator': 'FormItem',
+                      'x-component': 'ArrayItems.Index',
+                    },
+                  },
+                },
+                edit: {
+                  type: 'void',
+                  'x-component': 'Editable.Popover',
+                  title: '指标数据',
+                  properties: {
+                    id: {
+                      // 标识
+                      title: '标识',
+                      'x-decorator': 'FormItem',
+                      'x-component': 'Input',
+                      'x-decorator-props': {
+                        labelAlign: 'left',
+                        layout: 'vertical',
+                      },
+                    },
+                    name: {
+                      // 名称
+                      title: '名称',
+                      'x-decorator': 'FormItem',
+                      'x-component': 'Input',
+                      'x-decorator-props': {
+                        labelAlign: 'left',
+                        layout: 'vertical',
+                      },
+                    },
+                    space: {
+                      type: 'void',
+                      title: '指标值',
+                      'x-decorator': 'FormItem',
+                      'x-component': 'FormGrid',
+                      'x-decorator-props': {
+                        labelAlign: 'left',
+                        layout: 'vertical',
+                      },
+                      'x-component-props': {
+                        maxColumns: 12,
+                        minColumns: 12,
+                      },
+                      properties: {
+                        'value[0]': {
+                          'x-decorator': 'FormItem',
+                          'x-component': 'Input',
+                          'x-decorator-props': {
+                            gridSpan: 5,
+                          },
+                          'x-reactions': {
+                            dependencies: ['..range', 'valueType.type'],
+                            fulfill: {
+                              state: {
+                                decoratorProps: {
+                                  gridSpan: '{{!!$deps[0]?5:$deps[1]==="boolean"?12:10}}',
+                                },
+                                componentType:
+                                  '{{["int","long","double","float"].includes($deps[1])?"NumberPicker":["date"].includes($deps[1])?"DatePicker":"Input"}}',
+                              },
+                            },
+                          },
+                          // 根据数据类型来渲染不同的组件
+                        },
+                        'value[1]': {
+                          title: '~',
+                          'x-decorator': 'FormItem',
+                          'x-component': 'Input',
+                          'x-decorator-props': {
+                            gridSpan: 5,
+                          },
+                          'x-reactions': [
+                            {
+                              dependencies: ['..range', 'valueType.type'],
+                              fulfill: {
+                                state: {
+                                  visible: '{{!!$deps[0]}}',
+                                  componentType:
+                                    '{{["int","long","double","float"].includes($deps[1])?"NumberPicker":["date"].includes($deps[1])?"DatePicker":"Input"}}',
+                                },
+                              },
+                            },
+                            {
+                              dependencies: ['valueType.type'],
+                              fulfill: {
+                                state: {
+                                  visible: '{{!$deps[0]==="boolean"}}',
+                                },
+                              },
+                            },
+                          ],
+                        },
+                        // 根据数据类型来渲染不同的组件
+                        range: {
+                          type: 'boolean',
+                          default: false,
+                          'x-decorator': 'FormItem',
+                          'x-component': 'Checkbox',
+                          'x-component-props': {
+                            children: '范围',
+                          },
+                          'x-decorator-props': {
+                            gridSpan: 2,
+                          },
+                          'x-reactions': {
+                            dependencies: ['valueType.type'],
+                            when: '{{$deps[0]==="boolean"}}',
+                            fulfill: {
+                              state: {
+                                visible: false,
+                                decoratorProps: {
+                                  gridSpan: 0,
+                                },
+                              },
+                            },
+                            otherwise: {
+                              state: {
+                                visible: true,
+                                decoratorProps: {
+                                  gridSpan: 2,
+                                },
+                              },
+                            },
+                          },
+                        },
+                      },
+                    },
+                  },
+                },
+                right: {
+                  type: 'void',
+                  'x-component': 'Space',
+                  properties: {
+                    remove: {
+                      type: 'void',
+                      'x-component': 'ArrayItems.Remove',
+                    },
+                  },
+                },
+              },
+            },
+            properties: {
+              addition: {
+                type: 'void',
+                title: '添加指标',
+                'x-component': 'ArrayItems.Addition',
+              },
+            },
+            'x-reactions': {
+              dependencies: ['valueType.type'],
+              fulfill: {
+                state: {
+                  visible:
+                    "{{['int','float','double','long','date','string','boolean'].includes($deps[0])}}",
+                },
+              },
+            },
+          },
         },
         },
       },
       },
     },
     },