Commit 9cd1ea57ac4f12596f7906b512f79c3600bca610

Authored by fengwotao
2 parents 4d645387 c676c5e3

Merge branch 'main' into local_dev_ft

@@ -2,7 +2,8 @@ @@ -2,7 +2,8 @@
2 <div class="flex"> 2 <div class="flex">
3 <InputNumber 3 <InputNumber
4 placeholder="最小值" 4 placeholder="最小值"
5 - :value="getValue.min" 5 + :disabled="$props.disabled"
  6 + :value="getValue.min!"
6 style="width: 38%" 7 style="width: 38%"
7 @change="(value) => emitChange(value, 'min')" 8 @change="(value) => emitChange(value, 'min')"
8 /> 9 />
@@ -11,7 +12,8 @@ @@ -11,7 +12,8 @@
11 <span style="width: 8px"></span> 12 <span style="width: 8px"></span>
12 <InputNumber 13 <InputNumber
13 placeholder="最大值" 14 placeholder="最大值"
14 - :value="getValue.max" 15 + :disabled="$props.disabled"
  16 + :value="getValue.max!"
15 style="width: 38%" 17 style="width: 38%"
16 @change="(value) => emitChange(value, 'max')" 18 @change="(value) => emitChange(value, 'max')"
17 /> 19 />
@@ -33,6 +35,7 @@ @@ -33,6 +35,7 @@
33 min: Nullable<number>; 35 min: Nullable<number>;
34 max: Nullable<number>; 36 max: Nullable<number>;
35 }; 37 };
  38 + disabled: boolean;
36 }>(), 39 }>(),
37 { 40 {
38 value: () => ({ min: null, max: null }), 41 value: () => ({ min: null, max: null }),
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 const props = withDefaults( 19 const props = withDefaults(
20 defineProps<{ 20 defineProps<{
21 value: ModelOfMatterParams[]; 21 value: ModelOfMatterParams[];
  22 + disabled: boolean;
22 }>(), 23 }>(),
23 { 24 {
24 value: () => [], 25 value: () => [],
@@ -82,20 +83,33 @@ @@ -82,20 +83,33 @@
82 > 83 >
83 <div>参数名称: {{ item.functionName }}</div> 84 <div>参数名称: {{ item.functionName }}</div>
84 <div class="flex"> 85 <div class="flex">
85 - <Button class="!p-0" type="link" @click="handleUpdate(item)">编辑</Button> 86 + <Button class="!p-0" type="link" @click="handleUpdate(item)">
  87 + <span>{{ $props.disabled ? '查看' : '编辑' }}</span>
  88 + </Button>
86 <Divider type="vertical" /> 89 <Divider type="vertical" />
87 - <Button class="!p-0" type="link" @click="handleDelete(item)">删除</Button> 90 + <Button
  91 + :disabled="$props.disabled"
  92 + class="!p-0"
  93 + type="link"
  94 + @click="handleDelete(item)"
  95 + >
  96 + <span>删除</span>
  97 + </Button>
88 </div> 98 </div>
89 </div> 99 </div>
90 </section> 100 </section>
91 - <div> 101 + <div :class="$props.disabled && 'text-gray-400'">
92 <span class="mr-2"> 102 <span class="mr-2">
93 <PlusOutlined /> 103 <PlusOutlined />
94 </span> 104 </span>
95 - <span @click="handleCreateParams">增加参数</span> 105 + <span @click="!$props.disabled && handleCreateParams()">增加参数</span>
96 </div> 106 </div>
97 </div> 107 </div>
98 - <StructFormModel @register="registerModal" @submit="handleSaveStruct" /> 108 + <StructFormModel
  109 + :disabled="$props.disabled"
  110 + @register="registerModal"
  111 + @submit="handleSaveStruct"
  112 + />
99 </section> 113 </section>
100 </template> 114 </template>
101 115
@@ -18,14 +18,17 @@ @@ -18,14 +18,17 @@
18 mode: OpenModalMode.CREATE, 18 mode: OpenModalMode.CREATE,
19 }); 19 });
20 20
  21 + const props = defineProps<{ disabled: boolean }>();
  22 +
21 const emit = defineEmits(['register', 'submit']); 23 const emit = defineEmits(['register', 'submit']);
22 24
23 - const [register, { validate, setFieldsValue }] = useForm({ 25 + const [register, { validate, setFieldsValue, setProps }] = useForm({
24 labelWidth: 100, 26 labelWidth: 100,
25 schemas: formSchemas, 27 schemas: formSchemas,
26 actionColOptions: { 28 actionColOptions: {
27 span: 14, 29 span: 14,
28 }, 30 },
  31 + disabled: props.disabled,
29 showResetButton: false, 32 showResetButton: false,
30 submitOnReset: false, 33 submitOnReset: false,
31 showActionButtonGroup: false, 34 showActionButtonGroup: false,
@@ -46,6 +49,7 @@ @@ -46,6 +49,7 @@
46 49
47 setFieldsValue(value); 50 setFieldsValue(value);
48 } 51 }
  52 + setProps({ disabled: props.disabled });
49 }); 53 });
50 54
51 const handleSubmit = async () => { 55 const handleSubmit = async () => {
@@ -71,6 +75,7 @@ @@ -71,6 +75,7 @@
71 :width="800" 75 :width="800"
72 @ok="handleSubmit" 76 @ok="handleSubmit"
73 destroy-on-close 77 destroy-on-close
  78 + :show-ok-btn="!$props.disabled"
74 > 79 >
75 <BasicForm @register="register" /> 80 <BasicForm @register="register" />
76 </BasicModal> 81 </BasicModal>
@@ -11,7 +11,7 @@ export enum DataTypeEnum { @@ -11,7 +11,7 @@ export enum DataTypeEnum {
11 IS_BOOL = 'BOOL', 11 IS_BOOL = 'BOOL',
12 } 12 }
13 13
14 -const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => { 14 +export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => {
15 value = value || {}; 15 value = value || {};
16 const { min, max } = value; 16 const { min, max } = value;
17 if (min >= max) { 17 if (min >= max) {
@@ -20,7 +20,7 @@ const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callba @@ -20,7 +20,7 @@ const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callba
20 return Promise.resolve(); 20 return Promise.resolve();
21 }; 21 };
22 22
23 -const validateJSON = (_rule, value: ModelOfMatterParams[], _callback) => { 23 +export const validateJSON = (_rule, value: ModelOfMatterParams[], _callback) => {
24 if (value.length) { 24 if (value.length) {
25 return Promise.resolve(); 25 return Promise.resolve();
26 } 26 }
@@ -65,7 +65,15 @@ export const formSchemas: FormSchema[] = [ @@ -65,7 +65,15 @@ export const formSchemas: FormSchema[] = [
65 defaultValue: 'INT', 65 defaultValue: 'INT',
66 componentProps: { 66 componentProps: {
67 placeholder: '请选择数据类型', 67 placeholder: '请选择数据类型',
68 - api: findDictItemByCode, 68 + api: async (params: Recordable) => {
  69 + try {
  70 + const record = await findDictItemByCode(params);
  71 + return record.filter((item) => item.itemValue !== 'STRUCT');
  72 + } catch (error) {
  73 + console.log(error);
  74 + return [];
  75 + }
  76 + },
69 params: { 77 params: {
70 dictCode: 'data_type', 78 dictCode: 'data_type',
71 }, 79 },
1 -<template>  
2 - <BasicDrawer  
3 - v-bind="$attrs"  
4 - isDetail  
5 - @register="register"  
6 - destroyOnClose  
7 - @close="closeDrawer"  
8 - :title="deviceDetail.alias || deviceDetail.name"  
9 - width="80%"  
10 - >  
11 - <Tabs v-model:activeKey="activeKey" :size="size">  
12 - <TabPane key="1" tab="详情">  
13 - <Detail  
14 - ref="deviceDetailRef"  
15 - :deviceDetail="deviceDetail"  
16 - @open-gateway-device="handleOpenGatewayDevice"  
17 - />  
18 - </TabPane>  
19 - <TabPane key="modelOfMatter" tab="物模型数据">  
20 - <ModelOfMatter :deviceDetail="deviceDetail" />  
21 - </TabPane>  
22 - <!-- <TabPane key="2" tab="实时数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'">  
23 - <RealTimeData :deviceDetail="deviceDetail" />  
24 - </TabPane>  
25 - <TabPane key="7" tab="历史数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'">  
26 - <HistoryData :deviceDetail="deviceDetail" />  
27 - </TabPane> -->  
28 - <TabPane key="5" tab="命令下发" v-if="deviceDetail?.deviceType !== 'SENSOR'">  
29 - <CommandIssuance :deviceDetail="deviceDetail" />  
30 - </TabPane>  
31 - <TabPane key="3" tab="告警"><Alarm :id="deviceDetail.id" /></TabPane>  
32 - <TabPane key="4" tab="子设备" v-if="deviceDetail?.deviceType === 'GATEWAY'">  
33 - <ChildDevice  
34 - :fromId="deviceDetail?.tbDeviceId"  
35 - @openTbDeviceDetail="handleOpenTbDeviceDetail"  
36 - />  
37 - </TabPane>  
38 - <TabPane key="7" tab="命令下发记录">  
39 - <CommandRecord :fromId="deviceDetail?.tbDeviceId" />  
40 - </TabPane>  
41 - <!-- 网关设备并且场家是TBox -->  
42 - <TabPane  
43 - key="6"  
44 - tab="TBox"  
45 - v-if="deviceDetail?.deviceType === 'GATEWAY' && deviceDetail?.brand == 'TBox'"  
46 - >  
47 - <TBoxDetail :deviceDetail="deviceDetail" />  
48 - </TabPane>  
49 - <!-- 网关设备并且是TBox -->  
50 - </Tabs>  
51 - </BasicDrawer>  
52 -</template>  
53 -<script lang="ts">  
54 - import { defineComponent, ref } from 'vue';  
55 - import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';  
56 -  
57 - import { Tabs } from 'ant-design-vue';  
58 - import Detail from '../tabs/Detail.vue';  
59 - // import RealTimeData from '../tabs/RealTimeData.vue';  
60 - import Alarm from '../tabs/Alarm.vue';  
61 - import ChildDevice from '../tabs/ChildDevice.vue';  
62 - import TBoxDetail from '../tabs/TBoxDetail.vue';  
63 - import CommandIssuance from '../tabs/CommandIssuance.vue';  
64 - import { CommandRecord } from '../tabs/commandRecord/index';  
65 - import { getDeviceDetail } from '/@/api/device/deviceManager';  
66 - // import HistoryData from '../tabs/HistoryData.vue';  
67 - import ModelOfMatter from '../tabs/ModelOfMatter.vue';  
68 - export default defineComponent({  
69 - name: 'DeviceModal',  
70 - components: {  
71 - BasicDrawer,  
72 - Tabs,  
73 - TabPane: Tabs.TabPane,  
74 - Detail,  
75 - // RealTimeData,  
76 - Alarm,  
77 - ChildDevice,  
78 - CommandIssuance,  
79 - TBoxDetail,  
80 - // HistoryData,  
81 - ModelOfMatter,  
82 - CommandRecord,  
83 - },  
84 - emits: ['reload', 'register', 'openTbDeviceDetail', 'openGatewayDeviceDetail'],  
85 - setup(_props, { emit }) {  
86 - const activeKey = ref('1');  
87 - const size = ref('small');  
88 - const deviceDetailRef = ref();  
89 - const deviceDetail = ref<any>({});  
90 - const tbDeviceId = ref('');  
91 - // 详情回显  
92 - const [register] = useDrawerInner(async (data) => {  
93 - const { id } = data;  
94 - // 设备详情  
95 - const res = await getDeviceDetail(id);  
96 - deviceDetail.value = res;  
97 - const { latitude, longitude, address } = res.deviceInfo;  
98 - if (latitude) {  
99 - deviceDetailRef.value.initMap(longitude, latitude, address);  
100 - }  
101 - });  
102 - const closeDrawer = () => {  
103 - activeKey.value = '1';  
104 - };  
105 -  
106 - const handleOpenTbDeviceDetail = (data: { id: string; tbDeviceId: string }) => {  
107 - emit('openTbDeviceDetail', data);  
108 - };  
109 -  
110 - const handleOpenGatewayDevice = (data: { gatewayId: string; tbDeviceId: string }) => {  
111 - emit('openGatewayDeviceDetail', { id: data.gatewayId });  
112 - };  
113 -  
114 - return {  
115 - size,  
116 - activeKey,  
117 - register,  
118 - closeDrawer,  
119 - deviceDetail,  
120 - deviceDetailRef,  
121 - tbDeviceId,  
122 - handleOpenTbDeviceDetail,  
123 - handleOpenGatewayDevice,  
124 - };  
125 - },  
126 - });  
127 -</script> 1 +<template>
  2 + <BasicDrawer
  3 + v-bind="$attrs"
  4 + isDetail
  5 + @register="register"
  6 + destroyOnClose
  7 + @close="closeDrawer"
  8 + :title="deviceDetail.alias || deviceDetail.name"
  9 + width="80%"
  10 + >
  11 + <Tabs v-model:activeKey="activeKey" :size="size">
  12 + <TabPane key="1" tab="详情">
  13 + <Detail
  14 + ref="deviceDetailRef"
  15 + :deviceDetail="deviceDetail"
  16 + @open-gateway-device="handleOpenGatewayDevice"
  17 + />
  18 + </TabPane>
  19 + <TabPane key="modelOfMatter" tab="物模型数据">
  20 + <ModelOfMatter :deviceDetail="deviceDetail" />
  21 + </TabPane>
  22 + <!-- <TabPane key="2" tab="实时数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'">
  23 + <RealTimeData :deviceDetail="deviceDetail" />
  24 + </TabPane>
  25 + <TabPane key="7" tab="历史数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'">
  26 + <HistoryData :deviceDetail="deviceDetail" />
  27 + </TabPane> -->
  28 + <TabPane key="5" tab="命令下发" v-if="deviceDetail?.deviceType !== 'SENSOR'">
  29 + <CommandIssuance :deviceDetail="deviceDetail" />
  30 + </TabPane>
  31 + <TabPane key="3" tab="告警"><Alarm :id="deviceDetail.id" /></TabPane>
  32 + <TabPane key="4" tab="子设备" v-if="deviceDetail?.deviceType === 'GATEWAY'">
  33 + <ChildDevice
  34 + :fromId="deviceDetail?.tbDeviceId"
  35 + @openTbDeviceDetail="handleOpenTbDeviceDetail"
  36 + />
  37 + </TabPane>
  38 + <TabPane key="7" tab="命令下发记录">
  39 + <CommandRecord :fromId="deviceDetail?.tbDeviceId" />
  40 + </TabPane>
  41 + <!-- 网关设备并且场家是TBox -->
  42 + <TabPane
  43 + key="6"
  44 + tab="TBox"
  45 + v-if="deviceDetail?.deviceType === 'GATEWAY' && deviceDetail?.brand == 'TBox'"
  46 + >
  47 + <TBoxDetail :deviceDetail="deviceDetail" />
  48 + </TabPane>
  49 + <!-- 网关设备并且是TBox -->
  50 +
  51 + <TabPane key="eventManage" tab="事件管理">
  52 + <EventManage />
  53 + </TabPane>
  54 + </Tabs>
  55 + </BasicDrawer>
  56 +</template>
  57 +<script lang="ts">
  58 + import { defineComponent, ref } from 'vue';
  59 + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  60 +
  61 + import { Tabs } from 'ant-design-vue';
  62 + import Detail from '../tabs/Detail.vue';
  63 + // import RealTimeData from '../tabs/RealTimeData.vue';
  64 + import Alarm from '../tabs/Alarm.vue';
  65 + import ChildDevice from '../tabs/ChildDevice.vue';
  66 + import TBoxDetail from '../tabs/TBoxDetail.vue';
  67 + import CommandIssuance from '../tabs/CommandIssuance.vue';
  68 + import { CommandRecord } from '../tabs/commandRecord/index';
  69 + import { getDeviceDetail } from '/@/api/device/deviceManager';
  70 + // import HistoryData from '../tabs/HistoryData.vue';
  71 + import ModelOfMatter from '../tabs/ModelOfMatter.vue';
  72 + import EventManage from '../tabs/EventManage/index.vue';
  73 +
  74 + export default defineComponent({
  75 + name: 'DeviceModal',
  76 + components: {
  77 + BasicDrawer,
  78 + Tabs,
  79 + TabPane: Tabs.TabPane,
  80 + Detail,
  81 + // RealTimeData,
  82 + Alarm,
  83 + ChildDevice,
  84 + CommandIssuance,
  85 + TBoxDetail,
  86 + // HistoryData,
  87 + ModelOfMatter,
  88 + CommandRecord,
  89 + EventManage,
  90 + },
  91 + emits: ['reload', 'register', 'openTbDeviceDetail', 'openGatewayDeviceDetail'],
  92 + setup(_props, { emit }) {
  93 + const activeKey = ref('1');
  94 + const size = ref('small');
  95 + const deviceDetailRef = ref();
  96 + const deviceDetail = ref<any>({});
  97 + const tbDeviceId = ref('');
  98 + // 详情回显
  99 + const [register] = useDrawerInner(async (data) => {
  100 + const { id } = data;
  101 + // 设备详情
  102 + const res = await getDeviceDetail(id);
  103 + deviceDetail.value = res;
  104 + const { latitude, longitude, address } = res.deviceInfo;
  105 + if (latitude) {
  106 + deviceDetailRef.value.initMap(longitude, latitude, address);
  107 + }
  108 + });
  109 + const closeDrawer = () => {
  110 + activeKey.value = '1';
  111 + };
  112 +
  113 + const handleOpenTbDeviceDetail = (data: { id: string; tbDeviceId: string }) => {
  114 + emit('openTbDeviceDetail', data);
  115 + };
  116 +
  117 + const handleOpenGatewayDevice = (data: { gatewayId: string; tbDeviceId: string }) => {
  118 + emit('openGatewayDeviceDetail', { id: data.gatewayId });
  119 + };
  120 +
  121 + return {
  122 + size,
  123 + activeKey,
  124 + register,
  125 + closeDrawer,
  126 + deviceDetail,
  127 + deviceDetailRef,
  128 + tbDeviceId,
  129 + handleOpenTbDeviceDetail,
  130 + handleOpenGatewayDevice,
  131 + };
  132 + },
  133 + });
  134 +</script>
  1 +import { findDictItemByCode } from '/@/api/system/dict';
  2 +import { BasicColumn, FormSchema } from '/@/components/Table';
  3 +
  4 +export const columnSchema: BasicColumn[] = [
  5 + {
  6 + title: '时间',
  7 + dataIndex: 'time',
  8 + },
  9 + {
  10 + title: '标识符',
  11 + dataIndex: 'identifier',
  12 + helpMessage: ['标识符格式为模块标识符: 功能定义标识符'],
  13 + },
  14 + {
  15 + title: '事件名称',
  16 + dataIndex: 'eventName',
  17 + },
  18 + {
  19 + title: '事件类型',
  20 + dataIndex: 'eventType',
  21 + },
  22 + {
  23 + title: '输出参数',
  24 + dataIndex: 'outputParams',
  25 + slots: { customRender: 'outputParams' },
  26 + },
  27 +];
  28 +
  29 +export const formSchemas: FormSchema[] = [
  30 + {
  31 + field: 'identifier',
  32 + label: '标识符',
  33 + component: 'Input',
  34 + },
  35 + {
  36 + field: 'alarmType',
  37 + label: '告警类型',
  38 + component: 'ApiSelect',
  39 + defaultValue: 'INFO',
  40 + componentProps: {
  41 + placeholder: '请选择事件类型',
  42 + api: findDictItemByCode,
  43 + params: {
  44 + dictCode: 'event_type',
  45 + },
  46 + labelField: 'itemText',
  47 + valueField: 'itemValue',
  48 + },
  49 + },
  50 + {
  51 + field: 'dateRange',
  52 + label: '时间范围',
  53 + component: 'RangePicker',
  54 + colProps: { span: 10 },
  55 + },
  56 +];
  1 +<script lang="ts" setup>
  2 + import { BasicTable, useTable } from '/@/components/Table';
  3 + import { formSchemas, columnSchema } from './config';
  4 + import { BasicModal, useModal } from '/@/components/Modal';
  5 + import { Textarea } from 'ant-design-vue';
  6 + import { ref } from 'vue';
  7 + import { formatToDateTime } from '/@/utils/dateUtil';
  8 + import { EyeOutlined } from '@ant-design/icons-vue';
  9 +
  10 + const outputData = ref<string>();
  11 +
  12 + const [register] = useTable({
  13 + columns: columnSchema,
  14 + size: 'small',
  15 + showIndexColumn: false,
  16 + useSearchForm: true,
  17 + showTableSetting: true,
  18 + bordered: true,
  19 + dataSource: [
  20 + {
  21 + time: formatToDateTime(new Date()),
  22 + identifier: '标识符',
  23 + eventName: '事件名',
  24 + eventType: '告警',
  25 + outputParams: { test: 1 },
  26 + },
  27 + ],
  28 + formConfig: {
  29 + layout: 'inline',
  30 + baseColProps: { span: 6 },
  31 + labelWidth: 80,
  32 + schemas: formSchemas,
  33 + },
  34 + handleSearchInfoFn: (params: Recordable) => {
  35 + // console.log(params);
  36 + return params;
  37 + },
  38 + });
  39 +
  40 + const [registerModal, { openModal }] = useModal();
  41 +
  42 + const handleViewDetail = () => {
  43 + outputData.value = JSON.stringify(
  44 + {
  45 + test: '123',
  46 + },
  47 + null,
  48 + 2
  49 + );
  50 + openModal(true);
  51 + };
  52 +</script>
  53 +
  54 +<template>
  55 + <BasicTable class="event-manage-table" @register="register">
  56 + <template #outputParams>
  57 + <span class="cursor-pointer text-blue-500" @click="handleViewDetail">
  58 + <EyeOutlined class="svg:text-blue-500" />
  59 + <span class="ml-2">详情</span>
  60 + </span>
  61 + </template>
  62 + </BasicTable>
  63 + <BasicModal title="输出参数" @register="registerModal">
  64 + <Textarea v-model:value="outputData" :autosize="true" />
  65 + </BasicModal>
  66 +</template>
  67 +
  68 +<style lang="less" scoped>
  69 + .event-manage-table {
  70 + background-color: #f0f2f5;
  71 + }
  72 +</style>
1 -<template>  
2 - <div style="background-color: #f0f2f5">  
3 - <BasicTable @register="registerTable">  
4 - <template #recordContent="{ record }">  
5 - <a-button type="link" class="ml-2" @click="handleRecordContent(record)"> 查看 </a-button>  
6 - </template>  
7 - <template #responseContent="{ record }">  
8 - <div v-if="!record.request?.oneway">  
9 - <a-button v-if="record?.response === null" type="text" class="ml-2"> 无 </a-button>  
10 - <a-button v-else type="link" class="ml-2" @click="handleRecordResponseContent(record)">  
11 - 查看  
12 - </a-button>  
13 - </div>  
14 - </template>  
15 - </BasicTable>  
16 - </div>  
17 -</template>  
18 -<script lang="ts" setup>  
19 - import { h } from 'vue';  
20 - import { configColumns } from './config';  
21 - import { deviceCommandRecordGetQuery } from '/@/api/device/deviceConfigApi';  
22 - import { BasicTable, useTable } from '/@/components/Table';  
23 - import { Modal } from 'ant-design-vue';  
24 - import { JsonPreview } from '/@/components/CodeEditor';  
25 -  
26 - const props = defineProps({  
27 - fromId: {  
28 - type: String,  
29 - default: '',  
30 - },  
31 - });  
32 - const [registerTable] = useTable({  
33 - api: deviceCommandRecordGetQuery,  
34 - columns: configColumns,  
35 - beforeFetch: (params) => {  
36 - return {  
37 - ...params,  
38 - tbDeviceId: props.fromId,  
39 - };  
40 - },  
41 - showTableSetting: true,  
42 - bordered: true,  
43 - showIndexColumn: false,  
44 - });  
45 - const commonModalInfo = (title, value) => {  
46 - Modal.info({  
47 - title,  
48 - width: 600,  
49 - content: h(JsonPreview, { data: value }),  
50 - });  
51 - };  
52 - const handleRecordContent = (record) => {  
53 - if (!record?.request?.body?.params) return;  
54 - //如果是正常格式则返回params,否则输入什么内容则显示什么内容  
55 - const jsonParams = JSON.parse(record?.request?.body?.params);  
56 - commonModalInfo('命令下发内容', jsonParams?.params ? jsonParams?.params : jsonParams);  
57 - };  
58 - const handleRecordResponseContent = (record) => {  
59 - const jsonParams = record?.response;  
60 - commonModalInfo('响应结果', jsonParams);  
61 - };  
62 -</script> 1 +<template>
  2 + <BasicTable class="command-record-table" @register="registerTable">
  3 + <template #recordContent="{ record }">
  4 + <a-button type="link" class="ml-2" @click="handleRecordContent(record)"> 查看 </a-button>
  5 + </template>
  6 + <template #responseContent="{ record }">
  7 + <div v-if="!record.request?.oneway">
  8 + <a-button v-if="record?.response === null" type="text" class="ml-2"> 无 </a-button>
  9 + <a-button v-else type="link" class="ml-2" @click="handleRecordResponseContent(record)">
  10 + 查看
  11 + </a-button>
  12 + </div>
  13 + </template>
  14 + </BasicTable>
  15 +</template>
  16 +<script lang="ts" setup>
  17 + import { h } from 'vue';
  18 + import { configColumns } from './config';
  19 + import { deviceCommandRecordGetQuery } from '/@/api/device/deviceConfigApi';
  20 + import { BasicTable, useTable } from '/@/components/Table';
  21 + import { Modal } from 'ant-design-vue';
  22 + import { JsonPreview } from '/@/components/CodeEditor';
  23 +
  24 + const props = defineProps({
  25 + fromId: {
  26 + type: String,
  27 + default: '',
  28 + },
  29 + });
  30 + const [registerTable] = useTable({
  31 + api: deviceCommandRecordGetQuery,
  32 + columns: configColumns,
  33 + beforeFetch: (params) => {
  34 + return {
  35 + ...params,
  36 + tbDeviceId: props.fromId,
  37 + };
  38 + },
  39 + showTableSetting: true,
  40 + bordered: true,
  41 + showIndexColumn: false,
  42 + });
  43 + const commonModalInfo = (title, value) => {
  44 + Modal.info({
  45 + title,
  46 + width: 600,
  47 + content: h(JsonPreview, { data: value }),
  48 + });
  49 + };
  50 + const handleRecordContent = (record) => {
  51 + if (!record?.request?.body?.params) return;
  52 + //如果是正常格式则返回params,否则输入什么内容则显示什么内容
  53 + const jsonParams = JSON.parse(record?.request?.body?.params);
  54 + commonModalInfo('命令下发内容', jsonParams?.params ? jsonParams?.params : jsonParams);
  55 + };
  56 + const handleRecordResponseContent = (record) => {
  57 + const jsonParams = record?.response;
  58 + commonModalInfo('响应结果', jsonParams);
  59 + };
  60 +</script>
  61 +
  62 +<style lang="less" scoped>
  63 + .command-record-table {
  64 + background-color: #f0f2f5;
  65 + padding: 16px;
  66 + }
  67 +</style>
@@ -27,17 +27,26 @@ @@ -27,17 +27,26 @@
27 <TabPane :key="FunctionType.SERVICE" :disabled="isTCPGatewaySubDevice" tab="服务" /> 27 <TabPane :key="FunctionType.SERVICE" :disabled="isTCPGatewaySubDevice" tab="服务" />
28 <TabPane :key="FunctionType.EVENTS" tab="事件" :disabled="isTCPGatewaySubDevice" /> 28 <TabPane :key="FunctionType.EVENTS" tab="事件" :disabled="isTCPGatewaySubDevice" />
29 </Tabs> 29 </Tabs>
30 - <Attribute v-if="activeKey === FunctionType.PROPERTIES" ref="AttrRef" /> 30 + <Attribute
  31 + v-if="activeKey === FunctionType.PROPERTIES"
  32 + :openModalMode="openModalMode"
  33 + ref="AttrRef"
  34 + />
31 <Service 35 <Service
32 v-if="activeKey === FunctionType.SERVICE" 36 v-if="activeKey === FunctionType.SERVICE"
33 :record="$props.record" 37 :record="$props.record"
  38 + :openModalMode="openModalMode"
34 ref="ServiceRef" 39 ref="ServiceRef"
35 /> 40 />
36 - <Events v-if="activeKey === FunctionType.EVENTS" ref="EventsRef" />  
37 - <div 41 + <Events
  42 + v-if="activeKey === FunctionType.EVENTS"
  43 + :openModalMode="openModalMode"
  44 + ref="EventsRef"
  45 + />
  46 + <!-- <div
38 v-if="openModalMode === OpenModelMode.VIEW" 47 v-if="openModalMode === OpenModelMode.VIEW"
39 class="absolute w-full h-full top-0 cursor-not-allowed z-50" 48 class="absolute w-full h-full top-0 cursor-not-allowed z-50"
40 - ></div> 49 + ></div> -->
41 </div> 50 </div>
42 </BasicModal> 51 </BasicModal>
43 </div> 52 </div>
@@ -116,6 +125,9 @@ @@ -116,6 +125,9 @@
116 const title = unref(openModalMode) === OpenModelMode.UPDATE ? '编辑物模型' : '新增物模型'; 125 const title = unref(openModalMode) === OpenModelMode.UPDATE ? '编辑物模型' : '新增物模型';
117 setModalProps({ title, showOkBtn: true, showCancelBtn: true }); 126 setModalProps({ title, showOkBtn: true, showCancelBtn: true });
118 } 127 }
  128 + AttrRef.value?.setDisable(unref(openModalMode) === OpenModelMode.VIEW);
  129 + EventsRef.value?.setDisable(unref(openModalMode) === OpenModelMode.VIEW);
  130 + ServiceRef.value?.setDisable(unref(openModalMode) === OpenModelMode.VIEW);
119 } 131 }
120 ); 132 );
121 133
@@ -153,7 +165,7 @@ @@ -153,7 +165,7 @@
153 closeModal(); 165 closeModal();
154 emit('success'); 166 emit('success');
155 } catch (error) { 167 } catch (error) {
156 - throw Error(error); 168 + throw Error(error as string);
157 } finally { 169 } finally {
158 setModalProps({ loading: false, okButtonProps: { loading: false } }); 170 setModalProps({ loading: false, okButtonProps: { loading: false } });
159 } 171 }
@@ -4,18 +4,20 @@ @@ -4,18 +4,20 @@
4 <script lang="ts" setup> 4 <script lang="ts" setup>
5 import { BasicForm, useForm } from '/@/components/Form'; 5 import { BasicForm, useForm } from '/@/components/Form';
6 import { DataType, ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; 6 import { DataType, ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
7 - import { formSchemas } from '/@/components/Form/src/externalCompns/components/StructForm/config';  
8 import { StructFormValue } from '/@/components/Form/src/externalCompns/components/StructForm/type'; 7 import { StructFormValue } from '/@/components/Form/src/externalCompns/components/StructForm/type';
9 import { 8 import {
10 transfromToStructJSON, 9 transfromToStructJSON,
11 excludeIdInStructJSON, 10 excludeIdInStructJSON,
12 } from '/@/components/Form/src/externalCompns/components/StructForm/util'; 11 } from '/@/components/Form/src/externalCompns/components/StructForm/util';
13 - import { FunctionType } from './config'; 12 + import { FunctionType, attributeSchema } from './config';
14 import { isArray } from 'lodash'; 13 import { isArray } from 'lodash';
  14 + import { OpenModelMode } from '../types';
15 15
16 - const [register, { validate, resetFields, setFieldsValue }] = useForm({ 16 + defineProps<{ openModalMode: OpenModelMode }>();
  17 +
  18 + const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({
17 labelWidth: 100, 19 labelWidth: 100,
18 - schemas: formSchemas, 20 + schemas: attributeSchema,
19 actionColOptions: { 21 actionColOptions: {
20 span: 14, 22 span: 14,
21 }, 23 },
@@ -24,6 +26,10 @@ @@ -24,6 +26,10 @@
24 showActionButtonGroup: false, 26 showActionButtonGroup: false,
25 }); 27 });
26 28
  29 + const setDisable = (flag: boolean) => {
  30 + setProps({ disabled: flag });
  31 + };
  32 +
27 async function getFormData(): Promise<Partial<ModelOfMatterParams>> { 33 async function getFormData(): Promise<Partial<ModelOfMatterParams>> {
28 const _values = (await validate()) as StructFormValue; 34 const _values = (await validate()) as StructFormValue;
29 if (!_values) return {}; 35 if (!_values) return {};
@@ -68,6 +74,7 @@ @@ -68,6 +74,7 @@
68 resetFormData, 74 resetFormData,
69 getFormData, 75 getFormData,
70 setFormData, 76 setFormData,
  77 + setDisable,
71 }); 78 });
72 </script> 79 </script>
73 <style lang="less" scoped></style> 80 <style lang="less" scoped></style>
@@ -7,8 +7,11 @@ @@ -7,8 +7,11 @@
7 import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 7 import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel';
8 import { StructFormValue } from '/@/components/Form/src/externalCompns/components/StructForm/type'; 8 import { StructFormValue } from '/@/components/Form/src/externalCompns/components/StructForm/type';
9 import { excludeIdInStructJSON } from '/@/components/Form/src/externalCompns/components/StructForm/util'; 9 import { excludeIdInStructJSON } from '/@/components/Form/src/externalCompns/components/StructForm/util';
  10 + import { OpenModelMode } from '../types';
10 11
11 - const [register, { validate, resetFields, setFieldsValue }] = useForm({ 12 + defineProps<{ openModalMode: OpenModelMode }>();
  13 +
  14 + const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({
12 labelWidth: 100, 15 labelWidth: 100,
13 schemas: eventSchemas, 16 schemas: eventSchemas,
14 actionColOptions: { 17 actionColOptions: {
@@ -19,6 +22,10 @@ @@ -19,6 +22,10 @@
19 showActionButtonGroup: false, 22 showActionButtonGroup: false,
20 }); 23 });
21 24
  25 + const setDisable = (flag: boolean) => {
  26 + setProps({ disabled: flag });
  27 + };
  28 +
22 //回显数据 29 //回显数据
23 const setFormData = (record: ModelOfMatterParams) => { 30 const setFormData = (record: ModelOfMatterParams) => {
24 const { functionJson = {}, functionName, identifier, remark, eventType } = record; 31 const { functionJson = {}, functionName, identifier, remark, eventType } = record;
@@ -69,6 +76,7 @@ @@ -69,6 +76,7 @@
69 setFormData, 76 setFormData,
70 resetFormData, 77 resetFormData,
71 getFormData, 78 getFormData,
  79 + setDisable,
72 }); 80 });
73 </script> 81 </script>
74 <style lang="less" scoped></style> 82 <style lang="less" scoped></style>
@@ -9,22 +9,29 @@ @@ -9,22 +9,29 @@
9 import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 9 import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel';
10 import { DeviceRecord } from '/@/api/device/model/deviceModel'; 10 import { DeviceRecord } from '/@/api/device/model/deviceModel';
11 import { excludeIdInStructJSON } from '/@/components/Form/src/externalCompns/components/StructForm/util'; 11 import { excludeIdInStructJSON } from '/@/components/Form/src/externalCompns/components/StructForm/util';
  12 + import { OpenModelMode } from '../types';
12 13
13 const props = defineProps<{ 14 const props = defineProps<{
14 record: DeviceRecord; 15 record: DeviceRecord;
  16 + openModalMode: OpenModelMode;
15 }>(); 17 }>();
16 18
17 - const [register, { validate, resetFields, setFieldsValue }] = useForm({ 19 + const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({
18 labelWidth: 100, 20 labelWidth: 100,
19 schemas: serviceSchemas(props.record.transportType === 'TCP'), 21 schemas: serviceSchemas(props.record.transportType === 'TCP'),
20 actionColOptions: { 22 actionColOptions: {
21 span: 14, 23 span: 14,
22 }, 24 },
  25 + disabled: props.openModalMode === OpenModelMode.VIEW,
23 showResetButton: false, 26 showResetButton: false,
24 submitOnReset: false, 27 submitOnReset: false,
25 showActionButtonGroup: false, 28 showActionButtonGroup: false,
26 }); 29 });
27 30
  31 + const setDisable = (flag: boolean) => {
  32 + setProps({ disabled: flag });
  33 + };
  34 +
28 //回显数据 35 //回显数据
29 const setFormData = (record: ModelOfMatterParams) => { 36 const setFormData = (record: ModelOfMatterParams) => {
30 const { functionJson = {}, functionName, identifier, remark, callType } = record; 37 const { functionJson = {}, functionName, identifier, remark, callType } = record;
@@ -98,6 +105,7 @@ @@ -98,6 +105,7 @@
98 setFormData, 105 setFormData,
99 resetFormData, 106 resetFormData,
100 getFormData, 107 getFormData,
  108 + setDisable,
101 }); 109 });
102 </script> 110 </script>
103 <style lang="less" scoped></style> 111 <style lang="less" scoped></style>
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 +import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  4 +
  5 +export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => {
  6 + value = value || {};
  7 + const { min, max } = value;
  8 + if (min >= max) {
  9 + return Promise.reject('最大值小于最小值');
  10 + }
  11 + return Promise.resolve();
  12 +};
  13 +
  14 +export const validateJSON = (_rule, value: ModelOfMatterParams[], _callback) => {
  15 + if (value.length) {
  16 + return Promise.resolve();
  17 + }
  18 + return Promise.reject('JSON对象不能为空');
  19 +};
3 20
4 export enum FormField { 21 export enum FormField {
5 FUNCTION_NAME = 'functionName', 22 FUNCTION_NAME = 'functionName',
@@ -358,3 +375,265 @@ export const addParamsSchemas: FormSchema[] = [ @@ -358,3 +375,265 @@ export const addParamsSchemas: FormSchema[] = [
358 ifShow: ({ values }) => isNumber(values[FormField.TYPE]), 375 ifShow: ({ values }) => isNumber(values[FormField.TYPE]),
359 }, 376 },
360 ]; 377 ];
  378 +
  379 +export const attributeSchema: FormSchema[] = [
  380 + {
  381 + field: FormField.FUNCTION_NAME,
  382 + label: '功能名称',
  383 + required: true,
  384 + component: 'Input',
  385 + colProps: {
  386 + span: 18,
  387 + },
  388 + componentProps: {
  389 + maxLength: 255,
  390 + placeholder: '请输入功能名称',
  391 + },
  392 + },
  393 + {
  394 + field: FormField.IDENTIFIER,
  395 + label: '标识符',
  396 + required: true,
  397 + component: 'Input',
  398 + colProps: {
  399 + span: 18,
  400 + },
  401 + componentProps: {
  402 + maxLength: 255,
  403 + placeholder: '请输入标识符',
  404 + },
  405 + },
  406 + {
  407 + field: FormField.TYPE,
  408 + label: '数据类型',
  409 + required: true,
  410 + component: 'ApiSelect',
  411 + colProps: {
  412 + span: 9,
  413 + },
  414 + defaultValue: 'INT',
  415 + componentProps: {
  416 + placeholder: '请选择数据类型',
  417 + api: findDictItemByCode,
  418 + params: {
  419 + dictCode: 'data_type',
  420 + },
  421 + labelField: 'itemText',
  422 + valueField: 'itemValue',
  423 + getPopupContainer: () => document.body,
  424 + },
  425 + },
  426 + {
  427 + field: FormField.VALUE_RANGE,
  428 + label: '取值范围',
  429 + component: 'CustomMinMaxInput',
  430 + valueField: 'value',
  431 + changeEvent: 'update:value',
  432 + colProps: {
  433 + span: 18,
  434 + },
  435 + ifShow: ({ values }) =>
  436 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
  437 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
  438 + rules: [{ validator: validateValueRange }],
  439 + },
  440 + {
  441 + field: FormField.STEP,
  442 + label: '步长',
  443 + component: 'InputNumber',
  444 + colProps: {
  445 + span: 18,
  446 + },
  447 + componentProps: {
  448 + maxLength: 255,
  449 + placeholder: '请输入步长',
  450 + min: 1,
  451 + formatter: (value: number | string) => {
  452 + return value ? Math.floor(Number(value)) : value;
  453 + },
  454 + },
  455 + ifShow: ({ values }) =>
  456 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
  457 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
  458 + dynamicRules: ({ model }) => {
  459 + const valueRange = model[FormField.VALUE_RANGE] || {};
  460 + const { min = 0, max = 0 } = valueRange;
  461 + const step = model[FormField.STEP];
  462 + return [
  463 + {
  464 + validator: () => {
  465 + if (step > max - min) {
  466 + return Promise.reject('步长不能大于取值范围的差值');
  467 + }
  468 + return Promise.resolve();
  469 + },
  470 + },
  471 + ];
  472 + },
  473 + },
  474 + {
  475 + field: FormField.UNIT_NAME,
  476 + label: '单位名称',
  477 + component: 'Input',
  478 + show: false,
  479 + },
  480 + {
  481 + field: FormField.UNIT,
  482 + label: '单位',
  483 + component: 'ApiSelect',
  484 + colProps: {
  485 + span: 9,
  486 + },
  487 + componentProps: ({ formActionType }) => {
  488 + const { setFieldsValue } = formActionType;
  489 + return {
  490 + placeholder: '请选择单位',
  491 + api: async (params) => {
  492 + const list = await findDictItemByCode(params);
  493 + list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`));
  494 + return list;
  495 + },
  496 + params: {
  497 + dictCode: 'attribute_unit',
  498 + },
  499 + labelInValue: true,
  500 + labelField: 'itemText',
  501 + valueField: 'itemValue',
  502 + onChange(_, record: Record<'label' | 'value', string>) {
  503 + if (record) {
  504 + const { label } = record;
  505 + setFieldsValue({ [FormField.UNIT_NAME]: label });
  506 + }
  507 + },
  508 + getPopupContainer: () => document.body,
  509 + showSearch: true,
  510 + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => {
  511 + let { label, value } = option;
  512 + label = label.toLowerCase();
  513 + value = value.toLowerCase();
  514 + inputValue = inputValue.toLowerCase();
  515 + return label.includes(inputValue) || value.includes(inputValue);
  516 + },
  517 + };
  518 + },
  519 + ifShow: ({ values }) =>
  520 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
  521 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
  522 + },
  523 + {
  524 + field: FormField.BOOL_CLOSE,
  525 + component: 'Input',
  526 + required: true,
  527 + label: '0 -',
  528 + colProps: {
  529 + span: 18,
  530 + },
  531 + componentProps: {
  532 + placeholder: '如:关',
  533 + },
  534 + defaultValue: '关',
  535 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL,
  536 + dynamicRules: ({ model }) => {
  537 + const close = model[FormField.BOOL_CLOSE];
  538 + const open = model[FormField.BOOL_OPEN];
  539 + return [
  540 + {
  541 + required: true,
  542 + },
  543 + {
  544 + validator() {
  545 + if (open === close) return Promise.reject('布尔值不能相同');
  546 + return Promise.resolve();
  547 + },
  548 + },
  549 + ];
  550 + },
  551 + },
  552 + {
  553 + field: FormField.BOOL_OPEN,
  554 + component: 'Input',
  555 + required: true,
  556 + label: '1 -',
  557 + colProps: {
  558 + span: 18,
  559 + },
  560 + componentProps: {
  561 + placeholder: '如:开',
  562 + },
  563 + defaultValue: '开',
  564 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL,
  565 + dynamicRules: ({ model }) => {
  566 + const close = model[FormField.BOOL_CLOSE];
  567 + const open = model[FormField.BOOL_OPEN];
  568 + return [
  569 + {
  570 + required: true,
  571 + },
  572 + {
  573 + validator() {
  574 + if (open === close) return Promise.reject('布尔值不能相同');
  575 + return Promise.resolve();
  576 + },
  577 + },
  578 + ];
  579 + },
  580 + },
  581 + {
  582 + field: FormField.LENGTH,
  583 + component: 'Input',
  584 + required: true,
  585 + label: '数据长度',
  586 + defaultValue: '10240',
  587 + colProps: {
  588 + span: 8,
  589 + },
  590 + componentProps: {
  591 + placeholder: '请输入数据长度',
  592 + },
  593 + renderComponentContent: () => {
  594 + return {
  595 + suffix: () => '字节',
  596 + };
  597 + },
  598 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRING,
  599 + },
  600 + {
  601 + field: FormField.ACCESS_MODE,
  602 + component: 'ApiRadioGroup',
  603 + label: '读写类型',
  604 + required: true,
  605 + colProps: {
  606 + span: 24,
  607 + },
  608 + defaultValue: 'r',
  609 + componentProps: {
  610 + placeholder: '请选择读写类型',
  611 + api: findDictItemByCode,
  612 + params: {
  613 + dictCode: 'read_write_type',
  614 + },
  615 + labelField: 'itemText',
  616 + valueField: 'itemValue',
  617 + },
  618 + },
  619 + {
  620 + field: FormField.SPECS_LIST,
  621 + label: 'JSON对象',
  622 + component: 'StructForm',
  623 + valueField: 'value',
  624 + changeEvent: 'update:value',
  625 + colProps: { span: 24 },
  626 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRUCT,
  627 + rules: [{ required: true, validator: validateJSON }],
  628 + },
  629 + {
  630 + field: FormField.REFARK,
  631 + label: '备注',
  632 + component: 'InputTextArea',
  633 + componentProps: {
  634 + rows: 4,
  635 + maxLength: 100,
  636 + placeholder: '请输入描述',
  637 + },
  638 + },
  639 +];