Commit cea7fdf23a38e81c2e96acf3f188d1d7f4e70e35

Authored by xp.Huang
1 parent 7ddce7c2

Merge branch 'ft' into 'main_dev'

fix: 修复Teambition上的问题

See merge request yunteng/thingskit-front!533

(cherry picked from commit 6fee6dcc13983532958543c85383139aad19fdce)

c62f9e54 pref:优化测试接口表格,只有一项时,清除对应项
85cc1230 fix:DEFECT-1122 分页接口测试错误提示,修复post传参问题
e2c049f2 fix:DEFECT-1112 提示输入参数值 表格选择是否必须为是,则对应测试接口参数若为空则增加提示
f8d25bcd fix:DEFECT-1112 提示输入参数值 表格选择是否必须为是,则对应测试接口参数若为空则增加提示
eaaa19ee pref:调整脚本管理 转换函数
dda4b267 fix:DEFECT-1078 修复暗黑模式场景联动白框问题
b8876e1a fix:DEFECT-1079 修复暗黑模式转换脚本白框问题
1 <template> 1 <template>
2 - <div :class="prefixCls"> 2 + <div
  3 + :class="prefixCls"
  4 + :style="{ backgroundColor: modeSwitch === 'dark' ? '#1f1f1f' : 'rgb(242, 242, 242)' }"
  5 + >
3 <CollapseHeader 6 <CollapseHeader
4 v-bind="$props" 7 v-bind="$props"
5 :prefixCls="prefixCls" 8 :prefixCls="prefixCls"
@@ -29,7 +32,7 @@ @@ -29,7 +32,7 @@
29 </template> 32 </template>
30 <script lang="ts" setup> 33 <script lang="ts" setup>
31 import type { PropType } from 'vue'; 34 import type { PropType } from 'vue';
32 - import { ref } from 'vue'; 35 + import { ref, computed } from 'vue';
33 // component 36 // component
34 import { Skeleton } from 'ant-design-vue'; 37 import { Skeleton } from 'ant-design-vue';
35 import { CollapseTransition } from '/@/components/Transition'; 38 import { CollapseTransition } from '/@/components/Transition';
@@ -38,6 +41,7 @@ @@ -38,6 +41,7 @@
38 // hook 41 // hook
39 import { useTimeoutFn } from '/@/hooks/core/useTimeout'; 42 import { useTimeoutFn } from '/@/hooks/core/useTimeout';
40 import { useDesign } from '/@/hooks/web/useDesign'; 43 import { useDesign } from '/@/hooks/web/useDesign';
  44 + import { useAppStore } from '/@/store/modules/app';
41 45
42 const props = defineProps({ 46 const props = defineProps({
43 isClose: { type: Boolean, default: true }, 47 isClose: { type: Boolean, default: true },
@@ -64,6 +68,11 @@ @@ -64,6 +68,11 @@
64 */ 68 */
65 lazyTime: { type: Number, default: 0 }, 69 lazyTime: { type: Number, default: 0 },
66 }); 70 });
  71 +
  72 + const userStore = useAppStore();
  73 +
  74 + const modeSwitch = computed((): string => userStore.getDarkMode);
  75 +
67 const emit = defineEmits(['expand', 'change', 'hchange']); 76 const emit = defineEmits(['expand', 'change', 'hchange']);
68 77
69 const show = ref(true); 78 const show = ref(true);
@@ -92,7 +101,7 @@ @@ -92,7 +101,7 @@
92 @prefix-cls: ~'@{namespace}-collapse-container'; 101 @prefix-cls: ~'@{namespace}-collapse-container';
93 102
94 .@{prefix-cls} { 103 .@{prefix-cls} {
95 - background-color: @component-background; 104 + // background-color: @component-background;
96 border-radius: 2px; 105 border-radius: 2px;
97 transition: all 0.3s ease-in-out; 106 transition: all 0.3s ease-in-out;
98 107
@@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
18 placeholder="请选择" 18 placeholder="请选择"
19 :options="selectOptions" 19 :options="selectOptions"
20 @change="handleChange" 20 @change="handleChange"
  21 + @dropdownVisibleChange="hanldeDropdownVisibleChange"
21 allowClear 22 allowClear
22 /> 23 />
23 </td> 24 </td>
@@ -111,16 +112,20 @@ @@ -111,16 +112,20 @@
111 112
112 // 减少 113 // 减少
113 const remove = (item, index: number) => { 114 const remove = (item, index: number) => {
114 - if (tableArray.content.length !== 1) { 115 + if (tableArray.content.length > 1) {
115 selectOptions.value.forEach((ele) => { 116 selectOptions.value.forEach((ele) => {
116 if (ele.value == item.key) { 117 if (ele.value == item.key) {
117 ele.disabled = false; 118 ele.disabled = false;
118 } 119 }
119 }); 120 });
120 tableArray.content.splice(index, 1); 121 tableArray.content.splice(index, 1);
  122 + } else {
  123 + resetValue();
121 } 124 }
122 }; 125 };
123 126
  127 + const hanldeDropdownVisibleChange = () => handleChange();
  128 +
124 //Select互斥 129 //Select互斥
125 const handleChange = () => { 130 const handleChange = () => {
126 selectOptions.value.forEach((ele) => { 131 selectOptions.value.forEach((ele) => {
@@ -132,7 +137,7 @@ @@ -132,7 +137,7 @@
132 element.value = ''; 137 element.value = '';
133 element.editDisabled = true; 138 element.editDisabled = true;
134 } 139 }
135 - if (element.key === ele.value && element.key !== 'scope' && element.key !== 'fixed_date') { 140 + if (element.key === ele.value && element.key !== 'scope') {
136 ele.disabled = true; 141 ele.disabled = true;
137 } 142 }
138 }); 143 });
@@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
18 placeholder="请选择" 18 placeholder="请选择"
19 :options="selectOptions" 19 :options="selectOptions"
20 @change="handleChange" 20 @change="handleChange"
  21 + @dropdownVisibleChange="hanldeDropdownVisibleChange"
21 allowClear 22 allowClear
22 /> 23 />
23 </td> 24 </td>
@@ -111,13 +112,15 @@ @@ -111,13 +112,15 @@
111 112
112 // 减少 113 // 减少
113 const remove = (item, index: number) => { 114 const remove = (item, index: number) => {
114 - if (tableArray.content.length !== 1) { 115 + if (tableArray.content.length > 1) {
115 selectOptions.value.forEach((ele) => { 116 selectOptions.value.forEach((ele) => {
116 if (ele.value == item.key) { 117 if (ele.value == item.key) {
117 ele.disabled = false; 118 ele.disabled = false;
118 } 119 }
119 }); 120 });
120 tableArray.content.splice(index, 1); 121 tableArray.content.splice(index, 1);
  122 + } else {
  123 + resetValue();
121 } 124 }
122 }; 125 };
123 126
@@ -132,13 +135,15 @@ @@ -132,13 +135,15 @@
132 element.value = ''; 135 element.value = '';
133 element.editDisabled = true; 136 element.editDisabled = true;
134 } 137 }
135 - if (element.key === ele.value && element.key !== 'scope' && element.key !== 'fixed_date') { 138 + if (element.key === ele.value && element.key !== 'scope') {
136 ele.disabled = true; 139 ele.disabled = true;
137 } 140 }
138 }); 141 });
139 }); 142 });
140 }; 143 };
141 144
  145 + const hanldeDropdownVisibleChange = () => handleChange();
  146 +
142 //获取数据 147 //获取数据
143 const getValue = () => { 148 const getValue = () => {
144 const assemblyData = tableArray.content.map((it) => { 149 const assemblyData = tableArray.content.map((it) => {
@@ -31,6 +31,7 @@ @@ -31,6 +31,7 @@
31 import moment from 'moment'; 31 import moment from 'moment';
32 import { useUtils } from '../../../hooks/useUtils'; 32 import { useUtils } from '../../../hooks/useUtils';
33 import JsonEditor from '../../SimpleRequest/components/jsonEditor.vue'; 33 import JsonEditor from '../../SimpleRequest/components/jsonEditor.vue';
  34 + import { useMessage } from '/@/hooks/web/useMessage';
34 35
35 const emits = defineEmits(['testBodyInterface']); 36 const emits = defineEmits(['testBodyInterface']);
36 37
@@ -40,6 +41,8 @@ @@ -40,6 +41,8 @@
40 }, 41 },
41 }); 42 });
42 43
  44 + const { createMessage } = useMessage();
  45 +
43 const showTestEditCell = ref(false); 46 const showTestEditCell = ref(false);
44 47
45 const excuteData = ref({}); 48 const excuteData = ref({});
@@ -63,13 +66,12 @@ @@ -63,13 +66,12 @@
63 const getValue = async () => { 66 const getValue = async () => {
64 await nextTick(); 67 await nextTick();
65 await nextTick(() => { 68 await nextTick(() => {
66 - console.log(props.data?.list);  
67 if (props.data?.type === 'x-www-form-urlencoded' || props.data?.type === 'form-data') { 69 if (props.data?.type === 'x-www-form-urlencoded' || props.data?.type === 'form-data') {
68 const getSingleKey = props.data?.list; 70 const getSingleKey = props.data?.list;
69 - const getMuteKey = props.data?.list?.filter((it: any) => it.key.split(',').length > 1); 71 + const getMuteKey = props.data?.list?.filter((it: any) => it.key?.split(',').length > 1);
70 const getMuteKeys = getMultipleKeys(getMuteKey); 72 const getMuteKeys = getMultipleKeys(getMuteKey);
71 const mergeKeys = [...getSingleKey, ...getMuteKeys]?.filter( 73 const mergeKeys = [...getSingleKey, ...getMuteKeys]?.filter(
72 - (it: any) => it.key.split(',').length === 1 74 + (it: any) => it.key?.split(',').length === 1
73 ); 75 );
74 testEditCellTableRef.value?.setTableArray(mergeKeys); 76 testEditCellTableRef.value?.setTableArray(mergeKeys);
75 } else if (props.data?.type === 'json') { 77 } else if (props.data?.type === 'json') {
@@ -111,6 +113,7 @@ @@ -111,6 +113,7 @@
111 return { 113 return {
112 key, 114 key,
113 value, 115 value,
  116 + required: it.required,
114 }; 117 };
115 }); 118 });
116 }; 119 };
@@ -120,6 +123,11 @@ @@ -120,6 +123,11 @@
120 let params: any = {}; 123 let params: any = {};
121 if (props.data?.type === 'x-www-form-urlencoded' || props.data?.type === 'form-data') { 124 if (props.data?.type === 'x-www-form-urlencoded' || props.data?.type === 'form-data') {
122 const getTable = getTestTableKeyValue(); 125 const getTable = getTestTableKeyValue();
  126 + const hasRequired = getTable?.some((it) => it.required === true && !it.value);
  127 + if (hasRequired) {
  128 + createMessage.error('选择项为必须的,参数不能为空');
  129 + throw new Error('选择项为必须的,参数不能为空');
  130 + }
123 getTable?.map((it) => (params[it.key!] = it.value!)); 131 getTable?.map((it) => (params[it.key!] = it.value!));
124 } else if (props.data?.type === 'json') { 132 } else if (props.data?.type === 'json') {
125 params = jsonEditorRef.value?.getJsonValue(); 133 params = jsonEditorRef.value?.getJsonValue();
@@ -228,6 +228,7 @@ @@ -228,6 +228,7 @@
228 }; 228 };
229 229
230 const getAttributeOptions = async (params) => { 230 const getAttributeOptions = async (params) => {
  231 + console.log(params);
231 const res = await getDeviceAttributes(params); 232 const res = await getDeviceAttributes(params);
232 if (Object.keys(res).length === 0) return (attributeOptions.value.length = 0); 233 if (Object.keys(res).length === 0) return (attributeOptions.value.length = 0);
233 attributeOptions.value = res?.map((item) => ({ label: item.name, value: item.identifier })); 234 attributeOptions.value = res?.map((item) => ({ label: item.name, value: item.identifier }));
@@ -243,7 +244,9 @@ @@ -243,7 +244,9 @@
243 if (f.key === 'organizationId') organizationId = f.value; 244 if (f.key === 'organizationId') organizationId = f.value;
244 if (f.key === 'entityId') f.value = null; 245 if (f.key === 'entityId') f.value = null;
245 }); 246 });
246 - getAttributeOptions({ deviceProfileId: e.value }); 247 + if (e.value) {
  248 + getAttributeOptions({ deviceProfileId: e.value });
  249 + }
247 if (organizationId !== '') { 250 if (organizationId !== '') {
248 getEntityOptions(organizationId, e.value); 251 getEntityOptions(organizationId, e.value);
249 } 252 }
@@ -261,6 +264,9 @@ @@ -261,6 +264,9 @@
261 </script> 264 </script>
262 265
263 <style scoped lang="less"> 266 <style scoped lang="less">
  267 + :deep(.ant-select-selector) {
  268 + max-width: 16vw;
  269 + }
264 @table-color: #e5e7eb; 270 @table-color: #e5e7eb;
265 271
266 .table-border-color { 272 .table-border-color {
@@ -36,7 +36,7 @@ @@ -36,7 +36,7 @@
36 import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum'; 36 import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum';
37 import { getAuthCache } from '/@/utils/auth'; 37 import { getAuthCache } from '/@/utils/auth';
38 import { useMessage } from '/@/hooks/web/useMessage'; 38 import { useMessage } from '/@/hooks/web/useMessage';
39 - // import { useGlobSetting } from '/@/hooks/setting'; 39 + import { useUtils } from '../../../hooks/useUtils';
40 40
41 const emits = defineEmits(['emitExcute']); 41 const emits = defineEmits(['emitExcute']);
42 42
@@ -56,8 +56,6 @@ @@ -56,8 +56,6 @@
56 56
57 const socketUrls = ref(''); 57 const socketUrls = ref('');
58 58
59 - // const { socketUrl } = useGlobSetting();  
60 -  
61 const socketMessage: any = reactive({ 59 const socketMessage: any = reactive({
62 server: ``, 60 server: ``,
63 sendValue: { 61 sendValue: {
@@ -152,7 +150,7 @@ @@ -152,7 +150,7 @@
152 apiUrl, 150 apiUrl,
153 headers = {}, 151 headers = {},
154 params = {}, 152 params = {},
155 - body, 153 + body = {},
156 joinPrefix = false 154 joinPrefix = false
157 ) => { 155 ) => {
158 switch (apiType) { 156 switch (apiType) {
@@ -165,8 +163,9 @@ @@ -165,8 +163,9 @@
165 } 163 }
166 ); 164 );
167 case 'POST': 165 case 'POST':
  166 + const { convertObj } = useUtils();
168 return await otherHttp.post( 167 return await otherHttp.post(
169 - { url: apiUrl, data: body, headers, params }, 168 + { url: `${apiUrl}?${convertObj(params)}`, data: body, headers },
170 { 169 {
171 apiUrl: '', 170 apiUrl: '',
172 joinPrefix, 171 joinPrefix,
@@ -20,6 +20,7 @@ @@ -20,6 +20,7 @@
20 import { ref, nextTick } from 'vue'; 20 import { ref, nextTick } from 'vue';
21 import { Button } from 'ant-design-vue'; 21 import { Button } from 'ant-design-vue';
22 import TestHeaderEditCellTable from './testEditHeaderCellTable.vue'; 22 import TestHeaderEditCellTable from './testEditHeaderCellTable.vue';
  23 + import { useMessage } from '/@/hooks/web/useMessage';
23 24
24 const emits = defineEmits(['testHeaderInterface']); 25 const emits = defineEmits(['testHeaderInterface']);
25 26
@@ -29,6 +30,8 @@ @@ -29,6 +30,8 @@
29 }, 30 },
30 }); 31 });
31 32
  33 + const { createMessage } = useMessage();
  34 +
32 const showTestEditCell = ref(false); 35 const showTestEditCell = ref(false);
33 36
34 const excuteData = ref({}); 37 const excuteData = ref({});
@@ -59,6 +62,11 @@ @@ -59,6 +62,11 @@
59 //获取数据 62 //获取数据
60 const getTestValue = () => { 63 const getTestValue = () => {
61 const getTable = getTestTableKeyValue(); 64 const getTable = getTestTableKeyValue();
  65 + const hasRequired = getTable?.some((it) => it.required === true && !it.value);
  66 + if (hasRequired) {
  67 + createMessage.error('选择项为必须的,参数不能为空');
  68 + throw new Error('选择项为必须的,参数不能为空');
  69 + }
62 const params: any = {}; 70 const params: any = {};
63 getTable?.map((it: any) => (params[it.key!] = it.value!)); 71 getTable?.map((it: any) => (params[it.key!] = it.value!));
64 excuteData.value = { 72 excuteData.value = {
@@ -22,6 +22,7 @@ @@ -22,6 +22,7 @@
22 import TestParamsCellTable from './testEditParamsCellTable.vue'; 22 import TestParamsCellTable from './testEditParamsCellTable.vue';
23 import moment from 'moment'; 23 import moment from 'moment';
24 import { useUtils } from '../../../hooks/useUtils'; 24 import { useUtils } from '../../../hooks/useUtils';
  25 + import { useMessage } from '/@/hooks/web/useMessage';
25 26
26 const emits = defineEmits(['testParamsInterface']); 27 const emits = defineEmits(['testParamsInterface']);
27 28
@@ -31,6 +32,8 @@ @@ -31,6 +32,8 @@
31 }, 32 },
32 }); 33 });
33 34
  35 + const { createMessage } = useMessage();
  36 +
34 const showTestEditCell = ref(false); 37 const showTestEditCell = ref(false);
35 38
36 const excuteData = ref({}); 39 const excuteData = ref({});
@@ -92,6 +95,7 @@ @@ -92,6 +95,7 @@
92 return { 95 return {
93 key, 96 key,
94 value, 97 value,
  98 + required: it.required,
95 }; 99 };
96 }); 100 });
97 }; 101 };
@@ -99,6 +103,11 @@ @@ -99,6 +103,11 @@
99 //获取数据 103 //获取数据
100 const getTestValue = () => { 104 const getTestValue = () => {
101 const getTable = getTestTableKeyValue(); 105 const getTable = getTestTableKeyValue();
  106 + const hasRequired = getTable?.some((it) => it.required === true && !it.value);
  107 + if (hasRequired) {
  108 + createMessage.error('选择项为必须的,参数不能为空');
  109 + throw new Error('选择项为必须的,参数不能为空');
  110 + }
102 const params: any = {}; 111 const params: any = {};
103 getTable?.map((it) => (params[it.key!] = it.value!)); 112 getTable?.map((it) => (params[it.key!] = it.value!));
104 if (params['keys']) { 113 if (params['keys']) {
@@ -235,7 +235,9 @@ @@ -235,7 +235,9 @@
235 if (f.key === 'organizationId') organizationId = f.value; 235 if (f.key === 'organizationId') organizationId = f.value;
236 if (f.key === 'entityId') f.value = null; 236 if (f.key === 'entityId') f.value = null;
237 }); 237 });
238 - getAttributeOptions({ deviceProfileId: e.value }); 238 + if (e.value) {
  239 + getAttributeOptions({ deviceProfileId: e.value });
  240 + }
239 if (organizationId !== '') { 241 if (organizationId !== '') {
240 getEntityOptions(organizationId, e.value); 242 getEntityOptions(organizationId, e.value);
241 } 243 }
@@ -5,9 +5,14 @@ export const useUtils = () => { @@ -5,9 +5,14 @@ export const useUtils = () => {
5 list?.forEach((it) => { 5 list?.forEach((it) => {
6 const keys = it.key.split(','); 6 const keys = it.key.split(',');
7 const temp = keys.map((item) => { 7 const temp = keys.map((item) => {
8 - const obj: { key: string; value: string } = { key: '', value: '' }; 8 + const obj: { key: string; value: string; required: boolean } = {
  9 + key: '',
  10 + value: '',
  11 + required: false,
  12 + };
9 obj.key = item; 13 obj.key = item;
10 obj.value = item === 'scope' ? it.value : ''; 14 obj.value = item === 'scope' ? it.value : '';
  15 + obj.required = it.required;
11 return obj; 16 return obj;
12 }); 17 });
13 temps = temp; 18 temps = temp;
@@ -36,5 +41,20 @@ export const useUtils = () => { @@ -36,5 +41,20 @@ export const useUtils = () => {
36 type: '0', 41 type: '0',
37 }, 42 },
38 }; 43 };
39 - return { getMultipleKeys, pushObj, resetReqHttpType, resetUpdateSchema }; 44 + //对象转get params参数
  45 + const convertObj = (data: object) => {
  46 + const _result: any = [];
  47 + for (const key in data) {
  48 + const value = data[key];
  49 + if (value.constructor == Array) {
  50 + value.forEach(function (_value) {
  51 + _result.push(key + '=' + _value);
  52 + });
  53 + } else {
  54 + _result.push(key + '=' + value);
  55 + }
  56 + }
  57 + return _result.join('&');
  58 + };
  59 + return { getMultipleKeys, pushObj, resetReqHttpType, resetUpdateSchema, convertObj };
40 }; 60 };
1 <template> 1 <template>
2 <div> 2 <div>
3 - <CollapseContainer style="background-color: #f2f2f2" :title="`${title} ${index + 1}`"> 3 + <CollapseContainer :title="`${title} ${index + 1}`">
4 <template #action> 4 <template #action>
5 <div class="flex"> 5 <div class="flex">
6 <div class="flex"> 6 <div class="flex">
1 <template> 1 <template>
2 - <CollapseContainer style="background-color: #f2f2f2" :title="`执行动作 ${actionIndex + 1}`"> 2 + <CollapseContainer :title="`执行动作 ${actionIndex + 1}`">
3 <template #action> 3 <template #action>
4 <Tooltip title="移除" v-if="actionData.length > 1"> 4 <Tooltip title="移除" v-if="actionData.length > 1">
5 <Icon 5 <Icon
1 -<template>  
2 - <div>  
3 - <BasicDrawer  
4 - v-bind="$attrs"  
5 - :title="getTitle"  
6 - @register="register"  
7 - width="500px"  
8 - @ok="handleSubmit"  
9 - showFooter  
10 - >  
11 - <BasicForm @register="registerForm">  
12 - <template #function>  
13 - <Card title="转换函数" :bodyStyle="{ padding: 0, height: '280px' }">  
14 - <template #extra>  
15 - <Tag color="blue">Transform Function</Tag>  
16 - <a-button @click="handleFormat" size="small">格式化</a-button>  
17 - </template>  
18 - <div class="ml-8">function Transform(msg, metadata) {</div>  
19 - <div ref="aceRef" class="overflow-hidden"></div>  
20 - <div class="ml-7">}</div>  
21 - </Card>  
22 - <a-button type="primary" class="mt-4" @click="testTransformFunc">测试转换功能</a-button>  
23 - </template>  
24 - </BasicForm>  
25 - </BasicDrawer>  
26 - </div>  
27 -</template>  
28 -  
29 -<script lang="ts" setup>  
30 - import { ref, computed, unref } from 'vue';  
31 - import { useDrawerInner, BasicDrawer } from '/@/components/Drawer/index';  
32 - import { useForm, BasicForm } from '/@/components/Form/index';  
33 - import { formSchema } from '../config/config.data';  
34 - import { Card, Tag } from 'ant-design-vue';  
35 - import { createOrEditTransformScriptApi } from '/@/api/device/TransformScriptApi';  
36 - import { useMessage } from '/@/hooks/web/useMessage';  
37 - import ace from 'ace-builds';  
38 - import 'ace-builds/src-noconflict/theme-chrome'; // 默认设置的主题  
39 - import 'ace-builds/src-noconflict/mode-javascript'; // 默认设置的语言模式  
40 - import { beautify } from 'ace-builds/src-noconflict/ext-beautify.js';  
41 -  
42 - const emit = defineEmits(['register', 'isStatus', 'success']);  
43 - const isUpdate: any = ref(false);  
44 - const isView = ref(true);  
45 - const aceEditor = ref();  
46 - const aceRef = ref();  
47 - const getTitle = computed(() => (isUpdate.value ? '编辑转换脚本' : '新增转换脚本'));  
48 - const editId = ref('');  
49 - const [register, { setDrawerProps, closeDrawer }] = useDrawerInner((data) => {  
50 - resetFields();  
51 - setDrawerProps({ confirmLoading: false });  
52 - isUpdate.value = data.isUpdate;  
53 - initEditor(data.record?.configuration.jsScript);  
54 - switch (isUpdate.value) {  
55 - case 'view':  
56 - isView.value = false;  
57 - setDrawerProps({  
58 - showFooter: unref(isView),  
59 - title: '查看转换脚本',  
60 - loading: false,  
61 - });  
62 - editId.value = data.record.id;  
63 - setFieldsValue(data.record);  
64 - break;  
65 - case true:  
66 - isView.value = true;  
67 - setDrawerProps({  
68 - showFooter: unref(isView),  
69 - title: '编辑转换脚本',  
70 - loading: false,  
71 - });  
72 - editId.value = data.record.id;  
73 - setFieldsValue(data.record);  
74 - break;  
75 - case false:  
76 - isView.value = true;  
77 - setDrawerProps({  
78 - showFooter: unref(isView),  
79 - title: '新增转换脚本',  
80 - loading: false,  
81 - });  
82 - break;  
83 - }  
84 - });  
85 - const [registerForm, { validate, setFieldsValue, resetFields }] = useForm({  
86 - showActionButtonGroup: false,  
87 - colProps: { span: 24 },  
88 - schemas: formSchema,  
89 - });  
90 -  
91 - // 初始化编辑器  
92 - const initEditor = (jsScript?: string) => {  
93 - aceEditor.value = ace.edit(aceRef.value, {  
94 - maxLines: 12, // 最大行数,超过会自动出现滚动条  
95 - minLines: 12, // 最小行数,还未到最大行数时,编辑器会自动伸缩大小  
96 - fontSize: 14, // 编辑器内字体大小  
97 - theme: 'ace/theme/chrome', // 默认设置的主题  
98 - mode: 'ace/mode/javascript', // 默认设置的语言模式  
99 - tabSize: 2, // 制表符设置为 4 个空格大小  
100 - });  
101 -  
102 - aceEditor.value.setOptions({  
103 - enableBasicAutocompletion: true,  
104 - enableLiveAutocompletion: true,  
105 - });  
106 - aceEditor.value.setValue(jsScript ?? 'return {msg: msg, metadata: metadata};');  
107 - beautify(aceEditor.value.session);  
108 - };  
109 -  
110 - const testTransformFunc = () => {  
111 - closeDrawer();  
112 - const jsCode = aceEditor.value.getValue();  
113 - emit('isStatus', { status: 1, jsCode });  
114 - };  
115 - const handleSubmit = async () => {  
116 - const editIdPost = isUpdate.value ? { id: editId.value } : {};  
117 - try {  
118 - setDrawerProps({ confirmLoading: true });  
119 - const fieldsValue = await validate();  
120 - if (!fieldsValue) return;  
121 - await createOrEditTransformScriptApi({  
122 - configuration: {  
123 - jsScript: aceEditor.value.getValue(),  
124 - },  
125 - type: 'org.thingsboard.rule.engine.transform.TbTransformMsgNode',  
126 - ...fieldsValue,  
127 - ...editIdPost,  
128 - });  
129 - closeDrawer();  
130 - emit('success');  
131 - const { createMessage } = useMessage();  
132 - createMessage.success('保存成功');  
133 - } catch (e) {  
134 - } finally {  
135 - setTimeout(() => {  
136 - setDrawerProps({ confirmLoading: false });  
137 - }, 300);  
138 - }  
139 - };  
140 - const handleFormat = () => {  
141 - beautify(aceEditor.value.session);  
142 - };  
143 - defineExpose({ aceEditor });  
144 -</script> 1 +<template>
  2 + <div>
  3 + <BasicDrawer
  4 + v-bind="$attrs"
  5 + :title="getTitle"
  6 + @register="register"
  7 + width="500px"
  8 + @ok="handleSubmit"
  9 + showFooter
  10 + >
  11 + <BasicForm @register="registerForm">
  12 + <template #function>
  13 + <Card title="转换函数" :bodyStyle="{ padding: 0, height: '280px' }">
  14 + <template #extra>
  15 + <Tag color="blue">Transform Function</Tag>
  16 + <a-button @click="handleFormat" size="small">格式化</a-button>
  17 + </template>
  18 + <div class="ml-8">function Transform(msg, metadata) {</div>
  19 + <div ref="aceRef" class="overflow-hidden"></div>
  20 + <div class="ml-7">}</div>
  21 + </Card>
  22 + <a-button type="primary" class="mt-4" @click="testTransformFunc">测试转换功能</a-button>
  23 + </template>
  24 + </BasicForm>
  25 + </BasicDrawer>
  26 + </div>
  27 +</template>
  28 +
  29 +<script lang="ts" setup>
  30 + import { ref, computed, unref } from 'vue';
  31 + import { useDrawerInner, BasicDrawer } from '/@/components/Drawer/index';
  32 + import { useForm, BasicForm } from '/@/components/Form/index';
  33 + import { formSchema } from '../config/config.data';
  34 + import { Card, Tag } from 'ant-design-vue';
  35 + import { createOrEditTransformScriptApi } from '/@/api/device/TransformScriptApi';
  36 + import { useMessage } from '/@/hooks/web/useMessage';
  37 + import ace from 'ace-builds';
  38 + import 'ace-builds/src-noconflict/theme-chrome'; // 默认设置的主题
  39 + import 'ace-builds/src-noconflict/theme-terminal'; // 默认设置的主题
  40 + import 'ace-builds/src-noconflict/mode-javascript'; // 默认设置的语言模式
  41 + import { beautify } from 'ace-builds/src-noconflict/ext-beautify.js';
  42 + import { useAppStore } from '/@/store/modules/app';
  43 +
  44 + const emit = defineEmits(['register', 'isStatus', 'success']);
  45 + const userStore = useAppStore();
  46 + const getAceClass = computed((): string => userStore.getDarkMode);
  47 + const isUpdate: any = ref(false);
  48 + const isView = ref(true);
  49 + const aceEditor = ref();
  50 + const aceRef = ref();
  51 + const getTitle = computed(() => (isUpdate.value ? '编辑转换脚本' : '新增转换脚本'));
  52 + const editId = ref('');
  53 + const [register, { setDrawerProps, closeDrawer }] = useDrawerInner((data) => {
  54 + resetFields();
  55 + setDrawerProps({ confirmLoading: false });
  56 + isUpdate.value = data.isUpdate;
  57 + initEditor(data.record?.configuration.jsScript);
  58 + switch (isUpdate.value) {
  59 + case 'view':
  60 + isView.value = false;
  61 + setDrawerProps({
  62 + showFooter: unref(isView),
  63 + title: '查看转换脚本',
  64 + loading: false,
  65 + });
  66 + editId.value = data.record.id;
  67 + setFieldsValue(data.record);
  68 + break;
  69 + case true:
  70 + isView.value = true;
  71 + setDrawerProps({
  72 + showFooter: unref(isView),
  73 + title: '编辑转换脚本',
  74 + loading: false,
  75 + });
  76 + editId.value = data.record.id;
  77 + setFieldsValue(data.record);
  78 + break;
  79 + case false:
  80 + isView.value = true;
  81 + setDrawerProps({
  82 + showFooter: unref(isView),
  83 + title: '新增转换脚本',
  84 + loading: false,
  85 + });
  86 + break;
  87 + }
  88 + });
  89 + const [registerForm, { validate, setFieldsValue, resetFields }] = useForm({
  90 + showActionButtonGroup: false,
  91 + colProps: { span: 24 },
  92 + schemas: formSchema,
  93 + });
  94 +
  95 + // 初始化编辑器
  96 + const initEditor = (jsScript?: string) => {
  97 + aceEditor.value = ace.edit(aceRef.value, {
  98 + maxLines: 12, // 最大行数,超过会自动出现滚动条
  99 + minLines: 12, // 最小行数,还未到最大行数时,编辑器会自动伸缩大小
  100 + fontSize: 14, // 编辑器内字体大小
  101 + theme: 'ace/theme/chrome', // 默认设置的主题
  102 + mode: 'ace/mode/javascript', // 默认设置的语言模式
  103 + tabSize: 2, // 制表符设置为 4 个空格大小
  104 + });
  105 +
  106 + aceEditor.value.setOptions({
  107 + enableBasicAutocompletion: true,
  108 + enableLiveAutocompletion: true,
  109 + theme: getAceClass.value === 'dark' ? 'ace/theme/terminal' : 'ace/theme/chrome',
  110 + });
  111 + aceEditor.value.setValue(jsScript ?? 'return {msg: msg, metadata: metadata};');
  112 + beautify(aceEditor.value.session);
  113 + };
  114 +
  115 + const testTransformFunc = () => {
  116 + closeDrawer();
  117 + const jsCode = aceEditor.value.getValue();
  118 + emit('isStatus', { status: 1, jsCode });
  119 + };
  120 + const handleSubmit = async () => {
  121 + const editIdPost = isUpdate.value ? { id: editId.value } : {};
  122 + try {
  123 + setDrawerProps({ confirmLoading: true });
  124 + const fieldsValue = await validate();
  125 + if (!fieldsValue) return;
  126 + await createOrEditTransformScriptApi({
  127 + configuration: {
  128 + jsScript: aceEditor.value.getValue(),
  129 + },
  130 + type: 'org.thingsboard.rule.engine.transform.TbTransformMsgNode',
  131 + ...fieldsValue,
  132 + ...editIdPost,
  133 + });
  134 + closeDrawer();
  135 + emit('success');
  136 + const { createMessage } = useMessage();
  137 + createMessage.success('保存成功');
  138 + } catch (e) {
  139 + } finally {
  140 + setTimeout(() => {
  141 + setDrawerProps({ confirmLoading: false });
  142 + }, 300);
  143 + }
  144 + };
  145 + const handleFormat = () => {
  146 + beautify(aceEditor.value.session);
  147 + };
  148 + defineExpose({ aceEditor });
  149 +</script>
1 -<template>  
2 - <div>  
3 - <a-form  
4 - ref="formRef"  
5 - :model="scriptForm"  
6 - name="basic"  
7 - :label-col="{ span: 4 }"  
8 - :wrapper-col="{ span: 16 }"  
9 - autocomplete="off"  
10 - >  
11 - <a-form-item  
12 - :label="ifAdd ? '名称' : '输入参数(params)'"  
13 - :name="ifAdd ? 'name' : 'params'"  
14 - :rules="[{ required: true, message: ifAdd ? '请输入脚本名称' : '请输入参数' }]"  
15 - >  
16 - <a-input  
17 - v-if="ifAdd"  
18 - :maxlength="36"  
19 - v-model:value="scriptForm.name"  
20 - placeholder="请输入脚本名称"  
21 - />  
22 - <a-input  
23 - @change="handleInputChange"  
24 - v-else  
25 - v-model:value="scriptForm.params"  
26 - placeholder="请输入参数"  
27 - />  
28 - </a-form-item>  
29 - <a-form-item  
30 - label="上报数据类型"  
31 - name="dataType"  
32 - :rules="[{ required: true, message: '请选择上报数据类型' }]"  
33 - >  
34 - <a-space direction="vertical">  
35 - <a-radio-group v-model:value="scriptForm.dataType" :options="typeOptions" />  
36 - </a-space>  
37 - </a-form-item>  
38 - <a-form-item  
39 - label="保存原始数据"  
40 - name="saveOriginalData"  
41 - :rules="[{ required: true, message: '请选择保存原始数据' }]"  
42 - >  
43 - <a-space direction="vertical">  
44 - <a-radio-group v-model:value="scriptForm.saveOriginalData" :options="originalOptions" />  
45 - </a-space>  
46 - </a-form-item>  
47 - <a-form-item label="脚本内容" :name="ifAdd ? 'convertJs' : 'script'">  
48 - <Card title="脚本内容" :bodyStyle="{ padding: 0, height: '280px' }">  
49 - <template #extra>  
50 - <a-button @click="handleFormat" size="small">格式化</a-button>  
51 - <Tooltip :title="defaultTitle" class="ml-2">  
52 - <QuestionCircleOutlined style="font-size: 1rem" />  
53 - </Tooltip>  
54 - </template>  
55 - <div ref="aceRef" class="overflow-hidden"></div>  
56 - </Card>  
57 - <Button @click="handleCopy" class="mt-4">  
58 - <template #icon>  
59 - <CopyOutlined />  
60 - </template>  
61 - copy  
62 - </Button>  
63 - </a-form-item>  
64 - <a-form-item  
65 - :label="ifAdd ? '备注' : '输出参数(output)'"  
66 - :name="ifAdd ? 'description' : 'output'"  
67 - >  
68 - <a-textarea  
69 - :rows="3"  
70 - v-if="ifAdd"  
71 - v-model:value="scriptForm.description"  
72 - placeholder="请输入备注"  
73 - :maxlength="255"  
74 - />  
75 - <a-textarea  
76 - :rows="3"  
77 - v-else  
78 - v-model:value="scriptForm.output"  
79 - placeholder="输出参数为服务端返回的内容"  
80 - :maxlength="255"  
81 - />  
82 - </a-form-item>  
83 - </a-form>  
84 - </div>  
85 -</template>  
86 -<script setup lang="ts">  
87 - import { ref, unref, reactive, onMounted, toRefs, nextTick } from 'vue';  
88 - import ace from 'ace-builds';  
89 - import { Card, Button, Tooltip } from 'ant-design-vue';  
90 - import 'ace-builds/src-noconflict/theme-chrome'; // 默认设置的主题  
91 - import 'ace-builds/src-noconflict/mode-javascript'; // 默认设置的语言模式  
92 - import { beautify } from 'ace-builds/src-noconflict/ext-beautify.js';  
93 - import { CopyOutlined } from '@ant-design/icons-vue';  
94 - import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';  
95 - import { useMessage } from '/@/hooks/web/useMessage';  
96 - import { findDictItemByCode } from '/@/api/system/dict';  
97 - import { QuestionCircleOutlined } from '@ant-design/icons-vue';  
98 - import { defaultTitle } from './config.data';  
99 -  
100 - defineEmits(['register']);  
101 - const props = defineProps({  
102 - ifAdd: { type: Boolean, default: true },  
103 - });  
104 - const scriptForm = reactive({  
105 - name: '',  
106 - description: '',  
107 - convertJs: '',  
108 - script: '',  
109 - params: '',  
110 - output: '',  
111 - dataType: 'HEX',  
112 - saveOriginalData: 'true',  
113 - });  
114 - const reportTypeOptions = reactive({  
115 - typeOptions: [],  
116 - originalOptions: [],  
117 - });  
118 - const { originalOptions, typeOptions } = toRefs(reportTypeOptions);  
119 - const { createMessage } = useMessage();  
120 - const { clipboardRef, copiedRef } = useCopyToClipboard();  
121 - const aceEditor = ref();  
122 - const aceRef = ref();  
123 - const setDefaultRadio = (p1, p2) => {  
124 - scriptForm.dataType = p1;  
125 - scriptForm.saveOriginalData = p2;  
126 - };  
127 - onMounted(async () => {  
128 - const res: any = await findDictItemByCode({  
129 - dictCode: 'report_data_type',  
130 - });  
131 - const resOriginal: any = await findDictItemByCode({  
132 - dictCode: 'original_data',  
133 - });  
134 - reportTypeOptions.typeOptions = res.map((m) => {  
135 - return { label: m.itemText, value: m.itemValue };  
136 - });  
137 - reportTypeOptions.originalOptions = resOriginal.map((m) => {  
138 - return { label: m.itemText, value: m.itemValue };  
139 - });  
140 - });  
141 - // 初始化编辑器  
142 - const initEditor = () => {  
143 - aceEditor.value = ace.edit(aceRef.value, {  
144 - maxLines: 12, // 最大行数,超过会自动出现滚动条  
145 - minLines: 12, // 最小行数,还未到最大行数时,编辑器会自动伸缩大小  
146 - fontSize: 14, // 编辑器内字体大小  
147 - theme: 'ace/theme/chrome', // 默认设置的主题  
148 - mode: 'ace/mode/javascript', // 默认设置的语言模式  
149 - tabSize: 2, // 制表符设置为 4 个空格大小  
150 - });  
151 -  
152 - aceEditor.value.setOptions({  
153 - enableBasicAutocompletion: true,  
154 - enableLiveAutocompletion: true,  
155 - });  
156 - aceEditor.value.setValue('');  
157 - beautify(aceEditor.value.session);  
158 - // scriptForm.convertJs = aceEditor.value.getValue();  
159 - };  
160 - const handleCopy = () => {  
161 - const valueRef = aceEditor.value.getValue();  
162 - const value = unref(valueRef);  
163 - if (!value) {  
164 - createMessage.warning('请输入要拷贝的内容!');  
165 - return;  
166 - }  
167 - clipboardRef.value = value;  
168 - if (unref(copiedRef)) {  
169 - createMessage.success('复制成功!');  
170 - }  
171 - };  
172 - const formRef = ref();  
173 - const getFormData = async () => {  
174 - const value = await formRef.value.validateFields();  
175 - if (props.ifAdd) {  
176 - scriptForm.convertJs = aceEditor.value.getValue();  
177 - if (scriptForm.convertJs == '') {  
178 - createMessage.error('请编写脚本内容');  
179 - throw '请编写脚本内容';  
180 - }  
181 - } else {  
182 - scriptForm.script = aceEditor.value.getValue();  
183 - if (scriptForm.script == '') {  
184 - createMessage.error('请编写脚本内容');  
185 - throw '请编写脚本内容';  
186 - }  
187 - }  
188 - if (!value) return;  
189 - if (scriptForm.params) {  
190 - const trimParams = scriptForm.params.replace(/\s*/g, '');  
191 - Reflect.set(value, 'params', trimParams);  
192 - }  
193 - if (scriptForm.convertJs.length > 1000) {  
194 - createMessage.error('脚本内容长度不能大于1000');  
195 - throw '脚本内容长度不能大于1000';  
196 - }  
197 - return {  
198 - ...value,  
199 - ...{ convertJs: props.ifAdd ? scriptForm.convertJs : null },  
200 - ...{ script: !props.ifAdd ? scriptForm.script : null },  
201 - ...{ saveOriginalData: scriptForm.saveOriginalData === 'false' ? false : true },  
202 - };  
203 - };  
204 - const handleInputChange = (e) => {  
205 - const trimParams = e.target.value.replace(/\s*/g, '');  
206 - Reflect.set(scriptForm, 'params', trimParams);  
207 - };  
208 - const setFormData = (v) => {  
209 - if (v) {  
210 - for (let i in scriptForm) {  
211 - Reflect.set(scriptForm, i, v[i]);  
212 - }  
213 - nextTick(() => {  
214 - setTimeout(() => {  
215 - scriptForm.saveOriginalData = v.saveOriginalData === false ? 'false' : 'true';  
216 - scriptForm.dataType = v.dataType;  
217 - }, 10);  
218 - });  
219 - aceEditor.value.setValue(v.convertJs);  
220 - handleFormat();  
221 - }  
222 - };  
223 - const setScriptContentData = (v) => {  
224 - aceEditor.value.setValue(v);  
225 - handleFormat();  
226 - };  
227 - const resetFormData = () => {  
228 - for (let i in scriptForm) {  
229 - Reflect.set(scriptForm, i, '');  
230 - }  
231 - };  
232 - const setScriptOutputData = (v) => {  
233 - scriptForm.output = v;  
234 - };  
235 - const handleFormat = () => beautify(aceEditor.value.session);  
236 -  
237 - defineExpose({  
238 - initEditor,  
239 - getFormData,  
240 - resetFormData,  
241 - setFormData,  
242 - setScriptContentData,  
243 - setScriptOutputData,  
244 - setDefaultRadio,  
245 - });  
246 -</script>  
247 -<style lang="less" scoped>  
248 - @import url('./ConverScriptModal.less');  
249 -</style> 1 +<template>
  2 + <div>
  3 + <a-form
  4 + ref="formRef"
  5 + :model="scriptForm"
  6 + name="basic"
  7 + :label-col="{ span: 4 }"
  8 + :wrapper-col="{ span: 16 }"
  9 + autocomplete="off"
  10 + >
  11 + <a-form-item
  12 + :label="ifAdd ? '名称' : '输入参数(params)'"
  13 + :name="ifAdd ? 'name' : 'params'"
  14 + :rules="[{ required: true, message: ifAdd ? '请输入脚本名称' : '请输入参数' }]"
  15 + >
  16 + <a-input
  17 + v-if="ifAdd"
  18 + :maxlength="36"
  19 + @change="handleInputChange"
  20 + v-model:value="scriptForm.name"
  21 + placeholder="请输入脚本名称"
  22 + />
  23 + <a-input
  24 + @change="handleInputChange"
  25 + v-else
  26 + v-model:value="scriptForm.params"
  27 + placeholder="请输入参数"
  28 + />
  29 + </a-form-item>
  30 + <a-form-item
  31 + label="上报数据类型"
  32 + name="dataType"
  33 + :rules="[{ required: false, message: '请选择上报数据类型' }]"
  34 + >
  35 + <a-space direction="vertical">
  36 + <a-radio-group v-model:value="scriptForm.dataType" :options="typeOptions" />
  37 + </a-space>
  38 + </a-form-item>
  39 + <a-form-item
  40 + label="脚本类型"
  41 + name="scriptType"
  42 + :rules="[{ required: true, message: '请选择脚本类型' }]"
  43 + >
  44 + <a-space direction="vertical">
  45 + <a-radio-group
  46 + @change="handleScriptType"
  47 + v-model:value="scriptForm.scriptType"
  48 + :options="scriptTypeOptions"
  49 + />
  50 + </a-space>
  51 + </a-form-item>
  52 + <a-form-item
  53 + label="保存原始数据"
  54 + name="saveOriginalData"
  55 + :rules="[{ required: true, message: '请选择保存原始数据' }]"
  56 + >
  57 + <a-space direction="vertical">
  58 + <a-radio-group v-model:value="scriptForm.saveOriginalData" :options="originalOptions" />
  59 + </a-space>
  60 + </a-form-item>
  61 + <a-form-item label="脚本内容" :name="ifAdd ? 'convertJs' : 'script'">
  62 + <Card title="脚本内容" :bodyStyle="{ padding: 0, height: '280px' }">
  63 + <template #extra>
  64 + <a-button @click="handleFormat" size="small">格式化</a-button>
  65 + <Tooltip :title="defaultTitle" class="ml-2">
  66 + <QuestionCircleOutlined style="font-size: 1rem" />
  67 + </Tooltip>
  68 + </template>
  69 + {{ getAceClass }}
  70 + <div ref="aceRef" class="overflow-hidden"></div>
  71 + </Card>
  72 + <Button @click="handleCopy" class="mt-4">
  73 + <template #icon>
  74 + <CopyOutlined />
  75 + </template>
  76 + copy
  77 + </Button>
  78 + </a-form-item>
  79 + <a-form-item
  80 + :label="ifAdd ? '备注' : '输出参数(output)'"
  81 + :name="ifAdd ? 'description' : 'output'"
  82 + >
  83 + <a-textarea
  84 + :rows="3"
  85 + v-if="ifAdd"
  86 + v-model:value="scriptForm.description"
  87 + placeholder="请输入备注"
  88 + :maxlength="255"
  89 + />
  90 + <a-textarea
  91 + :rows="3"
  92 + v-else
  93 + v-model:value="scriptForm.output"
  94 + placeholder="输出参数为服务端返回的内容"
  95 + :maxlength="255"
  96 + />
  97 + </a-form-item>
  98 + </a-form>
  99 + </div>
  100 +</template>
  101 +<script setup lang="ts">
  102 + import { ref, unref, reactive, onMounted, toRefs, nextTick, computed } from 'vue';
  103 + import ace from 'ace-builds';
  104 + import { Card, Button, Tooltip } from 'ant-design-vue';
  105 + import 'ace-builds/src-noconflict/theme-chrome'; // 默认设置的主题
  106 + import 'ace-builds/src-noconflict/theme-terminal'; // 默认设置的主题
  107 + import 'ace-builds/src-noconflict/mode-javascript'; // 默认设置的语言模式
  108 + import { beautify } from 'ace-builds/src-noconflict/ext-beautify.js';
  109 + import { CopyOutlined } from '@ant-design/icons-vue';
  110 + import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
  111 + import { useMessage } from '/@/hooks/web/useMessage';
  112 + import { findDictItemByCode } from '/@/api/system/dict';
  113 + import { QuestionCircleOutlined } from '@ant-design/icons-vue';
  114 + import { defaultTitle, defaultScriptTypeContent } from './config.data';
  115 + import { useAppStore } from '/@/store/modules/app';
  116 +
  117 + defineEmits(['register']);
  118 + const props = defineProps({
  119 + ifAdd: { type: Boolean, default: true },
  120 + });
  121 +
  122 + const scriptForm = reactive({
  123 + name: '',
  124 + description: '',
  125 + convertJs: '',
  126 + script: '',
  127 + params: '',
  128 + output: '',
  129 + dataType: 'HEX',
  130 + scriptType: 'TRANSPORT_TCP_UP',
  131 + saveOriginalData: 'true',
  132 + });
  133 +
  134 + const reportTypeOptions = reactive({
  135 + typeOptions: [],
  136 + originalOptions: [],
  137 + scriptTypeOptions: [],
  138 + });
  139 +
  140 + const { originalOptions, typeOptions, scriptTypeOptions } = toRefs(reportTypeOptions);
  141 +
  142 + const { createMessage } = useMessage();
  143 +
  144 + const { clipboardRef, copiedRef } = useCopyToClipboard();
  145 +
  146 + const aceEditor = ref();
  147 +
  148 + const aceRef = ref();
  149 +
  150 + const userStore = useAppStore();
  151 +
  152 + const getAceClass = computed((): string => userStore.getDarkMode);
  153 +
  154 + const setDefaultRadio = (p1, p2, p3) => {
  155 + scriptForm.dataType = p1;
  156 + scriptForm.saveOriginalData = p2;
  157 + scriptForm.scriptType = p3;
  158 + };
  159 +
  160 + const getDictValue = async (dict_type) => {
  161 + const res = await findDictItemByCode({
  162 + dictCode: dict_type,
  163 + });
  164 + return res.map((m) => {
  165 + return { label: m.itemText, value: m.itemValue };
  166 + });
  167 + };
  168 +
  169 + onMounted(async () => {
  170 + reportTypeOptions.typeOptions = (await getDictValue('report_data_type')) as never as any;
  171 + reportTypeOptions.originalOptions = (await getDictValue('original_data')) as never as any;
  172 + reportTypeOptions.scriptTypeOptions = (await getDictValue('script_type')) as never as any;
  173 + });
  174 +
  175 + // 初始化编辑器
  176 + const initEditor = () => {
  177 + aceEditor.value = ace.edit(aceRef.value, {
  178 + maxLines: 12, // 最大行数,超过会自动出现滚动条
  179 + minLines: 12, // 最小行数,还未到最大行数时,编辑器会自动伸缩大小
  180 + fontSize: 14, // 编辑器内字体大小
  181 + theme: 'ace/theme/chrome', // 默认设置的主题
  182 + mode: 'ace/mode/javascript', // 默认设置的语言模式
  183 + tabSize: 2, // 制表符设置为 4 个空格大小
  184 + });
  185 +
  186 + aceEditor.value.setOptions({
  187 + enableBasicAutocompletion: true,
  188 + enableLiveAutocompletion: true,
  189 + theme: getAceClass.value === 'dark' ? 'ace/theme/terminal' : 'ace/theme/chrome',
  190 + });
  191 + aceEditor.value.setValue('');
  192 + beautify(aceEditor.value.session);
  193 + switchScriptTypeGetContent('TRANSPORT_TCP_UP');
  194 + };
  195 +
  196 + const handleScriptType = ({ target }) => {
  197 + const { value } = target;
  198 + switchScriptTypeGetContent(value);
  199 + };
  200 +
  201 + const switchScriptTypeGetContent = (type) => {
  202 + if (type === 'TRANSPORT_TCP_DOWN')
  203 + Reflect.set(
  204 + defaultScriptTypeContent,
  205 + 'TRANSPORT_TCP_DOWN',
  206 + `out.datas = "${scriptForm.params}";out.deviceName = "sensor";`
  207 + );
  208 + aceEditor.value.setValue(defaultScriptTypeContent[type]);
  209 + };
  210 +
  211 + const handleCopy = () => {
  212 + const valueRef = aceEditor.value.getValue();
  213 + const value = unref(valueRef);
  214 + if (!value) {
  215 + createMessage.warning('请输入要拷贝的内容!');
  216 + return;
  217 + }
  218 + clipboardRef.value = value;
  219 + if (unref(copiedRef)) {
  220 + createMessage.success('复制成功!');
  221 + }
  222 + };
  223 +
  224 + const formRef = ref();
  225 +
  226 + const getFormData = async () => {
  227 + const value = await formRef.value.validateFields();
  228 + if (props.ifAdd) {
  229 + scriptForm.convertJs = aceEditor.value.getValue();
  230 + if (scriptForm.convertJs == '') {
  231 + createMessage.error('请编写脚本内容');
  232 + throw '请编写脚本内容';
  233 + }
  234 + } else {
  235 + scriptForm.script = aceEditor.value.getValue();
  236 + if (scriptForm.script == '') {
  237 + createMessage.error('请编写脚本内容');
  238 + throw '请编写脚本内容';
  239 + }
  240 + }
  241 + if (!value) return;
  242 + if (scriptForm.params) {
  243 + const trimParams = scriptForm.params.replace(/\s*/g, '');
  244 + Reflect.set(value, 'params', trimParams);
  245 + }
  246 + if (scriptForm.convertJs.length > 1000) {
  247 + createMessage.error('脚本内容长度不能大于1000');
  248 + throw '脚本内容长度不能大于1000';
  249 + }
  250 + return {
  251 + ...value,
  252 + ...{ convertJs: props.ifAdd ? scriptForm.convertJs : null },
  253 + ...{ script: !props.ifAdd ? scriptForm.script : null },
  254 + ...{ saveOriginalData: scriptForm.saveOriginalData === 'false' ? false : true },
  255 + };
  256 + };
  257 +
  258 + const handleInputChange = (e) => {
  259 + const trimParams = e.target.value.replace(/\s*/g, '');
  260 + Reflect.set(scriptForm, 'params', trimParams);
  261 + if (scriptForm.scriptType === 'TRANSPORT_TCP_DOWN') {
  262 + aceEditor.value.setValue(`out.datas = "${scriptForm.params}";out.deviceName = "sensor";`);
  263 + }
  264 + };
  265 +
  266 + const setFormData = (v) => {
  267 + if (v) {
  268 + for (let i in scriptForm) {
  269 + Reflect.set(scriptForm, i, v[i]);
  270 + }
  271 + nextTick(() => {
  272 + setTimeout(() => {
  273 + scriptForm.saveOriginalData = v.saveOriginalData === false ? 'false' : 'true';
  274 + scriptForm.dataType = v.dataType;
  275 + }, 10);
  276 + });
  277 + aceEditor.value.setValue(v.convertJs);
  278 + handleFormat();
  279 + }
  280 + };
  281 +
  282 + const setScriptContentData = (v) => {
  283 + aceEditor.value.setValue(v);
  284 + handleFormat();
  285 + };
  286 +
  287 + const resetFormData = () => {
  288 + for (let i in scriptForm) {
  289 + Reflect.set(scriptForm, i, '');
  290 + }
  291 + };
  292 +
  293 + const setScriptOutputData = (v) => {
  294 + scriptForm.output = v;
  295 + };
  296 +
  297 + const handleFormat = () => beautify(aceEditor.value.session);
  298 +
  299 + defineExpose({
  300 + initEditor,
  301 + getFormData,
  302 + resetFormData,
  303 + setFormData,
  304 + setScriptContentData,
  305 + setScriptOutputData,
  306 + setDefaultRadio,
  307 + });
  308 +</script>
  309 +<style lang="less" scoped>
  310 + @import url('./ConverScriptModal.less');
  311 +</style>
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="isTest ? false : true" ref="converScriptRef" />  
14 - </BasicModal>  
15 - </div>  
16 -</template>  
17 -<script setup lang="ts">  
18 - import { ref, computed, unref, reactive } from 'vue';  
19 - import { BasicModal, useModalInner } from '/@/components/Modal';  
20 - import ConverScript from './ConverScript.vue';  
21 - import {  
22 - createOrEditScriptManage,  
23 - getScriptManageDetail,  
24 - testScriptManage,  
25 - } from '/@/api/scriptmanage/scriptManager';  
26 - import { useMessage } from '/@/hooks/web/useMessage';  
27 -  
28 - const emits = defineEmits(['success', 'register']);  
29 - const { createMessage } = useMessage();  
30 - const converScriptRef = ref<InstanceType<typeof ConverScript>>();  
31 - const getTitle = computed(() => (isUpdate.value ? '编辑转换脚本' : '新增转换脚本'));  
32 - const isUpdate = ref(false);  
33 - const isViewDetail = ref('');  
34 - const isTest = ref(false);  
35 - const isText = ref('');  
36 - const isTitle = ref('');  
37 - const editData = reactive({  
38 - data: {},  
39 - });  
40 - const [register, { setModalProps, closeModal }] = useModalInner(async (data) => {  
41 - setModalProps({ loading: true });  
42 - handleCancel(false);  
43 - isUpdate.value = data.isUpdate;  
44 - isViewDetail.value = data.isView;  
45 - isTest.value = data.isTest;  
46 - isText.value = data.isText;  
47 - isTitle.value = data.isTitle;  
48 - editData.data = data.record;  
49 - setModalProps({ loading: false });  
50 - converScriptRef.value?.initEditor();  
51 - if (!unref(isViewDetail)) {  
52 - const title =  
53 - unref(isTitle) == 'edit'  
54 - ? '编辑转换脚本'  
55 - : unref(isTitle) == 'add'  
56 - ? '新增转换脚本'  
57 - : '测试转换脚本';  
58 - const okText = isText.value == 'test' ? '测试' : '确定';  
59 - if (unref(isTitle) == 'add') {  
60 - converScriptRef.value?.setDefaultRadio('HEX', 'true');  
61 - }  
62 - if (unref(isTitle) == 'edit') {  
63 - converScriptRef.value?.setFormData(data.record);  
64 - }  
65 - if (unref(isTitle) == 'test') {  
66 - if (data.record) {  
67 - const res = await getScriptManageDetail(data.record);  
68 - converScriptRef.value?.setFormData(res);  
69 - } else {  
70 - converScriptRef.value?.setDefaultRadio('HEX', 'true');  
71 - }  
72 - }  
73 - setModalProps({ title, showOkBtn: true, showCancelBtn: true, okText });  
74 - if (!unref(isUpdate)) {  
75 - }  
76 - } else {  
77 - setModalProps({ showOkBtn: false, showCancelBtn: false, title: '查看转换脚本' });  
78 - const res = await getScriptManageDetail(data.record.id);  
79 - converScriptRef.value?.setFormData(res || {});  
80 - }  
81 - });  
82 -  
83 - const handleSubmit = async () => {  
84 - setModalProps({ confirmLoading: true });  
85 - try {  
86 - const val = await converScriptRef.value?.getFormData();  
87 - const tempObj = {  
88 - ...editData.data,  
89 - ...val,  
90 - };  
91 - const res: any =  
92 - isText.value == 'test'  
93 - ? await testScriptManage(val)  
94 - : await createOrEditScriptManage(tempObj);  
95 - createMessage.success(  
96 - unref(isTitle) == 'edit'  
97 - ? '编辑转换脚本成功'  
98 - : unref(isTitle) == 'add'  
99 - ? '新增转换脚本成功'  
100 - : '测试转换脚本成功'  
101 - );  
102 - if (unref(isTitle) == 'add' || unref(isTitle) == 'edit') {  
103 - setTimeout(() => {  
104 - closeModal();  
105 - }, 10);  
106 - emits('success', {  
107 - res,  
108 - text: isText.value,  
109 - });  
110 - } else {  
111 - if (res) {  
112 - converScriptRef.value?.setScriptOutputData(res?.output || res?.error);  
113 - }  
114 - }  
115 - } finally {  
116 - setModalProps({ confirmLoading: false });  
117 - }  
118 - };  
119 - const handleCancel = (flag) => {  
120 - if (flag) {  
121 - closeModal();  
122 - }  
123 - converScriptRef.value?.resetFormData();  
124 - };  
125 -</script>  
126 -<style lang="less" scoped>  
127 - @import url('./ConverScriptModal.less');  
128 -</style> 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="isTest ? false : true" ref="converScriptRef" />
  14 + </BasicModal>
  15 + </div>
  16 +</template>
  17 +<script setup lang="ts">
  18 + import { ref, computed, unref, reactive } from 'vue';
  19 + import { BasicModal, useModalInner } from '/@/components/Modal';
  20 + import ConverScript from './ConverScript.vue';
  21 + import {
  22 + createOrEditScriptManage,
  23 + getScriptManageDetail,
  24 + testScriptManage,
  25 + } from '/@/api/scriptmanage/scriptManager';
  26 + import { useMessage } from '/@/hooks/web/useMessage';
  27 +
  28 + const emits = defineEmits(['success', 'register']);
  29 + const { createMessage } = useMessage();
  30 + const converScriptRef = ref<InstanceType<typeof ConverScript>>();
  31 + const getTitle = computed(() => (isUpdate.value ? '编辑转换脚本' : '新增转换脚本'));
  32 + const isUpdate = ref(false);
  33 + const isViewDetail = ref('');
  34 + const isTest = ref(false);
  35 + const isText = ref('');
  36 + const isTitle = ref('');
  37 + const editData = reactive({
  38 + data: {},
  39 + });
  40 + const [register, { setModalProps, closeModal }] = useModalInner(async (data) => {
  41 + setModalProps({ loading: true });
  42 + handleCancel(false);
  43 + isUpdate.value = data.isUpdate;
  44 + isViewDetail.value = data.isView;
  45 + isTest.value = data.isTest;
  46 + isText.value = data.isText;
  47 + isTitle.value = data.isTitle;
  48 + editData.data = data.record;
  49 + setModalProps({ loading: false });
  50 + converScriptRef.value?.initEditor();
  51 + if (!unref(isViewDetail)) {
  52 + const title =
  53 + unref(isTitle) == 'edit'
  54 + ? '编辑转换脚本'
  55 + : unref(isTitle) == 'add'
  56 + ? '新增转换脚本'
  57 + : '测试转换脚本';
  58 + const okText = isText.value == 'test' ? '测试' : '确定';
  59 + if (unref(isTitle) == 'add') {
  60 + converScriptRef.value?.setDefaultRadio('HEX', 'true', 'TRANSPORT_TCP_UP');
  61 + }
  62 + if (unref(isTitle) == 'edit') {
  63 + converScriptRef.value?.setFormData(data.record);
  64 + }
  65 + if (unref(isTitle) == 'test') {
  66 + if (data.record) {
  67 + const res = await getScriptManageDetail(data.record);
  68 + converScriptRef.value?.setFormData(res);
  69 + } else {
  70 + converScriptRef.value?.setDefaultRadio('HEX', 'true', 'TRANSPORT_TCP_UP');
  71 + }
  72 + }
  73 + setModalProps({ title, showOkBtn: true, showCancelBtn: true, okText });
  74 + if (!unref(isUpdate)) {
  75 + }
  76 + } else {
  77 + setModalProps({ showOkBtn: false, showCancelBtn: false, title: '查看转换脚本' });
  78 + const res = await getScriptManageDetail(data.record.id);
  79 + converScriptRef.value?.setFormData(res || {});
  80 + }
  81 + });
  82 +
  83 + const handleSubmit = async () => {
  84 + setModalProps({ confirmLoading: true });
  85 + try {
  86 + const val = await converScriptRef.value?.getFormData();
  87 + const tempObj = {
  88 + ...editData.data,
  89 + ...val,
  90 + };
  91 + const res: any =
  92 + isText.value == 'test'
  93 + ? await testScriptManage(val)
  94 + : await createOrEditScriptManage(tempObj);
  95 + createMessage.success(
  96 + unref(isTitle) == 'edit'
  97 + ? '编辑转换脚本成功'
  98 + : unref(isTitle) == 'add'
  99 + ? '新增转换脚本成功'
  100 + : '测试转换脚本成功'
  101 + );
  102 + if (unref(isTitle) == 'add' || unref(isTitle) == 'edit') {
  103 + setTimeout(() => {
  104 + closeModal();
  105 + }, 10);
  106 + emits('success', {
  107 + res,
  108 + text: isText.value,
  109 + });
  110 + } else {
  111 + if (res) {
  112 + converScriptRef.value?.setScriptOutputData(res?.output || res?.error);
  113 + }
  114 + }
  115 + } finally {
  116 + setModalProps({ confirmLoading: false });
  117 + }
  118 + };
  119 + const handleCancel = (flag) => {
  120 + if (flag) {
  121 + closeModal();
  122 + }
  123 + converScriptRef.value?.resetFormData();
  124 + };
  125 +</script>
  126 +<style lang="less" scoped>
  127 + @import url('./ConverScriptModal.less');
  128 +</style>
1 -import { BasicColumn, FormSchema } from '/@/components/Table';  
2 -import moment from 'moment';  
3 -import { h } from 'vue';  
4 -  
5 -// 表格配置  
6 -export const columns: BasicColumn[] = [  
7 - {  
8 - title: '脚本名称',  
9 - dataIndex: 'name',  
10 - width: 80,  
11 - },  
12 - {  
13 - title: '脚本状态',  
14 - dataIndex: 'status',  
15 - width: 120,  
16 - slots: { customRender: 'status' },  
17 - },  
18 - {  
19 - title: '脚本内容',  
20 - dataIndex: 'convertJs',  
21 - width: 120,  
22 - slots: { customRender: 'convertJs' },  
23 - },  
24 - {  
25 - title: '备注',  
26 - dataIndex: 'description',  
27 - width: 120,  
28 - },  
29 - {  
30 - title: '创建时间',  
31 - dataIndex: 'createTime',  
32 - width: 180,  
33 - },  
34 -];  
35 -  
36 -// 查询配置  
37 -export const searchFormSchema: FormSchema[] = [  
38 - {  
39 - field: 'name',  
40 - label: '脚本名称',  
41 - component: 'Input',  
42 - colProps: { span: 6 },  
43 - componentProps: {  
44 - maxLength: 36,  
45 - placeholder: '请输入配置名称',  
46 - },  
47 - },  
48 - {  
49 - field: 'sendTime',  
50 - label: '创建时间',  
51 - component: 'RangePicker',  
52 - componentProps: {  
53 - showTime: {  
54 - defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')],  
55 - },  
56 - },  
57 - colProps: { span: 6 },  
58 - },  
59 -];  
60 -  
61 -export const defaultTitle = h('div', { style: 'background:#404040' }, [  
62 - h('h3', { style: 'color:white' }, '示例'),  
63 - h('h3', { style: 'color:white' }, '输入参数:'),  
64 - h('h3', { style: 'color:white' }, '0103040150008D3BBB'),  
65 - h('h3', { style: 'color:white' }, [  
66 - h('h3', { style: 'color:white' }, '脚本内容:'),  
67 - h(  
68 - 'h3',  
69 - { style: 'color:white' },  
70 - "out.humidity = (parseInt('0x'+params.substr(6, 4))*0.1).toFixed(2);"  
71 - ),  
72 - h(  
73 - 'h3',  
74 - { style: 'color:white' },  
75 - "out.temperature = (parseInt('0x'+params.substr(10, 4))*0.1).toFixed(2);"  
76 - ),  
77 - h('h3', { style: 'color:white' }, '输出参数:'),  
78 - h('h3', { style: 'color:white' }, "{'humidity':'33.60','temperature':'14.10'}"),  
79 - ]),  
80 -]); 1 +import { BasicColumn, FormSchema } from '/@/components/Table';
  2 +import moment from 'moment';
  3 +import { h } from 'vue';
  4 +
  5 +// 表格配置
  6 +export const columns: BasicColumn[] = [
  7 + {
  8 + title: '脚本名称',
  9 + dataIndex: 'name',
  10 + width: 80,
  11 + },
  12 + {
  13 + title: '脚本状态',
  14 + dataIndex: 'status',
  15 + width: 120,
  16 + slots: { customRender: 'status' },
  17 + },
  18 + {
  19 + title: '脚本内容',
  20 + dataIndex: 'convertJs',
  21 + width: 120,
  22 + slots: { customRender: 'convertJs' },
  23 + },
  24 + {
  25 + title: '备注',
  26 + dataIndex: 'description',
  27 + width: 120,
  28 + },
  29 + {
  30 + title: '创建时间',
  31 + dataIndex: 'createTime',
  32 + width: 180,
  33 + },
  34 +];
  35 +
  36 +// 查询配置
  37 +export const searchFormSchema: FormSchema[] = [
  38 + {
  39 + field: 'name',
  40 + label: '脚本名称',
  41 + component: 'Input',
  42 + colProps: { span: 6 },
  43 + componentProps: {
  44 + maxLength: 36,
  45 + placeholder: '请输入配置名称',
  46 + },
  47 + },
  48 + {
  49 + field: 'sendTime',
  50 + label: '创建时间',
  51 + component: 'RangePicker',
  52 + componentProps: {
  53 + showTime: {
  54 + defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')],
  55 + },
  56 + },
  57 + colProps: { span: 6 },
  58 + },
  59 +];
  60 +
  61 +export const defaultTitle = h('div', { style: 'background:#404040' }, [
  62 + h('h3', { style: 'color:white' }, '示例'),
  63 + h('h3', { style: 'color:white' }, '输入参数:'),
  64 + h('h3', { style: 'color:white' }, '0103040150008D3BBB'),
  65 + h('h3', { style: 'color:white' }, [
  66 + h('h3', { style: 'color:white' }, '脚本内容:'),
  67 + h(
  68 + 'h3',
  69 + { style: 'color:white' },
  70 + "out.humidity = (parseInt('0x'+params.substr(6, 4))*0.1).toFixed(2);"
  71 + ),
  72 + h(
  73 + 'h3',
  74 + { style: 'color:white' },
  75 + "out.temperature = (parseInt('0x'+params.substr(10, 4))*0.1).toFixed(2);"
  76 + ),
  77 + h('h3', { style: 'color:white' }, '输出参数:'),
  78 + h('h3', { style: 'color:white' }, "{'humidity':'33.60','temperature':'14.10'}"),
  79 + ]),
  80 +]);
  81 +
  82 +export const defaultScriptTypeContent = {
  83 + TRANSPORT_TCP_UP:
  84 + 'var attrData = {};var teleData = {};teleData.source= params;out.datas = teleData;out.telemetry =true;out.ackMsg = params;out.deviceName = "sensor";out.ts = Date.now();',
  85 + TRANSPORT_TCP_DOWN: 'out.datas = "";out.deviceName = "sensor";',
  86 + TRANSPORT_TCP_AUTH: 'out.password = params;out.success = params;',
  87 +};