Commit b564d32e15157592a3786476ba77c621d7f54a63

Authored by fengtao
2 parents a5b765ee a67c4f60

Merge branch 'main' into ft_local_dev

Showing 37 changed files with 1410 additions and 946 deletions
@@ -14,7 +14,7 @@ VITE_PUBLIC_PATH = / @@ -14,7 +14,7 @@ VITE_PUBLIC_PATH = /
14 # VITE_PROXY = [["/api","http://101.133.234.90:8080/api"]] 14 # VITE_PROXY = [["/api","http://101.133.234.90:8080/api"]]
15 # 线上测试环境 15 # 线上测试环境
16 # VITE_PROXY = [["/api","http://localhost:8080/api"],["/thingskit-drawio","http://localhost:3000/"]] 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 # VITE_PROXY = [["/api","http://121.37.251.8:8080/api"],["/thingskit-drawio","http://localhost:3000/"]] 18 # VITE_PROXY = [["/api","http://121.37.251.8:8080/api"],["/thingskit-drawio","http://localhost:3000/"]]
19 # VITE_PROXY = [["/api","http://192.168.10.136:8080/api"],["/thingskit-drawio","http://192.168.10.136:8080/api"]] 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,7 +43,7 @@ VITE_GLOB_CONFIGURATION = /thingskit-drawio
43 VITE_CONTENT_SECURITY_POLICY = false 43 VITE_CONTENT_SECURITY_POLICY = false
44 44
45 # Alarm Notify Polling Interval Time 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 # Alarm Notify Auto Close Time Unit is Second 48 # Alarm Notify Auto Close Time Unit is Second
49 VITE_ALARM_NOTIFY_DURATION = 5 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,3 +48,40 @@ export interface DeviceProfileModel {
48 export type ChildDeviceParams = BasicPageParams & { 48 export type ChildDeviceParams = BasicPageParams & {
49 formId: string; 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,6 +5,8 @@ export interface GetOtaPackagesParams {
5 page: number; 5 page: number;
6 textSearch?: string; 6 textSearch?: string;
7 title?: string; 7 title?: string;
  8 + sortOrder?: string;
  9 + sortProperty?: string;
8 } 10 }
9 11
10 export interface CreateOtaPackagesParams { 12 export interface CreateOtaPackagesParams {
@@ -20,7 +20,7 @@ enum SysDictApi { @@ -20,7 +20,7 @@ enum SysDictApi {
20 * @param code 20 * @param code
21 */ 21 */
22 export const findDictItemByCode = (params?: DictCodeParams) => { 22 export const findDictItemByCode = (params?: DictCodeParams) => {
23 - return defHttp.post<SysDictItemResult>({ 23 + return defHttp.post<SysDictItemResult[]>({
24 url: SysDictApi.CONFIG_ITEM_URL + '/find', 24 url: SysDictApi.CONFIG_ITEM_URL + '/find',
25 params, 25 params,
26 }); 26 });
@@ -12,6 +12,8 @@ export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; @@ -12,6 +12,8 @@ export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
12 export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; 12 export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
13 export { default as ApiUpload } from './src/components/ApiUpload.vue'; 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 export { 18 export {
17 JEasyCron, 19 JEasyCron,
@@ -35,7 +35,8 @@ import ColorPicker from './components/ColorPicker.vue'; @@ -35,7 +35,8 @@ import ColorPicker from './components/ColorPicker.vue';
35 import IconDrawer from './components/IconDrawer.vue'; 35 import IconDrawer from './components/IconDrawer.vue';
36 import ApiUpload from './components/ApiUpload.vue'; 36 import ApiUpload from './components/ApiUpload.vue';
37 import ApiSearchSelect from './components/ApiSearchSelect.vue'; 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 const componentMap = new Map<ComponentType, Component>(); 41 const componentMap = new Map<ComponentType, Component>();
41 42
@@ -78,7 +79,8 @@ componentMap.set('ColorPicker', ColorPicker); @@ -78,7 +79,8 @@ componentMap.set('ColorPicker', ColorPicker);
78 componentMap.set('IconDrawer', IconDrawer); 79 componentMap.set('IconDrawer', IconDrawer);
79 componentMap.set('ApiUpload', ApiUpload); 80 componentMap.set('ApiUpload', ApiUpload);
80 componentMap.set('ApiSearchSelect', ApiSearchSelect); 81 componentMap.set('ApiSearchSelect', ApiSearchSelect);
81 -componentMap.set('CustomeMinMaxInput', CustomeMinMaxInput); 82 +componentMap.set('CustomMinMaxInput', CustomMinMaxInput);
  83 +componentMap.set('StructForm', StructForm);
82 84
83 export function add(compName: ComponentType, component: Component) { 85 export function add(compName: ComponentType, component: Component) {
84 componentMap.set(compName, component); 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,9 +112,10 @@ export type ComponentType =
112 | 'Render' 112 | 'Render'
113 | 'Slider' 113 | 'Slider'
114 | 'JAddInput' 114 | 'JAddInput'
115 - | 'CustomeMinMaxInput' 115 + | 'CustomMinMaxInput'
116 | 'Rate' 116 | 'Rate'
117 | 'ColorPicker' 117 | 'ColorPicker'
118 | 'IconDrawer' 118 | 'IconDrawer'
119 | 'ApiUpload' 119 | 'ApiUpload'
120 - | 'ApiSearchSelect'; 120 + | 'ApiSearchSelect'
  121 + | 'StructForm';
@@ -3,8 +3,9 @@ @@ -3,8 +3,9 @@
3 <div 3 <div
4 class="cursor-pointer flex py-4 fold-icon absolute rounded svg:fill-gray-400 hover:bg-gray-200" 4 class="cursor-pointer flex py-4 fold-icon absolute rounded svg:fill-gray-400 hover:bg-gray-200"
5 :class="foldFlag ? '' : '-right-4'" 5 :class="foldFlag ? '' : '-right-4'"
  6 + @click="handleFold"
6 > 7 >
7 - <div @click="handleFold"> 8 + <div>
8 <CaretRightOutlined 9 <CaretRightOutlined
9 :class="[foldFlag ? '' : 'rotate-180']" 10 :class="[foldFlag ? '' : 'rotate-180']"
10 class="transform fill-gray-100" 11 class="transform fill-gray-100"
@@ -16,12 +16,15 @@ @@ -16,12 +16,15 @@
16 @open-gateway-device="handleOpenGatewayDevice" 16 @open-gateway-device="handleOpenGatewayDevice"
17 /> 17 />
18 </TabPane> 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 <RealTimeData :deviceDetail="deviceDetail" /> 23 <RealTimeData :deviceDetail="deviceDetail" />
21 </TabPane> 24 </TabPane>
22 <TabPane key="7" tab="历史数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'"> 25 <TabPane key="7" tab="历史数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'">
23 <HistoryData :deviceDetail="deviceDetail" /> 26 <HistoryData :deviceDetail="deviceDetail" />
24 - </TabPane> 27 + </TabPane> -->
25 <TabPane key="5" tab="命令下发" v-if="deviceDetail?.deviceType !== 'SENSOR'"> 28 <TabPane key="5" tab="命令下发" v-if="deviceDetail?.deviceType !== 'SENSOR'">
26 <CommandIssuance :deviceDetail="deviceDetail" /> 29 <CommandIssuance :deviceDetail="deviceDetail" />
27 </TabPane> 30 </TabPane>
@@ -50,13 +53,14 @@ @@ -50,13 +53,14 @@
50 53
51 import { Tabs } from 'ant-design-vue'; 54 import { Tabs } from 'ant-design-vue';
52 import Detail from '../tabs/Detail.vue'; 55 import Detail from '../tabs/Detail.vue';
53 - import RealTimeData from '../tabs/RealTimeData.vue'; 56 + // import RealTimeData from '../tabs/RealTimeData.vue';
54 import Alarm from '../tabs/Alarm.vue'; 57 import Alarm from '../tabs/Alarm.vue';
55 import ChildDevice from '../tabs/ChildDevice.vue'; 58 import ChildDevice from '../tabs/ChildDevice.vue';
56 import TBoxDetail from '../tabs/TBoxDetail.vue'; 59 import TBoxDetail from '../tabs/TBoxDetail.vue';
57 import CommandIssuance from '../tabs/CommandIssuance.vue'; 60 import CommandIssuance from '../tabs/CommandIssuance.vue';
58 import { getDeviceDetail } from '/@/api/device/deviceManager'; 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 export default defineComponent({ 64 export default defineComponent({
61 name: 'DeviceModal', 65 name: 'DeviceModal',
62 components: { 66 components: {
@@ -64,12 +68,13 @@ @@ -64,12 +68,13 @@
64 Tabs, 68 Tabs,
65 TabPane: Tabs.TabPane, 69 TabPane: Tabs.TabPane,
66 Detail, 70 Detail,
67 - RealTimeData, 71 + // RealTimeData,
68 Alarm, 72 Alarm,
69 ChildDevice, 73 ChildDevice,
70 CommandIssuance, 74 CommandIssuance,
71 TBoxDetail, 75 TBoxDetail,
72 - HistoryData, 76 + // HistoryData,
  77 + ModelOfMatter,
73 }, 78 },
74 emits: ['reload', 'register', 'openTbDeviceDetail', 'openGatewayDeviceDetail'], 79 emits: ['reload', 'register', 'openTbDeviceDetail', 'openGatewayDeviceDetail'],
75 setup(_props, { emit }) { 80 setup(_props, { emit }) {
@@ -23,6 +23,7 @@ @@ -23,6 +23,7 @@
23 23
24 const props = defineProps<{ 24 const props = defineProps<{
25 deviceDetail: DeviceDetail; 25 deviceDetail: DeviceDetail;
  26 + attr?: string;
26 }>(); 27 }>();
27 28
28 const chartRef = ref(); 29 const chartRef = ref();
@@ -119,6 +120,10 @@ @@ -119,6 +120,10 @@
119 const { tbDeviceId } = props.deviceDetail || {}; 120 const { tbDeviceId } = props.deviceDetail || {};
120 try { 121 try {
121 deviceAttrs.value = (await getDeviceDataKeys(tbDeviceId)) || []; 122 deviceAttrs.value = (await getDeviceDataKeys(tbDeviceId)) || [];
  123 +
  124 + if (props.attr) {
  125 + method.setFieldsValue({ keys: props.attr });
  126 + }
122 } catch (error) {} 127 } catch (error) {}
123 }; 128 };
124 129
@@ -139,9 +144,11 @@ @@ -139,9 +144,11 @@
139 144
140 if (!hasDeviceAttr()) return; 145 if (!hasDeviceAttr()) return;
141 146
  147 + const keys = props.attr ? props.attr : unref(deviceAttrs).join();
  148 +
142 const res = await getDeviceHistoryInfo({ 149 const res = await getDeviceHistoryInfo({
143 entityId: props.deviceDetail.tbDeviceId, 150 entityId: props.deviceDetail.tbDeviceId,
144 - keys: unref(deviceAttrs).join(), 151 + keys,
145 startTs: Date.now() - 1 * 24 * 60 * 60 * 1000, 152 startTs: Date.now() - 1 * 24 * 60 * 60 * 1000,
146 endTs: Date.now(), 153 endTs: Date.now(),
147 agg: AggregateDataEnum.NONE, 154 agg: AggregateDataEnum.NONE,
@@ -169,12 +176,12 @@ @@ -169,12 +176,12 @@
169 </script> 176 </script>
170 177
171 <template> 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 <TimePeriodForm @register="register" /> 181 <TimePeriodForm @register="register" />
175 </section> 182 </section>
176 <section class="bg-white p-3"> 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 <Spin :spinning="loading" :absolute="true" /> 185 <Spin :spinning="loading" :absolute="true" />
179 </div> 186 </div>
180 <Empty v-show="!isNull" /> 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 <template> 1 <template>
2 <BasicDrawer v-bind="$attrs" title="产品详情" @register="register" width="60%"> 2 <BasicDrawer v-bind="$attrs" title="产品详情" @register="register" width="60%">
3 <Tabs :animated="true" v-model:activeKey="activeKey" @change="handlePanelChange"> 3 <Tabs :animated="true" v-model:activeKey="activeKey" @change="handlePanelChange">
4 - <TabPane forceRender key="product" tab="产品"> 4 + <TabPane key="product" tab="产品">
5 <div class="relative"> 5 <div class="relative">
6 <DeviceConfigurationStep :ifShowBtn="false" ref="DevConStRef" /> 6 <DeviceConfigurationStep :ifShowBtn="false" ref="DevConStRef" />
7 <div class="absolute w-full h-full top-0 cursor-not-allowed"></div> 7 <div class="absolute w-full h-full top-0 cursor-not-allowed"></div>
8 </div> 8 </div>
9 </TabPane> 9 </TabPane>
10 - <TabPane forceRender key="transport" tab="传输配置"> 10 + <TabPane key="transport" tab="传输配置">
11 <div class="relative"> 11 <div class="relative">
12 <TransportConfigurationStep :ifShowBtn="false" ref="TransConStRef" /> 12 <TransportConfigurationStep :ifShowBtn="false" ref="TransConStRef" />
13 <div class="absolute w-full h-full top-0 cursor-not-allowed"></div> 13 <div class="absolute w-full h-full top-0 cursor-not-allowed"></div>
14 </div> 14 </div>
15 </TabPane> 15 </TabPane>
16 - <TabPane forceRender key="modelOfMatter" tab="物模型管理">  
17 - <PhysicalModelManagementStep /> 16 + <TabPane key="modelOfMatter" tab="物模型管理">
  17 + <PhysicalModelManagementStep :record="record" />
18 </TabPane> 18 </TabPane>
19 </Tabs> 19 </Tabs>
20 </BasicDrawer> 20 </BasicDrawer>
@@ -27,14 +27,15 @@ @@ -27,14 +27,15 @@
27 import PhysicalModelManagementStep from './step/PhysicalModelManagementStep.vue'; 27 import PhysicalModelManagementStep from './step/PhysicalModelManagementStep.vue';
28 import { ref, unref } from 'vue'; 28 import { ref, unref } from 'vue';
29 import { deviceConfigGetDetail } from '/@/api/device/deviceConfigApi'; 29 import { deviceConfigGetDetail } from '/@/api/device/deviceConfigApi';
  30 + import { DeviceRecord } from '/@/api/device/model/deviceModel';
30 31
31 defineEmits(['register']); 32 defineEmits(['register']);
32 33
33 type ActiveKey = 'product' | 'transport' | 'modelOfMatter'; 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 const DevConStRef = ref<InstanceType<typeof DeviceConfigurationStep>>(); 40 const DevConStRef = ref<InstanceType<typeof DeviceConfigurationStep>>();
40 const TransConStRef = ref<InstanceType<typeof TransportConfigurationStep>>(); 41 const TransConStRef = ref<InstanceType<typeof TransportConfigurationStep>>();
@@ -47,7 +48,7 @@ @@ -47,7 +48,7 @@
47 unref(TransConStRef)?.setFormData(res); 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 activeKey.value = 'product'; 52 activeKey.value = 'product';
52 record.value = await deviceConfigGetDetail(data.record.id); 53 record.value = await deviceConfigGetDetail(data.record.id);
53 setDeviceConfFormData(unref(record)); 54 setDeviceConfFormData(unref(record));
@@ -3,10 +3,9 @@ import { FormSchema } from '/@/components/Table'; @@ -3,10 +3,9 @@ import { FormSchema } from '/@/components/Table';
3 import { findDictItemByCode } from '/@/api/system/dict'; 3 import { findDictItemByCode } from '/@/api/system/dict';
4 import { MessageEnum } from '/@/enums/messageEnum'; 4 import { MessageEnum } from '/@/enums/messageEnum';
5 import { numberRule } from '/@/utils/rules'; 5 import { numberRule } from '/@/utils/rules';
6 -import { Tag } from 'ant-design-vue';  
7 -import { h } from 'vue';  
8 6
9 import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi'; 7 import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi';
  8 +import { FormField, FunctionType } from './step/cpns/physical/cpns/config';
10 9
11 export const steps = [ 10 export const steps = [
12 { 11 {
@@ -19,50 +18,37 @@ export const steps = [ @@ -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 export const physicalColumn: BasicColumn[] = [ 27 export const physicalColumn: BasicColumn[] = [
23 { 28 {
24 title: '功能类型', 29 title: '功能类型',
25 - dataIndex: 'dType', 30 + dataIndex: FormField.FUNCTION_TYPE,
26 width: 90, 31 width: 90,
  32 + format: (text: FunctionType) => {
  33 + return formatFunctionType[text];
  34 + },
27 }, 35 },
28 { 36 {
29 title: '功能名称', 37 title: '功能名称',
30 - dataIndex: 'name', 38 + dataIndex: FormField.FUNCTION_NAME,
31 width: 90, 39 width: 90,
32 }, 40 },
33 { 41 {
34 title: '标识符', 42 title: '标识符',
35 - dataIndex: 'type', 43 + dataIndex: FormField.IDENTIFIER,
36 width: 90, 44 width: 90,
37 }, 45 },
38 { 46 {
39 title: '数据类型', 47 title: '数据类型',
40 - dataIndex: 'transportType', 48 + dataIndex: 'functionJson.type',
41 width: 100, 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 title: '创建时间', 52 title: '创建时间',
67 dataIndex: 'createTime', 53 dataIndex: 'createTime',
68 width: 150, 54 width: 150,
@@ -411,130 +397,3 @@ export const formSchema: FormSchema[] = [ @@ -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,28 +8,29 @@
8 <template #toolbar> 8 <template #toolbar>
9 <div class="flex-auto"> 9 <div class="flex-auto">
10 <div class="mb-2"> 10 <div class="mb-2">
11 - <a-alert v-if="!isShowBtn" type="info" show-icon> 11 + <Alert type="info" show-icon>
12 <template #message> 12 <template #message>
13 - <span>  
14 - 当前展示的是已发布到线上的功能定义,如需修改,请点击 13 + <span v-if="!isShowBtn">
  14 + 当前展示的是已发布到线上的功能定义,如需修改,请点击
15 <span 15 <span
16 @click="handleEditPhysicalModel" 16 @click="handleEditPhysicalModel"
17 class="cursor-pointer text-blue-400" 17 class="cursor-pointer text-blue-400"
18 size="small" 18 size="small"
19 > 19 >
20 - “编辑物模型” 20 + "编辑物模型"
21 </span> 21 </span>
22 </span> 22 </span>
  23 + <span v-if="isShowBtn"> 您正在编辑的是草稿,需点击发布后,物模型才会正式生效. </span>
23 </template> 24 </template>
24 - </a-alert> 25 + </Alert>
25 </div> 26 </div>
26 <div class="flex justify-between items-end"> 27 <div class="flex justify-between items-end">
27 <div class="flex gap-2"> 28 <div class="flex gap-2">
28 <Authority value=""> 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 </Authority> 34 </Authority>
34 </div> 35 </div>
35 <div class="flex gap-2"> 36 <div class="flex gap-2">
@@ -40,25 +41,25 @@ @@ -40,25 +41,25 @@
40 cancel-text="取消" 41 cancel-text="取消"
41 @confirm="handleEmit" 42 @confirm="handleEmit"
42 > 43 >
43 - <a-button v-if="isShowBtn" type="primary"> 发布上线 </a-button> 44 + <Button v-if="isShowBtn" type="primary"> 发布上线 </Button>
44 </Popconfirm> 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 <Popconfirm 49 <Popconfirm
49 title="您确定要批量删除数据" 50 title="您确定要批量删除数据"
50 ok-text="确定" 51 ok-text="确定"
51 cancel-text="取消" 52 cancel-text="取消"
52 @confirm="handleDeleteOrBatchDelete(null)" 53 @confirm="handleDeleteOrBatchDelete(null)"
53 > 54 >
54 - <a-button 55 + <Button
55 style="display: none" 56 style="display: none"
56 type="primary" 57 type="primary"
57 color="error" 58 color="error"
58 :disabled="hasBatchDelete" 59 :disabled="hasBatchDelete"
59 > 60 >
60 批量删除 61 批量删除
61 - </a-button> 62 + </Button>
62 </Popconfirm> 63 </Popconfirm>
63 </Authority> 64 </Authority>
64 </div> 65 </div>
@@ -96,39 +97,46 @@ @@ -96,39 +97,46 @@
96 /> 97 />
97 </template> 98 </template>
98 </BasicTable> 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 </div> 106 </div>
102 </template> 107 </template>
103 <script lang="ts" setup> 108 <script lang="ts" setup>
104 - import { ref } from 'vue';  
105 import { BasicTable, useTable, TableAction } from '/@/components/Table'; 109 import { BasicTable, useTable, TableAction } from '/@/components/Table';
106 import { useModal } from '/@/components/Modal'; 110 import { useModal } from '/@/components/Modal';
107 import { physicalColumn } from '../device.profile.data'; 111 import { physicalColumn } from '../device.profile.data';
108 import { useBatchDelete } from '/@/hooks/web/useBatchDelete'; 112 import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
109 - import { deleteReportManage } from '/@/api/report/reportManager';  
110 import { Authority } from '/@/components/Authority'; 113 import { Authority } from '/@/components/Authority';
111 - // import { mockData } from '/@/api/device/deviceConfigApi';  
112 import PhysicalModelModal from './cpns/physical/PhysicalModelModal.vue'; 114 import PhysicalModelModal from './cpns/physical/PhysicalModelModal.vue';
113 import PhysicalModelTsl from './cpns/physical/PhysicalModelTsl.vue'; 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 import { useMessage } from '/@/hooks/web/useMessage'; 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 defineEmits(['register']); 123 defineEmits(['register']);
  124 +
  125 + defineProps<{
  126 + record: DeviceRecord;
  127 + }>();
  128 +
119 const { createMessage } = useMessage(); 129 const { createMessage } = useMessage();
120 const isShowBtn = ref(false); 130 const isShowBtn = ref(false);
121 const [registerModal, { openModal }] = useModal(); 131 const [registerModal, { openModal }] = useModal();
122 const [registerModalTsl, { openModal: openModalTsl }] = useModal(); 132 const [registerModalTsl, { openModal: openModalTsl }] = useModal();
123 133
124 const [registerTable, { reload, setProps }] = useTable({ 134 const [registerTable, { reload, setProps }] = useTable({
125 - // api: deviceConfigGetQuery,  
126 - api: mockData, 135 + api: getModelList,
127 columns: physicalColumn, 136 columns: physicalColumn,
128 showIndexColumn: false, 137 showIndexColumn: false,
129 clickToRowSelect: false, 138 clickToRowSelect: false,
130 useSearchForm: false, 139 useSearchForm: false,
131 - // rowKey: 'id',  
132 showTableSetting: true, 140 showTableSetting: true,
133 bordered: true, 141 bordered: true,
134 actionColumn: { 142 actionColumn: {
@@ -139,16 +147,18 @@ @@ -139,16 +147,18 @@
139 fixed: 'right', 147 fixed: 'right',
140 }, 148 },
141 }); 149 });
  150 +
142 // 刷新 151 // 刷新
143 const handleSuccess = () => { 152 const handleSuccess = () => {
144 reload(); 153 reload();
145 }; 154 };
146 155
147 const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete( 156 const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(
148 - deleteReportManage, 157 + deleteModel,
149 handleSuccess, 158 handleSuccess,
150 setProps 159 setProps
151 ); 160 );
  161 +
152 selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => { 162 selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => {
153 // Demo:status为1的选择框禁用 163 // Demo:status为1的选择框禁用
154 if (record.status === 1) { 164 if (record.status === 1) {
@@ -157,38 +167,36 @@ @@ -157,38 +167,36 @@
157 return { disabled: false }; 167 return { disabled: false };
158 } 168 }
159 }; 169 };
160 - const handleViewDetail = (record: Recordable | null) => { 170 +
  171 + const handleViewDetail = (record: ModelOfMatterParams) => {
161 if (record) { 172 if (record) {
162 openModal(true, { 173 openModal(true, {
163 - isUpdate: true,  
164 record, 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 if (record) { 182 if (record) {
173 openModal(true, { 183 openModal(true, {
174 - isUpdate: false, 184 + mode: OpenModelMode.UPDATE,
175 record, 185 record,
176 - isView: false,  
177 - isText: 'edit',  
178 - }); 186 + } as OpenModelOfMatterModelParams);
179 } else { 187 } else {
180 openModal(true, { 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 const handleOpenTsl = () => { 194 const handleOpenTsl = () => {
188 openModalTsl(true, { 195 openModalTsl(true, {
189 isUpdate: true, 196 isUpdate: true,
190 }); 197 });
191 }; 198 };
  199 +
192 const handleEditPhysicalModel = () => (isShowBtn.value = true); 200 const handleEditPhysicalModel = () => (isShowBtn.value = true);
193 201
194 const handleReturn = () => (isShowBtn.value = false); 202 const handleReturn = () => (isShowBtn.value = false);
@@ -2,69 +2,66 @@ @@ -2,69 +2,66 @@
2 <div> 2 <div>
3 <BasicModal 3 <BasicModal
4 :maskClosable="false" 4 :maskClosable="false"
  5 + destroy-on-close
5 v-bind="$attrs" 6 v-bind="$attrs"
6 width="55rem" 7 width="55rem"
7 @register="register" 8 @register="register"
8 @ok="handleSubmit" 9 @ok="handleSubmit"
9 @cancel="handleCancel" 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 <Typography> 14 <Typography>
19 <TypographyParagraph> 15 <TypographyParagraph>
20 - <blockquote style="background: #f2f2f2">{{ useBlockPhysicalContent }}</blockquote> 16 + <blockquote class="bg-gray-100">{{ blockContent }}</blockquote>
21 </TypographyParagraph> 17 </TypographyParagraph>
22 </Typography> 18 </Typography>
23 </div> 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 <TabPane 28 <TabPane
32 - :disabled="eventDisable"  
33 - forceRender  
34 - key="3"  
35 - v-show="activeKey === '3'" 29 + :key="FunctionType.EVENTS"
36 tab="事件" 30 tab="事件"
37 - >  
38 - <Events v-show="activeKey === '3'" ref="EventsRef" />  
39 - </TabPane> 31 + :disabled="$props.record.transportType === 'TCP'"
  32 + />
40 </Tabs> 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 </div> 37 </div>
42 </BasicModal> 38 </BasicModal>
43 </div> 39 </div>
44 </template> 40 </template>
45 <script lang="ts" setup> 41 <script lang="ts" setup>
46 - import { ref, unref, reactive, nextTick } from 'vue'; 42 + import { ref, unref } from 'vue';
47 import { BasicModal, useModalInner } from '/@/components/Modal'; 43 import { BasicModal, useModalInner } from '/@/components/Modal';
48 import { Tabs, TabPane, Typography, TypographyParagraph } from 'ant-design-vue'; 44 import { Tabs, TabPane, Typography, TypographyParagraph } from 'ant-design-vue';
49 import Attribute from './cpns/Attribute.vue'; 45 import Attribute from './cpns/Attribute.vue';
50 import Service from './cpns/Service.vue'; 46 import Service from './cpns/Service.vue';
51 import Events from './cpns/Events.vue'; 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 const size = ref('small'); 65 const size = ref('small');
69 66
70 const AttrRef = ref<InstanceType<typeof Attribute>>(); 67 const AttrRef = ref<InstanceType<typeof Attribute>>();
@@ -72,121 +69,87 @@ @@ -72,121 +69,87 @@
72 const ServiceRef = ref<InstanceType<typeof Service>>(); 69 const ServiceRef = ref<InstanceType<typeof Service>>();
73 70
74 const EventsRef = ref<InstanceType<typeof Events>>(); 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 const enums = { 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 const handleCancel = (flag) => { 116 const handleCancel = (flag) => {
162 AttrRef.value?.resetFormData(); 117 AttrRef.value?.resetFormData();
163 ServiceRef.value?.resetFormData(); 118 ServiceRef.value?.resetFormData();
164 EventsRef.value?.resetFormData(); 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 if (flag) { 121 if (flag) {
173 closeModal(); 122 closeModal();
174 } 123 }
175 }; 124 };
176 125
177 const handleSubmit = async () => { 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 </script> 154 </script>
192 155
1 <template> 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 </template> 3 </template>
19 <script lang="ts" setup> 4 <script lang="ts" setup>
20 - import { unref } from 'vue';  
21 import { BasicForm, useForm } from '/@/components/Form'; 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 labelWidth: 100, 16 labelWidth: 100,
40 - schemas: attrSchemas, 17 + schemas: formSchemas,
41 actionColOptions: { 18 actionColOptions: {
42 span: 14, 19 span: 14,
43 }, 20 },
@@ -46,84 +23,34 @@ @@ -46,84 +23,34 @@
46 showActionButtonGroup: false, 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 defineExpose({ 50 defineExpose({
124 - setFormData,  
125 resetFormData, 51 resetFormData,
126 getFormData, 52 getFormData,
  53 + setFormData,
127 }); 54 });
128 </script> 55 </script>
129 <style lang="less" scoped></style> 56 <style lang="less" scoped></style>
1 <template> 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 </template> 3 </template>
19 <script lang="ts" setup> 4 <script lang="ts" setup>
20 - import { unref } from 'vue';  
21 import { BasicForm, useForm } from '/@/components/Form'; 5 import { BasicForm, useForm } from '/@/components/Form';
22 import { eventSchemas } from './config'; 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 labelWidth: 100, 10 labelWidth: 100,
37 schemas: eventSchemas, 11 schemas: eventSchemas,
38 actionColOptions: { 12 actionColOptions: {
@@ -43,43 +17,18 @@ @@ -43,43 +17,18 @@
43 showActionButtonGroup: false, 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 async function getFormData() { 23 async function getFormData() {
67 - const values = await validate(); 24 + const values = (await validate()) as ModelOfMatterParams;
68 if (!values) return; 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 const resetFormData = () => { 30 const resetFormData = () => {
81 resetFields(); 31 resetFields();
82 - inputParamData.value = [];  
83 }; 32 };
84 33
85 defineExpose({ 34 defineExpose({
1 <template> 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 </template> 3 </template>
30 <script lang="ts" setup> 4 <script lang="ts" setup>
31 - import { unref } from 'vue';  
32 import { BasicForm, useForm } from '/@/components/Form'; 5 import { BasicForm, useForm } from '/@/components/Form';
33 import { serviceSchemas } from './config'; 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 labelWidth: 100, 10 labelWidth: 100,
49 schemas: serviceSchemas, 11 schemas: serviceSchemas,
50 actionColOptions: { 12 actionColOptions: {
@@ -55,57 +17,21 @@ @@ -55,57 +17,21 @@
55 showActionButtonGroup: false, 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 async function getFormData() { 24 async function getFormData() {
90 - const values = await validate(); 25 + const values = (await validate()) as ModelOfMatterParams;
91 if (!values) return; 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 const resetFormData = () => { 33 const resetFormData = () => {
106 resetFields(); 34 resetFields();
107 - inputParamData.value = [];  
108 - outputParamData.value = [];  
109 }; 35 };
110 36
111 defineExpose({ 37 defineExpose({
@@ -34,7 +34,7 @@ @@ -34,7 +34,7 @@
34 34
35 const [registerModal, { openModal }] = useModal(); 35 const [registerModal, { openModal }] = useModal();
36 36
37 - const [registerForm, { validate, setFieldsValue, resetFields, updateSchema }] = useForm({ 37 + const [registerForm, { validate, resetFields, updateSchema }] = useForm({
38 labelWidth: 100, 38 labelWidth: 100,
39 schemas: addParamsSchemas, 39 schemas: addParamsSchemas,
40 actionColOptions: { 40 actionColOptions: {
@@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
21 21
22 const emits = defineEmits(['register', 'data']); 22 const emits = defineEmits(['register', 'data']);
23 23
24 - const { validateValueStruct } = useParitalValid(); 24 + const {} = useParitalValid();
25 25
26 const setEditData: any = reactive({ 26 const setEditData: any = reactive({
27 getEditData: {}, 27 getEditData: {},
@@ -37,9 +37,9 @@ @@ -37,9 +37,9 @@
37 37
38 const getTitle = computed(() => (!isUpdate.value ? '编辑参数' : '新增参数')); 38 const getTitle = computed(() => (!isUpdate.value ? '编辑参数' : '新增参数'));
39 39
40 - const [register, { closeModal, setModalProps }] = useModalInner(async (data) => { 40 + const [register, { setModalProps }] = useModalInner(async (data) => {
41 setModalProps({ loading: true }); 41 setModalProps({ loading: true });
42 - handleCancel(false); 42 +
43 isUpdate.value = data.isUpdate; 43 isUpdate.value = data.isUpdate;
44 isFlag.value = data.flag; 44 isFlag.value = data.flag;
45 excludeStruct.value = data.excludeStruct; 45 excludeStruct.value = data.excludeStruct;
@@ -53,29 +53,15 @@ @@ -53,29 +53,15 @@
53 } 53 }
54 }); 54 });
55 55
56 - const handleCancel = (flag) => { 56 + const handleCancel = () => {
57 AddParamFormRef.value?.resetFormData(); 57 AddParamFormRef.value?.resetFormData();
58 - if (flag) {  
59 - closeModal();  
60 - }  
61 }; 58 };
62 59
63 const handleSubmit = async () => { 60 const handleSubmit = async () => {
64 const value = await AddParamFormRef.value?.getFormData(); 61 const value = await AddParamFormRef.value?.getFormData();
65 if (!value) return; 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 </script> 66 </script>
81 <style lang="less" scope></style> 67 <style lang="less" scope></style>
1 <template> 1 <template>
2 <div> 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 <template #extra> 4 <template #extra>
5 <div style="display: flex; align-items: center"> 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 <a-divider type="vertical" style="height: 12px; background-color: #d8d8d8" /> 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 >删除</span 9 >删除</span
10 > 10 >
11 </div> 11 </div>
@@ -14,34 +14,26 @@ @@ -14,34 +14,26 @@
14 </div> 14 </div>
15 </template> 15 </template>
16 <script lang="ts" setup> 16 <script lang="ts" setup>
  17 + import { FunctionJson } from '/@/api/device/model/modelOfMatterModel';
  18 +
17 const emit = defineEmits(['delete', 'edit']); 19 const emit = defineEmits(['delete', 'edit']);
18 20
  21 + type ItemRecrod = FunctionJson & { id: string };
  22 +
19 const props = defineProps({ 23 const props = defineProps({
20 - index: {  
21 - type: Number,  
22 - required: true,  
23 - },  
24 - title: {  
25 - type: String,  
26 - default: '',  
27 - },  
28 item: { 24 item: {
29 - type: Object, 25 + type: Object as PropType<ItemRecrod>,
30 required: true, 26 required: true,
31 default: () => {}, 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 const getFormData = () => { 39 const getFormData = () => {
1 import { FormSchema } from '/@/components/Table'; 1 import { FormSchema } from '/@/components/Table';
2 import { findDictItemByCode } from '/@/api/system/dict'; 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 export const defaultTslContent = { 62 export const defaultTslContent = {
5 schema: 'https://iotx-tsl.oss-ap-southeast-1.aliyuncs.com/schema.json', 63 schema: 'https://iotx-tsl.oss-ap-southeast-1.aliyuncs.com/schema.json',
6 profile: { 64 profile: {
@@ -24,180 +82,9 @@ export const defaultTslContent = { @@ -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 export const serviceSchemas: FormSchema[] = [ 85 export const serviceSchemas: FormSchema[] = [
199 { 86 {
200 - field: 'serviceName', 87 + field: FormField.FUNCTION_NAME,
201 label: '功能名称', 88 label: '功能名称',
202 required: true, 89 required: true,
203 component: 'Input', 90 component: 'Input',
@@ -210,7 +97,7 @@ export const serviceSchemas: FormSchema[] = [ @@ -210,7 +97,7 @@ export const serviceSchemas: FormSchema[] = [
210 }, 97 },
211 }, 98 },
212 { 99 {
213 - field: 'identifier', 100 + field: FormField.IDENTIFIER,
214 label: '标识符', 101 label: '标识符',
215 required: true, 102 required: true,
216 component: 'Input', 103 component: 'Input',
@@ -223,7 +110,7 @@ export const serviceSchemas: FormSchema[] = [ @@ -223,7 +110,7 @@ export const serviceSchemas: FormSchema[] = [
223 }, 110 },
224 }, 111 },
225 { 112 {
226 - field: 'callType', 113 + field: FormField.CALL_TYPE,
227 component: 'ApiRadioGroup', 114 component: 'ApiRadioGroup',
228 label: '调用方式', 115 label: '调用方式',
229 required: true, 116 required: true,
@@ -242,22 +129,24 @@ export const serviceSchemas: FormSchema[] = [ @@ -242,22 +129,24 @@ export const serviceSchemas: FormSchema[] = [
242 }, 129 },
243 }, 130 },
244 { 131 {
245 - field: 'inputParam', 132 + field: FormField.INPUT_PARAM,
246 label: '输入参数', 133 label: '输入参数',
247 - component: 'Input',  
248 - slot: 'inputParamSlot', 134 + component: 'StructForm',
  135 + valueField: 'value',
  136 + changeEvent: 'update:value',
249 colProps: { span: 24 }, 137 colProps: { span: 24 },
250 }, 138 },
251 { 139 {
252 - field: 'outputParam', 140 + field: FormField.OUTPUT_PARAM,
253 label: '输出参数', 141 label: '输出参数',
254 - component: 'Input',  
255 - slot: 'outputParamSlot', 142 + component: 'StructForm',
  143 + valueField: 'value',
  144 + changeEvent: 'update:value',
256 colProps: { span: 24 }, 145 colProps: { span: 24 },
257 }, 146 },
258 { 147 {
259 - label: '描述',  
260 - field: 'description', 148 + field: FormField.REFARK,
  149 + label: '备注',
261 component: 'InputTextArea', 150 component: 'InputTextArea',
262 componentProps: { 151 componentProps: {
263 rows: 6, 152 rows: 6,
@@ -269,7 +158,7 @@ export const serviceSchemas: FormSchema[] = [ @@ -269,7 +158,7 @@ export const serviceSchemas: FormSchema[] = [
269 158
270 export const eventSchemas: FormSchema[] = [ 159 export const eventSchemas: FormSchema[] = [
271 { 160 {
272 - field: 'eventName', 161 + field: FormField.FUNCTION_NAME,
273 label: '功能名称', 162 label: '功能名称',
274 required: true, 163 required: true,
275 component: 'Input', 164 component: 'Input',
@@ -282,7 +171,7 @@ export const eventSchemas: FormSchema[] = [ @@ -282,7 +171,7 @@ export const eventSchemas: FormSchema[] = [
282 }, 171 },
283 }, 172 },
284 { 173 {
285 - field: 'identifier', 174 + field: FormField.IDENTIFIER,
286 label: '标识符', 175 label: '标识符',
287 required: true, 176 required: true,
288 component: 'Input', 177 component: 'Input',
@@ -295,7 +184,7 @@ export const eventSchemas: FormSchema[] = [ @@ -295,7 +184,7 @@ export const eventSchemas: FormSchema[] = [
295 }, 184 },
296 }, 185 },
297 { 186 {
298 - field: 'eventType', 187 + field: FormField.EVENT_TYPE,
299 component: 'ApiRadioGroup', 188 component: 'ApiRadioGroup',
300 label: '事件类型', 189 label: '事件类型',
301 required: true, 190 required: true,
@@ -314,15 +203,16 @@ export const eventSchemas: FormSchema[] = [ @@ -314,15 +203,16 @@ export const eventSchemas: FormSchema[] = [
314 }, 203 },
315 }, 204 },
316 { 205 {
317 - field: 'outputParam', 206 + field: FormField.OUTPUT_PARAM,
318 label: '输出参数', 207 label: '输出参数',
319 - component: 'Input',  
320 - slot: 'outputParamSlot', 208 + component: 'StructForm',
  209 + valueField: 'value',
  210 + changeEvent: 'update:value',
321 colProps: { span: 24 }, 211 colProps: { span: 24 },
322 }, 212 },
323 { 213 {
324 - label: '描述',  
325 - field: 'description', 214 + field: FormField.REFARK,
  215 + label: '备注',
326 component: 'InputTextArea', 216 component: 'InputTextArea',
327 componentProps: { 217 componentProps: {
328 rows: 6, 218 rows: 6,
@@ -332,34 +222,9 @@ export const eventSchemas: FormSchema[] = [ @@ -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 export const addParamsSchemas: FormSchema[] = [ 225 export const addParamsSchemas: FormSchema[] = [
361 { 226 {
362 - field: 'name', 227 + field: FormField.FUNCTION_NAME,
363 label: '参数名称', 228 label: '参数名称',
364 required: true, 229 required: true,
365 component: 'Input', 230 component: 'Input',
@@ -372,7 +237,7 @@ export const addParamsSchemas: FormSchema[] = [ @@ -372,7 +237,7 @@ export const addParamsSchemas: FormSchema[] = [
372 }, 237 },
373 }, 238 },
374 { 239 {
375 - field: 'identifier', 240 + field: FormField.IDENTIFIER,
376 label: '标识符', 241 label: '标识符',
377 required: true, 242 required: true,
378 component: 'Input', 243 component: 'Input',
@@ -385,7 +250,7 @@ export const addParamsSchemas: FormSchema[] = [ @@ -385,7 +250,7 @@ export const addParamsSchemas: FormSchema[] = [
385 }, 250 },
386 }, 251 },
387 { 252 {
388 - field: 'dataType', 253 + field: FormField.TYPE,
389 label: '数据类型', 254 label: '数据类型',
390 required: true, 255 required: true,
391 component: 'ApiSelect', 256 component: 'ApiSelect',
@@ -404,17 +269,16 @@ export const addParamsSchemas: FormSchema[] = [ @@ -404,17 +269,16 @@ export const addParamsSchemas: FormSchema[] = [
404 }, 269 },
405 }, 270 },
406 { 271 {
407 - field: 'structSlot', 272 + field: FormField.STRUCT,
408 label: 'JSON 对象', 273 label: 'JSON 对象',
409 component: 'Input', 274 component: 'Input',
410 - slot: 'structSlot',  
411 colProps: { 275 colProps: {
412 span: 23, 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 component: 'Input', 282 component: 'Input',
419 required: true, 283 required: true,
420 label: '0 -', 284 label: '0 -',
@@ -424,10 +288,10 @@ export const addParamsSchemas: FormSchema[] = [ @@ -424,10 +288,10 @@ export const addParamsSchemas: FormSchema[] = [
424 componentProps: { 288 componentProps: {
425 placeholder: '如:关', 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 component: 'Input', 295 component: 'Input',
432 required: true, 296 required: true,
433 label: '1 -', 297 label: '1 -',
@@ -437,10 +301,10 @@ export const addParamsSchemas: FormSchema[] = [ @@ -437,10 +301,10 @@ export const addParamsSchemas: FormSchema[] = [
437 componentProps: { 301 componentProps: {
438 placeholder: '如:开', 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 component: 'Input', 308 component: 'Input',
445 required: true, 309 required: true,
446 label: '数据长度', 310 label: '数据长度',
@@ -456,19 +320,19 @@ export const addParamsSchemas: FormSchema[] = [ @@ -456,19 +320,19 @@ export const addParamsSchemas: FormSchema[] = [
456 suffix: () => '字节', 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 label: '取值范围', 327 label: '取值范围',
464 - component: 'CustomeMinMaxInput', 328 + component: 'CustomMinMaxInput',
465 colProps: { 329 colProps: {
466 span: 23, 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 label: '步长', 336 label: '步长',
473 component: 'Input', 337 component: 'Input',
474 colProps: { 338 colProps: {
@@ -478,10 +342,10 @@ export const addParamsSchemas: FormSchema[] = [ @@ -478,10 +342,10 @@ export const addParamsSchemas: FormSchema[] = [
478 maxLength: 255, 342 maxLength: 255,
479 placeholder: '请输入步长', 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 label: '单位', 349 label: '单位',
486 component: 'ApiSelect', 350 component: 'ApiSelect',
487 colProps: { 351 colProps: {
@@ -496,6 +360,6 @@ export const addParamsSchemas: FormSchema[] = [ @@ -496,6 +360,6 @@ export const addParamsSchemas: FormSchema[] = [
496 labelField: 'itemText', 360 labelField: 'itemText',
497 valueField: 'itemValue', 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 export interface IAllData { 14 export interface IAllData {
2 properties: string[]; 15 properties: string[];
3 events: string[]; 16 events: string[];
@@ -23,31 +23,40 @@ @@ -23,31 +23,40 @@
23 23
24 const loading = ref(false); 24 const loading = ref(false);
25 25
  26 + const isUpdate = ref(false);
  27 +
26 const otaRecord = ref<OtaRecordDatum>({} as unknown as OtaRecordDatum); 28 const otaRecord = ref<OtaRecordDatum>({} as unknown as OtaRecordDatum);
27 29
28 const deviceProfileInfo = ref<DeviceProfileRecord>({} as unknown as DeviceProfileRecord); 30 const deviceProfileInfo = ref<DeviceProfileRecord>({} as unknown as DeviceProfileRecord);
29 31
30 const { createConfirm, createMessage } = useMessage(); 32 const { createConfirm, createMessage } = useMessage();
31 33
32 - const [registerForm, { setFieldsValue, getFieldsValue }] = useForm({ 34 + const [registerForm, { setFieldsValue, getFieldsValue, updateSchema }] = useForm({
33 schemas: formSchema, 35 schemas: formSchema,
34 showActionButtonGroup: false, 36 showActionButtonGroup: false,
35 disabled: true, 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 // const [registerTBDrawer, TBDrawerMethod] = useDrawer(); 61 // const [registerTBDrawer, TBDrawerMethod] = useDrawer();
53 62
@@ -146,7 +155,9 @@ @@ -146,7 +155,9 @@
146 > 155 >
147 <Button class="mr-2" @click="closeDrawer">取消</Button> 156 <Button class="mr-2" @click="closeDrawer">取消</Button>
148 <Authority :value="OtaPermissionKey.UPDATE"> 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 </Authority> 161 </Authority>
151 </div> 162 </div>
152 </template> 163 </template>
@@ -13,6 +13,7 @@ export enum OtaPermissionKey { @@ -13,6 +13,7 @@ export enum OtaPermissionKey {
13 UPDATE = 'api:operation:ota:update', 13 UPDATE = 'api:operation:ota:update',
14 DELETE = 'api:operation:ota:delete', 14 DELETE = 'api:operation:ota:delete',
15 DOWNLOAD = 'api:operation:ota:download', 15 DOWNLOAD = 'api:operation:ota:download',
  16 + DETAIL = 'api:operation:ota:detail',
16 } 17 }
17 18
18 export const columns: BasicColumn[] = [ 19 export const columns: BasicColumn[] = [
@@ -22,7 +23,7 @@ export const columns: BasicColumn[] = [ @@ -22,7 +23,7 @@ export const columns: BasicColumn[] = [
22 format(text) { 23 format(text) {
23 return dateUtil(text).format(DEFAULT_DATE_FORMAT); 24 return dateUtil(text).format(DEFAULT_DATE_FORMAT);
24 }, 25 },
25 - width: 120, 26 + width: 140,
26 }, 27 },
27 { 28 {
28 title: '标题', 29 title: '标题',
@@ -80,6 +81,7 @@ export const columns: BasicColumn[] = [ @@ -80,6 +81,7 @@ export const columns: BasicColumn[] = [
80 dataIndex: 'action', 81 dataIndex: 'action',
81 flag: 'ACTION', 82 flag: 'ACTION',
82 fixed: 'right', 83 fixed: 'right',
  84 + width: 200,
83 slots: { 85 slots: {
84 customRender: 'action', 86 customRender: 'action',
85 }, 87 },
@@ -113,6 +113,5 @@ export const formSchema: FormSchema[] = [ @@ -113,6 +113,5 @@ export const formSchema: FormSchema[] = [
113 field: PackageField.DESCRIPTION, 113 field: PackageField.DESCRIPTION,
114 label: '描述', 114 label: '描述',
115 component: 'Input', 115 component: 'Input',
116 - dynamicDisabled: false,  
117 }, 116 },
118 ]; 117 ];
@@ -22,6 +22,8 @@ @@ -22,6 +22,8 @@
22 const data = await getOtaPackagesList({ 22 const data = await getOtaPackagesList({
23 ...params, 23 ...params,
24 page: params.page - 1, 24 page: params.page - 1,
  25 + sortOrder: 'DESC',
  26 + sortProperty: 'createdTime',
25 textSearch: params.title, 27 textSearch: params.title,
26 }); 28 });
27 return { ...data, page: params.page }; 29 return { ...data, page: params.page };
@@ -57,8 +59,8 @@ @@ -57,8 +59,8 @@
57 openModal(true, { isUpdate: false } as ModalPassRecord); 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 const downloadFile = async (record: OtaRecordDatum) => { 66 const downloadFile = async (record: OtaRecordDatum) => {
@@ -117,6 +119,20 @@ @@ -117,6 +119,20 @@
117 @click.stop 119 @click.stop
118 :actions="[ 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 label: '下载', 136 label: '下载',
121 icon: 'ant-design:download-outlined', 137 icon: 'ant-design:download-outlined',
122 auth: OtaPermissionKey.DOWNLOAD, 138 auth: OtaPermissionKey.DOWNLOAD,