Commit a67c4f60d73f0928d45a44027cb6c3b46976582e

Authored by xp.Huang
2 parents 5860e087 dd853e5b

Merge branch 'ww' into 'main'

model of matter

See merge request huang/yun-teng-iot-front!392
Showing 37 changed files with 1410 additions and 946 deletions
... ... @@ -14,7 +14,7 @@ VITE_PUBLIC_PATH = /
14 14 # VITE_PROXY = [["/api","http://101.133.234.90:8080/api"]]
15 15 # 线上测试环境
16 16 # VITE_PROXY = [["/api","http://localhost:8080/api"],["/thingskit-drawio","http://localhost:3000/"]]
17   -VITE_PROXY = [["/api","https://dev.thingskit.com/api"],["/thingskit-drawio","http://localhost:3000/"]]
  17 +VITE_PROXY = [["/api","http://222.180.200.114:48080/api"],["/thingskit-drawio","http://localhost:3000/"]]
18 18 # VITE_PROXY = [["/api","http://121.37.251.8:8080/api"],["/thingskit-drawio","http://localhost:3000/"]]
19 19 # VITE_PROXY = [["/api","http://192.168.10.136:8080/api"],["/thingskit-drawio","http://192.168.10.136:8080/api"]]
20 20
... ... @@ -43,7 +43,7 @@ VITE_GLOB_CONFIGURATION = /thingskit-drawio
43 43 VITE_CONTENT_SECURITY_POLICY = false
44 44
45 45 # Alarm Notify Polling Interval Time
46   -VITE_ALARM_NOTIFY_POLLING_INTERVAL_TIME = 5000
  46 +VITE_ALARM_NOTIFY_POLLING_INTERVAL_TIME = 500000
47 47
48 48 # Alarm Notify Auto Close Time Unit is Second
49 49 VITE_ALARM_NOTIFY_DURATION = 5
... ...
1 1 {
2   - "i18n-ally.localesPaths": ["src/locales", "src/locales/lang", "public/resource/tinymce/langs"]
  2 + "i18n-ally.localesPaths": [
  3 + "src/locales",
  4 + "src/locales/lang",
  5 + "public/resource/tinymce/langs"
  6 + ],
  7 + "cSpell.words": [
  8 + "unref",
  9 + "VITE"
  10 + ]
3 11 }
... ...
... ... @@ -48,3 +48,40 @@ export interface DeviceProfileModel {
48 48 export type ChildDeviceParams = BasicPageParams & {
49 49 formId: string;
50 50 };
  51 +
  52 +export interface Configuration {
  53 + type: string;
  54 +}
  55 +
  56 +export interface TransportConfiguration {
  57 + type: string;
  58 +}
  59 +
  60 +export interface ProvisionConfiguration {
  61 + type: string;
  62 + provisionDeviceSecret?: any;
  63 +}
  64 +
  65 +export interface ProfileData {
  66 + configuration: Configuration;
  67 + transportConfiguration: TransportConfiguration;
  68 + provisionConfiguration: ProvisionConfiguration;
  69 + alarms?: any;
  70 +}
  71 +
  72 +export interface DeviceRecord {
  73 + id: string;
  74 + creator: string;
  75 + createTime: string;
  76 + name: string;
  77 + tenantId: string;
  78 + transportType: string;
  79 + provisionType: string;
  80 + deviceType: string;
  81 + tbProfileId: string;
  82 + profileData: ProfileData;
  83 + defaultQueueName: string;
  84 + image: string;
  85 + type: string;
  86 + default: boolean;
  87 +}
... ...
  1 +import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
  2 +
  3 +export interface Specs {
  4 + min: string;
  5 + max: string;
  6 + unit: string;
  7 + unitName: string;
  8 + step: string;
  9 + length: string;
  10 + boolOpen: string;
  11 + boolClose: string;
  12 + valueRange?: {
  13 + min: string;
  14 + max: string;
  15 + };
  16 +}
  17 +
  18 +export interface FunctionJson {
  19 + type: string;
  20 + specs?: Partial<Specs> | ModelOfMatterParams[];
  21 +}
  22 +
  23 +export interface ModelOfMatterParams {
  24 + deviceProfileId: string;
  25 + functionJson: FunctionJson;
  26 + functionName: string;
  27 + functionType: FunctionType;
  28 + identifier: string;
  29 + remark: string;
  30 + id?: string;
  31 +}
  32 +
  33 +export interface GetModelTslParams {
  34 + functionType: FunctionType;
  35 + deviceProfileId: string;
  36 +}
... ...
  1 +import { BasicPageParams } from '../model/baseModel';
  2 +import { GetModelTslParams, ModelOfMatterParams } from './model/modelOfMatterModel';
  3 +import { defHttp } from '/@/utils/http/axios';
  4 +
  5 +enum ModelOfMatter {
  6 + CREATE = '/things_model',
  7 + UPDATE = '/things_model',
  8 + DELETE = '/things_model',
  9 + TSL = '/things_model',
  10 + LIST = '/things_model/page',
  11 +}
  12 +
  13 +export const getModelList = (params: BasicPageParams) => {
  14 + return defHttp.get({
  15 + url: `${ModelOfMatter.LIST}`,
  16 + params,
  17 + });
  18 +};
  19 +
  20 +export const getModelTsl = (params: GetModelTslParams) => {
  21 + const { functionType, deviceProfileId } = params;
  22 + return defHttp.get({
  23 + url: `${ModelOfMatter.TSL}/${functionType}/${deviceProfileId}`,
  24 + });
  25 +};
  26 +
  27 +export const createModel = (params: Partial<ModelOfMatterParams>) => {
  28 + return defHttp.post({
  29 + url: ModelOfMatter.CREATE,
  30 + params,
  31 + });
  32 +};
  33 +
  34 +export const updateModel = (params: Partial<ModelOfMatterParams>) => {
  35 + return defHttp.put({
  36 + url: ModelOfMatter.UPDATE,
  37 + params,
  38 + });
  39 +};
  40 +
  41 +export const deleteModel = (params: string[]) => {
  42 + return defHttp.delete({
  43 + url: ModelOfMatter.DELETE,
  44 + params: {
  45 + ids: params,
  46 + },
  47 + });
  48 +};
... ...
... ... @@ -5,6 +5,8 @@ export interface GetOtaPackagesParams {
5 5 page: number;
6 6 textSearch?: string;
7 7 title?: string;
  8 + sortOrder?: string;
  9 + sortProperty?: string;
8 10 }
9 11
10 12 export interface CreateOtaPackagesParams {
... ...
... ... @@ -20,7 +20,7 @@ enum SysDictApi {
20 20 * @param code
21 21 */
22 22 export const findDictItemByCode = (params?: DictCodeParams) => {
23   - return defHttp.post<SysDictItemResult>({
  23 + return defHttp.post<SysDictItemResult[]>({
24 24 url: SysDictApi.CONFIG_ITEM_URL + '/find',
25 25 params,
26 26 });
... ...
... ... @@ -12,6 +12,8 @@ export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
12 12 export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
13 13 export { default as ApiUpload } from './src/components/ApiUpload.vue';
14 14
  15 +export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue';
  16 +
15 17 //注册自定义组件
16 18 export {
17 19 JEasyCron,
... ...
... ... @@ -35,7 +35,8 @@ import ColorPicker from './components/ColorPicker.vue';
35 35 import IconDrawer from './components/IconDrawer.vue';
36 36 import ApiUpload from './components/ApiUpload.vue';
37 37 import ApiSearchSelect from './components/ApiSearchSelect.vue';
38   -import CustomeMinMaxInput from './externalCompns/components/CustomeMinMaxInput.vue';
  38 +import CustomMinMaxInput from './externalCompns/components/CustomMinMaxInput.vue';
  39 +import StructForm from './externalCompns/components/StructForm/StructForm.vue';
39 40
40 41 const componentMap = new Map<ComponentType, Component>();
41 42
... ... @@ -78,7 +79,8 @@ componentMap.set('ColorPicker', ColorPicker);
78 79 componentMap.set('IconDrawer', IconDrawer);
79 80 componentMap.set('ApiUpload', ApiUpload);
80 81 componentMap.set('ApiSearchSelect', ApiSearchSelect);
81   -componentMap.set('CustomeMinMaxInput', CustomeMinMaxInput);
  82 +componentMap.set('CustomMinMaxInput', CustomMinMaxInput);
  83 +componentMap.set('StructForm', StructForm);
82 84
83 85 export function add(compName: ComponentType, component: Component) {
84 86 componentMap.set(compName, component);
... ...
  1 +<template>
  2 + <div class="flex">
  3 + <InputNumber
  4 + placeholder="最小值"
  5 + :value="getValue.min"
  6 + style="width: 38%"
  7 + @change="(value) => emitChange(value, 'min')"
  8 + />
  9 + <span style="width: 8px"></span>
  10 + <span>~</span>
  11 + <span style="width: 8px"></span>
  12 + <InputNumber
  13 + placeholder="最大值"
  14 + :value="getValue.max"
  15 + style="width: 38%"
  16 + @change="(value) => emitChange(value, 'max')"
  17 + />
  18 + </div>
  19 +</template>
  20 +<script lang="ts">
  21 + export default {
  22 + inheritAttrs: false,
  23 + };
  24 +</script>
  25 +<script lang="ts" setup>
  26 + import { computed } from 'vue';
  27 + import { InputNumber } from 'ant-design-vue';
  28 +
  29 + const emit = defineEmits(['change', 'update:value']);
  30 + const props = withDefaults(
  31 + defineProps<{
  32 + value?: {
  33 + min: Nullable<number>;
  34 + max: Nullable<number>;
  35 + };
  36 + }>(),
  37 + {
  38 + value: () => ({ min: null, max: null }),
  39 + }
  40 + );
  41 +
  42 + const getValue = computed(() => {
  43 + const { value } = props;
  44 + return value;
  45 + });
  46 +
  47 + function emitChange(value: number, key: 'min' | 'max') {
  48 + const _value = { ...props.value, [key]: value };
  49 + emit('update:value', _value);
  50 + }
  51 +</script>
  52 +<style scoped></style>
... ...
1   -<template>
2   - <div style="display: flex">
3   - <a-input
4   - placeholder="最小值"
5   - v-model:value="param.min"
6   - style="width: 38%"
7   - @input="emitChange"
8   - />
9   - <span style="width: 8px"></span>
10   - <span>~</span>
11   - <span style="width: 8px"></span>
12   - <a-input
13   - placeholder="最大值"
14   - v-model:value="param.max"
15   - style="width: 38%"
16   - @input="emitChange"
17   - />
18   - </div>
19   -</template>
20   -<script lang="ts">
21   - import { defineComponent, reactive, watchEffect } from 'vue';
22   - import { propTypes } from '/@/utils/propTypes';
23   - import { isEmpty } from '/@/utils/is';
24   -
25   - export default defineComponent({
26   - name: 'JAddInput',
27   - //--------------不继承Antd Design Vue Input的所有属性 否则控制台报大片警告--------------
28   - inheritAttrs: false,
29   - props: {
30   - value: propTypes.object.def({}),
31   - },
32   - emits: ['change', 'update:value'],
33   - setup(props, { emit }) {
34   - const param = reactive({
35   - min: '',
36   - max: '',
37   - });
38   - watchEffect(() => {
39   - initVal();
40   - });
41   - function initVal() {
42   - if (props.value) {
43   - param.min = props.value.min;
44   - param.max = props.value.max;
45   - }
46   - }
47   - function emitChange() {
48   - emit('change', isEmpty(param) ? '' : param);
49   - emit('update:value', isEmpty(param) ? '' : param);
50   - }
51   - return {
52   - emitChange,
53   - param,
54   - };
55   - },
56   - });
57   -</script>
58   -<style scoped></style>
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: false,
  4 + };
  5 +</script>
  6 +<script lang="ts" setup>
  7 + import StructFormModel from './StructFormModel.vue';
  8 + import { useModal } from '/@/components/Modal';
  9 + import { PlusOutlined } from '@ant-design/icons-vue';
  10 + import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  11 + import { computed, unref } from 'vue';
  12 + import { buildUUID } from '/@/utils/uuid';
  13 + import { Divider, Button } from 'ant-design-vue';
  14 + import { OpenModalMode, OpenModalParams, StructRecord } from './type';
  15 + import { cloneDeep } from 'lodash-es';
  16 +
  17 + const emit = defineEmits(['update:value']);
  18 +
  19 + const props = withDefaults(
  20 + defineProps<{
  21 + value: ModelOfMatterParams[];
  22 + }>(),
  23 + {
  24 + value: () => [],
  25 + }
  26 + );
  27 +
  28 + const getValue = computed<StructRecord[]>(() => {
  29 + const { value } = props;
  30 + return value.map((item) => {
  31 + return {
  32 + ...(item as StructRecord),
  33 + ...((item as StructRecord).id ? {} : { id: buildUUID() }),
  34 + };
  35 + });
  36 + });
  37 +
  38 + const [registerModal, { openModal }] = useModal();
  39 +
  40 + const handleCreateParams = () => {
  41 + openModal(true, {
  42 + mode: OpenModalMode.CREATE,
  43 + } as OpenModalParams);
  44 + };
  45 +
  46 + const handleUpdate = (value: StructRecord) => {
  47 + openModal(true, {
  48 + mode: OpenModalMode.UPDATE,
  49 + record: value,
  50 + } as OpenModalParams);
  51 + };
  52 +
  53 + const handleDelete = (value: StructRecord) => {
  54 + const index = unref(getValue).findIndex((item) => item.id === value.id);
  55 + const _value = cloneDeep(unref(getValue));
  56 + _value.splice(index, 1);
  57 + emit('update:value', _value);
  58 + };
  59 +
  60 + const handleSaveStruct = (mode: OpenModalMode, value: StructRecord) => {
  61 + const _value = cloneDeep(unref(getValue));
  62 +
  63 + if (mode === OpenModalMode.UPDATE) {
  64 + const index = unref(getValue).findIndex((item) => item.id === value.id);
  65 + ~index && _value.splice(index, 1, value);
  66 + } else {
  67 + _value.push(value);
  68 + }
  69 +
  70 + emit('update:value', _value);
  71 + };
  72 +</script>
  73 +
  74 +<template>
  75 + <section>
  76 + <div class="text-blue-500 cursor-pointer">
  77 + <section>
  78 + <div
  79 + class="flex bg-blue-50 mb-2 p-2 text-gray-500 justify-between items-center"
  80 + v-for="item in getValue"
  81 + :key="item.id"
  82 + >
  83 + <div>参数名称: {{ item.functionName }}</div>
  84 + <div class="flex">
  85 + <Button class="!p-0" type="link" @click="handleUpdate(item)">编辑</Button>
  86 + <Divider type="vertical" />
  87 + <Button class="!p-0" type="link" @click="handleDelete(item)">删除</Button>
  88 + </div>
  89 + </div>
  90 + </section>
  91 + <div>
  92 + <span class="mr-2">
  93 + <PlusOutlined />
  94 + </span>
  95 + <span @click="handleCreateParams">增加参数</span>
  96 + </div>
  97 + </div>
  98 + <StructFormModel @register="registerModal" @submit="handleSaveStruct" />
  99 + </section>
  100 +</template>
  101 +
  102 +<style lang="less" scoped></style>
... ...
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: false,
  4 + };
  5 +</script>
  6 +<script lang="ts" setup>
  7 + import { BasicForm, useForm } from '/@/components/Form';
  8 + import { formSchemas } from './config';
  9 + import { BasicModal, useModalInner } from '/@/components/Modal';
  10 + import { OpenModalMode, OpenModalParams, StructRecord } from './type';
  11 + import { ref, unref } from 'vue';
  12 + import { transformFormValue } from './util';
  13 + import { cloneDeep } from 'lodash-es';
  14 +
  15 + const modalReceiveRecord = ref<OpenModalParams>({
  16 + mode: OpenModalMode.CREATE,
  17 + });
  18 +
  19 + const emit = defineEmits(['register', 'submit']);
  20 +
  21 + const [register, { validate, setFieldsValue }] = useForm({
  22 + labelWidth: 100,
  23 + schemas: formSchemas,
  24 + actionColOptions: {
  25 + span: 14,
  26 + },
  27 + showResetButton: false,
  28 + submitOnReset: false,
  29 + showActionButtonGroup: false,
  30 + });
  31 +
  32 + const [registerModal, { closeModal }] = useModalInner((record: OpenModalParams) => {
  33 + modalReceiveRecord.value = record;
  34 + const data = record.record || {};
  35 + const { functionJson = {} } = data! as StructRecord;
  36 + const { specs = {} } = functionJson as StructRecord['functionJson'];
  37 + if (record.record) {
  38 + const value = {
  39 + ...data,
  40 + ...functionJson,
  41 + ...specs,
  42 + valueRange: {
  43 + min: specs.min,
  44 + max: specs.max,
  45 + },
  46 + };
  47 +
  48 + setFieldsValue(value);
  49 + }
  50 + });
  51 +
  52 + const handleSubmit = async () => {
  53 + try {
  54 + const _value = await validate();
  55 + let value = transformFormValue(_value);
  56 + value = {
  57 + ...value,
  58 + ...(unref(modalReceiveRecord)?.record?.id
  59 + ? { id: unref(modalReceiveRecord)?.record?.id }
  60 + : {}),
  61 + };
  62 + emit('submit', unref(modalReceiveRecord).mode, cloneDeep(value));
  63 + closeModal();
  64 + } catch (error) {}
  65 + };
  66 +</script>
  67 +
  68 +<template>
  69 + <BasicModal
  70 + @register="registerModal"
  71 + :title="modalReceiveRecord.mode === OpenModalMode.CREATE ? '创建参数' : '编辑参数'"
  72 + :width="800"
  73 + @ok="handleSubmit"
  74 + destroy-on-close
  75 + >
  76 + <BasicForm @register="register" />
  77 + </BasicModal>
  78 +</template>
  79 +
  80 +<style lang="less" scoped></style>
... ...
  1 +import { h } from 'vue';
  2 +import { findDictItemByCode } from '/@/api/system/dict';
  3 +import { FormSchema } from '/@/components/Table';
  4 +import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
  5 +
  6 +export enum DateTypeEnum {
  7 + IS_NUMBER_INT = 'INT',
  8 + IS_NUMBER_DOUBLE = 'DOUBLE',
  9 + IS_STRING = 'TEXT',
  10 + IS_STRUCT = 'STRUCT',
  11 + IS_BOOL = 'BOOL',
  12 +}
  13 +
  14 +export const formSchemas: FormSchema[] = [
  15 + {
  16 + field: FormField.FUNCTION_NAME,
  17 + label: '功能名称',
  18 + required: true,
  19 + component: 'Input',
  20 + colProps: {
  21 + span: 18,
  22 + },
  23 + componentProps: {
  24 + maxLength: 255,
  25 + placeholder: '请输入功能名称',
  26 + },
  27 + },
  28 + {
  29 + field: FormField.IDENTIFIER,
  30 + label: '标识符',
  31 + required: true,
  32 + component: 'Input',
  33 + colProps: {
  34 + span: 18,
  35 + },
  36 + componentProps: {
  37 + maxLength: 255,
  38 + placeholder: '请输入标识符',
  39 + },
  40 + },
  41 + {
  42 + field: FormField.TYPE,
  43 + label: '数据类型',
  44 + required: true,
  45 + component: 'ApiSelect',
  46 + colProps: {
  47 + span: 9,
  48 + },
  49 + defaultValue: 'INT',
  50 + componentProps: {
  51 + placeholder: '请选择数据类型',
  52 + api: findDictItemByCode,
  53 + params: {
  54 + dictCode: 'data_type',
  55 + },
  56 + labelField: 'itemText',
  57 + valueField: 'itemValue',
  58 + getPopupContainer: () => document.body,
  59 + },
  60 + },
  61 + {
  62 + field: FormField.VALUE_RANGE,
  63 + label: '取值范围',
  64 + component: 'CustomMinMaxInput',
  65 + valueField: 'value',
  66 + changeEvent: 'update:value',
  67 + colProps: {
  68 + span: 18,
  69 + },
  70 + ifShow: ({ values }) =>
  71 + values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_INT ||
  72 + values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_DOUBLE,
  73 + },
  74 + {
  75 + field: FormField.STEP,
  76 + label: '步长',
  77 + component: 'InputNumber',
  78 + colProps: {
  79 + span: 18,
  80 + },
  81 + componentProps: {
  82 + maxLength: 255,
  83 + placeholder: '请输入步长',
  84 + },
  85 + ifShow: ({ values }) =>
  86 + values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_INT ||
  87 + values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_DOUBLE,
  88 + },
  89 + {
  90 + field: FormField.UNIT_NAME,
  91 + label: '单位名称',
  92 + component: 'Input',
  93 + show: false,
  94 + },
  95 + {
  96 + field: FormField.UNIT,
  97 + label: '单位',
  98 + component: 'ApiSelect',
  99 + colProps: {
  100 + span: 9,
  101 + },
  102 + componentProps: ({ formActionType }) => {
  103 + const { setFieldsValue } = formActionType;
  104 + return {
  105 + placeholder: '请选择单位',
  106 + api: findDictItemByCode,
  107 + params: {
  108 + dictCode: 'attribute_unit',
  109 + },
  110 + labelField: 'itemText',
  111 + valueField: 'itemValue',
  112 + onChange(_, record: Record<'label' | 'value', string>) {
  113 + const { label } = record;
  114 + setFieldsValue({ [FormField.UNIT_NAME]: label });
  115 + },
  116 + getPopupContainer: () => document.body,
  117 + };
  118 + },
  119 + ifShow: ({ values }) =>
  120 + values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_INT ||
  121 + values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_DOUBLE,
  122 + renderComponentContent: () => {
  123 + return {
  124 + option: (params: Record<'label' | 'value', string>) => {
  125 + const { label, value } = params;
  126 + return h('span', `${label} / ${value}`);
  127 + },
  128 + };
  129 + },
  130 + },
  131 + {
  132 + field: FormField.BOOL_CLOSE,
  133 + component: 'Input',
  134 + required: true,
  135 + label: '0 -',
  136 + colProps: {
  137 + span: 18,
  138 + },
  139 + componentProps: {
  140 + placeholder: '如:关',
  141 + },
  142 + ifShow: ({ values }) => values[FormField.TYPE] === DateTypeEnum.IS_BOOL,
  143 + },
  144 + {
  145 + field: FormField.BOOL_OPEN,
  146 + component: 'Input',
  147 + required: true,
  148 + label: '1 -',
  149 + colProps: {
  150 + span: 18,
  151 + },
  152 + componentProps: {
  153 + placeholder: '如:开',
  154 + },
  155 + ifShow: ({ values }) => values[FormField.TYPE] === DateTypeEnum.IS_BOOL,
  156 + },
  157 + {
  158 + field: FormField.LENGTH,
  159 + component: 'Input',
  160 + required: true,
  161 + label: '数据长度',
  162 + defaultValue: '10240',
  163 + colProps: {
  164 + span: 8,
  165 + },
  166 + componentProps: {
  167 + placeholder: '请输入数据长度',
  168 + },
  169 + renderComponentContent: () => {
  170 + return {
  171 + suffix: () => '字节',
  172 + };
  173 + },
  174 + ifShow: ({ values }) => values[FormField.TYPE] === DateTypeEnum.IS_STRING,
  175 + },
  176 + {
  177 + field: FormField.R_W_FLAG,
  178 + component: 'ApiRadioGroup',
  179 + label: '读写类型',
  180 + required: true,
  181 + colProps: {
  182 + span: 24,
  183 + },
  184 + defaultValue: 'READ_ONLY',
  185 + componentProps: {
  186 + placeholder: '请选择读写类型',
  187 + api: findDictItemByCode,
  188 + params: {
  189 + dictCode: 'read_write_type',
  190 + },
  191 + labelField: 'itemText',
  192 + valueField: 'itemValue',
  193 + },
  194 + },
  195 + {
  196 + field: FormField.SPECS_LIST,
  197 + label: 'JSON对象',
  198 + component: 'StructForm',
  199 + valueField: 'value',
  200 + changeEvent: 'update:value',
  201 + colProps: { span: 24 },
  202 + ifShow: ({ values }) => values[FormField.TYPE] === DateTypeEnum.IS_STRUCT,
  203 + rules: [
  204 + {
  205 + required: true,
  206 + validator(_rule, value, _callback) {
  207 + console.log(value);
  208 + return Promise.resolve();
  209 + },
  210 + },
  211 + ],
  212 + },
  213 + {
  214 + field: FormField.REFARK,
  215 + label: '备注',
  216 + component: 'InputTextArea',
  217 + componentProps: {
  218 + rows: 4,
  219 + maxLength: 100,
  220 + placeholder: '请输入描述',
  221 + },
  222 + },
  223 +];
... ...
  1 +import { DateTypeEnum } from './config';
  2 +import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  3 +import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
  4 +
  5 +export enum OpenModalMode {
  6 + CREATE = 'create',
  7 + UPDATE = 'update',
  8 +}
  9 +
  10 +export interface OpenModalParams {
  11 + mode: OpenModalMode;
  12 + record?: StructRecord;
  13 +}
  14 +
  15 +export interface StructFormValue
  16 + extends Partial<Record<Exclude<FormField, FormField.VALUE_RANGE | FormField.STRUCT>, string>> {
  17 + [FormField.TYPE]: DateTypeEnum;
  18 + [FormField.VALUE_RANGE]?: {
  19 + [FormField.MIN]: string;
  20 + [FormField.MAX]: string;
  21 + };
  22 + [FormField.STRUCT]: StructRecord[];
  23 +}
  24 +
  25 +export interface StructRecord extends ModelOfMatterParams {
  26 + id: string;
  27 +}
... ...
  1 +import { DateTypeEnum } from './config';
  2 +import { StructFormValue } from './type';
  3 +import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  4 +import { useMessage } from '/@/hooks/web/useMessage';
  5 +import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
  6 +
  7 +export const validateValueRangeAndStep = (min: number, step: number, max: number) => {
  8 + const { createMessage } = useMessage();
  9 + if (min > max) {
  10 + createMessage.error('最大值必须大于最小值,整数型不能有小数位,单精度有效位为7,双精度为16');
  11 + throw '最大值必须大于最小值,整数型不能有小数位,单精度有效位为7,双精度为16';
  12 + }
  13 +
  14 + if (step > max - min) {
  15 + createMessage.error('步长不能大于取值范围的差值');
  16 + throw '步长不能大于取值范围的差值';
  17 + }
  18 +};
  19 +
  20 +export const validateValueBool = (boolClose, boolOpen) => {
  21 + const { createMessage } = useMessage();
  22 +
  23 + if (boolClose == boolOpen) {
  24 + createMessage.error('布尔值不能相同');
  25 + throw '布尔值不能相同';
  26 + }
  27 +};
  28 +
  29 +export const validateValueStruct = (data: []) => {
  30 + const { createMessage } = useMessage();
  31 +
  32 + if (data.length === 0) {
  33 + createMessage.error('struct不能为空');
  34 + throw 'struct不能为空';
  35 + }
  36 +};
  37 +
  38 +export function transformFormValue(value: StructFormValue): Partial<ModelOfMatterParams> {
  39 + const {
  40 + type,
  41 + valueRange,
  42 + step,
  43 + length = '',
  44 + boolClose,
  45 + boolOpen,
  46 + unit,
  47 + unitName,
  48 + functionName,
  49 + identifier,
  50 + remark,
  51 + specs,
  52 + } = value;
  53 + const { min, max } = valueRange! || {};
  54 + const basic = { functionName, identifier, remark };
  55 + let functionJson = {} as unknown as ModelOfMatterParams['functionJson'];
  56 +
  57 + console.log(value);
  58 + switch (type) {
  59 + case DateTypeEnum.IS_NUMBER_INT:
  60 + validateValueRangeAndStep(Number(min), Number(step), Number(max));
  61 + functionJson = {
  62 + type,
  63 + specs: { max, min, valueRange, step, unit, unitName },
  64 + };
  65 + break;
  66 +
  67 + case DateTypeEnum.IS_NUMBER_DOUBLE:
  68 + validateValueRangeAndStep(Number(min), Number(step), Number(max));
  69 + functionJson = {
  70 + type,
  71 + specs: { max, min, valueRange, step, unit, unitName },
  72 + };
  73 + break;
  74 +
  75 + case DateTypeEnum.IS_BOOL:
  76 + validateValueBool(Number(boolClose), Number(boolOpen));
  77 + functionJson = {
  78 + type,
  79 + specs: {
  80 + boolOpen: boolOpen,
  81 + boolClose: boolClose,
  82 + },
  83 + };
  84 + break;
  85 +
  86 + case DateTypeEnum.IS_STRING:
  87 + functionJson = {
  88 + type,
  89 + specs: { length },
  90 + };
  91 + break;
  92 +
  93 + case DateTypeEnum.IS_STRUCT:
  94 + functionJson = {
  95 + type,
  96 + specs: specs! as unknown as ModelOfMatterParams[],
  97 + };
  98 + break;
  99 + }
  100 + return {
  101 + ...basic,
  102 + functionType: FunctionType.PROPERTIES,
  103 + functionJson,
  104 + };
  105 +}
... ...
... ... @@ -112,9 +112,10 @@ export type ComponentType =
112 112 | 'Render'
113 113 | 'Slider'
114 114 | 'JAddInput'
115   - | 'CustomeMinMaxInput'
  115 + | 'CustomMinMaxInput'
116 116 | 'Rate'
117 117 | 'ColorPicker'
118 118 | 'IconDrawer'
119 119 | 'ApiUpload'
120   - | 'ApiSearchSelect';
  120 + | 'ApiSearchSelect'
  121 + | 'StructForm';
... ...
... ... @@ -3,8 +3,9 @@
3 3 <div
4 4 class="cursor-pointer flex py-4 fold-icon absolute rounded svg:fill-gray-400 hover:bg-gray-200"
5 5 :class="foldFlag ? '' : '-right-4'"
  6 + @click="handleFold"
6 7 >
7   - <div @click="handleFold">
  8 + <div>
8 9 <CaretRightOutlined
9 10 :class="[foldFlag ? '' : 'rotate-180']"
10 11 class="transform fill-gray-100"
... ...
... ... @@ -16,12 +16,15 @@
16 16 @open-gateway-device="handleOpenGatewayDevice"
17 17 />
18 18 </TabPane>
19   - <TabPane key="2" tab="实时数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'">
  19 + <TabPane v-if="deviceDetail?.deviceType !== 'GATEWAY'" key="modelOfMatter" tab="物模型">
  20 + <ModelOfMatter :deviceDetail="deviceDetail" />
  21 + </TabPane>
  22 + <!-- <TabPane key="2" tab="实时数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'">
20 23 <RealTimeData :deviceDetail="deviceDetail" />
21 24 </TabPane>
22 25 <TabPane key="7" tab="历史数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'">
23 26 <HistoryData :deviceDetail="deviceDetail" />
24   - </TabPane>
  27 + </TabPane> -->
25 28 <TabPane key="5" tab="命令下发" v-if="deviceDetail?.deviceType !== 'SENSOR'">
26 29 <CommandIssuance :deviceDetail="deviceDetail" />
27 30 </TabPane>
... ... @@ -50,13 +53,14 @@
50 53
51 54 import { Tabs } from 'ant-design-vue';
52 55 import Detail from '../tabs/Detail.vue';
53   - import RealTimeData from '../tabs/RealTimeData.vue';
  56 + // import RealTimeData from '../tabs/RealTimeData.vue';
54 57 import Alarm from '../tabs/Alarm.vue';
55 58 import ChildDevice from '../tabs/ChildDevice.vue';
56 59 import TBoxDetail from '../tabs/TBoxDetail.vue';
57 60 import CommandIssuance from '../tabs/CommandIssuance.vue';
58 61 import { getDeviceDetail } from '/@/api/device/deviceManager';
59   - import HistoryData from '../tabs/HistoryData.vue';
  62 + // import HistoryData from '../tabs/HistoryData.vue';
  63 + import ModelOfMatter from '../tabs/ModelOfMatter.vue';
60 64 export default defineComponent({
61 65 name: 'DeviceModal',
62 66 components: {
... ... @@ -64,12 +68,13 @@
64 68 Tabs,
65 69 TabPane: Tabs.TabPane,
66 70 Detail,
67   - RealTimeData,
  71 + // RealTimeData,
68 72 Alarm,
69 73 ChildDevice,
70 74 CommandIssuance,
71 75 TBoxDetail,
72   - HistoryData,
  76 + // HistoryData,
  77 + ModelOfMatter,
73 78 },
74 79 emits: ['reload', 'register', 'openTbDeviceDetail', 'openGatewayDeviceDetail'],
75 80 setup(_props, { emit }) {
... ...
... ... @@ -23,6 +23,7 @@
23 23
24 24 const props = defineProps<{
25 25 deviceDetail: DeviceDetail;
  26 + attr?: string;
26 27 }>();
27 28
28 29 const chartRef = ref();
... ... @@ -119,6 +120,10 @@
119 120 const { tbDeviceId } = props.deviceDetail || {};
120 121 try {
121 122 deviceAttrs.value = (await getDeviceDataKeys(tbDeviceId)) || [];
  123 +
  124 + if (props.attr) {
  125 + method.setFieldsValue({ keys: props.attr });
  126 + }
122 127 } catch (error) {}
123 128 };
124 129
... ... @@ -139,9 +144,11 @@
139 144
140 145 if (!hasDeviceAttr()) return;
141 146
  147 + const keys = props.attr ? props.attr : unref(deviceAttrs).join();
  148 +
142 149 const res = await getDeviceHistoryInfo({
143 150 entityId: props.deviceDetail.tbDeviceId,
144   - keys: unref(deviceAttrs).join(),
  151 + keys,
145 152 startTs: Date.now() - 1 * 24 * 60 * 60 * 1000,
146 153 endTs: Date.now(),
147 154 agg: AggregateDataEnum.NONE,
... ... @@ -169,12 +176,12 @@
169 176 </script>
170 177
171 178 <template>
172   - <section class="flex flex-col p-4 h-full" style="color: #f0f2f5">
173   - <section class="bg-white p-3">
  179 + <section class="flex flex-col p-4 h-full" style="background-color: #f0f2f5">
  180 + <section class="bg-white p-3 mb-4">
174 181 <TimePeriodForm @register="register" />
175 182 </section>
176 183 <section class="bg-white p-3">
177   - <div v-show="isNull" ref="chartRef" :style="{ height: '550px', width: '100%' }">
  184 + <div v-show="isNull" ref="chartRef" :style="{ height: '400px', width: '100%' }">
178 185 <Spin :spinning="loading" :absolute="true" />
179 186 </div>
180 187 <Empty v-show="!isNull" />
... ...
  1 +<script lang="ts" setup>
  2 + import { nextTick, reactive, ref, unref } from 'vue';
  3 + import { List, Button, Tooltip, Card } from 'ant-design-vue';
  4 + import { PageWrapper } from '/@/components/Page';
  5 + import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons-vue';
  6 + import { BasicTable, useTable } from '/@/components/Table';
  7 + import { realTimeDataColumns } from '../../config/detail.config';
  8 + import { useWebSocket } from '@vueuse/core';
  9 + import { getAuthCache } from '/@/utils/auth';
  10 + import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum';
  11 + import { useMessage } from '/@/hooks/web/useMessage';
  12 + import { formatToDateTime } from '/@/utils/dateUtil';
  13 + import { BasicForm, useForm } from '/@/components/Form';
  14 + import HistoryData from './HistoryData.vue';
  15 + import { BasicModal, useModal } from '/@/components/Modal';
  16 +
  17 + interface ReceiveMessage {
  18 + data: {
  19 + [key: string]: [number, string][];
  20 + };
  21 + }
  22 +
  23 + interface DataSource {
  24 + key?: string;
  25 + value?: string;
  26 + time?: number;
  27 + }
  28 +
  29 + const props = defineProps<{
  30 + deviceDetail: Record<'tbDeviceId', string>;
  31 + }>();
  32 +
  33 + const grid = {
  34 + gutter: 8,
  35 + column: 3,
  36 + };
  37 +
  38 + const token = getAuthCache(JWT_TOKEN_KEY);
  39 +
  40 + const socketInfo = reactive({
  41 + origin: `${import.meta.env.VITE_WEB_SOCKET}${token}`,
  42 + attr: undefined as string | undefined,
  43 + originData: [] as DataSource[],
  44 + dataSource: [] as DataSource[],
  45 + message: {} as ReceiveMessage['data'],
  46 + sendValue: {
  47 + tsSubCmds: [
  48 + {
  49 + entityType: 'DEVICE',
  50 + entityId: props.deviceDetail!.tbDeviceId || 'd2526c70-60a9-11ed-ba9c-6b98bfcc8255',
  51 + scope: 'LATEST_TELEMETRY',
  52 + cmdId: 1,
  53 + },
  54 + ],
  55 + },
  56 + });
  57 +
  58 + const [registerForm, { getFieldsValue }] = useForm({
  59 + schemas: [
  60 + {
  61 + field: 'value',
  62 + label: '键/值',
  63 + component: 'Input',
  64 + colProps: { span: 8 },
  65 + componentProps: {
  66 + placeholder: '请输入键/值',
  67 + },
  68 + },
  69 + ],
  70 + labelWidth: 100,
  71 + compact: true,
  72 + showAdvancedButton: true,
  73 + submitFunc: async () => {
  74 + try {
  75 + const { value } = getFieldsValue() || {};
  76 + if (!value) setTableData(socketInfo.originData);
  77 + const data = unref(socketInfo.originData).filter(
  78 + (item) => item.key?.includes(value) || item.value?.includes(value)
  79 + );
  80 + await nextTick();
  81 + socketInfo.dataSource = data;
  82 + setTableData(data);
  83 + } catch (error) {}
  84 + },
  85 + resetFunc: async () => {
  86 + try {
  87 + socketInfo.dataSource = socketInfo.originData;
  88 + setTableData(socketInfo.originData);
  89 + } catch (error) {}
  90 + },
  91 + });
  92 +
  93 + const [registerTable, { setTableData }] = useTable({
  94 + columns: realTimeDataColumns,
  95 + showTableSetting: true,
  96 + bordered: true,
  97 + showIndexColumn: false,
  98 + });
  99 +
  100 + const [registerModal, { openModal }] = useModal();
  101 +
  102 + enum Mode {
  103 + TABLE = 'table',
  104 + CARD = 'card',
  105 + }
  106 +
  107 + const mode = ref<Mode>(Mode.CARD);
  108 +
  109 + const switchMode = async (value: Mode) => {
  110 + mode.value = value;
  111 + await nextTick();
  112 + setTableData(socketInfo.dataSource);
  113 + };
  114 +
  115 + const { createMessage } = useMessage();
  116 +
  117 + const { send, close, data } = useWebSocket(socketInfo.origin, {
  118 + onConnected() {
  119 + send(JSON.stringify(socketInfo.sendValue));
  120 + },
  121 + async onMessage() {
  122 + try {
  123 + const value = JSON.parse(unref(data)) as ReceiveMessage;
  124 + if (value) {
  125 + const { data } = value;
  126 + const keys = Object.keys(data);
  127 + keys.forEach((key) => {
  128 + socketInfo.message[key] = value.data[key];
  129 + });
  130 +
  131 + const allKeys = Object.keys(socketInfo.message);
  132 +
  133 + socketInfo.originData = socketInfo.dataSource = allKeys.map((key) => {
  134 + const [time, value] = socketInfo.message[key].at(0) || [];
  135 + return { key, value, time };
  136 + });
  137 +
  138 + await nextTick();
  139 + setTableData(socketInfo.dataSource);
  140 + }
  141 + } catch (error) {}
  142 + },
  143 + onDisconnected() {
  144 + console.log('断开连接了');
  145 + close();
  146 + },
  147 + onError() {
  148 + createMessage.error('webSocket连接超时,请联系管理员');
  149 + },
  150 + });
  151 +
  152 + const handleShowDetail = (record: DataSource) => {
  153 + const { key } = record;
  154 + socketInfo.attr = key;
  155 + openModal(true);
  156 + };
  157 +</script>
  158 +
  159 +<template>
  160 + <PageWrapper
  161 + dense
  162 + content-class="flex flex-col bg-transparent p-4"
  163 + :content-style="{ backgroundColor: '#F0F2F5' }"
  164 + >
  165 + <section class="flex flex-col justify-between w-full bg-light-50 pt-3 mb-4">
  166 + <div class="flex-auto">
  167 + <BasicForm @register="registerForm" />
  168 + </div>
  169 + </section>
  170 + <section class="bg-light-50">
  171 + <div v-show="mode === Mode.CARD" class="flex h-70px items-center justify-end p-2">
  172 + <Tooltip title="卡片模式">
  173 + <Button
  174 + :class="[mode === Mode.CARD && '!bg-blue-500 svg:text-light-50']"
  175 + class="!p-2 !children:flex flex justify-center items-center border-r-0"
  176 + @click="switchMode(Mode.CARD)"
  177 + >
  178 + <AppstoreOutlined />
  179 + </Button>
  180 + </Tooltip>
  181 +
  182 + <Tooltip title="列表模式">
  183 + <Button
  184 + class="!p-2 !children:flex flex justify-center items-center"
  185 + @click="switchMode(Mode.TABLE)"
  186 + >
  187 + <BarsOutlined />
  188 + </Button>
  189 + </Tooltip>
  190 + </div>
  191 + <List
  192 + v-if="mode === Mode.CARD"
  193 + class="list-mode !px-2"
  194 + :data-source="socketInfo.dataSource"
  195 + :grid="grid"
  196 + >
  197 + <template #renderItem="{ item }">
  198 + <List.Item>
  199 + <Card class="shadow-md">
  200 + <template #title>
  201 + <span class="text-base font-normal">{{ item.key }}</span>
  202 + </template>
  203 + <template #extra>
  204 + <Button type="link" class="!p-0" @click="handleShowDetail(item)">历史数据</Button>
  205 + </template>
  206 + <section>
  207 + <div class="flex font-bold text-lg mb-4">
  208 + <div>{{ item.value }}</div>
  209 + </div>
  210 + <div class="text-dark-800 text-xs">{{ formatToDateTime(item.time) }}</div>
  211 + </section>
  212 + </Card>
  213 + </List.Item>
  214 + </template>
  215 + </List>
  216 + </section>
  217 +
  218 + <BasicTable v-if="mode === Mode.TABLE" @register="registerTable">
  219 + <template #toolbar>
  220 + <div v-show="mode === Mode.TABLE" class="flex h-70px items-center justify-end p-2">
  221 + <Tooltip title="卡片模式">
  222 + <Button
  223 + class="!p-2 !children:flex flex justify-center items-center border-r-0"
  224 + @click="switchMode(Mode.CARD)"
  225 + >
  226 + <AppstoreOutlined />
  227 + </Button>
  228 + </Tooltip>
  229 +
  230 + <Tooltip title="列表模式">
  231 + <Button
  232 + :class="[mode === Mode.TABLE && '!bg-blue-500 svg:text-light-50']"
  233 + class="!p-2 !children:flex flex justify-center items-center"
  234 + @click="switchMode(Mode.TABLE)"
  235 + >
  236 + <BarsOutlined />
  237 + </Button>
  238 + </Tooltip>
  239 + </div>
  240 + </template>
  241 + </BasicTable>
  242 + <BasicModal
  243 + @register="registerModal"
  244 + title="历史数据"
  245 + width="50%"
  246 + destroy-on-close
  247 + dialogClass="history-modal"
  248 + >
  249 + <HistoryData :deviceDetail="props.deviceDetail" :attr="socketInfo.attr" />
  250 + </BasicModal>
  251 + </PageWrapper>
  252 +</template>
  253 +
  254 +<style scoped lang="less">
  255 + .list-mode:deep(.ant-card-head) {
  256 + border-bottom: 0;
  257 + }
  258 +
  259 + .list-mode:deep(.ant-card-body) {
  260 + padding-top: 0;
  261 + }
  262 +</style>
  263 +
  264 +<style>
  265 + .history-modal .ant-input-number {
  266 + min-width: 0 !important;
  267 + }
  268 +</style>
... ...
1 1 <template>
2 2 <BasicDrawer v-bind="$attrs" title="产品详情" @register="register" width="60%">
3 3 <Tabs :animated="true" v-model:activeKey="activeKey" @change="handlePanelChange">
4   - <TabPane forceRender key="product" tab="产品">
  4 + <TabPane key="product" tab="产品">
5 5 <div class="relative">
6 6 <DeviceConfigurationStep :ifShowBtn="false" ref="DevConStRef" />
7 7 <div class="absolute w-full h-full top-0 cursor-not-allowed"></div>
8 8 </div>
9 9 </TabPane>
10   - <TabPane forceRender key="transport" tab="传输配置">
  10 + <TabPane key="transport" tab="传输配置">
11 11 <div class="relative">
12 12 <TransportConfigurationStep :ifShowBtn="false" ref="TransConStRef" />
13 13 <div class="absolute w-full h-full top-0 cursor-not-allowed"></div>
14 14 </div>
15 15 </TabPane>
16   - <TabPane forceRender key="modelOfMatter" tab="物模型管理">
17   - <PhysicalModelManagementStep />
  16 + <TabPane key="modelOfMatter" tab="物模型管理">
  17 + <PhysicalModelManagementStep :record="record" />
18 18 </TabPane>
19 19 </Tabs>
20 20 </BasicDrawer>
... ... @@ -27,14 +27,15 @@
27 27 import PhysicalModelManagementStep from './step/PhysicalModelManagementStep.vue';
28 28 import { ref, unref } from 'vue';
29 29 import { deviceConfigGetDetail } from '/@/api/device/deviceConfigApi';
  30 + import { DeviceRecord } from '/@/api/device/model/deviceModel';
30 31
31 32 defineEmits(['register']);
32 33
33 34 type ActiveKey = 'product' | 'transport' | 'modelOfMatter';
34 35
35   - const activeKey = ref<ActiveKey>('product');
  36 + const activeKey = ref<ActiveKey>('modelOfMatter');
36 37
37   - const record = ref<Recordable>({});
  38 + const record = ref<DeviceRecord>({} as unknown as DeviceRecord);
38 39
39 40 const DevConStRef = ref<InstanceType<typeof DeviceConfigurationStep>>();
40 41 const TransConStRef = ref<InstanceType<typeof TransportConfigurationStep>>();
... ... @@ -47,7 +48,7 @@
47 48 unref(TransConStRef)?.setFormData(res);
48 49 };
49 50
50   - const [register, {}] = useDrawerInner(async (data: Recordable) => {
  51 + const [register, {}] = useDrawerInner(async (data: { record: DeviceRecord }) => {
51 52 activeKey.value = 'product';
52 53 record.value = await deviceConfigGetDetail(data.record.id);
53 54 setDeviceConfFormData(unref(record));
... ...
... ... @@ -3,10 +3,9 @@ import { FormSchema } from '/@/components/Table';
3 3 import { findDictItemByCode } from '/@/api/system/dict';
4 4 import { MessageEnum } from '/@/enums/messageEnum';
5 5 import { numberRule } from '/@/utils/rules';
6   -import { Tag } from 'ant-design-vue';
7   -import { h } from 'vue';
8 6
9 7 import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi';
  8 +import { FormField, FunctionType } from './step/cpns/physical/cpns/config';
10 9
11 10 export const steps = [
12 11 {
... ... @@ -19,50 +18,37 @@ export const steps = [
19 18 },
20 19 ];
21 20
  21 +export const formatFunctionType: Record<FunctionType, string> = {
  22 + [FunctionType.PROPERTIES]: '属性',
  23 + [FunctionType.EVENTS]: '事件',
  24 + [FunctionType.SERVICE]: '事件',
  25 +};
  26 +
22 27 export const physicalColumn: BasicColumn[] = [
23 28 {
24 29 title: '功能类型',
25   - dataIndex: 'dType',
  30 + dataIndex: FormField.FUNCTION_TYPE,
26 31 width: 90,
  32 + format: (text: FunctionType) => {
  33 + return formatFunctionType[text];
  34 + },
27 35 },
28 36 {
29 37 title: '功能名称',
30   - dataIndex: 'name',
  38 + dataIndex: FormField.FUNCTION_NAME,
31 39 width: 90,
32 40 },
33 41 {
34 42 title: '标识符',
35   - dataIndex: 'type',
  43 + dataIndex: FormField.IDENTIFIER,
36 44 width: 90,
37 45 },
38 46 {
39 47 title: '数据类型',
40   - dataIndex: 'transportType',
  48 + dataIndex: 'functionJson.type',
41 49 width: 100,
42 50 },
43 51 {
44   - title: '单位',
45   - dataIndex: 'description1',
46   - width: 90,
47   - },
48   - {
49   - title: '读写类型',
50   - dataIndex: 'default',
51   - width: 60,
52   - customRender: ({ record }) => {
53   - const status = record.actionStatus;
54   - const enable = status === 'SUCCESS' ? '读写' : '只读';
55   - const color = enable === '读写' ? 'blue' : 'green';
56   - const text = enable === '读写' ? '读写' : '只读';
57   - return h(Tag, { color }, () => text);
58   - },
59   - },
60   - {
61   - title: '创建人',
62   - dataIndex: 'description',
63   - width: 80,
64   - },
65   - {
66 52 title: '创建时间',
67 53 dataIndex: 'createTime',
68 54 width: 150,
... ... @@ -411,130 +397,3 @@ export const formSchema: FormSchema[] = [
411 397 },
412 398 },
413 399 ];
414   -
415   -export const mockData: any = async () => {
416   - const res = await [
417   - {
418   - dType: 'events',
419   - name: '亮度百分比',
420   - type: 'Brightness',
421   - transportType: 'int32(整数型)',
422   - description1: '饱和度/aw',
423   - default: '1',
424   - actionStatus: 'SUCCESS',
425   - description: 'cheche',
426   - createTime: '2022-10-20 10:24:22',
427   - },
428   - {
429   - dType: 'service',
430   - name: '运行状态',
431   - type: 'RunningState',
432   - transportType: 'bool(布尔型)',
433   - description1: '',
434   - default: '1',
435   - actionStatus: 'FA',
436   - description: 'cheche',
437   - createTime: '2022-10-20 10:24:22',
438   - },
439   - {
440   - dType: 'attr',
441   - name: '设备运行状态',
442   - type: 'E_Status_UP',
443   - transportType: 'text(字符串)',
444   - description1: '',
445   - default: '1',
446   - actionStatus: 'D',
447   - description: 'cheche',
448   - createTime: '2022-10-20 10:24:22',
449   - },
450   - {
451   - dType: 'attr',
452   - name: '过流告警使能',
453   - type: 'OverCurrentEnable',
454   - transportType: 'struct(结构体)',
455   - description1: '',
456   - default: '1',
457   - actionStatus: 'SUCCESS',
458   - description: 'cheche',
459   - createTime: '2022-10-20 10:24:22',
460   - },
461   - {
462   - dType: 'events',
463   - name: '变频器运行状态1',
464   - type: 'Brightness',
465   - transportType: 'int32(整数型)',
466   - description1: '转每分钟/turn/m',
467   - default: '1',
468   - actionStatus: 'Fa',
469   - description: 'cheche',
470   - createTime: '2022-10-20 10:24:22',
471   - },
472   - {
473   - dType: 'service',
474   - name: '产品序列号',
475   - type: 'SerialNo',
476   - transportType: 'text(字符串)',
477   - description1: '',
478   - default: '1',
479   - actionStatus: 'FA',
480   - description: 'cheche',
481   - createTime: '2022-10-20 10:24:22',
482   - },
483   - {
484   - dType: 'service',
485   - name: '音量百分比',
486   - type: 'Volume',
487   - transportType: 'int32(整数型)',
488   - description1: '分贝/db',
489   - default: '1',
490   - actionStatus: 'SUCCESS',
491   - description: 'cheche',
492   - createTime: '2022-10-20 10:24:22',
493   - },
494   - {
495   - dType: 'attr',
496   - name: '易释放氰化物',
497   - type: 'easy_release_cyanide',
498   - transportType: 'double(双精度浮点型)',
499   - description1: '毫克每升/mg/L',
500   - default: '1',
501   - actionStatus: 'SUCCESS',
502   - description: 'cheche',
503   - createTime: '2022-10-20 10:24:22',
504   - },
505   - {
506   - dType: 'attr',
507   - name: '湿度',
508   - type: 'Humidity',
509   - transportType: 'float(单精度浮点型)',
510   - description1: '相对湿度/%RH',
511   - default: '1',
512   - actionStatus: 'SUCCESS',
513   - description: 'cheche',
514   - createTime: '2022-10-20 10:24:22',
515   - },
516   - {
517   - dType: 'attr',
518   - name: '设备固件版本',
519   - type: 'FirmwareVersion',
520   - transportType: 'text(字符串)',
521   - description1: '',
522   - default: '1',
523   - actionStatus: 'SUCCESS',
524   - description: 'cheche',
525   - createTime: '2022-10-20 10:24:22',
526   - },
527   - {
528   - dType: 'events',
529   - name: '用电量',
530   - type: 'PowerConsumption',
531   - transportType: 'float(单精度浮点型)',
532   - description1: '千瓦时/kW.h',
533   - default: '1',
534   - actionStatus: 'F',
535   - description: 'cheche',
536   - createTime: '2022-10-20 10:24:22',
537   - },
538   - ];
539   - return res;
540   -};
... ...
... ... @@ -8,28 +8,29 @@
8 8 <template #toolbar>
9 9 <div class="flex-auto">
10 10 <div class="mb-2">
11   - <a-alert v-if="!isShowBtn" type="info" show-icon>
  11 + <Alert type="info" show-icon>
12 12 <template #message>
13   - <span>
14   - 当前展示的是已发布到线上的功能定义,如需修改,请点击
  13 + <span v-if="!isShowBtn">
  14 + 当前展示的是已发布到线上的功能定义,如需修改,请点击
15 15 <span
16 16 @click="handleEditPhysicalModel"
17 17 class="cursor-pointer text-blue-400"
18 18 size="small"
19 19 >
20   - “编辑物模型”
  20 + "编辑物模型"
21 21 </span>
22 22 </span>
  23 + <span v-if="isShowBtn"> 您正在编辑的是草稿,需点击发布后,物模型才会正式生效. </span>
23 24 </template>
24   - </a-alert>
  25 + </Alert>
25 26 </div>
26 27 <div class="flex justify-between items-end">
27 28 <div class="flex gap-2">
28 29 <Authority value="">
29   - <a-button v-if="isShowBtn" type="primary" @click="handleCreateOrEdit(null)">
  30 + <Button v-if="isShowBtn" type="primary" @click="handleCreateOrEdit()">
30 31 新增物模型
31   - </a-button>
32   - <a-button type="primary" @click="handleOpenTsl"> 物模型TSL </a-button>
  32 + </Button>
  33 + <Button type="primary" @click="handleOpenTsl"> 物模型TSL </Button>
33 34 </Authority>
34 35 </div>
35 36 <div class="flex gap-2">
... ... @@ -40,25 +41,25 @@
40 41 cancel-text="取消"
41 42 @confirm="handleEmit"
42 43 >
43   - <a-button v-if="isShowBtn" type="primary"> 发布上线 </a-button>
  44 + <Button v-if="isShowBtn" type="primary"> 发布上线 </Button>
44 45 </Popconfirm>
45   - <a-button v-if="isShowBtn" class="!bg-gray-200" type="text" @click="handleReturn">
  46 + <Button v-if="isShowBtn" class="!bg-gray-200" type="text" @click="handleReturn">
46 47 返回
47   - </a-button>
  48 + </Button>
48 49 <Popconfirm
49 50 title="您确定要批量删除数据"
50 51 ok-text="确定"
51 52 cancel-text="取消"
52 53 @confirm="handleDeleteOrBatchDelete(null)"
53 54 >
54   - <a-button
  55 + <Button
55 56 style="display: none"
56 57 type="primary"
57 58 color="error"
58 59 :disabled="hasBatchDelete"
59 60 >
60 61 批量删除
61   - </a-button>
  62 + </Button>
62 63 </Popconfirm>
63 64 </Authority>
64 65 </div>
... ... @@ -96,39 +97,46 @@
96 97 />
97 98 </template>
98 99 </BasicTable>
99   - <PhysicalModelModal @register="registerModal" @success="handleSuccess" />
100   - <PhysicalModelTsl @register="registerModalTsl" />
  100 + <PhysicalModelModal
  101 + :record="$props.record"
  102 + @register="registerModal"
  103 + @success="handleSuccess"
  104 + />
  105 + <PhysicalModelTsl :record="$props.record" @register="registerModalTsl" />
101 106 </div>
102 107 </template>
103 108 <script lang="ts" setup>
104   - import { ref } from 'vue';
105 109 import { BasicTable, useTable, TableAction } from '/@/components/Table';
106 110 import { useModal } from '/@/components/Modal';
107 111 import { physicalColumn } from '../device.profile.data';
108 112 import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
109   - import { deleteReportManage } from '/@/api/report/reportManager';
110 113 import { Authority } from '/@/components/Authority';
111   - // import { mockData } from '/@/api/device/deviceConfigApi';
112 114 import PhysicalModelModal from './cpns/physical/PhysicalModelModal.vue';
113 115 import PhysicalModelTsl from './cpns/physical/PhysicalModelTsl.vue';
114   - import { Popconfirm } from 'ant-design-vue';
  116 + import { Popconfirm, Button, Alert } from 'ant-design-vue';
115 117 import { useMessage } from '/@/hooks/web/useMessage';
116   - import { mockData } from '../device.profile.data';
117   -
  118 + import { DeviceRecord } from '/@/api/device/model/deviceModel';
  119 + import { deleteModel, getModelList } from '/@/api/device/modelOfMatter';
  120 + import { OpenModelOfMatterModelParams, OpenModelMode } from './cpns/physical/types';
  121 + import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  122 + import { ref } from 'vue';
118 123 defineEmits(['register']);
  124 +
  125 + defineProps<{
  126 + record: DeviceRecord;
  127 + }>();
  128 +
119 129 const { createMessage } = useMessage();
120 130 const isShowBtn = ref(false);
121 131 const [registerModal, { openModal }] = useModal();
122 132 const [registerModalTsl, { openModal: openModalTsl }] = useModal();
123 133
124 134 const [registerTable, { reload, setProps }] = useTable({
125   - // api: deviceConfigGetQuery,
126   - api: mockData,
  135 + api: getModelList,
127 136 columns: physicalColumn,
128 137 showIndexColumn: false,
129 138 clickToRowSelect: false,
130 139 useSearchForm: false,
131   - // rowKey: 'id',
132 140 showTableSetting: true,
133 141 bordered: true,
134 142 actionColumn: {
... ... @@ -139,16 +147,18 @@
139 147 fixed: 'right',
140 148 },
141 149 });
  150 +
142 151 // 刷新
143 152 const handleSuccess = () => {
144 153 reload();
145 154 };
146 155
147 156 const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(
148   - deleteReportManage,
  157 + deleteModel,
149 158 handleSuccess,
150 159 setProps
151 160 );
  161 +
152 162 selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => {
153 163 // Demo:status为1的选择框禁用
154 164 if (record.status === 1) {
... ... @@ -157,38 +167,36 @@
157 167 return { disabled: false };
158 168 }
159 169 };
160   - const handleViewDetail = (record: Recordable | null) => {
  170 +
  171 + const handleViewDetail = (record: ModelOfMatterParams) => {
161 172 if (record) {
162 173 openModal(true, {
163   - isUpdate: true,
164 174 record,
165   - isView: true,
166   - isText: 'view',
167   - });
  175 + mode: OpenModelMode.VIEW,
  176 + } as OpenModelOfMatterModelParams);
168 177 }
169 178 };
  179 +
170 180 // 新增或编辑
171   - const handleCreateOrEdit = (record: Recordable | null) => {
  181 + const handleCreateOrEdit = (record?: ModelOfMatterParams) => {
172 182 if (record) {
173 183 openModal(true, {
174   - isUpdate: false,
  184 + mode: OpenModelMode.UPDATE,
175 185 record,
176   - isView: false,
177   - isText: 'edit',
178   - });
  186 + } as OpenModelOfMatterModelParams);
179 187 } else {
180 188 openModal(true, {
181   - isUpdate: true,
182   - isView: false,
183   - isText: 'add',
184   - });
  189 + mode: OpenModelMode.CREATE,
  190 + } as OpenModelOfMatterModelParams);
185 191 }
186 192 };
  193 +
187 194 const handleOpenTsl = () => {
188 195 openModalTsl(true, {
189 196 isUpdate: true,
190 197 });
191 198 };
  199 +
192 200 const handleEditPhysicalModel = () => (isShowBtn.value = true);
193 201
194 202 const handleReturn = () => (isShowBtn.value = false);
... ...
... ... @@ -2,69 +2,66 @@
2 2 <div>
3 3 <BasicModal
4 4 :maskClosable="false"
  5 + destroy-on-close
5 6 v-bind="$attrs"
6 7 width="55rem"
7 8 @register="register"
8 9 @ok="handleSubmit"
9 10 @cancel="handleCancel"
10 11 >
11   - <div v-if="isViewDetail">
12   - <Attribute v-show="activeKey === '1'" ref="AttrRef" />
13   - <Service v-show="activeKey === '2'" ref="ServiceRef" />
14   - <Events v-show="activeKey === '3'" ref="EventsRef" />
15   - </div>
16   - <div v-if="!isViewDetail">
17   - <div>
  12 + <div>
  13 + <div v-if="openModalMode === OpenModelMode.CREATE">
18 14 <Typography>
19 15 <TypographyParagraph>
20   - <blockquote style="background: #f2f2f2">{{ useBlockPhysicalContent }}</blockquote>
  16 + <blockquote class="bg-gray-100">{{ blockContent }}</blockquote>
21 17 </TypographyParagraph>
22 18 </Typography>
23 19 </div>
24   - <Tabs type="card" v-model:activeKey="activeKey" :size="size">
25   - <TabPane :disabled="attrDisable" forceRender key="1" tab="属性">
26   - <Attribute v-show="activeKey === '1'" ref="AttrRef" />
27   - </TabPane>
28   - <TabPane :disabled="serveiceDisable" forceRender key="2" tab="服务">
29   - <Service v-show="activeKey === '2'" ref="ServiceRef" />
30   - </TabPane>
  20 + <Tabs
  21 + v-if="openModalMode === OpenModelMode.CREATE"
  22 + type="card"
  23 + v-model:activeKey="activeKey"
  24 + :size="size"
  25 + >
  26 + <TabPane :key="FunctionType.PROPERTIES" tab="属性" />
  27 + <TabPane :key="FunctionType.SERVICE" tab="服务" />
31 28 <TabPane
32   - :disabled="eventDisable"
33   - forceRender
34   - key="3"
35   - v-show="activeKey === '3'"
  29 + :key="FunctionType.EVENTS"
36 30 tab="事件"
37   - >
38   - <Events v-show="activeKey === '3'" ref="EventsRef" />
39   - </TabPane>
  31 + :disabled="$props.record.transportType === 'TCP'"
  32 + />
40 33 </Tabs>
  34 + <Attribute v-show="activeKey === FunctionType.PROPERTIES" ref="AttrRef" />
  35 + <Service v-show="activeKey === FunctionType.SERVICE" ref="ServiceRef" />
  36 + <Events v-show="activeKey === FunctionType.EVENTS" ref="EventsRef" />
41 37 </div>
42 38 </BasicModal>
43 39 </div>
44 40 </template>
45 41 <script lang="ts" setup>
46   - import { ref, unref, reactive, nextTick } from 'vue';
  42 + import { ref, unref } from 'vue';
47 43 import { BasicModal, useModalInner } from '/@/components/Modal';
48 44 import { Tabs, TabPane, Typography, TypographyParagraph } from 'ant-design-vue';
49 45 import Attribute from './cpns/Attribute.vue';
50 46 import Service from './cpns/Service.vue';
51 47 import Events from './cpns/Events.vue';
52   - import { mockData } from '../physical/cpns/components/mock';
53   - import useCommon from './hook/useCommon';
54   - import { IAllData } from './types';
55   -
56   - defineEmits(['register']);
57   -
58   - const attrDisable = ref(false);
59   -
60   - const serveiceDisable = ref(false);
61   -
62   - const eventDisable = ref(false);
63   -
64   - const { useBlockPhysicalContent } = useCommon();
65   -
66   - const activeKey = ref('1');
67   -
  48 + import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  49 + import { createModel, updateModel } from '/@/api/device/modelOfMatter';
  50 + import { DeviceRecord } from '/@/api/device/model/deviceModel';
  51 + import { useMessage } from '/@/hooks/web/useMessage';
  52 + import { OpenModelMode, OpenModelOfMatterModelParams } from './types/index';
  53 + import { FunctionType } from './cpns/config';
  54 + import { StructRecord } from '/@/components/Form/src/externalCompns/components/StructForm/type';
  55 +
  56 + const emit = defineEmits(['register', 'success']);
  57 +
  58 + const props = defineProps<{
  59 + record: DeviceRecord;
  60 + }>();
  61 +
  62 + const blockContent = `属性一般是设备的运行状态,如当前温度等;服务是设备可被调用的方法,支持定义参数,如执行某项任务;事件则是设备上报的
  63 +通知,如告警,需要被及时处理。`;
  64 + const activeKey = ref<FunctionType>(FunctionType.PROPERTIES);
68 65 const size = ref('small');
69 66
70 67 const AttrRef = ref<InstanceType<typeof Attribute>>();
... ... @@ -72,121 +69,87 @@
72 69 const ServiceRef = ref<InstanceType<typeof Service>>();
73 70
74 71 const EventsRef = ref<InstanceType<typeof Events>>();
  72 + const openModalMode = ref<OpenModelMode>(OpenModelMode.CREATE);
  73 + const openModalParams = ref<OpenModelOfMatterModelParams>();
75 74
76   - const isUpdate = ref(false);
77   -
78   - const isViewDetail = ref('');
79   -
80   - const isText = ref('');
81   -
82   - const dType = ref('');
83   -
84   - const allData = reactive<IAllData>({
85   - properties: [],
86   - events: [],
87   - services: [],
88   - productKey: '',
89   - _ppk: {},
90   - });
91   -
92   - const setAttrFormData = (data: {}) => AttrRef.value?.setFormData(data);
  75 + const functionType = ref<FunctionType>();
  76 + const { createMessage } = useMessage();
93 77
94   - const setServiceFormData = (data: {}) => ServiceRef.value?.setFormData(data);
95   -
96   - const setEventsFormData = (data: {}) => EventsRef.value?.setFormData(data);
  78 + const setAttrFormData = (data: StructRecord) => AttrRef.value?.setFormData(data);
  79 + const setServiceFormData = (data) => ServiceRef.value?.setFormData(data);
  80 + const setEventsFormData = (data) => EventsRef.value?.setFormData(data);
97 81
98 82 const enums = {
99   - attr: setAttrFormData,
100   - service: setServiceFormData,
101   - events: setEventsFormData,
  83 + [FunctionType.PROPERTIES]: setAttrFormData,
  84 + [FunctionType.SERVICE]: setServiceFormData,
  85 + [FunctionType.EVENTS]: setEventsFormData,
102 86 };
103 87
104   - function action(val, data) {
105   - let F = enums[val];
106   - F(data);
107   - }
108   -
109   - function dynamicDisable(a, s, e) {
110   - attrDisable.value = a;
111   - serveiceDisable.value = s;
112   - eventDisable.value = e;
  88 + function setFormData(type: FunctionType, value: StructRecord) {
  89 + const setFn = enums[type];
  90 + setFn(value);
113 91 }
114 92
115   - function dynamicActive(n, callback) {
116   - activeKey.value = n;
117   - callback;
118   - }
119   -
120   - const dynamicData = (t, a, s, e) => {
121   - switch (t) {
122   - case 'attr':
123   - dynamicActive('1', action(t, a));
124   - dynamicDisable(false, true, true);
125   - break;
126   - case 'service':
127   - dynamicActive('2', action(t, s));
128   - dynamicDisable(true, false, true);
129   - break;
130   - case 'events':
131   - dynamicActive('3', action(t, e));
132   - dynamicDisable(true, true, false);
133   - break;
134   - }
135   - };
136   -
137   - const [register, { closeModal, setModalProps }] = useModalInner(async (data) => {
138   - setModalProps({ loading: true });
139   - handleCancel(false);
140   - isUpdate.value = data.isUpdate;
141   - isViewDetail.value = data.isView;
142   - isText.value = data.isText;
143   - dType.value = data.record?.dType;
144   - if (!unref(isViewDetail)) {
145   - const title = !unref(isUpdate) ? '编辑物模型' : '新增物模型';
146   - if (!unref(isUpdate)) {
147   - nextTick(() => {
148   - dynamicData(dType.value, mockData.properties, mockData.services, mockData.events);
149   - });
  93 + const [register, { closeModal, setModalProps }] = useModalInner(
  94 + async (data: OpenModelOfMatterModelParams) => {
  95 + openModalParams.value = data;
  96 + const { mode, record } = data;
  97 + openModalMode.value = mode;
  98 + if (record) {
  99 + functionType.value = data.record.functionType;
  100 + activeKey.value = data.record.functionType;
  101 + setFormData(record.functionType, record as unknown as StructRecord);
  102 + }
  103 + if (unref(openModalMode) === OpenModelMode.VIEW) {
  104 + setModalProps({ showOkBtn: false, showCancelBtn: false, title: '查看物模型' });
  105 + // setFormData(functionType.value, record);
  106 + } else {
  107 + const title = unref(openModalMode) === OpenModelMode.UPDATE ? '编辑物模型' : '新增物模型';
  108 + if (OpenModelMode.UPDATE) {
  109 + // setFormData(functionType.value, record);
  110 + }
  111 + setModalProps({ title, showOkBtn: true, showCancelBtn: true });
150 112 }
151   - setModalProps({ title, showOkBtn: true, showCancelBtn: true });
152   - } else {
153   - setModalProps({ showOkBtn: false, showCancelBtn: false, title: '查看物模型' });
154   - nextTick(() => {
155   - dynamicData(dType.value, mockData.properties, mockData.services, mockData.events);
156   - });
157 113 }
158   - setModalProps({ loading: false });
159   - });
  114 + );
160 115
161 116 const handleCancel = (flag) => {
162 117 AttrRef.value?.resetFormData();
163 118 ServiceRef.value?.resetFormData();
164 119 EventsRef.value?.resetFormData();
165   - activeKey.value = '1';
166   - allData.properties = [];
167   - allData.events = [];
168   - allData.services = [];
169   - attrDisable.value = false;
170   - serveiceDisable.value = false;
171   - eventDisable.value = false;
  120 + activeKey.value = FunctionType.PROPERTIES;
172 121 if (flag) {
173 122 closeModal();
174 123 }
175 124 };
176 125
177 126 const handleSubmit = async () => {
178   - if (activeKey.value == '1') {
179   - const valueAttr = await AttrRef.value?.getFormData();
180   - allData.properties.push(valueAttr);
181   - } else if (activeKey.value == '2') {
182   - const valueService = await ServiceRef.value?.getFormData();
183   - allData.services.push(valueService);
184   - } else {
185   - const valueEvents = await EventsRef.value?.getFormData();
186   - allData.events.push(valueEvents);
  127 + try {
  128 + setModalProps({ loading: false, okButtonProps: { loading: true } });
  129 +
  130 + let params: Partial<ModelOfMatterParams>;
  131 + if (activeKey.value == FunctionType.PROPERTIES) {
  132 + params = (await AttrRef.value?.getFormData()) || {};
  133 + } else if (activeKey.value == FunctionType.SERVICE) {
  134 + params = (await ServiceRef.value?.getFormData()) || {};
  135 + } else {
  136 + params = await EventsRef.value?.getFormData();
  137 + }
  138 + params.deviceProfileId = props.record.id;
  139 + if (unref(openModalMode) === OpenModelMode.CREATE) {
  140 + await createModel(params);
  141 + createMessage.success('创建成功');
  142 + } else {
  143 + params.id = unref(openModalParams)?.record.id;
  144 + await updateModel(params);
  145 + createMessage.success('修改成功');
  146 + }
  147 + closeModal();
  148 + emit('success');
  149 + } catch (error) {
  150 + } finally {
  151 + setModalProps({ loading: false, okButtonProps: { loading: false } });
187 152 }
188   - console.log('搜集值', allData);
189   - closeModal();
190 153 };
191 154 </script>
192 155
... ...
1 1 <template>
2   - <div>
3   - <BasicForm @register="register">
4   - <template #outputParamSlot>
5   - <CommomParam
6   - ref="CommomParamInParamRef"
7   - :isExcludeStruct="true"
8   - :isInputParam="true"
9   - :inputParamData="inputParamData"
10   - @emitAddInParam="handleAddInParam"
11   - @emitEditInParam="handleEditInParam"
12   - @emitDeletelnParam="handleDelInParam"
13   - />
14   - </template>
15   - </BasicForm>
16   - <AddParamsModal @register="registerModal" @data="getData" />
17   - </div>
  2 + <BasicForm @register="register" />
18 3 </template>
19 4 <script lang="ts" setup>
20   - import { unref } from 'vue';
21 5 import { BasicForm, useForm } from '/@/components/Form';
22   - import { attrSchemas } from './config';
23   - import { useModal } from '/@/components/Modal';
24   - import useParitalValid from '../hook/useParitalValid';
25   - import useCommon from '../hook/useCommon';
26   - import AddParamsModal from './components/AddParamsModal.vue';
27   - import CommomParam from './components/CommomParam.vue';
28   - import useDefineVar from '../hook/useDefineVar';
29   -
30   - const { useChangeTypeGetTypeForm, useGetInOrOutData } = useCommon();
31   -
32   - const { validateValueStruct } = useParitalValid();
33   -
34   - const { inputParamData, CommomParamInParamRef } = useDefineVar();
35   -
36   - const [registerModal, { openModal }] = useModal();
37   -
38   - const [register, { validate, setFieldsValue, resetFields }] = useForm({
  6 + import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  7 + import { formSchemas } from '/@/components/Form/src/externalCompns/components/StructForm/config';
  8 + import { transformFormValue } from '/@/components/Form/src/externalCompns/components/StructForm/util';
  9 + import {
  10 + StructFormValue,
  11 + StructRecord,
  12 + } from '/@/components/Form/src/externalCompns/components/StructForm/type';
  13 + import { isObject } from 'lodash';
  14 +
  15 + const [register, { validate, resetFields, setFieldsValue }] = useForm({
39 16 labelWidth: 100,
40   - schemas: attrSchemas,
  17 + schemas: formSchemas,
41 18 actionColOptions: {
42 19 span: 14,
43 20 },
... ... @@ -46,84 +23,34 @@
46 23 showActionButtonGroup: false,
47 24 });
48 25
49   - const getData = (d, f) => useGetInOrOutData(d, f, unref(inputParamData), []);
50   -
51   - const handleAddInParam = (b, o) => openModal(b, o);
52   -
53   - const handleEditInParam = (b, o) => openModal(b, o);
54   -
55   - const handleDelInParam = (i) => unref(inputParamData).splice(i, 1);
56   -
57   - //回显不同类型数据
58   - const setTypeData = (T, D) => {
59   - if (T === 'INT' || T === 'DOUBLE' || T === 'TEXT') {
60   - return {
61   - ...D?.dataSpecs,
62   - valueRange: { ...D?.dataSpecs },
63   - };
64   - } else if (T === 'BOOL') {
65   - return {
66   - boolClose: D?.dataSpecsList[0]?.name,
67   - boolOpen: D?.dataSpecsList[1]?.name,
68   - };
69   - }
70   - };
  26 + async function getFormData(): Promise<Partial<ModelOfMatterParams>> {
  27 + const _values = (await validate()) as StructFormValue;
  28 + if (!_values) return {};
  29 + let value = transformFormValue(_values);
  30 + return value;
  31 + }
71 32
72   - const setFormData = (v) => {
73   - try {
74   - setFieldsValue({ ...v[0] });
75   - setFieldsValue(setTypeData(v[0]?.dataType, v[0]));
76   - //回显结构体数据
77   - const { dataSpecsList } = v[0];
78   - if (dataSpecsList !== undefined) {
79   - inputParamData.value = [...new Array(dataSpecsList.length).keys()] && dataSpecsList;
80   - }
81   - } catch (e) {
82   - console.log('Attribute error info', e);
83   - }
  33 + const resetFormData = () => {
  34 + resetFields();
84 35 };
85 36
86   - //获取结构体数据
87   - const getStructList = () => CommomParamInParamRef.value?.getInputStructList();
88   -
89   - const getBoolOrStructData = (T, S, B) => {
90   - if (T === 'STRUCT') {
91   - return S;
92   - } else {
93   - return B;
94   - }
95   - };
  37 + const setFormData = (record: StructRecord) => {
  38 + const { functionJson } = record as StructRecord;
  39 + const { specs = {} } = functionJson || ({} as StructRecord['functionJson']);
96 40
97   - async function getFormData() {
98   - const values = await validate();
99   - if (!values) return;
100   - const dataSpecsList = getStructList();
101   - if (values.dataType === 'STRUCT') {
102   - validateValueStruct(dataSpecsList as any);
103   - }
104   - const dataSpecs = useChangeTypeGetTypeForm(values.dataType, values);
105   - const dataSpecsListBool = useChangeTypeGetTypeForm(values.dataType, values);
106   - const { valueRange, step, unit, outputParam, ...value } = values;
107   - const none = [valueRange, step, unit, outputParam]; //没用,防止eslint报未使用变量
108   - console.log(none);
109   - return {
110   - ...value,
111   - ...{ dataSpecs },
112   - ...{
113   - dataSpecsList: getBoolOrStructData(values.dataType, dataSpecsList, dataSpecsListBool),
114   - },
  41 + const value = {
  42 + ...record,
  43 + ...functionJson,
  44 + ...(isObject(specs) ? specs : {}),
115 45 };
116   - }
117 46
118   - const resetFormData = () => {
119   - resetFields();
120   - inputParamData.value = [];
  47 + setFieldsValue(value);
121 48 };
122 49
123 50 defineExpose({
124   - setFormData,
125 51 resetFormData,
126 52 getFormData,
  53 + setFormData,
127 54 });
128 55 </script>
129 56 <style lang="less" scoped></style>
... ...
1 1 <template>
2   - <div>
3   - <BasicForm @register="register">
4   - <template #outputParamSlot>
5   - <CommomParam
6   - ref="CommomParamInParamRef"
7   - :isInputParam="true"
8   - :isExcludeStruct="false"
9   - :inputParamData="inputParamData"
10   - @emitAddInParam="handleAddInParam"
11   - @emitEditInParam="handleEditInParam"
12   - @emitDeletelnParam="handleDelInParam"
13   - />
14   - </template>
15   - </BasicForm>
16   - <AddParamsModal @register="registerModal" @data="getData" />
17   - </div>
  2 + <BasicForm @register="register" />
18 3 </template>
19 4 <script lang="ts" setup>
20   - import { unref } from 'vue';
21 5 import { BasicForm, useForm } from '/@/components/Form';
22 6 import { eventSchemas } from './config';
23   - import { useModal } from '/@/components/Modal';
24   - import AddParamsModal from './components/AddParamsModal.vue';
25   - import CommomParam from './components/CommomParam.vue';
26   - import useCommon from '../hook/useCommon';
27   - import useDefineVar from '../hook/useDefineVar';
  7 + import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
28 8
29   - const { useGetInOrOutData } = useCommon();
30   -
31   - const { inputParamData, CommomParamInParamRef } = useDefineVar();
32   -
33   - const [registerModal, { openModal }] = useModal();
34   -
35   - const [register, { validate, setFieldsValue, resetFields }] = useForm({
  9 + const [register, { validate, resetFields }] = useForm({
36 10 labelWidth: 100,
37 11 schemas: eventSchemas,
38 12 actionColOptions: {
... ... @@ -43,43 +17,18 @@
43 17 showActionButtonGroup: false,
44 18 });
45 19
46   - const getData = (d, f) => useGetInOrOutData(d, f, unref(inputParamData), []);
47   -
48   - const handleAddInParam = (b, o) => openModal(b, o);
49   -
50   - const handleEditInParam = (b, o) => openModal(b, o);
51   -
52   - const handleDelInParam = (i) => unref(inputParamData).splice(i, 1);
53   -
54 20 //回显数据
55   - const setFormData = (v) => {
56   - setFieldsValue(v[0]);
57   - const { outputData } = v[0];
58   - if (outputData !== undefined) {
59   - inputParamData.value = [...new Array(outputData.length).keys()] && outputData;
60   - }
61   - };
62   -
63   - //获取数据
64   - const getStructList = () => CommomParamInParamRef.value?.getInputStructList();
  21 + const setFormData = () => {};
65 22
66 23 async function getFormData() {
67   - const values = await validate();
  24 + const values = (await validate()) as ModelOfMatterParams;
68 25 if (!values) return;
69   - const outputData = getStructList();
70   - const { outputParam, ...value } = values;
71   - const none = [outputParam]; //没用,防止eslint报未使用变量
72   - console.log(none);
73   - return {
74   - ...value,
75   - ...{ outputData },
76   - };
  26 + console.log(values);
77 27 }
78 28
79 29 //清空数据
80 30 const resetFormData = () => {
81 31 resetFields();
82   - inputParamData.value = [];
83 32 };
84 33
85 34 defineExpose({
... ...
1 1 <template>
2   - <div>
3   - <BasicForm @register="register">
4   - <template #inputParamSlot>
5   - <CommomParam
6   - ref="CommomParamInParamRef"
7   - :isInputParam="true"
8   - :isExcludeStruct="false"
9   - :inputParamData="inputParamData"
10   - @emitAddInParam="handleAddInParam"
11   - @emitEditInParam="handleEditInParam"
12   - @emitDeletelnParam="handleDelInParam"
13   - />
14   - </template>
15   - <template #outputParamSlot>
16   - <CommomParam
17   - ref="CommomParamOutParamRef"
18   - :isOutputParam="true"
19   - :isExcludeStruct="false"
20   - :outputParamData="outputParamData"
21   - @emitAddOutParam="handleAddOutParam"
22   - @emitEditOutParam="handleEditOutParam"
23   - @emitDeleteOutParam="handleDelOutParam"
24   - />
25   - </template>
26   - </BasicForm>
27   - <AddParamsModal @register="registerModal" @data="getData" />
28   - </div>
  2 + <BasicForm @register="register" />
29 3 </template>
30 4 <script lang="ts" setup>
31   - import { unref } from 'vue';
32 5 import { BasicForm, useForm } from '/@/components/Form';
33 6 import { serviceSchemas } from './config';
34   - import { useModal } from '/@/components/Modal';
35   - import AddParamsModal from './components/AddParamsModal.vue';
36   - import CommomParam from './components/CommomParam.vue';
37   - import useCommon from '../hook/useCommon';
38   - import useDefineVar from '../hook/useDefineVar';
39   -
40   - const { inputParamData, outputParamData, CommomParamInParamRef, CommomParamOutParamRef } =
41   - useDefineVar();
42   -
43   - const { useGetInOrOutData } = useCommon();
44   -
45   - const [registerModal, { openModal }] = useModal();
46   -
47   - const [register, { validate, setFieldsValue, resetFields }] = useForm({
  7 + import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  8 + import { FunctionType } from './config';
  9 + const [register, { validate, resetFields }] = useForm({
48 10 labelWidth: 100,
49 11 schemas: serviceSchemas,
50 12 actionColOptions: {
... ... @@ -55,57 +17,21 @@
55 17 showActionButtonGroup: false,
56 18 });
57 19
58   - const getData = (d, f) => useGetInOrOutData(d, f, unref(inputParamData), unref(outputParamData));
59   -
60   - const handleAddInParam = (b, o) => openModal(b, o);
61   -
62   - const handleEditInParam = (b, o) => openModal(b, o);
63   -
64   - const handleDelInParam = (i) => unref(inputParamData).splice(i, 1);
65   -
66   - const handleAddOutParam = (b, o) => openModal(b, o);
67   -
68   - const handleEditOutParam = (b, o) => openModal(b, o);
69   -
70   - const handleDelOutParam = (i) => unref(outputParamData).splice(i, 1);
71   -
72 20 //回显数据
73   - const setFormData = (v) => {
74   - setFieldsValue(v[0]);
75   - const { inputParams, outputParams } = v[0];
76   - if (outputParams !== undefined) {
77   - outputParamData.value = [...new Array(outputParams.length).keys()] && outputParams;
78   - }
79   - if (inputParams !== undefined) {
80   - inputParamData.value = [...new Array(inputParams.length).keys()] && inputParams;
81   - }
82   - };
83   -
84   - const getInputStructList = () => CommomParamInParamRef.value?.getInputStructList();
85   -
86   - const getOutputStructList = () => CommomParamOutParamRef.value?.getOutputStructList();
  21 + const setFormData = () => {};
87 22
88 23 //获取数据
89 24 async function getFormData() {
90   - const values = await validate();
  25 + const values = (await validate()) as ModelOfMatterParams;
91 26 if (!values) return;
92   - const inputParams = getInputStructList();
93   - const outputParams = getOutputStructList();
94   - const { inputParam, outputParam, ...value } = values;
95   - const none = [inputParam, outputParam]; //没用,防止eslint报未使用变量
96   - console.log(none);
97   - return {
98   - ...value,
99   - ...{ inputParams },
100   - ...{ outputParams },
101   - };
  27 + values.functionType = FunctionType.SERVICE;
  28 + console.log(values);
  29 + return values;
102 30 }
103 31
104 32 //清空数据
105 33 const resetFormData = () => {
106 34 resetFields();
107   - inputParamData.value = [];
108   - outputParamData.value = [];
109 35 };
110 36
111 37 defineExpose({
... ...
... ... @@ -34,7 +34,7 @@
34 34
35 35 const [registerModal, { openModal }] = useModal();
36 36
37   - const [registerForm, { validate, setFieldsValue, resetFields, updateSchema }] = useForm({
  37 + const [registerForm, { validate, resetFields, updateSchema }] = useForm({
38 38 labelWidth: 100,
39 39 schemas: addParamsSchemas,
40 40 actionColOptions: {
... ...
... ... @@ -21,7 +21,7 @@
21 21
22 22 const emits = defineEmits(['register', 'data']);
23 23
24   - const { validateValueStruct } = useParitalValid();
  24 + const {} = useParitalValid();
25 25
26 26 const setEditData: any = reactive({
27 27 getEditData: {},
... ... @@ -37,9 +37,9 @@
37 37
38 38 const getTitle = computed(() => (!isUpdate.value ? '编辑参数' : '新增参数'));
39 39
40   - const [register, { closeModal, setModalProps }] = useModalInner(async (data) => {
  40 + const [register, { setModalProps }] = useModalInner(async (data) => {
41 41 setModalProps({ loading: true });
42   - handleCancel(false);
  42 +
43 43 isUpdate.value = data.isUpdate;
44 44 isFlag.value = data.flag;
45 45 excludeStruct.value = data.excludeStruct;
... ... @@ -53,29 +53,15 @@
53 53 }
54 54 });
55 55
56   - const handleCancel = (flag) => {
  56 + const handleCancel = () => {
57 57 AddParamFormRef.value?.resetFormData();
58   - if (flag) {
59   - closeModal();
60   - }
61 58 };
62 59
63 60 const handleSubmit = async () => {
64 61 const value = await AddParamFormRef.value?.getFormData();
65 62 if (!value) return;
66   - if (value.dataType === 'STRUCT') {
67   - validateValueStruct(value.dataSpecsList as any);
68   - }
69   - emits(
70   - 'data',
71   - {
72   - ...value,
73   - ...{ id: !isUpdate.value ? setEditData.getEditData.id : null },
74   - ...{ index: !isUpdate.value ? setEditData.getEditData.index : null },
75   - },
76   - isFlag.value
77   - );
78   - handleCancel(true);
  63 + emits('data', value);
  64 + handleCancel();
79 65 };
80 66 </script>
81 67 <style lang="less" scope></style>
... ...
1 1 <template>
2 2 <div>
3   - <a-card :title="`参数名称:${title}`" style="width: 540px; margin-top: -5px">
  3 + <a-card :title="`参数名称:${item.id}`" style="width: 540px; margin-top: -5px">
4 4 <template #extra>
5 5 <div style="display: flex; align-items: center">
6   - <span style="color: #0170cc; cursor: pointer" @click="handleEdit(item, index)">编辑</span>
  6 + <span style="color: #0170cc; cursor: pointer" @click="handleEdit">编辑</span>
7 7 <a-divider type="vertical" style="height: 12px; background-color: #d8d8d8" />
8   - <span style="color: #0170cc; cursor: pointer" class="ml-1" @click="handleDelete(index)"
  8 + <span style="color: #0170cc; cursor: pointer" class="ml-1" @click="handleDelete"
9 9 >删除</span
10 10 >
11 11 </div>
... ... @@ -14,34 +14,26 @@
14 14 </div>
15 15 </template>
16 16 <script lang="ts" setup>
  17 + import { FunctionJson } from '/@/api/device/model/modelOfMatterModel';
  18 +
17 19 const emit = defineEmits(['delete', 'edit']);
18 20
  21 + type ItemRecrod = FunctionJson & { id: string };
  22 +
19 23 const props = defineProps({
20   - index: {
21   - type: Number,
22   - required: true,
23   - },
24   - title: {
25   - type: String,
26   - default: '',
27   - },
28 24 item: {
29   - type: Object,
  25 + type: Object as PropType<ItemRecrod>,
30 26 required: true,
31 27 default: () => {},
32 28 },
33 29 });
34 30
35   - const handleDelete = (index) => {
36   - emit('delete', index);
  31 + const handleDelete = () => {
  32 + emit('delete', props.item);
37 33 };
38 34
39   - const handleEdit = (item, index) => {
40   - const value = {
41   - ...item,
42   - ...{ index },
43   - };
44   - emit('edit', value);
  35 + const handleEdit = () => {
  36 + emit('edit', props.item);
45 37 };
46 38
47 39 const getFormData = () => {
... ...
1 1 import { FormSchema } from '/@/components/Table';
2 2 import { findDictItemByCode } from '/@/api/system/dict';
3 3
  4 +export enum FormField {
  5 + FUNCTION_NAME = 'functionName',
  6 + FUNCTION_TYPE = 'functionType',
  7 + FUNCTION_JSON = 'functionJson',
  8 + IDENTIFIER = 'identifier',
  9 + TYPE = 'type',
  10 + REFARK = 'remark',
  11 + MIN = 'min',
  12 + MAX = 'max',
  13 + UNIT = 'unit',
  14 + UNIT_NAME = 'unitName',
  15 + STEP = 'step',
  16 + VALUE_RANGE = 'valueRange',
  17 + BOOL_CLOSE = 'boolClose',
  18 + BOOL_OPEN = 'boolOpen',
  19 + LENGTH = 'length',
  20 + R_W_FLAG = 'rwFlag',
  21 + SPECS_LIST = 'specs',
  22 + CALL_TYPE = 'callType',
  23 + INPUT_PARAM = 'inputData',
  24 + OUTPUT_PARAM = 'outputData',
  25 + EVENT_TYPE = 'eventType',
  26 +
  27 + STRUCT = 'struct',
  28 +}
  29 +
  30 +export enum FunctionType {
  31 + PROPERTIES = 'properties',
  32 + EVENTS = 'events',
  33 + SERVICE = 'services',
  34 +}
  35 +
  36 +/**
  37 + * 新增参数 动态显示表单
  38 + */
  39 +export enum DateTypeEnum {
  40 + IS_NUMBER_INT = 'INT',
  41 + IS_NUMBER_DOUBLE = 'DOUBLE',
  42 + IS_STRING = 'TEXT',
  43 + IS_STRUCT = 'STRUCT',
  44 + IS_BOOL = 'BOOL',
  45 +}
  46 +
  47 +const isNumber = (type: string) => {
  48 + return type === DateTypeEnum.IS_NUMBER_INT || type === DateTypeEnum.IS_NUMBER_DOUBLE;
  49 +};
  50 +
  51 +const isString = (type: string) => {
  52 + return type === DateTypeEnum.IS_STRING;
  53 +};
  54 +const isStruct = (type: string) => {
  55 + return type === DateTypeEnum.IS_STRUCT;
  56 +};
  57 +
  58 +const isBool = (type: string) => {
  59 + return type === DateTypeEnum.IS_BOOL;
  60 +};
  61 +
4 62 export const defaultTslContent = {
5 63 schema: 'https://iotx-tsl.oss-ap-southeast-1.aliyuncs.com/schema.json',
6 64 profile: {
... ... @@ -24,180 +82,9 @@ export const defaultTslContent = {
24 82 ],
25 83 };
26 84
27   -export const attrSchemas: FormSchema[] = [
28   - {
29   - field: 'name',
30   - label: '功能名称',
31   - required: true,
32   - component: 'Input',
33   - colProps: {
34   - span: 18,
35   - },
36   - componentProps: {
37   - maxLength: 255,
38   - placeholder: '请输入功能名称',
39   - },
40   - },
41   - {
42   - field: 'identifier',
43   - label: '标识符',
44   - required: true,
45   - component: 'Input',
46   - colProps: {
47   - span: 18,
48   - },
49   - componentProps: {
50   - maxLength: 255,
51   - placeholder: '请输入标识符',
52   - },
53   - },
54   - {
55   - field: 'dataType',
56   - label: '数据类型',
57   - required: true,
58   - component: 'ApiSelect',
59   - colProps: {
60   - span: 9,
61   - },
62   - defaultValue: 'INT',
63   - componentProps: {
64   - placeholder: '请选择数据类型',
65   - api: findDictItemByCode,
66   - params: {
67   - dictCode: 'data_type',
68   - },
69   - labelField: 'itemText',
70   - valueField: 'itemValue',
71   - },
72   - },
73   - {
74   - field: 'valueRange',
75   - label: '取值范围',
76   - component: 'CustomeMinMaxInput',
77   - colProps: {
78   - span: 18,
79   - },
80   - ifShow: ({ values }) => isNumber(values.dataType),
81   - },
82   - {
83   - field: 'step',
84   - label: '步长',
85   - component: 'Input',
86   - colProps: {
87   - span: 18,
88   - },
89   - componentProps: {
90   - maxLength: 255,
91   - placeholder: '请输入步长',
92   - },
93   - ifShow: ({ values }) => isNumber(values.dataType),
94   - },
95   - {
96   - field: 'unit',
97   - label: '单位',
98   - component: 'ApiSelect',
99   - colProps: {
100   - span: 9,
101   - },
102   - componentProps: {
103   - placeholder: '请选择单位',
104   - api: findDictItemByCode,
105   - params: {
106   - dictCode: 'attribute_unit',
107   - },
108   - labelField: 'itemText',
109   - valueField: 'itemValue',
110   - },
111   - ifShow: ({ values }) => isNumber(values.dataType),
112   - },
113   - {
114   - field: 'boolClose',
115   - component: 'Input',
116   - required: true,
117   - label: '0 -',
118   - colProps: {
119   - span: 18,
120   - },
121   - componentProps: {
122   - placeholder: '如:关',
123   - },
124   - ifShow: ({ values }) => isBool(values.dataType),
125   - },
126   - {
127   - field: 'boolOpen',
128   - component: 'Input',
129   - required: true,
130   - label: '1 -',
131   - colProps: {
132   - span: 18,
133   - },
134   - componentProps: {
135   - placeholder: '如:开',
136   - },
137   - ifShow: ({ values }) => isBool(values.dataType),
138   - },
139   - {
140   - field: 'length',
141   - component: 'Input',
142   - required: true,
143   - label: '数据长度',
144   - defaultValue: '10240',
145   - colProps: {
146   - span: 8,
147   - },
148   - componentProps: {
149   - placeholder: '请输入数据长度',
150   - },
151   - renderComponentContent: () => {
152   - return {
153   - suffix: () => '字节',
154   - };
155   - },
156   - ifShow: ({ values }) => isString(values.dataType),
157   - },
158   - {
159   - field: 'rwFlag',
160   - component: 'ApiRadioGroup',
161   - label: '读写类型',
162   - required: true,
163   - colProps: {
164   - span: 24,
165   - },
166   - defaultValue: 'READ_ONLY',
167   - componentProps: {
168   - placeholder: '请选择读写类型',
169   - api: findDictItemByCode,
170   - params: {
171   - dictCode: 'read_write_type',
172   - },
173   - labelField: 'itemText',
174   - valueField: 'itemValue',
175   - },
176   - },
177   - {
178   - field: 'outputParam',
179   - label: 'JSON 对象',
180   - component: 'Input',
181   - // required: true,
182   - slot: 'outputParamSlot',
183   - colProps: { span: 24 },
184   - ifShow: ({ values }) => isStruct(values.dataType),
185   - },
186   - {
187   - label: '描述',
188   - field: 'description',
189   - component: 'InputTextArea',
190   - componentProps: {
191   - rows: 6,
192   - maxLength: 100,
193   - placeholder: '请输入描述',
194   - },
195   - },
196   -];
197   -
198 85 export const serviceSchemas: FormSchema[] = [
199 86 {
200   - field: 'serviceName',
  87 + field: FormField.FUNCTION_NAME,
201 88 label: '功能名称',
202 89 required: true,
203 90 component: 'Input',
... ... @@ -210,7 +97,7 @@ export const serviceSchemas: FormSchema[] = [
210 97 },
211 98 },
212 99 {
213   - field: 'identifier',
  100 + field: FormField.IDENTIFIER,
214 101 label: '标识符',
215 102 required: true,
216 103 component: 'Input',
... ... @@ -223,7 +110,7 @@ export const serviceSchemas: FormSchema[] = [
223 110 },
224 111 },
225 112 {
226   - field: 'callType',
  113 + field: FormField.CALL_TYPE,
227 114 component: 'ApiRadioGroup',
228 115 label: '调用方式',
229 116 required: true,
... ... @@ -242,22 +129,24 @@ export const serviceSchemas: FormSchema[] = [
242 129 },
243 130 },
244 131 {
245   - field: 'inputParam',
  132 + field: FormField.INPUT_PARAM,
246 133 label: '输入参数',
247   - component: 'Input',
248   - slot: 'inputParamSlot',
  134 + component: 'StructForm',
  135 + valueField: 'value',
  136 + changeEvent: 'update:value',
249 137 colProps: { span: 24 },
250 138 },
251 139 {
252   - field: 'outputParam',
  140 + field: FormField.OUTPUT_PARAM,
253 141 label: '输出参数',
254   - component: 'Input',
255   - slot: 'outputParamSlot',
  142 + component: 'StructForm',
  143 + valueField: 'value',
  144 + changeEvent: 'update:value',
256 145 colProps: { span: 24 },
257 146 },
258 147 {
259   - label: '描述',
260   - field: 'description',
  148 + field: FormField.REFARK,
  149 + label: '备注',
261 150 component: 'InputTextArea',
262 151 componentProps: {
263 152 rows: 6,
... ... @@ -269,7 +158,7 @@ export const serviceSchemas: FormSchema[] = [
269 158
270 159 export const eventSchemas: FormSchema[] = [
271 160 {
272   - field: 'eventName',
  161 + field: FormField.FUNCTION_NAME,
273 162 label: '功能名称',
274 163 required: true,
275 164 component: 'Input',
... ... @@ -282,7 +171,7 @@ export const eventSchemas: FormSchema[] = [
282 171 },
283 172 },
284 173 {
285   - field: 'identifier',
  174 + field: FormField.IDENTIFIER,
286 175 label: '标识符',
287 176 required: true,
288 177 component: 'Input',
... ... @@ -295,7 +184,7 @@ export const eventSchemas: FormSchema[] = [
295 184 },
296 185 },
297 186 {
298   - field: 'eventType',
  187 + field: FormField.EVENT_TYPE,
299 188 component: 'ApiRadioGroup',
300 189 label: '事件类型',
301 190 required: true,
... ... @@ -314,15 +203,16 @@ export const eventSchemas: FormSchema[] = [
314 203 },
315 204 },
316 205 {
317   - field: 'outputParam',
  206 + field: FormField.OUTPUT_PARAM,
318 207 label: '输出参数',
319   - component: 'Input',
320   - slot: 'outputParamSlot',
  208 + component: 'StructForm',
  209 + valueField: 'value',
  210 + changeEvent: 'update:value',
321 211 colProps: { span: 24 },
322 212 },
323 213 {
324   - label: '描述',
325   - field: 'description',
  214 + field: FormField.REFARK,
  215 + label: '备注',
326 216 component: 'InputTextArea',
327 217 componentProps: {
328 218 rows: 6,
... ... @@ -332,34 +222,9 @@ export const eventSchemas: FormSchema[] = [
332 222 },
333 223 ];
334 224
335   -/**
336   - * 新增参数 动态显示表单
337   - */
338   -enum DateTypeEnum {
339   - IS_NUMBER_INT = 'INT',
340   - IS_NUMBER_DOUBLE = 'DOUBLE',
341   - IS_STRING = 'TEXT',
342   - IS_STRUCT = 'STRUCT',
343   - IS_BOOL = 'BOOL',
344   -}
345   -const isNumber = (type: string) => {
346   - return type === DateTypeEnum.IS_NUMBER_INT || type === DateTypeEnum.IS_NUMBER_DOUBLE;
347   -};
348   -
349   -const isString = (type: string) => {
350   - return type === DateTypeEnum.IS_STRING;
351   -};
352   -const isStruct = (type: string) => {
353   - return type === DateTypeEnum.IS_STRUCT;
354   -};
355   -
356   -const isBool = (type: string) => {
357   - return type === DateTypeEnum.IS_BOOL;
358   -};
359   -
360 225 export const addParamsSchemas: FormSchema[] = [
361 226 {
362   - field: 'name',
  227 + field: FormField.FUNCTION_NAME,
363 228 label: '参数名称',
364 229 required: true,
365 230 component: 'Input',
... ... @@ -372,7 +237,7 @@ export const addParamsSchemas: FormSchema[] = [
372 237 },
373 238 },
374 239 {
375   - field: 'identifier',
  240 + field: FormField.IDENTIFIER,
376 241 label: '标识符',
377 242 required: true,
378 243 component: 'Input',
... ... @@ -385,7 +250,7 @@ export const addParamsSchemas: FormSchema[] = [
385 250 },
386 251 },
387 252 {
388   - field: 'dataType',
  253 + field: FormField.TYPE,
389 254 label: '数据类型',
390 255 required: true,
391 256 component: 'ApiSelect',
... ... @@ -404,17 +269,16 @@ export const addParamsSchemas: FormSchema[] = [
404 269 },
405 270 },
406 271 {
407   - field: 'structSlot',
  272 + field: FormField.STRUCT,
408 273 label: 'JSON 对象',
409 274 component: 'Input',
410   - slot: 'structSlot',
411 275 colProps: {
412 276 span: 23,
413 277 },
414   - ifShow: ({ values }) => isStruct(values.dataType),
  278 + ifShow: ({ values }) => isStruct(values[FormField.TYPE]),
415 279 },
416 280 {
417   - field: 'boolClose',
  281 + field: FormField.BOOL_CLOSE,
418 282 component: 'Input',
419 283 required: true,
420 284 label: '0 -',
... ... @@ -424,10 +288,10 @@ export const addParamsSchemas: FormSchema[] = [
424 288 componentProps: {
425 289 placeholder: '如:关',
426 290 },
427   - ifShow: ({ values }) => isBool(values.dataType),
  291 + ifShow: ({ values }) => isBool(values[FormField.TYPE]),
428 292 },
429 293 {
430   - field: 'boolOpen',
  294 + field: FormField.BOOL_OPEN,
431 295 component: 'Input',
432 296 required: true,
433 297 label: '1 -',
... ... @@ -437,10 +301,10 @@ export const addParamsSchemas: FormSchema[] = [
437 301 componentProps: {
438 302 placeholder: '如:开',
439 303 },
440   - ifShow: ({ values }) => isBool(values.dataType),
  304 + ifShow: ({ values }) => isBool(values[FormField.TYPE]),
441 305 },
442 306 {
443   - field: 'length',
  307 + field: FormField.LENGTH,
444 308 component: 'Input',
445 309 required: true,
446 310 label: '数据长度',
... ... @@ -456,19 +320,19 @@ export const addParamsSchemas: FormSchema[] = [
456 320 suffix: () => '字节',
457 321 };
458 322 },
459   - ifShow: ({ values }) => isString(values.dataType),
  323 + ifShow: ({ values }) => isString(values[FormField.TYPE]),
460 324 },
461 325 {
462   - field: 'valueRange',
  326 + field: FormField.VALUE_RANGE,
463 327 label: '取值范围',
464   - component: 'CustomeMinMaxInput',
  328 + component: 'CustomMinMaxInput',
465 329 colProps: {
466 330 span: 23,
467 331 },
468   - ifShow: ({ values }) => isNumber(values.dataType),
  332 + ifShow: ({ values }) => isNumber(values[FormField.TYPE]),
469 333 },
470 334 {
471   - field: 'step',
  335 + field: FormField.STEP,
472 336 label: '步长',
473 337 component: 'Input',
474 338 colProps: {
... ... @@ -478,10 +342,10 @@ export const addParamsSchemas: FormSchema[] = [
478 342 maxLength: 255,
479 343 placeholder: '请输入步长',
480 344 },
481   - ifShow: ({ values }) => isNumber(values.dataType),
  345 + ifShow: ({ values }) => isNumber(values[FormField.TYPE]),
482 346 },
483 347 {
484   - field: 'unit',
  348 + field: FormField.UNIT,
485 349 label: '单位',
486 350 component: 'ApiSelect',
487 351 colProps: {
... ... @@ -496,6 +360,6 @@ export const addParamsSchemas: FormSchema[] = [
496 360 labelField: 'itemText',
497 361 valueField: 'itemValue',
498 362 },
499   - ifShow: ({ values }) => isNumber(values.dataType),
  363 + ifShow: ({ values }) => isNumber(values[FormField.TYPE]),
500 364 },
501 365 ];
... ...
  1 +import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  2 +
  3 +export enum OpenModelMode {
  4 + UPDATE = 'update',
  5 + CREATE = 'create',
  6 + VIEW = 'view',
  7 +}
  8 +
  9 +export interface OpenModelOfMatterModelParams {
  10 + mode: OpenModelMode;
  11 + record: ModelOfMatterParams;
  12 +}
  13 +
1 14 export interface IAllData {
2 15 properties: string[];
3 16 events: string[];
... ...
... ... @@ -23,31 +23,40 @@
23 23
24 24 const loading = ref(false);
25 25
  26 + const isUpdate = ref(false);
  27 +
26 28 const otaRecord = ref<OtaRecordDatum>({} as unknown as OtaRecordDatum);
27 29
28 30 const deviceProfileInfo = ref<DeviceProfileRecord>({} as unknown as DeviceProfileRecord);
29 31
30 32 const { createConfirm, createMessage } = useMessage();
31 33
32   - const [registerForm, { setFieldsValue, getFieldsValue }] = useForm({
  34 + const [registerForm, { setFieldsValue, getFieldsValue, updateSchema }] = useForm({
33 35 schemas: formSchema,
34 36 showActionButtonGroup: false,
35 37 disabled: true,
36 38 });
37 39
38   - const [register, { closeDrawer, changeLoading }] = useDrawerInner(async (id: string) => {
39   - try {
40   - const record = await getOtaPackageInfo(id);
41   - const deviceInfo = await getDeviceProfileInfoById(record.deviceProfileId.id);
42   - setFieldsValue({
43   - ...record,
44   - [PackageField.DESCRIPTION]: record.additionalInfo.description,
45   - [PackageField.DEVICE_PROFILE_INFO]: deviceInfo.name,
46   - });
47   - deviceProfileInfo.value = deviceInfo;
48   - otaRecord.value = record;
49   - } catch (error) {}
50   - });
  40 + const [register, { closeDrawer, changeLoading }] = useDrawerInner(
  41 + async (params: { id: string; isUpdate: boolean }) => {
  42 + try {
  43 + const { id, isUpdate: flag } = params;
  44 + isUpdate.value = flag;
  45 + const record = await getOtaPackageInfo(id);
  46 + const deviceInfo = await getDeviceProfileInfoById(record.deviceProfileId.id);
  47 + setFieldsValue({
  48 + ...record,
  49 + [PackageField.DESCRIPTION]: record.additionalInfo.description,
  50 + [PackageField.DEVICE_PROFILE_INFO]: deviceInfo.name,
  51 + });
  52 + deviceProfileInfo.value = deviceInfo;
  53 + otaRecord.value = record;
  54 + if (unref(isUpdate)) {
  55 + updateSchema({ field: PackageField.DESCRIPTION, dynamicDisabled: false });
  56 + }
  57 + } catch (error) {}
  58 + }
  59 + );
51 60
52 61 // const [registerTBDrawer, TBDrawerMethod] = useDrawer();
53 62
... ... @@ -146,7 +155,9 @@
146 155 >
147 156 <Button class="mr-2" @click="closeDrawer">取消</Button>
148 157 <Authority :value="OtaPermissionKey.UPDATE">
149   - <Button type="primary" :loading="loading" @click="handleSubmit">保存</Button>
  158 + <Button v-if="isUpdate" type="primary" :loading="loading" @click="handleSubmit">
  159 + 保存
  160 + </Button>
150 161 </Authority>
151 162 </div>
152 163 </template>
... ...
... ... @@ -13,6 +13,7 @@ export enum OtaPermissionKey {
13 13 UPDATE = 'api:operation:ota:update',
14 14 DELETE = 'api:operation:ota:delete',
15 15 DOWNLOAD = 'api:operation:ota:download',
  16 + DETAIL = 'api:operation:ota:detail',
16 17 }
17 18
18 19 export const columns: BasicColumn[] = [
... ... @@ -22,7 +23,7 @@ export const columns: BasicColumn[] = [
22 23 format(text) {
23 24 return dateUtil(text).format(DEFAULT_DATE_FORMAT);
24 25 },
25   - width: 120,
  26 + width: 140,
26 27 },
27 28 {
28 29 title: '标题',
... ... @@ -80,6 +81,7 @@ export const columns: BasicColumn[] = [
80 81 dataIndex: 'action',
81 82 flag: 'ACTION',
82 83 fixed: 'right',
  84 + width: 200,
83 85 slots: {
84 86 customRender: 'action',
85 87 },
... ...
... ... @@ -113,6 +113,5 @@ export const formSchema: FormSchema[] = [
113 113 field: PackageField.DESCRIPTION,
114 114 label: '描述',
115 115 component: 'Input',
116   - dynamicDisabled: false,
117 116 },
118 117 ];
... ...
... ... @@ -22,6 +22,8 @@
22 22 const data = await getOtaPackagesList({
23 23 ...params,
24 24 page: params.page - 1,
  25 + sortOrder: 'DESC',
  26 + sortProperty: 'createdTime',
25 27 textSearch: params.title,
26 28 });
27 29 return { ...data, page: params.page };
... ... @@ -57,8 +59,8 @@
57 59 openModal(true, { isUpdate: false } as ModalPassRecord);
58 60 };
59 61
60   - const handleOpenDetailDrawer = (record: OtaRecordDatum) => {
61   - openDrawer(true, record.id.id);
  62 + const handleOpenDetailDrawer = (record: OtaRecordDatum, isUpdate: boolean) => {
  63 + openDrawer(true, { id: record.id.id, isUpdate });
62 64 };
63 65
64 66 const downloadFile = async (record: OtaRecordDatum) => {
... ... @@ -117,6 +119,20 @@
117 119 @click.stop
118 120 :actions="[
119 121 {
  122 + label: '详情',
  123 + icon: 'ant-design:eye-outlined',
  124 + auth: OtaPermissionKey.DETAIL,
  125 + onClick: handleOpenDetailDrawer.bind(null, record, false),
  126 + },
  127 + {
  128 + label: '编辑',
  129 + icon: 'clarity:note-edit-line',
  130 + auth: OtaPermissionKey.UPDATE,
  131 + onClick: handleOpenDetailDrawer.bind(null, record, true),
  132 + },
  133 + ]"
  134 + :drop-down-actions="[
  135 + {
120 136 label: '下载',
121 137 icon: 'ant-design:download-outlined',
122 138 auth: OtaPermissionKey.DOWNLOAD,
... ...