Commit 26f450d6f00403e3db08e3ff38942cd8e36b41c9

Authored by xp.Huang
2 parents 221db713 7d692683

Merge branch 'ft_local_dev' into 'main'

feat:设备配置新增物模型静态页面

See merge request huang/yun-teng-iot-front!356
@@ -11,12 +11,12 @@ import projectSetting from '/@/settings/projectSetting'; @@ -11,12 +11,12 @@ import projectSetting from '/@/settings/projectSetting';
11 import { PermissionModeEnum } from '/@/enums/appEnum'; 11 import { PermissionModeEnum } from '/@/enums/appEnum';
12 import { asyncRoutes } from '/@/router/routes'; 12 import { asyncRoutes } from '/@/router/routes';
13 import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'; 13 import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
14 -import { filter, forEach } from '/@/utils/helper/treeHelper'; 14 +import { filter } from '/@/utils/helper/treeHelper';
15 import { getMenuList, getMenusIdsByRoleId } from '/@/api/sys/menu'; 15 import { getMenuList, getMenusIdsByRoleId } from '/@/api/sys/menu';
16 import { getPermCode } from '/@/api/sys/user'; 16 import { getPermCode } from '/@/api/sys/user';
17 import { useMessage } from '/@/hooks/web/useMessage'; 17 import { useMessage } from '/@/hooks/web/useMessage';
18 import { PageEnum } from '/@/enums/pageEnum'; 18 import { PageEnum } from '/@/enums/pageEnum';
19 -import { MENU_LIST, USER_INFO_KEY } from '/@/enums/cacheEnum'; 19 +import { USER_INFO_KEY } from '/@/enums/cacheEnum';
20 import { getAuthCache, setAuthCache } from '/@/utils/auth'; 20 import { getAuthCache, setAuthCache } from '/@/utils/auth';
21 import { createStorage } from '/@/utils/cache/index'; 21 import { createStorage } from '/@/utils/cache/index';
22 22
@@ -89,7 +89,8 @@ export const usePermissionStore = defineStore({ @@ -89,7 +89,8 @@ export const usePermissionStore = defineStore({
89 this.lastBuildMenuTime = 0; 89 this.lastBuildMenuTime = 0;
90 }, 90 },
91 async changePermissionCode() { 91 async changePermissionCode() {
92 - const filterMenu = (allMenuList, menuIdsList) => { 92 + //判空处理,如果没有数据则为空,空数组.filter是不会报错的
  93 + const filterMenu = (allMenuList: Recordable[] = [], menuIdsList) => {
93 return allMenuList 94 return allMenuList
94 .filter((item) => { 95 .filter((item) => {
95 return menuIdsList.indexOf(item.id) > -1; 96 return menuIdsList.indexOf(item.id) > -1;
@@ -3,50 +3,53 @@ @@ -3,50 +3,53 @@
3 <BasicModal 3 <BasicModal
4 :maskClosable="false" 4 :maskClosable="false"
5 v-bind="$attrs" 5 v-bind="$attrs"
6 - width="55rem" 6 + width="60rem"
7 @register="register" 7 @register="register"
8 @ok="handleSubmit" 8 @ok="handleSubmit"
9 @cancel="handleCancel" 9 @cancel="handleCancel"
10 > 10 >
11 - <div class="step-form-form">  
12 - <a-steps :current="current">  
13 - <a-step v-for="item in steps" :key="item.title" :title="item.title" />  
14 - </a-steps>  
15 - </div>  
16 - <div class="mt-5">  
17 - <DeviceConfigurationStep  
18 - v-show="current === 0"  
19 - ref="DevConStRef"  
20 - @next="handleStepNext(true, null)"  
21 - />  
22 - <TransportConfigurationStep  
23 - v-show="current === 1"  
24 - ref="TransConStRef"  
25 - @prev="handleStepPrev"  
26 - />  
27 - </div> 11 + <Tabs
  12 + type="card"
  13 + :animated="true"
  14 + v-model:activeKey="activeKey"
  15 + :size="size"
  16 + @change="handleChange"
  17 + >
  18 + <TabPane forceRender key="1" tab="设备配置">
  19 + <DeviceConfigurationStep v-show="activeKey === '1'" ref="DevConStRef" />
  20 + </TabPane>
  21 + <TabPane forceRender key="2" tab="传输配置">
  22 + <TransportConfigurationStep v-show="activeKey === '2'" ref="TransConStRef" />
  23 + </TabPane>
  24 + <TabPane forceRender key="3" v-show="activeKey === '3'" tab="物模型管理">
  25 + <PhysicalModelManagementStep v-show="activeKey === '3'" ref="PhysicalModManRef" />
  26 + </TabPane>
  27 + </Tabs>
28 </BasicModal> 28 </BasicModal>
29 </div> 29 </div>
30 </template> 30 </template>
31 <script lang="ts" setup> 31 <script lang="ts" setup>
32 import { ref, unref, reactive } from 'vue'; 32 import { ref, unref, reactive } from 'vue';
33 import { BasicModal, useModalInner } from '/@/components/Modal'; 33 import { BasicModal, useModalInner } from '/@/components/Modal';
34 - import DeviceConfigurationStep from './step/DeviceConfigurationStep.vue';  
35 - import TransportConfigurationStep from './step/TransportConfigurationStep.vue';  
36 import { deviceConfigAddOrEdit, deviceConfigGetDetail } from '/@/api/device/deviceConfigApi'; 34 import { deviceConfigAddOrEdit, deviceConfigGetDetail } from '/@/api/device/deviceConfigApi';
37 import { useMessage } from '/@/hooks/web/useMessage'; 35 import { useMessage } from '/@/hooks/web/useMessage';
38 - import { steps } from './device.profile.data';  
39 import { isEmpty } from '/@/utils/is'; 36 import { isEmpty } from '/@/utils/is';
  37 + import { Tabs, TabPane } from 'ant-design-vue';
  38 + import DeviceConfigurationStep from './step/DeviceConfigurationStep.vue';
  39 + import TransportConfigurationStep from './step/TransportConfigurationStep.vue';
  40 + import PhysicalModelManagementStep from './step/PhysicalModelManagementStep.vue';
40 41
41 const emits = defineEmits(['success', 'register']); 42 const emits = defineEmits(['success', 'register']);
  43 + const activeKey = ref('1');
  44 + const size = ref('small');
42 const isEditId = ref(''); 45 const isEditId = ref('');
43 const isEditCreatTime = ref(''); 46 const isEditCreatTime = ref('');
44 const { createMessage } = useMessage(); 47 const { createMessage } = useMessage();
45 const isViewDetail = ref(false); 48 const isViewDetail = ref(false);
46 const isUpdate = ref(false); 49 const isUpdate = ref(false);
47 - const current = ref(0);  
48 const DevConStRef = ref<InstanceType<typeof DeviceConfigurationStep>>(); 50 const DevConStRef = ref<InstanceType<typeof DeviceConfigurationStep>>();
49 const TransConStRef = ref<InstanceType<typeof TransportConfigurationStep>>(); 51 const TransConStRef = ref<InstanceType<typeof TransportConfigurationStep>>();
  52 + const PhysicalModManRef = ref<InstanceType<typeof PhysicalModelManagementStep>>();
50 const postSubmitFormData: any = reactive({ 53 const postSubmitFormData: any = reactive({
51 deviceConfData: {}, 54 deviceConfData: {},
52 }); 55 });
@@ -56,7 +59,6 @@ @@ -56,7 +59,6 @@
56 const transportTypeStr = ref(''); 59 const transportTypeStr = ref('');
57 const [register, { closeModal, setModalProps }] = useModalInner(async (data) => { 60 const [register, { closeModal, setModalProps }] = useModalInner(async (data) => {
58 setModalProps({ confirmLoading: false }); 61 setModalProps({ confirmLoading: false });
59 - current.value = 0;  
60 isUpdate.value = data.isUpdate; 62 isUpdate.value = data.isUpdate;
61 isViewDetail.value = data.isView; 63 isViewDetail.value = data.isView;
62 const res = data.record !== undefined ? await deviceConfigGetDetail(data.record.id) : {}; 64 const res = data.record !== undefined ? await deviceConfigGetDetail(data.record.id) : {};
@@ -77,16 +79,17 @@ @@ -77,16 +79,17 @@
77 handleStepNext(false, res); 79 handleStepNext(false, res);
78 } 80 }
79 }); 81 });
  82 + const handleChange = (e) => {
  83 + if (e == '2') {
  84 + handleStepNext(true, null);
  85 + }
  86 + };
80 const handleStepNext = (e, data) => { 87 const handleStepNext = (e, data) => {
81 if (e) { 88 if (e) {
82 - current.value++;  
83 } else { 89 } else {
84 setTransConfEditFormData(data); 90 setTransConfEditFormData(data);
85 } 91 }
86 }; 92 };
87 - const handleStepPrev = () => {  
88 - current.value--;  
89 - };  
90 93
91 const setDeviceConfEditFormData = async (res) => { 94 const setDeviceConfEditFormData = async (res) => {
92 await DevConStRef.value?.setFormData(res); 95 await DevConStRef.value?.setFormData(res);
@@ -122,6 +125,7 @@ @@ -122,6 +125,7 @@
122 125
123 const handleCancel = () => { 126 const handleCancel = () => {
124 closeModal(); 127 closeModal();
  128 + activeKey.value = '1';
125 DevConStRef.value?.resetFormData(); 129 DevConStRef.value?.resetFormData();
126 TransConStRef.value?.resetFormData(); 130 TransConStRef.value?.resetFormData();
127 }; 131 };
@@ -3,9 +3,56 @@ import { FormSchema } from '/@/components/Table'; @@ -3,9 +3,56 @@ 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';
6 8
7 import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi'; 9 import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi';
8 10
  11 +export const physicalColumn: BasicColumn[] = [
  12 + {
  13 + title: '功能名称',
  14 + dataIndex: 'name',
  15 + width: 80,
  16 + },
  17 + {
  18 + title: '标识符',
  19 + dataIndex: 'type',
  20 + width: 80,
  21 + },
  22 + {
  23 + title: '数据类型',
  24 + dataIndex: 'transportType',
  25 + width: 80,
  26 + },
  27 + {
  28 + title: '单位',
  29 + dataIndex: 'description1',
  30 + width: 70,
  31 + },
  32 + {
  33 + title: '读写类型',
  34 + dataIndex: 'default',
  35 + width: 70,
  36 + customRender: ({ record }) => {
  37 + const status = record.actionStatus;
  38 + const enable = status === 'SUCCESS' ? '读写' : '只读';
  39 + const color = enable === '读写' ? 'blue' : 'blue';
  40 + const text = enable === '读写' ? '读写' : '只读';
  41 + return h(Tag, { color }, () => text);
  42 + },
  43 + },
  44 + {
  45 + title: '创建人',
  46 + dataIndex: 'description',
  47 + width: 90,
  48 + },
  49 + {
  50 + title: '创建时间',
  51 + dataIndex: 'createTime',
  52 + width: 140,
  53 + },
  54 +];
  55 +
9 export const step1Schemas: FormSchema[] = [ 56 export const step1Schemas: FormSchema[] = [
10 { 57 {
11 field: 'image', 58 field: 'image',
@@ -18,6 +65,7 @@ export const step1Schemas: FormSchema[] = [ @@ -18,6 +65,7 @@ export const step1Schemas: FormSchema[] = [
18 label: '配置名称', 65 label: '配置名称',
19 required: true, 66 required: true,
20 component: 'Input', 67 component: 'Input',
  68 + colProps: { span: 14 },
21 componentProps() { 69 componentProps() {
22 return { 70 return {
23 disabled: false, 71 disabled: false,
@@ -30,6 +78,8 @@ export const step1Schemas: FormSchema[] = [ @@ -30,6 +78,8 @@ export const step1Schemas: FormSchema[] = [
30 field: 'defaultRuleChainId', 78 field: 'defaultRuleChainId',
31 label: '规则链', 79 label: '规则链',
32 component: 'ApiSelect', 80 component: 'ApiSelect',
  81 + colProps: { span: 14 },
  82 +
33 componentProps: { 83 componentProps: {
34 api: async () => { 84 api: async () => {
35 const data = await deviceConfigGetRuleChain(); 85 const data = await deviceConfigGetRuleChain();
@@ -50,6 +100,8 @@ export const step1Schemas: FormSchema[] = [ @@ -50,6 +100,8 @@ export const step1Schemas: FormSchema[] = [
50 field: 'defaultQueueName', 100 field: 'defaultQueueName',
51 label: '处理队列', 101 label: '处理队列',
52 component: 'ApiSelect', 102 component: 'ApiSelect',
  103 + colProps: { span: 14 },
  104 +
53 componentProps: { 105 componentProps: {
54 api: findDictItemByCode, 106 api: findDictItemByCode,
55 params: { 107 params: {
@@ -64,10 +116,12 @@ export const step1Schemas: FormSchema[] = [ @@ -64,10 +116,12 @@ export const step1Schemas: FormSchema[] = [
64 { 116 {
65 label: '描述', 117 label: '描述',
66 field: 'description', 118 field: 'description',
  119 + colProps: { span: 14 },
67 component: 'InputTextArea', 120 component: 'InputTextArea',
68 componentProps: { 121 componentProps: {
69 maxLength: 255, 122 maxLength: 255,
70 placeholder: '请输入描述', 123 placeholder: '请输入描述',
  124 + rows: 6,
71 }, 125 },
72 }, 126 },
73 ]; 127 ];
@@ -132,16 +186,6 @@ export const columns: BasicColumn[] = [ @@ -132,16 +186,6 @@ export const columns: BasicColumn[] = [
132 width: 120, 186 width: 120,
133 }, 187 },
134 ]; 188 ];
135 -export const steps = [  
136 - {  
137 - title: '设备配置',  
138 - content: 'First-content',  
139 - },  
140 - {  
141 - title: '传输配置',  
142 - content: 'Second-content',  
143 - },  
144 -];  
145 189
146 export const defaultObj = { 190 export const defaultObj = {
147 headers: { 191 headers: {
1 <template> 1 <template>
2 <div class="step1"> 2 <div class="step1">
3 - <div class="step1-form">  
4 - <div>  
5 - <BasicForm @register="register">  
6 - <template #imageSelect>  
7 - <Upload  
8 - style="width: 20vw"  
9 - name="avatar"  
10 - list-type="picture-card"  
11 - class="avatar-uploader"  
12 - :show-upload-list="false"  
13 - :customRequest="customUploadqrcodePic"  
14 - :before-upload="beforeUploadqrcodePic"  
15 - >  
16 - <img  
17 - v-if="deviceConfigPic"  
18 - :src="deviceConfigPic"  
19 - alt=""  
20 - style="width: 6.25rem; height: 6.25rem"  
21 - />  
22 - <div v-else>  
23 - <LoadingOutlined v-if="loading" />  
24 - <PlusOutlined v-else />  
25 - <div class="ant-upload-text">图片上传</div>  
26 - </div>  
27 - </Upload>  
28 - </template>  
29 - </BasicForm>  
30 - </div>  
31 - </div> 3 + <BasicForm @register="register">
  4 + <template #imageSelect>
  5 + <Upload
  6 + style="width: 20vw"
  7 + name="avatar"
  8 + list-type="picture-card"
  9 + class="avatar-uploader"
  10 + :show-upload-list="false"
  11 + :customRequest="customUploadqrcodePic"
  12 + :before-upload="beforeUploadqrcodePic"
  13 + >
  14 + <img
  15 + v-if="deviceConfigPic"
  16 + :src="deviceConfigPic"
  17 + alt=""
  18 + style="width: 6.25rem; height: 6.25rem"
  19 + />
  20 + <div v-else>
  21 + <LoadingOutlined v-if="loading" />
  22 + <PlusOutlined v-else />
  23 + <div class="ant-upload-text">图片上传</div>
  24 + </div>
  25 + </Upload>
  26 + </template>
  27 + </BasicForm>
32 </div> 28 </div>
33 </template> 29 </template>
34 <script lang="ts" setup> 30 <script lang="ts" setup>
@@ -41,7 +37,6 @@ @@ -41,7 +37,6 @@
41 import { useMessage } from '/@/hooks/web/useMessage'; 37 import { useMessage } from '/@/hooks/web/useMessage';
42 import type { FileItem } from '/@/components/Upload/src/typing'; 38 import type { FileItem } from '/@/components/Upload/src/typing';
43 39
44 - const emits = defineEmits(['next']);  
45 const loading = ref(false); 40 const loading = ref(false);
46 const { createMessage } = useMessage(); 41 const { createMessage } = useMessage();
47 const deviceConfigPic = ref(''); 42 const deviceConfigPic = ref('');
@@ -53,10 +48,8 @@ @@ -53,10 +48,8 @@
53 span: 14, 48 span: 14,
54 }, 49 },
55 showResetButton: false, 50 showResetButton: false,
56 - submitButtonOptions: {  
57 - text: '下一步',  
58 - },  
59 - submitFunc: customSubmitFunc, 51 + submitOnReset: false,
  52 + showActionButtonGroup: false,
60 }); 53 });
61 const editOrAddNameStatus = (nameStatus) => 54 const editOrAddNameStatus = (nameStatus) =>
62 updateSchema({ 55 updateSchema({
@@ -95,11 +88,6 @@ @@ -95,11 +88,6 @@
95 setFieldsValue(v); 88 setFieldsValue(v);
96 deviceConfigPic.value = v.image; 89 deviceConfigPic.value = v.image;
97 }; 90 };
98 - async function customSubmitFunc() {  
99 - const values = await validate();  
100 - if (!values) return;  
101 - emits('next', true, null);  
102 - }  
103 //获取数据 91 //获取数据
104 async function getFormData() { 92 async function getFormData() {
105 const values = await validate(); 93 const values = await validate();
  1 +<template>
  2 + <div>
  3 + <BasicTable
  4 + :rowSelection="{ type: 'checkbox' }"
  5 + :clickToRowSelect="false"
  6 + @register="registerTable"
  7 + >
  8 + <template #toolbar>
  9 + <div style="display: flex; justify-content: space-between; width: 773px">
  10 + <div>
  11 + <Authority value="">
  12 + <div style="display: flex">
  13 + <a-button
  14 + style="margin-left: -20px"
  15 + type="primary"
  16 + @click="handleCreateOrEdit(null)"
  17 + >
  18 + 新增物模型
  19 + </a-button>
  20 + <a-button class="ml-2" type="primary" @click="handleOpenTsl"> 物模型TSL </a-button>
  21 + </div>
  22 + </Authority>
  23 + </div>
  24 + <div>
  25 + <Authority value="">
  26 + <div style="display: flex">
  27 + <a-button type="primary"> 发布上线 </a-button>
  28 + <a-button class="ml-2" type="text"> 返回 </a-button>
  29 + <Popconfirm
  30 + title="您确定要批量删除数据"
  31 + ok-text="确定"
  32 + cancel-text="取消"
  33 + @confirm="handleDeleteOrBatchDelete(null)"
  34 + >
  35 + <a-button
  36 + style="display: none"
  37 + type="primary"
  38 + color="error"
  39 + :disabled="hasBatchDelete"
  40 + >
  41 + 批量删除
  42 + </a-button>
  43 + </Popconfirm>
  44 + </div>
  45 + </Authority>
  46 + </div>
  47 + </div>
  48 + </template>
  49 + <template #action="{ record }">
  50 + <TableAction
  51 + :actions="[
  52 + {
  53 + label: '查看',
  54 + icon: 'ant-design:eye-outlined',
  55 + onClick: handleViewDetail.bind(null, record),
  56 + },
  57 + {
  58 + label: '编辑',
  59 + icon: 'clarity:note-edit-line',
  60 + auth: '',
  61 + onClick: handleCreateOrEdit.bind(null, record),
  62 + },
  63 + {
  64 + label: '删除',
  65 + icon: 'ant-design:delete-outlined',
  66 + auth: '',
  67 + color: 'error',
  68 + popConfirm: {
  69 + title: '是否确认删除',
  70 + confirm: handleDeleteOrBatchDelete.bind(null, record),
  71 + },
  72 + },
  73 + ]"
  74 + />
  75 + </template>
  76 + </BasicTable>
  77 + <PhysicalModelModal @register="registerModal" @success="handleSuccess" />
  78 + <PhysicalModelTsl @register="registerModalTsl" />
  79 + </div>
  80 +</template>
  81 +<script lang="ts" setup>
  82 + import { BasicTable, useTable, TableAction } from '/@/components/Table';
  83 + import { useModal } from '/@/components/Modal';
  84 + import { physicalColumn } from '../device.profile.data';
  85 + import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
  86 + import { deleteReportManage } from '/@/api/report/reportManager';
  87 + import { Authority } from '/@/components/Authority';
  88 + import { deviceConfigGetQuery } from '/@/api/device/deviceConfigApi';
  89 + import PhysicalModelModal from './cpns/physical/PhysicalModelModal.vue';
  90 + import PhysicalModelTsl from './cpns/physical/PhysicalModelTsl.vue';
  91 + import { Popconfirm } from 'ant-design-vue';
  92 +
  93 + defineEmits(['register']);
  94 + const [registerModal, { openModal }] = useModal();
  95 + const [registerModalTsl, { openModal: openModalTsl }] = useModal();
  96 +
  97 + const [registerTable, { reload, setProps }] = useTable({
  98 + api: deviceConfigGetQuery,
  99 + columns: physicalColumn,
  100 + showIndexColumn: false,
  101 + clickToRowSelect: false,
  102 + useSearchForm: false,
  103 + rowKey: 'id',
  104 + showTableSetting: true,
  105 + bordered: true,
  106 + actionColumn: {
  107 + width: 200,
  108 + title: '操作',
  109 + dataIndex: 'action',
  110 + slots: { customRender: 'action' },
  111 + fixed: 'right',
  112 + },
  113 + });
  114 + // 刷新
  115 + const handleSuccess = () => {
  116 + reload();
  117 + };
  118 +
  119 + const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(
  120 + deleteReportManage,
  121 + handleSuccess,
  122 + setProps
  123 + );
  124 + selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => {
  125 + // Demo:status为1的选择框禁用
  126 + if (record.status === 1) {
  127 + return { disabled: true };
  128 + } else {
  129 + return { disabled: false };
  130 + }
  131 + };
  132 + const handleViewDetail = (record: Recordable | null) => {
  133 + if (record) {
  134 + openModal(true, {
  135 + isUpdate: true,
  136 + record,
  137 + isView: true,
  138 + });
  139 + }
  140 + };
  141 + // 新增或编辑
  142 + const handleCreateOrEdit = (record: Recordable | null) => {
  143 + if (record) {
  144 + openModal(true, {
  145 + isUpdate: false,
  146 + record,
  147 + isView: false,
  148 + });
  149 + } else {
  150 + openModal(true, {
  151 + isUpdate: true,
  152 + isView: false,
  153 + });
  154 + }
  155 + };
  156 + const handleOpenTsl = () => {
  157 + openModalTsl(true, {
  158 + isUpdate: true,
  159 + });
  160 + };
  161 + defineExpose({});
  162 +</script>
  163 +<style lang="less" scoped>
  164 + :deep(.ant-table-body) {
  165 + height: auto !important;
  166 + }
  167 +</style>
@@ -29,13 +29,6 @@ @@ -29,13 +29,6 @@
29 <div style="margin-top: 5vh" v-else-if="isMqttType == 'SNMP'"> 29 <div style="margin-top: 5vh" v-else-if="isMqttType == 'SNMP'">
30 <SnmpCpns ref="snmpRef" /> 30 <SnmpCpns ref="snmpRef" />
31 </div> 31 </div>
32 - <div class="btn-style">  
33 - <div style="display: flex; width: 4vw; height: 4vh; margin-top: 1.65vh">  
34 - <Button type="default" style="border-radius: 2px" class="mt-5" @click="customResetFunc"  
35 - >上一步</Button  
36 - >  
37 - </div>  
38 - </div>  
39 </div> 32 </div>
40 </div> 33 </div>
41 </template> 34 </template>
@@ -43,13 +36,11 @@ @@ -43,13 +36,11 @@
43 import { reactive, ref, onUnmounted, nextTick } from 'vue'; 36 import { reactive, ref, onUnmounted, nextTick } from 'vue';
44 import { BasicForm, useForm } from '/@/components/Form'; 37 import { BasicForm, useForm } from '/@/components/Form';
45 import { step2Schemas } from '../device.profile.data'; 38 import { step2Schemas } from '../device.profile.data';
46 - import { Button } from '/@/components/Button';  
47 import MqttCpns from './cpns/mqtt/Mqtt.vue'; 39 import MqttCpns from './cpns/mqtt/Mqtt.vue';
48 import CoapCpns from './cpns/coap/Coap.vue'; 40 import CoapCpns from './cpns/coap/Coap.vue';
49 import Lwm2mCpns from './cpns/lwm2m/index.vue'; 41 import Lwm2mCpns from './cpns/lwm2m/index.vue';
50 import SnmpCpns from './cpns/snmp/index.vue'; 42 import SnmpCpns from './cpns/snmp/index.vue';
51 43
52 - const emits = defineEmits(['prev']);  
53 const mqttRef = ref<InstanceType<typeof MqttCpns>>(); 44 const mqttRef = ref<InstanceType<typeof MqttCpns>>();
54 const coapRef = ref<InstanceType<typeof CoapCpns>>(); 45 const coapRef = ref<InstanceType<typeof CoapCpns>>();
55 const lwm2mRef = ref<InstanceType<typeof Lwm2mCpns>>(); 46 const lwm2mRef = ref<InstanceType<typeof Lwm2mCpns>>();
@@ -64,6 +55,9 @@ @@ -64,6 +55,9 @@
64 actionColOptions: { 55 actionColOptions: {
65 span: 14, 56 span: 14,
66 }, 57 },
  58 + showResetButton: false,
  59 + submitOnReset: false,
  60 + showActionButtonGroup: false,
67 }); 61 });
68 const setFormData = (v) => { 62 const setFormData = (v) => {
69 setFieldsValue({ 63 setFieldsValue({
@@ -86,9 +80,6 @@ @@ -86,9 +80,6 @@
86 snmpRef.value?.resetFormData(); 80 snmpRef.value?.resetFormData();
87 }); 81 });
88 }; 82 };
89 - async function customResetFunc() {  
90 - emits('prev');  
91 - }  
92 nextTick(() => { 83 nextTick(() => {
93 updateSchema({ 84 updateSchema({
94 field: 'transportType', 85 field: 'transportType',
  1 +<template>
  2 + <div>
  3 + <BasicModal
  4 + :maskClosable="false"
  5 + v-bind="$attrs"
  6 + width="55rem"
  7 + @register="register"
  8 + @ok="handleSubmit"
  9 + @cancel="handleCancel"
  10 + >
  11 + <Tabs v-model:activeKey="activeKey" :size="size">
  12 + <TabPane key="1" tab="属性">
  13 + <Attr v-show="activeKey === '1'" ref="AttrRef" />
  14 + </TabPane>
  15 + <TabPane disabled key="2" tab="服务">
  16 + <Service v-show="activeKey === '2'" ref="ServiceRef" />
  17 + </TabPane>
  18 + <TabPane disabled key="3" v-show="activeKey === '3'" tab="事件">
  19 + <Events v-show="activeKey === '3'" ref="EventsRef" />
  20 + </TabPane>
  21 + </Tabs>
  22 + </BasicModal>
  23 + </div>
  24 +</template>
  25 +<script lang="ts" setup>
  26 + import { ref, unref } from 'vue';
  27 + import { BasicModal, useModalInner } from '/@/components/Modal';
  28 + import { Tabs, TabPane } from 'ant-design-vue';
  29 + import Attr from './cpns/Attr.vue';
  30 + import Service from './cpns/Service.vue';
  31 + import Events from './cpns/Events.vue';
  32 +
  33 + defineEmits(['register']);
  34 + const activeKey = ref('1');
  35 + const size = ref('small');
  36 + const AttrRef = ref<InstanceType<typeof Attr>>();
  37 + const ServiceRef = ref<InstanceType<typeof Service>>();
  38 + const EventsRef = ref<InstanceType<typeof Events>>();
  39 + const isUpdate = ref(false);
  40 + const isViewDetail = ref('');
  41 + const [register, { closeModal, setModalProps }] = useModalInner(async (data) => {
  42 + setModalProps({ loading: true });
  43 + isUpdate.value = data.isUpdate;
  44 + isViewDetail.value = data.isView;
  45 + // AttrRef.value?.setFormData()
  46 + setModalProps({ loading: false });
  47 + if (!unref(isViewDetail)) {
  48 + const title = !unref(isUpdate) ? '编辑物模型' : '新增物模型';
  49 + setModalProps({ title, showOkBtn: true, showCancelBtn: true });
  50 + if (!unref(isUpdate)) {
  51 + }
  52 + } else {
  53 + setModalProps({ showOkBtn: false, showCancelBtn: false, title: '查看物模型' });
  54 + }
  55 + });
  56 + const handleCancel = () => {
  57 + AttrRef.value?.resetFormData();
  58 + closeModal();
  59 + };
  60 + const handleSubmit = async () => {
  61 + const value = await AttrRef.value?.getFormData();
  62 + if (!value) return;
  63 + console.log('搜集值', value);
  64 + };
  65 +</script>
  66 +
  67 +<style lang="less" scope></style>
  1 +<template>
  2 + <div>
  3 + <BasicModal
  4 + title="物模型TSL"
  5 + :maskClosable="false"
  6 + v-bind="$attrs"
  7 + width="55rem"
  8 + @register="register"
  9 + @ok="handleSubmit"
  10 + @cancel="handleCancel"
  11 + >
  12 + <TslContent ref="TslConRef" />
  13 + </BasicModal>
  14 + </div>
  15 +</template>
  16 +<script lang="ts" setup>
  17 + import { ref } from 'vue';
  18 + import { BasicModal, useModalInner } from '/@/components/Modal';
  19 + // import { useMessage } from '/@/hooks/web/useMessage';
  20 + import TslContent from './cpns/TslContent.vue';
  21 +
  22 + defineEmits(['register']);
  23 + // const { createMessage } = useMessage();
  24 + const TslConRef = ref<InstanceType<typeof TslContent>>();
  25 +
  26 + const [register, { closeModal, setModalProps }] = useModalInner(async (data) => {
  27 + setModalProps({ confirmLoading: true });
  28 + console.log(data);
  29 + setModalProps({ confirmLoading: false });
  30 + });
  31 + const handleCancel = () => {
  32 + TslConRef.value?.resetFormData();
  33 + closeModal();
  34 + };
  35 +
  36 + const handleSubmit = () => {
  37 + const value = TslConRef.value?.getFormData();
  38 + if (!value) return;
  39 + console.log('搜集值', value);
  40 + };
  41 +</script>
  42 +
  43 +<style lang="less" scope>
  44 + #jsoneditor {
  45 + width: 100%;
  46 + height: 450px;
  47 + }
  48 +</style>
  1 +<template>
  2 + <div>
  3 + <BasicForm @register="register" />
  4 + </div>
  5 +</template>
  6 +<script lang="ts" setup>
  7 + import { BasicForm, useForm } from '/@/components/Form';
  8 + import { attrSchemas } from './config';
  9 +
  10 + const [register, { validate, setFieldsValue, resetFields }] = useForm({
  11 + labelWidth: 100,
  12 + schemas: attrSchemas,
  13 + actionColOptions: {
  14 + span: 14,
  15 + },
  16 + showResetButton: false,
  17 + submitOnReset: false,
  18 + showActionButtonGroup: false,
  19 + });
  20 +
  21 + //回显数据
  22 + const setFormData = (v) => {
  23 + setFieldsValue(v);
  24 + };
  25 + //获取数据
  26 + async function getFormData() {
  27 + const values = await validate();
  28 + if (!values) return;
  29 + return values;
  30 + }
  31 + //清空数据
  32 + const resetFormData = () => {
  33 + resetFields();
  34 + };
  35 +
  36 + defineExpose({
  37 + setFormData,
  38 + resetFormData,
  39 + getFormData,
  40 + });
  41 +</script>
  42 +<style lang="less" scoped></style>
  1 +<template>
  2 + <div>
  3 + <div style="display: flex; justify-content: space-between">
  4 + <div>模型内容</div>
  5 + <div>
  6 + <Button @click="handlePremitter">
  7 + <template #icon>
  8 + <SortAscendingOutlined />
  9 + </template>
  10 + 格式化
  11 + </Button>
  12 + <Button class="ml-2" @click="handleCopy">
  13 + <template #icon>
  14 + <CopyOutlined />
  15 + </template>
  16 + copy
  17 + </Button>
  18 + </div>
  19 + </div>
  20 + <div class="mt-4">
  21 + <div id="jsoneditor" ref="jsoneditorRef"></div>
  22 + </div>
  23 + </div>
  24 +</template>
  25 +<script lang="ts" setup>
  26 + import { ref, unref, onMounted, nextTick } from 'vue';
  27 + import { CopyOutlined, SortAscendingOutlined } from '@ant-design/icons-vue';
  28 + import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
  29 + import { useMessage } from '/@/hooks/web/useMessage';
  30 + import jsoneditor from 'jsoneditor';
  31 + import 'jsoneditor/dist/jsoneditor.min.css';
  32 + import { Button } from 'ant-design-vue';
  33 + import { defaultTslContent } from './config';
  34 +
  35 + const { createMessage } = useMessage();
  36 + const jsonValue = ref(defaultTslContent);
  37 + const jsonInstance = ref();
  38 + const jsoneditorRef = ref();
  39 + onMounted(() => {
  40 + nextTick(() => {
  41 + let options = {
  42 + mode: 'code',
  43 + mainMenuBar: false,
  44 + statusBar: false,
  45 + };
  46 + let editor = new jsoneditor(jsoneditorRef.value, options);
  47 + editor.set(jsonValue.value);
  48 + jsonInstance.value = editor;
  49 + });
  50 + });
  51 + const { clipboardRef, copiedRef } = useCopyToClipboard();
  52 + const handleCopy = () => {
  53 + try {
  54 + const valueRef = unref(jsonInstance).get();
  55 + const value = JSON.stringify(unref(valueRef));
  56 + if (!value) {
  57 + createMessage.warning('请输入要拷贝的内容!');
  58 + return;
  59 + }
  60 + clipboardRef.value = value;
  61 + if (unref(copiedRef)) {
  62 + createMessage.success('复制成功!');
  63 + }
  64 + } catch (e) {
  65 + console.log(e);
  66 + }
  67 + };
  68 +
  69 + const getFormData = () => {
  70 + const value = unref(jsonInstance).get();
  71 + if (!value) return;
  72 + return value;
  73 + };
  74 + const resetFormData = () => {
  75 + unref(jsonInstance).set(defaultTslContent);
  76 + };
  77 + const handlePremitter = () => {
  78 + const value = unref(jsonInstance).get();
  79 + return unref(jsonInstance).set(value);
  80 + };
  81 + defineExpose({
  82 + getFormData,
  83 + resetFormData,
  84 + });
  85 +</script>
  86 +
  87 +<style lang="less" scope>
  88 + #jsoneditor {
  89 + width: 100%;
  90 + height: 450px;
  91 + }
  92 +</style>
  1 +import { FormSchema } from '/@/components/Table';
  2 +import { findDictItemByCode } from '/@/api/system/dict';
  3 +
  4 +export const defaultTslContent = {
  5 + schema: 'https://iotx-tsl.oss-ap-southeast-1.aliyuncs.com/schema.json',
  6 + profile: {
  7 + version: '1.5',
  8 + productKey: 'glzlnU7azMO',
  9 + },
  10 + properties: [
  11 + {
  12 + identifier: 'LightStatus',
  13 + name: '工作状态',
  14 + accessMode: 'rw',
  15 + required: true,
  16 + dataType: {
  17 + type: 'bool',
  18 + specs: {
  19 + '0': '关闭',
  20 + '1': '打开',
  21 + },
  22 + },
  23 + },
  24 + ],
  25 +};
  26 +
  27 +export const attrSchemas: FormSchema[] = [
  28 + {
  29 + field: 'configName',
  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: 'configName1',
  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: 'platformType',
  56 + label: '数据类型',
  57 + required: true,
  58 + component: 'ApiSelect',
  59 + colProps: {
  60 + span: 9,
  61 + },
  62 + componentProps: {
  63 + placeholder: '请选择数据类型',
  64 + api: findDictItemByCode,
  65 + params: {
  66 + dictCode: 'data_type',
  67 + },
  68 + labelField: 'itemText',
  69 + valueField: 'itemValue',
  70 + },
  71 + },
  72 + {
  73 + field: 'accessKeyId',
  74 + label: '单位',
  75 + required: true,
  76 + component: 'ApiSelect',
  77 + colProps: {
  78 + span: 9,
  79 + },
  80 + componentProps: {
  81 + placeholder: '请选择单位',
  82 + api: findDictItemByCode,
  83 + params: {
  84 + dictCode: 'attribute_unit',
  85 + },
  86 + labelField: 'itemText',
  87 + valueField: 'itemValue',
  88 + },
  89 + },
  90 + {
  91 + field: 'brand',
  92 + component: 'ApiRadioGroup',
  93 + label: '读写类型',
  94 + required: true,
  95 + colProps: {
  96 + span: 24,
  97 + },
  98 + defaultValue: 'readonly',
  99 + componentProps: {
  100 + placeholder: '请选择读写类型',
  101 + api: findDictItemByCode,
  102 + params: {
  103 + dictCode: 'read_write_type',
  104 + },
  105 + labelField: 'itemText',
  106 + valueField: 'itemValue',
  107 + },
  108 + },
  109 + {
  110 + label: '描述',
  111 + field: 'description',
  112 + component: 'InputTextArea',
  113 + componentProps: {
  114 + rows: 6,
  115 + maxLength: 255,
  116 + placeholder: '请输入描述',
  117 + },
  118 + },
  119 +];
@@ -25,6 +25,7 @@ @@ -25,6 +25,7 @@
25 const isViewDetail = ref(''); 25 const isViewDetail = ref('');
26 const [register, { setModalProps, closeModal }] = useModalInner(async (data) => { 26 const [register, { setModalProps, closeModal }] = useModalInner(async (data) => {
27 setModalProps({ loading: true }); 27 setModalProps({ loading: true });
  28 + handleCancel(false);
28 isUpdate.value = data.isUpdate; 29 isUpdate.value = data.isUpdate;
29 isViewDetail.value = data.isView; 30 isViewDetail.value = data.isView;
30 converScriptRef.value?.initEditor(data.record?.configuration?.jsScript); 31 converScriptRef.value?.initEditor(data.record?.configuration?.jsScript);
@@ -41,9 +42,12 @@ @@ -41,9 +42,12 @@
41 const handleSubmit = async () => { 42 const handleSubmit = async () => {
42 const val = await converScriptRef.value?.getFormData(); 43 const val = await converScriptRef.value?.getFormData();
43 console.log(val); 44 console.log(val);
  45 + handleCancel(true);
44 }; 46 };
45 - const handleCancel = () => {  
46 - closeModal(); 47 + const handleCancel = (flag) => {
  48 + if (flag) {
  49 + closeModal();
  50 + }
47 converScriptRef.value?.resetFormData(); 51 converScriptRef.value?.resetFormData();
48 }; 52 };
49 </script> 53 </script>