Commit be4155d1b60f8c27a60318a3d9f85b4caa73fdd7
Merge branch 'ft' into 'main_dev'
fix: 修改Teambition上的问题 See merge request yunteng/thingskit-front!542
Showing
23 changed files
with
786 additions
and
388 deletions
@@ -10,6 +10,8 @@ export type SchedueParam = { | @@ -10,6 +10,8 @@ export type SchedueParam = { | ||
10 | data?: any; | 10 | data?: any; |
11 | code?: number; | 11 | code?: number; |
12 | jobId?: string; | 12 | jobId?: string; |
13 | + startTime?: number; | ||
14 | + endTime?: number; | ||
13 | }; | 15 | }; |
14 | 16 | ||
15 | export interface ReportModel { | 17 | export interface ReportModel { |
@@ -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) => { |
@@ -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 | ||
@@ -139,6 +142,8 @@ | @@ -139,6 +142,8 @@ | ||
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) => { |
@@ -6,6 +6,7 @@ | @@ -6,6 +6,7 @@ | ||
6 | <ParamsTable ref="paramsCellTableRef" :method="method" /> | 6 | <ParamsTable ref="paramsCellTableRef" :method="method" /> |
7 | <ParamsTest | 7 | <ParamsTest |
8 | @testParamsInterface="handleTestParamsInterface" | 8 | @testParamsInterface="handleTestParamsInterface" |
9 | + @closeTest="onCloseTest" | ||
9 | ref="testParamsRequestRef" | 10 | ref="testParamsRequestRef" |
10 | :data="dataMap.mapParamsObj" | 11 | :data="dataMap.mapParamsObj" |
11 | /> | 12 | /> |
@@ -21,6 +22,7 @@ | @@ -21,6 +22,7 @@ | ||
21 | <BodyTest | 22 | <BodyTest |
22 | v-if="bodyType !== 'none'" | 23 | v-if="bodyType !== 'none'" |
23 | @testBodyInterface="handleTestBodyInterface" | 24 | @testBodyInterface="handleTestBodyInterface" |
25 | + @closeTest="onCloseTest" | ||
24 | ref="testBodyRequestRef" | 26 | ref="testBodyRequestRef" |
25 | :data="dataMap.mapBodyObj" | 27 | :data="dataMap.mapBodyObj" |
26 | /> | 28 | /> |
@@ -29,6 +31,7 @@ | @@ -29,6 +31,7 @@ | ||
29 | <HeaderTable ref="editHeaderCellTableRef" :method="method" /> | 31 | <HeaderTable ref="editHeaderCellTableRef" :method="method" /> |
30 | <HeaderTest | 32 | <HeaderTest |
31 | @testHeaderInterface="handleTestHeaderInterface" | 33 | @testHeaderInterface="handleTestHeaderInterface" |
34 | + @closeTest="onCloseTest" | ||
32 | ref="testHeaderRequestRef" | 35 | ref="testHeaderRequestRef" |
33 | :data="dataMap.mapHeaderObj" | 36 | :data="dataMap.mapHeaderObj" |
34 | /> | 37 | /> |
@@ -107,6 +110,8 @@ | @@ -107,6 +110,8 @@ | ||
107 | excuteTestRef.value?.resetValue(true); | 110 | excuteTestRef.value?.resetValue(true); |
108 | }; | 111 | }; |
109 | 112 | ||
113 | + const onCloseTest = () => excuteTestRef.value?.resetValue(true); | ||
114 | + | ||
110 | //if-else-if-else分支优化 | 115 | //if-else-if-else分支优化 |
111 | const dataForTypeMap = [ | 116 | const dataForTypeMap = [ |
112 | [(type) => type === 'Params', (data) => paramsCellTableRef.value?.setValue(data)], | 117 | [(type) => type === 'Params', (data) => paramsCellTableRef.value?.setValue(data)], |
1 | <template> | 1 | <template> |
2 | <div> | 2 | <div> |
3 | <div class="mt-8"> | 3 | <div class="mt-8"> |
4 | - <div> | ||
5 | - <Button @click="handleTest" type="primary"> 测试接口 </Button> | 4 | + <div class="flex"> |
5 | + <Button @click="handleTest" type="primary"> 打开测试接口 </Button> | ||
6 | + <Button class="ml-2" @click="onCloseTest" type="primary"> 关闭测试接口 </Button> | ||
6 | </div> | 7 | </div> |
7 | <div v-if="showTestEditCell" class="mt-8"> | 8 | <div v-if="showTestEditCell" class="mt-8"> |
8 | <a-row type="flex" justify="center"> | 9 | <a-row type="flex" justify="center"> |
@@ -31,8 +32,9 @@ | @@ -31,8 +32,9 @@ | ||
31 | import moment from 'moment'; | 32 | import moment from 'moment'; |
32 | import { useUtils } from '../../../hooks/useUtils'; | 33 | import { useUtils } from '../../../hooks/useUtils'; |
33 | import JsonEditor from '../../SimpleRequest/components/jsonEditor.vue'; | 34 | import JsonEditor from '../../SimpleRequest/components/jsonEditor.vue'; |
35 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
34 | 36 | ||
35 | - const emits = defineEmits(['testBodyInterface']); | 37 | + const emits = defineEmits(['testBodyInterface', 'closeTest']); |
36 | 38 | ||
37 | const props = defineProps({ | 39 | const props = defineProps({ |
38 | data: { | 40 | data: { |
@@ -40,6 +42,8 @@ | @@ -40,6 +42,8 @@ | ||
40 | }, | 42 | }, |
41 | }); | 43 | }); |
42 | 44 | ||
45 | + const { createMessage } = useMessage(); | ||
46 | + | ||
43 | const showTestEditCell = ref(false); | 47 | const showTestEditCell = ref(false); |
44 | 48 | ||
45 | const excuteData = ref({}); | 49 | const excuteData = ref({}); |
@@ -111,6 +115,7 @@ | @@ -111,6 +115,7 @@ | ||
111 | return { | 115 | return { |
112 | key, | 116 | key, |
113 | value, | 117 | value, |
118 | + required: it.required, | ||
114 | }; | 119 | }; |
115 | }); | 120 | }); |
116 | }; | 121 | }; |
@@ -120,6 +125,11 @@ | @@ -120,6 +125,11 @@ | ||
120 | let params: any = {}; | 125 | let params: any = {}; |
121 | if (props.data?.type === 'x-www-form-urlencoded' || props.data?.type === 'form-data') { | 126 | if (props.data?.type === 'x-www-form-urlencoded' || props.data?.type === 'form-data') { |
122 | const getTable = getTestTableKeyValue(); | 127 | const getTable = getTestTableKeyValue(); |
128 | + const hasRequired = getTable?.some((it) => it.required === true && !it.value); | ||
129 | + if (hasRequired) { | ||
130 | + createMessage.error('参数不能为空'); | ||
131 | + throw new Error('参数不能为空'); | ||
132 | + } | ||
123 | getTable?.map((it) => (params[it.key!] = it.value!)); | 133 | getTable?.map((it) => (params[it.key!] = it.value!)); |
124 | } else if (props.data?.type === 'json') { | 134 | } else if (props.data?.type === 'json') { |
125 | params = jsonEditorRef.value?.getJsonValue(); | 135 | params = jsonEditorRef.value?.getJsonValue(); |
@@ -141,6 +151,11 @@ | @@ -141,6 +151,11 @@ | ||
141 | testResult.value = ''; | 151 | testResult.value = ''; |
142 | }; | 152 | }; |
143 | 153 | ||
154 | + const onCloseTest = () => { | ||
155 | + showTestEditCell.value = false; | ||
156 | + emits('closeTest'); | ||
157 | + }; | ||
158 | + | ||
144 | defineExpose({ | 159 | defineExpose({ |
145 | setValue, | 160 | setValue, |
146 | handleTest, | 161 | handleTest, |
@@ -243,7 +243,9 @@ | @@ -243,7 +243,9 @@ | ||
243 | if (f.key === 'organizationId') organizationId = f.value; | 243 | if (f.key === 'organizationId') organizationId = f.value; |
244 | if (f.key === 'entityId') f.value = null; | 244 | if (f.key === 'entityId') f.value = null; |
245 | }); | 245 | }); |
246 | - getAttributeOptions({ deviceProfileId: e.value }); | 246 | + if (e.value) { |
247 | + getAttributeOptions({ deviceProfileId: e.value }); | ||
248 | + } | ||
247 | if (organizationId !== '') { | 249 | if (organizationId !== '') { |
248 | getEntityOptions(organizationId, e.value); | 250 | getEntityOptions(organizationId, e.value); |
249 | } | 251 | } |
@@ -5,10 +5,10 @@ | @@ -5,10 +5,10 @@ | ||
5 | </div> | 5 | </div> |
6 | <div class="mt-8"> | 6 | <div class="mt-8"> |
7 | <a-row type="flex" justify="center"> | 7 | <a-row type="flex" justify="center"> |
8 | - <a-col :span="2"> 测试结果 </a-col> | ||
9 | - <a-col :span="22"> | 8 | + <a-col :span="24"> |
10 | <a-textarea | 9 | <a-textarea |
11 | - v-if="ifWebSocket === '2'" | 10 | + disabled |
11 | + v-if="isWebSocketType === '2'" | ||
12 | allow-clear | 12 | allow-clear |
13 | show-count | 13 | show-count |
14 | v-model:value="testResult" | 14 | v-model:value="testResult" |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | :rows="8" | 16 | :rows="8" |
17 | /> | 17 | /> |
18 | <a-textarea | 18 | <a-textarea |
19 | + disabled | ||
19 | v-else | 20 | v-else |
20 | allow-clear | 21 | allow-clear |
21 | show-count | 22 | show-count |
@@ -29,14 +30,13 @@ | @@ -29,14 +30,13 @@ | ||
29 | </div> | 30 | </div> |
30 | </template> | 31 | </template> |
31 | <script lang="ts" setup name="testRequest"> | 32 | <script lang="ts" setup name="testRequest"> |
32 | - import { nextTick, ref, reactive } from 'vue'; | 33 | + import { nextTick, ref, reactive, onUnmounted } from 'vue'; |
33 | import { Button } from 'ant-design-vue'; | 34 | import { Button } from 'ant-design-vue'; |
34 | import { otherHttp } from '/@/utils/http/axios'; | 35 | import { otherHttp } from '/@/utils/http/axios'; |
35 | import { useWebSocket } from '@vueuse/core'; | 36 | import { useWebSocket } from '@vueuse/core'; |
36 | import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum'; | 37 | import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum'; |
37 | import { getAuthCache } from '/@/utils/auth'; | 38 | import { getAuthCache } from '/@/utils/auth'; |
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 | ||
@@ -48,16 +48,10 @@ | @@ -48,16 +48,10 @@ | ||
48 | 48 | ||
49 | const showTestFlag = ref(false); | 49 | const showTestFlag = ref(false); |
50 | 50 | ||
51 | - const ifWebSocket = ref(''); | ||
52 | - | ||
53 | - const { createMessage } = useMessage(); | ||
54 | - | ||
55 | const token = getAuthCache(JWT_TOKEN_KEY); | 51 | const token = getAuthCache(JWT_TOKEN_KEY); |
56 | 52 | ||
57 | const socketUrls = ref(''); | 53 | const socketUrls = ref(''); |
58 | 54 | ||
59 | - // const { socketUrl } = useGlobSetting(); | ||
60 | - | ||
61 | const socketMessage: any = reactive({ | 55 | const socketMessage: any = reactive({ |
62 | server: ``, | 56 | server: ``, |
63 | sendValue: { | 57 | sendValue: { |
@@ -89,6 +83,8 @@ | @@ -89,6 +83,8 @@ | ||
89 | 83 | ||
90 | const httpResult = ref(''); | 84 | const httpResult = ref(''); |
91 | 85 | ||
86 | + const isWebSocketType = ref(''); | ||
87 | + | ||
92 | //执行测试接口 | 88 | //执行测试接口 |
93 | const handleExcute = () => { | 89 | const handleExcute = () => { |
94 | emits('emitExcute'); | 90 | emits('emitExcute'); |
@@ -99,18 +95,23 @@ | @@ -99,18 +95,23 @@ | ||
99 | await nextTick(); | 95 | await nextTick(); |
100 | //获取Params和Header和Body | 96 | //获取Params和Header和Body |
101 | const Objects = props.data; | 97 | const Objects = props.data; |
102 | - const isWebSocketType = Objects?.method; | 98 | + isWebSocketType.value = Objects?.method; |
103 | const apiType = Objects?.apiType; | 99 | const apiType = Objects?.apiType; |
104 | const url = Objects?.apiGetUrl; | 100 | const url = Objects?.apiGetUrl; |
105 | const headers = Objects?.Header?.params; | 101 | const headers = Objects?.Header?.params; |
106 | const params = Objects?.Params?.params; | 102 | const params = Objects?.Params?.params; |
107 | const body = Objects?.Body?.params; | 103 | const body = Objects?.Body?.params; |
108 | const apiUrl = url?.restfulFormat(params); | 104 | const apiUrl = url?.restfulFormat(params); |
109 | - ifWebSocket.value = isWebSocketType; | ||
110 | - if (isWebSocketType === '2') { | 105 | + if (isWebSocketType.value === '2') { |
111 | socketUrls.value = url; | 106 | socketUrls.value = url; |
112 | socketMessage.server = `${socketUrls.value}?token=${token}`; | 107 | socketMessage.server = `${socketUrls.value}?token=${token}`; |
113 | - websocketRequest(params); | 108 | + const list = Object.values(params); |
109 | + const isEmpty = list.some((it) => it === '' || null || undefined); | ||
110 | + if (!isEmpty) { | ||
111 | + websocketRequest(params); | ||
112 | + } else { | ||
113 | + resetValue(false); | ||
114 | + } | ||
114 | } else { | 115 | } else { |
115 | const resp = await otherHttpRequest(apiType, apiUrl?.split('{?')[0], headers, params, body); | 116 | const resp = await otherHttpRequest(apiType, apiUrl?.split('{?')[0], headers, params, body); |
116 | if (!resp) return; | 117 | if (!resp) return; |
@@ -119,7 +120,7 @@ | @@ -119,7 +120,7 @@ | ||
119 | }; | 120 | }; |
120 | 121 | ||
121 | //websocket请求 | 122 | //websocket请求 |
122 | - const websocketRequest = (params) => { | 123 | + const websocketRequest = (params, destroy = false) => { |
123 | //websocket请求 | 124 | //websocket请求 |
124 | Reflect.deleteProperty(params, 'deviceProfileId'); | 125 | Reflect.deleteProperty(params, 'deviceProfileId'); |
125 | Reflect.deleteProperty(params, 'organizationId'); | 126 | Reflect.deleteProperty(params, 'organizationId'); |
@@ -140,12 +141,17 @@ | @@ -140,12 +141,17 @@ | ||
140 | console.log('断开连接了'); | 141 | console.log('断开连接了'); |
141 | close(); | 142 | close(); |
142 | }, | 143 | }, |
143 | - onError() { | ||
144 | - createMessage.error('webSocket连接超时,请联系管理员'); | ||
145 | - }, | 144 | + onError() {}, |
146 | }); | 145 | }); |
146 | + if (destroy) close(); | ||
147 | }; | 147 | }; |
148 | 148 | ||
149 | + onUnmounted(() => { | ||
150 | + if (isWebSocketType.value === '2') { | ||
151 | + websocketRequest(null, true); | ||
152 | + } | ||
153 | + }); | ||
154 | + | ||
149 | //TODO: 待优化 项目自带第三方请求 | 155 | //TODO: 待优化 项目自带第三方请求 |
150 | const otherHttpRequest = async ( | 156 | const otherHttpRequest = async ( |
151 | apiType, | 157 | apiType, |
@@ -165,8 +171,9 @@ | @@ -165,8 +171,9 @@ | ||
165 | } | 171 | } |
166 | ); | 172 | ); |
167 | case 'POST': | 173 | case 'POST': |
174 | + const { convertObj } = useUtils(); | ||
168 | return await otherHttp.post( | 175 | return await otherHttp.post( |
169 | - { url: apiUrl, data: body, headers, params }, | 176 | + { url: `${apiUrl}?${convertObj(params)}`, data: body, headers }, |
170 | { | 177 | { |
171 | apiUrl: '', | 178 | apiUrl: '', |
172 | joinPrefix, | 179 | joinPrefix, |
@@ -189,7 +196,7 @@ | @@ -189,7 +196,7 @@ | ||
189 | } | 196 | } |
190 | httpResult.value = '测试结果为:'; | 197 | httpResult.value = '测试结果为:'; |
191 | testResult.value = '测试结果为:'; | 198 | testResult.value = '测试结果为:'; |
192 | - ifWebSocket.value = '0'; | 199 | + isWebSocketType.value = '0'; |
193 | }; | 200 | }; |
194 | 201 | ||
195 | const showTest = () => (showTestFlag.value = true); | 202 | const showTest = () => (showTestFlag.value = true); |
1 | <template> | 1 | <template> |
2 | <div> | 2 | <div> |
3 | <div class="mt-8"> | 3 | <div class="mt-8"> |
4 | - <div> | ||
5 | - <Button @click="handleTest" type="primary"> 测试接口 </Button> | 4 | + <div class="flex"> |
5 | + <Button @click="handleTest" type="primary"> 打开测试接口 </Button> | ||
6 | + <Button class="ml-2" @click="onCloseTest" type="primary"> 关闭测试接口 </Button> | ||
6 | </div> | 7 | </div> |
7 | <div v-if="showTestEditCell" class="mt-8"> | 8 | <div v-if="showTestEditCell" class="mt-8"> |
8 | <a-row type="flex" justify="center"> | 9 | <a-row type="flex" justify="center"> |
@@ -20,8 +21,9 @@ | @@ -20,8 +21,9 @@ | ||
20 | import { ref, nextTick } from 'vue'; | 21 | import { ref, nextTick } from 'vue'; |
21 | import { Button } from 'ant-design-vue'; | 22 | import { Button } from 'ant-design-vue'; |
22 | import TestHeaderEditCellTable from './testEditHeaderCellTable.vue'; | 23 | import TestHeaderEditCellTable from './testEditHeaderCellTable.vue'; |
24 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
23 | 25 | ||
24 | - const emits = defineEmits(['testHeaderInterface']); | 26 | + const emits = defineEmits(['testHeaderInterface', 'closeTest']); |
25 | 27 | ||
26 | const props = defineProps({ | 28 | const props = defineProps({ |
27 | data: { | 29 | data: { |
@@ -29,6 +31,8 @@ | @@ -29,6 +31,8 @@ | ||
29 | }, | 31 | }, |
30 | }); | 32 | }); |
31 | 33 | ||
34 | + const { createMessage } = useMessage(); | ||
35 | + | ||
32 | const showTestEditCell = ref(false); | 36 | const showTestEditCell = ref(false); |
33 | 37 | ||
34 | const excuteData = ref({}); | 38 | const excuteData = ref({}); |
@@ -59,6 +63,11 @@ | @@ -59,6 +63,11 @@ | ||
59 | //获取数据 | 63 | //获取数据 |
60 | const getTestValue = () => { | 64 | const getTestValue = () => { |
61 | const getTable = getTestTableKeyValue(); | 65 | const getTable = getTestTableKeyValue(); |
66 | + const hasRequired = getTable?.some((it) => it.required === true && !it.value); | ||
67 | + if (hasRequired) { | ||
68 | + createMessage.error('参数不能为空'); | ||
69 | + throw new Error('参数不能为空'); | ||
70 | + } | ||
62 | const params: any = {}; | 71 | const params: any = {}; |
63 | getTable?.map((it: any) => (params[it.key!] = it.value!)); | 72 | getTable?.map((it: any) => (params[it.key!] = it.value!)); |
64 | excuteData.value = { | 73 | excuteData.value = { |
@@ -73,6 +82,11 @@ | @@ -73,6 +82,11 @@ | ||
73 | testResult.value = ''; | 82 | testResult.value = ''; |
74 | }; | 83 | }; |
75 | 84 | ||
85 | + const onCloseTest = () => { | ||
86 | + showTestEditCell.value = false; | ||
87 | + emits('closeTest'); | ||
88 | + }; | ||
89 | + | ||
76 | defineExpose({ | 90 | defineExpose({ |
77 | setValue, | 91 | setValue, |
78 | handleTest, | 92 | handleTest, |
1 | <template> | 1 | <template> |
2 | <div> | 2 | <div> |
3 | <div class="mt-8"> | 3 | <div class="mt-8"> |
4 | - <div> | ||
5 | - <Button @click="handleTest" type="primary"> 测试接口 </Button> | 4 | + <div class="flex"> |
5 | + <Button @click="handleTest" type="primary"> 打开测试接口 </Button> | ||
6 | + <Button class="ml-2" @click="onCloseTest" type="primary"> 关闭测试接口 </Button> | ||
6 | </div> | 7 | </div> |
7 | <div v-if="showTestEditCell" class="mt-8"> | 8 | <div v-if="showTestEditCell" class="mt-8"> |
8 | <a-row type="flex" justify="center"> | 9 | <a-row type="flex" justify="center"> |
@@ -22,8 +23,9 @@ | @@ -22,8 +23,9 @@ | ||
22 | import TestParamsCellTable from './testEditParamsCellTable.vue'; | 23 | import TestParamsCellTable from './testEditParamsCellTable.vue'; |
23 | import moment from 'moment'; | 24 | import moment from 'moment'; |
24 | import { useUtils } from '../../../hooks/useUtils'; | 25 | import { useUtils } from '../../../hooks/useUtils'; |
26 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
25 | 27 | ||
26 | - const emits = defineEmits(['testParamsInterface']); | 28 | + const emits = defineEmits(['testParamsInterface', 'closeTest']); |
27 | 29 | ||
28 | const props = defineProps({ | 30 | const props = defineProps({ |
29 | data: { | 31 | data: { |
@@ -31,6 +33,8 @@ | @@ -31,6 +33,8 @@ | ||
31 | }, | 33 | }, |
32 | }); | 34 | }); |
33 | 35 | ||
36 | + const { createMessage } = useMessage(); | ||
37 | + | ||
34 | const showTestEditCell = ref(false); | 38 | const showTestEditCell = ref(false); |
35 | 39 | ||
36 | const excuteData = ref({}); | 40 | const excuteData = ref({}); |
@@ -92,6 +96,7 @@ | @@ -92,6 +96,7 @@ | ||
92 | return { | 96 | return { |
93 | key, | 97 | key, |
94 | value, | 98 | value, |
99 | + required: it.required, | ||
95 | }; | 100 | }; |
96 | }); | 101 | }); |
97 | }; | 102 | }; |
@@ -99,6 +104,11 @@ | @@ -99,6 +104,11 @@ | ||
99 | //获取数据 | 104 | //获取数据 |
100 | const getTestValue = () => { | 105 | const getTestValue = () => { |
101 | const getTable = getTestTableKeyValue(); | 106 | const getTable = getTestTableKeyValue(); |
107 | + const hasRequired = getTable?.some((it) => it.required === true && !it.value); | ||
108 | + if (hasRequired) { | ||
109 | + createMessage.error('参数不能为空'); | ||
110 | + throw new Error('参数不能为空'); | ||
111 | + } | ||
102 | const params: any = {}; | 112 | const params: any = {}; |
103 | getTable?.map((it) => (params[it.key!] = it.value!)); | 113 | getTable?.map((it) => (params[it.key!] = it.value!)); |
104 | if (params['keys']) { | 114 | if (params['keys']) { |
@@ -116,6 +126,11 @@ | @@ -116,6 +126,11 @@ | ||
116 | testResult.value = ''; | 126 | testResult.value = ''; |
117 | }; | 127 | }; |
118 | 128 | ||
129 | + const onCloseTest = () => { | ||
130 | + showTestEditCell.value = false; | ||
131 | + emits('closeTest'); | ||
132 | + }; | ||
133 | + | ||
119 | defineExpose({ | 134 | defineExpose({ |
120 | setValue, | 135 | setValue, |
121 | handleTest, | 136 | handleTest, |
@@ -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 | } |
@@ -46,7 +46,7 @@ | @@ -46,7 +46,7 @@ | ||
46 | import { useMessage } from '/@/hooks/web/useMessage'; | 46 | import { useMessage } from '/@/hooks/web/useMessage'; |
47 | import { useUtils } from './hooks/useUtils'; | 47 | import { useUtils } from './hooks/useUtils'; |
48 | 48 | ||
49 | - const { resetReqHttpType, resetUpdateSchema } = useUtils(); | 49 | + const { resetReqHttpType } = useUtils(); |
50 | 50 | ||
51 | const emits = defineEmits(['success', 'register']); | 51 | const emits = defineEmits(['success', 'register']); |
52 | 52 | ||
@@ -69,10 +69,15 @@ | @@ -69,10 +69,15 @@ | ||
69 | const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => { | 69 | const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => { |
70 | await resetFields(); | 70 | await resetFields(); |
71 | await nextTick(); | 71 | await nextTick(); |
72 | - updateSchema(resetUpdateSchema); | ||
73 | setFieldsValue(resetReqHttpType); | 72 | setFieldsValue(resetReqHttpType); |
74 | const title = `${!data.isUpdate ? '新增' : '修改'}公共接口`; | 73 | const title = `${!data.isUpdate ? '新增' : '修改'}公共接口`; |
75 | setDrawerProps({ title }); | 74 | setDrawerProps({ title }); |
75 | + updateSchema({ | ||
76 | + field: 'requestHttpTypeAndUrl', | ||
77 | + componentProps: { | ||
78 | + type: '0', | ||
79 | + }, | ||
80 | + }); | ||
76 | isUpdate.value = data.isUpdate; | 81 | isUpdate.value = data.isUpdate; |
77 | !isUpdate.value ? (putId.value = '') : (putId.value = data.record.id); | 82 | !isUpdate.value ? (putId.value = '') : (putId.value = data.record.id); |
78 | simpleRequestRef.value?.resetValue() && testSqlRef.value?.resetValue(); | 83 | simpleRequestRef.value?.resetValue() && testSqlRef.value?.resetValue(); |
@@ -104,6 +109,14 @@ | @@ -104,6 +109,14 @@ | ||
104 | try { | 109 | try { |
105 | const values = await validate(); | 110 | const values = await validate(); |
106 | if (!values) return; | 111 | if (!values) return; |
112 | + const isRequestHttpTypeAndUrlEmpty = values?.requestHttpTypeAndUrl; | ||
113 | + if ( | ||
114 | + !Reflect.get(isRequestHttpTypeAndUrlEmpty, 'requestHttpType') || | ||
115 | + !Reflect.get(isRequestHttpTypeAndUrlEmpty, 'requestUrl') | ||
116 | + ) { | ||
117 | + createMessage.error('请填写请求类型&地址'); | ||
118 | + throw Error('请填写请求类型&地址'); | ||
119 | + } | ||
107 | const Objects = simpleRequestRef.value?.getValue(true); | 120 | const Objects = simpleRequestRef.value?.getValue(true); |
108 | const requestOriginUrl = | 121 | const requestOriginUrl = |
109 | values['originUrlType'] === 'server_url' ? 'localhost' : values['requestOriginUrl']; | 122 | values['originUrlType'] === 'server_url' ? 'localhost' : values['requestOriginUrl']; |
@@ -36,5 +36,20 @@ export const useUtils = () => { | @@ -36,5 +36,20 @@ export const useUtils = () => { | ||
36 | type: '0', | 36 | type: '0', |
37 | }, | 37 | }, |
38 | }; | 38 | }; |
39 | - return { getMultipleKeys, pushObj, resetReqHttpType, resetUpdateSchema }; | 39 | + //对象转get params参数 |
40 | + const convertObj = (data: object) => { | ||
41 | + const _result: any = []; | ||
42 | + for (const key in data) { | ||
43 | + const value = data[key]; | ||
44 | + if (value.constructor == Array) { | ||
45 | + value.forEach(function (_value) { | ||
46 | + _result.push(key + '=' + _value); | ||
47 | + }); | ||
48 | + } else { | ||
49 | + _result.push(key + '=' + value); | ||
50 | + } | ||
51 | + } | ||
52 | + return _result.join('&'); | ||
53 | + }; | ||
54 | + return { getMultipleKeys, pushObj, resetReqHttpType, resetUpdateSchema, convertObj }; | ||
40 | }; | 55 | }; |
@@ -39,7 +39,7 @@ | @@ -39,7 +39,7 @@ | ||
39 | icon: 'ant-design:node-expand-outlined', | 39 | icon: 'ant-design:node-expand-outlined', |
40 | onClick: handlePublish.bind(null, 'publish', record), | 40 | onClick: handlePublish.bind(null, 'publish', record), |
41 | ifShow: () => { | 41 | ifShow: () => { |
42 | - return record.state === 0; | 42 | + return record.state === 0 && record.creator === userId; |
43 | }, | 43 | }, |
44 | }, | 44 | }, |
45 | { | 45 | { |
@@ -47,7 +47,7 @@ | @@ -47,7 +47,7 @@ | ||
47 | icon: 'ant-design:node-collapse-outlined', | 47 | icon: 'ant-design:node-collapse-outlined', |
48 | onClick: handlePublish.bind(null, 'canelPublish', record), | 48 | onClick: handlePublish.bind(null, 'canelPublish', record), |
49 | ifShow: () => { | 49 | ifShow: () => { |
50 | - return record.state === 1; | 50 | + return record.state === 1 && record.creator === userId; |
51 | }, | 51 | }, |
52 | }, | 52 | }, |
53 | { | 53 | { |
@@ -55,7 +55,7 @@ | @@ -55,7 +55,7 @@ | ||
55 | icon: 'clarity:note-edit-line', | 55 | icon: 'clarity:note-edit-line', |
56 | onClick: handleCreateOrEdit.bind(null, record), | 56 | onClick: handleCreateOrEdit.bind(null, record), |
57 | ifShow: () => { | 57 | ifShow: () => { |
58 | - return record.state === 0; | 58 | + return record.state === 0 && record.creator === userId; |
59 | }, | 59 | }, |
60 | }, | 60 | }, |
61 | { | 61 | { |
@@ -63,7 +63,7 @@ | @@ -63,7 +63,7 @@ | ||
63 | icon: 'ant-design:delete-outlined', | 63 | icon: 'ant-design:delete-outlined', |
64 | color: 'error', | 64 | color: 'error', |
65 | ifShow: () => { | 65 | ifShow: () => { |
66 | - return record.state === 0; | 66 | + return record.state === 0 && record.creator === userId; |
67 | }, | 67 | }, |
68 | popConfirm: { | 68 | popConfirm: { |
69 | title: '是否确认删除', | 69 | title: '是否确认删除', |
@@ -93,6 +93,12 @@ | @@ -93,6 +93,12 @@ | ||
93 | import { Popconfirm, Modal } from 'ant-design-vue'; | 93 | import { Popconfirm, Modal } from 'ant-design-vue'; |
94 | import { JsonPreview } from '/@/components/CodeEditor'; | 94 | import { JsonPreview } from '/@/components/CodeEditor'; |
95 | import { useMessage } from '/@/hooks/web/useMessage'; | 95 | import { useMessage } from '/@/hooks/web/useMessage'; |
96 | + import { USER_INFO_KEY } from '/@/enums/cacheEnum'; | ||
97 | + import { getAuthCache } from '/@/utils/auth'; | ||
98 | + | ||
99 | + const userInfo = getAuthCache(USER_INFO_KEY) as any; | ||
100 | + | ||
101 | + const userId = ref(userInfo?.userId); | ||
96 | 102 | ||
97 | const [registerTable, { reload, clearSelectedRowKeys }] = useTable({ | 103 | const [registerTable, { reload, clearSelectedRowKeys }] = useTable({ |
98 | api: getDataViewInterfacePage, | 104 | api: getDataViewInterfacePage, |
@@ -52,6 +52,9 @@ | @@ -52,6 +52,9 @@ | ||
52 | compact: true, | 52 | compact: true, |
53 | baseColProps: { span: 8 }, | 53 | baseColProps: { span: 8 }, |
54 | schemas: searchFormSchema, | 54 | schemas: searchFormSchema, |
55 | + resetFunc: async () => { | ||
56 | + getDataSource({ name: '' }); | ||
57 | + }, | ||
55 | submitFunc: async () => { | 58 | submitFunc: async () => { |
56 | getDataSource({ pageSize: pagination.pageSize, page: 1 }); | 59 | getDataSource({ pageSize: pagination.pageSize, page: 1 }); |
57 | }, | 60 | }, |
1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
2 | import { Button, Tag } from 'ant-design-vue'; | 2 | import { Button, Tag } from 'ant-design-vue'; |
3 | - import { h, onMounted, ref, unref } from 'vue'; | 3 | + import { h, onMounted, ref, unref, Ref } from 'vue'; |
4 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; | 4 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
5 | - import { ScriptRecord } from '/@/api/scriptmanage/model/scriptModel'; | 5 | + // import { ScriptRecord } from '/@/api/scriptmanage/model/scriptModel'; |
6 | import { getScriptManageMeList } from '/@/api/scriptmanage/scriptManager'; | 6 | import { getScriptManageMeList } from '/@/api/scriptmanage/scriptManager'; |
7 | import { Description, useDescription } from '/@/components/Description'; | 7 | import { Description, useDescription } from '/@/components/Description'; |
8 | import { useModal } from '/@/components/Modal'; | 8 | import { useModal } from '/@/components/Modal'; |
9 | import CoverScriptModal from '/@/views/scriptmanage/converscript/ConverScriptModal.vue'; | 9 | import CoverScriptModal from '/@/views/scriptmanage/converscript/ConverScriptModal.vue'; |
10 | + import { SelectTypes } from 'ant-design-vue/es/select'; | ||
10 | 11 | ||
11 | const props = defineProps<{ | 12 | const props = defineProps<{ |
12 | record: DeviceRecord['profileData']['transportConfiguration']; | 13 | record: DeviceRecord['profileData']['transportConfiguration']; |
13 | }>(); | 14 | }>(); |
14 | 15 | ||
15 | - const scriptInfo = ref<ScriptRecord>({} as unknown as ScriptRecord); | 16 | + // const scriptInfo = ref<ScriptRecord>({} as unknown as ScriptRecord); |
17 | + const authScriptIdStr = ref(''); | ||
18 | + | ||
19 | + const upScriptIdStr = ref(''); | ||
20 | + | ||
21 | + const selectUpOptions: Ref<SelectTypes['options']> = ref([]); | ||
22 | + | ||
23 | + const selectAuthOptions: Ref<SelectTypes['options']> = ref([]); | ||
24 | + | ||
25 | + onMounted(async () => { | ||
26 | + selectUpOptions.value = await getAllScriptType('TRANSPORT_TCP_UP'); | ||
27 | + selectAuthOptions.value = await getAllScriptType('TRANSPORT_TCP_AUTH'); | ||
28 | + setDescProps({ | ||
29 | + data: Object.assign(props.record), | ||
30 | + }); | ||
31 | + }); | ||
32 | + | ||
33 | + const getAllScriptType = async (type) => { | ||
34 | + const rest = await getScriptManageMeList({ scriptType: type }); | ||
35 | + return rest.map((m) => ({ label: m.name, value: m.id })); | ||
36 | + }; | ||
37 | + | ||
38 | + const findScriptUpName = (scriptId) => { | ||
39 | + upScriptIdStr.value = scriptId; | ||
40 | + return selectUpOptions.value?.find((it) => it.value === scriptId)?.label; | ||
41 | + }; | ||
42 | + | ||
43 | + const findScriptAuthName = (scriptId) => { | ||
44 | + authScriptIdStr.value = scriptId; | ||
45 | + return selectAuthOptions.value?.find((it) => it.value === scriptId)?.label; | ||
46 | + }; | ||
16 | 47 | ||
17 | const [register, { setDescProps }] = useDescription({ | 48 | const [register, { setDescProps }] = useDescription({ |
18 | layout: 'vertical', | 49 | layout: 'vertical', |
@@ -25,14 +56,27 @@ | @@ -25,14 +56,27 @@ | ||
25 | span: 2, | 56 | span: 2, |
26 | }, | 57 | }, |
27 | { | 58 | { |
28 | - field: 'scriptName', | ||
29 | - label: '转换脚本', | 59 | + field: 'authScriptId', |
60 | + label: '鉴权脚本', | ||
30 | render: (value: string) => { | 61 | render: (value: string) => { |
31 | return ( | 62 | return ( |
32 | value && | 63 | value && |
33 | h('div', [ | 64 | h('div', [ |
34 | - h(Tag, { color: 'blue' }, () => value), | ||
35 | - h(Button, { type: 'link', onClick: handleTestScript }, () => '测试脚本'), | 65 | + h(Tag, { color: 'blue' }, () => findScriptAuthName(value)), |
66 | + h(Button, { type: 'link', onClick: handleTestAuthScript }, () => '测试脚本'), | ||
67 | + ]) | ||
68 | + ); | ||
69 | + }, | ||
70 | + }, | ||
71 | + { | ||
72 | + field: 'upScriptId', | ||
73 | + label: '上行脚本', | ||
74 | + render: (value: string) => { | ||
75 | + return ( | ||
76 | + value && | ||
77 | + h('div', [ | ||
78 | + h(Tag, { color: 'blue' }, () => findScriptUpName(value)), | ||
79 | + h(Button, { type: 'link', onClick: handleTestUpScript }, () => '测试脚本'), | ||
36 | ]) | 80 | ]) |
37 | ); | 81 | ); |
38 | }, | 82 | }, |
@@ -42,29 +86,24 @@ | @@ -42,29 +86,24 @@ | ||
42 | 86 | ||
43 | const [registerModal, { openModal }] = useModal(); | 87 | const [registerModal, { openModal }] = useModal(); |
44 | 88 | ||
45 | - const handleTestScript = () => { | 89 | + const handleTestAuthScript = () => { |
46 | openModal(true, { | 90 | openModal(true, { |
47 | isUpdate: false, | 91 | isUpdate: false, |
48 | isTest: true, | 92 | isTest: true, |
49 | - record: unref(scriptInfo).id, | 93 | + record: unref(authScriptIdStr), |
50 | isText: 'test', | 94 | isText: 'test', |
51 | isTitle: 'test', | 95 | isTitle: 'test', |
52 | }); | 96 | }); |
53 | }; | 97 | }; |
54 | 98 | ||
55 | - onMounted(() => { | ||
56 | - getTransforScriptInfo(); | ||
57 | - }); | ||
58 | - | ||
59 | - const getTransforScriptInfo = async () => { | ||
60 | - try { | ||
61 | - const list = await getScriptManageMeList(); | ||
62 | - const record = list.find((item) => item.id === props.record.scriptId); | ||
63 | - scriptInfo.value = record!; | ||
64 | - setDescProps({ | ||
65 | - data: Object.assign(props.record, record, { scriptName: record?.name || '' }), | ||
66 | - }); | ||
67 | - } catch (error) {} | 99 | + const handleTestUpScript = () => { |
100 | + openModal(true, { | ||
101 | + isUpdate: false, | ||
102 | + isTest: true, | ||
103 | + record: unref(upScriptIdStr), | ||
104 | + isText: 'test', | ||
105 | + isTitle: 'test', | ||
106 | + }); | ||
68 | }; | 107 | }; |
69 | </script> | 108 | </script> |
70 | 109 |
@@ -41,7 +41,7 @@ | @@ -41,7 +41,7 @@ | ||
41 | </BasicDrawer> | 41 | </BasicDrawer> |
42 | </template> | 42 | </template> |
43 | <script lang="ts" setup> | 43 | <script lang="ts" setup> |
44 | - import { ref, computed, unref, reactive, watch, Ref } from 'vue'; | 44 | + import { ref, computed, unref, reactive, watch, Ref, nextTick } from 'vue'; |
45 | import { BasicForm, useForm } from '/@/components/Form'; | 45 | import { BasicForm, useForm } from '/@/components/Form'; |
46 | import { formSchema, organizationId } from './config.data'; | 46 | import { formSchema, organizationId } from './config.data'; |
47 | import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; | 47 | import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; |
@@ -198,6 +198,7 @@ | @@ -198,6 +198,7 @@ | ||
198 | const isViewDetail = ref(false); | 198 | const isViewDetail = ref(false); |
199 | 199 | ||
200 | const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => { | 200 | const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => { |
201 | + await nextTick(); | ||
201 | await resetFields(); | 202 | await resetFields(); |
202 | setDrawerProps({ confirmLoading: false }); | 203 | setDrawerProps({ confirmLoading: false }); |
203 | isUpdate.value = !!data?.isUpdate; | 204 | isUpdate.value = !!data?.isUpdate; |
@@ -222,6 +223,10 @@ | @@ -222,6 +223,10 @@ | ||
222 | await setFieldsValue(editResData.data); | 223 | await setFieldsValue(editResData.data); |
223 | //回显嵌套数据 | 224 | //回显嵌套数据 |
224 | await setFieldsValue({ | 225 | await setFieldsValue({ |
226 | + dateGroupGap: | ||
227 | + editResData.data.queryCondition?.queryMode === 1 | ||
228 | + ? editResData.data.queryCondition?.interval | ||
229 | + : null, | ||
225 | agg: editResData.data.queryCondition?.agg, | 230 | agg: editResData.data.queryCondition?.agg, |
226 | interval: editResData.data.queryCondition?.interval, | 231 | interval: editResData.data.queryCondition?.interval, |
227 | limit: editResData.data.queryCondition?.limit, | 232 | limit: editResData.data.queryCondition?.limit, |
@@ -266,6 +271,7 @@ | @@ -266,6 +271,7 @@ | ||
266 | { label: '最大值', value: AggregateDataEnum.MAX }, | 271 | { label: '最大值', value: AggregateDataEnum.MAX }, |
267 | { label: '平均值', value: AggregateDataEnum.AVG }, | 272 | { label: '平均值', value: AggregateDataEnum.AVG }, |
268 | { label: '求和', value: AggregateDataEnum.SUM }, | 273 | { label: '求和', value: AggregateDataEnum.SUM }, |
274 | + { label: '计数', value: AggregateDataEnum.COUNT }, | ||
269 | { label: '空', value: AggregateDataEnum.NONE }, | 275 | { label: '空', value: AggregateDataEnum.NONE }, |
270 | ]; | 276 | ]; |
271 | const updateSchemaAgg = (options: {}) => { | 277 | const updateSchemaAgg = (options: {}) => { |
@@ -276,8 +282,8 @@ | @@ -276,8 +282,8 @@ | ||
276 | }, | 282 | }, |
277 | }); | 283 | }); |
278 | }; | 284 | }; |
279 | - if (editResData.data.dataType == 1) updateSchemaAgg(dataCompareOpions.slice(0, 4)); | ||
280 | - else updateSchemaAgg(dataCompareOpions.slice(4, 5)); | 285 | + if (editResData.data.dataType == 1) updateSchemaAgg(dataCompareOpions.slice(0, 5)); |
286 | + else updateSchemaAgg(dataCompareOpions.slice(5, 6)); | ||
281 | //回显执行方式和查询周期 | 287 | //回显执行方式和查询周期 |
282 | const dataQueryOpions = [ | 288 | const dataQueryOpions = [ |
283 | { label: '固定周期', value: QueryWay.LATEST }, | 289 | { label: '固定周期', value: QueryWay.LATEST }, |
@@ -442,7 +448,7 @@ | @@ -442,7 +448,7 @@ | ||
442 | } | 448 | } |
443 | queryCondition = { | 449 | queryCondition = { |
444 | agg: values.agg, | 450 | agg: values.agg, |
445 | - interval: values.interval, | 451 | + interval: values?.queryMode === 'latest' ? values.interval : values.dateGroupGap, |
446 | limit: values.limit, | 452 | limit: values.limit, |
447 | ...{ | 453 | ...{ |
448 | startTs: startTs.value, | 454 | startTs: startTs.value, |
1 | import { ref } from 'vue'; | 1 | import { ref } from 'vue'; |
2 | import { BasicColumn, FormSchema } from '/@/components/Table'; | 2 | import { BasicColumn, FormSchema } from '/@/components/Table'; |
3 | import type { FormSchema as QFormSchema } from '/@/components/Form/index'; | 3 | import type { FormSchema as QFormSchema } from '/@/components/Form/index'; |
4 | -import moment from 'moment'; | ||
5 | import { getOrganizationList } from '/@/api/system/system'; | 4 | import { getOrganizationList } from '/@/api/system/system'; |
6 | import { copyTransFun } from '/@/utils/fnUtils'; | 5 | import { copyTransFun } from '/@/utils/fnUtils'; |
7 | import { findDictItemByCode } from '/@/api/system/dict'; | 6 | import { findDictItemByCode } from '/@/api/system/dict'; |
@@ -12,6 +11,7 @@ import { | @@ -12,6 +11,7 @@ import { | ||
12 | getPacketIntervalByValue, | 11 | getPacketIntervalByValue, |
13 | intervalOption, | 12 | intervalOption, |
14 | } from '../../device/localtion/cpns/TimePeriodForm/helper'; | 13 | } from '../../device/localtion/cpns/TimePeriodForm/helper'; |
14 | +import moment, { Moment } from 'moment'; | ||
15 | 15 | ||
16 | export enum QueryWay { | 16 | export enum QueryWay { |
17 | LATEST = 'latest', | 17 | LATEST = 'latest', |
@@ -433,16 +433,70 @@ export const formSchema: QFormSchema[] = [ | @@ -433,16 +433,70 @@ export const formSchema: QFormSchema[] = [ | ||
433 | ifShow({ values }) { | 433 | ifShow({ values }) { |
434 | return values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD && !isFixedTime(values.executeWay); | 434 | return values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD && !isFixedTime(values.executeWay); |
435 | }, | 435 | }, |
436 | - componentProps: { | ||
437 | - showTime: { | ||
438 | - defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')], | ||
439 | - }, | 436 | + // componentProps: { |
437 | + // showTime: { | ||
438 | + // defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')], | ||
439 | + // }, | ||
440 | + // }, | ||
441 | + componentProps({ formActionType }) { | ||
442 | + const { setFieldsValue } = formActionType; | ||
443 | + let dates: Moment[] = []; | ||
444 | + return { | ||
445 | + showTime: { | ||
446 | + defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')], | ||
447 | + }, | ||
448 | + onCalendarChange(value: Moment[]) { | ||
449 | + dates = value; | ||
450 | + }, | ||
451 | + disabledDate(current: Moment) { | ||
452 | + if (!dates || dates.length === 0 || !current) { | ||
453 | + return false; | ||
454 | + } | ||
455 | + const diffDate = current.diff(dates[0], 'years', true); | ||
456 | + return Math.abs(diffDate) > 1; | ||
457 | + }, | ||
458 | + onChange() { | ||
459 | + dates = []; | ||
460 | + setFieldsValue({ dateGroupGap: null }); | ||
461 | + }, | ||
462 | + getPopupContainer: () => document.body, | ||
463 | + }; | ||
440 | }, | 464 | }, |
441 | colProps: { | 465 | colProps: { |
442 | span: 10, | 466 | span: 10, |
443 | }, | 467 | }, |
444 | }, | 468 | }, |
445 | { | 469 | { |
470 | + field: 'dateGroupGap', | ||
471 | + label: '分组间隔', | ||
472 | + component: 'Select', | ||
473 | + dynamicRules: ({ model }) => { | ||
474 | + return [ | ||
475 | + { | ||
476 | + required: model[SchemaFiled.AGG] !== AggregateDataEnum.NONE, | ||
477 | + message: '分组间隔为必填项', | ||
478 | + type: 'number', | ||
479 | + }, | ||
480 | + ]; | ||
481 | + }, | ||
482 | + ifShow({ values }) { | ||
483 | + return values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD && !isFixedTime(values.executeWay); | ||
484 | + }, | ||
485 | + componentProps({ formModel, formActionType }) { | ||
486 | + const options = | ||
487 | + formModel[SchemaFiled.WAY] === QueryWay.LATEST | ||
488 | + ? getPacketIntervalByValue(formModel[SchemaFiled.START_TS]) | ||
489 | + : getPacketIntervalByRange(formModel[SchemaFiled.DATE_RANGE]); | ||
490 | + if (formModel[SchemaFiled.AGG] !== AggregateDataEnum.NONE) { | ||
491 | + formActionType.setFieldsValue({ [SchemaFiled.LIMIT]: null }); | ||
492 | + } | ||
493 | + return { | ||
494 | + options, | ||
495 | + getPopupContainer: () => document.body, | ||
496 | + }; | ||
497 | + }, | ||
498 | + }, | ||
499 | + { | ||
446 | field: SchemaFiled.START_TS, | 500 | field: SchemaFiled.START_TS, |
447 | label: '最近时间', | 501 | label: '最近时间', |
448 | component: 'Select', | 502 | component: 'Select', |
@@ -128,6 +128,7 @@ | @@ -128,6 +128,7 @@ | ||
128 | import { findOperation } from './config/formatData'; | 128 | import { findOperation } from './config/formatData'; |
129 | import { formatToDateTime } from '/@/utils/dateUtil'; | 129 | import { formatToDateTime } from '/@/utils/dateUtil'; |
130 | import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue'; | 130 | import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue'; |
131 | + import { isEmpty } from '/@/utils/is'; | ||
131 | import { add } from '/@/components/Form/src/componentMap'; | 132 | import { add } from '/@/components/Form/src/componentMap'; |
132 | 133 | ||
133 | add('ObjectModelValidateForm', ObjectModelValidateForm); | 134 | add('ObjectModelValidateForm', ObjectModelValidateForm); |
@@ -156,7 +157,7 @@ | @@ -156,7 +157,7 @@ | ||
156 | const id = ref(undefined); | 157 | const id = ref(undefined); |
157 | const tenantId = ref(undefined); | 158 | const tenantId = ref(undefined); |
158 | const isView = ref(true); | 159 | const isView = ref(true); |
159 | - const [registerForm, { resetFields, validate, setFieldsValue }] = useForm({ | 160 | + const [registerForm, { resetFields, validate, setFieldsValue, getFieldsValue }] = useForm({ |
160 | labelWidth: 120, | 161 | labelWidth: 120, |
161 | schemas: formSchema, | 162 | schemas: formSchema, |
162 | showActionButtonGroup: false, | 163 | showActionButtonGroup: false, |
@@ -617,12 +618,25 @@ | @@ -617,12 +618,25 @@ | ||
617 | // item.updateFieldAlarmConfig(alarmConfigList); | 618 | // item.updateFieldAlarmConfig(alarmConfigList); |
618 | // }); | 619 | // }); |
619 | // } | 620 | // } |
621 | + | ||
622 | + const isEmptyThrowError = (obj) => { | ||
623 | + if (isEmpty(obj)) { | ||
624 | + createMessage.error('请选择组织'); | ||
625 | + throw Error('请选择组织'); | ||
626 | + } | ||
627 | + if (!Reflect.get(obj, 'organizationId')) { | ||
628 | + createMessage.error('请选择组织'); | ||
629 | + throw Error('请选择组织'); | ||
630 | + } | ||
631 | + }; | ||
632 | + | ||
620 | // 添加触发器 | 633 | // 添加触发器 |
621 | const addTrigger = () => { | 634 | const addTrigger = () => { |
622 | unref(triggerData).push(Date.now()); | 635 | unref(triggerData).push(Date.now()); |
623 | nextTick(() => { | 636 | nextTick(() => { |
624 | setFields(skipUnwrap.triggerItemRefs); | 637 | setFields(skipUnwrap.triggerItemRefs); |
625 | }); | 638 | }); |
639 | + isEmptyThrowError(getFieldsValue()); | ||
626 | }; | 640 | }; |
627 | // 添加执行条件 | 641 | // 添加执行条件 |
628 | const addCondition = () => { | 642 | const addCondition = () => { |
@@ -630,6 +644,7 @@ | @@ -630,6 +644,7 @@ | ||
630 | nextTick(() => { | 644 | nextTick(() => { |
631 | setFields(skipUnwrap.conditionItemRefs); | 645 | setFields(skipUnwrap.conditionItemRefs); |
632 | }); | 646 | }); |
647 | + isEmptyThrowError(getFieldsValue()); | ||
633 | }; | 648 | }; |
634 | // 添加执行动作 | 649 | // 添加执行动作 |
635 | const addAction = () => { | 650 | const addAction = () => { |
@@ -637,6 +652,7 @@ | @@ -637,6 +652,7 @@ | ||
637 | nextTick(() => { | 652 | nextTick(() => { |
638 | setFields(skipUnwrap.actionItemRefs); | 653 | setFields(skipUnwrap.actionItemRefs); |
639 | }); | 654 | }); |
655 | + isEmptyThrowError(getFieldsValue()); | ||
640 | }; | 656 | }; |
641 | 657 | ||
642 | /** | 658 | /** |
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="scriptType" | ||
33 | - :rules="[{ required: true, message: '请选择脚本类型' }]" | ||
34 | - > | ||
35 | - <a-space direction="vertical"> | ||
36 | - <a-radio-group | ||
37 | - @change="handleScriptType" | ||
38 | - v-model:value="scriptForm.scriptType" | ||
39 | - :options="scriptTypeOptions" | ||
40 | - /> | ||
41 | - </a-space> | ||
42 | - </a-form-item> | ||
43 | - <a-form-item | ||
44 | - label="保存原始数据" | ||
45 | - name="saveOriginalData" | ||
46 | - :rules="[{ required: true, message: '请选择保存原始数据' }]" | ||
47 | - > | ||
48 | - <a-space direction="vertical"> | ||
49 | - <a-radio-group v-model:value="scriptForm.saveOriginalData" :options="originalOptions" /> | ||
50 | - </a-space> | ||
51 | - </a-form-item> | ||
52 | - <a-form-item label="脚本内容" :name="ifAdd ? 'convertJs' : 'script'"> | ||
53 | - <Card title="脚本内容" :bodyStyle="{ padding: 0, height: '280px' }"> | ||
54 | - <template #extra> | ||
55 | - <a-button @click="handleFormat" size="small">格式化</a-button> | ||
56 | - <Tooltip :title="defaultTitle" class="ml-2"> | ||
57 | - <QuestionCircleOutlined style="font-size: 1rem" /> | ||
58 | - </Tooltip> | ||
59 | - </template> | ||
60 | - <div ref="aceRef" class="overflow-hidden"></div> | ||
61 | - </Card> | ||
62 | - <Button @click="handleCopy" class="mt-4"> | ||
63 | - <template #icon> | ||
64 | - <CopyOutlined /> | ||
65 | - </template> | ||
66 | - copy | ||
67 | - </Button> | ||
68 | - </a-form-item> | ||
69 | - <a-form-item | ||
70 | - :label="ifAdd ? '备注' : '输出参数(output)'" | ||
71 | - :name="ifAdd ? 'description' : 'output'" | ||
72 | - > | ||
73 | - <a-textarea | ||
74 | - :rows="3" | ||
75 | - v-if="ifAdd" | ||
76 | - v-model:value="scriptForm.description" | ||
77 | - placeholder="请输入备注" | ||
78 | - :maxlength="255" | ||
79 | - /> | ||
80 | - <a-textarea | ||
81 | - disabled | ||
82 | - :rows="5" | ||
83 | - v-else | ||
84 | - v-model:value="scriptForm.output" | ||
85 | - placeholder="输出参数为服务端返回的内容" | ||
86 | - :maxlength="255" | ||
87 | - /> | ||
88 | - </a-form-item> | ||
89 | - </a-form> | ||
90 | - </div> | ||
91 | -</template> | ||
92 | -<script setup lang="ts"> | ||
93 | - import { ref, unref, reactive, onMounted, toRefs, nextTick, computed } from 'vue'; | ||
94 | - import ace from 'ace-builds'; | ||
95 | - import { Card, Button, Tooltip } from 'ant-design-vue'; | ||
96 | - import 'ace-builds/src-noconflict/theme-chrome'; // 默认设置的主题 | ||
97 | - import 'ace-builds/src-noconflict/theme-terminal'; // 默认设置的主题 | ||
98 | - import 'ace-builds/src-noconflict/mode-javascript'; // 默认设置的语言模式 | ||
99 | - import { beautify } from 'ace-builds/src-noconflict/ext-beautify.js'; | ||
100 | - import { CopyOutlined } from '@ant-design/icons-vue'; | ||
101 | - import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard'; | ||
102 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
103 | - import { findDictItemByCode } from '/@/api/system/dict'; | ||
104 | - import { QuestionCircleOutlined } from '@ant-design/icons-vue'; | ||
105 | - import { defaultTitle, defaultScriptTypeContent } from './config.data'; | ||
106 | - import { useAppStore } from '/@/store/modules/app'; | ||
107 | - | ||
108 | - defineEmits(['register']); | ||
109 | - const props = defineProps({ | ||
110 | - ifAdd: { type: Boolean, default: true }, | ||
111 | - }); | ||
112 | - | ||
113 | - const scriptForm = reactive({ | ||
114 | - name: '', | ||
115 | - description: '', | ||
116 | - convertJs: '', | ||
117 | - script: '', | ||
118 | - params: '', | ||
119 | - output: '', | ||
120 | - scriptType: 'TRANSPORT_TCP_UP', | ||
121 | - saveOriginalData: 'true', | ||
122 | - }); | ||
123 | - | ||
124 | - const reportTypeOptions = reactive({ | ||
125 | - originalOptions: [], | ||
126 | - scriptTypeOptions: [], | ||
127 | - }); | ||
128 | - | ||
129 | - const { originalOptions, scriptTypeOptions } = toRefs(reportTypeOptions); | ||
130 | - | ||
131 | - const { createMessage } = useMessage(); | ||
132 | - | ||
133 | - const { clipboardRef, copiedRef } = useCopyToClipboard(); | ||
134 | - | ||
135 | - const aceEditor = ref(); | ||
136 | - | ||
137 | - const aceRef = ref(); | ||
138 | - | ||
139 | - const userStore = useAppStore(); | ||
140 | - | ||
141 | - const getAceClass = computed((): string => userStore.getDarkMode); | ||
142 | - | ||
143 | - const setDefaultRadio = (p2, p3) => { | ||
144 | - scriptForm.saveOriginalData = p2; | ||
145 | - scriptForm.scriptType = p3; | ||
146 | - }; | ||
147 | - | ||
148 | - const getDictValue = async (dict_type) => { | ||
149 | - const res = await findDictItemByCode({ | ||
150 | - dictCode: dict_type, | ||
151 | - }); | ||
152 | - return res.map((m) => { | ||
153 | - return { label: m.itemText, value: m.itemValue }; | ||
154 | - }); | ||
155 | - }; | ||
156 | - | ||
157 | - onMounted(async () => { | ||
158 | - reportTypeOptions.originalOptions = (await getDictValue('original_data')) as never as any; | ||
159 | - reportTypeOptions.scriptTypeOptions = (await getDictValue('script_type')) as never as any; | ||
160 | - }); | ||
161 | - | ||
162 | - // 初始化编辑器 | ||
163 | - const initEditor = () => { | ||
164 | - aceEditor.value = ace.edit(aceRef.value, { | ||
165 | - maxLines: 12, // 最大行数,超过会自动出现滚动条 | ||
166 | - minLines: 12, // 最小行数,还未到最大行数时,编辑器会自动伸缩大小 | ||
167 | - fontSize: 14, // 编辑器内字体大小 | ||
168 | - theme: 'ace/theme/chrome', // 默认设置的主题 | ||
169 | - mode: 'ace/mode/javascript', // 默认设置的语言模式 | ||
170 | - tabSize: 2, // 制表符设置为 4 个空格大小 | ||
171 | - }); | ||
172 | - | ||
173 | - aceEditor.value.setOptions({ | ||
174 | - enableBasicAutocompletion: true, | ||
175 | - enableLiveAutocompletion: true, | ||
176 | - theme: getAceClass.value === 'dark' ? 'ace/theme/terminal' : 'ace/theme/chrome', | ||
177 | - }); | ||
178 | - aceEditor.value.setValue(''); | ||
179 | - beautify(aceEditor.value.session); | ||
180 | - switchScriptTypeGetContent('TRANSPORT_TCP_UP'); | ||
181 | - }; | ||
182 | - | ||
183 | - const handleScriptType = ({ target }) => { | ||
184 | - const { value } = target; | ||
185 | - switchScriptTypeGetContent(value); | ||
186 | - }; | ||
187 | - | ||
188 | - const switchScriptTypeGetContent = (type) => { | ||
189 | - aceEditor.value.setValue(defaultScriptTypeContent[type]); | ||
190 | - }; | ||
191 | - | ||
192 | - const handleCopy = () => { | ||
193 | - const valueRef = aceEditor.value.getValue(); | ||
194 | - const value = unref(valueRef); | ||
195 | - if (!value) { | ||
196 | - createMessage.warning('请输入要拷贝的内容!'); | ||
197 | - return; | ||
198 | - } | ||
199 | - clipboardRef.value = value; | ||
200 | - if (unref(copiedRef)) { | ||
201 | - createMessage.success('复制成功!'); | ||
202 | - } | ||
203 | - }; | ||
204 | - | ||
205 | - const formRef = ref(); | ||
206 | - | ||
207 | - const getFormData = async () => { | ||
208 | - const value = await formRef.value.validateFields(); | ||
209 | - if (props.ifAdd) { | ||
210 | - scriptForm.convertJs = aceEditor.value.getValue(); | ||
211 | - if (scriptForm.convertJs == '') { | ||
212 | - createMessage.error('请编写脚本内容'); | ||
213 | - throw '请编写脚本内容'; | ||
214 | - } | ||
215 | - } else { | ||
216 | - scriptForm.script = aceEditor.value.getValue(); | ||
217 | - if (scriptForm.script == '') { | ||
218 | - createMessage.error('请编写脚本内容'); | ||
219 | - throw '请编写脚本内容'; | ||
220 | - } | ||
221 | - } | ||
222 | - if (!value) return; | ||
223 | - if (scriptForm.params) { | ||
224 | - const trimParams = scriptForm.params.replace(/\s*/g, ''); | ||
225 | - Reflect.set(value, 'params', trimParams); | ||
226 | - } | ||
227 | - if (scriptForm.convertJs.length > 1000) { | ||
228 | - createMessage.error('脚本内容长度不能大于1000'); | ||
229 | - throw '脚本内容长度不能大于1000'; | ||
230 | - } | ||
231 | - return { | ||
232 | - ...value, | ||
233 | - ...{ convertJs: props.ifAdd ? scriptForm.convertJs : null }, | ||
234 | - ...{ script: !props.ifAdd ? scriptForm.script : null }, | ||
235 | - ...{ saveOriginalData: scriptForm.saveOriginalData === 'false' ? false : true }, | ||
236 | - }; | ||
237 | - }; | ||
238 | - | ||
239 | - const handleInputChange = (e) => { | ||
240 | - const trimParams = e.target.value.replace(/\s*/g, ''); | ||
241 | - Reflect.set(scriptForm, 'params', trimParams); | ||
242 | - if (scriptForm.scriptType === 'TRANSPORT_TCP_DOWN') { | ||
243 | - aceEditor.value.setValue(`out.datas = "${scriptForm.params}";out.deviceName = "sensor";`); | ||
244 | - } | ||
245 | - }; | ||
246 | - | ||
247 | - const setFormData = (v) => { | ||
248 | - if (v) { | ||
249 | - for (let i in scriptForm) { | ||
250 | - Reflect.set(scriptForm, i, v[i]); | ||
251 | - } | ||
252 | - nextTick(() => { | ||
253 | - setTimeout(() => { | ||
254 | - scriptForm.saveOriginalData = v.saveOriginalData === false ? 'false' : 'true'; | ||
255 | - }, 10); | ||
256 | - }); | ||
257 | - aceEditor.value.setValue(v.convertJs); | ||
258 | - handleFormat(); | ||
259 | - } | ||
260 | - }; | ||
261 | - | ||
262 | - const setScriptContentData = (v) => { | ||
263 | - aceEditor.value.setValue(v); | ||
264 | - handleFormat(); | ||
265 | - }; | ||
266 | - | ||
267 | - const resetFormData = () => { | ||
268 | - for (let i in scriptForm) { | ||
269 | - Reflect.set(scriptForm, i, ''); | ||
270 | - } | ||
271 | - }; | ||
272 | - | ||
273 | - const setScriptOutputData = (v) => { | ||
274 | - scriptForm.output = v; | ||
275 | - }; | ||
276 | - | ||
277 | - const handleFormat = () => { | ||
278 | - beautify(aceEditor.value.session); | ||
279 | - aceEditor.value.getSession().setUseWrapMode(true); | ||
280 | - }; | ||
281 | - | ||
282 | - defineExpose({ | ||
283 | - initEditor, | ||
284 | - getFormData, | ||
285 | - resetFormData, | ||
286 | - setFormData, | ||
287 | - setScriptContentData, | ||
288 | - setScriptOutputData, | ||
289 | - setDefaultRadio, | ||
290 | - }); | ||
291 | -</script> | ||
292 | -<style lang="less" scoped> | ||
293 | - @import url('./ConverScriptModal.less'); | ||
294 | -</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-group compact> | ||
17 | + <a-input | ||
18 | + style="width: calc(100% - 200px)" | ||
19 | + v-if="ifAdd" | ||
20 | + :maxlength="36" | ||
21 | + @change="handleInputChange" | ||
22 | + v-model:value="scriptForm.name" | ||
23 | + placeholder="请输入脚本名称" | ||
24 | + /> | ||
25 | + <a-input | ||
26 | + @change="handleInputChange" | ||
27 | + v-else | ||
28 | + v-model:value="scriptForm.params" | ||
29 | + placeholder="请输入参数" | ||
30 | + /> | ||
31 | + <a-button @click="onHandleClick(text)" v-show="ifAdd && !view" type="primary"> | ||
32 | + 测试 | ||
33 | + </a-button> | ||
34 | + </a-input-group> | ||
35 | + </a-form-item> | ||
36 | + <a-form-item | ||
37 | + label="脚本类型" | ||
38 | + name="scriptType" | ||
39 | + :rules="[{ required: true, message: '请选择脚本类型' }]" | ||
40 | + > | ||
41 | + <a-space direction="vertical"> | ||
42 | + <a-radio-group | ||
43 | + @change="handleScriptType" | ||
44 | + v-model:value="scriptForm.scriptType" | ||
45 | + :options="scriptTypeOptions" | ||
46 | + /> | ||
47 | + </a-space> | ||
48 | + </a-form-item> | ||
49 | + <a-form-item | ||
50 | + label="保存原始数据" | ||
51 | + name="saveOriginalData" | ||
52 | + :rules="[{ required: true, message: '请选择保存原始数据' }]" | ||
53 | + > | ||
54 | + <a-space direction="vertical"> | ||
55 | + <a-radio-group v-model:value="scriptForm.saveOriginalData" :options="originalOptions" /> | ||
56 | + </a-space> | ||
57 | + </a-form-item> | ||
58 | + <a-form-item label="脚本内容" :name="ifAdd ? 'convertJs' : 'script'"> | ||
59 | + <Card title="脚本内容" :bodyStyle="{ padding: 0, height: '280px' }"> | ||
60 | + <template #extra> | ||
61 | + <a-button @click="handleFormat" size="small">格式化</a-button> | ||
62 | + <Tooltip v-if="!ifAdd" :title="defaultAuthTitle" class="ml-2"> | ||
63 | + <QuestionCircleOutlined style="font-size: 1rem" /> | ||
64 | + </Tooltip> | ||
65 | + <Tooltip v-if="!ifAdd" :title="defaultUpTitle" class="ml-2"> | ||
66 | + <QuestionCircleOutlined style="font-size: 1rem" /> | ||
67 | + </Tooltip> | ||
68 | + </template> | ||
69 | + <div ref="aceRef" class="overflow-hidden"></div> | ||
70 | + </Card> | ||
71 | + <Button @click="handleCopy" class="mt-4"> | ||
72 | + <template #icon> | ||
73 | + <CopyOutlined /> | ||
74 | + </template> | ||
75 | + copy | ||
76 | + </Button> | ||
77 | + </a-form-item> | ||
78 | + <a-form-item | ||
79 | + :label="ifAdd ? '备注' : '输出参数(output)'" | ||
80 | + :name="ifAdd ? 'description' : 'output'" | ||
81 | + > | ||
82 | + <a-textarea | ||
83 | + :rows="3" | ||
84 | + v-if="ifAdd" | ||
85 | + v-model:value="scriptForm.description" | ||
86 | + placeholder="请输入备注" | ||
87 | + :maxlength="255" | ||
88 | + /> | ||
89 | + <a-textarea | ||
90 | + :rows="5" | ||
91 | + v-else | ||
92 | + v-model:value="scriptForm.output" | ||
93 | + placeholder="输出参数为服务端返回的内容" | ||
94 | + :maxlength="255" | ||
95 | + /> | ||
96 | + </a-form-item> | ||
97 | + </a-form> | ||
98 | + <TestScriptModal @register="registerModal" @success="handleSuccess" /> | ||
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 { defaultAuthTitle, defaultUpTitle, defaultScriptTypeContent } from './config.data'; | ||
115 | + import { useAppStore } from '/@/store/modules/app'; | ||
116 | + import TestScriptModal from './TestScriptModal.vue'; | ||
117 | + import { useModal } from '/@/components/Modal'; | ||
118 | + | ||
119 | + defineEmits(['register']); | ||
120 | + const props = defineProps({ | ||
121 | + ifAdd: { type: Boolean, default: true }, | ||
122 | + text: { type: String, default: '' }, | ||
123 | + view: { type: Boolean, default: false }, | ||
124 | + }); | ||
125 | + | ||
126 | + const scriptForm = reactive({ | ||
127 | + name: '', | ||
128 | + description: '', | ||
129 | + convertJs: '', | ||
130 | + script: '', | ||
131 | + params: '', | ||
132 | + output: '', | ||
133 | + scriptType: 'TRANSPORT_TCP_UP', | ||
134 | + saveOriginalData: 'true', | ||
135 | + }); | ||
136 | + | ||
137 | + const reportTypeOptions = reactive({ | ||
138 | + originalOptions: [], | ||
139 | + scriptTypeOptions: [], | ||
140 | + }); | ||
141 | + | ||
142 | + const { originalOptions, scriptTypeOptions } = toRefs(reportTypeOptions); | ||
143 | + | ||
144 | + const { createMessage } = useMessage(); | ||
145 | + | ||
146 | + const { clipboardRef, copiedRef } = useCopyToClipboard(); | ||
147 | + | ||
148 | + const aceEditor = ref(); | ||
149 | + | ||
150 | + const aceRef = ref(); | ||
151 | + | ||
152 | + const userStore = useAppStore(); | ||
153 | + | ||
154 | + const getAceClass = computed((): string => userStore.getDarkMode); | ||
155 | + | ||
156 | + const setDefaultRadio = (p2, p3) => { | ||
157 | + scriptForm.saveOriginalData = p2; | ||
158 | + scriptForm.scriptType = p3; | ||
159 | + }; | ||
160 | + | ||
161 | + const getDictValue = async (dict_type) => { | ||
162 | + const res = await findDictItemByCode({ | ||
163 | + dictCode: dict_type, | ||
164 | + }); | ||
165 | + return res.map((m) => { | ||
166 | + return { label: m.itemText, value: m.itemValue }; | ||
167 | + }); | ||
168 | + }; | ||
169 | + | ||
170 | + onMounted(async () => { | ||
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 | + aceEditor.value.setValue(defaultScriptTypeContent[type]); | ||
203 | + }; | ||
204 | + | ||
205 | + const handleCopy = () => { | ||
206 | + const valueRef = aceEditor.value.getValue(); | ||
207 | + const value = unref(valueRef); | ||
208 | + if (!value) { | ||
209 | + createMessage.warning('请输入要拷贝的内容!'); | ||
210 | + return; | ||
211 | + } | ||
212 | + clipboardRef.value = value; | ||
213 | + if (unref(copiedRef)) { | ||
214 | + createMessage.success('复制成功!'); | ||
215 | + } | ||
216 | + }; | ||
217 | + | ||
218 | + const formRef = ref(); | ||
219 | + | ||
220 | + const getFormData = async () => { | ||
221 | + const value = await formRef.value.validateFields(); | ||
222 | + if (props.ifAdd) { | ||
223 | + scriptForm.convertJs = aceEditor.value.getValue(); | ||
224 | + if (scriptForm.convertJs == '') { | ||
225 | + createMessage.error('请编写脚本内容'); | ||
226 | + throw '请编写脚本内容'; | ||
227 | + } | ||
228 | + } else { | ||
229 | + scriptForm.script = aceEditor.value.getValue(); | ||
230 | + if (scriptForm.script == '') { | ||
231 | + createMessage.error('请编写脚本内容'); | ||
232 | + throw '请编写脚本内容'; | ||
233 | + } | ||
234 | + } | ||
235 | + if (!value) return; | ||
236 | + if (scriptForm.params) { | ||
237 | + const trimParams = scriptForm.params.replace(/\s*/g, ''); | ||
238 | + Reflect.set(value, 'params', trimParams); | ||
239 | + } | ||
240 | + if (scriptForm.convertJs.length > 1000) { | ||
241 | + createMessage.error('脚本内容长度不能大于1000'); | ||
242 | + throw '脚本内容长度不能大于1000'; | ||
243 | + } | ||
244 | + return { | ||
245 | + ...value, | ||
246 | + ...{ convertJs: props.ifAdd ? scriptForm.convertJs : null }, | ||
247 | + ...{ script: !props.ifAdd ? scriptForm.script : null }, | ||
248 | + ...{ saveOriginalData: scriptForm.saveOriginalData === 'false' ? false : true }, | ||
249 | + }; | ||
250 | + }; | ||
251 | + | ||
252 | + const handleInputChange = (e) => { | ||
253 | + const trimParams = e.target.value.replace(/\s*/g, ''); | ||
254 | + Reflect.set(scriptForm, 'params', trimParams); | ||
255 | + if (scriptForm.scriptType === 'TRANSPORT_TCP_DOWN') { | ||
256 | + aceEditor.value.setValue(`out.datas = "${scriptForm.params}";out.deviceName = "sensor";`); | ||
257 | + } | ||
258 | + }; | ||
259 | + | ||
260 | + const getRecordId = ref(''); | ||
261 | + | ||
262 | + const setFormData = (v) => { | ||
263 | + if (v) { | ||
264 | + getRecordId.value = v?.id; | ||
265 | + for (let i in scriptForm) { | ||
266 | + Reflect.set(scriptForm, i, v[i]); | ||
267 | + } | ||
268 | + nextTick(() => { | ||
269 | + setTimeout(() => { | ||
270 | + scriptForm.saveOriginalData = v.saveOriginalData === false ? 'false' : 'true'; | ||
271 | + }, 10); | ||
272 | + }); | ||
273 | + aceEditor.value.setValue(v.convertJs); | ||
274 | + handleFormat(); | ||
275 | + } | ||
276 | + }; | ||
277 | + | ||
278 | + const setScriptContentData = (v) => { | ||
279 | + aceEditor.value.setValue(v); | ||
280 | + handleFormat(); | ||
281 | + }; | ||
282 | + | ||
283 | + const resetFormData = () => { | ||
284 | + for (let i in scriptForm) { | ||
285 | + Reflect.set(scriptForm, i, ''); | ||
286 | + } | ||
287 | + }; | ||
288 | + | ||
289 | + const setScriptOutputData = (v) => { | ||
290 | + scriptForm.output = v; | ||
291 | + }; | ||
292 | + | ||
293 | + const handleFormat = () => { | ||
294 | + beautify(aceEditor.value.session); | ||
295 | + aceEditor.value.getSession().setUseWrapMode(true); | ||
296 | + }; | ||
297 | + | ||
298 | + const [registerModal, { openModal }] = useModal(); | ||
299 | + | ||
300 | + const onHandleClick = (o) => { | ||
301 | + openModal(true, { | ||
302 | + isAuth: '', | ||
303 | + isUpdate: false, | ||
304 | + record: o === 'add' ? null : getRecordId.value, | ||
305 | + isTest: true, | ||
306 | + isText: 'test', | ||
307 | + isTitle: 'test', | ||
308 | + }); | ||
309 | + }; | ||
310 | + | ||
311 | + defineExpose({ | ||
312 | + initEditor, | ||
313 | + getFormData, | ||
314 | + resetFormData, | ||
315 | + setFormData, | ||
316 | + setScriptContentData, | ||
317 | + setScriptOutputData, | ||
318 | + setDefaultRadio, | ||
319 | + }); | ||
320 | +</script> | ||
321 | +<style lang="less" scoped> | ||
322 | + @import url('./ConverScriptModal.less'); | ||
323 | +</style> |
@@ -10,7 +10,12 @@ | @@ -10,7 +10,12 @@ | ||
10 | @cancel="handleCancel" | 10 | @cancel="handleCancel" |
11 | @ok="handleSubmit" | 11 | @ok="handleSubmit" |
12 | > | 12 | > |
13 | - <ConverScript :ifAdd="isTest ? false : true" ref="converScriptRef" /> | 13 | + <ConverScript |
14 | + :view="isViewDetail" | ||
15 | + :text="isTitle" | ||
16 | + :ifAdd="isTest ? false : true" | ||
17 | + ref="converScriptRef" | ||
18 | + /> | ||
14 | </BasicModal> | 19 | </BasicModal> |
15 | </div> | 20 | </div> |
16 | </template> | 21 | </template> |
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 | ||
14 | + :view="isViewDetail" | ||
15 | + :text="isTitle" | ||
16 | + :ifAdd="isTest ? false : true" | ||
17 | + ref="converScriptRef" | ||
18 | + /> | ||
19 | + </BasicModal> | ||
20 | + </div> | ||
21 | +</template> | ||
22 | +<script setup lang="ts"> | ||
23 | + import { ref, computed, unref, reactive } from 'vue'; | ||
24 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
25 | + import ConverScript from './ConverScript.vue'; | ||
26 | + import { | ||
27 | + createOrEditScriptManage, | ||
28 | + getScriptManageDetail, | ||
29 | + testScriptManage, | ||
30 | + } from '/@/api/scriptmanage/scriptManager'; | ||
31 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
32 | + | ||
33 | + const emits = defineEmits(['success', 'register']); | ||
34 | + const { createMessage } = useMessage(); | ||
35 | + const converScriptRef = ref<InstanceType<typeof ConverScript>>(); | ||
36 | + const getTitle = computed(() => (isUpdate.value ? '编辑转换脚本' : '新增转换脚本')); | ||
37 | + const isUpdate = ref(false); | ||
38 | + const isViewDetail = ref(''); | ||
39 | + const isTest = ref(false); | ||
40 | + const isText = ref(''); | ||
41 | + const isTitle = ref(''); | ||
42 | + const editData = reactive({ | ||
43 | + data: {}, | ||
44 | + }); | ||
45 | + const [register, { setModalProps, closeModal }] = useModalInner(async (data) => { | ||
46 | + setModalProps({ loading: true }); | ||
47 | + handleCancel(false); | ||
48 | + isUpdate.value = data.isUpdate; | ||
49 | + isViewDetail.value = data.isView; | ||
50 | + isTest.value = data.isTest; | ||
51 | + isText.value = data.isText; | ||
52 | + isTitle.value = data.isTitle; | ||
53 | + editData.data = data.record; | ||
54 | + setModalProps({ loading: false }); | ||
55 | + converScriptRef.value?.initEditor(); | ||
56 | + if (!unref(isViewDetail)) { | ||
57 | + const title = | ||
58 | + unref(isTitle) == 'edit' | ||
59 | + ? '编辑转换脚本' | ||
60 | + : unref(isTitle) == 'add' | ||
61 | + ? '新增转换脚本' | ||
62 | + : '测试转换脚本'; | ||
63 | + const okText = isText.value == 'test' ? '测试' : '确定'; | ||
64 | + if (unref(isTitle) == 'add') { | ||
65 | + converScriptRef.value?.setDefaultRadio('true', 'TRANSPORT_TCP_UP'); | ||
66 | + } | ||
67 | + if (unref(isTitle) == 'edit') { | ||
68 | + converScriptRef.value?.setFormData(data.record); | ||
69 | + } | ||
70 | + if (unref(isTitle) == 'test') { | ||
71 | + if (data.record) { | ||
72 | + const res = await getScriptManageDetail(data.record); | ||
73 | + converScriptRef.value?.setFormData(res); | ||
74 | + } else { | ||
75 | + converScriptRef.value?.setDefaultRadio('true', 'TRANSPORT_TCP_UP'); | ||
76 | + } | ||
77 | + } | ||
78 | + setModalProps({ title, showOkBtn: true, showCancelBtn: true, okText }); | ||
79 | + if (!unref(isUpdate)) { | ||
80 | + } | ||
81 | + } else { | ||
82 | + setModalProps({ showOkBtn: false, showCancelBtn: false, title: '查看转换脚本' }); | ||
83 | + const res = await getScriptManageDetail(data.record.id); | ||
84 | + converScriptRef.value?.setFormData(res || {}); | ||
85 | + } | ||
86 | + }); | ||
87 | + | ||
88 | + const handleSubmit = async () => { | ||
89 | + setModalProps({ confirmLoading: true }); | ||
90 | + try { | ||
91 | + const val = await converScriptRef.value?.getFormData(); | ||
92 | + const tempObj = { | ||
93 | + ...editData.data, | ||
94 | + ...val, | ||
95 | + }; | ||
96 | + const res: any = | ||
97 | + isText.value == 'test' | ||
98 | + ? await testScriptManage(val) | ||
99 | + : await createOrEditScriptManage(tempObj); | ||
100 | + createMessage.success( | ||
101 | + unref(isTitle) == 'edit' | ||
102 | + ? '编辑转换脚本成功' | ||
103 | + : unref(isTitle) == 'add' | ||
104 | + ? '新增转换脚本成功' | ||
105 | + : '测试转换脚本成功' | ||
106 | + ); | ||
107 | + if (unref(isTitle) == 'add' || unref(isTitle) == 'edit') { | ||
108 | + setTimeout(() => { | ||
109 | + closeModal(); | ||
110 | + }, 10); | ||
111 | + emits('success', { | ||
112 | + res, | ||
113 | + text: isText.value, | ||
114 | + }); | ||
115 | + } else { | ||
116 | + if (res) { | ||
117 | + converScriptRef.value?.setScriptOutputData(res?.output || res?.error); | ||
118 | + } | ||
119 | + } | ||
120 | + } finally { | ||
121 | + setModalProps({ confirmLoading: false }); | ||
122 | + } | ||
123 | + }; | ||
124 | + const handleCancel = (flag) => { | ||
125 | + if (flag) { | ||
126 | + closeModal(); | ||
127 | + } | ||
128 | + converScriptRef.value?.resetFormData(); | ||
129 | + }; | ||
130 | +</script> | ||
131 | +<style lang="less" scoped> | ||
132 | + @import url('./ConverScriptModal.less'); | ||
133 | +</style> |
@@ -58,25 +58,30 @@ export const searchFormSchema: FormSchema[] = [ | @@ -58,25 +58,30 @@ export const searchFormSchema: FormSchema[] = [ | ||
58 | }, | 58 | }, |
59 | ]; | 59 | ]; |
60 | 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 | - ]), | 61 | +export const defaultAuthTitle = h('div', { style: 'background:#404040' }, [ |
62 | + h('h3', { style: 'color:white' }, '设备鉴权示例'), | ||
63 | + h('h3', { style: 'color:white' }, '输入参数:为16进制字符串'), | ||
64 | + h('h3', { style: 'color:white' }, '输出参数:{"password":"","success":""}'), | ||
65 | + h('h3', { style: 'color:white' }, 'password为设备鉴权信息,success为鉴权成功后响应给设备的内容'), | ||
66 | +]); | ||
67 | + | ||
68 | +export const defaultUpTitle = h('div', { style: 'background:#404040' }, [ | ||
69 | + h('h3', { style: 'color:white' }, '上行数据解析示例'), | ||
70 | + h('h3', { style: 'color:white' }, '输入参数:为字符串'), | ||
71 | + h( | ||
72 | + 'h3', | ||
73 | + { style: 'color:white' }, | ||
74 | + `输出参数:{"datas":{"source":""},"telemetry":true,"ackMsg":"","deviceName":"","ts":1681701034289}` | ||
75 | + ), | ||
76 | + h( | ||
77 | + 'h3', | ||
78 | + { style: 'color:white' }, | ||
79 | + `datas:json对象,属性名为遥测指标或子设备名称 | ||
80 | + telemetry: datas内容是否为遥测数据 | ||
81 | + ackMsg: 响应给设备的确认消息 | ||
82 | + deviceName: 设备名称 | ||
83 | + ts: 数据采集时间` | ||
84 | + ), | ||
80 | ]); | 85 | ]); |
81 | 86 | ||
82 | // TRANSPORT_TCP_DOWN: 'out.datas = "";out.deviceName = "sensor";', | 87 | // TRANSPORT_TCP_DOWN: 'out.datas = "";out.deviceName = "sensor";', |
@@ -273,6 +273,8 @@ | @@ -273,6 +273,8 @@ | ||
273 | status: searchInfo.status, | 273 | status: searchInfo.status, |
274 | page: pagination.current, | 274 | page: pagination.current, |
275 | pageSize: pagination.pageSize, | 275 | pageSize: pagination.pageSize, |
276 | + startTime: moment(searchInfo.sendTime.at(-2)).valueOf(), | ||
277 | + endTime: moment(searchInfo.sendTime.at(-1)).valueOf(), | ||
276 | }); | 278 | }); |
277 | setTableData(res.items); | 279 | setTableData(res.items); |
278 | pagination.total = res.total; | 280 | pagination.total = res.total; |