Commit bc35026eee21fb75465fe5b8220e181256175f31

Authored by xp.Huang
2 parents 8f44c3e6 96ebeab9

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;
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>  
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>  
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 -};  
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 -}  
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>
  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) {
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>  
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>  
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[];