Commit 0fe77900fc8adcb0f9f9542cc8a22350284e968a
Merge branch 'ww' into 'main'
feat: 场景联动服务调用新增根据物模型服务配置下发参数表单 See merge request yunteng/thingskit-front!495
Showing
11 changed files
with
307 additions
and
16 deletions
| ... | ... | @@ -40,6 +40,7 @@ import StructForm from './externalCompns/components/StructForm/StructForm.vue'; |
| 40 | 40 | import ApiSelectScrollLoad from './components/ApiSelectScrollLoad.vue'; |
| 41 | 41 | import TransferModal from './components/TransferModal.vue'; |
| 42 | 42 | import TransferTableModal from './components/TransferTableModal.vue'; |
| 43 | +import ObjectModelValidateForm from './externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue'; | |
| 43 | 44 | |
| 44 | 45 | const componentMap = new Map<ComponentType, Component>(); |
| 45 | 46 | |
| ... | ... | @@ -87,6 +88,7 @@ componentMap.set('StructForm', StructForm); |
| 87 | 88 | componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad); |
| 88 | 89 | componentMap.set('TransferModal', TransferModal); |
| 89 | 90 | componentMap.set('TransferTableModal', TransferTableModal); |
| 91 | +componentMap.set('ObjectModelValidateForm', ObjectModelValidateForm); | |
| 90 | 92 | |
| 91 | 93 | export function add(compName: ComponentType, component: Component) { |
| 92 | 94 | componentMap.set(compName, component); | ... | ... |
| 1 | +<script lang="ts" setup> | |
| 2 | + import { Card } from 'ant-design-vue'; | |
| 3 | + import { nextTick, onBeforeUpdate, onUpdated } from 'vue'; | |
| 4 | + import { DataTypeEnum } from '../StructForm/config'; | |
| 5 | + import { BasicCreateFormParams } from './type'; | |
| 6 | + import { DynamicProps } from '/#/utils'; | |
| 7 | + import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | |
| 8 | + import { BasicForm, FormProps, FormSchema, useForm } from '/@/components/Form'; | |
| 9 | + | |
| 10 | + const props = withDefaults( | |
| 11 | + defineProps<{ | |
| 12 | + inputData?: StructJSON[]; | |
| 13 | + formProps?: FormProps; | |
| 14 | + value?: Recordable; | |
| 15 | + }>(), | |
| 16 | + { | |
| 17 | + inputData: () => [] as StructJSON[], | |
| 18 | + formProps: () => ({}), | |
| 19 | + value: () => ({}), | |
| 20 | + } | |
| 21 | + ); | |
| 22 | + | |
| 23 | + const emit = defineEmits(['update:value']); | |
| 24 | + | |
| 25 | + const [register, { setProps, getFieldsValue, setFieldsValue }] = useForm({ | |
| 26 | + schemas: [], | |
| 27 | + showActionButtonGroup: false, | |
| 28 | + layout: 'inline', | |
| 29 | + labelWidth: 80, | |
| 30 | + wrapperCol: { span: 12 }, | |
| 31 | + ...(props.formProps || ({} as unknown as Partial<DynamicProps<FormProps>>)), | |
| 32 | + }); | |
| 33 | + | |
| 34 | + const syncValue = (key: string, value: any) => { | |
| 35 | + const record = getFieldsValue(); | |
| 36 | + emit('update:value', { ...record, [key]: value }); | |
| 37 | + }; | |
| 38 | + | |
| 39 | + const createInputNumber = ({ | |
| 40 | + identifier, | |
| 41 | + functionName, | |
| 42 | + specs, | |
| 43 | + }: BasicCreateFormParams): FormSchema => { | |
| 44 | + const { valueRange } = specs! as Partial<Specs>; | |
| 45 | + const { max = 2147483647, min = -2147483648 } = valueRange || {}; | |
| 46 | + return { | |
| 47 | + field: identifier, | |
| 48 | + label: functionName, | |
| 49 | + component: 'InputNumber', | |
| 50 | + rules: [ | |
| 51 | + { | |
| 52 | + type: 'number', | |
| 53 | + trigger: 'change', | |
| 54 | + validator: (_rule, value) => { | |
| 55 | + if (value < min || value > max) { | |
| 56 | + return Promise.reject(`${functionName}取值范围在${min}~${max}之间`); | |
| 57 | + } | |
| 58 | + return Promise.resolve(value); | |
| 59 | + }, | |
| 60 | + }, | |
| 61 | + ], | |
| 62 | + componentProps: { | |
| 63 | + max, | |
| 64 | + min, | |
| 65 | + // step: step, | |
| 66 | + // formatter: (value: string) => value, | |
| 67 | + // parser: (string: string) => { | |
| 68 | + // if (dataType === DataTypeEnum.IS_NUMBER_INT) { | |
| 69 | + // return Number(Number(string).toFixed()); | |
| 70 | + // } | |
| 71 | + // return Number(string); | |
| 72 | + // }, | |
| 73 | + onChange: (value: string) => { | |
| 74 | + syncValue(identifier, value); | |
| 75 | + }, | |
| 76 | + }, | |
| 77 | + } as FormSchema; | |
| 78 | + }; | |
| 79 | + | |
| 80 | + const createInput = ({ identifier, functionName, specs }: BasicCreateFormParams): FormSchema => { | |
| 81 | + const { length = 10240 } = specs! as Partial<Specs>; | |
| 82 | + return { | |
| 83 | + field: identifier, | |
| 84 | + label: functionName, | |
| 85 | + component: 'Input', | |
| 86 | + rules: [ | |
| 87 | + { | |
| 88 | + type: 'string', | |
| 89 | + trigger: 'change', | |
| 90 | + validator: (_rule, value) => { | |
| 91 | + if (value.length > length) { | |
| 92 | + return Promise.reject(`${functionName}数据长度应该小于${length}`); | |
| 93 | + } | |
| 94 | + return Promise.resolve(value); | |
| 95 | + }, | |
| 96 | + }, | |
| 97 | + ], | |
| 98 | + componentProps: { | |
| 99 | + maxLength: length, | |
| 100 | + onChange: (value: InputEvent) => { | |
| 101 | + syncValue(identifier, (value.target as HTMLInputElement).value); | |
| 102 | + }, | |
| 103 | + }, | |
| 104 | + } as FormSchema; | |
| 105 | + }; | |
| 106 | + | |
| 107 | + const createSelect = ({ identifier, functionName, specs }: BasicCreateFormParams): FormSchema => { | |
| 108 | + const { boolClose, boolOpen } = specs! as Partial<Specs>; | |
| 109 | + return { | |
| 110 | + field: identifier, | |
| 111 | + label: functionName, | |
| 112 | + component: 'Select', | |
| 113 | + componentProps: { | |
| 114 | + options: [ | |
| 115 | + { label: `${boolClose}-0`, value: 0 }, | |
| 116 | + { label: `${boolOpen}-0`, value: 1 }, | |
| 117 | + ], | |
| 118 | + onChange: (value: string) => { | |
| 119 | + syncValue(identifier, value); | |
| 120 | + }, | |
| 121 | + }, | |
| 122 | + }; | |
| 123 | + }; | |
| 124 | + | |
| 125 | + const createInputJson = ({ identifier, functionName }: BasicCreateFormParams): FormSchema => { | |
| 126 | + return { | |
| 127 | + field: identifier, | |
| 128 | + label: functionName, | |
| 129 | + component: 'InputTextArea', | |
| 130 | + componentProps: { | |
| 131 | + onChange: (value: InputEvent) => { | |
| 132 | + syncValue(identifier, (value.target as HTMLInputElement).value); | |
| 133 | + }, | |
| 134 | + }, | |
| 135 | + }; | |
| 136 | + }; | |
| 137 | + | |
| 138 | + const transformToFormSchema = (inputData: StructJSON[]) => { | |
| 139 | + const schemas: FormSchema[] = []; | |
| 140 | + for (const item of inputData) { | |
| 141 | + const { dataType, identifier, functionName } = item; | |
| 142 | + const { type, specs } = dataType! || {}; | |
| 143 | + | |
| 144 | + const params: BasicCreateFormParams = { | |
| 145 | + identifier: identifier!, | |
| 146 | + functionName: functionName!, | |
| 147 | + dataType: dataType! as unknown as DataTypeEnum, | |
| 148 | + specs: specs as Partial<Specs>, | |
| 149 | + }; | |
| 150 | + if (type === DataTypeEnum.IS_NUMBER_INT || type === DataTypeEnum.IS_NUMBER_DOUBLE) { | |
| 151 | + schemas.push(createInputNumber(params)); | |
| 152 | + } | |
| 153 | + | |
| 154 | + if (type === DataTypeEnum.IS_BOOL) { | |
| 155 | + schemas.push(createSelect(params)); | |
| 156 | + } | |
| 157 | + | |
| 158 | + if (type === DataTypeEnum.IS_STRING) { | |
| 159 | + schemas.push(createInput(params)); | |
| 160 | + } | |
| 161 | + | |
| 162 | + if (type === DataTypeEnum.IS_STRUCT) { | |
| 163 | + schemas.push(createInputJson(params)); | |
| 164 | + } | |
| 165 | + } | |
| 166 | + | |
| 167 | + return schemas; | |
| 168 | + }; | |
| 169 | + | |
| 170 | + onBeforeUpdate(() => { | |
| 171 | + if (props.inputData && props.inputData.length) { | |
| 172 | + const schemas = transformToFormSchema(props.inputData); | |
| 173 | + setProps({ schemas }); | |
| 174 | + } | |
| 175 | + }); | |
| 176 | + | |
| 177 | + onUpdated(async () => { | |
| 178 | + if (props.inputData && props.inputData.length) { | |
| 179 | + await nextTick(); | |
| 180 | + setFieldsValue(props.value); | |
| 181 | + } | |
| 182 | + }); | |
| 183 | +</script> | |
| 184 | + | |
| 185 | +<template> | |
| 186 | + <Card bordered class="!border-dashed !rounded-lg !border-2px"> | |
| 187 | + <!-- <Alert class="!mb-4 w-32" message="服务参数配置" type="info" /> --> | |
| 188 | + <BasicForm class="object-model-validate-form" @register="register" /> | |
| 189 | + </Card> | |
| 190 | +</template> | |
| 191 | + | |
| 192 | +<style lang="less"> | |
| 193 | + .object-model-validate-form { | |
| 194 | + .ant-input-number { | |
| 195 | + width: 100% !important; | |
| 196 | + } | |
| 197 | + } | |
| 198 | +</style> | ... | ... |
| ... | ... | @@ -7,21 +7,28 @@ |
| 7 | 7 | import { BasicForm, useForm } from '/@/components/Form'; |
| 8 | 8 | import { formSchemas } from './config'; |
| 9 | 9 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
| 10 | - import { OpenModalMode, OpenModalParams } from './type'; | |
| 10 | + import { OpenModalMode, OpenModalParams, StructRecord } from './type'; | |
| 11 | 11 | import { ref, unref } from 'vue'; |
| 12 | 12 | import { transfromToStructJSON } from './util'; |
| 13 | 13 | import { cloneDeep } from 'lodash-es'; |
| 14 | 14 | import { DataType, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
| 15 | 15 | import { isArray } from '/@/utils/is'; |
| 16 | + import { useMessage } from '/@/hooks/web/useMessage'; | |
| 16 | 17 | |
| 17 | 18 | const modalReceiveRecord = ref<OpenModalParams>({ |
| 18 | 19 | mode: OpenModalMode.CREATE, |
| 19 | 20 | }); |
| 20 | 21 | |
| 21 | - const props = defineProps<{ disabled: boolean; hasStructForm: boolean }>(); | |
| 22 | + const props = defineProps<{ | |
| 23 | + disabled: boolean; | |
| 24 | + hasStructForm: boolean; | |
| 25 | + valueList: StructRecord[]; | |
| 26 | + }>(); | |
| 22 | 27 | |
| 23 | 28 | const emit = defineEmits(['register', 'submit']); |
| 24 | 29 | |
| 30 | + const { createMessage } = useMessage(); | |
| 31 | + | |
| 25 | 32 | const [register, { validate, setFieldsValue, setProps }] = useForm({ |
| 26 | 33 | labelWidth: 100, |
| 27 | 34 | schemas: formSchemas(props.hasStructForm), |
| ... | ... | @@ -52,6 +59,10 @@ |
| 52 | 59 | setProps({ disabled: props.disabled }); |
| 53 | 60 | }); |
| 54 | 61 | |
| 62 | + const validateRepeat = (value: StructRecord, valueList: StructRecord[]) => { | |
| 63 | + return valueList.filter((item) => item.identifier === value.identifier).length >= 1; | |
| 64 | + }; | |
| 65 | + | |
| 55 | 66 | const handleSubmit = async () => { |
| 56 | 67 | try { |
| 57 | 68 | const _value = await validate(); |
| ... | ... | @@ -62,6 +73,12 @@ |
| 62 | 73 | ? { id: unref(modalReceiveRecord)?.record?.id } |
| 63 | 74 | : {}), |
| 64 | 75 | }; |
| 76 | + | |
| 77 | + if (validateRepeat(_value, props.valueList)) { | |
| 78 | + createMessage.error('存在一致的标识符'); | |
| 79 | + return; | |
| 80 | + } | |
| 81 | + | |
| 65 | 82 | emit('submit', unref(modalReceiveRecord).mode, cloneDeep(value)); |
| 66 | 83 | closeModal(); |
| 67 | 84 | } catch (error) {} | ... | ... |
| ... | ... | @@ -5,19 +5,19 @@ import { findDictItemByCode } from '/@/api/system/dict'; |
| 5 | 5 | import { BasicColumn, FormSchema } from '/@/components/Table'; |
| 6 | 6 | import { formatToDateTime } from '/@/utils/dateUtil'; |
| 7 | 7 | |
| 8 | -enum EventType { | |
| 8 | +export enum EventType { | |
| 9 | 9 | ERROR = 'ERROR', |
| 10 | 10 | INFO = 'INFO', |
| 11 | 11 | ALERT = 'ALERT', |
| 12 | 12 | } |
| 13 | 13 | |
| 14 | -enum EventTypeColor { | |
| 14 | +export enum EventTypeColor { | |
| 15 | 15 | ERROR = 'error', |
| 16 | - INFO = 'warning', | |
| 17 | - ALERT = 'default', | |
| 16 | + INFO = 'success', | |
| 17 | + ALERT = 'warning', | |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | -enum EventTypeName { | |
| 20 | +export enum EventTypeName { | |
| 21 | 21 | ERROR = '故障', |
| 22 | 22 | INFO = '信息', |
| 23 | 23 | ALERT = '告警', | ... | ... |
| ... | ... | @@ -8,6 +8,7 @@ import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi'; |
| 8 | 8 | import { FormField, FunctionType } from './step/cpns/physical/cpns/config'; |
| 9 | 9 | import { h } from 'vue'; |
| 10 | 10 | import { Tag } from 'ant-design-vue'; |
| 11 | +import { EventType, EventTypeColor, EventTypeName } from '../list/cpns/tabs/EventManage/config'; | |
| 11 | 12 | |
| 12 | 13 | export enum Mode { |
| 13 | 14 | CARD = 'card', |
| ... | ... | @@ -81,6 +82,19 @@ export const physicalColumn: BasicColumn[] = [ |
| 81 | 82 | }, |
| 82 | 83 | }, |
| 83 | 84 | { |
| 85 | + title: '事件类型', | |
| 86 | + dataIndex: 'eventType', | |
| 87 | + customRender({ text }) { | |
| 88 | + return h( | |
| 89 | + Tag, | |
| 90 | + { | |
| 91 | + color: EventTypeColor[text as EventType], | |
| 92 | + }, | |
| 93 | + () => EventTypeName[text as EventType] | |
| 94 | + ); | |
| 95 | + }, | |
| 96 | + }, | |
| 97 | + { | |
| 84 | 98 | title: '状态', |
| 85 | 99 | dataIndex: 'status', |
| 86 | 100 | width: 100, | ... | ... |
| ... | ... | @@ -567,11 +567,11 @@ |
| 567 | 567 | //TODO fengtao |
| 568 | 568 | orgId.value = newValue; |
| 569 | 569 | //TODO fengtao |
| 570 | - const data = await getOrganizationAlarmConfig({ organizationId: newValue }); | |
| 571 | - alarmConfigList.value = data.map((item) => ({ label: item.name, value: item.id })); | |
| 572 | 570 | setFields(skipUnwrap.triggerItemRefs, true); |
| 573 | 571 | setFields(skipUnwrap.conditionItemRefs, true); |
| 574 | 572 | setFields(skipUnwrap.actionItemRefs, true); |
| 573 | + const data = await getOrganizationAlarmConfig({ organizationId: newValue }); | |
| 574 | + alarmConfigList.value = data.map((item) => ({ label: item.name, value: item.id })); | |
| 575 | 575 | // setFields(skipUnwrap.actionItemRefs, true); |
| 576 | 576 | // console.log(unref(organizationIdRef)); |
| 577 | 577 | // setAlarmConfig(skipUnwrap.actionItemRefs, true); | ... | ... |
| ... | ... | @@ -20,6 +20,11 @@ export type TOption = { |
| 20 | 20 | value: string; |
| 21 | 21 | }; |
| 22 | 22 | |
| 23 | +export enum CommandTypeEnum { | |
| 24 | + CUSTOM = 0, | |
| 25 | + SERVICE = 1, | |
| 26 | +} | |
| 27 | + | |
| 23 | 28 | /** |
| 24 | 29 | * 所使用的枚举值 |
| 25 | 30 | */ |
| ... | ... | @@ -575,11 +580,28 @@ export const actionSchema: FormSchema[] = [ |
| 575 | 580 | span: 6, |
| 576 | 581 | }, |
| 577 | 582 | componentProps: ({ formModel, formActionType }) => { |
| 578 | - const { setFieldsValue } = formActionType; | |
| 579 | - const deviceProfileId = formModel['deviceProfileId']; | |
| 583 | + const { updateSchema } = formActionType; | |
| 584 | + const deviceProfileId = Reflect.get(formModel, 'deviceProfileId'); | |
| 585 | + const thingsModelId = Reflect.get(formModel, 'thingsModelId'); | |
| 580 | 586 | return { |
| 581 | 587 | placeholder: '请选择服务', |
| 582 | - api: getModelServices, | |
| 588 | + api: async (params: Recordable) => { | |
| 589 | + try { | |
| 590 | + const record = await getModelServices(params as Record<'deviceProfileId', string>); | |
| 591 | + const selected = record.find((item) => item.id === thingsModelId); | |
| 592 | + selected && | |
| 593 | + updateSchema({ | |
| 594 | + field: 'serviceInputValue', | |
| 595 | + componentProps: { | |
| 596 | + inputData: selected?.functionJson.inputData, | |
| 597 | + }, | |
| 598 | + }); | |
| 599 | + return record; | |
| 600 | + } catch (error) { | |
| 601 | + console.error(error); | |
| 602 | + return []; | |
| 603 | + } | |
| 604 | + }, | |
| 583 | 605 | params: { |
| 584 | 606 | deviceProfileId, |
| 585 | 607 | }, |
| ... | ... | @@ -587,7 +609,15 @@ export const actionSchema: FormSchema[] = [ |
| 587 | 609 | valueField: 'id', |
| 588 | 610 | getPopupContainer: () => document.body, |
| 589 | 611 | onChange: (_, options: ModelOfMatterParams) => { |
| 590 | - setFieldsValue({ doContext: { ...options.functionJson, callType: options.callType } }); | |
| 612 | + if (options) { | |
| 613 | + // setFieldsValue({ doContext: { ...options.functionJson, callType: options.callType } }); | |
| 614 | + updateSchema({ | |
| 615 | + field: 'serviceInputValue', | |
| 616 | + componentProps: { | |
| 617 | + inputData: options.functionJson.inputData, | |
| 618 | + }, | |
| 619 | + }); | |
| 620 | + } | |
| 591 | 621 | }, |
| 592 | 622 | }; |
| 593 | 623 | }, |
| ... | ... | @@ -665,6 +695,18 @@ export const actionSchema: FormSchema[] = [ |
| 665 | 695 | }, |
| 666 | 696 | slot: 'clearAlarm', |
| 667 | 697 | }, |
| 698 | + { | |
| 699 | + field: 'serviceInputValue', | |
| 700 | + component: 'ObjectModelValidateForm', | |
| 701 | + label: '', | |
| 702 | + changeEvent: 'update:value', | |
| 703 | + valueField: 'value', | |
| 704 | + show: ({ values }) => values['thingsModelId'], | |
| 705 | + colProps: { | |
| 706 | + span: 24, | |
| 707 | + }, | |
| 708 | + componentProps: {}, | |
| 709 | + }, | |
| 668 | 710 | ]; |
| 669 | 711 | |
| 670 | 712 | export const alarmScheduleSchemas: FormSchema[] = [ | ... | ... |
| ... | ... | @@ -103,7 +103,7 @@ |
| 103 | 103 | import { BasicForm, useForm } from '/@/components/Form/index'; |
| 104 | 104 | import { Tooltip, Select, Checkbox, Card } from 'ant-design-vue'; |
| 105 | 105 | import { Icon } from '/@/components/Icon'; |
| 106 | - import { actionSchema } from '../config/config.data'; | |
| 106 | + import { actionSchema, CommandTypeEnum } from '../config/config.data'; | |
| 107 | 107 | import jsoneditor from 'jsoneditor'; |
| 108 | 108 | import 'jsoneditor/dist/jsoneditor.min.css'; |
| 109 | 109 | import { QuestionCircleOutlined, PlusOutlined } from '@ant-design/icons-vue'; |
| ... | ... | @@ -261,19 +261,26 @@ |
| 261 | 261 | //TODO-fengtao-设备验证 |
| 262 | 262 | const value = getFieldsValue(); |
| 263 | 263 | const doContext = unref(jsonInstance)?.get() || {}; |
| 264 | + const serviceInputValue = Reflect.get(value, 'serviceInputValue'); | |
| 265 | + | |
| 264 | 266 | return { |
| 265 | 267 | ...value, |
| 266 | - ...(Number(value.commandType) === 0 ? { doContext } : {}), | |
| 268 | + ...(Number(value.commandType) === CommandTypeEnum.CUSTOM | |
| 269 | + ? { doContext } | |
| 270 | + : { doContext: serviceInputValue }), | |
| 267 | 271 | clearRule, |
| 268 | 272 | }; |
| 269 | 273 | }; |
| 270 | 274 | |
| 271 | 275 | const setFieldsFormValueFun = (fieldsValue) => { |
| 276 | + const doContext = Reflect.get(fieldsValue, 'doContext'); | |
| 277 | + const commandType = Reflect.get(fieldsValue, 'commandType'); | |
| 272 | 278 | setFieldsValue({ |
| 273 | 279 | ...fieldsValue, |
| 274 | 280 | ...(isNumber(fieldsValue.commandType) |
| 275 | 281 | ? { commandType: String(fieldsValue.commandType) } |
| 276 | 282 | : {}), |
| 283 | + serviceInputValue: commandType === CommandTypeEnum.SERVICE ? doContext.params || {} : {}, | |
| 277 | 284 | }); |
| 278 | 285 | }; |
| 279 | 286 | //ft-add | ... | ... |