Преглед изворни кода

merge(instance): instance func

Next xyh
Lind пре 3 година
родитељ
комит
4b01bc7a5e

+ 2 - 0
src/locales/zh-CN/pages.ts

@@ -29,6 +29,8 @@ export default {
   'pages.data.option.record': '通知记录',
   'pages.data.option.save': '保存',
   'pages.data.option.assets': '资产分配',
+  'pages.data.option.invoke': '执行',
+  'pages.data.option.cancel': '取消',
   'pages.searchTable.new': '新建',
   'pages.searchTable.titleStatus': '状态',
   'pages.searchTable.titleStatus.all': '全部',

+ 107 - 0
src/pages/device/Instance/Detail/Functions/AdvancedMode.tsx

@@ -0,0 +1,107 @@
+import type { FunctionMetadata } from '@/pages/device/Product/typings';
+import { useState, useEffect, useRef, useCallback } from 'react';
+import { Input, Button } from 'antd';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import { InstanceModel, service } from '@/pages/device/Instance';
+import MonacoEditor from 'react-monaco-editor';
+import { isObject } from 'lodash';
+
+import './index.less';
+
+type FunctionProps = {
+  data: FunctionMetadata;
+};
+
+export default (props: FunctionProps) => {
+  const intl = useIntl();
+  const [result, setResult] = useState('');
+  const [value, setValue] = useState('');
+  const monacoRef = useRef<any>();
+  /**
+   * 执行
+   */
+  const actionRun = useCallback(async () => {
+    const id = InstanceModel.detail?.id;
+    if (id && value) {
+      try {
+        // 验证是否为json格式
+        const objData: Record<string, unknown> = JSON.parse(value);
+        const isObj = isObject(objData);
+
+        if (!isObj) {
+          return;
+        }
+
+        const res = await service.invokeFunction(id, props.data.id, objData);
+        if (res.status === 200) {
+          setResult(res.result);
+        }
+      } catch (err) {
+        console.error(err);
+      }
+    }
+  }, [value]);
+
+  const handleData = (data: any) => {
+    const obj = {};
+    const properties = data.valueType ? data.valueType.properties : [];
+    for (const datum of properties) {
+      obj[datum.id] = '';
+    }
+    setValue(JSON.stringify(obj));
+
+    if (monacoRef.current) {
+      monacoRef.current.getAction('editor.action.formatDocument').run(); // 格式化
+    }
+  };
+
+  const editorDidMountHandle = (editor: any) => {
+    monacoRef.current = editor;
+  };
+
+  useEffect(() => {
+    handleData(props.data);
+  }, [props.data]);
+
+  return (
+    <div className="device-function-content">
+      <div className="left">
+        <div style={{ marginBottom: 12 }}>
+          <MonacoEditor
+            width={'100%'}
+            height={400}
+            theme="vs-dark"
+            language={'json'}
+            value={value}
+            onChange={(newValue) => {
+              setValue(newValue);
+            }}
+            editorDidMount={editorDidMountHandle}
+          />
+        </div>
+        <div className="button-tool">
+          <Button type={'primary'} onClick={actionRun}>
+            {intl.formatMessage({
+              id: 'pages.data.option.invoke',
+              defaultMessage: '执行',
+            })}
+          </Button>
+          <Button
+            onClick={() => {
+              setValue('');
+            }}
+          >
+            {intl.formatMessage({
+              id: 'pages.data.option.cancel',
+              defaultMessage: '取消',
+            })}
+          </Button>
+        </div>
+      </div>
+      <div className="right">
+        <p>执行结果:</p>
+        <Input.TextArea value={result} rows={6} />
+      </div>
+    </div>
+  );
+};

+ 138 - 0
src/pages/device/Instance/Detail/Functions/form.tsx

@@ -0,0 +1,138 @@
+import type { FunctionMetadata } from '@/pages/device/Product/typings';
+import React, { useState, useEffect, useRef } from 'react';
+import { Input, Button } from 'antd';
+import { useIntl } from '@@/plugin-locale/localeExports';
+import type { ProColumns } from '@jetlinks/pro-table';
+import { EditableProTable } from '@jetlinks/pro-table';
+import type { ProFormInstance } from '@ant-design/pro-form';
+import ProForm from '@ant-design/pro-form';
+import { InstanceModel, service } from '@/pages/device/Instance';
+import './index.less';
+
+type FunctionProps = {
+  data: FunctionMetadata;
+};
+
+type FunctionTableDataType = {
+  id: string;
+  name: string;
+  type: string;
+};
+
+export default (props: FunctionProps) => {
+  const intl = useIntl();
+  const [dataSource, setDataSource] = useState<FunctionTableDataType[]>([]);
+  const [result, setResult] = useState('');
+  const formRef = useRef<ProFormInstance<any>>();
+  const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
+
+  const columns: ProColumns<FunctionTableDataType>[] = [
+    {
+      dataIndex: 'name',
+      title: '1',
+      width: 200,
+      editable: false,
+    },
+    {
+      dataIndex: 'type',
+      title: '2',
+      width: 200,
+      editable: false,
+    },
+    {
+      title: intl.formatMessage({
+        id: 'pages.data.option',
+        defaultMessage: '操作',
+      }),
+      dataIndex: 'value',
+      align: 'center',
+      width: 260,
+    },
+  ];
+
+  const handleDataSource = (data: any) => {
+    const array = [];
+    const properties = data.valueType ? data.valueType.properties : [];
+
+    for (const datum of properties) {
+      array.push({
+        id: datum.id,
+        name: datum.name,
+        type: datum.valueType ? datum.valueType.type : '-',
+        value: '',
+      });
+    }
+    setEditableRowKeys(array.map((d) => d.id));
+    setDataSource(array);
+    formRef.current?.setFieldsValue({
+      table: array,
+    });
+  };
+
+  /**
+   * 执行
+   */
+  const actionRun = async () => {
+    const formData = await formRef.current?.validateFields();
+    console.log(formData);
+    const id = InstanceModel.detail?.id;
+    if (id && formData.table) {
+      const data = {};
+      formData.table.forEach((d: any) => {
+        data[d.id] = d.value;
+      });
+      const res = await service.invokeFunction(id, props.data.id, data);
+      if (res.status === 200) {
+        setResult(res.result);
+      }
+    }
+  };
+
+  useEffect(() => {
+    handleDataSource(props.data);
+  }, [props.data]);
+
+  return (
+    <div className="device-function-content">
+      <div className="left">
+        <ProForm<{ table: FunctionTableDataType[] }> formRef={formRef} submitter={false}>
+          <EditableProTable<FunctionTableDataType>
+            rowKey="id"
+            name="table"
+            columns={columns}
+            recordCreatorProps={false}
+            editable={{
+              type: 'multiple',
+              editableKeys,
+              onChange: setEditableRowKeys,
+            }}
+          />
+        </ProForm>
+        <div className="button-tool">
+          <Button type={'primary'} onClick={actionRun}>
+            {intl.formatMessage({
+              id: 'pages.data.option.invoke',
+              defaultMessage: '执行',
+            })}
+          </Button>
+          <Button
+            onClick={() => {
+              formRef.current?.setFieldsValue({
+                table: dataSource,
+              });
+            }}
+          >
+            {intl.formatMessage({
+              id: 'pages.data.option.cancel',
+              defaultMessage: '取消',
+            })}
+          </Button>
+        </div>
+      </div>
+      <div className="right">
+        <p>执行结果:</p>
+        <Input.TextArea value={result} rows={6} />
+      </div>
+    </div>
+  );
+};

+ 22 - 0
src/pages/device/Instance/Detail/Functions/index.less

@@ -0,0 +1,22 @@
+.device-function-content {
+  display: flex;
+
+  .left {
+    width: 50%;
+
+    .button-tool {
+      display: flex;
+      justify-content: flex-end;
+      width: 100%;
+
+      > button {
+        margin-left: 12px;
+      }
+    }
+  }
+
+  .right {
+    width: 50%;
+    padding-left: 24px;
+  }
+}

+ 29 - 98
src/pages/device/Instance/Detail/Functions/index.tsx

@@ -1,108 +1,39 @@
-import { Card } from 'antd';
-import { Submit, FormButtonGroup, Form, FormItem, Input, Select } from '@formily/antd';
-import { createSchemaField } from '@formily/react';
-import type { Field } from '@formily/core';
-import { createForm, onFieldValueChange } from '@formily/core';
-import type { ISchema } from '@formily/json-schema';
-import FMonacoEditor from '@/components/FMonacoEditor';
-import { InstanceModel, service } from '@/pages/device/Instance';
+import { Card, Tabs } from 'antd';
+import { InstanceModel } from '@/pages/device/Instance';
 import type { FunctionMetadata } from '@/pages/device/Product/typings';
+import FnForm from './form';
+import AModel from './AdvancedMode';
 
 const Functions = () => {
   const functionList = JSON.parse(InstanceModel.detail.metadata || '{}')
     .functions as FunctionMetadata[];
-  const form = createForm({
-    initialValues: {},
-    effects() {
-      onFieldValueChange('function', (field, f) => {
-        const funcId = (field as Field).value;
-        const params = functionList
-          .find((i) => i.id === funcId)
-          ?.inputs?.reduce((previousValue, currentValue) => {
-            let tip = '';
-            const type = currentValue.valueType?.type;
-            if (type === 'enum') {
-              tip = `(${currentValue.valueType.elements
-                .map((e: any) => e.text + e.value)
-                .join(';')})`;
-            } else if (type === 'date') {
-              tip = `(${currentValue.valueType.format})`;
-            }
-            previousValue[`${currentValue.id}`] = `${currentValue.name}${tip}`;
-            return previousValue;
-          }, {});
-        f.setFieldState('params', async (state) => {
-          state.loading = true;
-          state.value = JSON.stringify(params);
-          state.loading = false;
-        });
-      });
-    },
-  });
-  const SchemaField = createSchemaField({
-    components: {
-      FormItem,
-      Input,
-      Select,
-      FMonacoEditor,
-    },
-  });
 
-  const schema: ISchema = {
-    type: 'object',
-    properties: {
-      function: {
-        title: '功能列表',
-        'x-decorator': 'FormItem',
-        'x-component': 'Select',
-        enum: functionList.map((item: any) => ({
-          label: item.name,
-          value: item.id,
-        })),
-      },
-      params: {
-        title: '参数',
-        'x-decorator': 'FormItem',
-        'x-component': 'FMonacoEditor',
-        'x-component-props': {
-          height: 200,
-          theme: 'dark',
-          language: 'json',
-          editorDidMount: (editor1: any) => {
-            editor1.onDidContentSizeChange?.(() => {
-              editor1.getAction('editor.action.formatDocument').run();
-            });
-          },
-        },
-      },
-      result: {
-        title: '结果',
-        'x-decorator': 'FormItem',
-        'x-component': 'Input.TextArea',
-      },
-    },
-  };
-
-  const invokeFunction = async () => {
-    const values: any = await form.submit();
-
-    const id = InstanceModel.detail?.id;
-    if (!values || !id) {
-      return;
-    }
-    const response = await service.invokeFunction(id, values?.function, JSON.parse(values?.params));
-    form.setFieldState('result', (state) => {
-      state.value = response.result;
-    });
-  };
   return (
-    <Card title="功能调试">
-      <Form form={form} layout={'vertical'}>
-        <SchemaField schema={schema} />
-        <FormButtonGroup>
-          <Submit onSubmit={invokeFunction}>提交</Submit>
-        </FormButtonGroup>
-      </Form>
+    <Card>
+      <Tabs>
+        <Tabs.TabPane tab={'精简模式'} key={1}>
+          <Tabs tabPosition="left">
+            {functionList.map((fn) => {
+              return (
+                <Tabs.TabPane tab={fn.name} key={fn.id}>
+                  <FnForm data={fn} />
+                </Tabs.TabPane>
+              );
+            })}
+          </Tabs>
+        </Tabs.TabPane>
+        <Tabs.TabPane tab={'高级模式'} key={2}>
+          <Tabs tabPosition="left">
+            {functionList.map((fn) => {
+              return (
+                <Tabs.TabPane tab={fn.name} key={fn.id}>
+                  <AModel data={fn} />
+                </Tabs.TabPane>
+              );
+            })}
+          </Tabs>
+        </Tabs.TabPane>
+      </Tabs>
     </Card>
   );
 };