Showing
16 changed files
with
547 additions
and
107 deletions
... | ... | @@ -8,23 +8,50 @@ |
8 | 8 | @ok="handleSubmit" |
9 | 9 | @cancel="handleCancel" |
10 | 10 | > |
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> | |
11 | + <div v-if="!isViewDetail" 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 v-if="!isViewDetail" 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> | |
28 | + <div v-if="isViewDetail"> | |
29 | + <Tabs | |
30 | + type="card" | |
31 | + :animated="true" | |
32 | + v-model:activeKey="activeKey" | |
33 | + :size="size" | |
34 | + @change="handleChange" | |
35 | + > | |
36 | + <TabPane forceRender key="1" tab="设备配置"> | |
37 | + <DeviceConfigurationStep | |
38 | + :ifShowBtn="isViewDetail ? false : true" | |
39 | + v-show="activeKey === '1'" | |
40 | + ref="DevConStRef" | |
41 | + /> | |
42 | + </TabPane> | |
43 | + <TabPane forceRender key="2" tab="传输配置"> | |
44 | + <TransportConfigurationStep | |
45 | + :ifShowBtn="isViewDetail ? false : true" | |
46 | + v-show="activeKey === '2'" | |
47 | + ref="TransConStRef" | |
48 | + /> | |
49 | + </TabPane> | |
50 | + <TabPane forceRender key="3" v-show="activeKey === '3'" tab="物模型管理"> | |
51 | + <PhysicalModelManagementStep v-show="activeKey === '3'" ref="PhysicalModManRef" /> | |
52 | + </TabPane> | |
53 | + </Tabs> | |
54 | + </div> | |
28 | 55 | </BasicModal> |
29 | 56 | </div> |
30 | 57 | </template> |
... | ... | @@ -33,6 +60,7 @@ |
33 | 60 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
34 | 61 | import { deviceConfigAddOrEdit, deviceConfigGetDetail } from '/@/api/device/deviceConfigApi'; |
35 | 62 | import { useMessage } from '/@/hooks/web/useMessage'; |
63 | + import { steps } from './device.profile.data'; | |
36 | 64 | import { isEmpty } from '/@/utils/is'; |
37 | 65 | import { Tabs, TabPane } from 'ant-design-vue'; |
38 | 66 | import DeviceConfigurationStep from './step/DeviceConfigurationStep.vue'; |
... | ... | @@ -47,6 +75,7 @@ |
47 | 75 | const { createMessage } = useMessage(); |
48 | 76 | const isViewDetail = ref(false); |
49 | 77 | const isUpdate = ref(false); |
78 | + const current = ref(0); | |
50 | 79 | const DevConStRef = ref<InstanceType<typeof DeviceConfigurationStep>>(); |
51 | 80 | const TransConStRef = ref<InstanceType<typeof TransportConfigurationStep>>(); |
52 | 81 | const PhysicalModManRef = ref<InstanceType<typeof PhysicalModelManagementStep>>(); |
... | ... | @@ -59,6 +88,7 @@ |
59 | 88 | const transportTypeStr = ref(''); |
60 | 89 | const [register, { closeModal, setModalProps }] = useModalInner(async (data) => { |
61 | 90 | setModalProps({ confirmLoading: false }); |
91 | + current.value = 0; | |
62 | 92 | isUpdate.value = data.isUpdate; |
63 | 93 | isViewDetail.value = data.isView; |
64 | 94 | const res = data.record !== undefined ? await deviceConfigGetDetail(data.record.id) : {}; |
... | ... | @@ -73,7 +103,7 @@ |
73 | 103 | handleStepNext(false, res); |
74 | 104 | } |
75 | 105 | } else { |
76 | - setModalProps({ showOkBtn: false, showCancelBtn: false, title: '查看设备配置' }); | |
106 | + setModalProps({ showOkBtn: false, showCancelBtn: false, title: '设备配置详情' }); | |
77 | 107 | await setDeviceConfEditFormData(res); |
78 | 108 | await setTransConfEditFormData(res); |
79 | 109 | handleStepNext(false, res); |
... | ... | @@ -86,10 +116,14 @@ |
86 | 116 | }; |
87 | 117 | const handleStepNext = (e, data) => { |
88 | 118 | if (e) { |
119 | + current.value++; | |
89 | 120 | } else { |
90 | 121 | setTransConfEditFormData(data); |
91 | 122 | } |
92 | 123 | }; |
124 | + const handleStepPrev = () => { | |
125 | + current.value--; | |
126 | + }; | |
93 | 127 | |
94 | 128 | const setDeviceConfEditFormData = async (res) => { |
95 | 129 | await DevConStRef.value?.setFormData(res); | ... | ... |
... | ... | @@ -8,6 +8,17 @@ import { h } from 'vue'; |
8 | 8 | |
9 | 9 | import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi'; |
10 | 10 | |
11 | +export const steps = [ | |
12 | + { | |
13 | + title: '设备配置', | |
14 | + content: 'First-content', | |
15 | + }, | |
16 | + { | |
17 | + title: '传输配置', | |
18 | + content: 'Second-content', | |
19 | + }, | |
20 | +]; | |
21 | + | |
11 | 22 | export const physicalColumn: BasicColumn[] = [ |
12 | 23 | { |
13 | 24 | title: '功能名称', |
... | ... | @@ -61,6 +72,24 @@ export const step1Schemas: FormSchema[] = [ |
61 | 72 | slot: 'imageSelect', |
62 | 73 | }, |
63 | 74 | { |
75 | + field: 'brand', | |
76 | + component: 'ApiRadioGroup', | |
77 | + label: '设备类型', | |
78 | + required: true, | |
79 | + colProps: { | |
80 | + span: 14, | |
81 | + }, | |
82 | + defaultValue: 'GATEWAY', | |
83 | + componentProps: { | |
84 | + api: findDictItemByCode, | |
85 | + params: { | |
86 | + dictCode: 'device_type', | |
87 | + }, | |
88 | + labelField: 'itemText', | |
89 | + valueField: 'itemValue', | |
90 | + }, | |
91 | + }, | |
92 | + { | |
64 | 93 | field: 'name', |
65 | 94 | label: '配置名称', |
66 | 95 | required: true, | ... | ... |
... | ... | @@ -37,9 +37,13 @@ |
37 | 37 | import { useMessage } from '/@/hooks/web/useMessage'; |
38 | 38 | import type { FileItem } from '/@/components/Upload/src/typing'; |
39 | 39 | |
40 | + const emits = defineEmits(['next']); | |
40 | 41 | const loading = ref(false); |
41 | 42 | const { createMessage } = useMessage(); |
42 | 43 | const deviceConfigPic = ref(''); |
44 | + const props = defineProps({ | |
45 | + ifShowBtn: { type: Boolean, default: true }, | |
46 | + }); | |
43 | 47 | |
44 | 48 | const [register, { validate, setFieldsValue, resetFields, updateSchema }] = useForm({ |
45 | 49 | labelWidth: 100, |
... | ... | @@ -48,8 +52,11 @@ |
48 | 52 | span: 14, |
49 | 53 | }, |
50 | 54 | showResetButton: false, |
51 | - submitOnReset: false, | |
52 | - showActionButtonGroup: false, | |
55 | + showActionButtonGroup: props.ifShowBtn ? true : false, | |
56 | + submitButtonOptions: { | |
57 | + text: '下一步', | |
58 | + }, | |
59 | + submitFunc: customSubmitFunc, | |
53 | 60 | }); |
54 | 61 | const editOrAddNameStatus = (nameStatus) => |
55 | 62 | updateSchema({ |
... | ... | @@ -83,6 +90,11 @@ |
83 | 90 | } |
84 | 91 | return isJpgOrPng && isLt2M; |
85 | 92 | }; |
93 | + async function customSubmitFunc() { | |
94 | + const values = await validate(); | |
95 | + if (!values) return; | |
96 | + emits('next', true, null); | |
97 | + } | |
86 | 98 | //回显数据 |
87 | 99 | const setFormData = (v) => { |
88 | 100 | setFieldsValue(v); | ... | ... |
1 | 1 | <template> |
2 | - <div> | |
2 | + <div class="p-style"> | |
3 | 3 | <BasicTable |
4 | 4 | :rowSelection="{ type: 'checkbox' }" |
5 | 5 | :clickToRowSelect="false" |
6 | 6 | @register="registerTable" |
7 | 7 | > |
8 | 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> | |
9 | + <div class="p-column"> | |
10 | + <div class="p-content"> | |
11 | + <a-alert | |
12 | + style="width: 420px" | |
13 | + message="当前展示的是已发布到线上的功能定义,如需修改,请点击" | |
14 | + type="info" | |
15 | + show-icon | |
16 | + /> | |
17 | + <span | |
18 | + @click="handleEditPhysicalModel" | |
19 | + class="ml-2" | |
20 | + style="color: #409eff; cursor: pointer" | |
21 | + type="primary" | |
22 | + size="small" | |
23 | + >“编辑物模型”</span | |
24 | + > | |
23 | 25 | </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 | - > | |
26 | + <div style="height: 20px"></div> | |
27 | + <div class="p-bottom"> | |
28 | + <div> | |
29 | + <Authority value=""> | |
30 | + <div style="display: flex"> | |
35 | 31 | <a-button |
36 | - style="display: none" | |
32 | + v-if="isShowBtn" | |
33 | + style="margin-left: -20px" | |
37 | 34 | type="primary" |
38 | - color="error" | |
39 | - :disabled="hasBatchDelete" | |
35 | + @click="handleCreateOrEdit(null)" | |
40 | 36 | > |
41 | - 批量删除 | |
37 | + 新增物模型 | |
42 | 38 | </a-button> |
43 | - </Popconfirm> | |
44 | - </div> | |
45 | - </Authority> | |
39 | + <a-button | |
40 | + :style="[isShowBtn ? { left: 0 + 'px' } : { left: -29 + 'px' }]" | |
41 | + class="ml-2" | |
42 | + type="primary" | |
43 | + @click="handleOpenTsl" | |
44 | + > | |
45 | + 物模型TSL | |
46 | + </a-button> | |
47 | + </div> | |
48 | + </Authority> | |
49 | + </div> | |
50 | + <div> | |
51 | + <Authority value=""> | |
52 | + <div style="display: flex"> | |
53 | + <Popconfirm | |
54 | + title="是否需要发布上线?" | |
55 | + ok-text="确定" | |
56 | + cancel-text="取消" | |
57 | + @confirm="handleEmit" | |
58 | + > | |
59 | + <a-button v-if="isShowBtn" type="primary"> 发布上线 </a-button> | |
60 | + </Popconfirm> | |
61 | + <a-button | |
62 | + style="background: #d7d7d7" | |
63 | + v-if="isShowBtn" | |
64 | + class="ml-2" | |
65 | + type="text" | |
66 | + @click="handleReturn" | |
67 | + > | |
68 | + 返回 | |
69 | + </a-button> | |
70 | + <Popconfirm | |
71 | + title="您确定要批量删除数据" | |
72 | + ok-text="确定" | |
73 | + cancel-text="取消" | |
74 | + @confirm="handleDeleteOrBatchDelete(null)" | |
75 | + > | |
76 | + <a-button | |
77 | + style="display: none" | |
78 | + type="primary" | |
79 | + color="error" | |
80 | + :disabled="hasBatchDelete" | |
81 | + > | |
82 | + 批量删除 | |
83 | + </a-button> | |
84 | + </Popconfirm> | |
85 | + </div> | |
86 | + </Authority> | |
87 | + </div> | |
46 | 88 | </div> |
47 | 89 | </div> |
48 | 90 | </template> |
... | ... | @@ -53,18 +95,21 @@ |
53 | 95 | label: '查看', |
54 | 96 | icon: 'ant-design:eye-outlined', |
55 | 97 | onClick: handleViewDetail.bind(null, record), |
98 | + ifShow: isShowBtn ? false : true, | |
56 | 99 | }, |
57 | 100 | { |
58 | 101 | label: '编辑', |
59 | 102 | icon: 'clarity:note-edit-line', |
60 | 103 | auth: '', |
61 | 104 | onClick: handleCreateOrEdit.bind(null, record), |
105 | + ifShow: !isShowBtn ? false : true, | |
62 | 106 | }, |
63 | 107 | { |
64 | 108 | label: '删除', |
65 | 109 | icon: 'ant-design:delete-outlined', |
66 | 110 | auth: '', |
67 | 111 | color: 'error', |
112 | + ifShow: !isShowBtn ? false : true, | |
68 | 113 | popConfirm: { |
69 | 114 | title: '是否确认删除', |
70 | 115 | confirm: handleDeleteOrBatchDelete.bind(null, record), |
... | ... | @@ -79,6 +124,7 @@ |
79 | 124 | </div> |
80 | 125 | </template> |
81 | 126 | <script lang="ts" setup> |
127 | + import { ref } from 'vue'; | |
82 | 128 | import { BasicTable, useTable, TableAction } from '/@/components/Table'; |
83 | 129 | import { useModal } from '/@/components/Modal'; |
84 | 130 | import { physicalColumn } from '../device.profile.data'; |
... | ... | @@ -89,8 +135,11 @@ |
89 | 135 | import PhysicalModelModal from './cpns/physical/PhysicalModelModal.vue'; |
90 | 136 | import PhysicalModelTsl from './cpns/physical/PhysicalModelTsl.vue'; |
91 | 137 | import { Popconfirm } from 'ant-design-vue'; |
138 | + import { useMessage } from '/@/hooks/web/useMessage'; | |
92 | 139 | |
93 | 140 | defineEmits(['register']); |
141 | + const { createMessage } = useMessage(); | |
142 | + const isShowBtn = ref(false); | |
94 | 143 | const [registerModal, { openModal }] = useModal(); |
95 | 144 | const [registerModalTsl, { openModal: openModalTsl }] = useModal(); |
96 | 145 | |
... | ... | @@ -158,10 +207,27 @@ |
158 | 207 | isUpdate: true, |
159 | 208 | }); |
160 | 209 | }; |
210 | + const handleEditPhysicalModel = () => (isShowBtn.value = true); | |
211 | + | |
212 | + const handleReturn = () => (isShowBtn.value = false); | |
213 | + const handleEmit = () => { | |
214 | + createMessage.success('发布成功'); | |
215 | + }; | |
216 | + | |
161 | 217 | defineExpose({}); |
162 | 218 | </script> |
163 | 219 | <style lang="less" scoped> |
220 | + @import url('./common/PhysicalModelManagementStep.less'); | |
221 | + | |
164 | 222 | :deep(.ant-table-body) { |
165 | 223 | height: auto !important; |
166 | 224 | } |
225 | + | |
226 | + :deep(.ant-divider) { | |
227 | + margin-top: 58px; | |
228 | + } | |
229 | + | |
230 | + :deep(.table-settings) { | |
231 | + margin-top: 58px; | |
232 | + } | |
167 | 233 | </style> | ... | ... |
... | ... | @@ -13,6 +13,8 @@ |
13 | 13 | ? { minHeight: 55 + 'vh' } |
14 | 14 | : isMqttType == 'SNMP' |
15 | 15 | ? { minHeight: 60 + 'vh' } |
16 | + : isMqttType == 'TCP' | |
17 | + ? { minHeight: 15 + 'vh' } | |
16 | 18 | : { minHeight: 25 + 'vh' }, |
17 | 19 | ]" |
18 | 20 | > |
... | ... | @@ -29,6 +31,16 @@ |
29 | 31 | <div style="margin-top: 5vh" v-else-if="isMqttType == 'SNMP'"> |
30 | 32 | <SnmpCpns ref="snmpRef" /> |
31 | 33 | </div> |
34 | + <div style="margin-top: 5vh; margin-left: -102px" v-else-if="isMqttType == 'TCP'"> | |
35 | + <TcpCpns ref="tcpRef" /> | |
36 | + </div> | |
37 | + <div v-if="ifShowBtn" class="btn-style"> | |
38 | + <div style="display: flex; width: 4vw; height: 4vh; margin-top: 1.65vh; margin-left: 44px"> | |
39 | + <Button type="default" style="border-radius: 2px" class="mt-5" @click="customResetFunc" | |
40 | + >上一步</Button | |
41 | + > | |
42 | + </div> | |
43 | + </div> | |
32 | 44 | </div> |
33 | 45 | </div> |
34 | 46 | </template> |
... | ... | @@ -36,15 +48,22 @@ |
36 | 48 | import { reactive, ref, onUnmounted, nextTick } from 'vue'; |
37 | 49 | import { BasicForm, useForm } from '/@/components/Form'; |
38 | 50 | import { step2Schemas } from '../device.profile.data'; |
51 | + import { Button } from '/@/components/Button'; | |
39 | 52 | import MqttCpns from './cpns/mqtt/Mqtt.vue'; |
40 | 53 | import CoapCpns from './cpns/coap/Coap.vue'; |
41 | 54 | import Lwm2mCpns from './cpns/lwm2m/index.vue'; |
42 | 55 | import SnmpCpns from './cpns/snmp/index.vue'; |
56 | + import TcpCpns from './cpns/tcp/index.vue'; | |
43 | 57 | |
58 | + const emits = defineEmits(['prev']); | |
59 | + const props = defineProps({ | |
60 | + ifShowBtn: { type: Boolean, default: true }, | |
61 | + }); | |
44 | 62 | const mqttRef = ref<InstanceType<typeof MqttCpns>>(); |
45 | 63 | const coapRef = ref<InstanceType<typeof CoapCpns>>(); |
46 | 64 | const lwm2mRef = ref<InstanceType<typeof Lwm2mCpns>>(); |
47 | 65 | const snmpRef = ref<InstanceType<typeof SnmpCpns>>(); |
66 | + const tcpRef = ref<InstanceType<typeof TcpCpns>>(); | |
48 | 67 | const isMqttType = ref('DEFAULT'); |
49 | 68 | let step2Data = reactive({ |
50 | 69 | transportConfiguration: {}, |
... | ... | @@ -57,7 +76,7 @@ |
57 | 76 | }, |
58 | 77 | showResetButton: false, |
59 | 78 | submitOnReset: false, |
60 | - showActionButtonGroup: false, | |
79 | + showActionButtonGroup: props.ifShowBtn ? true : false, | |
61 | 80 | }); |
62 | 81 | const setFormData = (v) => { |
63 | 82 | setFieldsValue({ |
... | ... | @@ -68,6 +87,7 @@ |
68 | 87 | coapRef.value?.setFormData(v?.profileData?.transportConfiguration); |
69 | 88 | lwm2mRef.value?.setFormData(v?.profileData?.transportConfiguration); |
70 | 89 | snmpRef.value?.setFormData(v?.profileData?.transportConfiguration); |
90 | + tcpRef.value?.setFormData(v?.profileData?.transportConfiguration); | |
71 | 91 | }; |
72 | 92 | |
73 | 93 | const resetFormData = () => { |
... | ... | @@ -78,8 +98,12 @@ |
78 | 98 | coapRef.value?.resetFormData(); |
79 | 99 | lwm2mRef.value?.resetFormData(); |
80 | 100 | snmpRef.value?.resetFormData(); |
101 | + tcpRef.value?.resetFormData(); | |
81 | 102 | }); |
82 | 103 | }; |
104 | + async function customResetFunc() { | |
105 | + emits('prev'); | |
106 | + } | |
83 | 107 | nextTick(() => { |
84 | 108 | updateSchema({ |
85 | 109 | field: 'transportType', |
... | ... | @@ -91,6 +115,7 @@ |
91 | 115 | { label: 'CoAP', value: 'COAP' }, |
92 | 116 | { label: 'LWM2M', value: 'LWM2M' }, |
93 | 117 | { label: 'SNMP', value: 'SNMP' }, |
118 | + { label: 'TCP', value: 'TCP' }, | |
94 | 119 | ], |
95 | 120 | onChange(e) { |
96 | 121 | isMqttType.value = e; |
... | ... | @@ -109,11 +134,13 @@ |
109 | 134 | const getCoapVal = await coapRef.value?.getFormData(); |
110 | 135 | const getLwm2mVal = await lwm2mRef.value?.getFormData(); |
111 | 136 | const getSnmpVal = await snmpRef.value?.getFormData(); |
137 | + const getTcpVal = await tcpRef.value?.getFormData(); | |
112 | 138 | step2Data.transportConfiguration = { |
113 | 139 | ...getMqttVal, |
114 | 140 | ...getCoapVal, |
115 | 141 | ...getLwm2mVal, |
116 | 142 | ...getSnmpVal, |
143 | + ...getTcpVal, | |
117 | 144 | ...val, |
118 | 145 | }; |
119 | 146 | return step2Data; | ... | ... |
1 | +.p-style { | |
2 | + .p-column { | |
3 | + display: flex; | |
4 | + flex-direction: column; | |
5 | + | |
6 | + .p-content { | |
7 | + display: flex; | |
8 | + align-items: center; | |
9 | + margin-left: -20px; | |
10 | + } | |
11 | + | |
12 | + .p-bottom { | |
13 | + display: flex; | |
14 | + justify-content: space-between; | |
15 | + width: 773px; | |
16 | + margin-top: 0; | |
17 | + } | |
18 | + } | |
19 | +} | ... | ... |
... | ... | @@ -8,32 +8,46 @@ |
8 | 8 | @ok="handleSubmit" |
9 | 9 | @cancel="handleCancel" |
10 | 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> | |
11 | + <div v-if="isViewDetail"> | |
12 | + <Attribute v-show="activeKey === '1'" ref="AttrRef" /> | |
13 | + </div> | |
14 | + <div v-if="!isViewDetail"> | |
15 | + <div> | |
16 | + <Typography> | |
17 | + <TypographyParagraph> | |
18 | + <blockquote style="background: #f2f2f2">{{ blockContent }}</blockquote> | |
19 | + </TypographyParagraph> | |
20 | + </Typography> | |
21 | + </div> | |
22 | + <Tabs type="card" v-model:activeKey="activeKey" :size="size"> | |
23 | + <TabPane key="1" tab="属性"> | |
24 | + <Attribute v-show="activeKey === '1'" ref="AttrRef" /> | |
25 | + </TabPane> | |
26 | + <TabPane disabled key="2" tab="服务"> | |
27 | + <Service v-show="activeKey === '2'" ref="ServiceRef" /> | |
28 | + </TabPane> | |
29 | + <TabPane disabled key="3" v-show="activeKey === '3'" tab="事件"> | |
30 | + <Events v-show="activeKey === '3'" ref="EventsRef" /> | |
31 | + </TabPane> | |
32 | + </Tabs> | |
33 | + </div> | |
22 | 34 | </BasicModal> |
23 | 35 | </div> |
24 | 36 | </template> |
25 | 37 | <script lang="ts" setup> |
26 | 38 | import { ref, unref } from 'vue'; |
27 | 39 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
28 | - import { Tabs, TabPane } from 'ant-design-vue'; | |
29 | - import Attr from './cpns/Attr.vue'; | |
40 | + import { Tabs, TabPane, Typography, TypographyParagraph } from 'ant-design-vue'; | |
41 | + import Attribute from './cpns/Attribute.vue'; | |
30 | 42 | import Service from './cpns/Service.vue'; |
31 | 43 | import Events from './cpns/Events.vue'; |
32 | 44 | |
33 | 45 | defineEmits(['register']); |
46 | + const blockContent = `属性一般是设备的运行状态,如当前温度等;服务是设备可被调用的方法,支持定义参数,如执行某项任务;事件则是设备上报的 | |
47 | +通知,如告警,需要被及时处理。`; | |
34 | 48 | const activeKey = ref('1'); |
35 | 49 | const size = ref('small'); |
36 | - const AttrRef = ref<InstanceType<typeof Attr>>(); | |
50 | + const AttrRef = ref<InstanceType<typeof Attribute>>(); | |
37 | 51 | const ServiceRef = ref<InstanceType<typeof Service>>(); |
38 | 52 | const EventsRef = ref<InstanceType<typeof Events>>(); |
39 | 53 | const isUpdate = ref(false); | ... | ... |
src/views/device/profiles/step/cpns/physical/cpns/Attribute.vue
renamed from
src/views/device/profiles/step/cpns/physical/cpns/Attr.vue
1 | 1 | <template> |
2 | 2 | <div> |
3 | - <div style="display: flex; justify-content: space-between"> | |
3 | + <div> | |
4 | + <Typography> | |
5 | + <TypographyParagraph> | |
6 | + <blockquote style="background: #f2f2f2">{{ blockContent }}</blockquote> | |
7 | + </TypographyParagraph> | |
8 | + </Typography> | |
9 | + </div> | |
10 | + <div style="display: flex; justify-content: space-between; align-items: center"> | |
4 | 11 | <div>模型内容</div> |
5 | 12 | <div> |
6 | 13 | <Button @click="handlePremitter"> |
... | ... | @@ -29,10 +36,13 @@ |
29 | 36 | import { useMessage } from '/@/hooks/web/useMessage'; |
30 | 37 | import jsoneditor from 'jsoneditor'; |
31 | 38 | import 'jsoneditor/dist/jsoneditor.min.css'; |
32 | - import { Button } from 'ant-design-vue'; | |
39 | + import { Button, Typography, TypographyParagraph } from 'ant-design-vue'; | |
33 | 40 | import { defaultTslContent } from './config'; |
34 | 41 | |
35 | 42 | const { createMessage } = useMessage(); |
43 | + const blockContent = `物模型是对设备在云端的功能描述,包括设备的属性、服务和事件。物联网平台通过定义一种物的描述语言来描述物模型,称之为 TSL(即 Thing | |
44 | +Specification Language),采用 JSON 格式,您可以根据 TSL 组装上报设备的数据。您可以导出完整物模型,用于云端应用开发;您也可以只导出 | |
45 | +精简物模型,配合设备端 SDK 实现设备开发。`; | |
36 | 46 | const jsonValue = ref(defaultTslContent); |
37 | 47 | const jsonInstance = ref(); |
38 | 48 | const jsoneditorRef = ref(); | ... | ... |
1 | +<template> | |
2 | + <div> | |
3 | + <BasicModal | |
4 | + destroyOnClose | |
5 | + v-bind="$attrs" | |
6 | + width="60rem" | |
7 | + @register="register" | |
8 | + :title="getTitle" | |
9 | + :minHeight="500" | |
10 | + @cancel="handleCancel" | |
11 | + @ok="handleSubmit" | |
12 | + > | |
13 | + <ConverScript :ifAdd="!isUpdate ? false : true" ref="converScriptRef" /> | |
14 | + </BasicModal> | |
15 | + </div> | |
16 | +</template> | |
17 | +<script setup lang="ts"> | |
18 | + import { ref, computed, unref } from 'vue'; | |
19 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | |
20 | + import ConverScript from '/@/views/scriptmanage/converscript/ConverScript.vue'; | |
21 | + | |
22 | + const converScriptRef = ref<InstanceType<typeof ConverScript>>(); | |
23 | + const getTitle = computed(() => (isUpdate.value ? '测试脚本' : '新建脚本')); | |
24 | + const isUpdate = ref(false); | |
25 | + const [register, { setModalProps, closeModal }] = useModalInner(async (data) => { | |
26 | + setModalProps({ loading: true }); | |
27 | + handleCancel(false); | |
28 | + isUpdate.value = data.isUpdate; | |
29 | + converScriptRef.value?.initEditor(data.record?.configuration?.jsScript); | |
30 | + setModalProps({ loading: false }); | |
31 | + const title = !unref(isUpdate) ? '测试脚本' : '新建脚本'; | |
32 | + const okText = !unref(isUpdate) ? '测试' : '确定'; | |
33 | + setModalProps({ title, showOkBtn: true, showCancelBtn: true, okText }); | |
34 | + // converScriptRef.value?.setFormData(); | |
35 | + }); | |
36 | + const handleSubmit = async () => { | |
37 | + const val = await converScriptRef.value?.getFormData(); | |
38 | + console.log(val); | |
39 | + handleCancel(true); | |
40 | + }; | |
41 | + const handleCancel = (flag) => { | |
42 | + if (flag) { | |
43 | + closeModal(); | |
44 | + } | |
45 | + converScriptRef.value?.resetFormData(); | |
46 | + }; | |
47 | +</script> | |
48 | +<style lang="less" scoped> | |
49 | + @import url('/@/views/scriptmanage/converscript/ConverScriptModal.less'); | |
50 | +</style> | ... | ... |
1 | +import { FormSchema } from '/@/components/Form'; | |
2 | + | |
3 | +export const tcpSchemas: FormSchema[] = [ | |
4 | + { | |
5 | + field: 'script', | |
6 | + label: '转换脚本', | |
7 | + component: 'Input', | |
8 | + slot: 'script', | |
9 | + colProps: { span: 24 }, | |
10 | + }, | |
11 | +]; | |
12 | + | |
13 | +// 新增编辑配置 | |
14 | +export const formSchema: FormSchema[] = [ | |
15 | + { | |
16 | + field: 'name', | |
17 | + label: '输入参数', | |
18 | + colProps: { span: 24 }, | |
19 | + required: true, | |
20 | + component: 'Input', | |
21 | + componentProps: { | |
22 | + maxLength: 255, | |
23 | + placeholder: '请输入输入参数', | |
24 | + }, | |
25 | + }, | |
26 | + { | |
27 | + field: 'scriptContent', | |
28 | + label: '脚本内容', | |
29 | + required: true, | |
30 | + component: 'Input', | |
31 | + slot: 'scriptContent', | |
32 | + colProps: { span: 24 }, | |
33 | + }, | |
34 | + { | |
35 | + field: 'remark', | |
36 | + label: '输出参数', | |
37 | + colProps: { span: 24 }, | |
38 | + component: 'InputTextArea', | |
39 | + componentProps: { | |
40 | + rows: 6, | |
41 | + maxLength: 255, | |
42 | + placeholder: '请输入输出参数', | |
43 | + }, | |
44 | + }, | |
45 | +]; | ... | ... |
1 | +<template> | |
2 | + <div> | |
3 | + <BasicForm :showResetButton="false" :showSubmitButton="false" @register="register"> | |
4 | + <template #script> | |
5 | + <div style="display: flex; align-items: center"> | |
6 | + <div> | |
7 | + <Select | |
8 | + placeholder="请选择转换脚本" | |
9 | + v-model:value="selectScript.script" | |
10 | + style="width: 305px" | |
11 | + :options="selectOptions" | |
12 | + allowClear | |
13 | + /> | |
14 | + </div> | |
15 | + <div> | |
16 | + <span | |
17 | + @click="handleCreateOrEdit('add')" | |
18 | + class="ml-2" | |
19 | + style="color: #409eff; cursor: pointer" | |
20 | + type="primary" | |
21 | + size="small" | |
22 | + >新建转换脚本</span | |
23 | + > | |
24 | + </div> | |
25 | + </div> | |
26 | + <a-button @click="handleCreateOrEdit('test')" class="mt-4" type="primary" | |
27 | + >测试脚本</a-button | |
28 | + > | |
29 | + </template> | |
30 | + </BasicForm> | |
31 | + <ConverScriptModal @register="registerModal" /> | |
32 | + </div> | |
33 | +</template> | |
34 | +<script lang="ts" setup> | |
35 | + import { ref, Ref, reactive, onMounted } from 'vue'; | |
36 | + import { BasicForm, useForm } from '/@/components/Form'; | |
37 | + import { tcpSchemas } from './config'; | |
38 | + import { SelectTypes } from 'ant-design-vue/es/select'; | |
39 | + import { Select } from 'ant-design-vue'; | |
40 | + import { useModal } from '/@/components/Modal'; | |
41 | + import ConverScriptModal from './ConverScriptModal.vue'; | |
42 | + | |
43 | + const selectScript = reactive({ | |
44 | + script: null, | |
45 | + }); | |
46 | + const selectOptions: Ref<SelectTypes['options']> = ref([ | |
47 | + { | |
48 | + label: '电表转换脚本', | |
49 | + value: 1, | |
50 | + }, | |
51 | + { | |
52 | + label: '水表转换脚本', | |
53 | + value: 2, | |
54 | + }, | |
55 | + ]); | |
56 | + onMounted(() => {}); | |
57 | + | |
58 | + const [register] = useForm({ | |
59 | + labelWidth: 180, | |
60 | + schemas: tcpSchemas, | |
61 | + actionColOptions: { | |
62 | + span: 14, | |
63 | + }, | |
64 | + }); | |
65 | + const [registerModal, { openModal }] = useModal(); | |
66 | + | |
67 | + // 新增或编辑 | |
68 | + const handleCreateOrEdit = (c) => { | |
69 | + if (c === 'add') { | |
70 | + openModal(true, { | |
71 | + isUpdate: true, | |
72 | + }); | |
73 | + } else { | |
74 | + openModal(true, { | |
75 | + isUpdate: false, | |
76 | + }); | |
77 | + } | |
78 | + }; | |
79 | + | |
80 | + const getFormData = () => { | |
81 | + return selectScript; | |
82 | + }; | |
83 | + const resetFormData = () => { | |
84 | + selectScript.script = null; | |
85 | + }; | |
86 | + const setFormData = (v) => { | |
87 | + selectScript.script = v; | |
88 | + }; | |
89 | + defineExpose({ | |
90 | + getFormData, | |
91 | + resetFormData, | |
92 | + setFormData, | |
93 | + }); | |
94 | +</script> | |
95 | +<style lang="less" scoped></style> | ... | ... |
1 | 1 | <template> |
2 | 2 | <div> |
3 | - <BasicForm @register="registerForm"> | |
4 | - <template #scriptContent> | |
3 | + <a-form | |
4 | + ref="formRef" | |
5 | + :model="scriptForm" | |
6 | + name="basic" | |
7 | + :label-col="{ span: 3 }" | |
8 | + :wrapper-col="{ span: 17 }" | |
9 | + autocomplete="off" | |
10 | + > | |
11 | + <a-form-item | |
12 | + :label="ifAdd ? '名称' : '输入参数'" | |
13 | + :name="ifAdd ? 'scriptName' : 'inputParams'" | |
14 | + :rules="[{ required: true, message: ifAdd ? '请输入脚本名称' : '请输入参数' }]" | |
15 | + > | |
16 | + <a-input v-if="ifAdd" v-model:value="scriptForm.scriptName" placeholder="请输入脚本名称" /> | |
17 | + <a-input v-else v-model:value="scriptForm.inputParams" placeholder="请输入参数" /> | |
18 | + </a-form-item> | |
19 | + <a-form-item | |
20 | + label="脚本内容" | |
21 | + name="scriptContent" | |
22 | + :rules="[{ required: true, message: '请输入脚本内容' }]" | |
23 | + > | |
5 | 24 | <Card title="脚本内容" :bodyStyle="{ padding: 0, height: '280px' }"> |
25 | + <template #extra> | |
26 | + <a-button @click="handleFormat" size="small">格式化</a-button> | |
27 | + </template> | |
6 | 28 | <div ref="aceRef" class="overflow-hidden"></div> |
7 | 29 | </Card> |
8 | 30 | <Button @click="handleCopy" class="mt-4"> |
... | ... | @@ -11,14 +33,29 @@ |
11 | 33 | </template> |
12 | 34 | copy |
13 | 35 | </Button> |
14 | - </template> | |
15 | - </BasicForm> | |
36 | + </a-form-item> | |
37 | + <a-form-item | |
38 | + :label="ifAdd ? '备注' : '输出参数'" | |
39 | + :name="ifAdd ? 'scriptRemark' : 'outputParams'" | |
40 | + > | |
41 | + <a-textarea | |
42 | + :rows="5" | |
43 | + v-if="ifAdd" | |
44 | + v-model:value="scriptForm.scriptRemark" | |
45 | + placeholder="请输入备注" | |
46 | + /> | |
47 | + <a-textarea | |
48 | + :rows="5" | |
49 | + v-else | |
50 | + v-model:value="scriptForm.outputParams" | |
51 | + placeholder="请输入输出参数" | |
52 | + /> | |
53 | + </a-form-item> | |
54 | + </a-form> | |
16 | 55 | </div> |
17 | 56 | </template> |
18 | 57 | <script setup lang="ts"> |
19 | - import { ref, unref } from 'vue'; | |
20 | - import { formSchema } from './config.data'; | |
21 | - import { BasicForm, useForm } from '/@/components/Form'; | |
58 | + import { ref, unref, reactive } from 'vue'; | |
22 | 59 | import ace from 'ace-builds'; |
23 | 60 | import { Card, Button } from 'ant-design-vue'; |
24 | 61 | import 'ace-builds/src-noconflict/theme-chrome'; // 默认设置的主题 |
... | ... | @@ -29,15 +66,20 @@ |
29 | 66 | import { useMessage } from '/@/hooks/web/useMessage'; |
30 | 67 | |
31 | 68 | defineEmits(['register']); |
69 | + defineProps({ | |
70 | + ifAdd: { type: Boolean, default: true }, | |
71 | + }); | |
72 | + const scriptForm = reactive({ | |
73 | + scriptName: '', | |
74 | + scriptRemark: '', | |
75 | + scriptContent: '', | |
76 | + inputParams: '', | |
77 | + outputParams: '', | |
78 | + }); | |
32 | 79 | const { createMessage } = useMessage(); |
33 | 80 | const { clipboardRef, copiedRef } = useCopyToClipboard(); |
34 | 81 | const aceEditor = ref(); |
35 | 82 | const aceRef = ref(); |
36 | - const [registerForm, { validate, resetFields }] = useForm({ | |
37 | - labelWidth: 120, | |
38 | - schemas: formSchema, | |
39 | - showActionButtonGroup: false, | |
40 | - }); | |
41 | 83 | // 初始化编辑器 |
42 | 84 | const initEditor = (jsScript?: string) => { |
43 | 85 | aceEditor.value = ace.edit(aceRef.value, { |
... | ... | @@ -64,6 +106,7 @@ |
64 | 106 | }` |
65 | 107 | ); |
66 | 108 | beautify(aceEditor.value.session); |
109 | + scriptForm.scriptContent = aceEditor.value.getValue(); | |
67 | 110 | }; |
68 | 111 | const handleCopy = () => { |
69 | 112 | const valueRef = aceEditor.value.getValue(); |
... | ... | @@ -77,19 +120,29 @@ |
77 | 120 | createMessage.success('复制成功!'); |
78 | 121 | } |
79 | 122 | }; |
123 | + const formRef = ref(); | |
80 | 124 | const getFormData = async () => { |
81 | - const value = await validate(); | |
125 | + const value = await formRef.value.validateFields(); | |
82 | 126 | if (!value) return; |
83 | 127 | return value; |
84 | 128 | }; |
129 | + const setFormData = (v) => { | |
130 | + for (let i in scriptForm) { | |
131 | + Reflect.set(scriptForm, i, v[i]); | |
132 | + } | |
133 | + }; | |
85 | 134 | const resetFormData = () => { |
86 | - resetFields(); | |
135 | + for (let i in scriptForm) { | |
136 | + Reflect.set(scriptForm, i, ''); | |
137 | + } | |
87 | 138 | }; |
139 | + const handleFormat = () => beautify(aceEditor.value.session); | |
88 | 140 | |
89 | 141 | defineExpose({ |
90 | 142 | initEditor, |
91 | 143 | getFormData, |
92 | 144 | resetFormData, |
145 | + setFormData, | |
93 | 146 | }); |
94 | 147 | </script> |
95 | 148 | <style lang="less" scoped> | ... | ... |
1 | -/* | |
2 | - * @Author: fengtao 1400859700@qq.com | |
3 | - * @Date: 2022-10-12 09:29:11 | |
4 | - * @LastEditors: fengtao 1400859700@qq.com | |
5 | - * @LastEditTime: 2022-10-13 17:28:24 | |
6 | - * @FilePath: \yun-teng-iot-front\src\views\scriptmanage\converscript\config.data.ts | |
7 | - * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE | |
8 | - */ | |
9 | 1 | import { BasicColumn, FormSchema } from '/@/components/Table'; |
10 | 2 | import moment from 'moment'; |
11 | 3 | import { h } from 'vue'; |
... | ... | @@ -76,14 +68,11 @@ export const searchFormSchema: FormSchema[] = [ |
76 | 68 | export const formSchema: FormSchema[] = [ |
77 | 69 | { |
78 | 70 | field: 'name', |
79 | - label: '名称', | |
80 | - colProps: { span: 24 }, | |
71 | + label: '', | |
81 | 72 | required: true, |
82 | 73 | component: 'Input', |
83 | - componentProps: { | |
84 | - maxLength: 255, | |
85 | - placeholder: '请输入脚本名称', | |
86 | - }, | |
74 | + slot: 'scriptName', | |
75 | + colProps: { span: 24 }, | |
87 | 76 | }, |
88 | 77 | { |
89 | 78 | field: 'scriptContent', |
... | ... | @@ -95,13 +84,10 @@ export const formSchema: FormSchema[] = [ |
95 | 84 | }, |
96 | 85 | { |
97 | 86 | field: 'remark', |
98 | - label: '备注', | |
87 | + label: '', | |
88 | + required: true, | |
89 | + component: 'Input', | |
90 | + slot: 'scriptRemark', | |
99 | 91 | colProps: { span: 24 }, |
100 | - component: 'InputTextArea', | |
101 | - componentProps: { | |
102 | - rows: 6, | |
103 | - maxLength: 255, | |
104 | - placeholder: '请输入备注', | |
105 | - }, | |
106 | 92 | }, |
107 | 93 | ]; | ... | ... |