Showing
42 changed files
with
1388 additions
and
132 deletions
@@ -58,6 +58,7 @@ | @@ -58,6 +58,7 @@ | ||
58 | "flv.js": "^1.6.2", | 58 | "flv.js": "^1.6.2", |
59 | "hls.js": "^1.0.10", | 59 | "hls.js": "^1.0.10", |
60 | "intro.js": "^4.1.0", | 60 | "intro.js": "^4.1.0", |
61 | + "js-beautify": "^1.14.9", | ||
61 | "jsoneditor": "^9.7.2", | 62 | "jsoneditor": "^9.7.2", |
62 | "jwt-decode": "^3.1.2", | 63 | "jwt-decode": "^3.1.2", |
63 | "lodash-es": "^4.17.21", | 64 | "lodash-es": "^4.17.21", |
src/api/ruleChainDesigner/index.ts
0 → 100644
1 | +import { DeviceInfoItemType } from './model'; | ||
2 | +import { TBPaginationResult } from '/#/axios'; | ||
3 | +import { defHttp } from '/@/utils/http/axios'; | ||
4 | + | ||
5 | +enum Api { | ||
6 | + GET_DEVICE_INFOS = '/tenant/deviceInfos', | ||
7 | +} | ||
8 | + | ||
9 | +export const getDeviceInfos = () => { | ||
10 | + return defHttp.get<TBPaginationResult<DeviceInfoItemType>>( | ||
11 | + { | ||
12 | + url: Api.GET_DEVICE_INFOS, | ||
13 | + }, | ||
14 | + { joinPrefix: false } | ||
15 | + ); | ||
16 | +}; |
src/api/ruleChainDesigner/model/index.ts
0 → 100644
1 | +export interface DeviceInfoItemType { | ||
2 | + id: Id; | ||
3 | + createdTime: number; | ||
4 | + additionalInfo: AdditionalInfo; | ||
5 | + tenantId: Id; | ||
6 | + customerId: Id; | ||
7 | + name: string; | ||
8 | + type: string; | ||
9 | + label: string; | ||
10 | + deviceProfileId: Id; | ||
11 | + deviceData: DeviceData; | ||
12 | + firmwareId: any; | ||
13 | + softwareId: any; | ||
14 | + customerTitle: any; | ||
15 | + customerIsPublic: boolean; | ||
16 | + deviceProfileName: string; | ||
17 | +} | ||
18 | + | ||
19 | +export interface Id { | ||
20 | + entityType: string; | ||
21 | + id: string; | ||
22 | +} | ||
23 | + | ||
24 | +export interface DeviceData { | ||
25 | + configuration: Configuration; | ||
26 | + transportConfiguration: TransportConfiguration; | ||
27 | +} | ||
28 | + | ||
29 | +export interface AdditionalInfo { | ||
30 | + gateway: boolean; | ||
31 | + description: string; | ||
32 | + overwriteActivityTime: boolean; | ||
33 | +} | ||
34 | +export interface Configuration { | ||
35 | + type: string; | ||
36 | +} | ||
37 | + | ||
38 | +export interface TransportConfiguration { | ||
39 | + type: string; | ||
40 | +} |
1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
2 | - import { ref, watch } from 'vue'; | ||
3 | - import JSONEditor, { JSONEditorOptions } from 'jsoneditor'; | ||
4 | - import 'jsoneditor/dist/jsoneditor.min.css'; | ||
5 | - import { unref } from 'vue'; | ||
6 | - import { onMounted } from 'vue'; | ||
7 | - import { computed } from '@vue/reactivity'; | ||
8 | - import { onUnmounted } from 'vue'; | 2 | + import { onMounted, computed, onUnmounted, unref, ref, watch } from 'vue'; |
3 | + import { Tooltip } from 'ant-design-vue'; | ||
4 | + import { Icon } from '/@/components/Icon'; | ||
5 | + import { useFullscreen } from '@vueuse/core'; | ||
6 | + import { isNumber, isObject, isString } from '/@/utils/is'; | ||
7 | + import AceEditor, { Ace } from 'ace-builds'; | ||
8 | + import workerJsonUrl from 'ace-builds/src-noconflict/worker-json?url'; | ||
9 | + import githubTheme from 'ace-builds/src-noconflict/theme-github?url'; | ||
10 | + import 'ace-builds/src-noconflict/mode-json'; | ||
9 | 11 | ||
10 | enum EventEnum { | 12 | enum EventEnum { |
11 | UPDATE_VALUE = 'update:value', | 13 | UPDATE_VALUE = 'update:value', |
@@ -16,89 +18,133 @@ | @@ -16,89 +18,133 @@ | ||
16 | 18 | ||
17 | const props = withDefaults( | 19 | const props = withDefaults( |
18 | defineProps<{ | 20 | defineProps<{ |
19 | - value?: string; | ||
20 | - options?: JSONEditorOptions; | ||
21 | - height?: number; | 21 | + value?: string | Recordable; |
22 | + height?: number | string; | ||
23 | + title?: string; | ||
24 | + disabled?: boolean; | ||
22 | }>(), | 25 | }>(), |
23 | { | 26 | { |
24 | - options: () => | ||
25 | - ({ | ||
26 | - mode: 'code', | ||
27 | - mainMenuBar: false, | ||
28 | - statusBar: false, | ||
29 | - } as JSONEditorOptions), | ||
30 | height: 150, | 27 | height: 150, |
31 | } | 28 | } |
32 | ); | 29 | ); |
33 | 30 | ||
34 | const emit = defineEmits<{ | 31 | const emit = defineEmits<{ |
35 | - (e: EventEnum.UPDATE_VALUE, value: any, instance?: JSONEditor): void; | ||
36 | - (e: EventEnum.CHANGE, value: any, instance?: JSONEditor): void; | ||
37 | - (e: EventEnum.BLUR, event: Event, instance?: JSONEditor): void; | ||
38 | - (e: EventEnum.FOCUS, event: Event, instance?: JSONEditor): void; | 32 | + (e: EventEnum.UPDATE_VALUE, value: any, instance?: Ace.Editor): void; |
33 | + (e: EventEnum.CHANGE, value: any, instance?: Ace.Editor): void; | ||
34 | + (e: EventEnum.BLUR, event: Event, instance?: Ace.Editor): void; | ||
35 | + (e: EventEnum.FOCUS, event: Event, instance?: Ace.Editor): void; | ||
39 | }>(); | 36 | }>(); |
40 | 37 | ||
41 | const jsonEditorElRef = ref<Nullable<any>>(); | 38 | const jsonEditorElRef = ref<Nullable<any>>(); |
42 | 39 | ||
43 | - const editoreRef = ref<JSONEditor>(); | 40 | + const editoreRef = ref<Ace.Editor>(); |
44 | 41 | ||
45 | const isFocus = ref(false); | 42 | const isFocus = ref(false); |
46 | 43 | ||
47 | - const handleChange = (value: any) => { | 44 | + const handleOnChange = () => { |
45 | + const value = get(); | ||
48 | emit(EventEnum.UPDATE_VALUE, value, unref(editoreRef)); | 46 | emit(EventEnum.UPDATE_VALUE, value, unref(editoreRef)); |
49 | emit(EventEnum.CHANGE, value, unref(editoreRef)); | 47 | emit(EventEnum.CHANGE, value, unref(editoreRef)); |
50 | }; | 48 | }; |
51 | 49 | ||
52 | - const handleEmit = (event: Event, key: EventEnum) => { | ||
53 | - emit(key as EventEnum[keyof EventEnum], event, unref(editoreRef)); | 50 | + const handleOnBlur = (event: Event) => { |
51 | + isFocus.value = false; | ||
52 | + emit(EventEnum.BLUR, event, unref(editoreRef)); | ||
54 | }; | 53 | }; |
55 | 54 | ||
56 | - const getOptions = computed(() => { | ||
57 | - const { options } = props; | ||
58 | - return { | ||
59 | - ...options, | ||
60 | - onChangeText: handleChange, | ||
61 | - onBlur: (event: Event) => { | ||
62 | - isFocus.value = false; | ||
63 | - handleEmit(event, EventEnum.BLUR); | ||
64 | - }, | ||
65 | - onFocus: (event: Event) => { | ||
66 | - isFocus.value = true; | ||
67 | - handleEmit(event, EventEnum.FOCUS); | ||
68 | - }, | ||
69 | - } as JSONEditorOptions; | ||
70 | - }); | 55 | + const handleOnFocus = (event: Event) => { |
56 | + isFocus.value = true; | ||
57 | + emit(EventEnum.FOCUS, event, unref(editoreRef)); | ||
58 | + }; | ||
59 | + | ||
60 | + const getFormatValue = (value: Recordable | string = '') => { | ||
61 | + return isObject(value) ? JSON.stringify(value, null, 2) : value; | ||
62 | + }; | ||
71 | 63 | ||
72 | const initialize = () => { | 64 | const initialize = () => { |
73 | - editoreRef.value = new JSONEditor(unref(jsonEditorElRef), unref(getOptions)); | 65 | + AceEditor.config.setModuleUrl('ace/mode/json_worker', workerJsonUrl); |
66 | + AceEditor.config.setModuleUrl('ace/theme/github', githubTheme); | ||
67 | + const editor = AceEditor.edit(unref(jsonEditorElRef)!, { | ||
68 | + mode: 'ace/mode/json', | ||
69 | + }); | ||
70 | + editor.setTheme('ace/theme/github'); | ||
71 | + editor.setOptions({ | ||
72 | + fontSize: 14, | ||
73 | + }); | ||
74 | + | ||
75 | + editor.on('change', handleOnChange); | ||
76 | + editor.on('blur', handleOnBlur); | ||
77 | + editor.on('focus', handleOnFocus); | ||
78 | + | ||
79 | + editoreRef.value = editor; | ||
80 | + unref(editoreRef)?.setValue(getFormatValue(props.value), 1); | ||
81 | + unref(editoreRef)?.setReadOnly(props.disabled); | ||
74 | }; | 82 | }; |
75 | 83 | ||
76 | watch( | 84 | watch( |
77 | () => props.value, | 85 | () => props.value, |
78 | (target) => { | 86 | (target) => { |
79 | - if (unref(isFocus)) return; | ||
80 | - unref(editoreRef)?.setText(target || ''); | 87 | + // const position = unref(editoreRef)?.getCursorPosition(); |
88 | + unref(editoreRef)?.setValue(getFormatValue(target)); | ||
89 | + unref(editoreRef)?.clearSelection(); | ||
90 | + // position && unref(editoreRef)?.moveCursorToPosition(position!); | ||
81 | }, | 91 | }, |
82 | { | 92 | { |
83 | immediate: true, | 93 | immediate: true, |
84 | } | 94 | } |
85 | ); | 95 | ); |
86 | 96 | ||
97 | + watch( | ||
98 | + () => props.disabled, | ||
99 | + (value) => { | ||
100 | + unref(editoreRef)?.setReadOnly(value); | ||
101 | + } | ||
102 | + ); | ||
103 | + | ||
87 | const get = (): string => { | 104 | const get = (): string => { |
88 | - return unref(editoreRef)?.getText() || ''; | 105 | + return unref(editoreRef)?.getValue() || ''; |
89 | }; | 106 | }; |
90 | 107 | ||
91 | const set = (data: any) => { | 108 | const set = (data: any) => { |
92 | - return unref(editoreRef)?.set(data); | 109 | + return unref(editoreRef)?.setValue(getFormatValue(data)); |
93 | }; | 110 | }; |
94 | 111 | ||
95 | onMounted(() => { | 112 | onMounted(() => { |
96 | initialize(); | 113 | initialize(); |
97 | - unref(editoreRef)?.setText(props.value || ''); | 114 | + unref(editoreRef)?.setValue(getFormatValue(props.value)); |
98 | }); | 115 | }); |
99 | 116 | ||
100 | onUnmounted(() => { | 117 | onUnmounted(() => { |
118 | + unref(editoreRef)?.off('change', handleOnChange); | ||
119 | + unref(editoreRef)?.off('blur', handleOnBlur); | ||
120 | + unref(editoreRef)?.off('focus', handleOnFocus); | ||
101 | unref(editoreRef)?.destroy(); | 121 | unref(editoreRef)?.destroy(); |
122 | + unref(editoreRef)?.container.remove(); | ||
123 | + }); | ||
124 | + | ||
125 | + const handleFormat = () => { | ||
126 | + const value = get(); | ||
127 | + if (isString(value) && !value) return; | ||
128 | + unref(editoreRef)?.setValue(JSON.stringify(JSON.parse(value), null, 2)); | ||
129 | + unref(editoreRef)?.clearSelection(); | ||
130 | + }; | ||
131 | + | ||
132 | + const handleCompress = () => { | ||
133 | + const value = get(); | ||
134 | + if (isString(value) && !value) return; | ||
135 | + unref(editoreRef)?.setValue(JSON.stringify(JSON.parse(value))); | ||
136 | + unref(editoreRef)?.clearSelection(); | ||
137 | + }; | ||
138 | + | ||
139 | + const jsonEditorContainerElRef = ref<Nullable<HTMLDivElement>>(); | ||
140 | + const { isFullscreen, isSupported, toggle } = useFullscreen(jsonEditorContainerElRef); | ||
141 | + const handleFullScreen = () => { | ||
142 | + toggle(); | ||
143 | + }; | ||
144 | + | ||
145 | + const getHeight = computed(() => { | ||
146 | + const { height } = props; | ||
147 | + return isNumber(height) ? `${height}px` : height; | ||
102 | }); | 148 | }); |
103 | 149 | ||
104 | defineExpose({ | 150 | defineExpose({ |
@@ -108,22 +154,41 @@ | @@ -108,22 +154,41 @@ | ||
108 | </script> | 154 | </script> |
109 | 155 | ||
110 | <template> | 156 | <template> |
111 | - <div class="p-2 bg-gray-200" :style="{ height: `${height}px` }"> | ||
112 | - <div ref="jsonEditorElRef" class="jsoneditor"></div> | 157 | + <div |
158 | + ref="jsonEditorContainerElRef" | ||
159 | + class="p-2 bg-gray-200 flex flex-col" | ||
160 | + :style="{ height: getHeight }" | ||
161 | + > | ||
162 | + <div class="w-full h-8 flex justify-between items-center"> | ||
163 | + <div> | ||
164 | + {{ title }} | ||
165 | + </div> | ||
166 | + <slot name="header"></slot> | ||
167 | + <div class="flex h-8 gap-3 justify-end items-center text-dark-500 svg:text-2xl"> | ||
168 | + <slot name="beforeFormat"></slot> | ||
169 | + <Tooltip title="整洁"> | ||
170 | + <Icon @click="handleFormat" class="cursor-pointer" icon="gg:format-left" /> | ||
171 | + </Tooltip> | ||
172 | + <slot name="beforeCompress"></slot> | ||
173 | + | ||
174 | + <Tooltip title="迷你"> | ||
175 | + <Icon @click="handleCompress" class="cursor-pointer" icon="material-symbols:compress" /> | ||
176 | + </Tooltip> | ||
177 | + <slot name="beforeFullScreen"></slot> | ||
178 | + | ||
179 | + <Tooltip title="全屏"> | ||
180 | + <Icon | ||
181 | + v-if="isSupported" | ||
182 | + class="cursor-pointer" | ||
183 | + :icon=" | ||
184 | + isFullscreen ? 'material-symbols:fullscreen-exit' : 'material-symbols:fullscreen' | ||
185 | + " | ||
186 | + @click="handleFullScreen" | ||
187 | + /> | ||
188 | + </Tooltip> | ||
189 | + <slot name="afterFullScreen"></slot> | ||
190 | + </div> | ||
191 | + </div> | ||
192 | + <div ref="jsonEditorElRef" class="flex-auto"></div> | ||
113 | </div> | 193 | </div> |
114 | </template> | 194 | </template> |
115 | - | ||
116 | -<style lang="less" scoped> | ||
117 | - .jsoneditor { | ||
118 | - border: none !important; | ||
119 | - | ||
120 | - :deep(.jsoneditor) { | ||
121 | - border: none !important; | ||
122 | - | ||
123 | - .ace-jsoneditor, | ||
124 | - textarea.jsoneditor-text { | ||
125 | - min-height: auto; | ||
126 | - } | ||
127 | - } | ||
128 | - } | ||
129 | -</style> |
@@ -13,6 +13,7 @@ export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; | @@ -13,6 +13,7 @@ export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; | ||
13 | export { default as ApiUpload } from './src/components/ApiUpload.vue'; | 13 | export { default as ApiUpload } from './src/components/ApiUpload.vue'; |
14 | 14 | ||
15 | export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue'; | 15 | export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue'; |
16 | +export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue'; | ||
16 | 17 | ||
17 | //注册自定义组件 | 18 | //注册自定义组件 |
18 | export { | 19 | export { |
@@ -6,7 +6,7 @@ | @@ -6,7 +6,7 @@ | ||
6 | <script lang="ts" setup> | 6 | <script lang="ts" setup> |
7 | import { ref, watchEffect, computed, unref, watch, reactive } from 'vue'; | 7 | import { ref, watchEffect, computed, unref, watch, reactive } from 'vue'; |
8 | import { Select, Spin } from 'ant-design-vue'; | 8 | import { Select, Spin } from 'ant-design-vue'; |
9 | - import { isFunction } from '/@/utils/is'; | 9 | + import { isFunction, isNullAndUnDef } from '/@/utils/is'; |
10 | import { useRuleFormItem } from '/@/hooks/component/useFormItem'; | 10 | import { useRuleFormItem } from '/@/hooks/component/useFormItem'; |
11 | import { useAttrs } from '/@/hooks/core/useAttrs'; | 11 | import { useAttrs } from '/@/hooks/core/useAttrs'; |
12 | import { get, omit } from 'lodash-es'; | 12 | import { get, omit } from 'lodash-es'; |
@@ -30,8 +30,11 @@ | @@ -30,8 +30,11 @@ | ||
30 | labelField?: string; | 30 | labelField?: string; |
31 | valueField?: string; | 31 | valueField?: string; |
32 | immediate?: boolean; | 32 | immediate?: boolean; |
33 | - pagenation?: Pagination; | 33 | + searchField?: string; |
34 | + pagination?: Pagination; | ||
34 | queryEmptyDataAgin?: boolean; | 35 | queryEmptyDataAgin?: boolean; |
36 | + fetchSearch?: boolean; | ||
37 | + filterOption?: (inputValue: string, options: Recordable) => boolean; | ||
35 | }>(), | 38 | }>(), |
36 | { | 39 | { |
37 | resultField: '', | 40 | resultField: '', |
@@ -39,14 +42,15 @@ | @@ -39,14 +42,15 @@ | ||
39 | valueField: 'value', | 42 | valueField: 'value', |
40 | immediate: true, | 43 | immediate: true, |
41 | queryEmptyDataAgin: true, | 44 | queryEmptyDataAgin: true, |
42 | - pagenation: () => ({ page: 1, pageSize: 10 }), | 45 | + pagination: () => ({ page: 1, pageSize: 10 }), |
46 | + fetchSearch: false, | ||
43 | } | 47 | } |
44 | ); | 48 | ); |
45 | 49 | ||
46 | const OptionsItem = (_, { attrs }: { attrs: { vNode: any } }) => attrs.vNode; | 50 | const OptionsItem = (_, { attrs }: { attrs: { vNode: any } }) => attrs.vNode; |
47 | 51 | ||
48 | const options = ref<OptionsItem[]>([]); | 52 | const options = ref<OptionsItem[]>([]); |
49 | - const pagination = reactive(Object.assign({ total: 0, page: 1, pageSize: 10 }, props.pagenation)); | 53 | + const pagination = reactive<Record<'total' | 'page' | 'pageSize', number>>({} as any); |
50 | const scrollLoading = ref(false); | 54 | const scrollLoading = ref(false); |
51 | const lock = ref(false); | 55 | const lock = ref(false); |
52 | const loading = ref(false); | 56 | const loading = ref(false); |
@@ -55,6 +59,13 @@ | @@ -55,6 +59,13 @@ | ||
55 | const attrs = useAttrs(); | 59 | const attrs = useAttrs(); |
56 | const { t } = useI18n(); | 60 | const { t } = useI18n(); |
57 | 61 | ||
62 | + const getPagination = computed(() => { | ||
63 | + return { | ||
64 | + ...props.pagination, | ||
65 | + ...unref(pagination), | ||
66 | + }; | ||
67 | + }); | ||
68 | + | ||
58 | // Embedded in the form, just use the hook binding to perform form verification | 69 | // Embedded in the form, just use the hook binding to perform form verification |
59 | const [state] = useRuleFormItem(props, 'value', 'change', emitData); | 70 | const [state] = useRuleFormItem(props, 'value', 'change', emitData); |
60 | 71 | ||
@@ -86,16 +97,18 @@ | @@ -86,16 +97,18 @@ | ||
86 | { deep: true } | 97 | { deep: true } |
87 | ); | 98 | ); |
88 | 99 | ||
89 | - async function fetch() { | ||
90 | - const api = props.api; | 100 | + async function fetch(searchText?: string) { |
101 | + const { api, searchField, fetchSearch } = props; | ||
91 | if (!api || !isFunction(api)) return; | 102 | if (!api || !isFunction(api)) return; |
103 | + const isFetchSearchFlag = fetchSearch && !isNullAndUnDef(searchText) && searchField; | ||
92 | try { | 104 | try { |
93 | !unref(getOptions).length ? (loading.value = true) : (scrollLoading.value = true); | 105 | !unref(getOptions).length ? (loading.value = true) : (scrollLoading.value = true); |
94 | lock.value = true; | 106 | lock.value = true; |
95 | const { total, items } = await api({ | 107 | const { total, items } = await api({ |
96 | ...props.params, | 108 | ...props.params, |
97 | - page: pagination.page, | ||
98 | - pageSize: pagination.pageSize, | 109 | + page: unref(getPagination).page, |
110 | + pageSize: unref(getPagination).pageSize, | ||
111 | + ...(isFetchSearchFlag ? { [searchField!]: searchText } : {}), | ||
99 | }); | 112 | }); |
100 | 113 | ||
101 | pagination.total = total; | 114 | pagination.total = total; |
@@ -105,11 +118,13 @@ | @@ -105,11 +118,13 @@ | ||
105 | return; | 118 | return; |
106 | } | 119 | } |
107 | if (props.resultField) { | 120 | if (props.resultField) { |
108 | - options.value = [...options.value, ...(get(items, props.resultField) || [])]; | 121 | + options.value = isFetchSearchFlag |
122 | + ? get(items, props.resultField) || [] | ||
123 | + : [...options.value, ...(get(items, props.resultField) || [])]; | ||
109 | } | 124 | } |
110 | emitChange(); | 125 | emitChange(); |
111 | } catch (error) { | 126 | } catch (error) { |
112 | - pagination.page = Math.ceil(unref(getOptions).length / pagination.pageSize); | 127 | + pagination.page = Math.ceil(unref(getOptions).length / unref(getPagination).pageSize); |
113 | console.warn(error); | 128 | console.warn(error); |
114 | } finally { | 129 | } finally { |
115 | isFirstLoad.value = false; | 130 | isFirstLoad.value = false; |
@@ -134,17 +149,39 @@ | @@ -134,17 +149,39 @@ | ||
134 | emitData.value = args; | 149 | emitData.value = args; |
135 | } | 150 | } |
136 | 151 | ||
152 | + const sleep = async (number: number) => { | ||
153 | + return new Promise((resolve) => { | ||
154 | + setTimeout(() => { | ||
155 | + resolve(number); | ||
156 | + }, number); | ||
157 | + }); | ||
158 | + }; | ||
159 | + | ||
137 | async function handlePopupScroll(event: MouseEvent) { | 160 | async function handlePopupScroll(event: MouseEvent) { |
138 | const { scrollHeight, scrollTop, clientHeight } = event.target as HTMLDivElement; | 161 | const { scrollHeight, scrollTop, clientHeight } = event.target as HTMLDivElement; |
139 | if (scrollTop + clientHeight >= scrollHeight) { | 162 | if (scrollTop + clientHeight >= scrollHeight) { |
140 | - if (unref(getOptions).length < pagination.total && !unref(lock)) { | ||
141 | - pagination.page = pagination.page + 1; | 163 | + if (unref(getOptions).length < unref(getPagination).total && !unref(lock)) { |
164 | + pagination.page = unref(getPagination).page + 1; | ||
165 | + scrollLoading.value = true; | ||
166 | + await sleep(500); | ||
142 | await fetch(); | 167 | await fetch(); |
143 | } | 168 | } |
144 | } | 169 | } |
145 | } | 170 | } |
146 | 171 | ||
147 | const debounceHandlePopupScroll = useDebounceFn(handlePopupScroll, 100); | 172 | const debounceHandlePopupScroll = useDebounceFn(handlePopupScroll, 100); |
173 | + | ||
174 | + const handleFilterOption = async (inputValue: string, option: Recordable) => { | ||
175 | + const { filterOption, fetchSearch } = props; | ||
176 | + if (filterOption && isFunction(filterOption)) { | ||
177 | + filterOption?.(inputValue, option); | ||
178 | + return; | ||
179 | + } | ||
180 | + | ||
181 | + if (fetchSearch) { | ||
182 | + await fetch(inputValue); | ||
183 | + } | ||
184 | + }; | ||
148 | </script> | 185 | </script> |
149 | 186 | ||
150 | <template> | 187 | <template> |
@@ -153,6 +190,8 @@ | @@ -153,6 +190,8 @@ | ||
153 | v-bind="attrs" | 190 | v-bind="attrs" |
154 | @change="handleChange" | 191 | @change="handleChange" |
155 | :options="getOptions" | 192 | :options="getOptions" |
193 | + :filterOption="handleFilterOption" | ||
194 | + :showSearch="true" | ||
156 | v-model:value="state" | 195 | v-model:value="state" |
157 | @popup-scroll="debounceHandlePopupScroll" | 196 | @popup-scroll="debounceHandlePopupScroll" |
158 | > | 197 | > |
1 | +<script lang="ts" setup> | ||
2 | + import { Icon } from '/@/components/Icon'; | ||
3 | + import { Tooltip } from 'ant-design-vue'; | ||
4 | + import { computed, onMounted, onUnmounted, ref, shallowRef, unref, watch } from 'vue'; | ||
5 | + import AceEditor, { Ace } from 'ace-builds'; | ||
6 | + import workerJavascriptUrl from 'ace-builds/src-noconflict/worker-javascript?url'; | ||
7 | + import githubTheme from 'ace-builds/src-noconflict/theme-github?url'; | ||
8 | + import 'ace-builds/src-noconflict/mode-javascript'; | ||
9 | + import 'ace-builds/src-noconflict/ext-searchbox'; | ||
10 | + import 'ace-builds/src-noconflict/ext-language_tools'; | ||
11 | + import 'ace-builds/src-noconflict/snippets/javascript'; | ||
12 | + import { useBeautify } from '/@/hooks/business/useBeautify'; | ||
13 | + import { useFullscreen } from '@vueuse/core'; | ||
14 | + import { isNumber } from '/@/utils/is'; | ||
15 | + | ||
16 | + const emit = defineEmits(['update:value', 'focus', 'blur']); | ||
17 | + const props = withDefaults( | ||
18 | + defineProps<{ | ||
19 | + functionName?: string; | ||
20 | + paramsName?: string[]; | ||
21 | + height?: number | string; | ||
22 | + value?: string; | ||
23 | + disabled?: boolean; | ||
24 | + validateStatus?: boolean; | ||
25 | + }>(), | ||
26 | + { | ||
27 | + functionName: 'method', | ||
28 | + paramsName: () => [], | ||
29 | + height: 200, | ||
30 | + value: '', | ||
31 | + } | ||
32 | + ); | ||
33 | + | ||
34 | + const getHeight = computed(() => { | ||
35 | + const { height } = props; | ||
36 | + return isNumber(height) ? `${height}px` : height; | ||
37 | + }); | ||
38 | + | ||
39 | + const javaScriptEditorElRef = ref<Nullable<HTMLDivElement>>(); | ||
40 | + const editorInstance = shallowRef<Ace.Editor>(); | ||
41 | + const isFocus = ref(false); | ||
42 | + | ||
43 | + const handleFocus = () => { | ||
44 | + isFocus.value = true; | ||
45 | + emit('focus', unref(editorInstance)); | ||
46 | + }; | ||
47 | + | ||
48 | + const handleBlur = () => { | ||
49 | + isFocus.value = false; | ||
50 | + emit('update:value', get()); | ||
51 | + emit('blur', unref(editorInstance)); | ||
52 | + }; | ||
53 | + | ||
54 | + const handleChange = () => { | ||
55 | + emit('update:value', get()); | ||
56 | + }; | ||
57 | + | ||
58 | + const initEditor = () => { | ||
59 | + AceEditor.config.setModuleUrl('ace/mode/javascript_worker', workerJavascriptUrl); | ||
60 | + AceEditor.config.setModuleUrl('ace/theme/github', githubTheme); | ||
61 | + const editor = AceEditor.edit(unref(javaScriptEditorElRef)!, { | ||
62 | + mode: 'ace/mode/javascript', | ||
63 | + }); | ||
64 | + editor.setTheme('ace/theme/github'); | ||
65 | + editor.setOptions({ | ||
66 | + fontSize: 14, | ||
67 | + enableBasicAutocompletion: true, | ||
68 | + enableSnippets: true, | ||
69 | + enableLiveAutocompletion: true, | ||
70 | + }); | ||
71 | + | ||
72 | + editor.on('focus', handleFocus); | ||
73 | + editor.on('blur', handleBlur); | ||
74 | + editor.on('change', handleChange); | ||
75 | + editorInstance.value = editor; | ||
76 | + editor.setValue(props.value); | ||
77 | + editor?.clearSelection(); | ||
78 | + unref(editorInstance)?.setReadOnly(props.disabled); | ||
79 | + }; | ||
80 | + | ||
81 | + const get = () => { | ||
82 | + return unref(editorInstance)?.getValue(); | ||
83 | + }; | ||
84 | + | ||
85 | + const set = (val: string, cursorPos?: number) => { | ||
86 | + return unref(editorInstance)?.setValue(val, cursorPos); | ||
87 | + }; | ||
88 | + | ||
89 | + onMounted(() => { | ||
90 | + initEditor(); | ||
91 | + }); | ||
92 | + | ||
93 | + onUnmounted(() => { | ||
94 | + unref(editorInstance)?.off('change', handleChange); | ||
95 | + unref(editorInstance)?.off('focus', handleFocus); | ||
96 | + unref(editorInstance)?.off('blur', handleBlur); | ||
97 | + unref(editorInstance)?.destroy(); | ||
98 | + unref(editorInstance)?.container.remove(); | ||
99 | + }); | ||
100 | + | ||
101 | + const { beautifyJs } = useBeautify(); | ||
102 | + const handleFormatCode = async () => { | ||
103 | + const res = await beautifyJs(get() || '', { indent_size: 4, wrap_line_length: 60 }); | ||
104 | + set(res || '', -1); | ||
105 | + }; | ||
106 | + | ||
107 | + const jsFunctionContainerElRef = ref<Nullable<HTMLDivElement>>(); | ||
108 | + const { isFullscreen, isSupported, toggle } = useFullscreen(jsFunctionContainerElRef); | ||
109 | + const handleFullScreen = () => { | ||
110 | + toggle(); | ||
111 | + }; | ||
112 | + | ||
113 | + watch( | ||
114 | + () => props.value, | ||
115 | + (value) => { | ||
116 | + // const position = unref(editorInstance)?.getCursorPosition(); | ||
117 | + if (unref(isFocus)) return; | ||
118 | + set(value); | ||
119 | + unref(editorInstance)?.clearSelection(); | ||
120 | + // position && unref(editorInstance)?.moveCursorToPosition(position!); | ||
121 | + }, | ||
122 | + { | ||
123 | + immediate: true, | ||
124 | + } | ||
125 | + ); | ||
126 | + | ||
127 | + watch( | ||
128 | + () => props.disabled, | ||
129 | + (value) => { | ||
130 | + unref(editorInstance)?.setReadOnly(value); | ||
131 | + } | ||
132 | + ); | ||
133 | + | ||
134 | + defineExpose({ | ||
135 | + get, | ||
136 | + set, | ||
137 | + }); | ||
138 | +</script> | ||
139 | + | ||
140 | +<template> | ||
141 | + <section | ||
142 | + ref="jsFunctionContainerElRef" | ||
143 | + class="p-2 bg-gray-200 flex flex-col" | ||
144 | + :style="{ height: getHeight }" | ||
145 | + > | ||
146 | + <head class="flex justify-between h-8 items-center"> | ||
147 | + <div class="font-bold"> | ||
148 | + <span>function</span> | ||
149 | + <span class="ml-1">{{ functionName }}</span> | ||
150 | + <span>({{ paramsName.join(',') }})</span> | ||
151 | + <span class="ml-1">{</span> | ||
152 | + </div> | ||
153 | + <div class="flex gap-3 items-center svg:text-2xl"> | ||
154 | + <slot name="beforeFormat"></slot> | ||
155 | + | ||
156 | + <Tooltip title="整洁"> | ||
157 | + <Icon class="cursor-pointer" icon="gg:format-left" @click="handleFormatCode" /> | ||
158 | + </Tooltip> | ||
159 | + <slot name="beforeFullScreen"></slot> | ||
160 | + <Tooltip title="全屏"> | ||
161 | + <Icon | ||
162 | + v-if="isSupported" | ||
163 | + class="cursor-pointer" | ||
164 | + :icon=" | ||
165 | + isFullscreen ? 'material-symbols:fullscreen-exit' : 'material-symbols:fullscreen' | ||
166 | + " | ||
167 | + @click="handleFullScreen" | ||
168 | + /> | ||
169 | + </Tooltip> | ||
170 | + <slot name="afterFullScreen"></slot> | ||
171 | + </div> | ||
172 | + </head> | ||
173 | + <main ref="javaScriptEditorElRef" class="flex-auto"> </main> | ||
174 | + <footer class="font-bold">}</footer> | ||
175 | + </section> | ||
176 | +</template> |
@@ -130,4 +130,6 @@ export type ComponentType = | @@ -130,4 +130,6 @@ export type ComponentType = | ||
130 | | 'ControlGroup' | 130 | | 'ControlGroup' |
131 | | 'JSONEditor' | 131 | | 'JSONEditor' |
132 | | 'OrgTreeSelect' | 132 | | 'OrgTreeSelect' |
133 | - | 'ExtendDesc'; | 133 | + | 'ExtendDesc' |
134 | + | 'JavaScriptFunctionEditor' | ||
135 | + | 'JavaScriptFilterFunctionModal'; |
@@ -19,4 +19,8 @@ export enum DictEnum { | @@ -19,4 +19,8 @@ export enum DictEnum { | ||
19 | DISABLE_CUSTOMER_AUTH = 'disabled_customer_auth', | 19 | DISABLE_CUSTOMER_AUTH = 'disabled_customer_auth', |
20 | // 寄存器数据格式 | 20 | // 寄存器数据格式 |
21 | REGISTER_DATA_FORMAT = 'register_data_format', | 21 | REGISTER_DATA_FORMAT = 'register_data_format', |
22 | + // 消息类型 规则节点 Filter message type switch | ||
23 | + MESSAGE_TYPES_FILTER = 'message_types_filter', | ||
24 | + // 实体类型 规则节点 Filter originator types switch | ||
25 | + ORIGINATOR_TYPES = 'originator_types', | ||
22 | } | 26 | } |
src/hooks/business/useBatchSettingDict.ts
0 → 100644
1 | +import { saveOrEditDictItem } from '/@/api/system/dict'; | ||
2 | +import { SysDictItem } from '/@/api/system/model/dictModel'; | ||
3 | + | ||
4 | +interface BatchSettingParamsType { | ||
5 | + dictId: string; | ||
6 | + list: any[]; | ||
7 | + transformText: (item: any) => any; | ||
8 | + transformValue: (item: any) => any; | ||
9 | + update?: boolean; | ||
10 | +} | ||
11 | + | ||
12 | +export function useBatchSettingDict() { | ||
13 | + const batchSetting = async (options: BatchSettingParamsType) => { | ||
14 | + const { dictId, list, transformText, transformValue, update = false } = options; | ||
15 | + if (!transformText || !transformValue) return; | ||
16 | + let index = 1; | ||
17 | + for (const item of list) { | ||
18 | + const params: Partial<SysDictItem> & { dictId: string } = { | ||
19 | + dictId, | ||
20 | + sort: index++, | ||
21 | + status: 1, | ||
22 | + itemText: transformText(item), | ||
23 | + itemValue: transformValue(item), | ||
24 | + }; | ||
25 | + | ||
26 | + await saveOrEditDictItem(params as SysDictItem, update); | ||
27 | + } | ||
28 | + }; | ||
29 | + | ||
30 | + const batchDelete = () => {}; | ||
31 | + | ||
32 | + return { | ||
33 | + batchSetting, | ||
34 | + batchDelete, | ||
35 | + }; | ||
36 | +} |
src/hooks/business/useBeautify.ts
0 → 100644
1 | +export function useBeautify() { | ||
2 | + let jsBeautifyModule: any; | ||
3 | + | ||
4 | + async function loadJsBeautify() { | ||
5 | + if (jsBeautifyModule) return jsBeautifyModule; | ||
6 | + jsBeautifyModule = await import('js-beautify/js/lib/beautifier'); | ||
7 | + return jsBeautifyModule; | ||
8 | + } | ||
9 | + | ||
10 | + async function beautifyJs(source: string, options?: any) { | ||
11 | + const module = await loadJsBeautify(); | ||
12 | + return module.js(source, options); | ||
13 | + } | ||
14 | + return { beautifyJs }; | ||
15 | +} |
src/hooks/business/useJsonParse.ts
0 → 100644
1 | +import { isObject, isString } from '/@/utils/is'; | ||
2 | + | ||
3 | +export function useJsonParse(value: any, defaultValue = {}) { | ||
4 | + let flag = false; | ||
5 | + try { | ||
6 | + value = JSON.parse(value); | ||
7 | + if (isObject(value) || (isString(value) && value.trim() === '')) flag = true; | ||
8 | + } catch (error) { | ||
9 | + value = defaultValue; | ||
10 | + } | ||
11 | + return { flag, value }; | ||
12 | +} |
@@ -91,7 +91,7 @@ export const formSchema: FormSchema[] = [ | @@ -91,7 +91,7 @@ export const formSchema: FormSchema[] = [ | ||
91 | valueField: 'fileList', | 91 | valueField: 'fileList', |
92 | componentProps: () => { | 92 | componentProps: () => { |
93 | return { | 93 | return { |
94 | - listType: 'picture-card', | 94 | + // listType: 'picture-card', |
95 | maxFileLimit: 1, | 95 | maxFileLimit: 1, |
96 | api: async (file: File) => { | 96 | api: async (file: File) => { |
97 | try { | 97 | try { |
@@ -110,6 +110,10 @@ export const formSchema: FormSchema[] = [ | @@ -110,6 +110,10 @@ export const formSchema: FormSchema[] = [ | ||
110 | onPreview: (fileList: FileItem) => { | 110 | onPreview: (fileList: FileItem) => { |
111 | createImgPreview({ imageList: [fileList.url!] }); | 111 | createImgPreview({ imageList: [fileList.url!] }); |
112 | }, | 112 | }, |
113 | + showUploadList: { | ||
114 | + showDownloadIcon: true, | ||
115 | + showRemoveIcon: true, | ||
116 | + }, | ||
113 | }; | 117 | }; |
114 | }, | 118 | }, |
115 | }, | 119 | }, |
@@ -46,7 +46,7 @@ export const formSchema: FormSchema[] = [ | @@ -46,7 +46,7 @@ export const formSchema: FormSchema[] = [ | ||
46 | valueField: 'fileList', | 46 | valueField: 'fileList', |
47 | componentProps: () => { | 47 | componentProps: () => { |
48 | return { | 48 | return { |
49 | - listType: 'picture-card', | 49 | + // listType: 'picture-card', |
50 | maxFileLimit: 1, | 50 | maxFileLimit: 1, |
51 | api: async (file: File) => { | 51 | api: async (file: File) => { |
52 | try { | 52 | try { |
@@ -62,6 +62,10 @@ export const formSchema: FormSchema[] = [ | @@ -62,6 +62,10 @@ export const formSchema: FormSchema[] = [ | ||
62 | return {}; | 62 | return {}; |
63 | } | 63 | } |
64 | }, | 64 | }, |
65 | + showUploadList: true, | ||
66 | + onDownload(file) { | ||
67 | + console.log(file); | ||
68 | + }, | ||
65 | onPreview: (fileList: FileItem) => { | 69 | onPreview: (fileList: FileItem) => { |
66 | createImgPreview({ imageList: [fileList.url!] }); | 70 | createImgPreview({ imageList: [fileList.url!] }); |
67 | }, | 71 | }, |
@@ -23,7 +23,7 @@ | @@ -23,7 +23,7 @@ | ||
23 | layout: 'vertical', | 23 | layout: 'vertical', |
24 | }); | 24 | }); |
25 | 25 | ||
26 | - const { genForm } = useGenDynamicForm(); | 26 | + const { genForm, transformValue } = useGenDynamicForm(); |
27 | 27 | ||
28 | const keys = ref<string[]>([]); | 28 | const keys = ref<string[]>([]); |
29 | 29 | ||
@@ -127,8 +127,8 @@ | @@ -127,8 +127,8 @@ | ||
127 | 127 | ||
128 | sendValue.value = await genModbusCommand(unref(modBUSForm)); | 128 | sendValue.value = await genModbusCommand(unref(modBUSForm)); |
129 | } else { | 129 | } else { |
130 | - const _value = getFieldsValue(); | ||
131 | - | 130 | + await validate(); |
131 | + const _value = transformValue(getFieldsValue()); | ||
132 | sendValue.value = unref(keys).reduce((prev, next) => { | 132 | sendValue.value = unref(keys).reduce((prev, next) => { |
133 | return { ...prev, [next]: _value[next] }; | 133 | return { ...prev, [next]: _value[next] }; |
134 | }, {}); | 134 | }, {}); |
1 | import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | 1 | import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
2 | import { JSONEditor } from '/@/components/CodeEditor'; | 2 | import { JSONEditor } from '/@/components/CodeEditor'; |
3 | import { FormSchema, useComponentRegister } from '/@/components/Form'; | 3 | import { FormSchema, useComponentRegister } from '/@/components/Form'; |
4 | +import { useJsonParse } from '/@/hooks/business/useJsonParse'; | ||
4 | import { DataTypeEnum } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | 5 | import { DataTypeEnum } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; |
5 | 6 | ||
6 | export interface BasicCreateFormParams { | 7 | export interface BasicCreateFormParams { |
@@ -20,7 +21,6 @@ export const useGenDynamicForm = () => { | @@ -20,7 +21,6 @@ export const useGenDynamicForm = () => { | ||
20 | const { specs, type } = dataType; | 21 | const { specs, type } = dataType; |
21 | const { valueRange, step } = specs! as Partial<Specs>; | 22 | const { valueRange, step } = specs! as Partial<Specs>; |
22 | const { max, min } = valueRange || {}; | 23 | const { max, min } = valueRange || {}; |
23 | - console.log({ max, min }); | ||
24 | return { | 24 | return { |
25 | field: identifier, | 25 | field: identifier, |
26 | label: functionName, | 26 | label: functionName, |
@@ -107,6 +107,19 @@ export const useGenDynamicForm = () => { | @@ -107,6 +107,19 @@ export const useGenDynamicForm = () => { | ||
107 | field: identifier, | 107 | field: identifier, |
108 | label: functionName, | 108 | label: functionName, |
109 | component: 'JSONEditor', | 109 | component: 'JSONEditor', |
110 | + valueField: 'value', | ||
111 | + changeEvent: 'update:value', | ||
112 | + rules: [ | ||
113 | + { | ||
114 | + validator: (_rule, value: any) => { | ||
115 | + if (value) { | ||
116 | + const { flag } = useJsonParse(value); | ||
117 | + if (!flag) return Promise.reject(`${functionName} 不是一个有效的JSON对象`); | ||
118 | + } | ||
119 | + return Promise.resolve(); | ||
120 | + }, | ||
121 | + }, | ||
122 | + ], | ||
110 | }; | 123 | }; |
111 | }; | 124 | }; |
112 | 125 | ||
@@ -118,11 +131,14 @@ export const useGenDynamicForm = () => { | @@ -118,11 +131,14 @@ export const useGenDynamicForm = () => { | ||
118 | [DataTypeEnum.IS_STRUCT]: createInputJson, | 131 | [DataTypeEnum.IS_STRUCT]: createInputJson, |
119 | }; | 132 | }; |
120 | 133 | ||
134 | + const fieldTypeMap = new Map<string, DataTypeEnum>(); | ||
121 | const genForm = (schemas: StructJSON[]) => { | 135 | const genForm = (schemas: StructJSON[]) => { |
136 | + fieldTypeMap.clear(); | ||
122 | const formSchema = schemas.map((item) => { | 137 | const formSchema = schemas.map((item) => { |
123 | const { functionName, identifier, dataType } = item; | 138 | const { functionName, identifier, dataType } = item; |
124 | const { type } = dataType || {}; | 139 | const { type } = dataType || {}; |
125 | 140 | ||
141 | + fieldTypeMap.set(identifier!, dataType!.type); | ||
126 | const method = schemaMethod[type!]; | 142 | const method = schemaMethod[type!]; |
127 | 143 | ||
128 | const formSchema = method?.({ | 144 | const formSchema = method?.({ |
@@ -137,5 +153,21 @@ export const useGenDynamicForm = () => { | @@ -137,5 +153,21 @@ export const useGenDynamicForm = () => { | ||
137 | return formSchema.filter(Boolean); | 153 | return formSchema.filter(Boolean); |
138 | }; | 154 | }; |
139 | 155 | ||
140 | - return { genForm }; | 156 | + const transformValue = (value: Recordable) => { |
157 | + return Object.keys(value || {}).reduce((prev, next) => { | ||
158 | + const dataType = fieldTypeMap.get(next)!; | ||
159 | + | ||
160 | + let itemValue = value[next]; | ||
161 | + if (dataType === DataTypeEnum.IS_STRUCT) { | ||
162 | + const { value } = useJsonParse(itemValue); | ||
163 | + itemValue = value; | ||
164 | + } | ||
165 | + return { | ||
166 | + ...prev, | ||
167 | + [next]: itemValue, | ||
168 | + }; | ||
169 | + }, {} as Recordable); | ||
170 | + }; | ||
171 | + | ||
172 | + return { genForm, transformValue }; | ||
141 | }; | 173 | }; |
src/views/rule/designer/enum/form.ts
0 → 100644
1 | +/** | ||
2 | + * @description 方向 | ||
3 | + */ | ||
4 | +export enum DirectionEnum { | ||
5 | + FROM = 'FROM', | ||
6 | + TO = 'TO', | ||
7 | +} | ||
8 | + | ||
9 | +export enum DirectionNameEnum { | ||
10 | + FROM = '从', | ||
11 | + TO = '到', | ||
12 | +} | ||
13 | +/** | ||
14 | + * @description 关联类型 | ||
15 | + */ | ||
16 | +export enum RelationTypeEnum { | ||
17 | + CONTAINS = 'contains', | ||
18 | + MANAGES = 'manages', | ||
19 | +} | ||
20 | + | ||
21 | +/** | ||
22 | + * @description 类型 | ||
23 | + */ | ||
24 | +export enum EntityTypeEnum { | ||
25 | + DEVICE = 'DEVICE', | ||
26 | + ASSET = 'ASSET', | ||
27 | + ENTITY_VIEW = 'ENTITY_VIEW', | ||
28 | + TENANT = 'TENANT', | ||
29 | + CUSTOMER = 'CUSTOMER', | ||
30 | + USER = 'USER', | ||
31 | + DASHBOARD = 'DASHBOARD', | ||
32 | + EDGE = 'EDGE', | ||
33 | +} | ||
34 | + | ||
35 | +export enum EntityTypeNameEnum { | ||
36 | + DEVICE = '设备', | ||
37 | + ASSET = '资产', | ||
38 | + ENTITY_VIEW = '实体视图', | ||
39 | + TENANT = '租户', | ||
40 | + CUSTOMER = '客户', | ||
41 | + USER = '用户', | ||
42 | + DASHBOARD = '仪表板', | ||
43 | + EDGE = 'Edge', | ||
44 | +} | ||
45 | + | ||
46 | +/** | ||
47 | + * @description Filter Perimeter type | ||
48 | + */ | ||
49 | +export enum PerimeterTypeEnum { | ||
50 | + POLYGON = 'POLYGON', | ||
51 | + CIRCLE = 'CIRCLE', | ||
52 | +} | ||
53 | + | ||
54 | +/** | ||
55 | + * @description Filter Range util | ||
56 | + */ | ||
57 | +export enum RangeUtilEnum { | ||
58 | + METER = 'METER', | ||
59 | + KILOMETER = 'KILOMETER', | ||
60 | + FOOT = 'FOOT', | ||
61 | + MILE = 'MILE', | ||
62 | + NAUTICAL_MILE = 'NAUTICAL_MILE', | ||
63 | +} | ||
64 | + | ||
65 | +export enum RangeUtilNameEnum { | ||
66 | + METER = 'Meter', | ||
67 | + KILOMETER = 'Kilometer', | ||
68 | + FOOT = 'Foot', | ||
69 | + MILE = 'Mile', | ||
70 | + NAUTICAL_MILE = 'Nautical mile', | ||
71 | +} | ||
72 | + | ||
73 | +export enum MessageTypesFilterEnum { | ||
74 | + POST_ATTRIBUTES = 'POST_ATTRIBUTES', | ||
75 | + POST_TELEMETRY = 'POST_TELEMETRY', | ||
76 | + RPC_REQUEST_FROM_DEVICE = 'RPC_REQUEST_FROM_DEVICE', | ||
77 | + RPC_REQUEST_TO_DEVICE = 'RPC_REQUEST_TO_DEVICE', | ||
78 | + ACTIVITY_EVENT = 'ACTIVITY_EVENT', | ||
79 | + INACTIVITY_EVENT = 'INACTIVITY_EVENT', | ||
80 | + CONNECT_EVENT = 'CONNECT_EVENT', | ||
81 | + DISCONNECT_EVENT = 'DISCONNECT_EVENT', | ||
82 | + ENTITY_CREATED = 'ENTITY_CREATED', | ||
83 | + ENTITY_UPDATED = 'ENTITY_UPDATED', | ||
84 | + ENTITY_DELETED = 'ENTITY_DELETED', | ||
85 | + ENTITY_ASSIGNED = 'ENTITY_ASSIGNED', | ||
86 | + ENTITY_UNASSIGNED = 'ENTITY_UNASSIGNED', | ||
87 | + ATTRIBUTES_UPDATED = 'ATTRIBUTES_UPDATED', | ||
88 | + ATTRIBUTES_DELETED = 'ATTRIBUTES_DELETED', | ||
89 | + TIMESERIES_UPDATED = 'TIMESERIES_UPDATED', | ||
90 | + TIMESERIES_DELETED = 'TIMESERIES_DELETED', | ||
91 | + RPC_QUEUED = 'RPC_QUEUED', | ||
92 | + RPC_DELIVERED = 'RPC_DELIVERED', | ||
93 | + RPC_SUCCESSFUL = 'RPC_SUCCESSFUL', | ||
94 | + RPC_TIMEOUT = 'RPC_TIMEOUT', | ||
95 | + RPC_FAILED = 'RPC_FAILED', | ||
96 | +} | ||
97 | + | ||
98 | +export enum MessageTypesFilterNameEnum { | ||
99 | + POST_ATTRIBUTES = 'Post attributes', | ||
100 | + POST_TELEMETRY = 'Post telemetry', | ||
101 | + RPC_REQUEST_FROM_DEVICE = 'RPC Request from Device', | ||
102 | + RPC_REQUEST_TO_DEVICE = 'RPC Request to Device', | ||
103 | + ACTIVITY_EVENT = 'Activity Event', | ||
104 | + INACTIVITY_EVENT = 'Inactivity Event', | ||
105 | + CONNECT_EVENT = 'Connect Event', | ||
106 | + DISCONNECT_EVENT = 'Disconnect Event', | ||
107 | + ENTITY_CREATED = 'Entity Created', | ||
108 | + ENTITY_UPDATED = 'Entity Updated', | ||
109 | + ENTITY_DELETED = 'Entity Deleted', | ||
110 | + ENTITY_ASSIGNED = 'Entity Assigned', | ||
111 | + ENTITY_UNASSIGNED = 'Entity Unassigned', | ||
112 | + ATTRIBUTES_UPDATED = 'Attributes Updated', | ||
113 | + ATTRIBUTES_DELETED = 'Attributes Deleted', | ||
114 | + TIMESERIES_UPDATED = 'Timeseries Updated', | ||
115 | + TIMESERIES_DELETED = 'Timeseries Deleted', | ||
116 | + RPC_QUEUED = 'RPC Queued', | ||
117 | + RPC_DELIVERED = 'RPC Delivered', | ||
118 | + RPC_SUCCESSFUL = 'RPC Successful', | ||
119 | + RPC_TIMEOUT = 'RPC Timeout', | ||
120 | + RPC_FAILED = 'RPC Failed', | ||
121 | +} |
@@ -5,16 +5,85 @@ export enum FetchNodeComFlagTypeENum { | @@ -5,16 +5,85 @@ export enum FetchNodeComFlagTypeENum { | ||
5 | 5 | ||
6 | export enum NodeBindDataFieldEnum { | 6 | export enum NodeBindDataFieldEnum { |
7 | NAME = 'name', | 7 | NAME = 'name', |
8 | - ALARM_STATUS_LIST = 'alarmStatusList', | ||
9 | DESCRIPTION = 'description', | 8 | DESCRIPTION = 'description', |
10 | DEBUG_MODE = 'debugMode', | 9 | DEBUG_MODE = 'debugMode', |
10 | + | ||
11 | + // Filter Check Alarm status | ||
12 | + ALARM_STATUS_LIST = 'alarmStatusList', | ||
13 | + | ||
14 | + // Filter Check Existence Fields | ||
15 | + MESSAGE_NAMES = 'messageNames', | ||
16 | + METADATA_NAMES = 'metadataNames', | ||
17 | + CHECK_ALL_KEYS = 'checkAllKeys', | ||
18 | + | ||
19 | + // Filter Check Relation | ||
20 | + DIRECTION = 'direction', | ||
21 | + CHECK_FOR_SINGLE_ENTITY = 'checkForSingleEntity', | ||
22 | + ENTITY_TYPE = 'entityType', | ||
23 | + RELEATION_TYPE = 'relationType', | ||
24 | + ENTITY_ID = 'entityId', | ||
25 | + | ||
26 | + // Filter Gps geofencing filter | ||
27 | + LATITUDE_KEY_NAME = 'latitudeKeyName', | ||
28 | + LONGITUDE_KEY_NAME = 'longitudeKeyName', | ||
29 | + PERIMETER_TYPE = 'perimeterType', | ||
30 | + FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA = 'fetchPerimeterInfoFromMessageMetadata', | ||
31 | + PERIMETER_KEY_NAME = 'perimeterKeyName', | ||
32 | + CENTER_LATITUDE = 'centerLatitude', | ||
33 | + CENTER_LONGITUDE = 'centerLongitude', | ||
34 | + RANGE = 'range', | ||
35 | + RANGE_UNIT = 'rangeUnit', | ||
36 | + POLYGONS_DEFINITION = 'polygonsDefinition', | ||
37 | + | ||
38 | + // Filter Message Type | ||
39 | + MESSAGE_TYPES = 'messageTypes', | ||
40 | + | ||
41 | + // Filter Originator Type | ||
42 | + ORIGINATOR_TYPES = 'originatorTypes', | ||
43 | + | ||
44 | + // Filter Script | ||
45 | + JS_SCRIPT = 'jsScript', | ||
11 | } | 46 | } |
12 | 47 | ||
13 | export enum NodeBindDataFieldNameEnum { | 48 | export enum NodeBindDataFieldNameEnum { |
14 | NAME = '名称', | 49 | NAME = '名称', |
15 | - ALARM_STATUS_LIST = 'Alarm status filter', | ||
16 | DESCRIPTION = '说明', | 50 | DESCRIPTION = '说明', |
17 | DEBUG_MODE = '调试模式', | 51 | DEBUG_MODE = '调试模式', |
52 | + | ||
53 | + // Filter Check Alarm status | ||
54 | + ALARM_STATUS_LIST = 'Alarm status filter', | ||
55 | + | ||
56 | + // Filter Check Existence Fields | ||
57 | + MESSAGE_NAMES = '消息数据', | ||
58 | + METADATA_NAMES = '消息元数据', | ||
59 | + CHECK_ALL_KEYS = '检查所有选择的键是否都存在', | ||
60 | + | ||
61 | + // Filter Check Relation | ||
62 | + DIRECTION = '方向', | ||
63 | + CHECK_FOR_SINGLE_ENTITY = 'Check relation to specific entity', | ||
64 | + ENTITY_TYPE = '类型', | ||
65 | + RELEATION_TYPE = '关联类型', | ||
66 | + | ||
67 | + // Filter Gps geofencing filter | ||
68 | + LATITUDE_KEY_NAME = 'Latitude Key Name', | ||
69 | + LONGITUDE_KEY_NAME = 'Longitude Key Name', | ||
70 | + PERIMETER_TYPE = 'Perimeter Type', | ||
71 | + FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA = 'Fetch perimeter information from message metadata', | ||
72 | + CENTER_LATITUDE = 'Center latitude', | ||
73 | + CENTER_LONGITUDE = 'Center longitude', | ||
74 | + RANGE = 'Range', | ||
75 | + RANGE_UNIT = 'Range unit', | ||
76 | + PERIMETER_KEY_NAME = 'Perimeter key name', | ||
77 | + POLYGONS_DEFINITION = 'Polygons definition', | ||
78 | + | ||
79 | + // Filter Message Type | ||
80 | + MESSAGE_TYPES = 'Message Types Filter', | ||
81 | + | ||
82 | + // Filter Originator Type | ||
83 | + ORIGINATOR_TYPES = 'Originator types filter', | ||
84 | + | ||
85 | + // Filter Script | ||
86 | + JS_SCRIPT = 'Filter', | ||
18 | } | 87 | } |
19 | 88 | ||
20 | export enum EdgeBindDataFieldEnum { | 89 | export enum EdgeBindDataFieldEnum { |
@@ -53,7 +53,7 @@ export function useBasicDataTransform() { | @@ -53,7 +53,7 @@ export function useBasicDataTransform() { | ||
53 | 53 | ||
54 | nodes.forEach((item, index) => indexMap.set(index, item)); | 54 | nodes.forEach((item, index) => indexMap.set(index, item)); |
55 | 55 | ||
56 | - connections.forEach((item) => { | 56 | + (connections || []).forEach((item) => { |
57 | const { fromIndex, toIndex, type } = item; | 57 | const { fromIndex, toIndex, type } = item; |
58 | const key = `${fromIndex}${SEPARATOR}${toIndex}`; | 58 | const key = `${fromIndex}${SEPARATOR}${toIndex}`; |
59 | if (!groupByConnections.has(key)) groupByConnections.set(key, []); | 59 | if (!groupByConnections.has(key)) groupByConnections.set(key, []); |
@@ -124,7 +124,7 @@ export function useSaveAndRedo() { | @@ -124,7 +124,7 @@ export function useSaveAndRedo() { | ||
124 | firstNodeIndex, | 124 | firstNodeIndex, |
125 | ruleChainId: { | 125 | ruleChainId: { |
126 | entityType: 'RULE_CHAIN', | 126 | entityType: 'RULE_CHAIN', |
127 | - id: 'd6ddea70-25da-11ee-bc6b-47e715464e68', | 127 | + id: '992ddda0-3d97-11ee-8b46-0dfb0900ab17', |
128 | }, | 128 | }, |
129 | }); | 129 | }); |
130 | 130 | ||
@@ -140,7 +140,7 @@ export function useSaveAndRedo() { | @@ -140,7 +140,7 @@ export function useSaveAndRedo() { | ||
140 | try { | 140 | try { |
141 | loading.value = true; | 141 | loading.value = true; |
142 | 142 | ||
143 | - const id = 'd6ddea70-25da-11ee-bc6b-47e715464e68'; | 143 | + const id = '992ddda0-3d97-11ee-8b46-0dfb0900ab17'; |
144 | 144 | ||
145 | const data = await getRuleChainData(id); | 145 | const data = await getRuleChainData(id); |
146 | 146 |
@@ -3,8 +3,27 @@ import { FormSchema } from '/@/components/Form'; | @@ -3,8 +3,27 @@ import { FormSchema } from '/@/components/Form'; | ||
3 | 3 | ||
4 | export const formSchemas: FormSchema[] = [ | 4 | export const formSchemas: FormSchema[] = [ |
5 | { | 5 | { |
6 | - field: NodeBindDataFieldEnum.NAME, | ||
7 | - component: 'Input', | ||
8 | - label: NodeBindDataFieldNameEnum.NAME, | 6 | + field: NodeBindDataFieldEnum.MESSAGE_NAMES, |
7 | + component: 'Select', | ||
8 | + label: NodeBindDataFieldNameEnum.MESSAGE_NAMES, | ||
9 | + componentProps: { | ||
10 | + mode: 'tags', | ||
11 | + getPopupContainer: () => document.body, | ||
12 | + }, | ||
13 | + }, | ||
14 | + { | ||
15 | + field: NodeBindDataFieldEnum.METADATA_NAMES, | ||
16 | + component: 'Select', | ||
17 | + label: NodeBindDataFieldNameEnum.METADATA_NAMES, | ||
18 | + componentProps: { | ||
19 | + mode: 'tags', | ||
20 | + getPopupContainer: () => document.body, | ||
21 | + }, | ||
22 | + }, | ||
23 | + { | ||
24 | + field: NodeBindDataFieldEnum.CHECK_ALL_KEYS, | ||
25 | + component: 'Checkbox', | ||
26 | + label: NodeBindDataFieldNameEnum.CHECK_ALL_KEYS, | ||
27 | + labelWidth: 200, | ||
9 | }, | 28 | }, |
10 | ]; | 29 | ]; |
1 | +import { | ||
2 | + DirectionEnum, | ||
3 | + DirectionNameEnum, | ||
4 | + EntityTypeEnum, | ||
5 | + EntityTypeNameEnum, | ||
6 | + RelationTypeEnum, | ||
7 | +} from '../../../enum/form'; | ||
1 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; | 8 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; |
9 | +import { deviceProfilePage } from '/@/api/device/deviceManager'; | ||
2 | import { FormSchema } from '/@/components/Form'; | 10 | import { FormSchema } from '/@/components/Form'; |
3 | 11 | ||
4 | export const formSchemas: FormSchema[] = [ | 12 | export const formSchemas: FormSchema[] = [ |
@@ -7,4 +15,64 @@ export const formSchemas: FormSchema[] = [ | @@ -7,4 +15,64 @@ export const formSchemas: FormSchema[] = [ | ||
7 | component: 'Input', | 15 | component: 'Input', |
8 | label: NodeBindDataFieldNameEnum.NAME, | 16 | label: NodeBindDataFieldNameEnum.NAME, |
9 | }, | 17 | }, |
18 | + { | ||
19 | + field: NodeBindDataFieldEnum.CHECK_FOR_SINGLE_ENTITY, | ||
20 | + component: 'Checkbox', | ||
21 | + label: NodeBindDataFieldNameEnum.CHECK_FOR_SINGLE_ENTITY, | ||
22 | + labelWidth: 220, | ||
23 | + }, | ||
24 | + { | ||
25 | + field: NodeBindDataFieldEnum.DIRECTION, | ||
26 | + component: 'Select', | ||
27 | + label: NodeBindDataFieldEnum.DIRECTION, | ||
28 | + componentProps: { | ||
29 | + options: [ | ||
30 | + { label: DirectionNameEnum.FROM, value: DirectionEnum.FROM }, | ||
31 | + { label: DirectionNameEnum.TO, value: DirectionEnum.TO }, | ||
32 | + ], | ||
33 | + }, | ||
34 | + }, | ||
35 | + { | ||
36 | + field: NodeBindDataFieldEnum.ENTITY_TYPE, | ||
37 | + component: 'Select', | ||
38 | + label: NodeBindDataFieldNameEnum.ENTITY_TYPE, | ||
39 | + colProps: { span: 8 }, | ||
40 | + componentProps: { | ||
41 | + options: Object.keys(EntityTypeEnum).map((key) => ({ | ||
42 | + label: EntityTypeNameEnum[key], | ||
43 | + value: key, | ||
44 | + })), | ||
45 | + }, | ||
46 | + }, | ||
47 | + { | ||
48 | + field: NodeBindDataFieldEnum.ENTITY_ID, | ||
49 | + component: 'ApiSelectScrollLoad', | ||
50 | + label: ' ', | ||
51 | + colProps: { span: 16 }, | ||
52 | + show: ({ model }) => model[NodeBindDataFieldEnum.ENTITY_TYPE], | ||
53 | + componentProps: ({ formModel }) => { | ||
54 | + const entityType = formModel[NodeBindDataFieldEnum.ENTITY_TYPE]; | ||
55 | + | ||
56 | + return { | ||
57 | + showSearch: true, | ||
58 | + api: deviceProfilePage, | ||
59 | + resultField: 'items', | ||
60 | + labelField: 'name', | ||
61 | + valueField: 'id', | ||
62 | + placeholder: `请选择${EntityTypeNameEnum[entityType]}`, | ||
63 | + }; | ||
64 | + }, | ||
65 | + }, | ||
66 | + { | ||
67 | + field: NodeBindDataFieldEnum.RELEATION_TYPE, | ||
68 | + component: 'Select', | ||
69 | + label: NodeBindDataFieldNameEnum.RELEATION_TYPE, | ||
70 | + defaultValue: RelationTypeEnum.CONTAINS, | ||
71 | + componentProps: { | ||
72 | + options: [ | ||
73 | + { label: RelationTypeEnum.CONTAINS, value: RelationTypeEnum.CONTAINS }, | ||
74 | + { label: RelationTypeEnum.MANAGES, value: RelationTypeEnum.MANAGES }, | ||
75 | + ], | ||
76 | + }, | ||
77 | + }, | ||
10 | ]; | 78 | ]; |
1 | +import { PerimeterTypeEnum, RangeUtilEnum, RangeUtilNameEnum } from '../../../enum/form'; | ||
1 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; | 2 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; |
2 | import { FormSchema } from '/@/components/Form'; | 3 | import { FormSchema } from '/@/components/Form'; |
3 | 4 | ||
4 | export const formSchemas: FormSchema[] = [ | 5 | export const formSchemas: FormSchema[] = [ |
5 | { | 6 | { |
6 | - field: NodeBindDataFieldEnum.NAME, | 7 | + field: NodeBindDataFieldEnum.LATITUDE_KEY_NAME, |
7 | component: 'Input', | 8 | component: 'Input', |
8 | - label: NodeBindDataFieldNameEnum.NAME, | 9 | + label: NodeBindDataFieldNameEnum.LATITUDE_KEY_NAME, |
10 | + }, | ||
11 | + { | ||
12 | + field: NodeBindDataFieldEnum.LONGITUDE_KEY_NAME, | ||
13 | + component: 'Input', | ||
14 | + label: NodeBindDataFieldNameEnum.LONGITUDE_KEY_NAME, | ||
15 | + }, | ||
16 | + { | ||
17 | + field: NodeBindDataFieldEnum.PERIMETER_TYPE, | ||
18 | + component: 'Select', | ||
19 | + label: NodeBindDataFieldNameEnum.PERIMETER_TYPE, | ||
20 | + componentProps: { | ||
21 | + options: Object.keys(PerimeterTypeEnum).map((value) => ({ label: value, value })), | ||
22 | + }, | ||
23 | + }, | ||
24 | + { | ||
25 | + field: NodeBindDataFieldEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA, | ||
26 | + component: 'Checkbox', | ||
27 | + label: NodeBindDataFieldNameEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA, | ||
28 | + labelWidth: 350, | ||
29 | + }, | ||
30 | + { | ||
31 | + field: NodeBindDataFieldEnum.PERIMETER_KEY_NAME, | ||
32 | + component: 'Input', | ||
33 | + label: NodeBindDataFieldNameEnum.PERIMETER_KEY_NAME, | ||
34 | + show: ({ model }) => model[NodeBindDataFieldEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA], | ||
35 | + }, | ||
36 | + { | ||
37 | + field: NodeBindDataFieldEnum.POLYGONS_DEFINITION, | ||
38 | + component: 'Input', | ||
39 | + label: NodeBindDataFieldNameEnum.POLYGONS_DEFINITION, | ||
40 | + helpMessage: | ||
41 | + 'Please, use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].', | ||
42 | + show: ({ model }) => | ||
43 | + !model[NodeBindDataFieldEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA] && | ||
44 | + model[NodeBindDataFieldEnum.PERIMETER_TYPE] === PerimeterTypeEnum.POLYGON, | ||
45 | + }, | ||
46 | + { | ||
47 | + field: NodeBindDataFieldEnum.CENTER_LATITUDE, | ||
48 | + component: 'InputNumber', | ||
49 | + label: NodeBindDataFieldNameEnum.CENTER_LATITUDE, | ||
50 | + colProps: { span: 12 }, | ||
51 | + show: ({ model }) => | ||
52 | + model[NodeBindDataFieldEnum.PERIMETER_TYPE] === PerimeterTypeEnum.CIRCLE && | ||
53 | + !model[NodeBindDataFieldEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA], | ||
54 | + }, | ||
55 | + { | ||
56 | + field: NodeBindDataFieldEnum.CENTER_LONGITUDE, | ||
57 | + component: 'InputNumber', | ||
58 | + label: NodeBindDataFieldNameEnum.CENTER_LONGITUDE, | ||
59 | + colProps: { span: 12 }, | ||
60 | + show: ({ model }) => | ||
61 | + model[NodeBindDataFieldEnum.PERIMETER_TYPE] === PerimeterTypeEnum.CIRCLE && | ||
62 | + !model[NodeBindDataFieldEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA], | ||
63 | + }, | ||
64 | + { | ||
65 | + field: NodeBindDataFieldEnum.RANGE, | ||
66 | + component: 'InputNumber', | ||
67 | + label: NodeBindDataFieldNameEnum.RANGE, | ||
68 | + colProps: { span: 12 }, | ||
69 | + show: ({ model }) => | ||
70 | + model[NodeBindDataFieldEnum.PERIMETER_TYPE] === PerimeterTypeEnum.CIRCLE && | ||
71 | + !model[NodeBindDataFieldEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA], | ||
72 | + }, | ||
73 | + { | ||
74 | + field: NodeBindDataFieldEnum.RANGE_UNIT, | ||
75 | + component: 'Select', | ||
76 | + label: NodeBindDataFieldNameEnum.RANGE_UNIT, | ||
77 | + colProps: { span: 12 }, | ||
78 | + show: ({ model }) => | ||
79 | + model[NodeBindDataFieldEnum.PERIMETER_TYPE] === PerimeterTypeEnum.CIRCLE && | ||
80 | + !model[NodeBindDataFieldEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA], | ||
81 | + componentProps: { | ||
82 | + options: Object.keys(RangeUtilEnum).map((value) => ({ | ||
83 | + label: RangeUtilNameEnum[value], | ||
84 | + value, | ||
85 | + })), | ||
86 | + }, | ||
9 | }, | 87 | }, |
10 | ]; | 88 | ]; |
1 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; | 1 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; |
2 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
2 | import { FormSchema } from '/@/components/Form'; | 3 | import { FormSchema } from '/@/components/Form'; |
4 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
3 | 5 | ||
4 | export const formSchemas: FormSchema[] = [ | 6 | export const formSchemas: FormSchema[] = [ |
5 | { | 7 | { |
6 | - field: NodeBindDataFieldEnum.NAME, | ||
7 | - component: 'Input', | ||
8 | - label: NodeBindDataFieldNameEnum.NAME, | 8 | + field: NodeBindDataFieldEnum.MESSAGE_TYPES, |
9 | + component: 'ApiSelect', | ||
10 | + label: NodeBindDataFieldNameEnum.MESSAGE_TYPES, | ||
11 | + componentProps: { | ||
12 | + api: findDictItemByCode, | ||
13 | + params: { | ||
14 | + dictCode: DictEnum.MESSAGE_TYPES_FILTER, | ||
15 | + }, | ||
16 | + mode: 'multiple', | ||
17 | + labelField: 'itemText', | ||
18 | + valueField: 'itemValue', | ||
19 | + getPopupContainer: () => document.body, | ||
20 | + }, | ||
9 | }, | 21 | }, |
10 | ]; | 22 | ]; |
1 | -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; | ||
2 | import { FormSchema } from '/@/components/Form'; | 1 | import { FormSchema } from '/@/components/Form'; |
3 | 2 | ||
4 | -export const formSchemas: FormSchema[] = [ | ||
5 | - { | ||
6 | - field: NodeBindDataFieldEnum.NAME, | ||
7 | - component: 'Input', | ||
8 | - label: NodeBindDataFieldNameEnum.NAME, | ||
9 | - }, | ||
10 | -]; | 3 | +export const formSchemas: FormSchema[] = []; |
1 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; | 1 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; |
2 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
2 | import { FormSchema } from '/@/components/Form'; | 3 | import { FormSchema } from '/@/components/Form'; |
4 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
3 | 5 | ||
4 | export const formSchemas: FormSchema[] = [ | 6 | export const formSchemas: FormSchema[] = [ |
5 | { | 7 | { |
6 | - field: NodeBindDataFieldEnum.NAME, | ||
7 | - component: 'Input', | ||
8 | - label: NodeBindDataFieldNameEnum.NAME, | 8 | + field: NodeBindDataFieldEnum.ORIGINATOR_TYPES, |
9 | + component: 'ApiSelect', | ||
10 | + label: NodeBindDataFieldNameEnum.ORIGINATOR_TYPES, | ||
11 | + componentProps: { | ||
12 | + api: findDictItemByCode, | ||
13 | + params: { | ||
14 | + dictCode: DictEnum.ORIGINATOR_TYPES, | ||
15 | + }, | ||
16 | + mode: 'multiple', | ||
17 | + labelField: 'itemText', | ||
18 | + valueField: 'itemValue', | ||
19 | + getPopupContainer: () => document.body, | ||
20 | + }, | ||
9 | }, | 21 | }, |
10 | ]; | 22 | ]; |
1 | -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; | ||
2 | import { FormSchema } from '/@/components/Form'; | 1 | import { FormSchema } from '/@/components/Form'; |
3 | 2 | ||
4 | -export const formSchemas: FormSchema[] = [ | ||
5 | - { | ||
6 | - field: NodeBindDataFieldEnum.NAME, | ||
7 | - component: 'Input', | ||
8 | - label: NodeBindDataFieldNameEnum.NAME, | ||
9 | - }, | ||
10 | -]; | 3 | +export const formSchemas: FormSchema[] = []; |
1 | -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; | ||
2 | import { FormSchema } from '/@/components/Form'; | 1 | import { FormSchema } from '/@/components/Form'; |
2 | +import { isObject } from '/@/utils/is'; | ||
3 | + | ||
4 | +export const validateJSON = (value: string): { value: Recordable | string; flag: boolean } => { | ||
5 | + let flag = false; | ||
6 | + try { | ||
7 | + value = JSON.parse(value); | ||
8 | + if (isObject(value)) flag = true; | ||
9 | + } catch (error) {} | ||
10 | + return { value, flag }; | ||
11 | +}; | ||
3 | 12 | ||
4 | export const formSchemas: FormSchema[] = [ | 13 | export const formSchemas: FormSchema[] = [ |
5 | { | 14 | { |
6 | - field: NodeBindDataFieldEnum.NAME, | ||
7 | - component: 'Input', | ||
8 | - label: NodeBindDataFieldNameEnum.NAME, | 15 | + field: 'value', |
16 | + component: 'JSONEditor', | ||
17 | + label: '配置', | ||
18 | + valueField: 'value', | ||
19 | + changeEvent: 'update:value', | ||
20 | + rules: [ | ||
21 | + { | ||
22 | + required: true, | ||
23 | + validator: (_rule, value) => { | ||
24 | + const { flag } = validateJSON(value); | ||
25 | + return flag ? Promise.resolve() : Promise.reject('配置项不是一个JSON对象'); | ||
26 | + }, | ||
27 | + }, | ||
28 | + ], | ||
29 | + componentProps: { | ||
30 | + height: 300, | ||
31 | + }, | ||
9 | }, | 32 | }, |
10 | ]; | 33 | ]; |
1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
2 | import type { CreateModalDefineExposeType } from '../../../types'; | 2 | import type { CreateModalDefineExposeType } from '../../../types'; |
3 | import { BasicForm, useForm } from '/@/components/Form'; | 3 | import { BasicForm, useForm } from '/@/components/Form'; |
4 | - import { formSchemas } from './create.config'; | 4 | + import { formSchemas, validateJSON } from './create.config'; |
5 | import { NodeData } from '../../../types/node'; | 5 | import { NodeData } from '../../../types/node'; |
6 | 6 | ||
7 | defineProps<{ | 7 | defineProps<{ |
@@ -16,12 +16,13 @@ | @@ -16,12 +16,13 @@ | ||
16 | const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { | 16 | const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { |
17 | await validate(); | 17 | await validate(); |
18 | const value = getFieldsValue() || {}; | 18 | const value = getFieldsValue() || {}; |
19 | - return value; | 19 | + const { value: data } = validateJSON(value?.value); |
20 | + return data as Recordable; | ||
20 | }; | 21 | }; |
21 | 22 | ||
22 | const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { | 23 | const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { |
23 | resetFields(); | 24 | resetFields(); |
24 | - setFieldsValue(value); | 25 | + setFieldsValue({ value: JSON.stringify(value, null, 2) }); |
25 | }; | 26 | }; |
26 | 27 | ||
27 | defineExpose({ | 28 | defineExpose({ |
1 | +<script lang="ts" setup> | ||
2 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
3 | + import ScriptFilterTest from './ScriptFilterTest.vue'; | ||
4 | + import { MessageTypesFilterEnum } from '../../../enum/form'; | ||
5 | + import { ref } from 'vue'; | ||
6 | + import { JavaScriptFunctionEditor } from '/@/components/Form'; | ||
7 | + | ||
8 | + defineProps<{ | ||
9 | + visible?: boolean; | ||
10 | + javaScriptEditorProps?: InstanceType<typeof JavaScriptFunctionEditor>['$props']; | ||
11 | + }>(); | ||
12 | + | ||
13 | + const emit = defineEmits(['save', 'register']); | ||
14 | + | ||
15 | + interface SaveValueType { | ||
16 | + msg: Recordable; | ||
17 | + metadata: Recordable; | ||
18 | + msgType: MessageTypesFilterEnum; | ||
19 | + javascriptFunction: string; | ||
20 | + } | ||
21 | + | ||
22 | + const value = ref(''); | ||
23 | + | ||
24 | + const [register, { closeModal }] = useModalInner((data: ModalParamsType<string>) => { | ||
25 | + value.value = data.record; | ||
26 | + }); | ||
27 | + | ||
28 | + const handleSaveScript = (value: SaveValueType) => { | ||
29 | + emit('save', value); | ||
30 | + closeModal(); | ||
31 | + }; | ||
32 | +</script> | ||
33 | + | ||
34 | +<template> | ||
35 | + <BasicModal | ||
36 | + wrapClassName="scriptTestModal" | ||
37 | + @register="register" | ||
38 | + :visible="visible" | ||
39 | + title="测试脚本功能" | ||
40 | + defaultFullscreen | ||
41 | + :showCancelBtn="false" | ||
42 | + :showOkBtn="false" | ||
43 | + :footer="null" | ||
44 | + > | ||
45 | + <ScriptFilterTest | ||
46 | + :javaScriptEditorProps="javaScriptEditorProps" | ||
47 | + :value="value" | ||
48 | + @cancel="closeModal()" | ||
49 | + @save="handleSaveScript" | ||
50 | + /> | ||
51 | + </BasicModal> | ||
52 | +</template> |
1 | +<script lang="ts" setup> | ||
2 | + import { Icon } from '/@/components/Icon'; | ||
3 | + import { Tooltip } from 'ant-design-vue'; | ||
4 | + import { ref } from 'vue'; | ||
5 | + | ||
6 | + const visible = ref(false); | ||
7 | + const handleOpenHelpMessage = () => { | ||
8 | + visible.value = true; | ||
9 | + }; | ||
10 | +</script> | ||
11 | + | ||
12 | +<template> | ||
13 | + <Tooltip title="点击获取帮助"> | ||
14 | + <Icon | ||
15 | + icon="material-symbols:help-outline" | ||
16 | + class="cursor-pointer svg:text-xl" | ||
17 | + @click="handleOpenHelpMessage" | ||
18 | + /> | ||
19 | + </Tooltip> | ||
20 | +</template> |
1 | +<script setup lang="ts"> | ||
2 | + import { JavaScriptFunctionEditor } from '/@/components/Form'; | ||
3 | + import { Button } from 'ant-design-vue'; | ||
4 | + import FilterModal from './FilterModal.vue'; | ||
5 | + import { useModal } from '/@/components/Modal'; | ||
6 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
7 | + import { computed } from 'vue'; | ||
8 | + | ||
9 | + const props = withDefaults( | ||
10 | + defineProps<{ | ||
11 | + value?: string; | ||
12 | + buttonName?: string; | ||
13 | + javaScriptEditorProps?: InstanceType<typeof JavaScriptFunctionEditor>['$props']; | ||
14 | + }>(), | ||
15 | + { | ||
16 | + buttonName: 'Test Filter Function', | ||
17 | + javaScriptEditorProps: () => ({ | ||
18 | + functionName: 'Filter', | ||
19 | + paramsName: ['msg', 'metadata', 'msgType'], | ||
20 | + }), | ||
21 | + } | ||
22 | + ); | ||
23 | + | ||
24 | + const emit = defineEmits(['update:value']); | ||
25 | + const [register, { openModal }] = useModal(); | ||
26 | + | ||
27 | + const handleOpen = () => { | ||
28 | + openModal(true, { | ||
29 | + record: props.value, | ||
30 | + mode: DataActionModeEnum.CREATE, | ||
31 | + } as ModalParamsType<string>); | ||
32 | + }; | ||
33 | + | ||
34 | + const getValue = computed<string>({ | ||
35 | + get() { | ||
36 | + return props.value || ''; | ||
37 | + }, | ||
38 | + set(value) { | ||
39 | + emit('update:value', value); | ||
40 | + }, | ||
41 | + }); | ||
42 | + | ||
43 | + const handleSave = (value: Record<'javascriptFunction', string>) => { | ||
44 | + emit('update:value', value.javascriptFunction); | ||
45 | + }; | ||
46 | +</script> | ||
47 | + | ||
48 | +<template> | ||
49 | + <section> | ||
50 | + <JavaScriptFunctionEditor v-model:value="getValue" v-bind="javaScriptEditorProps" /> | ||
51 | + <Button class="mt-4" type="primary" @click="handleOpen">{{ buttonName }}</Button> | ||
52 | + <FilterModal | ||
53 | + :javaScriptEditorProps="javaScriptEditorProps" | ||
54 | + @register="register" | ||
55 | + @save="handleSave" | ||
56 | + /> | ||
57 | + </section> | ||
58 | +</template> |
1 | +<script lang="ts" setup> | ||
2 | + import { Card, Select, Tag, Form, Button, Input } from 'ant-design-vue'; | ||
3 | + import { ref, unref, watch } from 'vue'; | ||
4 | + import { MessageTypesFilterEnum, MessageTypesFilterNameEnum } from '../../../enum/form'; | ||
5 | + import { JSONEditor } from '/@/components/CodeEditor'; | ||
6 | + import { buildShortUUID } from '/@/utils/uuid'; | ||
7 | + import { Icon } from '/@/components/Icon'; | ||
8 | + import { JavaScriptFunctionEditor } from '/@/components/Form'; | ||
9 | + import { useJsonParse } from '/@/hooks/business/useJsonParse'; | ||
10 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
11 | + import { isNullOrUnDef } from '/@/utils/is'; | ||
12 | + | ||
13 | + interface MetaDataItemType { | ||
14 | + label: string; | ||
15 | + value: string; | ||
16 | + uuid: string; | ||
17 | + } | ||
18 | + | ||
19 | + interface Value { | ||
20 | + msg: Recordable; | ||
21 | + metadata: Recordable; | ||
22 | + msgType: MessageTypesFilterEnum; | ||
23 | + javascriptFunction: string; | ||
24 | + } | ||
25 | + | ||
26 | + const props = withDefaults( | ||
27 | + defineProps<{ | ||
28 | + value?: string; | ||
29 | + javaScriptEditorProps?: InstanceType<typeof JavaScriptFunctionEditor>['$props']; | ||
30 | + }>(), | ||
31 | + { | ||
32 | + javaScriptEditorProps: () => ({ | ||
33 | + functionName: 'Filter', | ||
34 | + paramsName: ['msg', 'metadata', 'msgType'], | ||
35 | + }), | ||
36 | + } | ||
37 | + ); | ||
38 | + | ||
39 | + const emit = defineEmits<{ | ||
40 | + (eventName: 'cancel'): void; | ||
41 | + (eventName: 'test', value: Value): void; | ||
42 | + (eventName: 'save', value: Value): void; | ||
43 | + }>(); | ||
44 | + | ||
45 | + const messageType = ref(MessageTypesFilterEnum.POST_TELEMETRY); | ||
46 | + const messageTypeOptions = Object.keys(MessageTypesFilterEnum).map((value) => ({ | ||
47 | + label: MessageTypesFilterNameEnum[value], | ||
48 | + value, | ||
49 | + })); | ||
50 | + | ||
51 | + const messageContent = ref({ | ||
52 | + temperature: 22.4, | ||
53 | + humidity: 78, | ||
54 | + }); | ||
55 | + | ||
56 | + const scriptContent = ref('return msg.temperature > 20;'); | ||
57 | + | ||
58 | + const outputContent = ref(''); | ||
59 | + | ||
60 | + const metaData = ref<MetaDataItemType[]>([ | ||
61 | + { uuid: buildShortUUID(), label: 'deviceName', value: 'Test Device' }, | ||
62 | + { uuid: buildShortUUID(), label: 'deviceType', value: 'default' }, | ||
63 | + { uuid: buildShortUUID(), label: 'ts', value: Date.now().toString() }, | ||
64 | + ]); | ||
65 | + | ||
66 | + const handleAddMetadata = () => { | ||
67 | + metaData.value.push({ uuid: buildShortUUID(), label: '', value: '' }); | ||
68 | + }; | ||
69 | + | ||
70 | + const handleRemove = (data: MetaDataItemType) => { | ||
71 | + const index = unref(metaData).findIndex((item) => item.uuid === data.uuid); | ||
72 | + ~index && unref(metaData).splice(index, 1); | ||
73 | + }; | ||
74 | + | ||
75 | + const { createMessage } = useMessage(); | ||
76 | + const handleValidate = () => { | ||
77 | + const message = unref(messageContent); | ||
78 | + const metadata = unref(metaData); | ||
79 | + const { flag } = useJsonParse(message); | ||
80 | + | ||
81 | + if (!flag) { | ||
82 | + createMessage.warning('消息不是一个有效的JSON对象'); | ||
83 | + return false; | ||
84 | + } | ||
85 | + | ||
86 | + for (const item of metadata) { | ||
87 | + const { label, value } = item; | ||
88 | + if (isNullOrUnDef(label) || isNullOrUnDef(value)) { | ||
89 | + createMessage.warning('元数据的键名和键值为必填项'); | ||
90 | + return false; | ||
91 | + } | ||
92 | + } | ||
93 | + return true; | ||
94 | + }; | ||
95 | + | ||
96 | + const getValue = (): Value => { | ||
97 | + const msg = unref(messageContent); | ||
98 | + const msgType = unref(messageType); | ||
99 | + const metadata = unref(metaData).reduce( | ||
100 | + (prev, next) => ({ ...prev, [next.label]: next.value }), | ||
101 | + {} as Recordable | ||
102 | + ); | ||
103 | + const javascriptFunction = unref(scriptContent); | ||
104 | + return { msg, msgType, metadata, javascriptFunction }; | ||
105 | + }; | ||
106 | + | ||
107 | + const handleTestScript = () => { | ||
108 | + const flag = handleValidate(); | ||
109 | + | ||
110 | + flag && emit('test', getValue()); | ||
111 | + }; | ||
112 | + | ||
113 | + const handleSave = () => { | ||
114 | + const flag = handleValidate(); | ||
115 | + flag && emit('save', getValue()); | ||
116 | + }; | ||
117 | + | ||
118 | + watch( | ||
119 | + () => props.value, | ||
120 | + (value) => { | ||
121 | + scriptContent.value = value || ''; | ||
122 | + } | ||
123 | + ); | ||
124 | +</script> | ||
125 | + | ||
126 | +<template> | ||
127 | + <section class="script-test-container w-full h-full flex flex-col"> | ||
128 | + <Form class="flex-auto grid grid-cols-2 grid-rows-2 bg-gray-100 gap-2"> | ||
129 | + <main> | ||
130 | + <Card class="w-full h-full"> | ||
131 | + <div class="w-full h-full flex flex-col"> | ||
132 | + <div class="flex justify-between items-center"> | ||
133 | + <div class="flex flex-col"> | ||
134 | + <Form.Item label="消息类型" labelAlign="left" :colon="false"> | ||
135 | + <Select | ||
136 | + v-model:value="messageType" | ||
137 | + :options="messageTypeOptions" | ||
138 | + :bordered="false" | ||
139 | + class="min-w-60 border-b-gray-400 border-b" | ||
140 | + /> | ||
141 | + </Form.Item> | ||
142 | + </div> | ||
143 | + <Tag color="blue"> 消息 </Tag> | ||
144 | + </div> | ||
145 | + <JSONEditor v-model:value="messageContent" title="消息" class="mt-4 flex-auto" /> | ||
146 | + </div> | ||
147 | + </Card> | ||
148 | + </main> | ||
149 | + <main> | ||
150 | + <Card class="w-full h-full"> | ||
151 | + <div class="flex flex-col"> | ||
152 | + <div class="flex justify-between"> | ||
153 | + <span class="text-gray-400">元数据</span> | ||
154 | + <Tag color="blue">元数据</Tag> | ||
155 | + </div> | ||
156 | + <div class="mt-4 overflow-y-auto"> | ||
157 | + <div v-for="item in metaData" :key="item.uuid" class="flex mt-2 gap-2 items-end"> | ||
158 | + <Input v-model:value="item.label" placeholder="请输入键名" /> | ||
159 | + <Input v-model:value="item.value" placeholder="请输入键值" /> | ||
160 | + <Icon | ||
161 | + icon="material-symbols:close" | ||
162 | + class="svg:text-2xl cursor-pointer" | ||
163 | + @click="handleRemove(item)" | ||
164 | + /> | ||
165 | + </div> | ||
166 | + </div> | ||
167 | + <div class="mt-4"> | ||
168 | + <Button type="primary" @click="handleAddMetadata">添加</Button> | ||
169 | + </div> | ||
170 | + </div> | ||
171 | + </Card> | ||
172 | + </main> | ||
173 | + <main> | ||
174 | + <Card class="w-full h-full"> | ||
175 | + <JavaScriptFunctionEditor | ||
176 | + v-model:value="scriptContent" | ||
177 | + v-bind="javaScriptEditorProps" | ||
178 | + height="100%" | ||
179 | + > | ||
180 | + <template #beforeFormat> | ||
181 | + <Tag color="blue" class="!mr-0">{{ javaScriptEditorProps.functionName }}</Tag> | ||
182 | + </template> | ||
183 | + <template #beforeFullScreen> | ||
184 | + <Icon icon="material-symbols:help-outline" class="cursor-pointer svg:text-xl" /> | ||
185 | + </template> | ||
186 | + </JavaScriptFunctionEditor> | ||
187 | + </Card> | ||
188 | + </main> | ||
189 | + <main> | ||
190 | + <Card class="w-full h-full"> | ||
191 | + <JSONEditor | ||
192 | + v-model:value="outputContent" | ||
193 | + title="输出" | ||
194 | + class="flex-auto" | ||
195 | + height="100%" | ||
196 | + :disabled="true" | ||
197 | + > | ||
198 | + <template #beforeFormat> | ||
199 | + <Tag color="blue" class="!mr-0">输出</Tag> | ||
200 | + </template> | ||
201 | + </JSONEditor> | ||
202 | + </Card> | ||
203 | + </main> | ||
204 | + </Form> | ||
205 | + <div class="h-12 flex items-center justify-between px-4 bg-light-50"> | ||
206 | + <Button type="primary" @click="handleTestScript">测试</Button> | ||
207 | + <div class="flex gap-4"> | ||
208 | + <Button @click="emit('cancel')">取消</Button> | ||
209 | + <Button type="primary" @click="handleSave">保存</Button> | ||
210 | + </div> | ||
211 | + </div> | ||
212 | + </section> | ||
213 | +</template> | ||
214 | + | ||
215 | +<style lang="less" scoped> | ||
216 | + .script-test-container { | ||
217 | + :deep(.ant-card-body) { | ||
218 | + @apply w-full h-full p-2; | ||
219 | + | ||
220 | + .ant-form-item { | ||
221 | + label { | ||
222 | + @apply text-xs pl-3 text-gray-400; | ||
223 | + } | ||
224 | + } | ||
225 | + | ||
226 | + .ant-input { | ||
227 | + @apply !border-transparent !border-b !border-b-dark-300; | ||
228 | + | ||
229 | + &:focus { | ||
230 | + @apply !shadow-none; | ||
231 | + } | ||
232 | + } | ||
233 | + } | ||
234 | + } | ||
235 | +</style> |
1 | +import { h } from 'vue'; | ||
1 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; | 2 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; |
2 | -import { FormSchema } from '/@/components/Form'; | 3 | +import { FormSchema, useComponentRegister } from '/@/components/Form'; |
4 | +import HelpMessage from './HelpMessage.vue'; | ||
5 | +import JavaScriptFilterFunctionModal from './JavaScriptFilterFunctionModal.vue'; | ||
6 | + | ||
7 | +useComponentRegister('JavaScriptFilterFunctionModal', JavaScriptFilterFunctionModal); | ||
3 | 8 | ||
4 | export const formSchemas: FormSchema[] = [ | 9 | export const formSchemas: FormSchema[] = [ |
5 | { | 10 | { |
6 | - field: NodeBindDataFieldEnum.NAME, | ||
7 | - component: 'Input', | ||
8 | - label: NodeBindDataFieldNameEnum.NAME, | 11 | + field: NodeBindDataFieldEnum.JS_SCRIPT, |
12 | + component: 'JavaScriptFilterFunctionModal', | ||
13 | + label: NodeBindDataFieldNameEnum.JS_SCRIPT, | ||
14 | + changeEvent: 'update:value', | ||
15 | + valueField: 'value', | ||
16 | + renderComponentContent: () => { | ||
17 | + return { | ||
18 | + beforeFullScreen: () => h(HelpMessage), | ||
19 | + }; | ||
20 | + }, | ||
9 | }, | 21 | }, |
10 | ]; | 22 | ]; |
1 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; | 1 | import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; |
2 | -import { FormSchema } from '/@/components/Form'; | 2 | +import { FormSchema, useComponentRegister } from '/@/components/Form'; |
3 | +import JavaScriptFilterFunctionModal from '../Script/JavaScriptFilterFunctionModal.vue'; | ||
3 | 4 | ||
5 | +useComponentRegister('JavaScriptFilterFunctionModal', JavaScriptFilterFunctionModal); | ||
4 | export const formSchemas: FormSchema[] = [ | 6 | export const formSchemas: FormSchema[] = [ |
5 | { | 7 | { |
6 | - field: NodeBindDataFieldEnum.NAME, | ||
7 | - component: 'Input', | ||
8 | - label: NodeBindDataFieldNameEnum.NAME, | 8 | + field: NodeBindDataFieldEnum.JS_SCRIPT, |
9 | + component: 'JavaScriptFilterFunctionModal', | ||
10 | + label: NodeBindDataFieldNameEnum.JS_SCRIPT, | ||
11 | + changeEvent: 'update:value', | ||
12 | + valueField: 'value', | ||
13 | + componentProps: { | ||
14 | + buttonName: 'Test Switch Function', | ||
15 | + javaScriptEditorProps: { | ||
16 | + functionName: 'Switch', | ||
17 | + paramsName: ['msg', 'metadata', 'mstType'], | ||
18 | + }, | ||
19 | + }, | ||
20 | + renderComponentContent: () => { | ||
21 | + return { | ||
22 | + // beforeFullScreen: () => h(HelpMessage), | ||
23 | + }; | ||
24 | + }, | ||
9 | }, | 25 | }, |
10 | ]; | 26 | ]; |
@@ -27,8 +27,8 @@ export const FilterComponents: NodeItemConfigType[] = [ | @@ -27,8 +27,8 @@ export const FilterComponents: NodeItemConfigType[] = [ | ||
27 | GpsGeofencingFilterConfig, | 27 | GpsGeofencingFilterConfig, |
28 | MessageTypeConfig, | 28 | MessageTypeConfig, |
29 | MessageTypeSwitchConfig, | 29 | MessageTypeSwitchConfig, |
30 | - OriginatorTypeSwitchConfig, | ||
31 | OriginatorTypeConfig, | 30 | OriginatorTypeConfig, |
31 | + OriginatorTypeSwitchConfig, | ||
32 | SceneReactConfig, | 32 | SceneReactConfig, |
33 | ScriptConfig, | 33 | ScriptConfig, |
34 | SwitchConfig, | 34 | SwitchConfig, |
@@ -75,6 +75,7 @@ | @@ -75,6 +75,7 @@ | ||
75 | <section v-if="shadowComponent" class="w-full h-full"> | 75 | <section v-if="shadowComponent" class="w-full h-full"> |
76 | <BasicForm @register="topFormRegister" /> | 76 | <BasicForm @register="topFormRegister" /> |
77 | <component | 77 | <component |
78 | + class="rule-node-form" | ||
78 | :is="shadowComponent" | 79 | :is="shadowComponent" |
79 | ref="createComponentEl" | 80 | ref="createComponentEl" |
80 | :key="getComponentKey" | 81 | :key="getComponentKey" |
@@ -87,3 +88,12 @@ | @@ -87,3 +88,12 @@ | ||
87 | </Spin> | 88 | </Spin> |
88 | </BasicModal> | 89 | </BasicModal> |
89 | </template> | 90 | </template> |
91 | + | ||
92 | +<style lang="less" scoped> | ||
93 | + .rule-node-form { | ||
94 | + :deep(.ant-input-number) { | ||
95 | + width: 100%; | ||
96 | + min-width: none; | ||
97 | + } | ||
98 | + } | ||
99 | +</style> |
@@ -10,6 +10,6 @@ | @@ -10,6 +10,6 @@ | ||
10 | <p class="text-gray-600 italic"> | 10 | <p class="text-gray-600 italic"> |
11 | {{ nodeData?.config?.configurationDescriptor.nodeDefinition.description }}</p | 11 | {{ nodeData?.config?.configurationDescriptor.nodeDefinition.description }}</p |
12 | > | 12 | > |
13 | - <p>{{ nodeData?.config?.configurationDescriptor.nodeDefinition.details }}</p> | 13 | + <p v-html="nodeData?.config?.configurationDescriptor.nodeDefinition.details"> </p> |
14 | </section> | 14 | </section> |
15 | </template> | 15 | </template> |
@@ -99,6 +99,7 @@ | @@ -99,6 +99,7 @@ | ||
99 | <section v-if="shadowComponent" class="w-full h-full"> | 99 | <section v-if="shadowComponent" class="w-full h-full"> |
100 | <BasicForm @register="topFormRegister" @vue:mounted="handleTopFormMounted" /> | 100 | <BasicForm @register="topFormRegister" @vue:mounted="handleTopFormMounted" /> |
101 | <component | 101 | <component |
102 | + class="rule-node-form" | ||
102 | :is="shadowComponent" | 103 | :is="shadowComponent" |
103 | ref="createComponentEl" | 104 | ref="createComponentEl" |
104 | :config="nodeData" | 105 | :config="nodeData" |
@@ -119,3 +120,12 @@ | @@ -119,3 +120,12 @@ | ||
119 | </Tabs> | 120 | </Tabs> |
120 | </BasicDrawer> | 121 | </BasicDrawer> |
121 | </template> | 122 | </template> |
123 | + | ||
124 | +<style lang="less" scoped> | ||
125 | + .rule-node-form { | ||
126 | + :deep(.ant-input-number) { | ||
127 | + width: 100%; | ||
128 | + min-width: none; | ||
129 | + } | ||
130 | + } | ||
131 | +</style> |
@@ -136,7 +136,6 @@ export const tenantFormSchema: FormSchema[] = [ | @@ -136,7 +136,6 @@ export const tenantFormSchema: FormSchema[] = [ | ||
136 | } | 136 | } |
137 | return { items, total }; | 137 | return { items, total }; |
138 | }, | 138 | }, |
139 | - showSearch: true, | ||
140 | labelField: 'name', | 139 | labelField: 'name', |
141 | valueField: 'id.id', | 140 | valueField: 'id.id', |
142 | filterOption: (inputValue: string, options: Record<'label' | 'value', string>) => { | 141 | filterOption: (inputValue: string, options: Record<'label' | 'value', string>) => { |
@@ -49,3 +49,10 @@ export interface PaginationResult<T = Recordable> { | @@ -49,3 +49,10 @@ export interface PaginationResult<T = Recordable> { | ||
49 | items: T[]; | 49 | items: T[]; |
50 | total: number; | 50 | total: number; |
51 | } | 51 | } |
52 | + | ||
53 | +export interface TBPaginationResult<T = Recordable> { | ||
54 | + data: T[]; | ||
55 | + hasNext: boolean; | ||
56 | + totalElements: number; | ||
57 | + totalPages: number; | ||
58 | +} |