Commit bc35026eee21fb75465fe5b8220e181256175f31
Merge branch 'perf/product-object-model' into 'main_dev'
perf: 优化产品物模型表单&&新增分段控制器组件 See merge request yunteng/thingskit-front!1181
Showing
43 changed files
with
2111 additions
and
1371 deletions
@@ -2,21 +2,21 @@ import { DataTypeEnum } from '/@/enums/objectModelEnum'; | @@ -2,21 +2,21 @@ import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
2 | import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | 2 | import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; |
3 | 3 | ||
4 | export interface Specs { | 4 | export interface Specs { |
5 | - min: string; | ||
6 | - max: string; | 5 | + min: number; |
6 | + max: number; | ||
7 | unit: string; | 7 | unit: string; |
8 | unitName: string; | 8 | unitName: string; |
9 | 9 | ||
10 | dataType?: string; | 10 | dataType?: string; |
11 | name?: string; | 11 | name?: string; |
12 | value?: string | number; | 12 | value?: string | number; |
13 | - step: string; | ||
14 | - length: string; | 13 | + step: number; |
14 | + length: number; | ||
15 | boolOpen: string; | 15 | boolOpen: string; |
16 | boolClose: string; | 16 | boolClose: string; |
17 | valueRange?: { | 17 | valueRange?: { |
18 | - min: string; | ||
19 | - max: string; | 18 | + min: number; |
19 | + max: number; | ||
20 | }; | 20 | }; |
21 | } | 21 | } |
22 | 22 | ||
@@ -34,16 +34,15 @@ export interface ExtensionDesc { | @@ -34,16 +34,15 @@ export interface ExtensionDesc { | ||
34 | } | 34 | } |
35 | 35 | ||
36 | export interface StructJSON { | 36 | export interface StructJSON { |
37 | - functionName?: string; | 37 | + functionName: string; |
38 | identifier: string; | 38 | identifier: string; |
39 | - remark?: string; | ||
40 | dataType?: DataType; | 39 | dataType?: DataType; |
40 | + remark?: string; | ||
41 | serviceCommand?: string; | 41 | serviceCommand?: string; |
42 | - accessMode?: string; | ||
43 | } | 42 | } |
44 | 43 | ||
45 | export interface FunctionJson { | 44 | export interface FunctionJson { |
46 | - dataType?: DataType | DataType[]; | 45 | + dataType?: DataType; |
47 | inputData?: StructJSON[]; | 46 | inputData?: StructJSON[]; |
48 | outputData?: StructJSON[]; | 47 | outputData?: StructJSON[]; |
49 | serviceCommand?: string; | 48 | serviceCommand?: string; |
@@ -55,7 +54,7 @@ export interface ModelOfMatterParams { | @@ -55,7 +54,7 @@ export interface ModelOfMatterParams { | ||
55 | functionName: string; | 54 | functionName: string; |
56 | functionType: FunctionTypeEnum; | 55 | functionType: FunctionTypeEnum; |
57 | identifier: string; | 56 | identifier: string; |
58 | - remark: string; | 57 | + remark?: string; |
59 | id?: string; | 58 | id?: string; |
60 | categoryId?: string; | 59 | categoryId?: string; |
61 | callType?: string; | 60 | callType?: string; |
@@ -13,7 +13,6 @@ export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; | @@ -13,7 +13,6 @@ 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 | export { default as ApiCascader } from './src/components/ApiCascader.vue'; | 14 | export { default as ApiCascader } from './src/components/ApiCascader.vue'; |
15 | 15 | ||
16 | -export { default as StructForm } from './src/components/StructForm/StructForm.vue'; | ||
17 | export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue'; | 16 | export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue'; |
18 | 17 | ||
19 | export { ThingsModelForm } from './src/components/ThingsModelForm'; | 18 | export { ThingsModelForm } from './src/components/ThingsModelForm'; |
@@ -36,13 +36,11 @@ import IconDrawer from './components/IconDrawer.vue'; | @@ -36,13 +36,11 @@ import IconDrawer from './components/IconDrawer.vue'; | ||
36 | import ApiUpload from './components/ApiUpload.vue'; | 36 | import ApiUpload from './components/ApiUpload.vue'; |
37 | import ApiSearchSelect from './components/ApiSearchSelect.vue'; | 37 | import ApiSearchSelect from './components/ApiSearchSelect.vue'; |
38 | import CustomMinMaxInput from './components/CustomMinMaxInput.vue'; | 38 | import CustomMinMaxInput from './components/CustomMinMaxInput.vue'; |
39 | -import StructForm from './components/StructForm/StructForm.vue'; | ||
40 | import ApiSelectScrollLoad from './components/ApiSelectScrollLoad.vue'; | 39 | import ApiSelectScrollLoad from './components/ApiSelectScrollLoad.vue'; |
41 | import InputGroup from './components/InputGroup.vue'; | 40 | import InputGroup from './components/InputGroup.vue'; |
42 | import RegisterAddressInput from '/@/views/task/center/components/PollCommandInput/RegisterAddressInput.vue'; | 41 | import RegisterAddressInput from '/@/views/task/center/components/PollCommandInput/RegisterAddressInput.vue'; |
43 | -import ExtendDesc from '/@/components/Form/src/components/ExtendDesc/index.vue'; | ||
44 | import DeviceProfileForm from '/@/components/Form/src/components/DeviceProfileForm/index.vue'; | 42 | import DeviceProfileForm from '/@/components/Form/src/components/DeviceProfileForm/index.vue'; |
45 | -import EnumList from './components/StructForm/EnumList.vue'; | 43 | +import { Segmented } from './components/Segmented'; |
46 | 44 | ||
47 | const componentMap = new Map<ComponentType, Component>(); | 45 | const componentMap = new Map<ComponentType, Component>(); |
48 | 46 | ||
@@ -76,6 +74,7 @@ componentMap.set('TimePicker', TimePicker); | @@ -76,6 +74,7 @@ componentMap.set('TimePicker', TimePicker); | ||
76 | componentMap.set('StrengthMeter', StrengthMeter); | 74 | componentMap.set('StrengthMeter', StrengthMeter); |
77 | componentMap.set('IconPicker', IconPicker); | 75 | componentMap.set('IconPicker', IconPicker); |
78 | componentMap.set('InputCountDown', CountdownInput); | 76 | componentMap.set('InputCountDown', CountdownInput); |
77 | +componentMap.set('Segmented', Segmented); | ||
79 | 78 | ||
80 | componentMap.set('Upload', BasicUpload); | 79 | componentMap.set('Upload', BasicUpload); |
81 | //注册自定义组件 | 80 | //注册自定义组件 |
@@ -86,12 +85,9 @@ componentMap.set('IconDrawer', IconDrawer); | @@ -86,12 +85,9 @@ componentMap.set('IconDrawer', IconDrawer); | ||
86 | componentMap.set('ApiUpload', ApiUpload); | 85 | componentMap.set('ApiUpload', ApiUpload); |
87 | componentMap.set('ApiSearchSelect', ApiSearchSelect); | 86 | componentMap.set('ApiSearchSelect', ApiSearchSelect); |
88 | componentMap.set('CustomMinMaxInput', CustomMinMaxInput); | 87 | componentMap.set('CustomMinMaxInput', CustomMinMaxInput); |
89 | -componentMap.set('StructForm', StructForm); | ||
90 | componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad); | 88 | componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad); |
91 | componentMap.set('InputGroup', InputGroup); | 89 | componentMap.set('InputGroup', InputGroup); |
92 | componentMap.set('RegisterAddressInput', RegisterAddressInput); | 90 | componentMap.set('RegisterAddressInput', RegisterAddressInput); |
93 | -componentMap.set('ExtendDesc', ExtendDesc); | ||
94 | -componentMap.set('EnumList', EnumList); | ||
95 | componentMap.set('DeviceProfileForm', DeviceProfileForm); | 91 | componentMap.set('DeviceProfileForm', DeviceProfileForm); |
96 | 92 | ||
97 | export function add(compName: ComponentType, component: Component) { | 93 | export function add(compName: ComponentType, component: Component) { |
@@ -4,17 +4,13 @@ | @@ -4,17 +4,13 @@ | ||
4 | placeholder="最小值" | 4 | placeholder="最小值" |
5 | :disabled="$props.disabled" | 5 | :disabled="$props.disabled" |
6 | :value="getValue.min!" | 6 | :value="getValue.min!" |
7 | - style="width: 38%" | ||
8 | @change="(value) => emitChange(value, 'min')" | 7 | @change="(value) => emitChange(value, 'min')" |
9 | /> | 8 | /> |
10 | - <span style="width: 8px"></span> | ||
11 | - <span>~</span> | ||
12 | - <span style="width: 8px"></span> | 9 | + <div class="text-center flex-shrink-0 w-6">~</div> |
13 | <InputNumber | 10 | <InputNumber |
14 | placeholder="最大值" | 11 | placeholder="最大值" |
15 | :disabled="$props.disabled" | 12 | :disabled="$props.disabled" |
16 | :value="getValue.max!" | 13 | :value="getValue.max!" |
17 | - style="width: 38%" | ||
18 | @change="(value) => emitChange(value, 'max')" | 14 | @change="(value) => emitChange(value, 'max')" |
19 | /> | 15 | /> |
20 | </div> | 16 | </div> |
1 | +<script setup lang="ts"> | ||
2 | + import { | ||
3 | + computed, | ||
4 | + CSSProperties, | ||
5 | + nextTick, | ||
6 | + onBeforeUnmount, | ||
7 | + ref, | ||
8 | + TransitionProps, | ||
9 | + unref, | ||
10 | + watch, | ||
11 | + } from 'vue'; | ||
12 | + import { addClass } from './styleClass'; | ||
13 | + import { ThumbReact } from './type'; | ||
14 | + import { removeClass } from '/@/utils/domUtils'; | ||
15 | + | ||
16 | + const calcThumbStyle = (targetElement: HTMLElement | null | undefined): ThumbReact => | ||
17 | + targetElement | ||
18 | + ? { | ||
19 | + left: targetElement.offsetLeft, | ||
20 | + right: | ||
21 | + (targetElement.parentElement!.clientWidth as number) - | ||
22 | + targetElement.clientWidth - | ||
23 | + targetElement.offsetLeft, | ||
24 | + width: targetElement.clientWidth, | ||
25 | + } | ||
26 | + : null; | ||
27 | + | ||
28 | + const toPX = (value?: number) => (value !== undefined ? `${value}px` : undefined); | ||
29 | + | ||
30 | + const props = defineProps<{ | ||
31 | + value?: string | number; | ||
32 | + getValueIndex: (value: string | number) => number; | ||
33 | + containerRef?: HTMLDivElement; | ||
34 | + prefixCls: string; | ||
35 | + direction?: 'rtl' | 'ltr'; | ||
36 | + motionName: string; | ||
37 | + }>(); | ||
38 | + | ||
39 | + const emits = defineEmits(['motionStart', 'motionEnd']); | ||
40 | + | ||
41 | + const thumbRef = ref<HTMLDivElement>(); | ||
42 | + | ||
43 | + const findValueElement = (val: string | number) => { | ||
44 | + const { getValueIndex, prefixCls } = props; | ||
45 | + const index = getValueIndex(val); | ||
46 | + const ele = unref(props.containerRef)?.querySelectorAll<HTMLDivElement>(`.${prefixCls}-item`)?.[ | ||
47 | + index | ||
48 | + ]; | ||
49 | + | ||
50 | + return ele?.offsetParent && ele; | ||
51 | + }; | ||
52 | + | ||
53 | + const prevStyle = ref<ThumbReact>(null); | ||
54 | + const nextStyle = ref<ThumbReact>(null); | ||
55 | + | ||
56 | + watch( | ||
57 | + () => props.value, | ||
58 | + (value, prevValue) => { | ||
59 | + const prev = findValueElement(prevValue as number); | ||
60 | + const next = findValueElement(value as number); | ||
61 | + | ||
62 | + const calcPrevStyle = calcThumbStyle(prev); | ||
63 | + const calcNextStyle = calcThumbStyle(next); | ||
64 | + | ||
65 | + prevStyle.value = calcPrevStyle; | ||
66 | + nextStyle.value = calcNextStyle; | ||
67 | + | ||
68 | + prev && next ? emits('motionStart') : emits('motionEnd'); | ||
69 | + }, | ||
70 | + { flush: 'post' } | ||
71 | + ); | ||
72 | + | ||
73 | + const thumbStart = computed(() => { | ||
74 | + const { direction } = props; | ||
75 | + | ||
76 | + return direction === 'rtl' | ||
77 | + ? toPX(-(prevStyle.value?.right as number)) | ||
78 | + : toPX(prevStyle.value?.left as number); | ||
79 | + }); | ||
80 | + | ||
81 | + const thumbActive = computed(() => { | ||
82 | + const { direction } = props; | ||
83 | + | ||
84 | + return direction === 'rtl' | ||
85 | + ? toPX(-(nextStyle.value?.right as number)) | ||
86 | + : toPX(nextStyle.value?.left as number); | ||
87 | + }); | ||
88 | + | ||
89 | + let timeId: any; | ||
90 | + const onAppearStart: TransitionProps['onBeforeEnter'] = (el: HTMLDivElement) => { | ||
91 | + clearTimeout(timeId); | ||
92 | + nextTick(() => { | ||
93 | + if (el) { | ||
94 | + el.style.transform = `translateX(var(--thumb-start-left))`; | ||
95 | + el.style.width = `var(--thumb-start-width)`; | ||
96 | + } | ||
97 | + }); | ||
98 | + }; | ||
99 | + | ||
100 | + const onAppearActive: TransitionProps['onEnter'] = (el: HTMLDivElement) => { | ||
101 | + timeId = setTimeout(() => { | ||
102 | + if (el) { | ||
103 | + addClass(el, `${props.motionName}-appear-active`); | ||
104 | + el.style.transform = `translateX(var(--thumb-active-left))`; | ||
105 | + el.style.width = `var(--thumb-active-width)`; | ||
106 | + } | ||
107 | + }); | ||
108 | + }; | ||
109 | + | ||
110 | + const onAppearEnd: TransitionProps['onAfterEnter'] = (el: HTMLDivElement) => { | ||
111 | + prevStyle.value = null; | ||
112 | + nextStyle.value = null; | ||
113 | + if (el) { | ||
114 | + el.style.transform = ''; | ||
115 | + el.style.width = ''; | ||
116 | + removeClass(el, `${props.motionName}-appear-active`); | ||
117 | + } | ||
118 | + emits('motionEnd'); | ||
119 | + }; | ||
120 | + | ||
121 | + const mergedStyle = computed( | ||
122 | + () => | ||
123 | + ({ | ||
124 | + '--thumb-start-left': thumbStart.value, | ||
125 | + '--thumb-start-width': toPX(prevStyle.value?.width), | ||
126 | + '--thumb-active-left': thumbActive.value, | ||
127 | + '--thumb-active-width': toPX(nextStyle.value?.width), | ||
128 | + } as CSSProperties) | ||
129 | + ); | ||
130 | + | ||
131 | + onBeforeUnmount(() => { | ||
132 | + clearTimeout(timeId); | ||
133 | + }); | ||
134 | + | ||
135 | + const motionProps = computed(() => { | ||
136 | + return { | ||
137 | + ref: thumbRef, | ||
138 | + style: mergedStyle.value, | ||
139 | + class: `${props.prefixCls}-thumb`, | ||
140 | + }; | ||
141 | + }); | ||
142 | +</script> | ||
143 | + | ||
144 | +<template> | ||
145 | + <Transition | ||
146 | + appear | ||
147 | + @before-enter="onAppearStart" | ||
148 | + @enter="onAppearActive" | ||
149 | + @after-enter="onAppearEnd" | ||
150 | + > | ||
151 | + <div v-if="prevStyle && nextStyle" v-bind="motionProps"></div> | ||
152 | + </Transition> | ||
153 | +</template> |
1 | +<script setup lang="ts"> | ||
2 | + import { computed, ref } from 'vue'; | ||
3 | + import MotionThumb from './MotionThumb.vue'; | ||
4 | + import { SegmentedBaseOption, SegmentedOption } from './type'; | ||
5 | + | ||
6 | + const props = withDefaults( | ||
7 | + defineProps<{ | ||
8 | + block?: boolean; | ||
9 | + disabled?: boolean; | ||
10 | + options?: string[] | number[] | SegmentedOption[]; | ||
11 | + value?: string | number; | ||
12 | + motionName?: string; | ||
13 | + }>(), | ||
14 | + { | ||
15 | + options: () => [], | ||
16 | + motionName: 'thumb-motion', | ||
17 | + } | ||
18 | + ); | ||
19 | + | ||
20 | + const slots = defineSlots<{ label?(props: SegmentedBaseOption): any }>(); | ||
21 | + | ||
22 | + const prefixCls = 'segmented'; | ||
23 | + | ||
24 | + const thumbShow = ref(false); | ||
25 | + | ||
26 | + const rootRef = ref<HTMLDivElement>(); | ||
27 | + | ||
28 | + const getOptions = computed<SegmentedBaseOption[]>(() => { | ||
29 | + const { options } = props; | ||
30 | + return (options as SegmentedOption[]).map((item) => { | ||
31 | + return { | ||
32 | + value: item?.value || item, | ||
33 | + disabled: item?.disabled || false, | ||
34 | + payload: item?.payload || {}, | ||
35 | + title: item?.title || item, | ||
36 | + className: item?.className || '', | ||
37 | + } as SegmentedBaseOption; | ||
38 | + }); | ||
39 | + }); | ||
40 | + | ||
41 | + const emits = defineEmits(['change', 'update:value']); | ||
42 | + | ||
43 | + const handleChange = (value: string | number) => { | ||
44 | + emits('update:value', value); | ||
45 | + emits('change', value); | ||
46 | + }; | ||
47 | +</script> | ||
48 | + | ||
49 | +<template> | ||
50 | + <div :class="prefixCls" ref="rootRef"> | ||
51 | + <div :class="`${prefixCls}-group`"> | ||
52 | + <MotionThumb | ||
53 | + :containerRef="rootRef" | ||
54 | + :prefix-cls="prefixCls" | ||
55 | + :value="value" | ||
56 | + :motion-name="`${prefixCls}-${motionName}`" | ||
57 | + direction="ltr" | ||
58 | + :get-value-index="(val) => getOptions.findIndex((item) => item.value === val)" | ||
59 | + @motion-start="thumbShow = true" | ||
60 | + @motion-end="thumbShow = false" | ||
61 | + /> | ||
62 | + <label | ||
63 | + v-for="item in getOptions" | ||
64 | + :key="item.value" | ||
65 | + :class="[ | ||
66 | + `${prefixCls}-item`, | ||
67 | + value === item.value && !thumbShow && `${prefixCls}-item-selected`, | ||
68 | + (disabled || item.disabled) && `${prefixCls}-item-disabled`, | ||
69 | + ]" | ||
70 | + @click="!disabled && !item.disabled && handleChange(item.value)" | ||
71 | + > | ||
72 | + <input type="radio" class="absolute pointer-events-none w-0 h-0 opacity-0" /> | ||
73 | + <slot v-if="slots.label" name="label" v-bind="item"></slot> | ||
74 | + <template v-else> | ||
75 | + <div :class="`${prefixCls}-item-label`"> {{ item.title }}</div> | ||
76 | + </template> | ||
77 | + </label> | ||
78 | + </div> | ||
79 | + </div> | ||
80 | +</template> | ||
81 | + | ||
82 | +<style lang="less"> | ||
83 | + .segmented { | ||
84 | + box-sizing: border-box; | ||
85 | + padding: 2px; | ||
86 | + color: #000000a6; | ||
87 | + font-size: 14px; | ||
88 | + display: inline-block; | ||
89 | + border-radius: 4px; | ||
90 | + background-color: #f5f5f5; | ||
91 | + transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); | ||
92 | + | ||
93 | + &-block { | ||
94 | + display: flex; | ||
95 | + } | ||
96 | + | ||
97 | + &-block.segmented-item { | ||
98 | + flex: 1; | ||
99 | + } | ||
100 | + | ||
101 | + &-group { | ||
102 | + position: relative; | ||
103 | + display: flex; | ||
104 | + align-items: stretch; | ||
105 | + justify-content: flex-start; | ||
106 | + width: 100%; | ||
107 | + border-radius: inherit; | ||
108 | + transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); | ||
109 | + } | ||
110 | + | ||
111 | + &-item { | ||
112 | + position: relative; | ||
113 | + cursor: pointer; | ||
114 | + border-radius: inherit; | ||
115 | + text-align: center; | ||
116 | + transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); | ||
117 | + | ||
118 | + &-label { | ||
119 | + min-height: 28px; | ||
120 | + line-height: 28px; | ||
121 | + padding: 0 11px; | ||
122 | + overflow: hidden; | ||
123 | + white-space: nowrap; | ||
124 | + text-overflow: ellipsis; | ||
125 | + } | ||
126 | + | ||
127 | + &::after { | ||
128 | + content: ''; | ||
129 | + position: absolute; | ||
130 | + width: 100%; | ||
131 | + height: 100%; | ||
132 | + top: 0; | ||
133 | + inset-inline-start: 0; | ||
134 | + border-radius: inherit; | ||
135 | + transition: background-color 0.2s; | ||
136 | + } | ||
137 | + | ||
138 | + &:hover:not(.segmented-item-selected):not(.segmented-item-disabled) { | ||
139 | + color: #000000e0; | ||
140 | + | ||
141 | + &::after { | ||
142 | + background-color: rgba(0, 0, 0, 0.06); | ||
143 | + } | ||
144 | + } | ||
145 | + | ||
146 | + &-disabled { | ||
147 | + cursor: not-allowed; | ||
148 | + color: rgba(0, 0, 0, 0.25); | ||
149 | + } | ||
150 | + | ||
151 | + &-selected { | ||
152 | + background-color: #fff; | ||
153 | + | ||
154 | + ::after { | ||
155 | + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02), | ||
156 | + 0 2px 4px 0 rgba(0, 0, 0, 0.02); | ||
157 | + } | ||
158 | + } | ||
159 | + | ||
160 | + &-selected:not(.segmented-item-disabled) { | ||
161 | + color: rgba(0, 0, 0, 0.88); | ||
162 | + } | ||
163 | + } | ||
164 | + | ||
165 | + &-thumb { | ||
166 | + position: absolute; | ||
167 | + inset-block-start: 0; | ||
168 | + inset-inline-start: 0; | ||
169 | + width: 0; | ||
170 | + height: 100%; | ||
171 | + background-color: #fff; | ||
172 | + | ||
173 | + &-motion-appear-active { | ||
174 | + transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), | ||
175 | + width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); | ||
176 | + will-change: transform, width; | ||
177 | + } | ||
178 | + | ||
179 | + & ~ .segmented-item:not(.segmented-item-selected):not(.segmented-item-disabled)::after { | ||
180 | + background-color: transparent; | ||
181 | + } | ||
182 | + } | ||
183 | + } | ||
184 | +</style> |
1 | +export { default as Segmented } from './Segmented.vue'; |
1 | +export function hasClass(node: HTMLElement, className: string) { | ||
2 | + if (node.classList) { | ||
3 | + return node.classList.contains(className); | ||
4 | + } | ||
5 | + const originClass = node.className; | ||
6 | + return ` ${originClass} `.indexOf(` ${className} `) > -1; | ||
7 | +} | ||
8 | + | ||
9 | +export function addClass(node: HTMLElement, className: string) { | ||
10 | + if (node.classList) { | ||
11 | + node.classList.add(className); | ||
12 | + } else { | ||
13 | + if (!hasClass(node, className)) { | ||
14 | + node.className = `${node.className} ${className}`; | ||
15 | + } | ||
16 | + } | ||
17 | +} |
1 | +export interface SegmentedBaseOption { | ||
2 | + value: string | number; | ||
3 | + disabled?: boolean; | ||
4 | + payload?: any; | ||
5 | + title?: string; | ||
6 | + className?: string; | ||
7 | +} | ||
8 | + | ||
9 | +export interface SegmentedOption extends SegmentedBaseOption { | ||
10 | + label?: VueNode | ((option: SegmentedBaseOption) => VueNode); | ||
11 | +} | ||
12 | + | ||
13 | +export type ThumbReact = { | ||
14 | + left: number; | ||
15 | + right: number; | ||
16 | + width: number; | ||
17 | +} | null; |
src/components/Form/src/components/StructForm/StructForm.vue
deleted
100644 → 0
1 | -<script lang="ts"> | ||
2 | - export default { | ||
3 | - inheritAttrs: false, | ||
4 | - }; | ||
5 | -</script> | ||
6 | -<script lang="ts" setup> | ||
7 | - import StructFormModel from './StructFormModel.vue'; | ||
8 | - import { useModal } from '/@/components/Modal'; | ||
9 | - import { PlusOutlined } from '@ant-design/icons-vue'; | ||
10 | - import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | ||
11 | - import { computed, unref } from 'vue'; | ||
12 | - import { buildUUID } from '/@/utils/uuid'; | ||
13 | - import { Divider, Button } from 'ant-design-vue'; | ||
14 | - import { OpenModalMode, OpenModalParams, StructRecord } from './type'; | ||
15 | - import { cloneDeep } from 'lodash-es'; | ||
16 | - import { isArray } from '/@/utils/is'; | ||
17 | - import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | ||
18 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
19 | - | ||
20 | - const emit = defineEmits(['update:value']); | ||
21 | - | ||
22 | - const props = withDefaults( | ||
23 | - defineProps<{ | ||
24 | - value: ModelOfMatterParams[]; | ||
25 | - disabled: boolean; | ||
26 | - hasStructForm?: boolean; | ||
27 | - hiddenAccessMode?: boolean; | ||
28 | - }>(), | ||
29 | - { | ||
30 | - value: () => [], | ||
31 | - hiddenAccessMode: false, | ||
32 | - hasStructForm: false, | ||
33 | - } | ||
34 | - ); | ||
35 | - | ||
36 | - const getValue = computed<StructRecord[]>(() => { | ||
37 | - const { value } = props; | ||
38 | - | ||
39 | - return (isArray(value) ? value : []).map((item) => { | ||
40 | - return { | ||
41 | - ...(item as StructRecord), | ||
42 | - ...((item as StructRecord).id ? {} : { id: buildUUID() }), | ||
43 | - }; | ||
44 | - }); | ||
45 | - }); | ||
46 | - | ||
47 | - const [registerModal, { openModal }] = useModal(); | ||
48 | - | ||
49 | - const handleCreateParams = () => { | ||
50 | - openModal(true, { | ||
51 | - mode: OpenModalMode.CREATE, | ||
52 | - } as OpenModalParams); | ||
53 | - }; | ||
54 | - | ||
55 | - const handleUpdate = (value: StructRecord) => { | ||
56 | - openModal(true, { | ||
57 | - mode: OpenModalMode.UPDATE, | ||
58 | - record: { | ||
59 | - ...value, | ||
60 | - [FormField.HAS_STRUCT_FROM]: value?.dataType?.type === DataTypeEnum.STRUCT, | ||
61 | - }, | ||
62 | - } as OpenModalParams); | ||
63 | - }; | ||
64 | - | ||
65 | - const handleDelete = (value: StructRecord) => { | ||
66 | - const index = unref(getValue).findIndex((item) => item.id === value.id); | ||
67 | - const _value = cloneDeep(unref(getValue)); | ||
68 | - _value.splice(index, 1); | ||
69 | - emit('update:value', _value); | ||
70 | - }; | ||
71 | - | ||
72 | - const handleSaveStruct = (mode: OpenModalMode, value: StructRecord) => { | ||
73 | - const _value = cloneDeep(unref(getValue)); | ||
74 | - | ||
75 | - if (mode === OpenModalMode.UPDATE) { | ||
76 | - const index = unref(getValue).findIndex((item) => item.id === value.id); | ||
77 | - ~index && _value.splice(index, 1, value); | ||
78 | - } else { | ||
79 | - _value.push(value); | ||
80 | - } | ||
81 | - | ||
82 | - emit('update:value', _value); | ||
83 | - }; | ||
84 | -</script> | ||
85 | - | ||
86 | -<template> | ||
87 | - <section> | ||
88 | - <div class="text-blue-500 cursor-pointer"> | ||
89 | - <section> | ||
90 | - <div | ||
91 | - class="flex bg-blue-50 mb-2 p-2 text-gray-500 justify-between items-center" | ||
92 | - v-for="item in getValue" | ||
93 | - :key="item.id" | ||
94 | - > | ||
95 | - <div>参数名称: {{ item.functionName }}</div> | ||
96 | - <div class="flex"> | ||
97 | - <Button class="!p-0" type="link" @click="handleUpdate(item)"> | ||
98 | - <span>{{ disabled ? '查看' : '编辑' }}</span> | ||
99 | - </Button> | ||
100 | - <Divider type="vertical" /> | ||
101 | - <Button :disabled="disabled" class="!p-0" type="link" @click="handleDelete(item)"> | ||
102 | - <span>删除</span> | ||
103 | - </Button> | ||
104 | - </div> | ||
105 | - </div> | ||
106 | - </section> | ||
107 | - <div :class="$props.disabled && 'text-gray-400'"> | ||
108 | - <span class="mr-2"> | ||
109 | - <PlusOutlined /> | ||
110 | - </span> | ||
111 | - <span @click="!disabled && handleCreateParams()">增加参数</span> | ||
112 | - </div> | ||
113 | - </div> | ||
114 | - <StructFormModel | ||
115 | - :has-struct-form="hasStructForm!" | ||
116 | - :hidden-access-mode="hiddenAccessMode" | ||
117 | - :disabled="disabled" | ||
118 | - :value-list="getValue" | ||
119 | - @register="registerModal" | ||
120 | - @submit="handleSaveStruct" | ||
121 | - /> | ||
122 | - </section> | ||
123 | -</template> | ||
124 | - | ||
125 | -<style lang="less" scoped></style> |
src/components/Form/src/components/StructForm/StructFormModel.vue
deleted
100644 → 0
1 | -<script lang="ts"> | ||
2 | - export default { | ||
3 | - inheritAttrs: false, | ||
4 | - }; | ||
5 | -</script> | ||
6 | -<script lang="ts" setup> | ||
7 | - import { BasicForm, useForm } from '/@/components/Form'; | ||
8 | - import { formSchemas } from './config'; | ||
9 | - import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
10 | - import { OpenModalMode, OpenModalParams, StructRecord } from './type'; | ||
11 | - import { computed, ref, unref } from 'vue'; | ||
12 | - import { transfromToStructJSON } from './util'; | ||
13 | - import { cloneDeep } from 'lodash-es'; | ||
14 | - import { DataType, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
15 | - import { isArray } from '/@/utils/is'; | ||
16 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
17 | - import EnumList from './EnumList.vue'; | ||
18 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
19 | - | ||
20 | - const modalReceiveRecord = ref<OpenModalParams>({ | ||
21 | - mode: OpenModalMode.CREATE, | ||
22 | - }); | ||
23 | - | ||
24 | - const props = defineProps<{ | ||
25 | - disabled: boolean; | ||
26 | - hasStructForm: boolean; | ||
27 | - valueList: StructRecord[]; | ||
28 | - hiddenAccessMode: boolean; | ||
29 | - }>(); | ||
30 | - | ||
31 | - const enumListRef = ref<InstanceType<typeof EnumList>>(); | ||
32 | - | ||
33 | - const emit = defineEmits(['register', 'submit']); | ||
34 | - | ||
35 | - const { createMessage } = useMessage(); | ||
36 | - | ||
37 | - const [register, { validate, setFieldsValue, setProps }] = useForm({ | ||
38 | - labelWidth: 100, | ||
39 | - actionColOptions: { | ||
40 | - span: 14, | ||
41 | - }, | ||
42 | - disabled: props.disabled, | ||
43 | - showResetButton: false, | ||
44 | - submitOnReset: false, | ||
45 | - showActionButtonGroup: false, | ||
46 | - }); | ||
47 | - | ||
48 | - const getFormSchemas = computed(() => { | ||
49 | - const { hasStructForm, hiddenAccessMode } = props; | ||
50 | - return formSchemas({ | ||
51 | - hasStructForm, | ||
52 | - hiddenAccessMode, | ||
53 | - }); | ||
54 | - }); | ||
55 | - | ||
56 | - const [registerModal, { closeModal }] = useModalInner((record: OpenModalParams) => { | ||
57 | - modalReceiveRecord.value = record; | ||
58 | - const data = record.record || {}; | ||
59 | - const { dataType = {} } = data! as StructJSON; | ||
60 | - const { specs = {}, type, specsList } = dataType as DataType; | ||
61 | - | ||
62 | - if (record.record) { | ||
63 | - const value = { | ||
64 | - type, | ||
65 | - ...data, | ||
66 | - ...(isArray(specs) ? { specs } : { ...specs }), | ||
67 | - enumList: type === DataTypeEnum.ENUM ? specsList : [], | ||
68 | - }; | ||
69 | - | ||
70 | - setFieldsValue(value); | ||
71 | - } | ||
72 | - setProps({ disabled: props.disabled }); | ||
73 | - }); | ||
74 | - | ||
75 | - const validateRepeat = (value: StructRecord, valueList: StructRecord[]) => { | ||
76 | - return valueList.filter((item) => item.identifier === value.identifier).length >= 1; | ||
77 | - }; | ||
78 | - | ||
79 | - const handleSubmit = async () => { | ||
80 | - try { | ||
81 | - const _value = await validate(); | ||
82 | - await unref(enumListRef)?.validate?.(); | ||
83 | - let structJSON = transfromToStructJSON(_value, unref(enumListRef)?.getFieldsValue?.() || []); | ||
84 | - const value = { | ||
85 | - ...structJSON, | ||
86 | - ...(unref(modalReceiveRecord)?.record?.id | ||
87 | - ? { id: unref(modalReceiveRecord)?.record?.id } | ||
88 | - : {}), | ||
89 | - }; | ||
90 | - | ||
91 | - if ( | ||
92 | - unref(modalReceiveRecord).mode === OpenModalMode.CREATE && | ||
93 | - validateRepeat(_value, props.valueList) | ||
94 | - ) { | ||
95 | - createMessage.error('存在一致的标识符'); | ||
96 | - return; | ||
97 | - } | ||
98 | - emit('submit', unref(modalReceiveRecord).mode, cloneDeep(value)); | ||
99 | - closeModal(); | ||
100 | - } catch (error) {} | ||
101 | - }; | ||
102 | -</script> | ||
103 | - | ||
104 | -<template> | ||
105 | - <BasicModal | ||
106 | - @register="registerModal" | ||
107 | - :title="modalReceiveRecord.mode === OpenModalMode.CREATE ? '创建参数' : '编辑参数'" | ||
108 | - :width="800" | ||
109 | - @ok="handleSubmit" | ||
110 | - destroy-on-close | ||
111 | - :show-ok-btn="!$props.disabled" | ||
112 | - > | ||
113 | - <BasicForm @register="register" :schemas="getFormSchemas"> | ||
114 | - <template #EnumList="{ field, model }"> | ||
115 | - <EnumList ref="enumListRef" :value="model[field]" :disabled="disabled" /> | ||
116 | - </template> | ||
117 | - </BasicForm> | ||
118 | - </BasicModal> | ||
119 | -</template> | ||
120 | - | ||
121 | -<style lang="less" scoped></style> |
src/components/Form/src/components/StructForm/config.ts
deleted
100644 → 0
1 | -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | ||
2 | -import { findDictItemByCode } from '/@/api/system/dict'; | ||
3 | -import { Rule } from '/@/components/Form'; | ||
4 | -import { FormSchema } from '/@/components/Table'; | ||
5 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
6 | -import { isNullOrUnDef } from '/@/utils/is'; | ||
7 | -import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | ||
8 | - | ||
9 | -export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => { | ||
10 | - value = value || {}; | ||
11 | - const { min, max } = value; | ||
12 | - if (min > max) { | ||
13 | - return Promise.reject('最大值小于最小值'); | ||
14 | - } | ||
15 | - return Promise.resolve(); | ||
16 | -}; | ||
17 | - | ||
18 | -export const validateJSON = (_rule, value = [] as ModelOfMatterParams[], _callback) => { | ||
19 | - if (value.length) { | ||
20 | - return Promise.resolve(); | ||
21 | - } | ||
22 | - return Promise.reject('JSON对象不能为空'); | ||
23 | -}; | ||
24 | - | ||
25 | -interface StructFormSchemasParmasType { | ||
26 | - hasStructForm: boolean; | ||
27 | - hiddenAccessMode: boolean; | ||
28 | - isTcp?: boolean; | ||
29 | -} | ||
30 | - | ||
31 | -//功能名称和标识符 输入框不能包含逗号 | ||
32 | -const validateExcludeComma = (field: string, errorName: string): Rule[] => { | ||
33 | - return [ | ||
34 | - { | ||
35 | - required: true, | ||
36 | - trigger: 'blur', | ||
37 | - validator: () => { | ||
38 | - const reg = /[,,]+/; | ||
39 | - if (reg.test(field)) { | ||
40 | - return Promise.reject(`${errorName}不能包含逗号`); | ||
41 | - } | ||
42 | - return Promise.resolve(); | ||
43 | - }, | ||
44 | - }, | ||
45 | - ]; | ||
46 | -}; | ||
47 | - | ||
48 | -export const formSchemas = ({ | ||
49 | - hasStructForm, | ||
50 | - hiddenAccessMode, | ||
51 | - isTcp = false, | ||
52 | -}: StructFormSchemasParmasType): FormSchema[] => { | ||
53 | - return [ | ||
54 | - { | ||
55 | - field: FormField.FUNCTION_NAME, | ||
56 | - label: '功能名称', | ||
57 | - required: true, | ||
58 | - component: 'Input', | ||
59 | - colProps: { | ||
60 | - span: 18, | ||
61 | - }, | ||
62 | - componentProps: { | ||
63 | - maxLength: 32, | ||
64 | - placeholder: '请输入功能名称', | ||
65 | - }, | ||
66 | - dynamicRules: ({ values }) => { | ||
67 | - return [ | ||
68 | - { required: true, message: '请输入功能名称' }, | ||
69 | - ...validateExcludeComma(values[FormField.FUNCTION_NAME], '功能名称'), | ||
70 | - ]; | ||
71 | - }, | ||
72 | - }, | ||
73 | - { | ||
74 | - field: FormField.IDENTIFIER, | ||
75 | - label: '标识符', | ||
76 | - required: true, | ||
77 | - component: 'Input', | ||
78 | - colProps: { | ||
79 | - span: 18, | ||
80 | - }, | ||
81 | - componentProps: { | ||
82 | - maxLength: 128, | ||
83 | - placeholder: '请输入标识符', | ||
84 | - }, | ||
85 | - dynamicRules: ({ values }) => { | ||
86 | - return [ | ||
87 | - { required: true, message: '请输入标识符' }, | ||
88 | - ...validateExcludeComma(values[FormField.IDENTIFIER], '标识符'), | ||
89 | - ]; | ||
90 | - }, | ||
91 | - }, | ||
92 | - { | ||
93 | - field: FormField.HAS_STRUCT_FROM, | ||
94 | - label: '是否已存在结构体', | ||
95 | - component: 'Input', | ||
96 | - ifShow: false, | ||
97 | - }, | ||
98 | - { | ||
99 | - field: FormField.TYPE, | ||
100 | - label: '数据类型', | ||
101 | - required: true, | ||
102 | - component: 'ApiSelect', | ||
103 | - colProps: { | ||
104 | - span: 9, | ||
105 | - }, | ||
106 | - defaultValue: 'INT', | ||
107 | - componentProps: ({ formActionType }) => { | ||
108 | - const { setFieldsValue } = formActionType; | ||
109 | - return { | ||
110 | - placeholder: '请选择数据类型', | ||
111 | - api: async (params: Recordable) => { | ||
112 | - try { | ||
113 | - const record = await findDictItemByCode(params); | ||
114 | - | ||
115 | - if (isTcp) { | ||
116 | - // TCP 产品 属性可创建范围 | ||
117 | - return record.filter((item) => | ||
118 | - [ | ||
119 | - DataTypeEnum.BOOL, | ||
120 | - DataTypeEnum.NUMBER_DOUBLE, | ||
121 | - DataTypeEnum.NUMBER_INT, | ||
122 | - DataTypeEnum.STRING, | ||
123 | - ].includes(item.itemValue as DataTypeEnum) | ||
124 | - ); | ||
125 | - } | ||
126 | - | ||
127 | - return hasStructForm | ||
128 | - ? record.filter((item) => item.itemValue !== DataTypeEnum.STRUCT) | ||
129 | - : record; | ||
130 | - } catch (error) { | ||
131 | - return []; | ||
132 | - } | ||
133 | - }, | ||
134 | - params: { | ||
135 | - dictCode: 'data_type', | ||
136 | - }, | ||
137 | - labelField: 'itemText', | ||
138 | - valueField: 'itemValue', | ||
139 | - getPopupContainer: () => document.body, | ||
140 | - onChange: (value: string) => { | ||
141 | - if (value == DataTypeEnum.STRUCT) { | ||
142 | - setFieldsValue({ [FormField.SPECS_LIST]: [], [FormField.HAS_STRUCT_FROM]: true }); | ||
143 | - } | ||
144 | - }, | ||
145 | - }; | ||
146 | - }, | ||
147 | - }, | ||
148 | - { | ||
149 | - field: FormField.ENUM_LIST, | ||
150 | - component: 'Input', | ||
151 | - label: '枚举', | ||
152 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.ENUM, | ||
153 | - slot: 'EnumList', | ||
154 | - colProps: { | ||
155 | - span: 24, | ||
156 | - }, | ||
157 | - }, | ||
158 | - { | ||
159 | - field: FormField.VALUE_RANGE, | ||
160 | - label: '取值范围', | ||
161 | - component: 'CustomMinMaxInput', | ||
162 | - valueField: 'value', | ||
163 | - changeEvent: 'update:value', | ||
164 | - colProps: { | ||
165 | - span: 18, | ||
166 | - }, | ||
167 | - ifShow: ({ values }) => | ||
168 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | ||
169 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | ||
170 | - rules: [{ validator: validateValueRange }], | ||
171 | - }, | ||
172 | - { | ||
173 | - field: FormField.STEP, | ||
174 | - label: '步长', | ||
175 | - component: 'InputNumber', | ||
176 | - colProps: { | ||
177 | - span: 18, | ||
178 | - }, | ||
179 | - componentProps: { | ||
180 | - maxLength: 255, | ||
181 | - placeholder: '请输入步长', | ||
182 | - min: 1, | ||
183 | - formatter: (value: number | string) => { | ||
184 | - return value ? Math.floor(Number(value)) : value; | ||
185 | - }, | ||
186 | - }, | ||
187 | - ifShow: ({ values }) => | ||
188 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | ||
189 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | ||
190 | - dynamicRules: ({ model }) => { | ||
191 | - const valueRange = model[FormField.VALUE_RANGE] || {}; | ||
192 | - const { min, max } = valueRange; | ||
193 | - const step = model[FormField.STEP]; | ||
194 | - return [ | ||
195 | - { | ||
196 | - validator: () => { | ||
197 | - if ([min, max].every(isNullOrUnDef)) return Promise.resolve(); | ||
198 | - if (step > max - min) { | ||
199 | - return Promise.reject('步长不能大于取值范围的差值'); | ||
200 | - } | ||
201 | - return Promise.resolve(); | ||
202 | - }, | ||
203 | - }, | ||
204 | - ]; | ||
205 | - }, | ||
206 | - }, | ||
207 | - { | ||
208 | - field: FormField.UNIT_NAME, | ||
209 | - label: '单位名称', | ||
210 | - component: 'Input', | ||
211 | - show: false, | ||
212 | - }, | ||
213 | - { | ||
214 | - field: FormField.UNIT, | ||
215 | - label: '单位', | ||
216 | - component: 'ApiSelect', | ||
217 | - colProps: { | ||
218 | - span: 9, | ||
219 | - }, | ||
220 | - componentProps: ({ formActionType }) => { | ||
221 | - const { setFieldsValue } = formActionType; | ||
222 | - return { | ||
223 | - placeholder: '请选择单位', | ||
224 | - api: async (params) => { | ||
225 | - const list = await findDictItemByCode(params); | ||
226 | - list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`)); | ||
227 | - return list; | ||
228 | - }, | ||
229 | - params: { | ||
230 | - dictCode: 'attribute_unit', | ||
231 | - }, | ||
232 | - labelInValue: true, | ||
233 | - labelField: 'itemText', | ||
234 | - valueField: 'itemValue', | ||
235 | - onChange(_, record: Record<'label' | 'value', string>) { | ||
236 | - if (record) { | ||
237 | - const { label } = record; | ||
238 | - setFieldsValue({ [FormField.UNIT_NAME]: label }); | ||
239 | - } | ||
240 | - }, | ||
241 | - getPopupContainer: () => document.body, | ||
242 | - showSearch: true, | ||
243 | - filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | ||
244 | - let { label, value } = option; | ||
245 | - label = label.toLowerCase(); | ||
246 | - value = value.toLowerCase(); | ||
247 | - inputValue = inputValue.toLowerCase(); | ||
248 | - return label.includes(inputValue) || value.includes(inputValue); | ||
249 | - }, | ||
250 | - }; | ||
251 | - }, | ||
252 | - ifShow: ({ values }) => | ||
253 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | ||
254 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | ||
255 | - }, | ||
256 | - { | ||
257 | - field: FormField.BOOL_CLOSE, | ||
258 | - component: 'Input', | ||
259 | - required: true, | ||
260 | - label: '0 -', | ||
261 | - colProps: { | ||
262 | - span: 18, | ||
263 | - }, | ||
264 | - componentProps: { | ||
265 | - placeholder: '如:关', | ||
266 | - }, | ||
267 | - defaultValue: '关', | ||
268 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL, | ||
269 | - dynamicRules: ({ model }) => { | ||
270 | - const close = model[FormField.BOOL_CLOSE]; | ||
271 | - const open = model[FormField.BOOL_OPEN]; | ||
272 | - return [ | ||
273 | - { | ||
274 | - required: true, | ||
275 | - }, | ||
276 | - { | ||
277 | - validator() { | ||
278 | - if (open === close) return Promise.reject('布尔值不能相同'); | ||
279 | - return Promise.resolve(); | ||
280 | - }, | ||
281 | - }, | ||
282 | - ]; | ||
283 | - }, | ||
284 | - }, | ||
285 | - { | ||
286 | - field: FormField.BOOL_OPEN, | ||
287 | - component: 'Input', | ||
288 | - required: true, | ||
289 | - label: '1 -', | ||
290 | - colProps: { | ||
291 | - span: 18, | ||
292 | - }, | ||
293 | - componentProps: { | ||
294 | - placeholder: '如:开', | ||
295 | - }, | ||
296 | - defaultValue: '开', | ||
297 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL, | ||
298 | - dynamicRules: ({ model }) => { | ||
299 | - const close = model[FormField.BOOL_CLOSE]; | ||
300 | - const open = model[FormField.BOOL_OPEN]; | ||
301 | - return [ | ||
302 | - { | ||
303 | - required: true, | ||
304 | - }, | ||
305 | - { | ||
306 | - validator() { | ||
307 | - if (open === close) return Promise.reject('布尔值不能相同'); | ||
308 | - return Promise.resolve(); | ||
309 | - }, | ||
310 | - }, | ||
311 | - ]; | ||
312 | - }, | ||
313 | - }, | ||
314 | - { | ||
315 | - field: FormField.LENGTH, | ||
316 | - component: 'Input', | ||
317 | - required: true, | ||
318 | - label: '数据长度', | ||
319 | - defaultValue: '10240', | ||
320 | - colProps: { | ||
321 | - span: 8, | ||
322 | - }, | ||
323 | - componentProps: { | ||
324 | - placeholder: '请输入数据长度', | ||
325 | - }, | ||
326 | - renderComponentContent: () => { | ||
327 | - return { | ||
328 | - suffix: () => '字节', | ||
329 | - }; | ||
330 | - }, | ||
331 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRING, | ||
332 | - }, | ||
333 | - { | ||
334 | - field: FormField.EXTENSION_DESC, | ||
335 | - component: 'ExtendDesc', | ||
336 | - label: '扩展描述', | ||
337 | - valueField: 'value', | ||
338 | - changeEvent: 'update:value', | ||
339 | - ifShow: isTcp, | ||
340 | - colProps: { | ||
341 | - span: 16, | ||
342 | - }, | ||
343 | - componentProps: ({ formModel }) => { | ||
344 | - return { | ||
345 | - dataType: formModel[FormField.TYPE], | ||
346 | - }; | ||
347 | - }, | ||
348 | - }, | ||
349 | - { | ||
350 | - field: FormField.ACCESS_MODE, | ||
351 | - component: 'ApiRadioGroup', | ||
352 | - label: '读写类型', | ||
353 | - required: true, | ||
354 | - colProps: { | ||
355 | - span: 24, | ||
356 | - }, | ||
357 | - ifShow: () => !hiddenAccessMode && !hasStructForm, | ||
358 | - defaultValue: 'r', | ||
359 | - componentProps: { | ||
360 | - placeholder: '请选择读写类型', | ||
361 | - api: findDictItemByCode, | ||
362 | - params: { | ||
363 | - dictCode: 'read_write_type', | ||
364 | - }, | ||
365 | - labelField: 'itemText', | ||
366 | - valueField: 'itemValue', | ||
367 | - }, | ||
368 | - }, | ||
369 | - { | ||
370 | - field: FormField.SPECS_LIST, | ||
371 | - label: 'JSON对象', | ||
372 | - component: 'StructForm', | ||
373 | - valueField: 'value', | ||
374 | - changeEvent: 'update:value', | ||
375 | - colProps: { span: 24 }, | ||
376 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRUCT, | ||
377 | - rules: [{ required: true, validator: validateJSON }], | ||
378 | - componentProps: ({ formModel }) => { | ||
379 | - return { | ||
380 | - hasStructForm: formModel[FormField.HAS_STRUCT_FROM], | ||
381 | - }; | ||
382 | - }, | ||
383 | - }, | ||
384 | - { | ||
385 | - field: FormField.REFARK, | ||
386 | - label: '备注', | ||
387 | - component: 'InputTextArea', | ||
388 | - componentProps: { | ||
389 | - rows: 4, | ||
390 | - maxLength: 100, | ||
391 | - placeholder: '请输入描述', | ||
392 | - }, | ||
393 | - }, | ||
394 | - ]; | ||
395 | -}; |
src/components/Form/src/components/StructForm/type.ts
deleted
100644 → 0
1 | -import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
2 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
3 | -import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | ||
4 | - | ||
5 | -export enum OpenModalMode { | ||
6 | - CREATE = 'create', | ||
7 | - UPDATE = 'update', | ||
8 | -} | ||
9 | - | ||
10 | -export interface OpenModalParams { | ||
11 | - mode: OpenModalMode; | ||
12 | - record?: StructRecord; | ||
13 | -} | ||
14 | - | ||
15 | -export interface StructFormValue | ||
16 | - extends Partial<Record<Exclude<FormField, FormField.VALUE_RANGE | FormField.STRUCT>, string>> { | ||
17 | - [FormField.TYPE]: DataTypeEnum; | ||
18 | - [FormField.VALUE_RANGE]?: { | ||
19 | - [FormField.MIN]: string; | ||
20 | - [FormField.MAX]: string; | ||
21 | - }; | ||
22 | - [FormField.STRUCT]: StructRecord[]; | ||
23 | -} | ||
24 | - | ||
25 | -export interface StructRecord extends StructJSON { | ||
26 | - id: string; | ||
27 | -} |
src/components/Form/src/components/StructForm/util.ts
deleted
100644 → 0
1 | -import { cloneDeep } from 'lodash-es'; | ||
2 | -import { StructFormValue } from './type'; | ||
3 | -import { | ||
4 | - DataType, | ||
5 | - ModelOfMatterParams, | ||
6 | - Specs, | ||
7 | - StructJSON, | ||
8 | -} from '/@/api/device/model/modelOfMatterModel'; | ||
9 | -import { isArray } from '/@/utils/is'; | ||
10 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
11 | - | ||
12 | -export function transfromToStructJSON(value: StructFormValue, enumList: Specs[] = []): StructJSON { | ||
13 | - const { | ||
14 | - type, | ||
15 | - valueRange, | ||
16 | - step, | ||
17 | - length = '', | ||
18 | - boolClose, | ||
19 | - boolOpen, | ||
20 | - unit, | ||
21 | - unitName, | ||
22 | - functionName, | ||
23 | - identifier, | ||
24 | - remark, | ||
25 | - specs, | ||
26 | - accessMode, | ||
27 | - } = value; | ||
28 | - const basic = { functionName, identifier, remark, accessMode }; | ||
29 | - let dataType = {} as unknown as DataType; | ||
30 | - | ||
31 | - switch (type) { | ||
32 | - case DataTypeEnum.NUMBER_INT: | ||
33 | - dataType = { | ||
34 | - type, | ||
35 | - specs: { valueRange, step, unit, unitName }, | ||
36 | - }; | ||
37 | - break; | ||
38 | - | ||
39 | - case DataTypeEnum.NUMBER_DOUBLE: | ||
40 | - dataType = { | ||
41 | - type, | ||
42 | - specs: { valueRange, step, unit, unitName }, | ||
43 | - }; | ||
44 | - break; | ||
45 | - | ||
46 | - case DataTypeEnum.BOOL: | ||
47 | - dataType = { | ||
48 | - type, | ||
49 | - specs: { | ||
50 | - boolOpen: boolOpen, | ||
51 | - boolClose: boolClose, | ||
52 | - }, | ||
53 | - }; | ||
54 | - break; | ||
55 | - | ||
56 | - case DataTypeEnum.STRING: | ||
57 | - dataType = { | ||
58 | - type, | ||
59 | - specs: { length }, | ||
60 | - }; | ||
61 | - break; | ||
62 | - | ||
63 | - case DataTypeEnum.ENUM: | ||
64 | - dataType = { | ||
65 | - type, | ||
66 | - specsList: enumList, | ||
67 | - }; | ||
68 | - break; | ||
69 | - | ||
70 | - case DataTypeEnum.STRUCT: | ||
71 | - dataType = { | ||
72 | - type, | ||
73 | - specs: specs! as unknown as ModelOfMatterParams[], | ||
74 | - }; | ||
75 | - break; | ||
76 | - } | ||
77 | - return { ...basic, dataType } as StructJSON; | ||
78 | -} | ||
79 | - | ||
80 | -export const excludeIdInStructJSON = (struct: DataType) => { | ||
81 | - const _value = cloneDeep(struct); | ||
82 | - const { specs } = _value; | ||
83 | - if (!specs) return _value; | ||
84 | - const list = [specs]; | ||
85 | - | ||
86 | - while (list.length) { | ||
87 | - for (const item of list) { | ||
88 | - if (isArray(item)) { | ||
89 | - (item as StructJSON[]).forEach((temp) => { | ||
90 | - if (temp.dataType?.specs) { | ||
91 | - list.push(temp.dataType.specs); | ||
92 | - } | ||
93 | - Reflect.has(temp, 'id') && Reflect.deleteProperty(temp, 'id'); | ||
94 | - }); | ||
95 | - } else { | ||
96 | - Reflect.has(item as Recordable, 'id') && Reflect.deleteProperty(item as Recordable, 'id'); | ||
97 | - } | ||
98 | - list.shift(); | ||
99 | - } | ||
100 | - } | ||
101 | - | ||
102 | - return _value; | ||
103 | -}; |
@@ -145,4 +145,6 @@ export type ComponentType = | @@ -145,4 +145,6 @@ export type ComponentType = | ||
145 | | 'TriggerDurationInput' | 145 | | 'TriggerDurationInput' |
146 | | 'AlarmProfileSelect' | 146 | | 'AlarmProfileSelect' |
147 | | 'LockControlGroup' | 147 | | 'LockControlGroup' |
148 | - | 'EnumList'; | 148 | + | 'EnumList' |
149 | + | 'Segmented' | ||
150 | + | 'StructFormItem'; |
1 | export enum DictEnum { | 1 | export enum DictEnum { |
2 | + // 物模型数据类型 | ||
3 | + DATA_TYPE = 'data_type', | ||
4 | + | ||
5 | + // 事件类型 | ||
6 | + EVENT_TYPE = 'event_type', | ||
7 | + // 物模型单位 | ||
8 | + ATTRIBUTE_UNIT = 'attribute_unit', | ||
9 | + // 物模型读写类型 | ||
10 | + READ_WRITE_TYP = 'read_write_type', | ||
2 | // 从机地址 | 11 | // 从机地址 |
3 | SLAVE_ADDRESS = 'slave_address', | 12 | SLAVE_ADDRESS = 'slave_address', |
4 | // 功能码 | 13 | // 功能码 |
@@ -9,8 +9,26 @@ export enum DataTypeEnum { | @@ -9,8 +9,26 @@ export enum DataTypeEnum { | ||
9 | 9 | ||
10 | export enum FunctionTypeEnum { | 10 | export enum FunctionTypeEnum { |
11 | PROPERTIES = 'properties', | 11 | PROPERTIES = 'properties', |
12 | - EVENTS = 'events', | ||
13 | SERVICE = 'services', | 12 | SERVICE = 'services', |
13 | + EVENTS = 'events', | ||
14 | +} | ||
15 | + | ||
16 | +export enum FunctionTypeNameEnum { | ||
17 | + PROPERTIES = '属性', | ||
18 | + SERVICE = '服务', | ||
19 | + EVENTS = '事件', | ||
20 | +} | ||
21 | + | ||
22 | +export enum ObjectEventTypeEnum { | ||
23 | + INFO = 'INFO', | ||
24 | + ALERT = 'ALERT', | ||
25 | + ERROR = 'ERROR', | ||
26 | +} | ||
27 | + | ||
28 | +export enum ObjectEventTypeNameEnum { | ||
29 | + INFO = '信息', | ||
30 | + ALERT = '告警', | ||
31 | + ERROR = '故障', | ||
14 | } | 32 | } |
15 | 33 | ||
16 | export enum RegisterDataTypeEnum { | 34 | export enum RegisterDataTypeEnum { |
@@ -33,6 +51,11 @@ export enum RegisterActionTypeNameEnum { | @@ -33,6 +51,11 @@ export enum RegisterActionTypeNameEnum { | ||
33 | DOUBLE = '16写入多个保持寄存器', | 51 | DOUBLE = '16写入多个保持寄存器', |
34 | } | 52 | } |
35 | 53 | ||
54 | +export enum ObjectModelAccessModeEnum { | ||
55 | + READ = 'r', | ||
56 | + READ_AND_WRITE = 'rw', | ||
57 | +} | ||
58 | + | ||
36 | export enum ModbusCRCEnum { | 59 | export enum ModbusCRCEnum { |
37 | CRC_16_LOWER = 'CRC_16_LOWER', | 60 | CRC_16_LOWER = 'CRC_16_LOWER', |
38 | } | 61 | } |
1 | +import { unref } from 'vue'; | ||
2 | +import { FormFieldsEnum, FormFieldsNameEnum } from '../config'; | ||
3 | +import { useObjectModelFormContext } from '../useObjectModelFormContext'; | ||
4 | +import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
5 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
6 | +import { FormSchema } from '/@/components/Form'; | ||
7 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
8 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
9 | +import { DataTypeEnum, FunctionTypeEnum } from '/@/enums/objectModelEnum'; | ||
10 | +import { isNullOrUnDef } from '/@/utils/is'; | ||
11 | +import { ValidatorRule } from 'ant-design-vue/lib/form/interface'; | ||
12 | + | ||
13 | +export interface DataTypeFormGetFieldsValueType { | ||
14 | + [FormFieldsEnum.FUNCTION_NAME]: string; | ||
15 | + [FormFieldsEnum.IDENTIFIER]: string; | ||
16 | + [FormFieldsEnum.DATA_TYPE]: DataTypeEnum; | ||
17 | + [FormFieldsEnum.VALUE_RANGE]?: Record<'min' | 'max', number>; | ||
18 | + [FormFieldsEnum.STEP]?: number; | ||
19 | + [FormFieldsEnum.UNIT]?: string; | ||
20 | + [FormFieldsEnum.UNIT_NAME]?: string; | ||
21 | + [FormFieldsEnum.BOOL_CLOSE]?: string; | ||
22 | + [FormFieldsEnum.BOOL_OPEN]?: string; | ||
23 | + [FormFieldsEnum.LENGTH]?: number; | ||
24 | + [FormFieldsEnum.ENUMS_DATA]?: Specs[]; | ||
25 | + [FormFieldsEnum.STRUCT_DATA]?: StructJSON[]; | ||
26 | + [FormFieldsEnum.REMARK]?: string; | ||
27 | +} | ||
28 | + | ||
29 | +export const validateValueRange: ValidatorRule['validator'] = ( | ||
30 | + _rule, | ||
31 | + value: Record<'min' | 'max', number>, | ||
32 | + _callback | ||
33 | +) => { | ||
34 | + value = value || {}; | ||
35 | + const { min, max } = value; | ||
36 | + if (min > max) { | ||
37 | + return Promise.reject('最大值小于最小值'); | ||
38 | + } | ||
39 | + return Promise.resolve(); | ||
40 | +}; | ||
41 | + | ||
42 | +export const validateFunctionName: ValidatorRule['validator'] = (_rule, value: any) => { | ||
43 | + if (/^[a-zA-Z0-9_\-\u4e00-\u9fa5]+$/.test(value)) return Promise.resolve(); | ||
44 | + return Promise.reject('支持中文、大小写字母、数字、短划线、下划线.'); | ||
45 | +}; | ||
46 | + | ||
47 | +export const validateIdentifier: ValidatorRule['validator'] = (_rule, value: any) => { | ||
48 | + if (/^[a-zA-Z0-9_]+$/.test(value)) { | ||
49 | + return Promise.resolve(); | ||
50 | + } | ||
51 | + return Promise.reject('支持大小写字母、数字和下划线.'); | ||
52 | +}; | ||
53 | + | ||
54 | +export const createFunctionNameFormItem = (): FormSchema => { | ||
55 | + return { | ||
56 | + field: FormFieldsEnum.FUNCTION_NAME, | ||
57 | + label: FormFieldsNameEnum.FUNCTION_NAME, | ||
58 | + component: 'Input', | ||
59 | + required: true, | ||
60 | + helpMessage: '支持中文、大小写字母、数字、短划线、下划线。', | ||
61 | + rules: [ | ||
62 | + { | ||
63 | + required: true, | ||
64 | + validator: validateFunctionName, | ||
65 | + }, | ||
66 | + ], | ||
67 | + componentProps: { | ||
68 | + placeholder: `请输入${FormFieldsNameEnum.FUNCTION_NAME}`, | ||
69 | + }, | ||
70 | + }; | ||
71 | +}; | ||
72 | + | ||
73 | +export const createIdentifierFormItem = (): FormSchema => { | ||
74 | + return { | ||
75 | + field: FormFieldsEnum.IDENTIFIER, | ||
76 | + label: FormFieldsNameEnum.IDENTIFIER, | ||
77 | + required: true, | ||
78 | + component: 'Input', | ||
79 | + helpMessage: '支持大小写字母、数字和下划线.', | ||
80 | + componentProps: { | ||
81 | + maxLength: 128, | ||
82 | + placeholder: '请输入标识符', | ||
83 | + }, | ||
84 | + rules: [ | ||
85 | + { | ||
86 | + required: true, | ||
87 | + validator: validateIdentifier, | ||
88 | + }, | ||
89 | + ], | ||
90 | + }; | ||
91 | +}; | ||
92 | + | ||
93 | +export const getFormSchemas = (dataType: DataTypeEnum[], showRemark: boolean): FormSchema[] => { | ||
94 | + const { getTransportType } = useObjectModelFormContext(); | ||
95 | + return [ | ||
96 | + createFunctionNameFormItem(), | ||
97 | + createIdentifierFormItem(), | ||
98 | + { | ||
99 | + field: FormFieldsEnum.DATA_TYPE, | ||
100 | + label: FormFieldsNameEnum.DATA_TYPE, | ||
101 | + required: true, | ||
102 | + component: 'ApiSelect', | ||
103 | + defaultValue: DataTypeEnum.NUMBER_INT, | ||
104 | + ifShow: () => !!dataType.length, | ||
105 | + componentProps: () => { | ||
106 | + return { | ||
107 | + placeholder: '请选择数据类型', | ||
108 | + api: async (params: Recordable) => { | ||
109 | + let result = await findDictItemByCode(params); | ||
110 | + if (unref(getTransportType) === TransportTypeEnum.TCP) { | ||
111 | + result = result.filter( | ||
112 | + (item) => | ||
113 | + ![DataTypeEnum.STRUCT, DataTypeEnum.ENUM].includes(item.itemValue as DataTypeEnum) | ||
114 | + ); | ||
115 | + } | ||
116 | + return result.filter((item) => dataType.includes(item.itemValue as DataTypeEnum)); | ||
117 | + }, | ||
118 | + params: { | ||
119 | + dictCode: DictEnum.DATA_TYPE, | ||
120 | + }, | ||
121 | + allowClear: false, | ||
122 | + labelField: 'itemText', | ||
123 | + valueField: 'itemValue', | ||
124 | + getPopupContainer: () => document.body, | ||
125 | + }; | ||
126 | + }, | ||
127 | + }, | ||
128 | + { | ||
129 | + field: FormFieldsEnum.VALUE_RANGE, | ||
130 | + label: FormFieldsNameEnum.VALUE_RANGE, | ||
131 | + component: 'CustomMinMaxInput', | ||
132 | + valueField: 'value', | ||
133 | + changeEvent: 'update:value', | ||
134 | + ifShow: ({ model }) => | ||
135 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
136 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
137 | + ) && | ||
138 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
139 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
140 | + dynamicRules: ({ model }) => [ | ||
141 | + { | ||
142 | + required: | ||
143 | + model[FormFieldsEnum.VALUE_RANGE]?.min || model[FormFieldsEnum.VALUE_RANGE]?.max, | ||
144 | + validator: validateValueRange, | ||
145 | + }, | ||
146 | + ], | ||
147 | + }, | ||
148 | + { | ||
149 | + field: FormFieldsEnum.STEP, | ||
150 | + label: FormFieldsNameEnum.STEP, | ||
151 | + component: 'InputNumber', | ||
152 | + componentProps: { | ||
153 | + maxLength: 255, | ||
154 | + placeholder: '请输入步长', | ||
155 | + min: 1, | ||
156 | + formatter: (value: number | string) => { | ||
157 | + return value ? Math.floor(Number(value)) : value; | ||
158 | + }, | ||
159 | + }, | ||
160 | + ifShow: ({ model }) => | ||
161 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
162 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
163 | + ) && | ||
164 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
165 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
166 | + dynamicRules: ({ model }) => { | ||
167 | + const valueRange = model[FormFieldsEnum.VALUE_RANGE] || {}; | ||
168 | + const { min, max } = valueRange; | ||
169 | + const step = model[FormFieldsEnum.STEP]; | ||
170 | + return [ | ||
171 | + { | ||
172 | + validator: () => { | ||
173 | + if ([min, max].every(isNullOrUnDef)) return Promise.resolve(); | ||
174 | + if (step > max - min) { | ||
175 | + return Promise.reject('步长不能大于取值范围的差值'); | ||
176 | + } | ||
177 | + return Promise.resolve(); | ||
178 | + }, | ||
179 | + }, | ||
180 | + ]; | ||
181 | + }, | ||
182 | + }, | ||
183 | + { | ||
184 | + field: FormFieldsEnum.UNIT_NAME, | ||
185 | + label: FormFieldsNameEnum.UNIT_NAME, | ||
186 | + component: 'Input', | ||
187 | + show: false, | ||
188 | + }, | ||
189 | + { | ||
190 | + field: FormFieldsEnum.UNIT, | ||
191 | + label: FormFieldsNameEnum.UNIT, | ||
192 | + component: 'ApiSelect', | ||
193 | + ifShow: ({ model }) => | ||
194 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
195 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
196 | + ) && | ||
197 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
198 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
199 | + componentProps: ({ formActionType }) => { | ||
200 | + const { setFieldsValue } = formActionType; | ||
201 | + return { | ||
202 | + placeholder: '请选择单位', | ||
203 | + api: async (params: Recordable) => { | ||
204 | + const list = await findDictItemByCode(params); | ||
205 | + list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`)); | ||
206 | + return list; | ||
207 | + }, | ||
208 | + params: { | ||
209 | + dictCode: DictEnum.ATTRIBUTE_UNIT, | ||
210 | + }, | ||
211 | + labelField: 'itemText', | ||
212 | + valueField: 'itemValue', | ||
213 | + onChange(_, record: Record<'label' | 'value', string>) { | ||
214 | + if (record) { | ||
215 | + const { label } = record; | ||
216 | + setFieldsValue({ [FormFieldsEnum.UNIT_NAME]: label }); | ||
217 | + } | ||
218 | + }, | ||
219 | + getPopupContainer: () => document.body, | ||
220 | + showSearch: true, | ||
221 | + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | ||
222 | + let { label, value } = option; | ||
223 | + label = label.toLowerCase(); | ||
224 | + value = value.toLowerCase(); | ||
225 | + inputValue = inputValue.toLowerCase(); | ||
226 | + return label.includes(inputValue) || value.includes(inputValue); | ||
227 | + }, | ||
228 | + }; | ||
229 | + }, | ||
230 | + }, | ||
231 | + { | ||
232 | + field: FormFieldsEnum.BOOL_CLOSE, | ||
233 | + component: 'Input', | ||
234 | + required: true, | ||
235 | + label: FormFieldsNameEnum.BOOL_CLOSE, | ||
236 | + componentProps: { | ||
237 | + placeholder: '如:关', | ||
238 | + }, | ||
239 | + defaultValue: '关', | ||
240 | + ifShow: ({ model }) => | ||
241 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
242 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
243 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | ||
244 | + dynamicRules: ({ model }) => { | ||
245 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | ||
246 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | ||
247 | + return [ | ||
248 | + { | ||
249 | + required: true, | ||
250 | + message: `布尔值不能为空`, | ||
251 | + }, | ||
252 | + { | ||
253 | + validator() { | ||
254 | + if (open === close) return Promise.reject('布尔值不能相同'); | ||
255 | + return Promise.resolve(); | ||
256 | + }, | ||
257 | + }, | ||
258 | + ]; | ||
259 | + }, | ||
260 | + }, | ||
261 | + { | ||
262 | + field: FormFieldsEnum.BOOL_OPEN, | ||
263 | + component: 'Input', | ||
264 | + required: true, | ||
265 | + label: FormFieldsNameEnum.BOOL_OPEN, | ||
266 | + componentProps: { | ||
267 | + placeholder: '如:开', | ||
268 | + }, | ||
269 | + defaultValue: '开', | ||
270 | + ifShow: ({ model }) => | ||
271 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
272 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
273 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | ||
274 | + dynamicRules: ({ model }) => { | ||
275 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | ||
276 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | ||
277 | + return [ | ||
278 | + { | ||
279 | + required: true, | ||
280 | + message: `布尔值不能为空`, | ||
281 | + }, | ||
282 | + { | ||
283 | + validator() { | ||
284 | + if (open === close) return Promise.reject('布尔值不能相同'); | ||
285 | + return Promise.resolve(); | ||
286 | + }, | ||
287 | + }, | ||
288 | + ]; | ||
289 | + }, | ||
290 | + }, | ||
291 | + { | ||
292 | + field: FormFieldsEnum.LENGTH, | ||
293 | + component: 'InputNumber', | ||
294 | + required: true, | ||
295 | + label: FormFieldsNameEnum.LENGTH, | ||
296 | + defaultValue: 1024, | ||
297 | + componentProps: { | ||
298 | + placeholder: '请输入数据长度', | ||
299 | + }, | ||
300 | + ifShow: ({ model }) => | ||
301 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
302 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
303 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRING, | ||
304 | + }, | ||
305 | + { | ||
306 | + field: FormFieldsEnum.ENUMS_DATA, | ||
307 | + label: FormFieldsNameEnum.ENUMS_DATA, | ||
308 | + component: 'Input', | ||
309 | + slot: FormFieldsEnum.ENUMS_DATA, | ||
310 | + changeEvent: 'update:value', | ||
311 | + valueField: 'value', | ||
312 | + ifShow: ({ model }) => | ||
313 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
314 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
315 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.ENUM, | ||
316 | + }, | ||
317 | + { | ||
318 | + field: FormFieldsEnum.STRUCT_DATA, | ||
319 | + label: FormFieldsNameEnum.STRUCT_DATA, | ||
320 | + component: 'Input', | ||
321 | + slot: FormFieldsEnum.STRUCT_DATA, | ||
322 | + changeEvent: 'update:value', | ||
323 | + valueField: 'value', | ||
324 | + required: true, | ||
325 | + ifShow: ({ model }) => | ||
326 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRUCT && | ||
327 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
328 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
329 | + ), | ||
330 | + }, | ||
331 | + | ||
332 | + { | ||
333 | + field: FormFieldsEnum.REMARK, | ||
334 | + label: FormFieldsNameEnum.REMARK, | ||
335 | + component: 'InputTextArea', | ||
336 | + ifShow: showRemark, | ||
337 | + componentProps: { | ||
338 | + rows: 4, | ||
339 | + maxLength: 100, | ||
340 | + placeholder: '请输入描述', | ||
341 | + }, | ||
342 | + }, | ||
343 | + ]; | ||
344 | +}; |
1 | +export { default as DataTypeForm } from './index.vue'; |
1 | +<script setup lang="ts"> | ||
2 | + import { useForm, BasicForm } from '/@/components/Form'; | ||
3 | + import { getFormSchemas } from './config'; | ||
4 | + import { EnumList } from '../EnumList'; | ||
5 | + import { ref } from 'vue'; | ||
6 | + import { StructFormItem } from '../StructFormItem'; | ||
7 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
8 | + import { useDataTypeFormData } from './useDataTypeFormData'; | ||
9 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
10 | + | ||
11 | + const props = withDefaults( | ||
12 | + defineProps<{ | ||
13 | + dataType?: DataTypeEnum[]; | ||
14 | + mode?: DataActionModeEnum; | ||
15 | + showRemark?: boolean; | ||
16 | + }>(), | ||
17 | + { | ||
18 | + dataType: () => Object.values(DataTypeEnum), | ||
19 | + showRemark: false, | ||
20 | + } | ||
21 | + ); | ||
22 | + | ||
23 | + defineEmits<{ | ||
24 | + (event: 'field-value-change', field: string, value: any): void; | ||
25 | + }>(); | ||
26 | + | ||
27 | + const [register, formActionType] = useForm({ | ||
28 | + schemas: getFormSchemas(props.dataType, props.showRemark), | ||
29 | + layout: 'vertical', | ||
30 | + showActionButtonGroup: false, | ||
31 | + }); | ||
32 | + | ||
33 | + const enumListRef = ref<InstanceType<typeof EnumList>>(); | ||
34 | + | ||
35 | + const { getFieldsValue, setFieldsValue, validate, resetFieldsValue } = useDataTypeFormData({ | ||
36 | + formActionType, | ||
37 | + enumListRef, | ||
38 | + }); | ||
39 | + | ||
40 | + defineExpose({ getFieldsValue, setFieldsValue, validate, resetFieldsValue }); | ||
41 | +</script> | ||
42 | + | ||
43 | +<template> | ||
44 | + <BasicForm | ||
45 | + @register="register" | ||
46 | + class="data-type-form" | ||
47 | + :disabled="mode === DataActionModeEnum.READ" | ||
48 | + @field-value-change="(field, value) => $emit('field-value-change', field, value)" | ||
49 | + > | ||
50 | + <template #enumsData="{ model, field }"> | ||
51 | + <EnumList | ||
52 | + ref="enumListRef" | ||
53 | + v-model:value="model[field]" | ||
54 | + :disabled="mode === DataActionModeEnum.READ" | ||
55 | + /> | ||
56 | + </template> | ||
57 | + <template #structData="{ model, field }"> | ||
58 | + <StructFormItem | ||
59 | + v-model:value="model[field]" | ||
60 | + :mode="mode" | ||
61 | + :dataType="[ | ||
62 | + DataTypeEnum.BOOL, | ||
63 | + DataTypeEnum.ENUM, | ||
64 | + DataTypeEnum.NUMBER_DOUBLE, | ||
65 | + DataTypeEnum.NUMBER_INT, | ||
66 | + DataTypeEnum.STRING, | ||
67 | + ]" | ||
68 | + /> | ||
69 | + </template> | ||
70 | + </BasicForm> | ||
71 | +</template> | ||
72 | + | ||
73 | +<style lang="less" scoped> | ||
74 | + .data-type-form { | ||
75 | + :deep(.ant-input-number) { | ||
76 | + width: 100%; | ||
77 | + } | ||
78 | + } | ||
79 | +</style> | ||
80 | +./config |
1 | +import { Ref, unref } from 'vue'; | ||
2 | +import { FormActionType } from '/@/components/Form'; | ||
3 | +import EnumList from './EnumList.vue'; | ||
4 | +import { DefineComponentsBasicExpose } from '/#/utils'; | ||
5 | +import { DataTypeFormGetFieldsValueType } from './config'; | ||
6 | +import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
7 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
8 | +import { isObject } from '/@/utils/is'; | ||
9 | + | ||
10 | +interface UseDataTypeFormDataParams { | ||
11 | + formActionType: FormActionType; | ||
12 | + enumListRef: Ref<InstanceType<typeof EnumList> | undefined>; | ||
13 | +} | ||
14 | + | ||
15 | +function getDataType(value: DataTypeFormGetFieldsValueType, enumListFormValue?: Specs[]): DataType { | ||
16 | + const { dataType, valueRange, unit, unitName, step, length, boolClose, boolOpen, structData } = | ||
17 | + value; | ||
18 | + | ||
19 | + const basic: DataType = { type: dataType }; | ||
20 | + | ||
21 | + if (dataType === DataTypeEnum.BOOL) { | ||
22 | + basic.specs = { | ||
23 | + boolClose, | ||
24 | + boolOpen, | ||
25 | + }; | ||
26 | + } else if (dataType === DataTypeEnum.ENUM) { | ||
27 | + basic.specsList = enumListFormValue; | ||
28 | + } else if ([DataTypeEnum.NUMBER_DOUBLE, DataTypeEnum.NUMBER_INT].includes(dataType)) { | ||
29 | + basic.specs = { | ||
30 | + valueRange, | ||
31 | + unit, | ||
32 | + unitName, | ||
33 | + step, | ||
34 | + }; | ||
35 | + } else if (dataType === DataTypeEnum.STRING) { | ||
36 | + basic.specs = { | ||
37 | + length, | ||
38 | + }; | ||
39 | + } else if (dataType === DataTypeEnum.STRUCT) { | ||
40 | + basic.specs = structData || []; | ||
41 | + } | ||
42 | + | ||
43 | + return basic; | ||
44 | +} | ||
45 | + | ||
46 | +export function useDataTypeFormData({ | ||
47 | + formActionType, | ||
48 | + enumListRef, | ||
49 | +}: UseDataTypeFormDataParams): DefineComponentsBasicExpose<StructJSON> { | ||
50 | + const getFieldsValue = (): StructJSON => { | ||
51 | + const value = formActionType.getFieldsValue() as DataTypeFormGetFieldsValueType; | ||
52 | + const enumListFormValue = unref(enumListRef)?.getFieldsValue(); | ||
53 | + const { functionName, identifier, remark } = value; | ||
54 | + | ||
55 | + return { | ||
56 | + functionName, | ||
57 | + identifier, | ||
58 | + remark, | ||
59 | + dataType: getDataType(value, enumListFormValue), | ||
60 | + }; | ||
61 | + }; | ||
62 | + | ||
63 | + const setFieldsValue = (record: StructJSON) => { | ||
64 | + const { dataType, identifier, functionName, remark } = record; | ||
65 | + const { type, specs, specsList = [] } = dataType || {}; | ||
66 | + | ||
67 | + // 兼容处理 unit | ||
68 | + if (isObject(specs) && isObject((specs as Specs).unit)) { | ||
69 | + (specs as Specs).unit = ((specs as Specs).unit as unknown as Record<'value', string>)?.value; | ||
70 | + } | ||
71 | + | ||
72 | + // 兼容处理 length 数据长度类型 | ||
73 | + if (isObject(specs) && isObject((specs as Specs).length)) { | ||
74 | + (specs as Specs).length = Number((specs as Specs).length); | ||
75 | + } | ||
76 | + | ||
77 | + formActionType.setFieldsValue({ | ||
78 | + dataType: type, | ||
79 | + identifier, | ||
80 | + functionName, | ||
81 | + remark, | ||
82 | + ...(isObject(specs) ? specs : {}), | ||
83 | + structData: type === DataTypeEnum.STRUCT ? specs : [], | ||
84 | + enumsData: type === DataTypeEnum.ENUM ? specsList : [], | ||
85 | + } as DataTypeFormGetFieldsValueType); | ||
86 | + }; | ||
87 | + | ||
88 | + const validate = async () => { | ||
89 | + await formActionType.validate(); | ||
90 | + await unref(enumListRef)?.validate?.(); | ||
91 | + }; | ||
92 | + | ||
93 | + const resetFieldsValue = () => { | ||
94 | + formActionType.resetFields(); | ||
95 | + }; | ||
96 | + | ||
97 | + return { | ||
98 | + validate, | ||
99 | + getFieldsValue, | ||
100 | + setFieldsValue, | ||
101 | + resetFieldsValue, | ||
102 | + }; | ||
103 | +} |
src/views/device/profiles/components/ObjectModelForm/EnumList/config.ts
renamed from
src/components/Form/src/components/StructForm/EnumList.config.ts
1 | -import { FormSchema } from '/@/components/Table'; | 1 | +import { FormSchema } from '/@/components/Form'; |
2 | 2 | ||
3 | export enum FormFieldsEnum { | 3 | export enum FormFieldsEnum { |
4 | VALUE = 'value', | 4 | VALUE = 'value', |
@@ -41,14 +41,7 @@ export const getFormSchemas = (): FormSchema[] => { | @@ -41,14 +41,7 @@ export const getFormSchemas = (): FormSchema[] => { | ||
41 | field: FormFieldsEnum.NAME, | 41 | field: FormFieldsEnum.NAME, |
42 | label: '', | 42 | label: '', |
43 | component: 'Input', | 43 | component: 'Input', |
44 | - rules: [ | ||
45 | - { | ||
46 | - required: true, | ||
47 | - message: `支持中文、英文大小写、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符`, | ||
48 | - type: 'string', | ||
49 | - pattern: /^[a-zA-Z0-9\u4e00-\u9fa5a][\u4e00-\u9fa5a-zA-Z0-9_-]*$/, | ||
50 | - }, | ||
51 | - ], | 44 | + rules: [{ required: true, message: `参数描述不能为空`, type: 'string' }], |
52 | componentProps: () => { | 45 | componentProps: () => { |
53 | return { | 46 | return { |
54 | placeholder: '对该枚举项的描述', | 47 | placeholder: '对该枚举项的描述', |
1 | +export { default as EnumList } from './index.vue'; |
src/views/device/profiles/components/ObjectModelForm/EnumList/index.vue
renamed from
src/components/Form/src/components/StructForm/EnumList.vue
1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
2 | import { Button, Tooltip } from 'ant-design-vue'; | 2 | import { Button, Tooltip } from 'ant-design-vue'; |
3 | import { computed, nextTick, ref, unref, watch } from 'vue'; | 3 | import { computed, nextTick, ref, unref, watch } from 'vue'; |
4 | - import { useForm, BasicForm } from '/@/components/Form'; | 4 | + import { useForm, BasicForm, FormActionType } from '/@/components/Form'; |
5 | import { Specs } from '/@/api/device/model/modelOfMatterModel'; | 5 | import { Specs } from '/@/api/device/model/modelOfMatterModel'; |
6 | import { Icon } from '/@/components/Icon'; | 6 | import { Icon } from '/@/components/Icon'; |
7 | - import { getFormSchemas } from './EnumList.config'; | ||
8 | - import { FormActionType } from '../../../types/form'; | 7 | + import { getFormSchemas } from './config'; |
9 | import { buildUUID } from '/@/utils/uuid'; | 8 | import { buildUUID } from '/@/utils/uuid'; |
10 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 9 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
11 | import { isNullOrUnDef } from '/@/utils/is'; | 10 | import { isNullOrUnDef } from '/@/utils/is'; |
@@ -65,7 +64,6 @@ | @@ -65,7 +64,6 @@ | ||
65 | 64 | ||
66 | const setFieldsValue = (spaceList: Specs[]) => { | 65 | const setFieldsValue = (spaceList: Specs[]) => { |
67 | enumsListElRef.value = spaceList.map((item) => ({ uuid: buildUUID(), dataSource: item })); | 66 | enumsListElRef.value = spaceList.map((item) => ({ uuid: buildUUID(), dataSource: item })); |
68 | - | ||
69 | nextTick(() => { | 67 | nextTick(() => { |
70 | unref(enumsListElRef).forEach((item) => | 68 | unref(enumsListElRef).forEach((item) => |
71 | item.formActionType?.setFieldsValue?.(item.dataSource) | 69 | item.formActionType?.setFieldsValue?.(item.dataSource) |
@@ -76,10 +74,7 @@ | @@ -76,10 +74,7 @@ | ||
76 | const handleDeleteEnums = (item: EnumElItemType) => { | 74 | const handleDeleteEnums = (item: EnumElItemType) => { |
77 | const index = unref(enumsListElRef).findIndex((temp) => item.uuid === temp.uuid); | 75 | const index = unref(enumsListElRef).findIndex((temp) => item.uuid === temp.uuid); |
78 | 76 | ||
79 | - if (~index) { | ||
80 | - enumsListElRef.value.splice(index, 1); | ||
81 | - validateSameEnum(); | ||
82 | - } | 77 | + ~index && enumsListElRef.value.splice(index, 1); |
83 | }; | 78 | }; |
84 | 79 | ||
85 | const handleAddEnums = () => { | 80 | const handleAddEnums = () => { |
@@ -107,15 +102,17 @@ | @@ -107,15 +102,17 @@ | ||
107 | <section class="w-full"> | 102 | <section class="w-full"> |
108 | <header class="flex h-8 items-center"> | 103 | <header class="flex h-8 items-center"> |
109 | <div class="w-1/2"> | 104 | <div class="w-1/2"> |
110 | - <span>参考值</span> | 105 | + <span class="mr-1 text-red-400">*</span> |
106 | + <span> 参考值 </span> | ||
111 | <Tooltip title="支持整型,取值范围:-2147483648 ~ 2147483647"> | 107 | <Tooltip title="支持整型,取值范围:-2147483648 ~ 2147483647"> |
112 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> | 108 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> |
113 | </Tooltip> | 109 | </Tooltip> |
114 | </div> | 110 | </div> |
115 | <div class="w-1/2"> | 111 | <div class="w-1/2"> |
116 | - <span>参考描述</span> | 112 | + <span class="mr-1 text-red-400">*</span> |
113 | + <span> 参考描述 </span> | ||
117 | <Tooltip | 114 | <Tooltip |
118 | - title="支持中文、英文大小写、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符" | 115 | + title="支持中文、英文大小写、日文、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符" |
119 | > | 116 | > |
120 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> | 117 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> |
121 | </Tooltip> | 118 | </Tooltip> |
src/views/device/profiles/components/ObjectModelForm/ExtendDesc/config.ts
renamed from
src/components/Form/src/components/ExtendDesc/config.ts
1 | +import { unref } from 'vue'; | ||
2 | +import { useObjectModelFormContext } from '../useObjectModelFormContext'; | ||
1 | import { findDictItemByCode } from '/@/api/system/dict'; | 3 | import { findDictItemByCode } from '/@/api/system/dict'; |
2 | import { FormSchema } from '/@/components/Table'; | 4 | import { FormSchema } from '/@/components/Table'; |
3 | import { DictEnum } from '/@/enums/dictEnum'; | 5 | import { DictEnum } from '/@/enums/dictEnum'; |
@@ -37,66 +39,62 @@ function getActionTypeByObjectModelType(dataType: DataTypeEnum) { | @@ -37,66 +39,62 @@ function getActionTypeByObjectModelType(dataType: DataTypeEnum) { | ||
37 | return list; | 39 | return list; |
38 | } | 40 | } |
39 | 41 | ||
40 | -export const formSchemas: FormSchema[] = [ | ||
41 | - { | ||
42 | - field: FormFieldsEnum.OBJECT_MODEL_TYPE, | ||
43 | - component: 'Input', | ||
44 | - label: '物模型数据类型', | ||
45 | - ifShow: false, | ||
46 | - }, | ||
47 | - { | ||
48 | - field: FormFieldsEnum.REGISTER_ADDRESS, | ||
49 | - component: 'RegisterAddressInput', | ||
50 | - label: '寄存器地址', | ||
51 | - changeEvent: 'update:value', | ||
52 | - valueField: 'value', | ||
53 | - rules: [{ message: '请输入寄存器地址', required: true, type: 'number' }], | ||
54 | - componentProps: { | ||
55 | - placeholder: '请输入寄存器地址', | ||
56 | - }, | ||
57 | - }, | ||
58 | - { | ||
59 | - field: FormFieldsEnum.DATA_TYPE, | ||
60 | - component: 'ApiSelect', | ||
61 | - label: '数据格式', | ||
62 | - rules: [{ message: '请选择数据格式', required: true }], | ||
63 | - defaultValue: RegisterDataTypeEnum.UN_SHORT, | ||
64 | - componentProps: { | ||
65 | - api: findDictItemByCode, | ||
66 | - params: { | ||
67 | - dictCode: DictEnum.REGISTER_DATA_FORMAT, | 42 | +export const getFormSchemas = (): FormSchema[] => { |
43 | + const { getDataType } = useObjectModelFormContext(); | ||
44 | + return [ | ||
45 | + { | ||
46 | + field: FormFieldsEnum.REGISTER_ADDRESS, | ||
47 | + component: 'RegisterAddressInput', | ||
48 | + label: '寄存器地址', | ||
49 | + changeEvent: 'update:value', | ||
50 | + valueField: 'value', | ||
51 | + rules: [{ message: '请输入寄存器地址', required: true, type: 'number' }], | ||
52 | + componentProps: { | ||
53 | + placeholder: '请输入寄存器地址', | ||
68 | }, | 54 | }, |
69 | - labelField: 'itemText', | ||
70 | - valueField: 'itemValue', | ||
71 | - placeholder: '请选择数据格式', | ||
72 | - getPopupContainer: () => document.body, | ||
73 | }, | 55 | }, |
74 | - }, | ||
75 | - { | ||
76 | - field: FormFieldsEnum.ACTION_TYPE, | ||
77 | - component: 'Select', | ||
78 | - label: '操作类型', | ||
79 | - rules: [{ message: '请选择操作类型', required: true }], | ||
80 | - componentProps: ({ formModel }) => { | ||
81 | - const objectModelType = formModel[FormFieldsEnum.OBJECT_MODEL_TYPE]; | ||
82 | - return { | ||
83 | - options: getActionTypeByObjectModelType(objectModelType), | ||
84 | - placeholder: '请选择操作类型', | 56 | + { |
57 | + field: FormFieldsEnum.DATA_TYPE, | ||
58 | + component: 'ApiSelect', | ||
59 | + label: '数据格式', | ||
60 | + rules: [{ message: '请选择数据格式', required: true }], | ||
61 | + defaultValue: RegisterDataTypeEnum.UN_SHORT, | ||
62 | + componentProps: { | ||
63 | + api: findDictItemByCode, | ||
64 | + params: { | ||
65 | + dictCode: DictEnum.REGISTER_DATA_FORMAT, | ||
66 | + }, | ||
67 | + labelField: 'itemText', | ||
68 | + valueField: 'itemValue', | ||
69 | + placeholder: '请选择数据格式', | ||
85 | getPopupContainer: () => document.body, | 70 | getPopupContainer: () => document.body, |
86 | - }; | 71 | + }, |
87 | }, | 72 | }, |
88 | - }, | ||
89 | - { | ||
90 | - field: FormFieldsEnum.ZOOM_FACTOR, | ||
91 | - component: 'InputNumber', | ||
92 | - label: '缩放因子', | ||
93 | - helpMessage: ['缩放因子不能为0,默认为1'], | ||
94 | - defaultValue: 1, | ||
95 | - ifShow: ({ model }) => | ||
96 | - ![DataTypeEnum.BOOL, DataTypeEnum.STRING].includes(model[FormFieldsEnum.OBJECT_MODEL_TYPE]), | ||
97 | - componentProps: { | ||
98 | - min: 1, | ||
99 | - placeholder: '请输入缩放因子', | 73 | + { |
74 | + field: FormFieldsEnum.ACTION_TYPE, | ||
75 | + component: 'Select', | ||
76 | + label: '操作类型', | ||
77 | + rules: [{ message: '请选择操作类型', required: true }], | ||
78 | + componentProps: () => { | ||
79 | + return { | ||
80 | + options: getActionTypeByObjectModelType(unref(getDataType)), | ||
81 | + placeholder: '请选择操作类型', | ||
82 | + getPopupContainer: () => document.body, | ||
83 | + }; | ||
84 | + }, | ||
85 | + }, | ||
86 | + { | ||
87 | + field: FormFieldsEnum.ZOOM_FACTOR, | ||
88 | + component: 'InputNumber', | ||
89 | + label: '缩放因子', | ||
90 | + helpMessage: ['缩放因子不能为0,默认为1'], | ||
91 | + defaultValue: 1, | ||
92 | + ifShow: ({ model }) => | ||
93 | + ![DataTypeEnum.BOOL, DataTypeEnum.STRING].includes(model[FormFieldsEnum.OBJECT_MODEL_TYPE]), | ||
94 | + componentProps: { | ||
95 | + min: 1, | ||
96 | + placeholder: '请输入缩放因子', | ||
97 | + }, | ||
100 | }, | 98 | }, |
101 | - }, | ||
102 | -]; | 99 | + ]; |
100 | +}; |
1 | +export { default as ExtendDesc } from './index.vue'; |
src/views/device/profiles/components/ObjectModelForm/ExtendDesc/index.vue
renamed from
src/components/Form/src/components/ExtendDesc/index.vue
@@ -4,19 +4,18 @@ | @@ -4,19 +4,18 @@ | ||
4 | import { BasicForm, useForm } from '/@/components/Form'; | 4 | import { BasicForm, useForm } from '/@/components/Form'; |
5 | import { BasicModal } from '/@/components/Modal'; | 5 | import { BasicModal } from '/@/components/Modal'; |
6 | import { PlusCircleOutlined } from '@ant-design/icons-vue'; | 6 | import { PlusCircleOutlined } from '@ant-design/icons-vue'; |
7 | - import { FormFieldsEnum, formSchemas } from './config'; | ||
8 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 7 | + import { getFormSchemas } from './config'; |
8 | + import { ExtensionDesc } from '/@/api/device/model/modelOfMatterModel'; | ||
9 | 9 | ||
10 | const show = ref(false); | 10 | const show = ref(false); |
11 | 11 | ||
12 | const props = withDefaults( | 12 | const props = withDefaults( |
13 | defineProps<{ | 13 | defineProps<{ |
14 | - value?: object; | 14 | + value?: ExtensionDesc; |
15 | disabled?: boolean; | 15 | disabled?: boolean; |
16 | - dataType?: DataTypeEnum; | ||
17 | }>(), | 16 | }>(), |
18 | { | 17 | { |
19 | - value: () => ({}), | 18 | + value: () => ({} as ExtensionDesc), |
20 | } | 19 | } |
21 | ); | 20 | ); |
22 | 21 | ||
@@ -24,7 +23,7 @@ | @@ -24,7 +23,7 @@ | ||
24 | 23 | ||
25 | const [registerForm, { setFieldsValue, getFieldsValue, setProps, validate, resetFields }] = | 24 | const [registerForm, { setFieldsValue, getFieldsValue, setProps, validate, resetFields }] = |
26 | useForm({ | 25 | useForm({ |
27 | - schemas: formSchemas, | 26 | + schemas: getFormSchemas(), |
28 | showActionButtonGroup: false, | 27 | showActionButtonGroup: false, |
29 | }); | 28 | }); |
30 | 29 | ||
@@ -39,7 +38,6 @@ | @@ -39,7 +38,6 @@ | ||
39 | const handleSubmit = async () => { | 38 | const handleSubmit = async () => { |
40 | await validate(); | 39 | await validate(); |
41 | const value = getFieldsValue(); | 40 | const value = getFieldsValue(); |
42 | - Reflect.deleteProperty(value, FormFieldsEnum.OBJECT_MODEL_TYPE); | ||
43 | emit('update:value', value); | 41 | emit('update:value', value); |
44 | show.value = false; | 42 | show.value = false; |
45 | }; | 43 | }; |
@@ -47,22 +45,15 @@ | @@ -47,22 +45,15 @@ | ||
47 | watch(show, async (value) => { | 45 | watch(show, async (value) => { |
48 | if (value) { | 46 | if (value) { |
49 | await nextTick(); | 47 | await nextTick(); |
50 | - setFieldsValue({ [FormFieldsEnum.OBJECT_MODEL_TYPE]: props.dataType }); | 48 | + setFieldsValue({ ...props.value }); |
51 | } | 49 | } |
52 | }); | 50 | }); |
53 | - | ||
54 | - watch( | ||
55 | - () => props.value, | ||
56 | - (value) => { | ||
57 | - setFieldsValue(value); | ||
58 | - } | ||
59 | - ); | ||
60 | </script> | 51 | </script> |
61 | 52 | ||
62 | <template> | 53 | <template> |
63 | <section> | 54 | <section> |
64 | <Button type="link" @click="handleClick"><PlusCircleOutlined />新增扩展描述</Button> | 55 | <Button type="link" @click="handleClick"><PlusCircleOutlined />新增扩展描述</Button> |
65 | - <BasicModal title="扩展描述" v-model:visible="show" @ok="handleSubmit"> | 56 | + <BasicModal title="扩展描述" v-model:visible="show" @ok="handleSubmit" :show-ok-btn="!disabled"> |
66 | <BasicForm class="extension-form" @register="registerForm" /> | 57 | <BasicForm class="extension-form" @register="registerForm" /> |
67 | </BasicModal> | 58 | </BasicModal> |
68 | </section> | 59 | </section> |
src/views/device/profiles/components/ObjectModelForm/StructFormItem/CreateStructModal.vue
0 → 100644
1 | +<script setup lang="ts"> | ||
2 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
3 | + import { ref, unref } from 'vue'; | ||
4 | + import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
5 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
6 | + import { DataTypeForm } from '../DataTypeForm'; | ||
7 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
8 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
9 | + | ||
10 | + const props = withDefaults( | ||
11 | + defineProps<{ value?: StructJSON[]; dataType?: DataTypeEnum[]; mode?: DataActionModeEnum }>(), | ||
12 | + { | ||
13 | + value: () => [], | ||
14 | + dataType: () => Object.values(DataTypeEnum), | ||
15 | + } | ||
16 | + ); | ||
17 | + | ||
18 | + const dataTypeFormRef = ref<InstanceType<typeof DataTypeForm>>(); | ||
19 | + | ||
20 | + const emits = defineEmits(['register', 'complete']); | ||
21 | + | ||
22 | + const currentMode = ref(DataActionModeEnum.CREATE); | ||
23 | + const [registerModal] = useModalInner(({ mode, record }: ModalParamsType<StructJSON>) => { | ||
24 | + unref(dataTypeFormRef)?.resetFieldsValue?.(); | ||
25 | + currentMode.value = mode; | ||
26 | + if (mode === DataActionModeEnum.UPDATE) { | ||
27 | + unref(dataTypeFormRef)?.setFieldsValue?.(record); | ||
28 | + } | ||
29 | + }); | ||
30 | + | ||
31 | + const { createMessage } = useMessage(); | ||
32 | + | ||
33 | + const handleValidateHasSameIdentifier = (formValue?: StructJSON) => { | ||
34 | + const { identifier } = formValue || {}; | ||
35 | + if ( | ||
36 | + unref(currentMode) === DataActionModeEnum.CREATE && | ||
37 | + props.value.filter((item) => item.identifier === identifier).length >= 1 | ||
38 | + ) { | ||
39 | + const message = '存在一致的标识符'; | ||
40 | + createMessage.warn(message); | ||
41 | + return Promise.reject(message); | ||
42 | + } | ||
43 | + | ||
44 | + return Promise.resolve(); | ||
45 | + }; | ||
46 | + | ||
47 | + const handleOk = async () => { | ||
48 | + await unref(dataTypeFormRef)?.validate?.(); | ||
49 | + const value = unref(dataTypeFormRef)?.getFieldsValue?.(); | ||
50 | + await handleValidateHasSameIdentifier(value); | ||
51 | + emits('complete', value); | ||
52 | + }; | ||
53 | +</script> | ||
54 | + | ||
55 | +<template> | ||
56 | + <BasicModal | ||
57 | + @register="registerModal" | ||
58 | + title="新增参数" | ||
59 | + @ok="handleOk" | ||
60 | + :width="480" | ||
61 | + :show-ok-btn="mode !== DataActionModeEnum.READ" | ||
62 | + > | ||
63 | + <DataTypeForm ref="dataTypeFormRef" :data-type="dataType" show-remark :mode="mode" /> | ||
64 | + </BasicModal> | ||
65 | +</template> |
1 | +import { FormFieldsEnum, FormFieldsNameEnum } from '../config'; | ||
2 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
3 | +import { FormSchema } from '/@/components/Form'; | ||
4 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
5 | +import { DataTypeEnum, FunctionTypeEnum, FunctionTypeNameEnum } from '/@/enums/objectModelEnum'; | ||
6 | +import { isNullOrUnDef } from '/@/utils/is'; | ||
7 | + | ||
8 | +export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => { | ||
9 | + value = value || {}; | ||
10 | + const { min, max } = value; | ||
11 | + if (min > max) { | ||
12 | + return Promise.reject('最大值小于最小值'); | ||
13 | + } | ||
14 | + return Promise.resolve(); | ||
15 | +}; | ||
16 | + | ||
17 | +export const getFormSchemas = (): FormSchema[] => { | ||
18 | + return [ | ||
19 | + { | ||
20 | + field: FormFieldsEnum.FUNCTION_TYPE, | ||
21 | + label: FormFieldsNameEnum.FUNCTION_TYPE, | ||
22 | + component: 'Segmented', | ||
23 | + defaultValue: FunctionTypeEnum.PROPERTIES, | ||
24 | + required: true, | ||
25 | + componentProps: ({ formActionType }) => { | ||
26 | + return { | ||
27 | + options: Object.keys(FunctionTypeEnum).map((key) => ({ | ||
28 | + title: FunctionTypeNameEnum[key], | ||
29 | + value: FunctionTypeEnum[key], | ||
30 | + })), | ||
31 | + onChange() { | ||
32 | + const { setFieldsValue, clearValidate } = formActionType; | ||
33 | + setFieldsValue({ | ||
34 | + [FormFieldsEnum.INPUT_DATA]: [], | ||
35 | + [FormFieldsEnum.OUTPUT_DATA]: [], | ||
36 | + [FormFieldsEnum.STRUCT_DATA]: [], | ||
37 | + [FormFieldsEnum.SERVICE_COMMAND]: null, | ||
38 | + }); | ||
39 | + clearValidate(); | ||
40 | + }, | ||
41 | + }; | ||
42 | + }, | ||
43 | + }, | ||
44 | + { | ||
45 | + field: FormFieldsEnum.FUNCTION_NAME, | ||
46 | + label: FormFieldsNameEnum.FUNCTION_NAME, | ||
47 | + component: 'Input', | ||
48 | + required: true, | ||
49 | + dynamicRules: ({ values }) => { | ||
50 | + return [ | ||
51 | + { required: true, message: '请输入功能名称' }, | ||
52 | + { | ||
53 | + validator: () => { | ||
54 | + const reg = /[,,]+/; | ||
55 | + if (reg.test(values?.[FormFieldsEnum.FUNCTION_NAME])) { | ||
56 | + return Promise.reject(`${FormFieldsNameEnum.FUNCTION_NAME}不能包含逗号`); | ||
57 | + } | ||
58 | + return Promise.resolve(); | ||
59 | + }, | ||
60 | + }, | ||
61 | + ]; | ||
62 | + }, | ||
63 | + componentProps: { | ||
64 | + placeholder: `请输入${FormFieldsNameEnum.FUNCTION_NAME}`, | ||
65 | + }, | ||
66 | + }, | ||
67 | + { | ||
68 | + field: FormFieldsEnum.IDENTIFIER, | ||
69 | + label: FormFieldsNameEnum.IDENTIFIER, | ||
70 | + required: true, | ||
71 | + component: 'Input', | ||
72 | + componentProps: { | ||
73 | + maxLength: 128, | ||
74 | + placeholder: '请输入标识符', | ||
75 | + }, | ||
76 | + dynamicRules: ({ values }) => { | ||
77 | + return [ | ||
78 | + { required: true, message: '请输入标识符' }, | ||
79 | + { | ||
80 | + validator: () => { | ||
81 | + const reg = /[,,]+/; | ||
82 | + if (reg.test(values?.[FormFieldsEnum.IDENTIFIER])) { | ||
83 | + return Promise.reject(`${FormFieldsNameEnum.IDENTIFIER}不能包含逗号`); | ||
84 | + } | ||
85 | + return Promise.resolve(); | ||
86 | + }, | ||
87 | + }, | ||
88 | + ]; | ||
89 | + }, | ||
90 | + }, | ||
91 | + { | ||
92 | + field: FormFieldsEnum.DATA_TYPE, | ||
93 | + label: FormFieldsNameEnum.DATA_TYPE, | ||
94 | + required: true, | ||
95 | + component: 'ApiSelect', | ||
96 | + defaultValue: DataTypeEnum.NUMBER_INT, | ||
97 | + ifShow: ({ model }) => | ||
98 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
99 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
100 | + ), | ||
101 | + componentProps: () => { | ||
102 | + return { | ||
103 | + placeholder: '请选择数据类型', | ||
104 | + api: async (params: Recordable) => { | ||
105 | + const result = await findDictItemByCode(params); | ||
106 | + return result; | ||
107 | + }, | ||
108 | + params: { | ||
109 | + dictCode: DictEnum.DATA_TYPE, | ||
110 | + }, | ||
111 | + labelField: 'itemText', | ||
112 | + valueField: 'itemValue', | ||
113 | + getPopupContainer: () => document.body, | ||
114 | + }; | ||
115 | + }, | ||
116 | + }, | ||
117 | + { | ||
118 | + field: FormFieldsEnum.VALUE_RANGE, | ||
119 | + label: FormFieldsNameEnum.VALUE_RANGE, | ||
120 | + component: 'CustomMinMaxInput', | ||
121 | + valueField: 'value', | ||
122 | + changeEvent: 'update:value', | ||
123 | + ifShow: ({ model }) => | ||
124 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
125 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
126 | + ) && | ||
127 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
128 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
129 | + rules: [{ validator: validateValueRange }], | ||
130 | + }, | ||
131 | + { | ||
132 | + field: FormFieldsEnum.STEP, | ||
133 | + label: FormFieldsNameEnum.STEP, | ||
134 | + component: 'InputNumber', | ||
135 | + componentProps: { | ||
136 | + maxLength: 255, | ||
137 | + placeholder: '请输入步长', | ||
138 | + min: 1, | ||
139 | + formatter: (value: number | string) => { | ||
140 | + return value ? Math.floor(Number(value)) : value; | ||
141 | + }, | ||
142 | + }, | ||
143 | + ifShow: ({ model }) => | ||
144 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
145 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
146 | + ) && | ||
147 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
148 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
149 | + dynamicRules: ({ model }) => { | ||
150 | + const valueRange = model[FormFieldsEnum.VALUE_RANGE] || {}; | ||
151 | + const { min, max } = valueRange; | ||
152 | + const step = model[FormFieldsEnum.STEP]; | ||
153 | + return [ | ||
154 | + { | ||
155 | + validator: () => { | ||
156 | + if ([min, max].every(isNullOrUnDef)) return Promise.resolve(); | ||
157 | + if (step > max - min) { | ||
158 | + return Promise.reject('步长不能大于取值范围的差值'); | ||
159 | + } | ||
160 | + return Promise.resolve(); | ||
161 | + }, | ||
162 | + }, | ||
163 | + ]; | ||
164 | + }, | ||
165 | + }, | ||
166 | + { | ||
167 | + field: FormFieldsEnum.UNIT_NAME, | ||
168 | + label: FormFieldsNameEnum.UNIT_NAME, | ||
169 | + component: 'Input', | ||
170 | + show: false, | ||
171 | + }, | ||
172 | + { | ||
173 | + field: FormFieldsEnum.UNIT, | ||
174 | + label: FormFieldsNameEnum.UNIT, | ||
175 | + component: 'ApiSelect', | ||
176 | + componentProps: ({ formActionType }) => { | ||
177 | + const { setFieldsValue } = formActionType; | ||
178 | + return { | ||
179 | + placeholder: '请选择单位', | ||
180 | + api: async (params: Recordable) => { | ||
181 | + const list = await findDictItemByCode(params); | ||
182 | + list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`)); | ||
183 | + return list; | ||
184 | + }, | ||
185 | + params: { | ||
186 | + dictCode: DictEnum.ATTRIBUTE_UNIT, | ||
187 | + }, | ||
188 | + labelInValue: true, | ||
189 | + labelField: 'itemText', | ||
190 | + valueField: 'itemValue', | ||
191 | + onChange(_, record: Record<'label' | 'value', string>) { | ||
192 | + if (record) { | ||
193 | + const { label } = record; | ||
194 | + setFieldsValue({ [FormFieldsEnum.UNIT_NAME]: label }); | ||
195 | + } | ||
196 | + }, | ||
197 | + getPopupContainer: () => document.body, | ||
198 | + showSearch: true, | ||
199 | + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | ||
200 | + let { label, value } = option; | ||
201 | + label = label.toLowerCase(); | ||
202 | + value = value.toLowerCase(); | ||
203 | + inputValue = inputValue.toLowerCase(); | ||
204 | + return label.includes(inputValue) || value.includes(inputValue); | ||
205 | + }, | ||
206 | + }; | ||
207 | + }, | ||
208 | + ifShow: ({ model }) => | ||
209 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
210 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
211 | + ) && | ||
212 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
213 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
214 | + }, | ||
215 | + { | ||
216 | + field: FormFieldsEnum.BOOL_CLOSE, | ||
217 | + component: 'Input', | ||
218 | + required: true, | ||
219 | + label: FormFieldsNameEnum.BOOL_CLOSE, | ||
220 | + componentProps: { | ||
221 | + placeholder: '如:关', | ||
222 | + }, | ||
223 | + defaultValue: '关', | ||
224 | + ifShow: ({ model }) => | ||
225 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
226 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
227 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | ||
228 | + dynamicRules: ({ model }) => { | ||
229 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | ||
230 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | ||
231 | + return [ | ||
232 | + { | ||
233 | + required: true, | ||
234 | + message: `布尔值不能为空`, | ||
235 | + }, | ||
236 | + { | ||
237 | + validator() { | ||
238 | + if (open === close) return Promise.reject('布尔值不能相同'); | ||
239 | + return Promise.resolve(); | ||
240 | + }, | ||
241 | + }, | ||
242 | + ]; | ||
243 | + }, | ||
244 | + }, | ||
245 | + { | ||
246 | + field: FormFieldsEnum.BOOL_OPEN, | ||
247 | + component: 'Input', | ||
248 | + required: true, | ||
249 | + label: FormFieldsNameEnum.BOOL_OPEN, | ||
250 | + componentProps: { | ||
251 | + placeholder: '如:开', | ||
252 | + }, | ||
253 | + defaultValue: '开', | ||
254 | + ifShow: ({ model }) => | ||
255 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
256 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
257 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | ||
258 | + dynamicRules: ({ model }) => { | ||
259 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | ||
260 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | ||
261 | + return [ | ||
262 | + { | ||
263 | + required: true, | ||
264 | + message: `布尔值不能为空`, | ||
265 | + }, | ||
266 | + { | ||
267 | + validator() { | ||
268 | + if (open === close) return Promise.reject('布尔值不能相同'); | ||
269 | + return Promise.resolve(); | ||
270 | + }, | ||
271 | + }, | ||
272 | + ]; | ||
273 | + }, | ||
274 | + }, | ||
275 | + { | ||
276 | + field: FormFieldsEnum.LENGTH, | ||
277 | + component: 'Input', | ||
278 | + required: true, | ||
279 | + label: FormFieldsNameEnum.LENGTH, | ||
280 | + defaultValue: '10240', | ||
281 | + colProps: { | ||
282 | + span: 8, | ||
283 | + }, | ||
284 | + componentProps: { | ||
285 | + placeholder: '请输入数据长度', | ||
286 | + }, | ||
287 | + renderComponentContent: () => { | ||
288 | + return { | ||
289 | + suffix: () => '字节', | ||
290 | + }; | ||
291 | + }, | ||
292 | + ifShow: ({ model }) => | ||
293 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
294 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
295 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRING, | ||
296 | + }, | ||
297 | + { | ||
298 | + field: FormFieldsEnum.ENUMS_DATA, | ||
299 | + label: FormFieldsNameEnum.ENUMS_DATA, | ||
300 | + component: 'Input', | ||
301 | + slot: FormFieldsEnum.ENUMS_DATA, | ||
302 | + changeEvent: 'update:value', | ||
303 | + valueField: 'value', | ||
304 | + ifShow: ({ model }) => | ||
305 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
306 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
307 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.ENUM, | ||
308 | + }, | ||
309 | + { | ||
310 | + field: FormFieldsEnum.STRUCT_DATA, | ||
311 | + label: FormFieldsNameEnum.STRUCT_DATA, | ||
312 | + component: 'Input', | ||
313 | + slot: FormFieldsEnum.STRUCT_DATA, | ||
314 | + changeEvent: 'update:value', | ||
315 | + valueField: 'value', | ||
316 | + required: true, | ||
317 | + ifShow: ({ model }) => | ||
318 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRUCT && | ||
319 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
320 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
321 | + ), | ||
322 | + }, | ||
323 | + ]; | ||
324 | +}; |
1 | +export { default as StructFormItem } from './index.vue'; |
1 | +<script setup lang="ts"> | ||
2 | + import { Button, Divider } from 'ant-design-vue'; | ||
3 | + import { cloneDeep } from 'lodash'; | ||
4 | + import { toRaw, unref } from 'vue'; | ||
5 | + import CreateStructModal from './CreateStructModal.vue'; | ||
6 | + import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
7 | + import { useModal } from '/@/components/Modal'; | ||
8 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
9 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
10 | + | ||
11 | + const props = withDefaults( | ||
12 | + defineProps<{ | ||
13 | + buttonName?: string; | ||
14 | + disabled?: boolean; | ||
15 | + value?: StructJSON[]; | ||
16 | + dataType?: DataTypeEnum[]; | ||
17 | + mode?: DataActionModeEnum; | ||
18 | + }>(), | ||
19 | + { | ||
20 | + buttonName: '+ 新增参数', | ||
21 | + value: () => [], | ||
22 | + dataType: () => Object.values(DataTypeEnum), | ||
23 | + } | ||
24 | + ); | ||
25 | + | ||
26 | + const emits = defineEmits(['update:value']); | ||
27 | + | ||
28 | + const [registerModal, { openModal, closeModal }] = useModal(); | ||
29 | + | ||
30 | + const handleOpenCreateModal = () => { | ||
31 | + openModal(true, { | ||
32 | + mode: DataActionModeEnum.CREATE, | ||
33 | + } as ModalParamsType); | ||
34 | + }; | ||
35 | + | ||
36 | + const handleEdit = (item: StructJSON) => { | ||
37 | + openModal(true, { | ||
38 | + mode: DataActionModeEnum.UPDATE, | ||
39 | + record: cloneDeep(item), | ||
40 | + } as ModalParamsType); | ||
41 | + }; | ||
42 | + | ||
43 | + const handleDelete = (item: StructJSON) => { | ||
44 | + const index = props.value.findIndex((temp) => item.identifier === temp.identifier); | ||
45 | + | ||
46 | + if (~index) { | ||
47 | + const _values = cloneDeep(props.value); | ||
48 | + _values.splice(index, 1); | ||
49 | + emits('update:value', _values); | ||
50 | + } | ||
51 | + }; | ||
52 | + | ||
53 | + const handleEditComplete = (value: StructJSON) => { | ||
54 | + const _value = cloneDeep(toRaw(unref(props.value))); | ||
55 | + const index = _value.findIndex((item) => item.identifier === value.identifier); | ||
56 | + ~index ? _value.splice(index, 1, value) : _value.push(value); | ||
57 | + emits('update:value', _value); | ||
58 | + closeModal(); | ||
59 | + }; | ||
60 | +</script> | ||
61 | + | ||
62 | +<template> | ||
63 | + <section> | ||
64 | + <main> | ||
65 | + <div | ||
66 | + v-for="item in value" | ||
67 | + :key="item.identifier" | ||
68 | + class="flex items-center justify-between px-2 py-1 mb-2 bg-blue-50 text-gray-600" | ||
69 | + > | ||
70 | + <div> | ||
71 | + <span>参数名称:</span> | ||
72 | + <span class="ml-1">{{ item.functionName }}</span> | ||
73 | + </div> | ||
74 | + <div> | ||
75 | + <Button type="link" @click="handleEdit(item)"> | ||
76 | + {{ mode === DataActionModeEnum.READ ? '查看' : '编辑' }} | ||
77 | + </Button> | ||
78 | + <Divider type="vertical" /> | ||
79 | + <Button | ||
80 | + type="link" | ||
81 | + @click="handleDelete(item)" | ||
82 | + :disabled="mode === DataActionModeEnum.READ" | ||
83 | + > | ||
84 | + 删除 | ||
85 | + </Button> | ||
86 | + </div> | ||
87 | + </div> | ||
88 | + </main> | ||
89 | + <Button type="link" @click="handleOpenCreateModal" :disabled="mode === DataActionModeEnum.READ"> | ||
90 | + {{ buttonName }} | ||
91 | + </Button> | ||
92 | + <CreateStructModal | ||
93 | + :value="value" | ||
94 | + @register="registerModal" | ||
95 | + @complete="handleEditComplete" | ||
96 | + :dataType="dataType" | ||
97 | + :mode="mode" | ||
98 | + /> | ||
99 | + </section> | ||
100 | +</template> | ||
101 | +./index.config |
1 | +import { unref } from 'vue'; | ||
2 | +import { createHexCommandRuleValidator } from '.'; | ||
3 | +import { createFunctionNameFormItem, createIdentifierFormItem } from './DataTypeForm/config'; | ||
4 | +import { useObjectModelFormContext } from './useObjectModelFormContext'; | ||
5 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
6 | +import { FormSchema } from '/@/components/Form'; | ||
7 | +import { | ||
8 | + ServiceCallTypeEnum, | ||
9 | + ServiceCallTypeNameEnum, | ||
10 | + TransportTypeEnum, | ||
11 | +} from '/@/enums/deviceEnum'; | ||
12 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
13 | +import { | ||
14 | + ObjectModelAccessModeEnum, | ||
15 | + FunctionTypeEnum, | ||
16 | + FunctionTypeNameEnum, | ||
17 | + ObjectEventTypeEnum, | ||
18 | +} from '/@/enums/objectModelEnum'; | ||
19 | +import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
20 | + | ||
21 | +export enum FormFieldsEnum { | ||
22 | + FUNCTION_TYPE = 'functionType', | ||
23 | + FUNCTION_NAME = 'functionName', | ||
24 | + IDENTIFIER = 'identifier', | ||
25 | + DATA_TYPE = 'dataType', | ||
26 | + ACCESS_MODE = 'accessMode', | ||
27 | + VALUE_RANGE = 'valueRange', | ||
28 | + STEP = 'step', | ||
29 | + UNIT = 'unit', | ||
30 | + UNIT_NAME = 'unitName', | ||
31 | + REMARK = 'remark', | ||
32 | + BOOL_CLOSE = 'boolClose', | ||
33 | + BOOL_OPEN = 'boolOpen', | ||
34 | + LENGTH = 'length', | ||
35 | + EXTENSION_DESC = 'extensionDesc', | ||
36 | + ENUMS_DATA = 'enumsData', | ||
37 | + STRUCT_DATA = 'structData', | ||
38 | + | ||
39 | + CALL_TYPE = 'callType', | ||
40 | + SERVICE_COMMAND = 'serviceCommand', | ||
41 | + INPUT_DATA = 'inputData', | ||
42 | + OUTPUT_DATA = 'outputData', | ||
43 | + | ||
44 | + EVENT_TYPE = 'eventType', | ||
45 | + | ||
46 | + DATA_TYPE_FORM = 'dataTypeForm', | ||
47 | +} | ||
48 | + | ||
49 | +export enum FormFieldsNameEnum { | ||
50 | + FUNCTION_TYPE = '功能类型', | ||
51 | + FUNCTION_NAME = '功能名称', | ||
52 | + IDENTIFIER = '功能标识符', | ||
53 | + DATA_TYPE = '数据类型', | ||
54 | + ACCESS_MODE = '读写类型', | ||
55 | + VALUE_RANGE = '取值范围', | ||
56 | + STEP = '步长', | ||
57 | + UNIT = '单位', | ||
58 | + UNIT_NAME = '单位名称', | ||
59 | + REMARK = '备注', | ||
60 | + BOOL_CLOSE = '0 - 关', | ||
61 | + BOOL_OPEN = '1 - 开', | ||
62 | + LENGTH = '数据长度(字节)', | ||
63 | + EXTENSION_DESC = '拓展描述符', | ||
64 | + ENUMS_DATA = '枚举项', | ||
65 | + STRUCT_DATA = 'JSON对象', | ||
66 | + | ||
67 | + CALL_TYPE = '调用方式', | ||
68 | + SERVICE_COMMAND = '输入参数', | ||
69 | + INPUT_DATA = '输入参数', | ||
70 | + OUTPUT_DATA = '输出参数', | ||
71 | + | ||
72 | + EVENT_TYPE = '事件类型', | ||
73 | +} | ||
74 | + | ||
75 | +export const getFormSchemas = ({ | ||
76 | + transportType, | ||
77 | +}: { | ||
78 | + transportType?: string; | ||
79 | + mode?: DataActionModeEnum; | ||
80 | +}): FormSchema[] => { | ||
81 | + return [ | ||
82 | + { | ||
83 | + field: FormFieldsEnum.FUNCTION_TYPE, | ||
84 | + label: FormFieldsNameEnum.FUNCTION_TYPE, | ||
85 | + component: 'Segmented', | ||
86 | + defaultValue: FunctionTypeEnum.PROPERTIES, | ||
87 | + required: true, | ||
88 | + dynamicDisabled: () => { | ||
89 | + const { getModalMode } = useObjectModelFormContext(); | ||
90 | + return unref(getModalMode) !== DataActionModeEnum.CREATE; | ||
91 | + }, | ||
92 | + componentProps: ({ formActionType }) => { | ||
93 | + return { | ||
94 | + options: Object.keys(FunctionTypeEnum).map((key) => ({ | ||
95 | + title: FunctionTypeNameEnum[key], | ||
96 | + value: FunctionTypeEnum[key], | ||
97 | + })), | ||
98 | + onChange() { | ||
99 | + const { setFieldsValue, clearValidate } = formActionType; | ||
100 | + setFieldsValue({ | ||
101 | + [FormFieldsEnum.INPUT_DATA]: [], | ||
102 | + [FormFieldsEnum.OUTPUT_DATA]: [], | ||
103 | + [FormFieldsEnum.STRUCT_DATA]: [], | ||
104 | + [FormFieldsEnum.SERVICE_COMMAND]: null, | ||
105 | + }); | ||
106 | + clearValidate(); | ||
107 | + }, | ||
108 | + }; | ||
109 | + }, | ||
110 | + }, | ||
111 | + { | ||
112 | + field: FormFieldsEnum.DATA_TYPE_FORM, | ||
113 | + component: 'Input', | ||
114 | + label: '', | ||
115 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.PROPERTIES, | ||
116 | + colSlot: FormFieldsEnum.DATA_TYPE_FORM, | ||
117 | + }, | ||
118 | + createFunctionNameFormItem(), | ||
119 | + createIdentifierFormItem(), | ||
120 | + { | ||
121 | + field: FormFieldsEnum.EXTENSION_DESC, | ||
122 | + component: 'Input', | ||
123 | + label: FormFieldsNameEnum.EXTENSION_DESC, | ||
124 | + slot: FormFieldsEnum.EXTENSION_DESC, | ||
125 | + ifShow: ({ model }) => | ||
126 | + transportType === TransportTypeEnum.TCP && | ||
127 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
128 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
129 | + ), | ||
130 | + }, | ||
131 | + { | ||
132 | + field: FormFieldsEnum.ACCESS_MODE, | ||
133 | + component: 'ApiRadioGroup', | ||
134 | + label: FormFieldsNameEnum.ACCESS_MODE, | ||
135 | + required: true, | ||
136 | + ifShow: ({ model }) => | ||
137 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
138 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
139 | + ), | ||
140 | + defaultValue: ObjectModelAccessModeEnum.READ_AND_WRITE, | ||
141 | + componentProps: { | ||
142 | + placeholder: '请选择读写类型', | ||
143 | + api: findDictItemByCode, | ||
144 | + params: { | ||
145 | + dictCode: DictEnum.READ_WRITE_TYP, | ||
146 | + }, | ||
147 | + labelField: 'itemText', | ||
148 | + valueField: 'itemValue', | ||
149 | + }, | ||
150 | + }, | ||
151 | + | ||
152 | + // 服务 | ||
153 | + { | ||
154 | + field: FormFieldsEnum.CALL_TYPE, | ||
155 | + label: FormFieldsNameEnum.CALL_TYPE, | ||
156 | + component: 'RadioGroup', | ||
157 | + defaultValue: ServiceCallTypeEnum.ASYNC, | ||
158 | + required: true, | ||
159 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE, | ||
160 | + componentProps: () => { | ||
161 | + return { | ||
162 | + options: Object.keys(ServiceCallTypeEnum).map((value) => ({ | ||
163 | + label: ServiceCallTypeNameEnum[value], | ||
164 | + value, | ||
165 | + })), | ||
166 | + }; | ||
167 | + }, | ||
168 | + }, | ||
169 | + { | ||
170 | + field: FormFieldsEnum.SERVICE_COMMAND, | ||
171 | + label: FormFieldsNameEnum.SERVICE_COMMAND, | ||
172 | + component: 'Input', | ||
173 | + required: true, | ||
174 | + rules: [{ message: '输入参数为必填项', required: true }, ...createHexCommandRuleValidator()], | ||
175 | + ifShow: ({ model }) => | ||
176 | + model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE && | ||
177 | + transportType === TransportTypeEnum.TCP, | ||
178 | + componentProps: () => { | ||
179 | + return { | ||
180 | + placeholder: '请输入ASCII或HEX服务命令', | ||
181 | + }; | ||
182 | + }, | ||
183 | + }, | ||
184 | + { | ||
185 | + field: FormFieldsEnum.INPUT_DATA, | ||
186 | + label: FormFieldsNameEnum.INPUT_DATA, | ||
187 | + component: 'Input', | ||
188 | + slot: FormFieldsEnum.INPUT_DATA, | ||
189 | + changeEvent: 'update:value', | ||
190 | + valueField: 'value', | ||
191 | + required: true, | ||
192 | + ifShow: ({ model }) => | ||
193 | + model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE && | ||
194 | + transportType !== TransportTypeEnum.TCP, | ||
195 | + }, | ||
196 | + | ||
197 | + // 事件 | ||
198 | + { | ||
199 | + field: FormFieldsEnum.EVENT_TYPE, | ||
200 | + label: FormFieldsNameEnum.EVENT_TYPE, | ||
201 | + required: true, | ||
202 | + component: 'ApiRadioGroup', | ||
203 | + defaultValue: ObjectEventTypeEnum.INFO, | ||
204 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.EVENTS, | ||
205 | + componentProps: () => { | ||
206 | + return { | ||
207 | + placeholder: '请选择事件类型', | ||
208 | + api: findDictItemByCode, | ||
209 | + params: { | ||
210 | + dictCode: DictEnum.EVENT_TYPE, | ||
211 | + }, | ||
212 | + labelField: 'itemText', | ||
213 | + valueField: 'itemValue', | ||
214 | + getPopupContainer: () => document.body, | ||
215 | + }; | ||
216 | + }, | ||
217 | + }, | ||
218 | + { | ||
219 | + field: FormFieldsEnum.OUTPUT_DATA, | ||
220 | + label: FormFieldsNameEnum.OUTPUT_DATA, | ||
221 | + component: 'Input', | ||
222 | + slot: FormFieldsEnum.OUTPUT_DATA, | ||
223 | + changeEvent: 'update:value', | ||
224 | + valueField: 'value', | ||
225 | + ifShow: ({ model }) => | ||
226 | + [FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
227 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
228 | + ), | ||
229 | + }, | ||
230 | + | ||
231 | + { | ||
232 | + field: FormFieldsEnum.REMARK, | ||
233 | + label: FormFieldsNameEnum.REMARK, | ||
234 | + component: 'InputTextArea', | ||
235 | + componentProps: { | ||
236 | + rows: 4, | ||
237 | + maxLength: 100, | ||
238 | + placeholder: '请输入描述', | ||
239 | + }, | ||
240 | + }, | ||
241 | + ]; | ||
242 | +}; |
1 | +import { Rule } from '/@/components/Form'; | ||
2 | + | ||
3 | +export { default as ObjectModelForm } from './index.vue'; | ||
4 | + | ||
5 | +export function createHexCommandRuleValidator(): Rule[] { | ||
6 | + return [ | ||
7 | + { | ||
8 | + message: '请输入ASCII或HEX服务命令(0~9/A~F)', | ||
9 | + validator(_rule, value, _callback) { | ||
10 | + const reg = /^[\s0-9a-fA-F]+$/; | ||
11 | + | ||
12 | + if (reg.test(value)) return Promise.resolve(); | ||
13 | + return Promise.reject('请输入ASCII或HEX服务命令(0~9/A~F)'); | ||
14 | + }, | ||
15 | + }, | ||
16 | + ]; | ||
17 | +} |
1 | +<script setup lang="ts"> | ||
2 | + import { useForm, BasicForm } from '/@/components/Form'; | ||
3 | + import { getFormSchemas, FormFieldsEnum } from './config'; | ||
4 | + import { StructFormItem } from './StructFormItem'; | ||
5 | + import { useObjectFormData } from './useObjectFormData'; | ||
6 | + import { computed, ref, unref } from 'vue'; | ||
7 | + import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
8 | + import { DataTypeForm } from './DataTypeForm'; | ||
9 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
10 | + import { ExtendDesc } from './ExtendDesc'; | ||
11 | + import { createObjectModelFormContext } from './useObjectModelFormContext'; | ||
12 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
13 | + | ||
14 | + const props = defineProps<{ | ||
15 | + transportType?: TransportTypeEnum; | ||
16 | + mode?: DataActionModeEnum; | ||
17 | + }>(); | ||
18 | + | ||
19 | + const dataTypeFormRef = ref<InstanceType<typeof DataTypeForm>>(); | ||
20 | + | ||
21 | + const [register, formActionType] = useForm({ | ||
22 | + schemas: getFormSchemas(props), | ||
23 | + layout: 'vertical', | ||
24 | + showActionButtonGroup: false, | ||
25 | + }); | ||
26 | + | ||
27 | + const { getFieldsValue, setFieldsValue, validate, resetFieldsValue } = useObjectFormData({ | ||
28 | + formActionType, | ||
29 | + dataTypeFormRef, | ||
30 | + transportType: props.transportType!, | ||
31 | + }); | ||
32 | + | ||
33 | + const objectModelType = ref(DataTypeEnum.NUMBER_INT); | ||
34 | + | ||
35 | + createObjectModelFormContext({ | ||
36 | + getTransportType: computed(() => props.transportType), | ||
37 | + getDataType: computed(() => unref(objectModelType)), | ||
38 | + getModalMode: computed(() => props.mode), | ||
39 | + }); | ||
40 | + | ||
41 | + const handleDataTypeFormChange = (field: string, value: any) => { | ||
42 | + if (field === FormFieldsEnum.DATA_TYPE) { | ||
43 | + objectModelType.value = value; | ||
44 | + } | ||
45 | + }; | ||
46 | + | ||
47 | + defineExpose({ | ||
48 | + getFieldsValue, | ||
49 | + setFieldsValue, | ||
50 | + validate, | ||
51 | + resetFieldsValue, | ||
52 | + }); | ||
53 | +</script> | ||
54 | + | ||
55 | +<template> | ||
56 | + <BasicForm @register="register" :disabled="mode === DataActionModeEnum.READ"> | ||
57 | + <template #dataTypeForm> | ||
58 | + <DataTypeForm | ||
59 | + ref="dataTypeFormRef" | ||
60 | + :mode="mode" | ||
61 | + @field-value-change="handleDataTypeFormChange" | ||
62 | + /> | ||
63 | + </template> | ||
64 | + <template #inputData="{ model, field }"> | ||
65 | + <StructFormItem v-model:value="model[field]" buttonName="+ 增加参数" :mode="mode" /> | ||
66 | + </template> | ||
67 | + <template #outputData="{ model, field }"> | ||
68 | + <StructFormItem v-model:value="model[field]" buttonName="+ 增加参数" :mode="mode" /> | ||
69 | + </template> | ||
70 | + <template #extensionDesc="{ model, field }"> | ||
71 | + <ExtendDesc v-model:value="model[field]" :disabled="mode === DataActionModeEnum.READ" /> | ||
72 | + </template> | ||
73 | + </BasicForm> | ||
74 | +</template> |
1 | +import { FormFieldsEnum } from './config'; | ||
2 | +import { ExtensionDesc, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
3 | +import { ServiceCallTypeEnum } from '/@/enums/deviceEnum'; | ||
4 | +import { | ||
5 | + FunctionTypeEnum, | ||
6 | + ObjectEventTypeEnum, | ||
7 | + ObjectModelAccessModeEnum, | ||
8 | +} from '/@/enums/objectModelEnum'; | ||
9 | + | ||
10 | +export interface ObjectModelFormGetFieldsValueType { | ||
11 | + [FormFieldsEnum.FUNCTION_TYPE]: FunctionTypeEnum; | ||
12 | + [FormFieldsEnum.ACCESS_MODE]?: ObjectModelAccessModeEnum; | ||
13 | + [FormFieldsEnum.REMARK]?: string; | ||
14 | + [FormFieldsEnum.FUNCTION_NAME]: string; | ||
15 | + [FormFieldsEnum.IDENTIFIER]: string; | ||
16 | + [FormFieldsEnum.EVENT_TYPE]?: ObjectEventTypeEnum; | ||
17 | + | ||
18 | + [FormFieldsEnum.CALL_TYPE]?: ServiceCallTypeEnum; | ||
19 | + [FormFieldsEnum.INPUT_DATA]?: StructJSON[]; | ||
20 | + [FormFieldsEnum.OUTPUT_DATA]?: StructJSON[]; | ||
21 | + | ||
22 | + [FormFieldsEnum.SERVICE_COMMAND]?: string; | ||
23 | + [FormFieldsEnum.EXTENSION_DESC]?: ExtensionDesc; | ||
24 | + | ||
25 | + // EXTENSION_DESC = 'extensionDesc', | ||
26 | + // STRUCT_DATA = 'structData', | ||
27 | + // INPUT_DATA = 'inputData', | ||
28 | + // OUTPUT_DATA = 'outputData', | ||
29 | +} |
1 | +import DataTypeForm from './DataTypeForm.vue'; | ||
2 | +import { FormFieldsEnum } from './config'; | ||
3 | +import { ObjectModelFormGetFieldsValueType } from './types'; | ||
4 | +import { DefineComponentsBasicExpose } from '/#/utils'; | ||
5 | +import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
6 | +import { FormActionType } from '/@/components/Form'; | ||
7 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
8 | +import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | ||
9 | +import { Ref, toRaw, unref } from 'vue'; | ||
10 | + | ||
11 | +interface UseObjectFormDataParamsType { | ||
12 | + formActionType: FormActionType; | ||
13 | + dataTypeFormRef: Ref<InstanceType<typeof DataTypeForm> | undefined>; | ||
14 | + transportType: TransportTypeEnum; | ||
15 | +} | ||
16 | + | ||
17 | +function handlePropertiesType( | ||
18 | + value: ObjectModelFormGetFieldsValueType, | ||
19 | + dataTypeFormValue: StructJSON, | ||
20 | + transportType?: TransportTypeEnum | ||
21 | +): ModelOfMatterParams { | ||
22 | + const { functionType, accessMode, remark, extensionDesc } = value; | ||
23 | + const { functionName, identifier, dataType } = dataTypeFormValue; | ||
24 | + | ||
25 | + const result: ModelOfMatterParams = { | ||
26 | + functionType, | ||
27 | + functionName, | ||
28 | + identifier, | ||
29 | + functionJson: { | ||
30 | + dataType, | ||
31 | + }, | ||
32 | + accessMode, | ||
33 | + remark, | ||
34 | + }; | ||
35 | + | ||
36 | + if (transportType === TransportTypeEnum.TCP && extensionDesc) | ||
37 | + result.extensionDesc = extensionDesc; | ||
38 | + | ||
39 | + return result; | ||
40 | +} | ||
41 | + | ||
42 | +function handleServiceType( | ||
43 | + value: ObjectModelFormGetFieldsValueType, | ||
44 | + transportType: TransportTypeEnum | ||
45 | +): ModelOfMatterParams { | ||
46 | + const { | ||
47 | + functionName, | ||
48 | + identifier, | ||
49 | + functionType, | ||
50 | + remark, | ||
51 | + inputData = [], | ||
52 | + outputData = [], | ||
53 | + serviceCommand, | ||
54 | + } = value; | ||
55 | + | ||
56 | + return { | ||
57 | + functionType, | ||
58 | + functionName, | ||
59 | + identifier, | ||
60 | + remark, | ||
61 | + functionJson: { | ||
62 | + inputData: | ||
63 | + transportType === TransportTypeEnum.TCP | ||
64 | + ? [{ [FormFieldsEnum.SERVICE_COMMAND]: serviceCommand } as unknown as StructJSON] | ||
65 | + : inputData, | ||
66 | + outputData, | ||
67 | + }, | ||
68 | + }; | ||
69 | +} | ||
70 | + | ||
71 | +function handleEventType(value: ObjectModelFormGetFieldsValueType): ModelOfMatterParams { | ||
72 | + const { functionName, identifier, functionType, remark, eventType, outputData = [] } = value; | ||
73 | + | ||
74 | + return { | ||
75 | + functionType, | ||
76 | + functionName, | ||
77 | + identifier, | ||
78 | + remark, | ||
79 | + eventType, | ||
80 | + functionJson: { | ||
81 | + outputData, | ||
82 | + }, | ||
83 | + }; | ||
84 | +} | ||
85 | + | ||
86 | +export const useObjectFormData = ({ | ||
87 | + formActionType, | ||
88 | + dataTypeFormRef, | ||
89 | + transportType, | ||
90 | +}: UseObjectFormDataParamsType): DefineComponentsBasicExpose<ModelOfMatterParams> => { | ||
91 | + const getFieldsValue = (): ModelOfMatterParams => { | ||
92 | + const value = formActionType.getFieldsValue() as ObjectModelFormGetFieldsValueType; | ||
93 | + const { functionType } = value; | ||
94 | + | ||
95 | + if (functionType === FunctionTypeEnum.PROPERTIES) { | ||
96 | + const dataTypeFormValue = unref(dataTypeFormRef)?.getFieldsValue?.(); | ||
97 | + return handlePropertiesType(value, dataTypeFormValue!, transportType); | ||
98 | + } | ||
99 | + | ||
100 | + if (functionType === FunctionTypeEnum.EVENTS) { | ||
101 | + return handleEventType(value); | ||
102 | + } else { | ||
103 | + return handleServiceType(value, transportType); | ||
104 | + } | ||
105 | + }; | ||
106 | + | ||
107 | + const setFieldsValue = (values: ModelOfMatterParams) => { | ||
108 | + values = toRaw(unref(values)); | ||
109 | + const { | ||
110 | + functionName, | ||
111 | + identifier, | ||
112 | + functionJson, | ||
113 | + functionType, | ||
114 | + accessMode, | ||
115 | + callType, | ||
116 | + remark, | ||
117 | + extensionDesc, | ||
118 | + } = values; | ||
119 | + const { dataType, inputData = [], outputData = [] } = functionJson || {}; | ||
120 | + | ||
121 | + formActionType.setFieldsValue({ | ||
122 | + identifier, | ||
123 | + functionName, | ||
124 | + functionType, | ||
125 | + accessMode, | ||
126 | + callType, | ||
127 | + inputData, | ||
128 | + outputData, | ||
129 | + remark, | ||
130 | + serviceCommand: transportType === TransportTypeEnum.TCP ? inputData?.[0]?.serviceCommand : '', | ||
131 | + extensionDesc, | ||
132 | + } as ObjectModelFormGetFieldsValueType); | ||
133 | + | ||
134 | + if (functionType === FunctionTypeEnum.PROPERTIES) { | ||
135 | + unref(dataTypeFormRef)?.setFieldsValue({ | ||
136 | + functionName, | ||
137 | + identifier, | ||
138 | + dataType, | ||
139 | + } as StructJSON); | ||
140 | + } | ||
141 | + }; | ||
142 | + | ||
143 | + const validate = async () => { | ||
144 | + await formActionType.validate(); | ||
145 | + await unref(dataTypeFormRef)?.validate?.(); | ||
146 | + }; | ||
147 | + | ||
148 | + const resetFieldsValue = () => { | ||
149 | + formActionType.resetFields(); | ||
150 | + }; | ||
151 | + | ||
152 | + return { getFieldsValue, setFieldsValue, validate, resetFieldsValue }; | ||
153 | +}; |
1 | +import type { InjectionKey, ComputedRef } from 'vue'; | ||
2 | +import { createContext, useContext } from '/@/hooks/core/useContext'; | ||
3 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
4 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
5 | +import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
6 | + | ||
7 | +export interface ObjectModelFormContextProps { | ||
8 | + getTransportType: ComputedRef<TransportTypeEnum | undefined>; | ||
9 | + getDataType: ComputedRef<DataTypeEnum>; | ||
10 | + getModalMode: ComputedRef<DataActionModeEnum | undefined>; | ||
11 | +} | ||
12 | + | ||
13 | +const key: InjectionKey<ObjectModelFormContextProps> = Symbol(); | ||
14 | + | ||
15 | +export function createObjectModelFormContext(context: ObjectModelFormContextProps) { | ||
16 | + return createContext<ObjectModelFormContextProps>(context, key, { native: true }); | ||
17 | +} | ||
18 | + | ||
19 | +export function useObjectModelFormContext() { | ||
20 | + return useContext<ObjectModelFormContextProps>(key); | ||
21 | +} |
@@ -147,7 +147,6 @@ | @@ -147,7 +147,6 @@ | ||
147 | getModelList, | 147 | getModelList, |
148 | releaseModel, | 148 | releaseModel, |
149 | } from '/@/api/device/modelOfMatter'; | 149 | } from '/@/api/device/modelOfMatter'; |
150 | - import { OpenModelOfMatterModelParams, OpenModelMode } from './cpns/physical/types'; | ||
151 | import { | 150 | import { |
152 | ModelOfMatterItemRecordType, | 151 | ModelOfMatterItemRecordType, |
153 | ModelOfMatterParams, | 152 | ModelOfMatterParams, |
@@ -158,6 +157,7 @@ | @@ -158,6 +157,7 @@ | ||
158 | import { ExportModelCategory } from '/@/api/device/modelOfMatter'; | 157 | import { ExportModelCategory } from '/@/api/device/modelOfMatter'; |
159 | import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | 158 | import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; |
160 | import SelectImport from '../components/SelectImport.vue'; | 159 | import SelectImport from '../components/SelectImport.vue'; |
160 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
161 | 161 | ||
162 | const { isPlatformAdmin, isSysadmin } = useRole(); | 162 | const { isPlatformAdmin, isSysadmin } = useRole(); |
163 | defineEmits(['register']); | 163 | defineEmits(['register']); |
@@ -209,23 +209,17 @@ | @@ -209,23 +209,17 @@ | ||
209 | if (record) { | 209 | if (record) { |
210 | openModal(true, { | 210 | openModal(true, { |
211 | record, | 211 | record, |
212 | - mode: OpenModelMode.VIEW, | ||
213 | - } as OpenModelOfMatterModelParams); | 212 | + mode: DataActionModeEnum.READ, |
213 | + } as ModalParamsType<ModelOfMatterParams>); | ||
214 | } | 214 | } |
215 | }; | 215 | }; |
216 | 216 | ||
217 | // 新增或编辑 | 217 | // 新增或编辑 |
218 | const handleCreateOrEdit = (record?: ModelOfMatterParams) => { | 218 | const handleCreateOrEdit = (record?: ModelOfMatterParams) => { |
219 | - if (record) { | ||
220 | - openModal(true, { | ||
221 | - mode: OpenModelMode.UPDATE, | ||
222 | - record, | ||
223 | - } as OpenModelOfMatterModelParams); | ||
224 | - } else { | ||
225 | - openModal(true, { | ||
226 | - mode: OpenModelMode.CREATE, | ||
227 | - } as OpenModelOfMatterModelParams); | ||
228 | - } | 219 | + openModal(true, { |
220 | + record, | ||
221 | + mode: record ? DataActionModeEnum.UPDATE : DataActionModeEnum.CREATE, | ||
222 | + } as ModalParamsType<ModelOfMatterParams>); | ||
229 | }; | 223 | }; |
230 | 224 | ||
231 | const handleDeleteOrBatchDelete = async (record?: ModelOfMatterItemRecordType) => { | 225 | const handleDeleteOrBatchDelete = async (record?: ModelOfMatterItemRecordType) => { |
1 | <template> | 1 | <template> |
2 | - <div> | ||
3 | - <BasicModal | ||
4 | - :maskClosable="false" | ||
5 | - destroy-on-close | ||
6 | - v-bind="$attrs" | ||
7 | - width="55rem" | ||
8 | - @register="register" | ||
9 | - @ok="handleSubmit" | ||
10 | - @cancel="handleCancel" | ||
11 | - > | ||
12 | - <div class="relative"> | ||
13 | - <div v-if="openModalMode === OpenModelMode.CREATE"> | ||
14 | - <Typography> | ||
15 | - <TypographyParagraph> | ||
16 | - <blockquote class="bg-gray-100">{{ blockContent }}</blockquote> | ||
17 | - </TypographyParagraph> | ||
18 | - </Typography> | ||
19 | - </div> | ||
20 | - <Tabs | ||
21 | - v-if="openModalMode === OpenModelMode.CREATE" | ||
22 | - type="card" | ||
23 | - v-model:activeKey="activeKey" | ||
24 | - :size="size" | ||
25 | - > | ||
26 | - <TabPane :key="FunctionTypeEnum.PROPERTIES" tab="属性" /> | ||
27 | - <TabPane :key="FunctionTypeEnum.SERVICE" :disabled="isTCPGatewaySubDevice" tab="服务" /> | ||
28 | - <TabPane :key="FunctionTypeEnum.EVENTS" tab="事件" :disabled="isTCPGatewaySubDevice" /> | ||
29 | - </Tabs> | ||
30 | - <Attribute | ||
31 | - v-if="activeKey === FunctionTypeEnum.PROPERTIES" | ||
32 | - :openModalMode="openModalMode" | ||
33 | - :transportType="record?.transportType" | ||
34 | - ref="AttrRef" | ||
35 | - /> | ||
36 | - <Service | ||
37 | - v-if="activeKey === FunctionTypeEnum.SERVICE" | ||
38 | - :record="$props.record" | ||
39 | - :openModalMode="openModalMode" | ||
40 | - ref="ServiceRef" | ||
41 | - /> | ||
42 | - <Events | ||
43 | - v-if="activeKey === FunctionTypeEnum.EVENTS" | ||
44 | - :openModalMode="openModalMode" | ||
45 | - ref="EventsRef" | ||
46 | - /> | ||
47 | - <!-- <div | ||
48 | - v-if="openModalMode === OpenModelMode.VIEW" | ||
49 | - class="absolute w-full h-full top-0 cursor-not-allowed z-50" | ||
50 | - ></div> --> | ||
51 | - </div> | ||
52 | - </BasicModal> | ||
53 | - </div> | 2 | + <BasicModal v-bind="$attrs" :width="480" @register="register" @ok="handleSubmit"> |
3 | + <Typography> | ||
4 | + <TypographyParagraph> | ||
5 | + <blockquote class="bg-gray-100">{{ blockContent }}</blockquote> | ||
6 | + </TypographyParagraph> | ||
7 | + </Typography> | ||
8 | + <ObjectModelForm | ||
9 | + ref="objectModelElRef" | ||
10 | + :mode="openModalMode" | ||
11 | + :transport-type="(record.transportType as TransportTypeEnum)" | ||
12 | + /> | ||
13 | + </BasicModal> | ||
54 | </template> | 14 | </template> |
55 | <script lang="ts" setup> | 15 | <script lang="ts" setup> |
56 | - import { ref, unref, nextTick, computed } from 'vue'; | 16 | + import { ref, unref, nextTick } from 'vue'; |
57 | import { BasicModal, useModalInner } from '/@/components/Modal'; | 17 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
58 | - import { Tabs, TabPane, Typography, TypographyParagraph } from 'ant-design-vue'; | ||
59 | - import Attribute from './cpns/Attribute.vue'; | ||
60 | - import Service from './cpns/Service.vue'; | ||
61 | - import Events from './cpns/Events.vue'; | 18 | + import { Typography, TypographyParagraph } from 'ant-design-vue'; |
62 | import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | 19 | import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; |
63 | import { | 20 | import { |
64 | createModel, | 21 | createModel, |
@@ -66,128 +23,77 @@ | @@ -66,128 +23,77 @@ | ||
66 | createModelCategory, | 23 | createModelCategory, |
67 | updateModelCategory, | 24 | updateModelCategory, |
68 | } from '/@/api/device/modelOfMatter'; | 25 | } from '/@/api/device/modelOfMatter'; |
69 | - import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | 26 | + import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
70 | import { useMessage } from '/@/hooks/web/useMessage'; | 27 | import { useMessage } from '/@/hooks/web/useMessage'; |
71 | - import { OpenModelMode, OpenModelOfMatterModelParams } from './types/index'; | ||
72 | - import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | ||
73 | import { useRole } from '/@/hooks/business/useRole'; | 28 | import { useRole } from '/@/hooks/business/useRole'; |
29 | + import { ObjectModelForm } from '../../../components/ObjectModelForm'; | ||
30 | + import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
31 | + import { DataActionModeEnum, DataActionModeNameEnum } from '/@/enums/toolEnum'; | ||
74 | 32 | ||
75 | const { isPlatformAdmin, isSysadmin } = useRole(); | 33 | const { isPlatformAdmin, isSysadmin } = useRole(); |
76 | 34 | ||
77 | const emit = defineEmits(['register', 'success']); | 35 | const emit = defineEmits(['register', 'success']); |
78 | 36 | ||
37 | + const objectModelElRef = ref<InstanceType<typeof ObjectModelForm>>(); | ||
38 | + | ||
79 | const props = defineProps<{ | 39 | const props = defineProps<{ |
80 | record: DeviceRecord; | 40 | record: DeviceRecord; |
81 | }>(); | 41 | }>(); |
82 | 42 | ||
83 | - const isTCPGatewaySubDevice = computed(() => { | ||
84 | - const { record } = props; | ||
85 | - const { deviceType, transportType } = record || {}; | ||
86 | - return deviceType === DeviceTypeEnum.SENSOR && transportType === 'TCP'; | ||
87 | - }); | ||
88 | - | ||
89 | const blockContent = `属性一般是指设备的运行状态,如当前温度等;服务是指设备可被调用的方法,支持定义参数,如执行某项任务;事件则是指设备上报的通知,如告警,需要被及时处理。`; | 43 | const blockContent = `属性一般是指设备的运行状态,如当前温度等;服务是指设备可被调用的方法,支持定义参数,如执行某项任务;事件则是指设备上报的通知,如告警,需要被及时处理。`; |
90 | - const activeKey = ref<FunctionTypeEnum>(FunctionTypeEnum.PROPERTIES); | ||
91 | - const size = ref('small'); | ||
92 | - | ||
93 | - const AttrRef = ref<InstanceType<typeof Attribute>>(); | ||
94 | 44 | ||
95 | - const ServiceRef = ref<InstanceType<typeof Service>>(); | 45 | + const openModalMode = ref<DataActionModeEnum>(DataActionModeEnum.CREATE); |
96 | 46 | ||
97 | - const EventsRef = ref<InstanceType<typeof Events>>(); | ||
98 | - const openModalMode = ref<OpenModelMode>(OpenModelMode.CREATE); | ||
99 | - const openModalParams = ref<OpenModelOfMatterModelParams>(); | ||
100 | - | ||
101 | - const functionType = ref<FunctionTypeEnum>(); | ||
102 | const { createMessage } = useMessage(); | 47 | const { createMessage } = useMessage(); |
103 | 48 | ||
104 | - const setAttrFormData = (data: ModelOfMatterParams) => AttrRef.value?.setFormData(data); | ||
105 | - const setServiceFormData = (data: ModelOfMatterParams) => ServiceRef.value?.setFormData(data); | ||
106 | - const setEventsFormData = (data: ModelOfMatterParams) => EventsRef.value?.setFormData(data); | ||
107 | - | ||
108 | - const enums = { | ||
109 | - [FunctionTypeEnum.PROPERTIES]: setAttrFormData, | ||
110 | - [FunctionTypeEnum.SERVICE]: setServiceFormData, | ||
111 | - [FunctionTypeEnum.EVENTS]: setEventsFormData, | ||
112 | - }; | ||
113 | - | ||
114 | - function setFormData(type: FunctionTypeEnum, value: ModelOfMatterParams) { | ||
115 | - const setFn = enums[type]; | ||
116 | - setFn(value); | ||
117 | - } | 49 | + const currentActionModel = ref<ModelOfMatterParams>(); |
118 | 50 | ||
119 | const [register, { closeModal, setModalProps }] = useModalInner( | 51 | const [register, { closeModal, setModalProps }] = useModalInner( |
120 | - async (data: OpenModelOfMatterModelParams) => { | ||
121 | - openModalParams.value = data; | 52 | + async (data: ModalParamsType<ModelOfMatterParams>) => { |
122 | const { mode, record } = data; | 53 | const { mode, record } = data; |
123 | openModalMode.value = mode; | 54 | openModalMode.value = mode; |
124 | if (record) { | 55 | if (record) { |
125 | - functionType.value = data.record.functionType; | ||
126 | - activeKey.value = data.record.functionType; | ||
127 | await nextTick(); | 56 | await nextTick(); |
128 | - setFormData(record.functionType, record as unknown as ModelOfMatterParams); | ||
129 | } | 57 | } |
130 | - if (unref(openModalMode) === OpenModelMode.VIEW) { | ||
131 | - setModalProps({ | ||
132 | - showOkBtn: false, | ||
133 | - showCancelBtn: false, | ||
134 | - title: '查看物模型', | ||
135 | - }); | ||
136 | - } else { | ||
137 | - const title = unref(openModalMode) === OpenModelMode.UPDATE ? '编辑物模型' : '新增物模型'; | ||
138 | - setModalProps({ title, showOkBtn: true, showCancelBtn: true }); | 58 | + const title = `${DataActionModeNameEnum[mode]}物模型`; |
59 | + setModalProps({ | ||
60 | + showOkBtn: mode !== DataActionModeEnum.READ, | ||
61 | + title, | ||
62 | + }); | ||
63 | + | ||
64 | + if (mode !== DataActionModeEnum.CREATE && record) { | ||
65 | + currentActionModel.value = record; | ||
66 | + unref(objectModelElRef)?.setFieldsValue(record || {}); | ||
139 | } | 67 | } |
140 | - AttrRef.value?.setDisable(unref(openModalMode) === OpenModelMode.VIEW); | ||
141 | - EventsRef.value?.setDisable(unref(openModalMode) === OpenModelMode.VIEW); | ||
142 | - ServiceRef.value?.setDisable(unref(openModalMode) === OpenModelMode.VIEW); | ||
143 | } | 68 | } |
144 | ); | 69 | ); |
145 | 70 | ||
146 | - const handleCancel = (flag) => { | ||
147 | - AttrRef.value?.resetFormData(); | ||
148 | - ServiceRef.value?.resetFormData(); | ||
149 | - EventsRef.value?.resetFormData(); | ||
150 | - activeKey.value = FunctionTypeEnum.PROPERTIES; | ||
151 | - if (flag) { | ||
152 | - closeModal(); | ||
153 | - } | ||
154 | - }; | ||
155 | - | ||
156 | const handleSubmit = async () => { | 71 | const handleSubmit = async () => { |
157 | try { | 72 | try { |
158 | setModalProps({ loading: false, okButtonProps: { loading: true } }); | 73 | setModalProps({ loading: false, okButtonProps: { loading: true } }); |
74 | + await unref(objectModelElRef)?.validate?.(); | ||
75 | + const value = unref(objectModelElRef)!.getFieldsValue(); | ||
159 | 76 | ||
160 | - let params: Partial<ModelOfMatterParams>; | ||
161 | - if (activeKey.value == FunctionTypeEnum.PROPERTIES) { | ||
162 | - params = (await AttrRef.value?.getFormData()) || {}; | ||
163 | - } else if (activeKey.value == FunctionTypeEnum.SERVICE) { | ||
164 | - params = (await ServiceRef.value?.getFormData()) || {}; | ||
165 | - } else { | ||
166 | - params = (await EventsRef.value?.getFormData()) || {}; | ||
167 | - } | ||
168 | - params.deviceProfileId = props.record.id; | ||
169 | - | ||
170 | - if (unref(openModalMode) === OpenModelMode.CREATE) { | ||
171 | - (unref(isSysadmin) || unref(isPlatformAdmin)) && props.record.ifShowClass | ||
172 | - ? await createModelCategory({ | ||
173 | - ...params, | ||
174 | - deviceProfileId: undefined, | ||
175 | - categoryId: props.record.id, | ||
176 | - }) | ||
177 | - : await createModel(params); | ||
178 | - createMessage.success('创建成功'); | ||
179 | - } else { | ||
180 | - params.id = unref(openModalParams)?.record.id; | ||
181 | - (unref(isSysadmin) || unref(isPlatformAdmin)) && props.record.ifShowClass | ||
182 | - ? await updateModelCategory({ | ||
183 | - ...params, | ||
184 | - deviceProfileId: undefined, | ||
185 | - categoryId: props.record.id, | ||
186 | - }) | ||
187 | - : await updateModel(params); | ||
188 | - createMessage.success('修改成功'); | ||
189 | - } | 77 | + const isCategoryAction = |
78 | + (unref(isSysadmin) || unref(isPlatformAdmin)) && props.record.ifShowClass; | ||
79 | + | ||
80 | + const submitFn = | ||
81 | + unref(openModalMode) === DataActionModeEnum.CREATE | ||
82 | + ? isCategoryAction | ||
83 | + ? createModelCategory | ||
84 | + : createModel | ||
85 | + : isCategoryAction | ||
86 | + ? updateModelCategory | ||
87 | + : updateModel; | ||
88 | + | ||
89 | + value[isCategoryAction ? 'categoryId' : 'deviceProfileId'] = props.record.id; | ||
90 | + | ||
91 | + if (unref(openModalMode) !== DataActionModeEnum.CREATE) | ||
92 | + value.id = unref(currentActionModel)?.id; | ||
93 | + | ||
94 | + await submitFn(value); | ||
190 | 95 | ||
96 | + createMessage.success('操作成功'); | ||
191 | closeModal(); | 97 | closeModal(); |
192 | emit('success'); | 98 | emit('success'); |
193 | } catch (error) { | 99 | } catch (error) { |
src/views/device/profiles/step/cpns/physical/cpns/Attribute.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { BasicForm, useForm } from '/@/components/Form'; | ||
3 | - import { DataType, ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | ||
4 | - import { StructFormValue } from '/@/components/Form/src/components/StructForm/type'; | ||
5 | - import { | ||
6 | - transfromToStructJSON, | ||
7 | - excludeIdInStructJSON, | ||
8 | - } from '/@/components/Form/src/components/StructForm/util'; | ||
9 | - import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | ||
10 | - import { isArray } from 'lodash'; | ||
11 | - import { OpenModelMode } from '../types'; | ||
12 | - import { formSchemas } from '/@/components/Form/src/components/StructForm/config'; | ||
13 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
14 | - import { ref, unref } from 'vue'; | ||
15 | - import EnumList from '/@/components/Form/src/components/StructForm/EnumList.vue'; | ||
16 | - import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
17 | - | ||
18 | - const props = defineProps<{ openModalMode: OpenModelMode; transportType?: string | undefined }>(); | ||
19 | - | ||
20 | - const enumListRef = ref<InstanceType<typeof EnumList>>(); | ||
21 | - | ||
22 | - const [register, { validate, getFieldsValue, resetFields, setFieldsValue }] = useForm({ | ||
23 | - labelWidth: 100, | ||
24 | - schemas: formSchemas({ | ||
25 | - hasStructForm: false, | ||
26 | - hiddenAccessMode: false, | ||
27 | - isTcp: props.transportType === TransportTypeEnum.TCP, | ||
28 | - }), | ||
29 | - showActionButtonGroup: false, | ||
30 | - }); | ||
31 | - | ||
32 | - const disabled = ref(false); | ||
33 | - const setDisable = (flag: boolean) => { | ||
34 | - disabled.value = flag; | ||
35 | - }; | ||
36 | - | ||
37 | - async function getFormData(): Promise<Partial<ModelOfMatterParams>> { | ||
38 | - await validate(); | ||
39 | - await unref(enumListRef)?.validate?.(); | ||
40 | - | ||
41 | - const _values = getFieldsValue() as StructFormValue; | ||
42 | - const { functionName, remark, identifier, accessMode } = _values; | ||
43 | - const structJSON = transfromToStructJSON(_values, unref(enumListRef)?.getFieldsValue?.()); | ||
44 | - | ||
45 | - const dataType = excludeIdInStructJSON(structJSON.dataType!); | ||
46 | - | ||
47 | - const value = { | ||
48 | - functionName, | ||
49 | - functionType: FunctionTypeEnum.PROPERTIES, | ||
50 | - remark, | ||
51 | - identifier, | ||
52 | - accessMode, | ||
53 | - extensionDesc: _values.extensionDesc, | ||
54 | - functionJson: { | ||
55 | - dataType: dataType, | ||
56 | - }, | ||
57 | - } as ModelOfMatterParams; | ||
58 | - | ||
59 | - return value; | ||
60 | - } | ||
61 | - | ||
62 | - const resetFormData = () => { | ||
63 | - resetFields(); | ||
64 | - }; | ||
65 | - | ||
66 | - const setFormData = (record: ModelOfMatterParams) => { | ||
67 | - const { functionJson } = record; | ||
68 | - const { dataType } = functionJson!; | ||
69 | - | ||
70 | - const { specs } = (dataType! || {}) as DataType; | ||
71 | - | ||
72 | - const value = { | ||
73 | - ...record, | ||
74 | - ...functionJson, | ||
75 | - ...dataType, | ||
76 | - ...(isArray(specs) ? specs : { ...specs }), | ||
77 | - hasStructForm: (dataType as DataType)?.type === DataTypeEnum.STRUCT, | ||
78 | - enumList: | ||
79 | - (dataType as DataType)?.type === DataTypeEnum.ENUM | ||
80 | - ? unref((dataType as DataType).specsList) | ||
81 | - : [], | ||
82 | - }; | ||
83 | - setFieldsValue(value); | ||
84 | - }; | ||
85 | - | ||
86 | - defineExpose({ | ||
87 | - resetFormData, | ||
88 | - getFormData, | ||
89 | - setFormData, | ||
90 | - setDisable, | ||
91 | - }); | ||
92 | -</script> | ||
93 | - | ||
94 | -<template> | ||
95 | - <BasicForm @register="register" :disabled="disabled"> | ||
96 | - <template #EnumList="{ field, model }"> | ||
97 | - <EnumList ref="enumListRef" :value="model[field]" :disabled="disabled" /> | ||
98 | - </template> | ||
99 | - </BasicForm> | ||
100 | -</template> | ||
101 | - | ||
102 | -<style lang="less" scoped></style> |
src/views/device/profiles/step/cpns/physical/cpns/Events.vue
deleted
100644 → 0
1 | -<template> | ||
2 | - <BasicForm @register="register" /> | ||
3 | -</template> | ||
4 | -<script lang="ts" setup> | ||
5 | - import { BasicForm, useForm } from '/@/components/Form'; | ||
6 | - import { eventSchemas } from './config'; | ||
7 | - import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | ||
8 | - import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
9 | - import { StructFormValue } from '/@/components/Form/src/components/StructForm/type'; | ||
10 | - import { excludeIdInStructJSON } from '/@/components/Form/src/components/StructForm/util'; | ||
11 | - import { OpenModelMode } from '../types'; | ||
12 | - | ||
13 | - defineProps<{ openModalMode: OpenModelMode }>(); | ||
14 | - | ||
15 | - const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({ | ||
16 | - labelWidth: 100, | ||
17 | - schemas: eventSchemas, | ||
18 | - actionColOptions: { | ||
19 | - span: 14, | ||
20 | - }, | ||
21 | - showResetButton: false, | ||
22 | - submitOnReset: false, | ||
23 | - showActionButtonGroup: false, | ||
24 | - }); | ||
25 | - | ||
26 | - const setDisable = (flag: boolean) => { | ||
27 | - setProps({ disabled: flag }); | ||
28 | - }; | ||
29 | - | ||
30 | - //回显数据 | ||
31 | - const setFormData = (record: ModelOfMatterParams) => { | ||
32 | - const { functionJson = {}, functionName, identifier, remark, eventType } = record; | ||
33 | - const { outputData } = functionJson; | ||
34 | - const value = { | ||
35 | - functionName, | ||
36 | - identifier, | ||
37 | - remark, | ||
38 | - outputData, | ||
39 | - eventType, | ||
40 | - }; | ||
41 | - setFieldsValue(value); | ||
42 | - }; | ||
43 | - | ||
44 | - async function getFormData() { | ||
45 | - const _values = (await validate()) as StructFormValue; | ||
46 | - const { functionName, remark, identifier, outputData: _outputData = [], eventType } = _values; | ||
47 | - if (!_values) return {} as unknown as ModelOfMatterParams; | ||
48 | - | ||
49 | - const outputData = (_outputData as unknown as StructJSON[]).map((item) => { | ||
50 | - const dataType = excludeIdInStructJSON(item.dataType!); | ||
51 | - return { | ||
52 | - ...item, | ||
53 | - dataType, | ||
54 | - }; | ||
55 | - }); | ||
56 | - | ||
57 | - const value = { | ||
58 | - functionName, | ||
59 | - identifier, | ||
60 | - remark, | ||
61 | - functionType: FunctionTypeEnum.EVENTS, | ||
62 | - eventType, | ||
63 | - functionJson: { | ||
64 | - outputData, | ||
65 | - }, | ||
66 | - } as ModelOfMatterParams; | ||
67 | - | ||
68 | - return value; | ||
69 | - } | ||
70 | - | ||
71 | - //清空数据 | ||
72 | - const resetFormData = () => { | ||
73 | - resetFields(); | ||
74 | - }; | ||
75 | - | ||
76 | - defineExpose({ | ||
77 | - setFormData, | ||
78 | - resetFormData, | ||
79 | - getFormData, | ||
80 | - setDisable, | ||
81 | - }); | ||
82 | -</script> | ||
83 | -<style lang="less" scoped></style> |
src/views/device/profiles/step/cpns/physical/cpns/Service.vue
deleted
100644 → 0
1 | -<template> | ||
2 | - <BasicForm @register="register" /> | ||
3 | -</template> | ||
4 | -<script lang="ts" setup> | ||
5 | - import { BasicForm, useForm } from '/@/components/Form'; | ||
6 | - import { FormField, serviceSchemas } from './config'; | ||
7 | - import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | ||
8 | - import { StructFormValue } from '/@/components/Form/src/components/StructForm/type'; | ||
9 | - import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
10 | - import { DeviceRecord } from '/@/api/device/model/deviceModel'; | ||
11 | - import { excludeIdInStructJSON } from '/@/components/Form/src/components/StructForm/util'; | ||
12 | - import { OpenModelMode } from '../types'; | ||
13 | - import { isArray } from '/@/utils/is'; | ||
14 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
15 | - | ||
16 | - const props = defineProps<{ | ||
17 | - record: DeviceRecord; | ||
18 | - openModalMode: OpenModelMode; | ||
19 | - }>(); | ||
20 | - | ||
21 | - const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({ | ||
22 | - labelWidth: 100, | ||
23 | - schemas: serviceSchemas(props.record.transportType === 'TCP'), | ||
24 | - actionColOptions: { | ||
25 | - span: 14, | ||
26 | - }, | ||
27 | - disabled: props.openModalMode === OpenModelMode.VIEW, | ||
28 | - showResetButton: false, | ||
29 | - submitOnReset: false, | ||
30 | - showActionButtonGroup: false, | ||
31 | - }); | ||
32 | - | ||
33 | - const setDisable = (flag: boolean) => { | ||
34 | - setProps({ disabled: flag }); | ||
35 | - }; | ||
36 | - | ||
37 | - //回显数据 | ||
38 | - const setFormData = (record: ModelOfMatterParams) => { | ||
39 | - const { functionJson = {}, functionName, identifier, remark, callType } = record; | ||
40 | - const { inputData = [], outputData } = functionJson; | ||
41 | - const { serviceCommand } = | ||
42 | - (inputData.at(0) as unknown as { serviceCommand: string }) || | ||
43 | - ({} as { serviceCommand: string }); | ||
44 | - const value = { | ||
45 | - functionName, | ||
46 | - identifier, | ||
47 | - remark, | ||
48 | - inputData, | ||
49 | - outputData, | ||
50 | - serviceCommand, | ||
51 | - callType, | ||
52 | - }; | ||
53 | - | ||
54 | - if ( | ||
55 | - isArray(inputData) && | ||
56 | - inputData.find((item) => item?.dataType?.type === DataTypeEnum.STRUCT) | ||
57 | - ) { | ||
58 | - Reflect.set(value, FormField.HAS_STRUCT_FROM, true); | ||
59 | - } | ||
60 | - | ||
61 | - setFieldsValue(value); | ||
62 | - }; | ||
63 | - | ||
64 | - //获取数据 | ||
65 | - async function getFormData() { | ||
66 | - const _values = (await validate()) as StructFormValue; | ||
67 | - const { | ||
68 | - functionName, | ||
69 | - remark, | ||
70 | - identifier, | ||
71 | - inputData: _inputData = [], | ||
72 | - outputData: _outputData = [], | ||
73 | - serviceCommand, | ||
74 | - callType, | ||
75 | - } = _values; | ||
76 | - if (!_values) return {}; | ||
77 | - | ||
78 | - const outputData = (_outputData as unknown as StructJSON[]).map((item) => { | ||
79 | - const dataType = excludeIdInStructJSON(item.dataType!); | ||
80 | - return { | ||
81 | - ...item, | ||
82 | - dataType, | ||
83 | - }; | ||
84 | - }); | ||
85 | - const inputData = (_inputData as unknown as StructJSON[]).map((item) => { | ||
86 | - const dataType = excludeIdInStructJSON(item.dataType!); | ||
87 | - return { | ||
88 | - ...item, | ||
89 | - dataType, | ||
90 | - }; | ||
91 | - }); | ||
92 | - | ||
93 | - const value = { | ||
94 | - functionName, | ||
95 | - identifier, | ||
96 | - remark, | ||
97 | - functionType: FunctionTypeEnum.SERVICE, | ||
98 | - callType, | ||
99 | - functionJson: { | ||
100 | - inputData, | ||
101 | - outputData, | ||
102 | - ...(serviceCommand | ||
103 | - ? { inputData: [{ serviceCommand: serviceCommand.replaceAll(/\s/g, '').toUpperCase() }] } | ||
104 | - : {}), | ||
105 | - }, | ||
106 | - } as ModelOfMatterParams; | ||
107 | - | ||
108 | - return value; | ||
109 | - } | ||
110 | - | ||
111 | - //清空数据 | ||
112 | - const resetFormData = () => { | ||
113 | - resetFields(); | ||
114 | - }; | ||
115 | - | ||
116 | - defineExpose({ | ||
117 | - setFormData, | ||
118 | - resetFormData, | ||
119 | - getFormData, | ||
120 | - setDisable, | ||
121 | - }); | ||
122 | -</script> | ||
123 | -<style lang="less" scoped></style> |
1 | -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | ||
2 | - | ||
3 | -export enum OpenModelMode { | ||
4 | - UPDATE = 'update', | ||
5 | - CREATE = 'create', | ||
6 | - VIEW = 'view', | ||
7 | -} | ||
8 | - | ||
9 | -export interface OpenModelOfMatterModelParams { | ||
10 | - mode: OpenModelMode; | ||
11 | - record: ModelOfMatterParams; | ||
12 | -} | ||
13 | - | ||
14 | export interface IAllData { | 1 | export interface IAllData { |
15 | properties: string[]; | 2 | properties: string[]; |
16 | events: string[]; | 3 | events: string[]; |