Showing
151 changed files
with
4041 additions
and
4151 deletions
| 1 | 1 | import { DeviceProfileModel } from '../../device/model/deviceModel'; |
| 2 | -import { HistoryData } from './model'; | |
| 2 | +import { DeviceAttributeItemType, HistoryData } from './model'; | |
| 3 | 3 | import { defHttp } from '/@/utils/http/axios'; |
| 4 | 4 | import { isString } from '/@/utils/is'; |
| 5 | 5 | import { OrderByEnum } from '/@/views/device/localtion/cpns/TimePeriodForm/config'; |
| ... | ... | @@ -17,7 +17,7 @@ export const getDeviceHistoryInfo = (params: Recordable, orderBy = OrderByEnum.D |
| 17 | 17 | return defHttp.get<HistoryData>( |
| 18 | 18 | { |
| 19 | 19 | url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`, |
| 20 | - params: { orderBy, ...params, entityId: null }, | |
| 20 | + params: { ...params, entityId: null, orderBy }, | |
| 21 | 21 | }, |
| 22 | 22 | { |
| 23 | 23 | joinPrefix: false, |
| ... | ... | @@ -38,7 +38,7 @@ export const getDeviceDataKeys = (id: string) => { |
| 38 | 38 | }; |
| 39 | 39 | // 获取设备状态,在线 or 离线时间 |
| 40 | 40 | export const getDeviceActiveTime = (entityId: string) => { |
| 41 | - return defHttp.get( | |
| 41 | + return defHttp.get<DeviceAttributeItemType[]>( | |
| 42 | 42 | { |
| 43 | 43 | url: `/plugins/telemetry/DEVICE/${entityId}/values/attributes?keys=active`, |
| 44 | 44 | }, | ... | ... |
| ... | ... | @@ -13,7 +13,7 @@ import { ChildDeviceParams } from './model/deviceModel'; |
| 13 | 13 | import { PaginationResult } from '/#/axios'; |
| 14 | 14 | import { AlarmLogItem } from './model/deviceConfigModel'; |
| 15 | 15 | import { omit } from 'lodash-es'; |
| 16 | -import { CommandDeliveryWayEnum } from '/@/enums/toolEnum'; | |
| 16 | +import { CommandDeliveryWayEnum } from '/@/enums/deviceEnum'; | |
| 17 | 17 | enum DeviceManagerApi { |
| 18 | 18 | /** |
| 19 | 19 | * 设备URL |
| ... | ... | @@ -137,7 +137,7 @@ export const createOrEditDevice = (data) => { |
| 137 | 137 | |
| 138 | 138 | // 查询设备详情 |
| 139 | 139 | export const getDeviceDetail = (id: string) => { |
| 140 | - return defHttp.get({ | |
| 140 | + return defHttp.get<DeviceRecord>({ | |
| 141 | 141 | url: DeviceManagerApi.DEVICE_URL + `/${id}`, |
| 142 | 142 | }); |
| 143 | 143 | }; | ... | ... |
| 1 | 1 | import { BasicPageParams } from '/@/api/model/baseModel'; |
| 2 | +import { CommandTypeEnum, RPCCommandMethodEnum } from '/@/enums/deviceEnum'; | |
| 2 | 3 | |
| 3 | 4 | export type TDeviceConfigPageQueryParam = BasicPageParams & TDeviceConfigParams; |
| 4 | 5 | |
| ... | ... | @@ -184,3 +185,10 @@ export interface Configuration { |
| 184 | 185 | export interface TransportConfiguration { |
| 185 | 186 | type: string; |
| 186 | 187 | } |
| 188 | + | |
| 189 | +export interface RpcCommandType { | |
| 190 | + additionalInfo: { cmdType: CommandTypeEnum }; | |
| 191 | + method: RPCCommandMethodEnum; | |
| 192 | + params: Recordable | string | number; | |
| 193 | + persistent: boolean; | |
| 194 | +} | ... | ... |
| 1 | 1 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 2 | -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | |
| 2 | +import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
| 3 | 3 | |
| 4 | 4 | export interface Specs { |
| 5 | - min: string; | |
| 6 | - max: string; | |
| 5 | + min: number; | |
| 6 | + max: number; | |
| 7 | 7 | unit: string; |
| 8 | 8 | unitName: string; |
| 9 | 9 | |
| 10 | 10 | dataType?: string; |
| 11 | 11 | name?: string; |
| 12 | - value?: string; | |
| 13 | - step: string; | |
| 14 | - length: string; | |
| 12 | + value?: string | number; | |
| 13 | + step: number; | |
| 14 | + length: number; | |
| 15 | 15 | boolOpen: string; |
| 16 | 16 | boolClose: string; |
| 17 | 17 | valueRange?: { |
| 18 | - min: string; | |
| 19 | - max: string; | |
| 18 | + min: number; | |
| 19 | + max: number; | |
| 20 | 20 | }; |
| 21 | 21 | } |
| 22 | 22 | |
| ... | ... | @@ -26,17 +26,23 @@ export interface DataType { |
| 26 | 26 | specsList?: Specs[]; |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | +export interface ExtensionDesc { | |
| 30 | + zoomFactor?: number; | |
| 31 | + actionType?: string; | |
| 32 | + dataType: string; | |
| 33 | + registerAddress: number; | |
| 34 | +} | |
| 35 | + | |
| 29 | 36 | export interface StructJSON { |
| 30 | - functionName?: string; | |
| 37 | + functionName: string; | |
| 31 | 38 | identifier: string; |
| 32 | - remark?: string; | |
| 33 | 39 | dataType?: DataType; |
| 40 | + remark?: string; | |
| 34 | 41 | serviceCommand?: string; |
| 35 | - accessMode?: string; | |
| 36 | 42 | } |
| 37 | 43 | |
| 38 | 44 | export interface FunctionJson { |
| 39 | - dataType?: DataType | DataType[]; | |
| 45 | + dataType?: DataType; | |
| 40 | 46 | inputData?: StructJSON[]; |
| 41 | 47 | outputData?: StructJSON[]; |
| 42 | 48 | serviceCommand?: string; |
| ... | ... | @@ -46,19 +52,19 @@ export interface ModelOfMatterParams { |
| 46 | 52 | deviceProfileId?: string; |
| 47 | 53 | functionJson: FunctionJson; |
| 48 | 54 | functionName: string; |
| 49 | - functionType: FunctionType; | |
| 55 | + functionType: FunctionTypeEnum; | |
| 50 | 56 | identifier: string; |
| 51 | - remark: string; | |
| 57 | + remark?: string; | |
| 52 | 58 | id?: string; |
| 53 | 59 | categoryId?: string; |
| 54 | 60 | callType?: string; |
| 55 | 61 | eventType?: string; |
| 56 | 62 | accessMode?: string; |
| 57 | - extensionDesc?: Recordable; | |
| 63 | + extensionDesc?: ExtensionDesc; | |
| 58 | 64 | } |
| 59 | 65 | |
| 60 | 66 | export interface GetModelTslParams { |
| 61 | - functionType: FunctionType; | |
| 67 | + functionType: FunctionTypeEnum; | |
| 62 | 68 | deviceProfileId: string; |
| 63 | 69 | ifShowClass?: string | Boolean; |
| 64 | 70 | } |
| ... | ... | @@ -85,3 +91,26 @@ export interface ModelOfMatterItemRecordType { |
| 85 | 91 | status: number; |
| 86 | 92 | deviceProfileId: string; |
| 87 | 93 | } |
| 94 | + | |
| 95 | +export interface BatchGetObjectModelItemType { | |
| 96 | + id: string; | |
| 97 | + name: string; | |
| 98 | + transportType: string; | |
| 99 | + deviceType: string; | |
| 100 | + tsl: Tsl[]; | |
| 101 | +} | |
| 102 | + | |
| 103 | +export interface Tsl { | |
| 104 | + functionName: string; | |
| 105 | + identifier: string; | |
| 106 | + functionType: string; | |
| 107 | + accessMode?: string; | |
| 108 | + specs?: { | |
| 109 | + dataType: DataType; | |
| 110 | + }; | |
| 111 | + extensionDesc: ExtensionDesc; | |
| 112 | + eventType?: string; | |
| 113 | + outputData?: StructJSON[]; | |
| 114 | + callType?: string; | |
| 115 | + inputData?: StructJSON[]; | |
| 116 | +} | ... | ... |
| 1 | 1 | import { BasicPageParams } from '../model/baseModel'; |
| 2 | 2 | import { |
| 3 | + BatchGetObjectModelItemType, | |
| 3 | 4 | GetModelTslParams, |
| 4 | 5 | ImportModelOfMatterType, |
| 5 | 6 | ModelOfMatterItemRecordType, |
| 6 | 7 | ModelOfMatterParams, |
| 7 | 8 | } from './model/modelOfMatterModel'; |
| 9 | +import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
| 8 | 10 | import { defHttp } from '/@/utils/http/axios'; |
| 9 | -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | |
| 10 | 11 | |
| 11 | 12 | enum ModelOfMatter { |
| 12 | 13 | CREATE = '/things_model', |
| ... | ... | @@ -26,12 +27,14 @@ enum ModelOfMatter { |
| 26 | 27 | |
| 27 | 28 | IMPORT_CSV = '/things_model/csvImport', |
| 28 | 29 | EXCEL_EXPORT = '/things_model/downloadTemplate', |
| 30 | + | |
| 31 | + BATCH_GET_TSL_BY_DEVICE_PROFILES = '/things_model/batch/get_tsl', | |
| 29 | 32 | } |
| 30 | 33 | |
| 31 | 34 | export const getModelList = ( |
| 32 | 35 | params: BasicPageParams & { |
| 33 | 36 | deviceProfileId?: string; |
| 34 | - functionTyp?: FunctionType; | |
| 37 | + functionTyp?: FunctionTypeEnum; | |
| 35 | 38 | nameOrIdentifier?: string; |
| 36 | 39 | selectType?: string | undefined; |
| 37 | 40 | id?: string; |
| ... | ... | @@ -181,3 +184,16 @@ export const excelExport = () => { |
| 181 | 184 | responseType: 'blob', |
| 182 | 185 | }); |
| 183 | 186 | }; |
| 187 | + | |
| 188 | +export const batchGetObjectModel = ({ | |
| 189 | + deviceProfileIds, | |
| 190 | + functionTypeEnum = 'all', | |
| 191 | +}: { | |
| 192 | + deviceProfileIds: string[]; | |
| 193 | + functionTypeEnum?: FunctionTypeEnum | 'all'; | |
| 194 | +}) => { | |
| 195 | + return defHttp.post<BatchGetObjectModelItemType[]>({ | |
| 196 | + url: ModelOfMatter.BATCH_GET_TSL_BY_DEVICE_PROFILES, | |
| 197 | + data: { deviceProfileIds, functionTypeEnum }, | |
| 198 | + }); | |
| 199 | +}; | ... | ... |
| ... | ... | @@ -2,7 +2,7 @@ import { withInstall } from '/@/utils'; |
| 2 | 2 | // @ts-ignore |
| 3 | 3 | import codeEditor from './src/CodeEditor.vue'; |
| 4 | 4 | import jsonPreview from './src/json-preview/JsonPreview.vue'; |
| 5 | -export { JSONEditor } from './src/JSONEditor'; | |
| 5 | +export { JSONEditor, JSONEditorValidator, parseStringToJSON } from './src/JSONEditor'; | |
| 6 | 6 | |
| 7 | 7 | export const CodeEditor = withInstall(codeEditor); |
| 8 | 8 | export const JsonPreview = withInstall(jsonPreview); | ... | ... |
| ... | ... | @@ -11,18 +11,18 @@ export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.v |
| 11 | 11 | export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; |
| 12 | 12 | export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; |
| 13 | 13 | export { default as ApiUpload } from './src/components/ApiUpload.vue'; |
| 14 | +export { default as ApiCascader } from './src/components/ApiCascader.vue'; | |
| 14 | 15 | |
| 15 | -export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue'; | |
| 16 | 16 | export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue'; |
| 17 | 17 | |
| 18 | -export { ThingsModelForm } from './src/externalCompns/components/ThingsModelForm'; | |
| 18 | +export { ThingsModelForm } from './src/components/ThingsModelForm'; | |
| 19 | 19 | |
| 20 | 20 | //注册自定义组件 |
| 21 | 21 | export { |
| 22 | 22 | JEasyCron, |
| 23 | 23 | JEasyCronInner, |
| 24 | 24 | JEasyCronModal, |
| 25 | -} from '/@/components/Form/src/externalCompns/components/JEasyCron'; | |
| 25 | +} from '/@/components/Form/src/components/JEasyCron'; | |
| 26 | 26 | // Jeecg自定义校验 |
| 27 | -export { JCronValidator } from '/@/components/Form/src/externalCompns/components/JEasyCron'; | |
| 27 | +export { JCronValidator } from '/@/components/Form/src/components/JEasyCron'; | |
| 28 | 28 | // export { BasicForm }; | ... | ... |
| ... | ... | @@ -29,20 +29,18 @@ import { IconPicker } from '/@/components/Icon'; |
| 29 | 29 | import { CountdownInput } from '/@/components/CountDown'; |
| 30 | 30 | import ApiRadioGroup from './components/ApiRadioGroup.vue'; |
| 31 | 31 | //自定义组件 |
| 32 | -import JAddInput from './externalCompns/components/JAddInput.vue'; | |
| 33 | -import { JEasyCron } from './externalCompns/components/JEasyCron'; | |
| 32 | +import JAddInput from './components/JAddInput.vue'; | |
| 33 | +import { JEasyCron } from './components/JEasyCron'; | |
| 34 | 34 | import ColorPicker from './components/ColorPicker.vue'; |
| 35 | 35 | import IconDrawer from './components/IconDrawer.vue'; |
| 36 | 36 | import ApiUpload from './components/ApiUpload.vue'; |
| 37 | 37 | import ApiSearchSelect from './components/ApiSearchSelect.vue'; |
| 38 | -import CustomMinMaxInput from './externalCompns/components/CustomMinMaxInput.vue'; | |
| 39 | -import StructForm from './externalCompns/components/StructForm/StructForm.vue'; | |
| 38 | +import CustomMinMaxInput from './components/CustomMinMaxInput.vue'; | |
| 40 | 39 | import ApiSelectScrollLoad from './components/ApiSelectScrollLoad.vue'; |
| 41 | 40 | import InputGroup from './components/InputGroup.vue'; |
| 42 | 41 | import RegisterAddressInput from '/@/views/task/center/components/PollCommandInput/RegisterAddressInput.vue'; |
| 43 | -import ExtendDesc from '/@/components/Form/src/externalCompns/components/ExtendDesc/index.vue'; | |
| 44 | -import DeviceProfileForm from '/@/components/Form/src/externalCompns/components/DeviceProfileForm/index.vue'; | |
| 45 | -import EnumList from './externalCompns/components/StructForm/EnumList.vue'; | |
| 42 | +import DeviceProfileForm from '/@/components/Form/src/components/DeviceProfileForm/index.vue'; | |
| 43 | +import { Segmented } from './components/Segmented'; | |
| 46 | 44 | |
| 47 | 45 | const componentMap = new Map<ComponentType, Component>(); |
| 48 | 46 | |
| ... | ... | @@ -76,6 +74,7 @@ componentMap.set('TimePicker', TimePicker); |
| 76 | 74 | componentMap.set('StrengthMeter', StrengthMeter); |
| 77 | 75 | componentMap.set('IconPicker', IconPicker); |
| 78 | 76 | componentMap.set('InputCountDown', CountdownInput); |
| 77 | +componentMap.set('Segmented', Segmented); | |
| 79 | 78 | |
| 80 | 79 | componentMap.set('Upload', BasicUpload); |
| 81 | 80 | //注册自定义组件 |
| ... | ... | @@ -86,12 +85,9 @@ componentMap.set('IconDrawer', IconDrawer); |
| 86 | 85 | componentMap.set('ApiUpload', ApiUpload); |
| 87 | 86 | componentMap.set('ApiSearchSelect', ApiSearchSelect); |
| 88 | 87 | componentMap.set('CustomMinMaxInput', CustomMinMaxInput); |
| 89 | -componentMap.set('StructForm', StructForm); | |
| 90 | 88 | componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad); |
| 91 | 89 | componentMap.set('InputGroup', InputGroup); |
| 92 | 90 | componentMap.set('RegisterAddressInput', RegisterAddressInput); |
| 93 | -componentMap.set('ExtendDesc', ExtendDesc); | |
| 94 | -componentMap.set('EnumList', EnumList); | |
| 95 | 91 | componentMap.set('DeviceProfileForm', DeviceProfileForm); |
| 96 | 92 | |
| 97 | 93 | export function add(compName: ComponentType, component: Component) { | ... | ... |
| 1 | +<script setup lang="ts"> | |
| 2 | + import { Cascader } from 'ant-design-vue'; | |
| 3 | + import { LoadingOutlined } from '@ant-design/icons-vue'; | |
| 4 | + import { CascaderOptionType } from 'ant-design-vue/lib/cascader'; | |
| 5 | + import { get } from 'lodash'; | |
| 6 | + import { ref, unref, watch, watchEffect } from 'vue'; | |
| 7 | + import { useRuleFormItem } from '/@/hooks/component/useFormItem'; | |
| 8 | + import { useI18n } from '/@/hooks/web/useI18n'; | |
| 9 | + import { isFunction } from '/@/utils/is'; | |
| 10 | + | |
| 11 | + const { t } = useI18n(); | |
| 12 | + | |
| 13 | + const props = withDefaults( | |
| 14 | + defineProps<{ | |
| 15 | + value?: (string | number)[]; | |
| 16 | + immediate?: boolean; | |
| 17 | + params?: any; | |
| 18 | + api?: Fn<any, Promise<any>>; | |
| 19 | + options?: CascaderOptionType[]; | |
| 20 | + resultField?: string; | |
| 21 | + fieldNames?: Record<'label' | 'value' | 'children', string>; | |
| 22 | + }>(), | |
| 23 | + { | |
| 24 | + value: () => [], | |
| 25 | + immediate: true, | |
| 26 | + fieldNames: () => ({ label: 'label', value: 'value', children: 'children' }), | |
| 27 | + } | |
| 28 | + ); | |
| 29 | + | |
| 30 | + const emit = defineEmits(['change', 'options-change', 'update:value']); | |
| 31 | + | |
| 32 | + const options = ref<CascaderOptionType[]>([]); | |
| 33 | + const loading = ref(false); | |
| 34 | + const isFirstLoad = ref(true); | |
| 35 | + const emitData = ref<any[]>([]); | |
| 36 | + | |
| 37 | + // Embedded in the form, just use the hook binding to perform form verification | |
| 38 | + const [state] = useRuleFormItem(props, 'value', 'update:value', emitData); | |
| 39 | + | |
| 40 | + watchEffect(() => { | |
| 41 | + props.immediate && fetch(); | |
| 42 | + }); | |
| 43 | + | |
| 44 | + watch( | |
| 45 | + () => props.params, | |
| 46 | + () => { | |
| 47 | + !unref(isFirstLoad) && fetch(); | |
| 48 | + }, | |
| 49 | + { deep: true } | |
| 50 | + ); | |
| 51 | + | |
| 52 | + async function fetch() { | |
| 53 | + const api = props.api; | |
| 54 | + if (!api || !isFunction(api)) return; | |
| 55 | + options.value = []; | |
| 56 | + try { | |
| 57 | + loading.value = true; | |
| 58 | + const res = await api(props.params); | |
| 59 | + if (Array.isArray(res)) { | |
| 60 | + options.value = res; | |
| 61 | + emitChange(); | |
| 62 | + return; | |
| 63 | + } | |
| 64 | + if (props.resultField) { | |
| 65 | + options.value = get(res, props.resultField) || []; | |
| 66 | + } | |
| 67 | + emitChange(); | |
| 68 | + } catch (error) { | |
| 69 | + console.warn(error); | |
| 70 | + } finally { | |
| 71 | + loading.value = false; | |
| 72 | + } | |
| 73 | + } | |
| 74 | + | |
| 75 | + async function handleFetch(open) { | |
| 76 | + if (open) { | |
| 77 | + await fetch(); | |
| 78 | + } | |
| 79 | + } | |
| 80 | + | |
| 81 | + function emitChange() { | |
| 82 | + emit('options-change', options); | |
| 83 | + } | |
| 84 | + | |
| 85 | + function handleChange(_, ...args) { | |
| 86 | + emitData.value = args; | |
| 87 | + } | |
| 88 | +</script> | |
| 89 | + | |
| 90 | +<template> | |
| 91 | + <Cascader | |
| 92 | + v-bind="$attrs" | |
| 93 | + @change="handleChange" | |
| 94 | + :fieldNames="fieldNames" | |
| 95 | + :options="options" | |
| 96 | + v-model:value="state" | |
| 97 | + @popupVisibleChange="handleFetch" | |
| 98 | + > | |
| 99 | + <template #notFoundContent v-if="loading"> | |
| 100 | + <span> | |
| 101 | + <LoadingOutlined spin class="mr-1" /> | |
| 102 | + {{ t('component.form.apiSelectNotFound') }} | |
| 103 | + </span> | |
| 104 | + </template> | |
| 105 | + </Cascader> | |
| 106 | +</template> | ... | ... |
src/components/Form/src/components/CustomMinMaxInput.vue
renamed from
src/components/Form/src/externalCompns/components/CustomMinMaxInput.vue
| ... | ... | @@ -4,17 +4,13 @@ |
| 4 | 4 | placeholder="最小值" |
| 5 | 5 | :disabled="$props.disabled" |
| 6 | 6 | :value="getValue.min!" |
| 7 | - style="width: 38%" | |
| 8 | 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 | 10 | <InputNumber |
| 14 | 11 | placeholder="最大值" |
| 15 | 12 | :disabled="$props.disabled" |
| 16 | 13 | :value="getValue.max!" |
| 17 | - style="width: 38%" | |
| 18 | 14 | @change="(value) => emitChange(value, 'max')" |
| 19 | 15 | /> |
| 20 | 16 | </div> | ... | ... |
src/components/Form/src/components/DeviceProfileForm/index.vue
renamed from
src/components/Form/src/externalCompns/components/DeviceProfileForm/index.vue
src/components/Form/src/components/JAddInput.vue
renamed from
src/components/Form/src/externalCompns/components/JAddInput.vue
src/components/Form/src/components/JEasyCron/EasyCronInner.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/EasyCronInner.vue
src/components/Form/src/components/JEasyCron/EasyCronInput.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/EasyCronInput.vue
src/components/Form/src/components/JEasyCron/EasyCronModal.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/EasyCronModal.vue
src/components/Form/src/components/JEasyCron/LICENSE
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/LICENSE
src/components/Form/src/components/JEasyCron/easy.cron.data.ts
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/easy.cron.data.ts
src/components/Form/src/components/JEasyCron/easy.cron.inner.less
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/easy.cron.inner.less
src/components/Form/src/components/JEasyCron/easy.cron.input.less
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/easy.cron.input.less
src/components/Form/src/components/JEasyCron/index.ts
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/index.ts
src/components/Form/src/components/JEasyCron/tabs/DayUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/DayUI.vue
src/components/Form/src/components/JEasyCron/tabs/HourUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/HourUI.vue
src/components/Form/src/components/JEasyCron/tabs/MinuteUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/MinuteUI.vue
src/components/Form/src/components/JEasyCron/tabs/MonthUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/MonthUI.vue
src/components/Form/src/components/JEasyCron/tabs/SecondUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/SecondUI.vue
src/components/Form/src/components/JEasyCron/tabs/WeekUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/WeekUI.vue
src/components/Form/src/components/JEasyCron/tabs/YearUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/YearUI.vue
src/components/Form/src/components/JEasyCron/tabs/useTabMixin.ts
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/useTabMixin.ts
src/components/Form/src/components/JEasyCron/validator.ts
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/validator.ts
| 1 | +<script setup lang="ts"> | |
| 2 | + import { | |
| 3 | + computed, | |
| 4 | + CSSProperties, | |
| 5 | + nextTick, | |
| 6 | + onBeforeUnmount, | |
| 7 | + ref, | |
| 8 | + TransitionProps, | |
| 9 | + unref, | |
| 10 | + watch, | |
| 11 | + } from 'vue'; | |
| 12 | + import { addClass } from './styleClass'; | |
| 13 | + import { ThumbReact } from './type'; | |
| 14 | + import { removeClass } from '/@/utils/domUtils'; | |
| 15 | + | |
| 16 | + const calcThumbStyle = (targetElement: HTMLElement | null | undefined): ThumbReact => | |
| 17 | + targetElement | |
| 18 | + ? { | |
| 19 | + left: targetElement.offsetLeft, | |
| 20 | + right: | |
| 21 | + (targetElement.parentElement!.clientWidth as number) - | |
| 22 | + targetElement.clientWidth - | |
| 23 | + targetElement.offsetLeft, | |
| 24 | + width: targetElement.clientWidth, | |
| 25 | + } | |
| 26 | + : null; | |
| 27 | + | |
| 28 | + const toPX = (value?: number) => (value !== undefined ? `${value}px` : undefined); | |
| 29 | + | |
| 30 | + const props = defineProps<{ | |
| 31 | + value?: string | number; | |
| 32 | + getValueIndex: (value: string | number) => number; | |
| 33 | + containerRef?: HTMLDivElement; | |
| 34 | + prefixCls: string; | |
| 35 | + direction?: 'rtl' | 'ltr'; | |
| 36 | + motionName: string; | |
| 37 | + }>(); | |
| 38 | + | |
| 39 | + const emits = defineEmits(['motionStart', 'motionEnd']); | |
| 40 | + | |
| 41 | + const thumbRef = ref<HTMLDivElement>(); | |
| 42 | + | |
| 43 | + const findValueElement = (val: string | number) => { | |
| 44 | + const { getValueIndex, prefixCls } = props; | |
| 45 | + const index = getValueIndex(val); | |
| 46 | + const ele = unref(props.containerRef)?.querySelectorAll<HTMLDivElement>(`.${prefixCls}-item`)?.[ | |
| 47 | + index | |
| 48 | + ]; | |
| 49 | + | |
| 50 | + return ele?.offsetParent && ele; | |
| 51 | + }; | |
| 52 | + | |
| 53 | + const prevStyle = ref<ThumbReact>(null); | |
| 54 | + const nextStyle = ref<ThumbReact>(null); | |
| 55 | + | |
| 56 | + watch( | |
| 57 | + () => props.value, | |
| 58 | + (value, prevValue) => { | |
| 59 | + const prev = findValueElement(prevValue as number); | |
| 60 | + const next = findValueElement(value as number); | |
| 61 | + | |
| 62 | + const calcPrevStyle = calcThumbStyle(prev); | |
| 63 | + const calcNextStyle = calcThumbStyle(next); | |
| 64 | + | |
| 65 | + prevStyle.value = calcPrevStyle; | |
| 66 | + nextStyle.value = calcNextStyle; | |
| 67 | + | |
| 68 | + prev && next ? emits('motionStart') : emits('motionEnd'); | |
| 69 | + }, | |
| 70 | + { flush: 'post' } | |
| 71 | + ); | |
| 72 | + | |
| 73 | + const thumbStart = computed(() => { | |
| 74 | + const { direction } = props; | |
| 75 | + | |
| 76 | + return direction === 'rtl' | |
| 77 | + ? toPX(-(prevStyle.value?.right as number)) | |
| 78 | + : toPX(prevStyle.value?.left as number); | |
| 79 | + }); | |
| 80 | + | |
| 81 | + const thumbActive = computed(() => { | |
| 82 | + const { direction } = props; | |
| 83 | + | |
| 84 | + return direction === 'rtl' | |
| 85 | + ? toPX(-(nextStyle.value?.right as number)) | |
| 86 | + : toPX(nextStyle.value?.left as number); | |
| 87 | + }); | |
| 88 | + | |
| 89 | + let timeId: any; | |
| 90 | + const onAppearStart: TransitionProps['onBeforeEnter'] = (el: HTMLDivElement) => { | |
| 91 | + clearTimeout(timeId); | |
| 92 | + nextTick(() => { | |
| 93 | + if (el) { | |
| 94 | + el.style.transform = `translateX(var(--thumb-start-left))`; | |
| 95 | + el.style.width = `var(--thumb-start-width)`; | |
| 96 | + } | |
| 97 | + }); | |
| 98 | + }; | |
| 99 | + | |
| 100 | + const onAppearActive: TransitionProps['onEnter'] = (el: HTMLDivElement) => { | |
| 101 | + timeId = setTimeout(() => { | |
| 102 | + if (el) { | |
| 103 | + addClass(el, `${props.motionName}-appear-active`); | |
| 104 | + el.style.transform = `translateX(var(--thumb-active-left))`; | |
| 105 | + el.style.width = `var(--thumb-active-width)`; | |
| 106 | + } | |
| 107 | + }); | |
| 108 | + }; | |
| 109 | + | |
| 110 | + const onAppearEnd: TransitionProps['onAfterEnter'] = (el: HTMLDivElement) => { | |
| 111 | + prevStyle.value = null; | |
| 112 | + nextStyle.value = null; | |
| 113 | + if (el) { | |
| 114 | + el.style.transform = ''; | |
| 115 | + el.style.width = ''; | |
| 116 | + removeClass(el, `${props.motionName}-appear-active`); | |
| 117 | + } | |
| 118 | + emits('motionEnd'); | |
| 119 | + }; | |
| 120 | + | |
| 121 | + const mergedStyle = computed( | |
| 122 | + () => | |
| 123 | + ({ | |
| 124 | + '--thumb-start-left': thumbStart.value, | |
| 125 | + '--thumb-start-width': toPX(prevStyle.value?.width), | |
| 126 | + '--thumb-active-left': thumbActive.value, | |
| 127 | + '--thumb-active-width': toPX(nextStyle.value?.width), | |
| 128 | + } as CSSProperties) | |
| 129 | + ); | |
| 130 | + | |
| 131 | + onBeforeUnmount(() => { | |
| 132 | + clearTimeout(timeId); | |
| 133 | + }); | |
| 134 | + | |
| 135 | + const motionProps = computed(() => { | |
| 136 | + return { | |
| 137 | + ref: thumbRef, | |
| 138 | + style: mergedStyle.value, | |
| 139 | + class: `${props.prefixCls}-thumb`, | |
| 140 | + }; | |
| 141 | + }); | |
| 142 | +</script> | |
| 143 | + | |
| 144 | +<template> | |
| 145 | + <Transition | |
| 146 | + appear | |
| 147 | + @before-enter="onAppearStart" | |
| 148 | + @enter="onAppearActive" | |
| 149 | + @after-enter="onAppearEnd" | |
| 150 | + > | |
| 151 | + <div v-if="prevStyle && nextStyle" v-bind="motionProps"></div> | |
| 152 | + </Transition> | |
| 153 | +</template> | ... | ... |
| 1 | +<script setup lang="ts"> | |
| 2 | + import { computed, ref } from 'vue'; | |
| 3 | + import MotionThumb from './MotionThumb.vue'; | |
| 4 | + import { SegmentedBaseOption, SegmentedOption } from './type'; | |
| 5 | + | |
| 6 | + const props = withDefaults( | |
| 7 | + defineProps<{ | |
| 8 | + block?: boolean; | |
| 9 | + disabled?: boolean; | |
| 10 | + options?: string[] | number[] | SegmentedOption[]; | |
| 11 | + value?: string | number; | |
| 12 | + motionName?: string; | |
| 13 | + }>(), | |
| 14 | + { | |
| 15 | + options: () => [], | |
| 16 | + motionName: 'thumb-motion', | |
| 17 | + } | |
| 18 | + ); | |
| 19 | + | |
| 20 | + const slots = defineSlots<{ label?(props: SegmentedBaseOption): any }>(); | |
| 21 | + | |
| 22 | + const prefixCls = 'segmented'; | |
| 23 | + | |
| 24 | + const thumbShow = ref(false); | |
| 25 | + | |
| 26 | + const rootRef = ref<HTMLDivElement>(); | |
| 27 | + | |
| 28 | + const getOptions = computed<SegmentedBaseOption[]>(() => { | |
| 29 | + const { options } = props; | |
| 30 | + return (options as SegmentedOption[]).map((item) => { | |
| 31 | + return { | |
| 32 | + value: item?.value || item, | |
| 33 | + disabled: item?.disabled || false, | |
| 34 | + payload: item?.payload || {}, | |
| 35 | + title: item?.title || item, | |
| 36 | + className: item?.className || '', | |
| 37 | + } as SegmentedBaseOption; | |
| 38 | + }); | |
| 39 | + }); | |
| 40 | + | |
| 41 | + const emits = defineEmits(['change', 'update:value']); | |
| 42 | + | |
| 43 | + const handleChange = (value: string | number) => { | |
| 44 | + emits('update:value', value); | |
| 45 | + emits('change', value); | |
| 46 | + }; | |
| 47 | +</script> | |
| 48 | + | |
| 49 | +<template> | |
| 50 | + <div :class="prefixCls" ref="rootRef"> | |
| 51 | + <div :class="`${prefixCls}-group`"> | |
| 52 | + <MotionThumb | |
| 53 | + :containerRef="rootRef" | |
| 54 | + :prefix-cls="prefixCls" | |
| 55 | + :value="value" | |
| 56 | + :motion-name="`${prefixCls}-${motionName}`" | |
| 57 | + direction="ltr" | |
| 58 | + :get-value-index="(val) => getOptions.findIndex((item) => item.value === val)" | |
| 59 | + @motion-start="thumbShow = true" | |
| 60 | + @motion-end="thumbShow = false" | |
| 61 | + /> | |
| 62 | + <label | |
| 63 | + v-for="item in getOptions" | |
| 64 | + :key="item.value" | |
| 65 | + :class="[ | |
| 66 | + `${prefixCls}-item`, | |
| 67 | + value === item.value && !thumbShow && `${prefixCls}-item-selected`, | |
| 68 | + (disabled || item.disabled) && `${prefixCls}-item-disabled`, | |
| 69 | + ]" | |
| 70 | + @click="!disabled && !item.disabled && handleChange(item.value)" | |
| 71 | + > | |
| 72 | + <input type="radio" class="absolute pointer-events-none w-0 h-0 opacity-0" /> | |
| 73 | + <slot v-if="slots.label" name="label" v-bind="item"></slot> | |
| 74 | + <template v-else> | |
| 75 | + <div :class="`${prefixCls}-item-label`"> {{ item.title }}</div> | |
| 76 | + </template> | |
| 77 | + </label> | |
| 78 | + </div> | |
| 79 | + </div> | |
| 80 | +</template> | |
| 81 | + | |
| 82 | +<style lang="less"> | |
| 83 | + .segmented { | |
| 84 | + box-sizing: border-box; | |
| 85 | + padding: 2px; | |
| 86 | + color: #000000a6; | |
| 87 | + font-size: 14px; | |
| 88 | + display: inline-block; | |
| 89 | + border-radius: 4px; | |
| 90 | + background-color: #f5f5f5; | |
| 91 | + transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); | |
| 92 | + | |
| 93 | + &-block { | |
| 94 | + display: flex; | |
| 95 | + } | |
| 96 | + | |
| 97 | + &-block.segmented-item { | |
| 98 | + flex: 1; | |
| 99 | + } | |
| 100 | + | |
| 101 | + &-group { | |
| 102 | + position: relative; | |
| 103 | + display: flex; | |
| 104 | + align-items: stretch; | |
| 105 | + justify-content: flex-start; | |
| 106 | + width: 100%; | |
| 107 | + border-radius: inherit; | |
| 108 | + transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); | |
| 109 | + } | |
| 110 | + | |
| 111 | + &-item { | |
| 112 | + position: relative; | |
| 113 | + cursor: pointer; | |
| 114 | + border-radius: inherit; | |
| 115 | + text-align: center; | |
| 116 | + transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); | |
| 117 | + | |
| 118 | + &-label { | |
| 119 | + min-height: 28px; | |
| 120 | + line-height: 28px; | |
| 121 | + padding: 0 11px; | |
| 122 | + overflow: hidden; | |
| 123 | + white-space: nowrap; | |
| 124 | + text-overflow: ellipsis; | |
| 125 | + } | |
| 126 | + | |
| 127 | + &::after { | |
| 128 | + content: ''; | |
| 129 | + position: absolute; | |
| 130 | + width: 100%; | |
| 131 | + height: 100%; | |
| 132 | + top: 0; | |
| 133 | + inset-inline-start: 0; | |
| 134 | + border-radius: inherit; | |
| 135 | + transition: background-color 0.2s; | |
| 136 | + } | |
| 137 | + | |
| 138 | + &:hover:not(.segmented-item-selected):not(.segmented-item-disabled) { | |
| 139 | + color: #000000e0; | |
| 140 | + | |
| 141 | + &::after { | |
| 142 | + background-color: rgba(0, 0, 0, 0.06); | |
| 143 | + } | |
| 144 | + } | |
| 145 | + | |
| 146 | + &-disabled { | |
| 147 | + cursor: not-allowed; | |
| 148 | + color: rgba(0, 0, 0, 0.25); | |
| 149 | + } | |
| 150 | + | |
| 151 | + &-selected { | |
| 152 | + background-color: #fff; | |
| 153 | + | |
| 154 | + ::after { | |
| 155 | + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02), | |
| 156 | + 0 2px 4px 0 rgba(0, 0, 0, 0.02); | |
| 157 | + } | |
| 158 | + } | |
| 159 | + | |
| 160 | + &-selected:not(.segmented-item-disabled) { | |
| 161 | + color: rgba(0, 0, 0, 0.88); | |
| 162 | + } | |
| 163 | + } | |
| 164 | + | |
| 165 | + &-thumb { | |
| 166 | + position: absolute; | |
| 167 | + inset-block-start: 0; | |
| 168 | + inset-inline-start: 0; | |
| 169 | + width: 0; | |
| 170 | + height: 100%; | |
| 171 | + background-color: #fff; | |
| 172 | + | |
| 173 | + &-motion-appear-active { | |
| 174 | + transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), | |
| 175 | + width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); | |
| 176 | + will-change: transform, width; | |
| 177 | + } | |
| 178 | + | |
| 179 | + & ~ .segmented-item:not(.segmented-item-selected):not(.segmented-item-disabled)::after { | |
| 180 | + background-color: transparent; | |
| 181 | + } | |
| 182 | + } | |
| 183 | + } | |
| 184 | +</style> | ... | ... |
| 1 | +export { default as Segmented } from './Segmented.vue'; | ... | ... |
| 1 | +export function hasClass(node: HTMLElement, className: string) { | |
| 2 | + if (node.classList) { | |
| 3 | + return node.classList.contains(className); | |
| 4 | + } | |
| 5 | + const originClass = node.className; | |
| 6 | + return ` ${originClass} `.indexOf(` ${className} `) > -1; | |
| 7 | +} | |
| 8 | + | |
| 9 | +export function addClass(node: HTMLElement, className: string) { | |
| 10 | + if (node.classList) { | |
| 11 | + node.classList.add(className); | |
| 12 | + } else { | |
| 13 | + if (!hasClass(node, className)) { | |
| 14 | + node.className = `${node.className} ${className}`; | |
| 15 | + } | |
| 16 | + } | |
| 17 | +} | ... | ... |
| 1 | +export interface SegmentedBaseOption { | |
| 2 | + value: string | number; | |
| 3 | + disabled?: boolean; | |
| 4 | + payload?: any; | |
| 5 | + title?: string; | |
| 6 | + className?: string; | |
| 7 | +} | |
| 8 | + | |
| 9 | +export interface SegmentedOption extends SegmentedBaseOption { | |
| 10 | + label?: VueNode | ((option: SegmentedBaseOption) => VueNode); | |
| 11 | +} | |
| 12 | + | |
| 13 | +export type ThumbReact = { | |
| 14 | + left: number; | |
| 15 | + right: number; | |
| 16 | + width: number; | |
| 17 | +} | null; | ... | ... |
src/components/Form/src/components/ThingsModelForm/StructPreview.vue
renamed from
src/components/Form/src/externalCompns/components/ThingsModelForm/StructPreview.vue
src/components/Form/src/components/ThingsModelForm/config.ts
renamed from
src/components/Form/src/externalCompns/components/ThingsModelForm/config.ts
| 1 | -import { FormSchema } from '../../../types/form'; | |
| 1 | +import { FormSchema } from '/@/components/Form'; | |
| 2 | 2 | import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
| 3 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 3 | 4 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 4 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 5 | +import { validateTCPCustomCommand } from '.'; | |
| 5 | 6 | |
| 6 | 7 | export const getFormSchemas = ({ |
| 7 | 8 | structJSON: structJson, |
| ... | ... | @@ -127,6 +128,7 @@ export const getFormSchemas = ({ |
| 127 | 128 | label: functionName!, |
| 128 | 129 | component: 'Input', |
| 129 | 130 | changeEvent: 'update:value', |
| 131 | + rules: [{ required: required, validator: () => Promise.resolve() }], | |
| 130 | 132 | componentProps: () => { |
| 131 | 133 | return { |
| 132 | 134 | inputData: dataType?.specs || [], |
| ... | ... | @@ -143,6 +145,7 @@ export const getFormSchemas = ({ |
| 143 | 145 | component: 'Input', |
| 144 | 146 | required, |
| 145 | 147 | defaultValue: serviceCommand, |
| 148 | + rules: [{ validator: validateTCPCustomCommand }], | |
| 146 | 149 | componentProps: { |
| 147 | 150 | placeholder: `请输入服务命令`, |
| 148 | 151 | }, | ... | ... |
src/components/Form/src/components/ThingsModelForm/index.ts
renamed from
src/components/Form/src/externalCompns/components/ThingsModelForm/index.ts
src/components/Form/src/components/ThingsModelForm/index.vue
renamed from
src/components/Form/src/externalCompns/components/ThingsModelForm/index.vue
| 1 | 1 | <script setup lang="ts"> |
| 2 | + import { | |
| 3 | + ComponentPublicInstance, | |
| 4 | + computed, | |
| 5 | + nextTick, | |
| 6 | + reactive, | |
| 7 | + ref, | |
| 8 | + toRaw, | |
| 9 | + unref, | |
| 10 | + watch, | |
| 11 | + } from 'vue'; | |
| 2 | 12 | import { Card } from 'ant-design-vue'; |
| 3 | - import { ComponentPublicInstance, computed, nextTick, reactive, unref, watch } from 'vue'; | |
| 4 | 13 | import { getFormSchemas } from './config'; |
| 5 | 14 | import { ThingsModelForm } from '.'; |
| 6 | - import { DefineComponentsBasicExpose } from '/#/utils'; | |
| 7 | 15 | import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
| 8 | - import { useForm } from '../../../hooks/useForm'; | |
| 16 | + import { useForm } from '../../hooks/useForm'; | |
| 9 | 17 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 10 | - import { BasicForm } from '/@/components/Form'; | |
| 11 | - | |
| 12 | - const props = withDefaults( | |
| 13 | - defineProps<{ | |
| 14 | - value?: Recordable; | |
| 15 | - inputData?: StructJSON[]; | |
| 16 | - required?: boolean; | |
| 17 | - title?: string; | |
| 18 | - transportType?: string; | |
| 19 | - disabled?: boolean; | |
| 20 | - identifier?: string; | |
| 21 | - }>(), | |
| 22 | - { | |
| 23 | - inputData: () => [], | |
| 24 | - required: true, | |
| 25 | - } | |
| 26 | - ); | |
| 18 | + import { BasicForm, FormProps } from '/@/components/Form'; | |
| 19 | + import { deepMerge } from '/@/utils'; | |
| 20 | + | |
| 21 | + interface ThingsModelFormPropsType { | |
| 22 | + value?: Recordable; | |
| 23 | + inputData?: StructJSON[]; | |
| 24 | + required?: boolean; | |
| 25 | + title?: string; | |
| 26 | + transportType?: string; | |
| 27 | + disabled?: boolean; | |
| 28 | + identifier?: string; | |
| 29 | + formProps?: FormProps; | |
| 30 | + } | |
| 31 | + | |
| 32 | + const props = withDefaults(defineProps<ThingsModelFormPropsType>(), { | |
| 33 | + inputData: () => [], | |
| 34 | + required: true, | |
| 35 | + formProps: () => ({}), | |
| 36 | + }); | |
| 27 | 37 | |
| 28 | 38 | const thingsModelFormListElMap = reactive< |
| 29 | 39 | Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }> |
| 30 | 40 | >({}); |
| 31 | 41 | |
| 42 | + const propsRef = ref<Partial<ThingsModelFormPropsType>>({}); | |
| 43 | + | |
| 44 | + const getProps = computed<ThingsModelFormPropsType>( | |
| 45 | + () => ({ ...props, ...unref(propsRef) } as ThingsModelFormPropsType) | |
| 46 | + ); | |
| 47 | + | |
| 32 | 48 | const [register, formActionType] = useForm({ |
| 33 | - schemas: getFormSchemas({ | |
| 34 | - structJSON: props.inputData || [], | |
| 35 | - required: props.required, | |
| 36 | - transportType: props.transportType, | |
| 37 | - }), | |
| 38 | - showActionButtonGroup: false, | |
| 39 | 49 | layout: 'inline', |
| 40 | 50 | labelWidth: 100, |
| 51 | + ...toRaw(unref(getProps)), | |
| 52 | + schemas: getFormSchemasByProps(), | |
| 53 | + showActionButtonGroup: false, | |
| 41 | 54 | }); |
| 42 | 55 | |
| 43 | 56 | const getStructFormItem = computed(() => { |
| 44 | - const { inputData } = props; | |
| 45 | - return inputData.filter((item) => item.dataType?.type === DataTypeEnum.STRUCT); | |
| 57 | + const { inputData } = unref(getProps); | |
| 58 | + return (inputData || []).filter((item) => item.dataType?.type === DataTypeEnum.STRUCT); | |
| 46 | 59 | }); |
| 47 | 60 | |
| 48 | 61 | const setFormElRef = ( |
| ... | ... | @@ -87,7 +100,7 @@ |
| 87 | 100 | }; |
| 88 | 101 | |
| 89 | 102 | watch( |
| 90 | - () => props.value, | |
| 103 | + () => unref(getProps).value, | |
| 91 | 104 | async (value) => { |
| 92 | 105 | await nextTick(); |
| 93 | 106 | formActionType.resetFields(); |
| ... | ... | @@ -97,30 +110,36 @@ |
| 97 | 110 | ); |
| 98 | 111 | |
| 99 | 112 | watch( |
| 100 | - () => [props.inputData, props.identifier], | |
| 113 | + () => [unref(getProps).inputData, unref(getProps).identifier], | |
| 101 | 114 | (value) => { |
| 102 | - if (value && value.length) { | |
| 103 | - const schemas = getFormSchemas({ | |
| 104 | - structJSON: props.inputData || [], | |
| 105 | - required: props.required, | |
| 106 | - transportType: props.transportType, | |
| 107 | - }); | |
| 108 | - formActionType.setProps({ schemas }); | |
| 109 | - } | |
| 115 | + if (value && value.length) formActionType.setProps({ schemas: getFormSchemasByProps() }); | |
| 110 | 116 | } |
| 111 | 117 | ); |
| 112 | 118 | |
| 113 | 119 | watch( |
| 114 | - () => props.disabled, | |
| 120 | + () => unref(getProps).disabled, | |
| 115 | 121 | (value) => { |
| 116 | 122 | formActionType.setProps({ disabled: value }); |
| 117 | 123 | } |
| 118 | 124 | ); |
| 119 | 125 | |
| 120 | - defineExpose<DefineComponentsBasicExpose>({ | |
| 126 | + function getFormSchemasByProps() { | |
| 127 | + return getFormSchemas({ | |
| 128 | + structJSON: unref(getProps).inputData || [], | |
| 129 | + required: unref(getProps).required, | |
| 130 | + transportType: unref(getProps).transportType, | |
| 131 | + }); | |
| 132 | + } | |
| 133 | + | |
| 134 | + function setProps(props: Partial<ThingsModelFormPropsType>) { | |
| 135 | + propsRef.value = deepMerge(unref(propsRef) || {}, props); | |
| 136 | + } | |
| 137 | + | |
| 138 | + defineExpose({ | |
| 121 | 139 | getFieldsValue, |
| 122 | 140 | setFieldsValue, |
| 123 | 141 | validate, |
| 142 | + setProps, | |
| 124 | 143 | }); |
| 125 | 144 | </script> |
| 126 | 145 | |
| ... | ... | @@ -155,6 +174,10 @@ |
| 155 | 174 | width: 100%; |
| 156 | 175 | } |
| 157 | 176 | |
| 177 | + :deep(.ant-col.ant-form-item-control) { | |
| 178 | + min-height: auto; | |
| 179 | + } | |
| 180 | + | |
| 158 | 181 | :deep(.ant-input-number) { |
| 159 | 182 | width: 100%; |
| 160 | 183 | } | ... | ... |
src/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue
deleted
100644 → 0
| 1 | -<script lang="ts" setup> | |
| 2 | - import { Card } from 'ant-design-vue'; | |
| 3 | - import { computed, nextTick, onMounted, onUpdated, unref, watch } from 'vue'; | |
| 4 | - import { BasicCreateFormParams } from './type'; | |
| 5 | - import { DynamicProps } from '/#/utils'; | |
| 6 | - import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | |
| 7 | - import { BasicForm, FormProps, FormSchema, useForm } from '/@/components/Form'; | |
| 8 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 9 | - import { ReadAndWriteEnum } from '/@/enums/toolEnum'; | |
| 10 | - | |
| 11 | - const props = withDefaults( | |
| 12 | - defineProps<{ | |
| 13 | - inputData?: StructJSON[]; | |
| 14 | - formProps?: FormProps; | |
| 15 | - value?: Recordable; | |
| 16 | - required?: boolean; | |
| 17 | - }>(), | |
| 18 | - { | |
| 19 | - inputData: () => [] as StructJSON[], | |
| 20 | - formProps: () => ({}), | |
| 21 | - value: () => ({}), | |
| 22 | - } | |
| 23 | - ); | |
| 24 | - | |
| 25 | - const emit = defineEmits(['update:value']); | |
| 26 | - | |
| 27 | - const getCurrentKeys = computed(() => { | |
| 28 | - const { inputData } = props; | |
| 29 | - return (inputData || []).map((item) => item.identifier); | |
| 30 | - }); | |
| 31 | - | |
| 32 | - const [register, { setProps, getFieldsValue, setFieldsValue }] = useForm({ | |
| 33 | - schemas: [], | |
| 34 | - showActionButtonGroup: false, | |
| 35 | - layout: 'inline', | |
| 36 | - labelWidth: 80, | |
| 37 | - wrapperCol: { span: 12 }, | |
| 38 | - ...(props.formProps || ({} as unknown as Partial<DynamicProps<FormProps>>)), | |
| 39 | - }); | |
| 40 | - | |
| 41 | - const syncValue = (key: string, value: any) => { | |
| 42 | - let record = getFieldsValue(); | |
| 43 | - record = { ...record, [key]: value }; | |
| 44 | - const setValues = {}; | |
| 45 | - Object.keys(record).forEach((key) => { | |
| 46 | - const hasKey = unref(getCurrentKeys).includes(key); | |
| 47 | - if (hasKey) { | |
| 48 | - setValues[key] = record[key]; | |
| 49 | - } | |
| 50 | - }); | |
| 51 | - emit('update:value', setValues); | |
| 52 | - }; | |
| 53 | - | |
| 54 | - const createInputNumber = ({ | |
| 55 | - identifier, | |
| 56 | - functionName, | |
| 57 | - specs, | |
| 58 | - }: BasicCreateFormParams): FormSchema => { | |
| 59 | - const { valueRange } = specs! as Partial<Specs>; | |
| 60 | - const { max = 2147483647, min = -2147483648 } = valueRange || {}; | |
| 61 | - return { | |
| 62 | - field: identifier, | |
| 63 | - label: functionName, | |
| 64 | - component: 'InputNumber', | |
| 65 | - rules: [ | |
| 66 | - { | |
| 67 | - required: props.required, | |
| 68 | - message: `${functionName}是必填项`, | |
| 69 | - }, | |
| 70 | - { | |
| 71 | - type: 'number', | |
| 72 | - trigger: 'change', | |
| 73 | - validator: (_rule, value) => { | |
| 74 | - const reg = /^[0-9]*$/; | |
| 75 | - if (!reg.test(value)) return Promise.reject(`${functionName}不是一个有效的数字`); | |
| 76 | - if (value < min || value > max) { | |
| 77 | - return Promise.reject(`${functionName}取值范围在${min}~${max}之间`); | |
| 78 | - } | |
| 79 | - return Promise.resolve(value); | |
| 80 | - }, | |
| 81 | - }, | |
| 82 | - ], | |
| 83 | - componentProps: { | |
| 84 | - max, | |
| 85 | - min, | |
| 86 | - // step: step, | |
| 87 | - // formatter: (value: string) => value, | |
| 88 | - // parser: (string: string) => { | |
| 89 | - // if (dataType === DataTypeEnum.NUMBER_INT) { | |
| 90 | - // return Number(Number(string).toFixed()); | |
| 91 | - // } | |
| 92 | - // return Number(string); | |
| 93 | - // }, | |
| 94 | - onChange: (value: string) => { | |
| 95 | - syncValue(identifier, value); | |
| 96 | - }, | |
| 97 | - }, | |
| 98 | - } as FormSchema; | |
| 99 | - }; | |
| 100 | - | |
| 101 | - const createInput = ({ identifier, functionName, specs }: BasicCreateFormParams): FormSchema => { | |
| 102 | - const { length = 10240 } = specs! as Partial<Specs>; | |
| 103 | - return { | |
| 104 | - field: identifier, | |
| 105 | - label: functionName, | |
| 106 | - component: 'Input', | |
| 107 | - rules: [ | |
| 108 | - { | |
| 109 | - required: props.required, | |
| 110 | - message: `${functionName}是必填项`, | |
| 111 | - }, | |
| 112 | - { | |
| 113 | - type: 'string', | |
| 114 | - trigger: 'change', | |
| 115 | - validator: (_rule, value) => { | |
| 116 | - if (value.length > length) { | |
| 117 | - return Promise.reject(`${functionName}数据长度应该小于${length}`); | |
| 118 | - } | |
| 119 | - return Promise.resolve(value); | |
| 120 | - }, | |
| 121 | - }, | |
| 122 | - ], | |
| 123 | - componentProps: { | |
| 124 | - maxLength: length, | |
| 125 | - onChange: (value: InputEvent) => { | |
| 126 | - syncValue(identifier, (value.target as HTMLInputElement).value); | |
| 127 | - }, | |
| 128 | - }, | |
| 129 | - } as FormSchema; | |
| 130 | - }; | |
| 131 | - | |
| 132 | - const createSelect = ({ identifier, functionName, specs }: BasicCreateFormParams): FormSchema => { | |
| 133 | - const { boolClose, boolOpen } = specs! as Partial<Specs>; | |
| 134 | - return { | |
| 135 | - field: identifier, | |
| 136 | - label: functionName, | |
| 137 | - component: 'Select', | |
| 138 | - rules: [ | |
| 139 | - { | |
| 140 | - required: props.required, | |
| 141 | - message: `${functionName}是必填项`, | |
| 142 | - type: 'number', | |
| 143 | - }, | |
| 144 | - ], | |
| 145 | - componentProps: { | |
| 146 | - options: [ | |
| 147 | - { label: `${boolClose}-0`, value: 0 }, | |
| 148 | - { label: `${boolOpen}-1`, value: 1 }, | |
| 149 | - ], | |
| 150 | - onChange: (value: string) => { | |
| 151 | - syncValue(identifier, value); | |
| 152 | - }, | |
| 153 | - }, | |
| 154 | - }; | |
| 155 | - }; | |
| 156 | - | |
| 157 | - const createInputJson = ({ identifier, functionName }: BasicCreateFormParams): FormSchema => { | |
| 158 | - return { | |
| 159 | - field: identifier, | |
| 160 | - label: functionName, | |
| 161 | - component: 'InputTextArea', | |
| 162 | - rules: [ | |
| 163 | - { | |
| 164 | - required: props.required, | |
| 165 | - message: `${functionName}是必填项`, | |
| 166 | - }, | |
| 167 | - ], | |
| 168 | - componentProps: { | |
| 169 | - onChange: (value: InputEvent) => { | |
| 170 | - syncValue(identifier, (value.target as HTMLInputElement).value); | |
| 171 | - }, | |
| 172 | - }, | |
| 173 | - }; | |
| 174 | - }; | |
| 175 | - | |
| 176 | - const transformToFormSchema = (inputData: StructJSON[]) => { | |
| 177 | - const schemas: FormSchema[] = []; | |
| 178 | - for (const item of inputData) { | |
| 179 | - const { dataType, identifier, functionName, accessMode } = item; | |
| 180 | - if (accessMode === ReadAndWriteEnum.READ) { | |
| 181 | - continue; | |
| 182 | - } | |
| 183 | - const { type, specs } = dataType! || {}; | |
| 184 | - | |
| 185 | - const params: BasicCreateFormParams = { | |
| 186 | - identifier: identifier!, | |
| 187 | - functionName: functionName!, | |
| 188 | - dataType: dataType! as unknown as DataTypeEnum, | |
| 189 | - specs: specs as Partial<Specs>, | |
| 190 | - }; | |
| 191 | - if (type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE) { | |
| 192 | - schemas.push(createInputNumber(params)); | |
| 193 | - } | |
| 194 | - | |
| 195 | - if (type === DataTypeEnum.BOOL) { | |
| 196 | - schemas.push(createSelect(params)); | |
| 197 | - } | |
| 198 | - | |
| 199 | - if (type === DataTypeEnum.STRING) { | |
| 200 | - schemas.push(createInput(params)); | |
| 201 | - } | |
| 202 | - | |
| 203 | - if (type === DataTypeEnum.STRUCT) { | |
| 204 | - schemas.push(createInputJson(params)); | |
| 205 | - } | |
| 206 | - } | |
| 207 | - | |
| 208 | - return schemas; | |
| 209 | - }; | |
| 210 | - | |
| 211 | - const generateSchemas = (value: StructJSON[]) => { | |
| 212 | - if (value && value.length) { | |
| 213 | - const schemas = transformToFormSchema(value); | |
| 214 | - setProps({ schemas }); | |
| 215 | - } | |
| 216 | - }; | |
| 217 | - | |
| 218 | - onMounted(() => { | |
| 219 | - generateSchemas(props.inputData); | |
| 220 | - }); | |
| 221 | - | |
| 222 | - onUpdated(async () => { | |
| 223 | - if (props.inputData && props.inputData.length) { | |
| 224 | - await nextTick(); | |
| 225 | - setFieldsValue(props.value || {}); | |
| 226 | - } | |
| 227 | - }); | |
| 228 | - | |
| 229 | - watch( | |
| 230 | - () => props.inputData, | |
| 231 | - (value) => { | |
| 232 | - generateSchemas(value); | |
| 233 | - } | |
| 234 | - ); | |
| 235 | - | |
| 236 | - watch( | |
| 237 | - () => props.formProps, | |
| 238 | - (value) => { | |
| 239 | - setProps(value); | |
| 240 | - } | |
| 241 | - ); | |
| 242 | -</script> | |
| 243 | - | |
| 244 | -<template> | |
| 245 | - <Card bordered class="!border-dashed !rounded-lg !border-2px"> | |
| 246 | - <!-- <Alert class="!mb-4 w-32" message="服务参数配置" type="info" /> --> | |
| 247 | - <BasicForm class="object-model-validate-form" @register="register" /> | |
| 248 | - </Card> | |
| 249 | -</template> | |
| 250 | - | |
| 251 | -<style lang="less"> | |
| 252 | - .object-model-validate-form { | |
| 253 | - .ant-input-number { | |
| 254 | - width: 100% !important; | |
| 255 | - } | |
| 256 | - } | |
| 257 | -</style> |
src/components/Form/src/externalCompns/components/StructForm/StructForm.vue
deleted
100644 → 0
| 1 | -<script lang="ts"> | |
| 2 | - export default { | |
| 3 | - inheritAttrs: false, | |
| 4 | - }; | |
| 5 | -</script> | |
| 6 | -<script lang="ts" setup> | |
| 7 | - import StructFormModel from './StructFormModel.vue'; | |
| 8 | - import { useModal } from '/@/components/Modal'; | |
| 9 | - import { PlusOutlined } from '@ant-design/icons-vue'; | |
| 10 | - import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | |
| 11 | - import { computed, unref } from 'vue'; | |
| 12 | - import { buildUUID } from '/@/utils/uuid'; | |
| 13 | - import { Divider, Button } from 'ant-design-vue'; | |
| 14 | - import { OpenModalMode, OpenModalParams, StructRecord } from './type'; | |
| 15 | - import { cloneDeep } from 'lodash-es'; | |
| 16 | - import { isArray } from '/@/utils/is'; | |
| 17 | - import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | |
| 18 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 19 | - | |
| 20 | - const emit = defineEmits(['update:value']); | |
| 21 | - | |
| 22 | - const props = withDefaults( | |
| 23 | - defineProps<{ | |
| 24 | - value: ModelOfMatterParams[]; | |
| 25 | - disabled: boolean; | |
| 26 | - hasStructForm?: boolean; | |
| 27 | - hiddenAccessMode?: boolean; | |
| 28 | - }>(), | |
| 29 | - { | |
| 30 | - value: () => [], | |
| 31 | - hiddenAccessMode: false, | |
| 32 | - hasStructForm: false, | |
| 33 | - } | |
| 34 | - ); | |
| 35 | - | |
| 36 | - const getValue = computed<StructRecord[]>(() => { | |
| 37 | - const { value } = props; | |
| 38 | - | |
| 39 | - return (isArray(value) ? value : []).map((item) => { | |
| 40 | - return { | |
| 41 | - ...(item as StructRecord), | |
| 42 | - ...((item as StructRecord).id ? {} : { id: buildUUID() }), | |
| 43 | - }; | |
| 44 | - }); | |
| 45 | - }); | |
| 46 | - | |
| 47 | - const [registerModal, { openModal }] = useModal(); | |
| 48 | - | |
| 49 | - const handleCreateParams = () => { | |
| 50 | - openModal(true, { | |
| 51 | - mode: OpenModalMode.CREATE, | |
| 52 | - } as OpenModalParams); | |
| 53 | - }; | |
| 54 | - | |
| 55 | - const handleUpdate = (value: StructRecord) => { | |
| 56 | - openModal(true, { | |
| 57 | - mode: OpenModalMode.UPDATE, | |
| 58 | - record: { | |
| 59 | - ...value, | |
| 60 | - [FormField.HAS_STRUCT_FROM]: value?.dataType?.type === DataTypeEnum.STRUCT, | |
| 61 | - }, | |
| 62 | - } as OpenModalParams); | |
| 63 | - }; | |
| 64 | - | |
| 65 | - const handleDelete = (value: StructRecord) => { | |
| 66 | - const index = unref(getValue).findIndex((item) => item.id === value.id); | |
| 67 | - const _value = cloneDeep(unref(getValue)); | |
| 68 | - _value.splice(index, 1); | |
| 69 | - emit('update:value', _value); | |
| 70 | - }; | |
| 71 | - | |
| 72 | - const handleSaveStruct = (mode: OpenModalMode, value: StructRecord) => { | |
| 73 | - const _value = cloneDeep(unref(getValue)); | |
| 74 | - | |
| 75 | - if (mode === OpenModalMode.UPDATE) { | |
| 76 | - const index = unref(getValue).findIndex((item) => item.id === value.id); | |
| 77 | - ~index && _value.splice(index, 1, value); | |
| 78 | - } else { | |
| 79 | - _value.push(value); | |
| 80 | - } | |
| 81 | - | |
| 82 | - emit('update:value', _value); | |
| 83 | - }; | |
| 84 | -</script> | |
| 85 | - | |
| 86 | -<template> | |
| 87 | - <section> | |
| 88 | - <div class="text-blue-500 cursor-pointer"> | |
| 89 | - <section> | |
| 90 | - <div | |
| 91 | - class="flex bg-blue-50 mb-2 p-2 text-gray-500 justify-between items-center" | |
| 92 | - v-for="item in getValue" | |
| 93 | - :key="item.id" | |
| 94 | - > | |
| 95 | - <div>参数名称: {{ item.functionName }}</div> | |
| 96 | - <div class="flex"> | |
| 97 | - <Button class="!p-0" type="link" @click="handleUpdate(item)"> | |
| 98 | - <span>{{ disabled ? '查看' : '编辑' }}</span> | |
| 99 | - </Button> | |
| 100 | - <Divider type="vertical" /> | |
| 101 | - <Button :disabled="disabled" class="!p-0" type="link" @click="handleDelete(item)"> | |
| 102 | - <span>删除</span> | |
| 103 | - </Button> | |
| 104 | - </div> | |
| 105 | - </div> | |
| 106 | - </section> | |
| 107 | - <div :class="$props.disabled && 'text-gray-400'"> | |
| 108 | - <span class="mr-2"> | |
| 109 | - <PlusOutlined /> | |
| 110 | - </span> | |
| 111 | - <span @click="!disabled && handleCreateParams()">增加参数</span> | |
| 112 | - </div> | |
| 113 | - </div> | |
| 114 | - <StructFormModel | |
| 115 | - :has-struct-form="hasStructForm!" | |
| 116 | - :hidden-access-mode="hiddenAccessMode" | |
| 117 | - :disabled="disabled" | |
| 118 | - :value-list="getValue" | |
| 119 | - @register="registerModal" | |
| 120 | - @submit="handleSaveStruct" | |
| 121 | - /> | |
| 122 | - </section> | |
| 123 | -</template> | |
| 124 | - | |
| 125 | -<style lang="less" scoped></style> |
| 1 | -<script lang="ts"> | |
| 2 | - export default { | |
| 3 | - inheritAttrs: false, | |
| 4 | - }; | |
| 5 | -</script> | |
| 6 | -<script lang="ts" setup> | |
| 7 | - import { BasicForm, useForm } from '/@/components/Form'; | |
| 8 | - import { formSchemas } from './config'; | |
| 9 | - import { BasicModal, useModalInner } from '/@/components/Modal'; | |
| 10 | - import { OpenModalMode, OpenModalParams, StructRecord } from './type'; | |
| 11 | - import { computed, ref, unref } from 'vue'; | |
| 12 | - import { transfromToStructJSON } from './util'; | |
| 13 | - import { cloneDeep } from 'lodash-es'; | |
| 14 | - import { DataType, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | |
| 15 | - import { isArray } from '/@/utils/is'; | |
| 16 | - import { useMessage } from '/@/hooks/web/useMessage'; | |
| 17 | - import EnumList from './EnumList.vue'; | |
| 18 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 19 | - | |
| 20 | - const modalReceiveRecord = ref<OpenModalParams>({ | |
| 21 | - mode: OpenModalMode.CREATE, | |
| 22 | - }); | |
| 23 | - | |
| 24 | - const props = defineProps<{ | |
| 25 | - disabled: boolean; | |
| 26 | - hasStructForm: boolean; | |
| 27 | - valueList: StructRecord[]; | |
| 28 | - hiddenAccessMode: boolean; | |
| 29 | - }>(); | |
| 30 | - | |
| 31 | - const enumListRef = ref<InstanceType<typeof EnumList>>(); | |
| 32 | - | |
| 33 | - const emit = defineEmits(['register', 'submit']); | |
| 34 | - | |
| 35 | - const { createMessage } = useMessage(); | |
| 36 | - | |
| 37 | - const [register, { validate, setFieldsValue, setProps }] = useForm({ | |
| 38 | - labelWidth: 100, | |
| 39 | - actionColOptions: { | |
| 40 | - span: 14, | |
| 41 | - }, | |
| 42 | - disabled: props.disabled, | |
| 43 | - showResetButton: false, | |
| 44 | - submitOnReset: false, | |
| 45 | - showActionButtonGroup: false, | |
| 46 | - }); | |
| 47 | - | |
| 48 | - const getFormSchemas = computed(() => { | |
| 49 | - const { hasStructForm, hiddenAccessMode } = props; | |
| 50 | - return formSchemas({ | |
| 51 | - hasStructForm, | |
| 52 | - hiddenAccessMode, | |
| 53 | - }); | |
| 54 | - }); | |
| 55 | - | |
| 56 | - const [registerModal, { closeModal }] = useModalInner((record: OpenModalParams) => { | |
| 57 | - modalReceiveRecord.value = record; | |
| 58 | - const data = record.record || {}; | |
| 59 | - const { dataType = {} } = data! as StructJSON; | |
| 60 | - const { specs = {}, type, specsList } = dataType as DataType; | |
| 61 | - | |
| 62 | - if (record.record) { | |
| 63 | - const value = { | |
| 64 | - type, | |
| 65 | - ...data, | |
| 66 | - ...(isArray(specs) ? { specs } : { ...specs }), | |
| 67 | - enumList: type === DataTypeEnum.ENUM ? specsList : [], | |
| 68 | - }; | |
| 69 | - | |
| 70 | - setFieldsValue(value); | |
| 71 | - } | |
| 72 | - setProps({ disabled: props.disabled }); | |
| 73 | - }); | |
| 74 | - | |
| 75 | - const validateRepeat = (value: StructRecord, valueList: StructRecord[]) => { | |
| 76 | - return valueList.filter((item) => item.identifier === value.identifier).length >= 1; | |
| 77 | - }; | |
| 78 | - | |
| 79 | - const handleSubmit = async () => { | |
| 80 | - try { | |
| 81 | - const _value = await validate(); | |
| 82 | - await unref(enumListRef)?.validate?.(); | |
| 83 | - let structJSON = transfromToStructJSON(_value, unref(enumListRef)?.getFieldsValue?.() || []); | |
| 84 | - const value = { | |
| 85 | - ...structJSON, | |
| 86 | - ...(unref(modalReceiveRecord)?.record?.id | |
| 87 | - ? { id: unref(modalReceiveRecord)?.record?.id } | |
| 88 | - : {}), | |
| 89 | - }; | |
| 90 | - | |
| 91 | - if ( | |
| 92 | - unref(modalReceiveRecord).mode === OpenModalMode.CREATE && | |
| 93 | - validateRepeat(_value, props.valueList) | |
| 94 | - ) { | |
| 95 | - createMessage.error('存在一致的标识符'); | |
| 96 | - return; | |
| 97 | - } | |
| 98 | - emit('submit', unref(modalReceiveRecord).mode, cloneDeep(value)); | |
| 99 | - closeModal(); | |
| 100 | - } catch (error) {} | |
| 101 | - }; | |
| 102 | -</script> | |
| 103 | - | |
| 104 | -<template> | |
| 105 | - <BasicModal | |
| 106 | - @register="registerModal" | |
| 107 | - :title="modalReceiveRecord.mode === OpenModalMode.CREATE ? '创建参数' : '编辑参数'" | |
| 108 | - :width="800" | |
| 109 | - @ok="handleSubmit" | |
| 110 | - destroy-on-close | |
| 111 | - :show-ok-btn="!$props.disabled" | |
| 112 | - > | |
| 113 | - <BasicForm @register="register" :schemas="getFormSchemas"> | |
| 114 | - <template #EnumList="{ field, model }"> | |
| 115 | - <EnumList ref="enumListRef" :value="model[field]" :disabled="disabled" /> | |
| 116 | - </template> | |
| 117 | - </BasicForm> | |
| 118 | - </BasicModal> | |
| 119 | -</template> | |
| 120 | - | |
| 121 | -<style lang="less" scoped></style> |
src/components/Form/src/externalCompns/components/StructForm/config.ts
deleted
100644 → 0
| 1 | -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | |
| 2 | -import { findDictItemByCode } from '/@/api/system/dict'; | |
| 3 | -import { Rule } from '/@/components/Form'; | |
| 4 | -import { FormSchema } from '/@/components/Table'; | |
| 5 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 6 | -import { isNullOrUnDef } from '/@/utils/is'; | |
| 7 | -import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | |
| 8 | - | |
| 9 | -export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => { | |
| 10 | - value = value || {}; | |
| 11 | - const { min, max } = value; | |
| 12 | - if (min > max) { | |
| 13 | - return Promise.reject('最大值小于最小值'); | |
| 14 | - } | |
| 15 | - return Promise.resolve(); | |
| 16 | -}; | |
| 17 | - | |
| 18 | -export const validateJSON = (_rule, value = [] as ModelOfMatterParams[], _callback) => { | |
| 19 | - if (value.length) { | |
| 20 | - return Promise.resolve(); | |
| 21 | - } | |
| 22 | - return Promise.reject('JSON对象不能为空'); | |
| 23 | -}; | |
| 24 | - | |
| 25 | -interface StructFormSchemasParmasType { | |
| 26 | - hasStructForm: boolean; | |
| 27 | - hiddenAccessMode: boolean; | |
| 28 | - isTcp?: boolean; | |
| 29 | -} | |
| 30 | - | |
| 31 | -//功能名称和标识符 输入框不能包含逗号 | |
| 32 | -const validateExcludeComma = (field: string, errorName: string): Rule[] => { | |
| 33 | - return [ | |
| 34 | - { | |
| 35 | - required: true, | |
| 36 | - trigger: 'blur', | |
| 37 | - validator: () => { | |
| 38 | - const reg = /[,,]+/; | |
| 39 | - if (reg.test(field)) { | |
| 40 | - return Promise.reject(`${errorName}不能包含逗号`); | |
| 41 | - } | |
| 42 | - return Promise.resolve(); | |
| 43 | - }, | |
| 44 | - }, | |
| 45 | - ]; | |
| 46 | -}; | |
| 47 | - | |
| 48 | -export const formSchemas = ({ | |
| 49 | - hasStructForm, | |
| 50 | - hiddenAccessMode, | |
| 51 | - isTcp = false, | |
| 52 | -}: StructFormSchemasParmasType): FormSchema[] => { | |
| 53 | - return [ | |
| 54 | - { | |
| 55 | - field: FormField.FUNCTION_NAME, | |
| 56 | - label: '功能名称', | |
| 57 | - required: true, | |
| 58 | - component: 'Input', | |
| 59 | - colProps: { | |
| 60 | - span: 18, | |
| 61 | - }, | |
| 62 | - componentProps: { | |
| 63 | - maxLength: 32, | |
| 64 | - placeholder: '请输入功能名称', | |
| 65 | - }, | |
| 66 | - dynamicRules: ({ values }) => { | |
| 67 | - return [ | |
| 68 | - { required: true, message: '请输入功能名称' }, | |
| 69 | - ...validateExcludeComma(values[FormField.FUNCTION_NAME], '功能名称'), | |
| 70 | - ]; | |
| 71 | - }, | |
| 72 | - }, | |
| 73 | - { | |
| 74 | - field: FormField.IDENTIFIER, | |
| 75 | - label: '标识符', | |
| 76 | - required: true, | |
| 77 | - component: 'Input', | |
| 78 | - colProps: { | |
| 79 | - span: 18, | |
| 80 | - }, | |
| 81 | - componentProps: { | |
| 82 | - maxLength: 128, | |
| 83 | - placeholder: '请输入标识符', | |
| 84 | - }, | |
| 85 | - dynamicRules: ({ values }) => { | |
| 86 | - return [ | |
| 87 | - { required: true, message: '请输入标识符' }, | |
| 88 | - ...validateExcludeComma(values[FormField.IDENTIFIER], '标识符'), | |
| 89 | - ]; | |
| 90 | - }, | |
| 91 | - }, | |
| 92 | - { | |
| 93 | - field: FormField.HAS_STRUCT_FROM, | |
| 94 | - label: '是否已存在结构体', | |
| 95 | - component: 'Input', | |
| 96 | - ifShow: false, | |
| 97 | - }, | |
| 98 | - { | |
| 99 | - field: FormField.TYPE, | |
| 100 | - label: '数据类型', | |
| 101 | - required: true, | |
| 102 | - component: 'ApiSelect', | |
| 103 | - colProps: { | |
| 104 | - span: 9, | |
| 105 | - }, | |
| 106 | - defaultValue: 'INT', | |
| 107 | - componentProps: ({ formActionType }) => { | |
| 108 | - const { setFieldsValue } = formActionType; | |
| 109 | - return { | |
| 110 | - placeholder: '请选择数据类型', | |
| 111 | - api: async (params: Recordable) => { | |
| 112 | - try { | |
| 113 | - const record = await findDictItemByCode(params); | |
| 114 | - | |
| 115 | - if (isTcp) { | |
| 116 | - // TCP 产品 属性可创建范围 | |
| 117 | - return record.filter((item) => | |
| 118 | - [ | |
| 119 | - DataTypeEnum.BOOL, | |
| 120 | - DataTypeEnum.NUMBER_DOUBLE, | |
| 121 | - DataTypeEnum.NUMBER_INT, | |
| 122 | - DataTypeEnum.STRING, | |
| 123 | - ].includes(item.itemValue as DataTypeEnum) | |
| 124 | - ); | |
| 125 | - } | |
| 126 | - | |
| 127 | - return hasStructForm | |
| 128 | - ? record.filter((item) => item.itemValue !== DataTypeEnum.STRUCT) | |
| 129 | - : record; | |
| 130 | - } catch (error) { | |
| 131 | - return []; | |
| 132 | - } | |
| 133 | - }, | |
| 134 | - params: { | |
| 135 | - dictCode: 'data_type', | |
| 136 | - }, | |
| 137 | - labelField: 'itemText', | |
| 138 | - valueField: 'itemValue', | |
| 139 | - getPopupContainer: () => document.body, | |
| 140 | - onChange: (value: string) => { | |
| 141 | - if (value == DataTypeEnum.STRUCT) { | |
| 142 | - setFieldsValue({ [FormField.SPECS_LIST]: [], [FormField.HAS_STRUCT_FROM]: true }); | |
| 143 | - } | |
| 144 | - }, | |
| 145 | - }; | |
| 146 | - }, | |
| 147 | - }, | |
| 148 | - { | |
| 149 | - field: FormField.ENUM_LIST, | |
| 150 | - component: 'Input', | |
| 151 | - label: '枚举', | |
| 152 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.ENUM, | |
| 153 | - slot: 'EnumList', | |
| 154 | - colProps: { | |
| 155 | - span: 24, | |
| 156 | - }, | |
| 157 | - }, | |
| 158 | - { | |
| 159 | - field: FormField.VALUE_RANGE, | |
| 160 | - label: '取值范围', | |
| 161 | - component: 'CustomMinMaxInput', | |
| 162 | - valueField: 'value', | |
| 163 | - changeEvent: 'update:value', | |
| 164 | - colProps: { | |
| 165 | - span: 18, | |
| 166 | - }, | |
| 167 | - ifShow: ({ values }) => | |
| 168 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | |
| 169 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | |
| 170 | - rules: [{ validator: validateValueRange }], | |
| 171 | - }, | |
| 172 | - { | |
| 173 | - field: FormField.STEP, | |
| 174 | - label: '步长', | |
| 175 | - component: 'InputNumber', | |
| 176 | - colProps: { | |
| 177 | - span: 18, | |
| 178 | - }, | |
| 179 | - componentProps: { | |
| 180 | - maxLength: 255, | |
| 181 | - placeholder: '请输入步长', | |
| 182 | - min: 1, | |
| 183 | - formatter: (value: number | string) => { | |
| 184 | - return value ? Math.floor(Number(value)) : value; | |
| 185 | - }, | |
| 186 | - }, | |
| 187 | - ifShow: ({ values }) => | |
| 188 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | |
| 189 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | |
| 190 | - dynamicRules: ({ model }) => { | |
| 191 | - const valueRange = model[FormField.VALUE_RANGE] || {}; | |
| 192 | - const { min, max } = valueRange; | |
| 193 | - const step = model[FormField.STEP]; | |
| 194 | - return [ | |
| 195 | - { | |
| 196 | - validator: () => { | |
| 197 | - if ([min, max].every(isNullOrUnDef)) return Promise.resolve(); | |
| 198 | - if (step > max - min) { | |
| 199 | - return Promise.reject('步长不能大于取值范围的差值'); | |
| 200 | - } | |
| 201 | - return Promise.resolve(); | |
| 202 | - }, | |
| 203 | - }, | |
| 204 | - ]; | |
| 205 | - }, | |
| 206 | - }, | |
| 207 | - { | |
| 208 | - field: FormField.UNIT_NAME, | |
| 209 | - label: '单位名称', | |
| 210 | - component: 'Input', | |
| 211 | - show: false, | |
| 212 | - }, | |
| 213 | - { | |
| 214 | - field: FormField.UNIT, | |
| 215 | - label: '单位', | |
| 216 | - component: 'ApiSelect', | |
| 217 | - colProps: { | |
| 218 | - span: 9, | |
| 219 | - }, | |
| 220 | - componentProps: ({ formActionType }) => { | |
| 221 | - const { setFieldsValue } = formActionType; | |
| 222 | - return { | |
| 223 | - placeholder: '请选择单位', | |
| 224 | - api: async (params) => { | |
| 225 | - const list = await findDictItemByCode(params); | |
| 226 | - list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`)); | |
| 227 | - return list; | |
| 228 | - }, | |
| 229 | - params: { | |
| 230 | - dictCode: 'attribute_unit', | |
| 231 | - }, | |
| 232 | - labelInValue: true, | |
| 233 | - labelField: 'itemText', | |
| 234 | - valueField: 'itemValue', | |
| 235 | - onChange(_, record: Record<'label' | 'value', string>) { | |
| 236 | - if (record) { | |
| 237 | - const { label } = record; | |
| 238 | - setFieldsValue({ [FormField.UNIT_NAME]: label }); | |
| 239 | - } | |
| 240 | - }, | |
| 241 | - getPopupContainer: () => document.body, | |
| 242 | - showSearch: true, | |
| 243 | - filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | |
| 244 | - let { label, value } = option; | |
| 245 | - label = label.toLowerCase(); | |
| 246 | - value = value.toLowerCase(); | |
| 247 | - inputValue = inputValue.toLowerCase(); | |
| 248 | - return label.includes(inputValue) || value.includes(inputValue); | |
| 249 | - }, | |
| 250 | - }; | |
| 251 | - }, | |
| 252 | - ifShow: ({ values }) => | |
| 253 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | |
| 254 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | |
| 255 | - }, | |
| 256 | - { | |
| 257 | - field: FormField.BOOL_CLOSE, | |
| 258 | - component: 'Input', | |
| 259 | - required: true, | |
| 260 | - label: '0 -', | |
| 261 | - colProps: { | |
| 262 | - span: 18, | |
| 263 | - }, | |
| 264 | - componentProps: { | |
| 265 | - placeholder: '如:关', | |
| 266 | - }, | |
| 267 | - defaultValue: '关', | |
| 268 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL, | |
| 269 | - dynamicRules: ({ model }) => { | |
| 270 | - const close = model[FormField.BOOL_CLOSE]; | |
| 271 | - const open = model[FormField.BOOL_OPEN]; | |
| 272 | - return [ | |
| 273 | - { | |
| 274 | - required: true, | |
| 275 | - }, | |
| 276 | - { | |
| 277 | - validator() { | |
| 278 | - if (open === close) return Promise.reject('布尔值不能相同'); | |
| 279 | - return Promise.resolve(); | |
| 280 | - }, | |
| 281 | - }, | |
| 282 | - ]; | |
| 283 | - }, | |
| 284 | - }, | |
| 285 | - { | |
| 286 | - field: FormField.BOOL_OPEN, | |
| 287 | - component: 'Input', | |
| 288 | - required: true, | |
| 289 | - label: '1 -', | |
| 290 | - colProps: { | |
| 291 | - span: 18, | |
| 292 | - }, | |
| 293 | - componentProps: { | |
| 294 | - placeholder: '如:开', | |
| 295 | - }, | |
| 296 | - defaultValue: '开', | |
| 297 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL, | |
| 298 | - dynamicRules: ({ model }) => { | |
| 299 | - const close = model[FormField.BOOL_CLOSE]; | |
| 300 | - const open = model[FormField.BOOL_OPEN]; | |
| 301 | - return [ | |
| 302 | - { | |
| 303 | - required: true, | |
| 304 | - }, | |
| 305 | - { | |
| 306 | - validator() { | |
| 307 | - if (open === close) return Promise.reject('布尔值不能相同'); | |
| 308 | - return Promise.resolve(); | |
| 309 | - }, | |
| 310 | - }, | |
| 311 | - ]; | |
| 312 | - }, | |
| 313 | - }, | |
| 314 | - { | |
| 315 | - field: FormField.LENGTH, | |
| 316 | - component: 'Input', | |
| 317 | - required: true, | |
| 318 | - label: '数据长度', | |
| 319 | - defaultValue: '10240', | |
| 320 | - colProps: { | |
| 321 | - span: 8, | |
| 322 | - }, | |
| 323 | - componentProps: { | |
| 324 | - placeholder: '请输入数据长度', | |
| 325 | - }, | |
| 326 | - renderComponentContent: () => { | |
| 327 | - return { | |
| 328 | - suffix: () => '字节', | |
| 329 | - }; | |
| 330 | - }, | |
| 331 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRING, | |
| 332 | - }, | |
| 333 | - { | |
| 334 | - field: FormField.EXTENSION_DESC, | |
| 335 | - component: 'ExtendDesc', | |
| 336 | - label: '扩展描述', | |
| 337 | - valueField: 'value', | |
| 338 | - changeEvent: 'update:value', | |
| 339 | - ifShow: isTcp, | |
| 340 | - colProps: { | |
| 341 | - span: 16, | |
| 342 | - }, | |
| 343 | - componentProps: ({ formModel }) => { | |
| 344 | - return { | |
| 345 | - dataType: formModel[FormField.TYPE], | |
| 346 | - }; | |
| 347 | - }, | |
| 348 | - }, | |
| 349 | - { | |
| 350 | - field: FormField.ACCESS_MODE, | |
| 351 | - component: 'ApiRadioGroup', | |
| 352 | - label: '读写类型', | |
| 353 | - required: true, | |
| 354 | - colProps: { | |
| 355 | - span: 24, | |
| 356 | - }, | |
| 357 | - ifShow: () => !hiddenAccessMode && !hasStructForm, | |
| 358 | - defaultValue: 'r', | |
| 359 | - componentProps: { | |
| 360 | - placeholder: '请选择读写类型', | |
| 361 | - api: findDictItemByCode, | |
| 362 | - params: { | |
| 363 | - dictCode: 'read_write_type', | |
| 364 | - }, | |
| 365 | - labelField: 'itemText', | |
| 366 | - valueField: 'itemValue', | |
| 367 | - }, | |
| 368 | - }, | |
| 369 | - { | |
| 370 | - field: FormField.SPECS_LIST, | |
| 371 | - label: 'JSON对象', | |
| 372 | - component: 'StructForm', | |
| 373 | - valueField: 'value', | |
| 374 | - changeEvent: 'update:value', | |
| 375 | - colProps: { span: 24 }, | |
| 376 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRUCT, | |
| 377 | - rules: [{ required: true, validator: validateJSON }], | |
| 378 | - componentProps: ({ formModel }) => { | |
| 379 | - return { | |
| 380 | - hasStructForm: formModel[FormField.HAS_STRUCT_FROM], | |
| 381 | - }; | |
| 382 | - }, | |
| 383 | - }, | |
| 384 | - { | |
| 385 | - field: FormField.REFARK, | |
| 386 | - label: '备注', | |
| 387 | - component: 'InputTextArea', | |
| 388 | - componentProps: { | |
| 389 | - rows: 4, | |
| 390 | - maxLength: 100, | |
| 391 | - placeholder: '请输入描述', | |
| 392 | - }, | |
| 393 | - }, | |
| 394 | - ]; | |
| 395 | -}; |
src/components/Form/src/externalCompns/components/StructForm/type.ts
deleted
100644 → 0
| 1 | -import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | |
| 2 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 3 | -import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | |
| 4 | - | |
| 5 | -export enum OpenModalMode { | |
| 6 | - CREATE = 'create', | |
| 7 | - UPDATE = 'update', | |
| 8 | -} | |
| 9 | - | |
| 10 | -export interface OpenModalParams { | |
| 11 | - mode: OpenModalMode; | |
| 12 | - record?: StructRecord; | |
| 13 | -} | |
| 14 | - | |
| 15 | -export interface StructFormValue | |
| 16 | - extends Partial<Record<Exclude<FormField, FormField.VALUE_RANGE | FormField.STRUCT>, string>> { | |
| 17 | - [FormField.TYPE]: DataTypeEnum; | |
| 18 | - [FormField.VALUE_RANGE]?: { | |
| 19 | - [FormField.MIN]: string; | |
| 20 | - [FormField.MAX]: string; | |
| 21 | - }; | |
| 22 | - [FormField.STRUCT]: StructRecord[]; | |
| 23 | -} | |
| 24 | - | |
| 25 | -export interface StructRecord extends StructJSON { | |
| 26 | - id: string; | |
| 27 | -} |
src/components/Form/src/externalCompns/components/StructForm/util.ts
deleted
100644 → 0
| 1 | -import { cloneDeep } from 'lodash-es'; | |
| 2 | -import { StructFormValue } from './type'; | |
| 3 | -import { | |
| 4 | - DataType, | |
| 5 | - ModelOfMatterParams, | |
| 6 | - Specs, | |
| 7 | - StructJSON, | |
| 8 | -} from '/@/api/device/model/modelOfMatterModel'; | |
| 9 | -import { isArray } from '/@/utils/is'; | |
| 10 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 11 | - | |
| 12 | -export function transfromToStructJSON(value: StructFormValue, enumList: Specs[] = []): StructJSON { | |
| 13 | - const { | |
| 14 | - type, | |
| 15 | - valueRange, | |
| 16 | - step, | |
| 17 | - length = '', | |
| 18 | - boolClose, | |
| 19 | - boolOpen, | |
| 20 | - unit, | |
| 21 | - unitName, | |
| 22 | - functionName, | |
| 23 | - identifier, | |
| 24 | - remark, | |
| 25 | - specs, | |
| 26 | - accessMode, | |
| 27 | - } = value; | |
| 28 | - const basic = { functionName, identifier, remark, accessMode }; | |
| 29 | - let dataType = {} as unknown as DataType; | |
| 30 | - | |
| 31 | - switch (type) { | |
| 32 | - case DataTypeEnum.NUMBER_INT: | |
| 33 | - dataType = { | |
| 34 | - type, | |
| 35 | - specs: { valueRange, step, unit, unitName }, | |
| 36 | - }; | |
| 37 | - break; | |
| 38 | - | |
| 39 | - case DataTypeEnum.NUMBER_DOUBLE: | |
| 40 | - dataType = { | |
| 41 | - type, | |
| 42 | - specs: { valueRange, step, unit, unitName }, | |
| 43 | - }; | |
| 44 | - break; | |
| 45 | - | |
| 46 | - case DataTypeEnum.BOOL: | |
| 47 | - dataType = { | |
| 48 | - type, | |
| 49 | - specs: { | |
| 50 | - boolOpen: boolOpen, | |
| 51 | - boolClose: boolClose, | |
| 52 | - }, | |
| 53 | - }; | |
| 54 | - break; | |
| 55 | - | |
| 56 | - case DataTypeEnum.STRING: | |
| 57 | - dataType = { | |
| 58 | - type, | |
| 59 | - specs: { length }, | |
| 60 | - }; | |
| 61 | - break; | |
| 62 | - | |
| 63 | - case DataTypeEnum.ENUM: | |
| 64 | - dataType = { | |
| 65 | - type, | |
| 66 | - specsList: enumList, | |
| 67 | - }; | |
| 68 | - break; | |
| 69 | - | |
| 70 | - case DataTypeEnum.STRUCT: | |
| 71 | - dataType = { | |
| 72 | - type, | |
| 73 | - specs: specs! as unknown as ModelOfMatterParams[], | |
| 74 | - }; | |
| 75 | - break; | |
| 76 | - } | |
| 77 | - return { ...basic, dataType } as StructJSON; | |
| 78 | -} | |
| 79 | - | |
| 80 | -export const excludeIdInStructJSON = (struct: DataType) => { | |
| 81 | - const _value = cloneDeep(struct); | |
| 82 | - const { specs } = _value; | |
| 83 | - if (!specs) return _value; | |
| 84 | - const list = [specs]; | |
| 85 | - | |
| 86 | - while (list.length) { | |
| 87 | - for (const item of list) { | |
| 88 | - if (isArray(item)) { | |
| 89 | - (item as StructJSON[]).forEach((temp) => { | |
| 90 | - if (temp.dataType?.specs) { | |
| 91 | - list.push(temp.dataType.specs); | |
| 92 | - } | |
| 93 | - Reflect.has(temp, 'id') && Reflect.deleteProperty(temp, 'id'); | |
| 94 | - }); | |
| 95 | - } else { | |
| 96 | - Reflect.has(item as Recordable, 'id') && Reflect.deleteProperty(item as Recordable, 'id'); | |
| 97 | - } | |
| 98 | - list.shift(); | |
| 99 | - } | |
| 100 | - } | |
| 101 | - | |
| 102 | - return _value; | |
| 103 | -}; |
| ... | ... | @@ -117,12 +117,12 @@ export type ComponentType = |
| 117 | 117 | | 'ColorPicker' |
| 118 | 118 | | 'IconDrawer' |
| 119 | 119 | | 'ApiUpload' |
| 120 | + | 'ApiCascader' | |
| 120 | 121 | | 'ApiSearchSelect' |
| 121 | 122 | | 'StructForm' |
| 122 | 123 | | 'ApiSelectScrollLoad' |
| 123 | 124 | | 'TransferModal' |
| 124 | 125 | | 'TransferTableModal' |
| 125 | - | 'ObjectModelValidateForm' | |
| 126 | 126 | | 'ThingsModelForm' |
| 127 | 127 | | 'DevicePicker' |
| 128 | 128 | | 'ProductPicker' |
| ... | ... | @@ -145,4 +145,6 @@ export type ComponentType = |
| 145 | 145 | | 'TriggerDurationInput' |
| 146 | 146 | | 'AlarmProfileSelect' |
| 147 | 147 | | 'LockControlGroup' |
| 148 | - | 'EnumList'; | |
| 148 | + | 'EnumList' | |
| 149 | + | 'Segmented' | |
| 150 | + | 'StructFormItem'; | ... | ... |
src/enums/deviceEnum.ts
0 → 100644
| 1 | +export enum TransportTypeEnum { | |
| 2 | + DEFAULT = 'DEFAULT', | |
| 3 | + MQTT = 'MQTT', | |
| 4 | + COAP = 'COAP', | |
| 5 | + LWM2M = 'LWM2M', | |
| 6 | + SNMP = 'SNMP', | |
| 7 | + TCP = 'TCP', | |
| 8 | +} | |
| 9 | + | |
| 10 | +export enum ReadAndWriteEnum { | |
| 11 | + READ = 'r', | |
| 12 | + READ_AND_WRITE = 'rw', | |
| 13 | +} | |
| 14 | + | |
| 15 | +export enum ServiceCallTypeEnum { | |
| 16 | + ASYNC = 'ASYNC', | |
| 17 | + SYNC = 'SYNC', | |
| 18 | +} | |
| 19 | + | |
| 20 | +export enum ServiceCallTypeNameEnum { | |
| 21 | + ASYNC = '异步', | |
| 22 | + SYNC = '同步', | |
| 23 | +} | |
| 24 | + | |
| 25 | +export enum CommandDeliveryWayEnum { | |
| 26 | + ONE_WAY = 'oneway', | |
| 27 | + TWO_WAY = 'twoway', | |
| 28 | +} | |
| 29 | + | |
| 30 | +export enum CommandDeliveryWayNameEnum { | |
| 31 | + ONE_WAY = '单向', | |
| 32 | + TWO_WAY = '双向', | |
| 33 | +} | |
| 34 | + | |
| 35 | +export enum CommandTypeEnum { | |
| 36 | + CUSTOM = 0, | |
| 37 | + SERVICE = 1, | |
| 38 | + ATTRIBUTE = 2, | |
| 39 | + API = 'api', | |
| 40 | +} | |
| 41 | + | |
| 42 | +export enum CommandTypeNameEnum { | |
| 43 | + CUSTOM = '自定义', | |
| 44 | + SERVICE = '服务', | |
| 45 | + ATTRIBUTE = '属性', | |
| 46 | +} | |
| 47 | + | |
| 48 | +export enum RPCCommandMethodEnum { | |
| 49 | + THINGSKIT = 'methodThingskit', | |
| 50 | +} | ... | ... |
| ... | ... | @@ -154,16 +154,3 @@ export enum ExecutionActionNameEnum { |
| 154 | 154 | DEVICE_OUT = '设备输出', |
| 155 | 155 | MSG_NOTIFY = '告警输出', |
| 156 | 156 | } |
| 157 | - | |
| 158 | -export enum CommandTypeEnum { | |
| 159 | - CUSTOM = 0, | |
| 160 | - SERVICE = 1, | |
| 161 | - ATTRIBUTE = 2, | |
| 162 | - API = 'api', | |
| 163 | -} | |
| 164 | - | |
| 165 | -export enum CommandTypeNameEnum { | |
| 166 | - CUSTOM = '自定义', | |
| 167 | - SERVICE = '服务', | |
| 168 | - ATTRIBUTE = '属性', | |
| 169 | -} | ... | ... |
| ... | ... | @@ -7,6 +7,30 @@ export enum DataTypeEnum { |
| 7 | 7 | ENUM = 'ENUM', |
| 8 | 8 | } |
| 9 | 9 | |
| 10 | +export enum FunctionTypeEnum { | |
| 11 | + PROPERTIES = 'properties', | |
| 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 = '故障', | |
| 32 | +} | |
| 33 | + | |
| 10 | 34 | export enum RegisterDataTypeEnum { |
| 11 | 35 | UN_SHORT = 'unshort', |
| 12 | 36 | } |
| ... | ... | @@ -26,3 +50,12 @@ export enum RegisterActionTypeNameEnum { |
| 26 | 50 | INT = '06写入单个保持寄存器', |
| 27 | 51 | DOUBLE = '16写入多个保持寄存器', |
| 28 | 52 | } |
| 53 | + | |
| 54 | +export enum ObjectModelAccessModeEnum { | |
| 55 | + READ = 'r', | |
| 56 | + READ_AND_WRITE = 'rw', | |
| 57 | +} | |
| 58 | + | |
| 59 | +export enum ModbusCRCEnum { | |
| 60 | + CRC_16_LOWER = 'CRC_16_LOWER', | |
| 61 | +} | ... | ... |
| ... | ... | @@ -29,23 +29,3 @@ export enum BooleanStringEnum { |
| 29 | 29 | TRUE = 'true', |
| 30 | 30 | FALSE = 'false', |
| 31 | 31 | } |
| 32 | - | |
| 33 | -export enum ReadAndWriteEnum { | |
| 34 | - READ = 'r', | |
| 35 | - READ_AND_WRITE = 'rw', | |
| 36 | -} | |
| 37 | - | |
| 38 | -export enum ServiceCallTypeEnum { | |
| 39 | - ASYNC = 'ASYNC', | |
| 40 | - SYNC = 'SYNC', | |
| 41 | -} | |
| 42 | - | |
| 43 | -export enum ServiceCallTypeNameEnum { | |
| 44 | - ASYNC = '异步', | |
| 45 | - SYNC = '同步', | |
| 46 | -} | |
| 47 | - | |
| 48 | -export enum CommandDeliveryWayEnum { | |
| 49 | - ONE_WAY = 'oneway', | |
| 50 | - TWO_WAY = 'twoway', | |
| 51 | -} | ... | ... |
| ... | ... | @@ -6,6 +6,7 @@ |
| 6 | 6 | :title="getTitle" |
| 7 | 7 | width="30%" |
| 8 | 8 | @ok="handleSubmit" |
| 9 | + wrapClassName="camera-configration-drawer" | |
| 9 | 10 | > |
| 10 | 11 | <BasicForm @register="registerForm"> |
| 11 | 12 | <template #videoPlatformIdSlot="{ model, field }"> |
| ... | ... | @@ -125,7 +126,11 @@ |
| 125 | 126 | }); |
| 126 | 127 | } |
| 127 | 128 | |
| 128 | - setFieldsValue({ ...record, videoType: record.videoPlatformDTO?.type }); | |
| 129 | + setFieldsValue({ | |
| 130 | + ...record, | |
| 131 | + videoType: record.videoPlatformDTO?.type, | |
| 132 | + ...(record?.params || {}), | |
| 133 | + }); | |
| 129 | 134 | } else { |
| 130 | 135 | editId.value = ''; |
| 131 | 136 | } |
| ... | ... | @@ -157,6 +162,7 @@ |
| 157 | 162 | ) { |
| 158 | 163 | values.streamType = values.articulation; |
| 159 | 164 | values.playProtocol = values.videoFormat; |
| 165 | + values.params = values.channelNo ? { channelNo: values.channelNo } : null; | |
| 160 | 166 | } |
| 161 | 167 | |
| 162 | 168 | await createOrEditCameraManage(values); |
| ... | ... | @@ -190,3 +196,11 @@ |
| 190 | 196 | }, |
| 191 | 197 | }); |
| 192 | 198 | </script> |
| 199 | + | |
| 200 | +<style lang="less"> | |
| 201 | + .camera-configration-drawer { | |
| 202 | + .ant-input-number { | |
| 203 | + width: 100% !important; | |
| 204 | + } | |
| 205 | + } | |
| 206 | +</style> | ... | ... |
| ... | ... | @@ -21,11 +21,6 @@ export enum CameraPermission { |
| 21 | 21 | DELETE = 'api:yt:video:delete', |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | -export enum VideoPlatformEnum { | |
| 25 | - HAIKANG = 0, | |
| 26 | - FLUORITE = 1, | |
| 27 | -} | |
| 28 | - | |
| 29 | 24 | export enum AccessMode { |
| 30 | 25 | ManuallyEnter = 0, |
| 31 | 26 | Streaming = 1, |
| ... | ... | @@ -250,16 +245,28 @@ export const formSchema: QFormSchema[] = [ |
| 250 | 245 | required: true, |
| 251 | 246 | defaultValue: VideoPlatformEnum.SCI, |
| 252 | 247 | ifShow: ({ values }) => values.accessMode === AccessMode.Streaming, |
| 253 | - componentProps: { | |
| 254 | - api: async (params) => { | |
| 255 | - const values = await findDictItemByCode(params); | |
| 256 | - return values.map((item) => ({ label: item.itemText, value: Number(item.itemValue) })); | |
| 257 | - }, | |
| 258 | - params: { | |
| 259 | - dictCode: DictEnum.STREAMING_MEDIA_TYPE, | |
| 260 | - }, | |
| 261 | - getPopupContainer: () => document.body, | |
| 262 | - placeholder: `请选择平台类型`, | |
| 248 | + componentProps: ({ formActionType }) => { | |
| 249 | + return { | |
| 250 | + api: async (params) => { | |
| 251 | + const values = await findDictItemByCode(params); | |
| 252 | + return values.map((item) => ({ label: item.itemText, value: Number(item.itemValue) })); | |
| 253 | + }, | |
| 254 | + params: { | |
| 255 | + dictCode: DictEnum.STREAMING_MEDIA_TYPE, | |
| 256 | + }, | |
| 257 | + getPopupContainer: () => document.body, | |
| 258 | + placeholder: `请选择平台类型`, | |
| 259 | + onChange() { | |
| 260 | + const { setFieldsValue } = formActionType; | |
| 261 | + setFieldsValue({ | |
| 262 | + articulation: ArticulationEnumType.HIGH_DEFINITION, | |
| 263 | + playProtocol: PlayProtocol.HTTP, | |
| 264 | + sn: null, | |
| 265 | + channelNo: null, | |
| 266 | + videoPlatformId: null, | |
| 267 | + }); | |
| 268 | + }, | |
| 269 | + }; | |
| 263 | 270 | }, |
| 264 | 271 | }, |
| 265 | 272 | { |
| ... | ... | @@ -384,4 +391,22 @@ export const formSchema: QFormSchema[] = [ |
| 384 | 391 | placeholder: '请输入监控点编号', |
| 385 | 392 | }, |
| 386 | 393 | }, |
| 394 | + { | |
| 395 | + field: 'channelNo', | |
| 396 | + label: '通道号', | |
| 397 | + component: 'InputNumber', | |
| 398 | + ifShow({ values }) { | |
| 399 | + return ( | |
| 400 | + values.accessMode === AccessMode.Streaming && | |
| 401 | + values.videoType === VideoPlatformEnum.FLUORITE | |
| 402 | + ); | |
| 403 | + }, | |
| 404 | + componentProps: { | |
| 405 | + min: 1, | |
| 406 | + max: 10000, | |
| 407 | + step: 1, | |
| 408 | + placeholder: '请输入通道号', | |
| 409 | + precision: 0, | |
| 410 | + }, | |
| 411 | + }, | |
| 387 | 412 | ]; | ... | ... |
| 1 | -import { FormProps, FormSchema, useComponentRegister } from '/@/components/Form'; | |
| 1 | +import { FormSchema, useComponentRegister } from '/@/components/Form'; | |
| 2 | 2 | import { findDictItemByCode } from '/@/api/system/dict'; |
| 3 | 3 | import { getGatewayDevice, queryDeviceProfileBy } from '/@/api/device/deviceManager'; |
| 4 | -import { TransportTypeEnum } from '../../profiles/components/TransportDescript/const'; | |
| 5 | -import { JSONEditorValidator } from '/@/components/CodeEditor/src/JSONEditor'; | |
| 6 | 4 | import { JSONEditor } from '/@/components/CodeEditor'; |
| 7 | 5 | import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; |
| 8 | -import { getModelServices } from '/@/api/device/modelOfMatter'; | |
| 9 | -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | |
| 10 | -import { h, toRaw, unref } from 'vue'; | |
| 11 | -import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue'; | |
| 12 | -import { CommandDeliveryWayEnum, ServiceCallTypeEnum } from '/@/enums/toolEnum'; | |
| 6 | +import { h } from 'vue'; | |
| 13 | 7 | import { TaskTypeEnum } from '/@/views/task/center/config'; |
| 14 | 8 | import { AddressTypeEnum } from '/@/views/task/center/components/PollCommandInput'; |
| 15 | 9 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; |
| ... | ... | @@ -17,9 +11,9 @@ import { createImgPreview } from '/@/components/Preview'; |
| 17 | 11 | import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; |
| 18 | 12 | import LockControlGroup from '/@/components/Form/src/components/LockControlGroup.vue'; |
| 19 | 13 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; |
| 14 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 20 | 15 | |
| 21 | 16 | useComponentRegister('JSONEditor', JSONEditor); |
| 22 | -useComponentRegister('ObjectModelValidateForm', ObjectModelValidateForm); | |
| 23 | 17 | useComponentRegister('LockControlGroup', LockControlGroup); |
| 24 | 18 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
| 25 | 19 | |
| ... | ... | @@ -910,214 +904,3 @@ export const TokenSchemas: FormSchema[] = [ |
| 910 | 904 | }, |
| 911 | 905 | }, |
| 912 | 906 | ]; |
| 913 | - | |
| 914 | -export enum ValueType { | |
| 915 | - JSON = 'json', | |
| 916 | - STRING = 'string', | |
| 917 | -} | |
| 918 | - | |
| 919 | -export enum CommandFieldsEnum { | |
| 920 | - COMMAND_TYPE = 'commandType', | |
| 921 | - VALUE_TYPE = 'valueType', | |
| 922 | - COMMAND_TEXT = 'commandText', | |
| 923 | - COMAND_VALUE = 'commandValue', | |
| 924 | - SERVICE = 'service', | |
| 925 | - SERVICE_TYPE = 'service_type', | |
| 926 | - TCP_SERVICE = 'tcpService', | |
| 927 | - MODEL_INPUT = 'modelInput', | |
| 928 | - CUSTOM_TYPE = 'customType', | |
| 929 | -} | |
| 930 | - | |
| 931 | -export enum CommandType { | |
| 932 | - CUSTOM = 'custom', | |
| 933 | - SERVICE = 'service', | |
| 934 | -} | |
| 935 | - | |
| 936 | -export const CommandSchemas = ( | |
| 937 | - transportType: TransportTypeEnum, | |
| 938 | - deviceProfileId: string | |
| 939 | -): FormSchema[] => { | |
| 940 | - return [ | |
| 941 | - { | |
| 942 | - field: CommandFieldsEnum.COMMAND_TYPE, | |
| 943 | - component: 'RadioGroup', | |
| 944 | - label: '下发类型', | |
| 945 | - defaultValue: CommandType.CUSTOM, | |
| 946 | - componentProps: ({ formActionType }) => { | |
| 947 | - const { setFieldsValue } = formActionType; | |
| 948 | - return { | |
| 949 | - options: [ | |
| 950 | - { label: '自定义', value: CommandType.CUSTOM }, | |
| 951 | - { label: '服务', value: CommandType.SERVICE }, | |
| 952 | - ], | |
| 953 | - onChange() { | |
| 954 | - setFieldsValue({ | |
| 955 | - [CommandFieldsEnum.SERVICE]: null, | |
| 956 | - [CommandFieldsEnum.MODEL_INPUT]: null, | |
| 957 | - [CommandFieldsEnum.COMAND_VALUE]: null, | |
| 958 | - [CommandFieldsEnum.COMMAND_TEXT]: null, | |
| 959 | - }); | |
| 960 | - }, | |
| 961 | - }; | |
| 962 | - }, | |
| 963 | - }, | |
| 964 | - { | |
| 965 | - field: CommandFieldsEnum.CUSTOM_TYPE, | |
| 966 | - component: 'RadioGroup', | |
| 967 | - label: '单向/双向', | |
| 968 | - defaultValue: CommandDeliveryWayEnum.ONE_WAY, | |
| 969 | - ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM, | |
| 970 | - componentProps: { | |
| 971 | - options: [ | |
| 972 | - { | |
| 973 | - label: '单向', | |
| 974 | - value: CommandDeliveryWayEnum.ONE_WAY, | |
| 975 | - }, | |
| 976 | - { | |
| 977 | - label: '双向', | |
| 978 | - value: CommandDeliveryWayEnum.TWO_WAY, | |
| 979 | - }, | |
| 980 | - ], | |
| 981 | - }, | |
| 982 | - }, | |
| 983 | - { | |
| 984 | - field: CommandFieldsEnum.VALUE_TYPE, | |
| 985 | - label: '命令类型', | |
| 986 | - component: 'RadioGroup', | |
| 987 | - ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM, | |
| 988 | - defaultValue: transportType === TransportTypeEnum.TCP ? ValueType.STRING : ValueType.JSON, | |
| 989 | - componentProps: () => { | |
| 990 | - const options: Record<'label' | 'value', string>[] = []; | |
| 991 | - if (transportType === TransportTypeEnum.TCP) { | |
| 992 | - options.push({ label: '字符串', value: ValueType.STRING }); | |
| 993 | - } else { | |
| 994 | - options.push({ label: 'JSON', value: ValueType.JSON }); | |
| 995 | - } | |
| 996 | - return { | |
| 997 | - options, | |
| 998 | - }; | |
| 999 | - }, | |
| 1000 | - }, | |
| 1001 | - { | |
| 1002 | - field: CommandFieldsEnum.COMMAND_TEXT, | |
| 1003 | - label: '命令', | |
| 1004 | - ifShow: ({ model }) => | |
| 1005 | - model[CommandFieldsEnum.VALUE_TYPE] === ValueType.STRING && | |
| 1006 | - model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM, | |
| 1007 | - component: 'InputTextArea', | |
| 1008 | - componentProps: { | |
| 1009 | - autoSize: { | |
| 1010 | - minRows: 3, | |
| 1011 | - }, | |
| 1012 | - placeholder: '请输入命令内容', | |
| 1013 | - }, | |
| 1014 | - dynamicRules: () => { | |
| 1015 | - return [ | |
| 1016 | - { | |
| 1017 | - required: false, | |
| 1018 | - validator: (_, value) => { | |
| 1019 | - const zg = /^[0-9a-zA-Z]*$/; | |
| 1020 | - if (!zg.test(value)) { | |
| 1021 | - return Promise.reject('输入的内容只能是字母和数字的组合'); | |
| 1022 | - } else { | |
| 1023 | - return Promise.resolve(); | |
| 1024 | - } | |
| 1025 | - }, | |
| 1026 | - }, | |
| 1027 | - ]; | |
| 1028 | - }, | |
| 1029 | - }, | |
| 1030 | - { | |
| 1031 | - field: CommandFieldsEnum.COMAND_VALUE, | |
| 1032 | - label: '命令', | |
| 1033 | - component: 'JSONEditor', | |
| 1034 | - colProps: { span: 20 }, | |
| 1035 | - changeEvent: 'update:value', | |
| 1036 | - valueField: 'value', | |
| 1037 | - rules: [...JSONEditorValidator()], | |
| 1038 | - ifShow: ({ model }) => | |
| 1039 | - model[CommandFieldsEnum.VALUE_TYPE] === ValueType.JSON && | |
| 1040 | - model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM, | |
| 1041 | - | |
| 1042 | - componentProps: { | |
| 1043 | - height: 250, | |
| 1044 | - }, | |
| 1045 | - }, | |
| 1046 | - { | |
| 1047 | - field: CommandFieldsEnum.SERVICE, | |
| 1048 | - label: '服务', | |
| 1049 | - component: 'ApiSelect', | |
| 1050 | - ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM, | |
| 1051 | - rules: [{ required: true, message: '请选择服务' }], | |
| 1052 | - componentProps: ({ formActionType }) => { | |
| 1053 | - const { setFieldsValue, updateSchema } = formActionType; | |
| 1054 | - return { | |
| 1055 | - api: async () => { | |
| 1056 | - try { | |
| 1057 | - const result = await getModelServices({ deviceProfileId }); | |
| 1058 | - return result || []; | |
| 1059 | - } catch (error) { | |
| 1060 | - return []; | |
| 1061 | - } | |
| 1062 | - }, | |
| 1063 | - valueField: 'identifier', | |
| 1064 | - labelField: 'functionName', | |
| 1065 | - getPopupContainer: () => document.body, | |
| 1066 | - onChange(value: string, options: ModelOfMatterParams) { | |
| 1067 | - if (!value) return; | |
| 1068 | - | |
| 1069 | - const setValues = { | |
| 1070 | - [CommandFieldsEnum.CUSTOM_TYPE]: | |
| 1071 | - options.callType === ServiceCallTypeEnum.ASYNC | |
| 1072 | - ? CommandDeliveryWayEnum.ONE_WAY | |
| 1073 | - : CommandDeliveryWayEnum.TWO_WAY, | |
| 1074 | - [CommandFieldsEnum.MODEL_INPUT]: null, | |
| 1075 | - }; | |
| 1076 | - | |
| 1077 | - if (transportType !== TransportTypeEnum.TCP) { | |
| 1078 | - updateSchema({ | |
| 1079 | - field: CommandFieldsEnum.MODEL_INPUT, | |
| 1080 | - componentProps: { | |
| 1081 | - inputData: toRaw(unref(options.functionJson.inputData)), | |
| 1082 | - }, | |
| 1083 | - }); | |
| 1084 | - } else { | |
| 1085 | - Object.assign(setValues, { | |
| 1086 | - [CommandFieldsEnum.TCP_SERVICE]: | |
| 1087 | - options.functionJson?.inputData?.[0]?.serviceCommand, | |
| 1088 | - }); | |
| 1089 | - } | |
| 1090 | - | |
| 1091 | - setFieldsValue(setValues); | |
| 1092 | - }, | |
| 1093 | - }; | |
| 1094 | - }, | |
| 1095 | - }, | |
| 1096 | - { | |
| 1097 | - field: CommandFieldsEnum.TCP_SERVICE, | |
| 1098 | - component: 'Input', | |
| 1099 | - label: '命令', | |
| 1100 | - dynamicDisabled: true, | |
| 1101 | - ifShow: ({ model }) => | |
| 1102 | - model[CommandFieldsEnum.SERVICE] && | |
| 1103 | - transportType === TransportTypeEnum.TCP && | |
| 1104 | - model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM, | |
| 1105 | - }, | |
| 1106 | - { | |
| 1107 | - field: CommandFieldsEnum.MODEL_INPUT, | |
| 1108 | - component: 'ObjectModelValidateForm', | |
| 1109 | - label: '输入参数', | |
| 1110 | - changeEvent: 'update:value', | |
| 1111 | - valueField: 'value', | |
| 1112 | - ifShow: ({ model }) => | |
| 1113 | - model[CommandFieldsEnum.SERVICE] && | |
| 1114 | - transportType !== TransportTypeEnum.TCP && | |
| 1115 | - model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM, | |
| 1116 | - componentProps: { | |
| 1117 | - formProps: { | |
| 1118 | - wrapperCol: { span: 24 }, | |
| 1119 | - } as FormProps, | |
| 1120 | - }, | |
| 1121 | - }, | |
| 1122 | - ]; | |
| 1123 | -}; | ... | ... |
| ... | ... | @@ -5,8 +5,8 @@ import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; |
| 5 | 5 | import { findDictItemByCode } from '/@/api/system/dict'; |
| 6 | 6 | import { FormSchema, useComponentRegister } from '/@/components/Form'; |
| 7 | 7 | import { BasicColumn } from '/@/components/Table'; |
| 8 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 8 | 9 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; |
| 9 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 10 | 10 | import XLSX, { CellObject } from 'xlsx'; |
| 11 | 11 | |
| 12 | 12 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); | ... | ... |
| 1 | +import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | |
| 2 | +import { getModelServices } from '/@/api/device/modelOfMatter'; | |
| 3 | +import { FormProps, FormSchema, useComponentRegister } from '/@/components/Form'; | |
| 4 | +import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm'; | |
| 5 | +import { JSONEditor, JSONEditorValidator } from '/@/components/CodeEditor'; | |
| 6 | +import { | |
| 7 | + TransportTypeEnum, | |
| 8 | + CommandTypeNameEnum, | |
| 9 | + CommandTypeEnum, | |
| 10 | + ServiceCallTypeEnum, | |
| 11 | + CommandDeliveryWayEnum, | |
| 12 | + CommandDeliveryWayNameEnum, | |
| 13 | +} from '/@/enums/deviceEnum'; | |
| 14 | + | |
| 15 | +export interface CommandDeliveryFormFieldType { | |
| 16 | + [CommandFieldsEnum.COMMAND_TYPE]: CommandTypeEnum; | |
| 17 | + [CommandFieldsEnum.TCP_COMMAND_VALUE]?: string; | |
| 18 | + [CommandFieldsEnum.COMAND_VALUE]?: string; | |
| 19 | + [CommandFieldsEnum.SERVICE]?: string; | |
| 20 | + [CommandFieldsEnum.MODEL_INPUT]?: ModelOfMatterParams; | |
| 21 | + [CommandFieldsEnum.CALL_TYPE]: CommandDeliveryWayEnum; | |
| 22 | +} | |
| 23 | + | |
| 24 | +export enum CommandFieldsEnum { | |
| 25 | + COMMAND_TYPE = 'commandType', | |
| 26 | + TCP_COMMAND_VALUE = 'tcpCommandValue', | |
| 27 | + COMAND_VALUE = 'commandValue', | |
| 28 | + SERVICE = 'service', | |
| 29 | + MODEL_INPUT = 'modelInput', | |
| 30 | + CALL_TYPE = 'callType', | |
| 31 | + | |
| 32 | + SERVICE_COMMAND = 'serviceCommand', | |
| 33 | + | |
| 34 | + SERVICE_OBJECT_MODEL = 'serviceObjectModel', | |
| 35 | +} | |
| 36 | + | |
| 37 | +useComponentRegister('JSONEditor', JSONEditor); | |
| 38 | + | |
| 39 | +export const CommandSchemas = ( | |
| 40 | + transportType: TransportTypeEnum, | |
| 41 | + deviceProfileId: string | |
| 42 | +): FormSchema[] => { | |
| 43 | + return [ | |
| 44 | + { | |
| 45 | + field: CommandFieldsEnum.COMMAND_TYPE, | |
| 46 | + component: 'RadioGroup', | |
| 47 | + label: '下发类型', | |
| 48 | + defaultValue: CommandTypeEnum.CUSTOM, | |
| 49 | + required: true, | |
| 50 | + componentProps: ({ formActionType }) => { | |
| 51 | + const { setFieldsValue } = formActionType; | |
| 52 | + return { | |
| 53 | + options: [ | |
| 54 | + { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM }, | |
| 55 | + { label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE }, | |
| 56 | + ], | |
| 57 | + onChange() { | |
| 58 | + setFieldsValue({ | |
| 59 | + [CommandFieldsEnum.SERVICE]: null, | |
| 60 | + [CommandFieldsEnum.MODEL_INPUT]: null, | |
| 61 | + [CommandFieldsEnum.COMAND_VALUE]: null, | |
| 62 | + [CommandFieldsEnum.TCP_COMMAND_VALUE]: null, | |
| 63 | + }); | |
| 64 | + }, | |
| 65 | + }; | |
| 66 | + }, | |
| 67 | + }, | |
| 68 | + { | |
| 69 | + field: CommandFieldsEnum.CALL_TYPE, | |
| 70 | + component: 'RadioGroup', | |
| 71 | + label: '单向/双向', | |
| 72 | + required: true, | |
| 73 | + defaultValue: CommandDeliveryWayEnum.ONE_WAY, | |
| 74 | + ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM, | |
| 75 | + componentProps: { | |
| 76 | + options: Object.keys(CommandDeliveryWayEnum).map((key) => ({ | |
| 77 | + label: CommandDeliveryWayNameEnum[key], | |
| 78 | + value: CommandDeliveryWayEnum[key], | |
| 79 | + })), | |
| 80 | + }, | |
| 81 | + }, | |
| 82 | + { | |
| 83 | + field: CommandFieldsEnum.TCP_COMMAND_VALUE, | |
| 84 | + label: '命令', | |
| 85 | + required: true, | |
| 86 | + ifShow: ({ model }) => | |
| 87 | + transportType === TransportTypeEnum.TCP && | |
| 88 | + model[CommandFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM, | |
| 89 | + component: 'Input', | |
| 90 | + rules: [{ validator: validateTCPCustomCommand }], | |
| 91 | + componentProps: { | |
| 92 | + placeholder: '请输入命令', | |
| 93 | + }, | |
| 94 | + }, | |
| 95 | + { | |
| 96 | + field: CommandFieldsEnum.COMAND_VALUE, | |
| 97 | + label: '命令', | |
| 98 | + component: 'JSONEditor', | |
| 99 | + colProps: { span: 20 }, | |
| 100 | + changeEvent: 'update:value', | |
| 101 | + valueField: 'value', | |
| 102 | + required: true, | |
| 103 | + rules: JSONEditorValidator(), | |
| 104 | + ifShow: ({ model }) => | |
| 105 | + transportType !== TransportTypeEnum.TCP && | |
| 106 | + model[CommandFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM, | |
| 107 | + componentProps: { | |
| 108 | + height: 250, | |
| 109 | + }, | |
| 110 | + }, | |
| 111 | + { | |
| 112 | + field: CommandFieldsEnum.SERVICE, | |
| 113 | + label: '服务', | |
| 114 | + component: 'ApiSelect', | |
| 115 | + required: true, | |
| 116 | + ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] !== CommandTypeEnum.CUSTOM, | |
| 117 | + rules: [{ required: true, message: '请选择服务' }], | |
| 118 | + componentProps: ({ formActionType }) => { | |
| 119 | + const { setFieldsValue } = formActionType; | |
| 120 | + return { | |
| 121 | + api: getModelServices, | |
| 122 | + params: { | |
| 123 | + deviceProfileId, | |
| 124 | + }, | |
| 125 | + valueField: 'identifier', | |
| 126 | + labelField: 'functionName', | |
| 127 | + getPopupContainer: () => document.body, | |
| 128 | + placeholder: '请选择服务', | |
| 129 | + onChange( | |
| 130 | + value: string, | |
| 131 | + options: ModelOfMatterParams & Record<'label' | 'value', string> | |
| 132 | + ) { | |
| 133 | + if (!value) return; | |
| 134 | + setFieldsValue({ | |
| 135 | + [CommandFieldsEnum.CALL_TYPE]: | |
| 136 | + options.callType === ServiceCallTypeEnum.ASYNC | |
| 137 | + ? CommandDeliveryWayEnum.ONE_WAY | |
| 138 | + : CommandDeliveryWayEnum.TWO_WAY, | |
| 139 | + [CommandFieldsEnum.MODEL_INPUT]: null, | |
| 140 | + [CommandFieldsEnum.SERVICE_OBJECT_MODEL]: Object.assign(options, { | |
| 141 | + functionName: options.label, | |
| 142 | + identifier: options.value, | |
| 143 | + }), | |
| 144 | + }); | |
| 145 | + }, | |
| 146 | + }; | |
| 147 | + }, | |
| 148 | + }, | |
| 149 | + { | |
| 150 | + field: CommandFieldsEnum.SERVICE_OBJECT_MODEL, | |
| 151 | + label: '服务物模型', | |
| 152 | + component: 'Input', | |
| 153 | + ifShow: false, | |
| 154 | + }, | |
| 155 | + { | |
| 156 | + field: CommandFieldsEnum.MODEL_INPUT, | |
| 157 | + component: 'Input', | |
| 158 | + label: '输入参数', | |
| 159 | + changeEvent: 'update:value', | |
| 160 | + valueField: 'value', | |
| 161 | + ifShow: ({ model }) => | |
| 162 | + model[CommandFieldsEnum.SERVICE] && | |
| 163 | + model[CommandFieldsEnum.SERVICE_OBJECT_MODEL] && | |
| 164 | + model[CommandFieldsEnum.COMMAND_TYPE] !== CommandTypeEnum.CUSTOM, | |
| 165 | + componentProps: { | |
| 166 | + formProps: { | |
| 167 | + wrapperCol: { span: 24 }, | |
| 168 | + } as FormProps, | |
| 169 | + }, | |
| 170 | + slot: 'serviceCommand', | |
| 171 | + }, | |
| 172 | + ]; | |
| 173 | +}; | ... | ... |
| 1 | +export { default as CommandDeliveryModal } from './index.vue'; | ... | ... |
| 1 | +<template> | |
| 2 | + <BasicModal | |
| 3 | + title="命令下发" | |
| 4 | + :width="650" | |
| 5 | + @register="registerModal" | |
| 6 | + @ok="handleOk" | |
| 7 | + @cancel="handleCancel" | |
| 8 | + > | |
| 9 | + <BasicForm @register="registerForm"> | |
| 10 | + <template #serviceCommand="{ field, model }"> | |
| 11 | + <ThingsModelForm | |
| 12 | + :disabled="deviceDetail?.transportType === TransportTypeEnum.TCP" | |
| 13 | + ref="thingsModelFormRef" | |
| 14 | + v-model:value="model[field]" | |
| 15 | + :key="model[CommandFieldsEnum.SERVICE_OBJECT_MODEL]?.identifier" | |
| 16 | + :inputData="model[CommandFieldsEnum.SERVICE_OBJECT_MODEL]?.functionJson?.inputData" | |
| 17 | + :transportType="deviceDetail?.transportType" | |
| 18 | + /> | |
| 19 | + </template> | |
| 20 | + </BasicForm> | |
| 21 | + </BasicModal> | |
| 22 | +</template> | |
| 23 | +<script lang="ts" setup> | |
| 24 | + import { nextTick, ref, unref } from 'vue'; | |
| 25 | + import { BasicForm, ThingsModelForm, useForm } from '/@/components/Form'; | |
| 26 | + import { CommandDeliveryFormFieldType, CommandFieldsEnum, CommandSchemas } from './config'; | |
| 27 | + import { useMessage } from '/@/hooks/web/useMessage'; | |
| 28 | + import { DeviceRecord } from '/@/api/device/model/deviceModel'; | |
| 29 | + import { CommandDeliveryWayEnum } from '/@/enums/deviceEnum'; | |
| 30 | + import { CommandTypeEnum, RPCCommandMethodEnum, TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 31 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | |
| 32 | + import { getDeviceActiveTime } from '/@/api/alarm/position'; | |
| 33 | + import { RpcCommandType } from '/@/api/device/model/deviceConfigModel'; | |
| 34 | + import { parseStringToJSON } from '/@/components/CodeEditor'; | |
| 35 | + import { commandIssuanceApi } from '/@/api/device/deviceManager'; | |
| 36 | + | |
| 37 | + defineEmits(['register']); | |
| 38 | + | |
| 39 | + const thingsModelFormRef = ref<InstanceType<typeof ThingsModelForm>>(); | |
| 40 | + const deviceDetail = ref<DeviceRecord>(); | |
| 41 | + | |
| 42 | + const [registerModal, { setModalProps }] = useModalInner( | |
| 43 | + (params: ModalParamsType<DeviceRecord>) => { | |
| 44 | + const { record } = params; | |
| 45 | + deviceDetail.value = record; | |
| 46 | + setProps({ | |
| 47 | + schemas: CommandSchemas(record.transportType as TransportTypeEnum, record.deviceProfileId), | |
| 48 | + }); | |
| 49 | + } | |
| 50 | + ); | |
| 51 | + | |
| 52 | + const { createMessage } = useMessage(); | |
| 53 | + | |
| 54 | + const [registerForm, { setProps, getFieldsValue, validate, resetFields, clearValidate }] = | |
| 55 | + useForm({ | |
| 56 | + labelWidth: 120, | |
| 57 | + baseColProps: { span: 20 }, | |
| 58 | + labelAlign: 'right', | |
| 59 | + showSubmitButton: false, | |
| 60 | + showResetButton: false, | |
| 61 | + }); | |
| 62 | + | |
| 63 | + const handleCancel = async () => { | |
| 64 | + await resetFields(); | |
| 65 | + await nextTick(); | |
| 66 | + await clearValidate(); | |
| 67 | + }; | |
| 68 | + | |
| 69 | + const handleValidate = async () => { | |
| 70 | + await validate(); | |
| 71 | + await unref(thingsModelFormRef)?.validate?.(); | |
| 72 | + }; | |
| 73 | + | |
| 74 | + const handleValidateDeviceActive = async (): Promise<boolean> => { | |
| 75 | + const result = await getDeviceActiveTime(unref(deviceDetail)!.tbDeviceId); | |
| 76 | + const [firstItem] = result; | |
| 77 | + return !!firstItem.value; | |
| 78 | + }; | |
| 79 | + | |
| 80 | + const handleCommandParams = ( | |
| 81 | + values: CommandDeliveryFormFieldType, | |
| 82 | + serviceCommand: Recordable | |
| 83 | + ) => { | |
| 84 | + const { commandType, service } = values; | |
| 85 | + | |
| 86 | + const isTcpDevice = unref(deviceDetail)?.transportType === TransportTypeEnum.TCP; | |
| 87 | + if (commandType === CommandTypeEnum.CUSTOM) { | |
| 88 | + if (isTcpDevice) { | |
| 89 | + return values.tcpCommandValue; | |
| 90 | + } | |
| 91 | + return parseStringToJSON(values.commandValue!).json; | |
| 92 | + } else { | |
| 93 | + if (isTcpDevice) return Reflect.get(serviceCommand, CommandFieldsEnum.SERVICE_COMMAND); | |
| 94 | + return { | |
| 95 | + [service!]: serviceCommand, | |
| 96 | + }; | |
| 97 | + } | |
| 98 | + }; | |
| 99 | + | |
| 100 | + const handleOk = async () => { | |
| 101 | + await handleValidate(); | |
| 102 | + | |
| 103 | + try { | |
| 104 | + setModalProps({ loading: true, confirmLoading: true }); | |
| 105 | + | |
| 106 | + const values = getFieldsValue() as CommandDeliveryFormFieldType; | |
| 107 | + const { callType, commandType } = values; | |
| 108 | + const serviceCommand = unref(thingsModelFormRef)?.getFieldsValue() || {}; | |
| 109 | + | |
| 110 | + if (callType === CommandDeliveryWayEnum.TWO_WAY && !(await handleValidateDeviceActive())) { | |
| 111 | + createMessage.warn('当前设备不在线'); | |
| 112 | + return; | |
| 113 | + } | |
| 114 | + | |
| 115 | + const rpcCommands: RpcCommandType = { | |
| 116 | + additionalInfo: { | |
| 117 | + cmdType: | |
| 118 | + commandType === CommandTypeEnum.CUSTOM | |
| 119 | + ? CommandTypeEnum.CUSTOM | |
| 120 | + : CommandTypeEnum.SERVICE, | |
| 121 | + }, | |
| 122 | + method: RPCCommandMethodEnum.THINGSKIT, | |
| 123 | + persistent: true, | |
| 124 | + params: handleCommandParams(values, serviceCommand), | |
| 125 | + }; | |
| 126 | + | |
| 127 | + await commandIssuanceApi(callType, unref(deviceDetail)!.tbDeviceId, rpcCommands); | |
| 128 | + | |
| 129 | + createMessage.success('命令下发成功'); | |
| 130 | + } finally { | |
| 131 | + setModalProps({ loading: false, confirmLoading: false }); | |
| 132 | + } | |
| 133 | + }; | |
| 134 | +</script> | |
| 135 | +<style scoped lang="less"></style> | ... | ... |
src/views/device/list/cpns/tabs/CommandIssuance.vue
deleted
100644 → 0
| 1 | -<template> | |
| 2 | - <div class="tabs-detail"> | |
| 3 | - <div> | |
| 4 | - <BasicForm @register="registerForm" /> | |
| 5 | - <Space class="w-full justify-end py-2" justify="end"> | |
| 6 | - <Button :loading="loading" type="primary" @click="handleOk" class="mr-2">确定</Button> | |
| 7 | - <Button type="default" @click="handleCancel" class="mr-2">重置</Button> | |
| 8 | - </Space> | |
| 9 | - </div> | |
| 10 | - </div> | |
| 11 | -</template> | |
| 12 | -<script lang="ts"> | |
| 13 | - import { defineComponent, nextTick, ref } from 'vue'; | |
| 14 | - import { BasicForm, useForm } from '/@/components/Form'; | |
| 15 | - import { CommandFieldsEnum, CommandSchemas, CommandType, ValueType } from '../../config/data'; | |
| 16 | - import { commandIssuanceApi } from '/@/api/device/deviceManager'; | |
| 17 | - import { useMessage } from '/@/hooks/web/useMessage'; | |
| 18 | - import { Button } from '/@/components/Button'; | |
| 19 | - import { Space } from 'ant-design-vue'; | |
| 20 | - import { DeviceRecord } from '/@/api/device/model/deviceModel'; | |
| 21 | - import { TransportTypeEnum } from '../../../profiles/components/TransportDescript/const'; | |
| 22 | - import { parseStringToJSON } from '/@/components/CodeEditor/src/JSONEditor'; | |
| 23 | - import { CommandDeliveryWayEnum } from '/@/enums/toolEnum'; | |
| 24 | - | |
| 25 | - export default defineComponent({ | |
| 26 | - components: { BasicForm, Button, Space }, | |
| 27 | - props: { | |
| 28 | - deviceDetail: { | |
| 29 | - type: Object as PropType<DeviceRecord>, | |
| 30 | - required: true, | |
| 31 | - }, | |
| 32 | - }, | |
| 33 | - emits: ['register'], | |
| 34 | - setup(props) { | |
| 35 | - const { createMessage } = useMessage(); | |
| 36 | - const loading = ref(false); | |
| 37 | - | |
| 38 | - const [registerForm, { getFieldsValue, validate, resetFields, clearValidate }] = useForm({ | |
| 39 | - labelWidth: 120, | |
| 40 | - schemas: CommandSchemas( | |
| 41 | - props.deviceDetail.deviceProfile.transportType as TransportTypeEnum, | |
| 42 | - props.deviceDetail.deviceProfileId | |
| 43 | - ), | |
| 44 | - baseColProps: { span: 20 }, | |
| 45 | - labelAlign: 'right', | |
| 46 | - showSubmitButton: false, | |
| 47 | - showResetButton: false, | |
| 48 | - }); | |
| 49 | - | |
| 50 | - const handleCancel = async () => { | |
| 51 | - await resetFields(); | |
| 52 | - await nextTick(); | |
| 53 | - await clearValidate(); | |
| 54 | - }; | |
| 55 | - | |
| 56 | - const handleOk = async () => { | |
| 57 | - loading.value = true; | |
| 58 | - try { | |
| 59 | - // 验证 | |
| 60 | - const valid = await validate(); | |
| 61 | - if (!valid) return; | |
| 62 | - // 收集表单数据 | |
| 63 | - const field = getFieldsValue(); | |
| 64 | - let command: Recordable = { | |
| 65 | - persistent: true, | |
| 66 | - method: 'methodThingskit', | |
| 67 | - params: field[CommandFieldsEnum.COMMAND_TEXT], | |
| 68 | - }; | |
| 69 | - | |
| 70 | - if (field[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM) { | |
| 71 | - if (field[CommandFieldsEnum.VALUE_TYPE] === ValueType.JSON) { | |
| 72 | - const { json } = parseStringToJSON(field.commandValue); | |
| 73 | - command.params = json; | |
| 74 | - } | |
| 75 | - } else { | |
| 76 | - const { transportType } = props.deviceDetail.deviceProfile; | |
| 77 | - command.params = | |
| 78 | - transportType === TransportTypeEnum.TCP | |
| 79 | - ? field[CommandFieldsEnum.TCP_SERVICE] | |
| 80 | - : { | |
| 81 | - [field[CommandFieldsEnum.SERVICE]]: field[CommandFieldsEnum.MODEL_INPUT], | |
| 82 | - }; | |
| 83 | - command.additionalInfo = { cmdType: 1 }; | |
| 84 | - } | |
| 85 | - commandIssuanceApi( | |
| 86 | - field[CommandFieldsEnum.CUSTOM_TYPE] as CommandDeliveryWayEnum, | |
| 87 | - props.deviceDetail.tbDeviceId, | |
| 88 | - command | |
| 89 | - ) | |
| 90 | - .then((res) => { | |
| 91 | - if (!res) return; | |
| 92 | - createMessage.success('命令下发成功'); | |
| 93 | - loading.value = true; | |
| 94 | - // 请求 | |
| 95 | - handleCancel(); | |
| 96 | - }) | |
| 97 | - .catch((e) => { | |
| 98 | - if (e?.message) { | |
| 99 | - createMessage.error(e?.message); | |
| 100 | - } | |
| 101 | - handleCancel(); | |
| 102 | - }) | |
| 103 | - .finally(() => { | |
| 104 | - setTimeout(() => { | |
| 105 | - loading.value = false; | |
| 106 | - }, 300); | |
| 107 | - }); | |
| 108 | - } catch (e) { | |
| 109 | - throw e; | |
| 110 | - } finally { | |
| 111 | - loading.value = false; | |
| 112 | - } | |
| 113 | - }; | |
| 114 | - return { | |
| 115 | - registerForm, | |
| 116 | - handleCancel, | |
| 117 | - handleOk, | |
| 118 | - loading, | |
| 119 | - }; | |
| 120 | - }, | |
| 121 | - }); | |
| 122 | -</script> | |
| 123 | -<style scoped lang="less"> | |
| 124 | - .jsoneditor-transform { | |
| 125 | - background-position: -144px -96px; | |
| 126 | - display: none !important; | |
| 127 | - } | |
| 128 | - | |
| 129 | - .tabs-detail:deep(.object-model-validate-form) { | |
| 130 | - > .ant-row { | |
| 131 | - @apply w-full; | |
| 132 | - } | |
| 133 | - } | |
| 134 | -</style> |
| ... | ... | @@ -14,7 +14,7 @@ |
| 14 | 14 | import { ColEx } from '/@/components/Form/src/types'; |
| 15 | 15 | import { useHistoryData } from '../../hook/useHistoryData'; |
| 16 | 16 | import { formatToDateTime } from '/@/utils/dateUtil'; |
| 17 | - import { useTable, BasicTable, BasicColumn } from '/@/components/Table'; | |
| 17 | + import { useTable, BasicTable, BasicColumn, SorterResult } from '/@/components/Table'; | |
| 18 | 18 | import { |
| 19 | 19 | ModeSwitchButton, |
| 20 | 20 | TABLE_CHART_MODE_LIST, |
| ... | ... | @@ -64,7 +64,7 @@ |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | 66 | const sortOrder = ref<any>('descend'); |
| 67 | - const columns: BasicColumn[] | any = computed(() => { | |
| 67 | + const columns = computed<BasicColumn[]>(() => { | |
| 68 | 68 | return [ |
| 69 | 69 | { |
| 70 | 70 | title: '属性', |
| ... | ... | @@ -119,7 +119,7 @@ |
| 119 | 119 | loading.value = false; |
| 120 | 120 | }; |
| 121 | 121 | |
| 122 | - const handleTableChange = async (_pag, _filters, sorter: any) => { | |
| 122 | + const handleTableChange = async (_pag, _filters, sorter: SorterResult) => { | |
| 123 | 123 | sortOrder.value = sorter.order; |
| 124 | 124 | await setColumns(unref(columns)); |
| 125 | 125 | if (sorter.field == 'ts') { | ... | ... |
| ... | ... | @@ -18,7 +18,8 @@ |
| 18 | 18 | import { useGlobSetting } from '/@/hooks/setting'; |
| 19 | 19 | import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget'; |
| 20 | 20 | import { toRaw } from 'vue'; |
| 21 | - import { DataActionModeEnum, ReadAndWriteEnum } from '/@/enums/toolEnum'; | |
| 21 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | |
| 22 | + import { ReadAndWriteEnum } from '/@/enums/deviceEnum'; | |
| 22 | 23 | import { ObjectModelCommandDeliveryModal } from './ObjectModelCommandDeliveryModal'; |
| 23 | 24 | import { ModalParamsType } from '/#/utils'; |
| 24 | 25 | import { AreaChartOutlined } from '@ant-design/icons-vue'; | ... | ... |
| 1 | 1 | import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
| 2 | 2 | import { FormSchema } from '/@/components/Form'; |
| 3 | -import { validateTCPCustomCommand } from '/@/components/Form/src/externalCompns/components/ThingsModelForm'; | |
| 3 | +import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm'; | |
| 4 | 4 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 5 | 5 | |
| 6 | 6 | const InsertString = (t, c, n) => { | ... | ... |
| ... | ... | @@ -12,8 +12,8 @@ |
| 12 | 12 | import { genModbusCommand } from '/@/api/task'; |
| 13 | 13 | import { TaskTypeEnum } from '/@/views/task/center/config'; |
| 14 | 14 | import { SingleToHex, formSchemasConfig } from './config'; |
| 15 | - import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 16 | 15 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 16 | + import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 17 | 17 | |
| 18 | 18 | defineEmits(['register']); |
| 19 | 19 | const props = defineProps<{ deviceId: string; deviceName: string }>(); | ... | ... |
| ... | ... | @@ -97,7 +97,7 @@ |
| 97 | 97 | |
| 98 | 98 | const handleControl = (action: number, direction: string) => { |
| 99 | 99 | const organizationId = getId(); |
| 100 | - controlling({ cameralndexCode: organizationId, action, command: direction }); | |
| 100 | + controlling({ cameraIndexCode: organizationId, action, command: direction }); | |
| 101 | 101 | }; |
| 102 | 102 | |
| 103 | 103 | const isPlay = ref<Boolean | null | undefined>(false); | ... | ... |
| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | <template #toolbar> |
| 7 | 7 | <Space> |
| 8 | 8 | <Authority value="api:yt:device:rpc"> |
| 9 | - <Button type="primary" @click="openModal(true)">命令下发</Button> | |
| 9 | + <Button type="primary" @click="handleOpenModal">命令下发</Button> | |
| 10 | 10 | </Authority> |
| 11 | 11 | </Space> |
| 12 | 12 | </template> |
| ... | ... | @@ -24,16 +24,7 @@ |
| 24 | 24 | </template> |
| 25 | 25 | </BasicTable> |
| 26 | 26 | |
| 27 | - <BasicModal | |
| 28 | - @register="registerCommandIssuanceModal" | |
| 29 | - width="700px" | |
| 30 | - title="命令下发" | |
| 31 | - :showOkBtn="false" | |
| 32 | - cancelText="关闭" | |
| 33 | - :footer="null" | |
| 34 | - > | |
| 35 | - <CommandIssuance :deviceDetail="deviceDetail" /> | |
| 36 | - </BasicModal> | |
| 27 | + <CommandDeliveryModal @register="registerCommandDeliverModal" :deviceDetail="deviceDetail" /> | |
| 37 | 28 | </template> |
| 38 | 29 | <script lang="ts" setup> |
| 39 | 30 | import { h } from 'vue'; |
| ... | ... | @@ -43,9 +34,10 @@ |
| 43 | 34 | import { Button, Modal, Space } from 'ant-design-vue'; |
| 44 | 35 | import { JsonPreview } from '/@/components/CodeEditor'; |
| 45 | 36 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
| 46 | - import { BasicModal, useModal } from '/@/components/Modal'; | |
| 47 | - import CommandIssuance from '../CommandIssuance.vue'; | |
| 37 | + import { useModal } from '/@/components/Modal'; | |
| 38 | + import { CommandDeliveryModal } from '../CommandDeliveryModal'; | |
| 48 | 39 | import { Authority } from '/@/components/Authority'; |
| 40 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | |
| 49 | 41 | |
| 50 | 42 | const props = defineProps({ |
| 51 | 43 | fromId: { |
| ... | ... | @@ -58,7 +50,7 @@ |
| 58 | 50 | }, |
| 59 | 51 | }); |
| 60 | 52 | |
| 61 | - const [registerCommandIssuanceModal, { openModal }] = useModal(); | |
| 53 | + const [registerCommandDeliverModal, { openModal }] = useModal(); | |
| 62 | 54 | |
| 63 | 55 | const [registerTable] = useTable({ |
| 64 | 56 | api: deviceCommandRecordGetQuery, |
| ... | ... | @@ -79,6 +71,7 @@ |
| 79 | 71 | }, |
| 80 | 72 | useSearchForm: true, |
| 81 | 73 | }); |
| 74 | + | |
| 82 | 75 | const commonModalInfo = (title, value) => { |
| 83 | 76 | Modal.info({ |
| 84 | 77 | title, |
| ... | ... | @@ -86,14 +79,23 @@ |
| 86 | 79 | content: h(JsonPreview, { data: value }), |
| 87 | 80 | }); |
| 88 | 81 | }; |
| 82 | + | |
| 89 | 83 | const handleRecordContent = (record) => { |
| 90 | 84 | if (!record?.request?.body) return; |
| 91 | 85 | if (Object.prototype.toString.call(record?.request?.body) !== '[object Object]') return; |
| 92 | 86 | const jsonParams = record?.request?.body?.params; |
| 93 | 87 | commonModalInfo('命令下发内容', jsonParams); |
| 94 | 88 | }; |
| 89 | + | |
| 95 | 90 | const handleRecordResponseContent = (record) => { |
| 96 | 91 | const jsonParams = record?.response; |
| 97 | 92 | commonModalInfo('响应内容', jsonParams); |
| 98 | 93 | }; |
| 94 | + | |
| 95 | + function handleOpenModal() { | |
| 96 | + openModal(true, { | |
| 97 | + mode: DataActionModeEnum.READ, | |
| 98 | + record: props.deviceDetail, | |
| 99 | + } as ModalParamsType<DeviceRecord>); | |
| 100 | + } | |
| 99 | 101 | </script> | ... | ... |
| 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 = (params: Partial<FormSchema> = {}): 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 | + ...params, | |
| 71 | + }; | |
| 72 | +}; | |
| 73 | + | |
| 74 | +export const createIdentifierFormItem = (params: Partial<FormSchema> = {}): FormSchema => { | |
| 75 | + return { | |
| 76 | + field: FormFieldsEnum.IDENTIFIER, | |
| 77 | + label: FormFieldsNameEnum.IDENTIFIER, | |
| 78 | + required: true, | |
| 79 | + component: 'Input', | |
| 80 | + helpMessage: '支持大小写字母、数字和下划线.', | |
| 81 | + componentProps: { | |
| 82 | + maxLength: 128, | |
| 83 | + placeholder: '请输入标识符', | |
| 84 | + }, | |
| 85 | + rules: [ | |
| 86 | + { | |
| 87 | + required: true, | |
| 88 | + validator: validateIdentifier, | |
| 89 | + }, | |
| 90 | + ], | |
| 91 | + ...params, | |
| 92 | + }; | |
| 93 | +}; | |
| 94 | + | |
| 95 | +export const getFormSchemas = (dataType: DataTypeEnum[], showRemark: boolean): FormSchema[] => { | |
| 96 | + const { getTransportType } = useObjectModelFormContext(); | |
| 97 | + return [ | |
| 98 | + createFunctionNameFormItem(), | |
| 99 | + createIdentifierFormItem(), | |
| 100 | + { | |
| 101 | + field: FormFieldsEnum.DATA_TYPE, | |
| 102 | + label: FormFieldsNameEnum.DATA_TYPE, | |
| 103 | + required: true, | |
| 104 | + component: 'ApiSelect', | |
| 105 | + defaultValue: DataTypeEnum.NUMBER_INT, | |
| 106 | + ifShow: () => !!dataType.length, | |
| 107 | + componentProps: () => { | |
| 108 | + return { | |
| 109 | + placeholder: '请选择数据类型', | |
| 110 | + api: async (params: Recordable) => { | |
| 111 | + let result = await findDictItemByCode(params); | |
| 112 | + if (unref(getTransportType) === TransportTypeEnum.TCP) { | |
| 113 | + result = result.filter( | |
| 114 | + (item) => | |
| 115 | + ![DataTypeEnum.STRUCT, DataTypeEnum.ENUM].includes(item.itemValue as DataTypeEnum) | |
| 116 | + ); | |
| 117 | + } | |
| 118 | + return result.filter((item) => dataType.includes(item.itemValue as DataTypeEnum)); | |
| 119 | + }, | |
| 120 | + params: { | |
| 121 | + dictCode: DictEnum.DATA_TYPE, | |
| 122 | + }, | |
| 123 | + allowClear: false, | |
| 124 | + labelField: 'itemText', | |
| 125 | + valueField: 'itemValue', | |
| 126 | + getPopupContainer: () => document.body, | |
| 127 | + }; | |
| 128 | + }, | |
| 129 | + }, | |
| 130 | + { | |
| 131 | + field: FormFieldsEnum.VALUE_RANGE, | |
| 132 | + label: FormFieldsNameEnum.VALUE_RANGE, | |
| 133 | + component: 'CustomMinMaxInput', | |
| 134 | + valueField: 'value', | |
| 135 | + changeEvent: 'update:value', | |
| 136 | + ifShow: ({ model }) => | |
| 137 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 138 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 139 | + ) && | |
| 140 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | |
| 141 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | |
| 142 | + dynamicRules: ({ model }) => [ | |
| 143 | + { | |
| 144 | + required: | |
| 145 | + model[FormFieldsEnum.VALUE_RANGE]?.min || model[FormFieldsEnum.VALUE_RANGE]?.max, | |
| 146 | + validator: validateValueRange, | |
| 147 | + }, | |
| 148 | + ], | |
| 149 | + }, | |
| 150 | + { | |
| 151 | + field: FormFieldsEnum.STEP, | |
| 152 | + label: FormFieldsNameEnum.STEP, | |
| 153 | + component: 'InputNumber', | |
| 154 | + componentProps: { | |
| 155 | + maxLength: 255, | |
| 156 | + placeholder: '请输入步长', | |
| 157 | + min: 1, | |
| 158 | + formatter: (value: number | string) => { | |
| 159 | + return value ? Math.floor(Number(value)) : value; | |
| 160 | + }, | |
| 161 | + }, | |
| 162 | + ifShow: ({ model }) => | |
| 163 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 164 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 165 | + ) && | |
| 166 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | |
| 167 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | |
| 168 | + dynamicRules: ({ model }) => { | |
| 169 | + const valueRange = model[FormFieldsEnum.VALUE_RANGE] || {}; | |
| 170 | + const { min, max } = valueRange; | |
| 171 | + const step = model[FormFieldsEnum.STEP]; | |
| 172 | + return [ | |
| 173 | + { | |
| 174 | + validator: () => { | |
| 175 | + if ([min, max].every(isNullOrUnDef)) return Promise.resolve(); | |
| 176 | + if (step > max - min) { | |
| 177 | + return Promise.reject('步长不能大于取值范围的差值'); | |
| 178 | + } | |
| 179 | + return Promise.resolve(); | |
| 180 | + }, | |
| 181 | + }, | |
| 182 | + ]; | |
| 183 | + }, | |
| 184 | + }, | |
| 185 | + { | |
| 186 | + field: FormFieldsEnum.UNIT_NAME, | |
| 187 | + label: FormFieldsNameEnum.UNIT_NAME, | |
| 188 | + component: 'Input', | |
| 189 | + show: false, | |
| 190 | + }, | |
| 191 | + { | |
| 192 | + field: FormFieldsEnum.UNIT, | |
| 193 | + label: FormFieldsNameEnum.UNIT, | |
| 194 | + component: 'ApiSelect', | |
| 195 | + ifShow: ({ model }) => | |
| 196 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 197 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 198 | + ) && | |
| 199 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | |
| 200 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | |
| 201 | + componentProps: ({ formActionType }) => { | |
| 202 | + const { setFieldsValue } = formActionType; | |
| 203 | + return { | |
| 204 | + placeholder: '请选择单位', | |
| 205 | + api: async (params: Recordable) => { | |
| 206 | + const list = await findDictItemByCode(params); | |
| 207 | + list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`)); | |
| 208 | + return list; | |
| 209 | + }, | |
| 210 | + params: { | |
| 211 | + dictCode: DictEnum.ATTRIBUTE_UNIT, | |
| 212 | + }, | |
| 213 | + labelField: 'itemText', | |
| 214 | + valueField: 'itemValue', | |
| 215 | + onChange(_, record: Record<'label' | 'value', string>) { | |
| 216 | + if (record) { | |
| 217 | + const { label } = record; | |
| 218 | + setFieldsValue({ [FormFieldsEnum.UNIT_NAME]: label }); | |
| 219 | + } | |
| 220 | + }, | |
| 221 | + getPopupContainer: () => document.body, | |
| 222 | + showSearch: true, | |
| 223 | + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | |
| 224 | + let { label, value } = option; | |
| 225 | + label = label.toLowerCase(); | |
| 226 | + value = value.toLowerCase(); | |
| 227 | + inputValue = inputValue.toLowerCase(); | |
| 228 | + return label.includes(inputValue) || value.includes(inputValue); | |
| 229 | + }, | |
| 230 | + }; | |
| 231 | + }, | |
| 232 | + }, | |
| 233 | + { | |
| 234 | + field: FormFieldsEnum.BOOL_CLOSE, | |
| 235 | + component: 'Input', | |
| 236 | + required: true, | |
| 237 | + label: FormFieldsNameEnum.BOOL_CLOSE, | |
| 238 | + componentProps: { | |
| 239 | + placeholder: '如:关', | |
| 240 | + }, | |
| 241 | + defaultValue: '关', | |
| 242 | + ifShow: ({ model }) => | |
| 243 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 244 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 245 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | |
| 246 | + dynamicRules: ({ model }) => { | |
| 247 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | |
| 248 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | |
| 249 | + return [ | |
| 250 | + { | |
| 251 | + required: true, | |
| 252 | + message: `布尔值不能为空`, | |
| 253 | + }, | |
| 254 | + { | |
| 255 | + validator() { | |
| 256 | + if (open === close) return Promise.reject('布尔值不能相同'); | |
| 257 | + return Promise.resolve(); | |
| 258 | + }, | |
| 259 | + }, | |
| 260 | + ]; | |
| 261 | + }, | |
| 262 | + }, | |
| 263 | + { | |
| 264 | + field: FormFieldsEnum.BOOL_OPEN, | |
| 265 | + component: 'Input', | |
| 266 | + required: true, | |
| 267 | + label: FormFieldsNameEnum.BOOL_OPEN, | |
| 268 | + componentProps: { | |
| 269 | + placeholder: '如:开', | |
| 270 | + }, | |
| 271 | + defaultValue: '开', | |
| 272 | + ifShow: ({ model }) => | |
| 273 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 274 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 275 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | |
| 276 | + dynamicRules: ({ model }) => { | |
| 277 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | |
| 278 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | |
| 279 | + return [ | |
| 280 | + { | |
| 281 | + required: true, | |
| 282 | + message: `布尔值不能为空`, | |
| 283 | + }, | |
| 284 | + { | |
| 285 | + validator() { | |
| 286 | + if (open === close) return Promise.reject('布尔值不能相同'); | |
| 287 | + return Promise.resolve(); | |
| 288 | + }, | |
| 289 | + }, | |
| 290 | + ]; | |
| 291 | + }, | |
| 292 | + }, | |
| 293 | + { | |
| 294 | + field: FormFieldsEnum.LENGTH, | |
| 295 | + component: 'InputNumber', | |
| 296 | + required: true, | |
| 297 | + label: FormFieldsNameEnum.LENGTH, | |
| 298 | + defaultValue: 1024, | |
| 299 | + componentProps: { | |
| 300 | + placeholder: '请输入数据长度', | |
| 301 | + }, | |
| 302 | + ifShow: ({ model }) => | |
| 303 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 304 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 305 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRING, | |
| 306 | + }, | |
| 307 | + { | |
| 308 | + field: FormFieldsEnum.ENUMS_DATA, | |
| 309 | + label: FormFieldsNameEnum.ENUMS_DATA, | |
| 310 | + component: 'Input', | |
| 311 | + slot: FormFieldsEnum.ENUMS_DATA, | |
| 312 | + changeEvent: 'update:value', | |
| 313 | + valueField: 'value', | |
| 314 | + ifShow: ({ model }) => | |
| 315 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 316 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 317 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.ENUM, | |
| 318 | + }, | |
| 319 | + { | |
| 320 | + field: FormFieldsEnum.STRUCT_DATA, | |
| 321 | + label: FormFieldsNameEnum.STRUCT_DATA, | |
| 322 | + component: 'Input', | |
| 323 | + slot: FormFieldsEnum.STRUCT_DATA, | |
| 324 | + changeEvent: 'update:value', | |
| 325 | + valueField: 'value', | |
| 326 | + required: true, | |
| 327 | + ifShow: ({ model }) => | |
| 328 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRUCT && | |
| 329 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 330 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 331 | + ), | |
| 332 | + }, | |
| 333 | + | |
| 334 | + { | |
| 335 | + field: FormFieldsEnum.REMARK, | |
| 336 | + label: FormFieldsNameEnum.REMARK, | |
| 337 | + component: 'InputTextArea', | |
| 338 | + ifShow: showRemark, | |
| 339 | + componentProps: { | |
| 340 | + rows: 4, | |
| 341 | + maxLength: 100, | |
| 342 | + placeholder: '请输入描述', | |
| 343 | + }, | |
| 344 | + }, | |
| 345 | + ]; | |
| 346 | +}; | ... | ... |
| 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/externalCompns/components/StructForm/EnumList.config.ts
| 1 | -import { FormSchema } from '/@/components/Table'; | |
| 1 | +import { FormSchema } from '/@/components/Form'; | |
| 2 | 2 | |
| 3 | 3 | export enum FormFieldsEnum { |
| 4 | 4 | VALUE = 'value', |
| ... | ... | @@ -41,14 +41,7 @@ export const getFormSchemas = (): FormSchema[] => { |
| 41 | 41 | field: FormFieldsEnum.NAME, |
| 42 | 42 | label: '', |
| 43 | 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 | 45 | componentProps: () => { |
| 53 | 46 | return { |
| 54 | 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/externalCompns/components/StructForm/EnumList.vue
| 1 | 1 | <script setup lang="ts"> |
| 2 | 2 | import { Button, Tooltip } from 'ant-design-vue'; |
| 3 | 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 | 5 | import { Specs } from '/@/api/device/model/modelOfMatterModel'; |
| 6 | 6 | import { Icon } from '/@/components/Icon'; |
| 7 | - import { getFormSchemas } from './EnumList.config'; | |
| 8 | - import { FormActionType } from '../../../types/form'; | |
| 7 | + import { getFormSchemas } from './config'; | |
| 9 | 8 | import { buildUUID } from '/@/utils/uuid'; |
| 10 | 9 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 11 | 10 | import { isNullOrUnDef } from '/@/utils/is'; |
| ... | ... | @@ -65,7 +64,6 @@ |
| 65 | 64 | |
| 66 | 65 | const setFieldsValue = (spaceList: Specs[]) => { |
| 67 | 66 | enumsListElRef.value = spaceList.map((item) => ({ uuid: buildUUID(), dataSource: item })); |
| 68 | - | |
| 69 | 67 | nextTick(() => { |
| 70 | 68 | unref(enumsListElRef).forEach((item) => |
| 71 | 69 | item.formActionType?.setFieldsValue?.(item.dataSource) |
| ... | ... | @@ -76,10 +74,7 @@ |
| 76 | 74 | const handleDeleteEnums = (item: EnumElItemType) => { |
| 77 | 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 | 80 | const handleAddEnums = () => { |
| ... | ... | @@ -107,15 +102,17 @@ |
| 107 | 102 | <section class="w-full"> |
| 108 | 103 | <header class="flex h-8 items-center"> |
| 109 | 104 | <div class="w-1/2"> |
| 110 | - <span>参考值</span> | |
| 105 | + <span class="mr-1 text-red-400">*</span> | |
| 106 | + <span> 参考值 </span> | |
| 111 | 107 | <Tooltip title="支持整型,取值范围:-2147483648 ~ 2147483647"> |
| 112 | 108 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> |
| 113 | 109 | </Tooltip> |
| 114 | 110 | </div> |
| 115 | 111 | <div class="w-1/2"> |
| 116 | - <span>参考描述</span> | |
| 112 | + <span class="mr-1 text-red-400">*</span> | |
| 113 | + <span> 参考描述 </span> | |
| 117 | 114 | <Tooltip |
| 118 | - title="支持中文、英文大小写、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符" | |
| 115 | + title="支持中文、英文大小写、日文、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符" | |
| 119 | 116 | > |
| 120 | 117 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> |
| 121 | 118 | </Tooltip> | ... | ... |
src/views/device/profiles/components/ObjectModelForm/ExtendDesc/config.ts
renamed from
src/components/Form/src/externalCompns/components/ExtendDesc/config.ts
| 1 | +import { unref } from 'vue'; | |
| 2 | +import { useObjectModelFormContext } from '../useObjectModelFormContext'; | |
| 1 | 3 | import { findDictItemByCode } from '/@/api/system/dict'; |
| 2 | 4 | import { FormSchema } from '/@/components/Table'; |
| 3 | 5 | import { DictEnum } from '/@/enums/dictEnum'; |
| ... | ... | @@ -37,66 +39,62 @@ function getActionTypeByObjectModelType(dataType: DataTypeEnum) { |
| 37 | 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 | 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/externalCompns/components/ExtendDesc/index.vue
| ... | ... | @@ -4,19 +4,18 @@ |
| 4 | 4 | import { BasicForm, useForm } from '/@/components/Form'; |
| 5 | 5 | import { BasicModal } from '/@/components/Modal'; |
| 6 | 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 | 10 | const show = ref(false); |
| 11 | 11 | |
| 12 | 12 | const props = withDefaults( |
| 13 | 13 | defineProps<{ |
| 14 | - value?: object; | |
| 14 | + value?: ExtensionDesc; | |
| 15 | 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 | 23 | |
| 25 | 24 | const [registerForm, { setFieldsValue, getFieldsValue, setProps, validate, resetFields }] = |
| 26 | 25 | useForm({ |
| 27 | - schemas: formSchemas, | |
| 26 | + schemas: getFormSchemas(), | |
| 28 | 27 | showActionButtonGroup: false, |
| 29 | 28 | }); |
| 30 | 29 | |
| ... | ... | @@ -39,7 +38,6 @@ |
| 39 | 38 | const handleSubmit = async () => { |
| 40 | 39 | await validate(); |
| 41 | 40 | const value = getFieldsValue(); |
| 42 | - Reflect.deleteProperty(value, FormFieldsEnum.OBJECT_MODEL_TYPE); | |
| 43 | 41 | emit('update:value', value); |
| 44 | 42 | show.value = false; |
| 45 | 43 | }; |
| ... | ... | @@ -47,22 +45,15 @@ |
| 47 | 45 | watch(show, async (value) => { |
| 48 | 46 | if (value) { |
| 49 | 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 | 51 | </script> |
| 61 | 52 | |
| 62 | 53 | <template> |
| 63 | 54 | <section> |
| 64 | 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 | 57 | <BasicForm class="extension-form" @register="registerForm" /> |
| 67 | 58 | </BasicModal> |
| 68 | 59 | </section> | ... | ... |
src/views/device/profiles/components/ObjectModelForm/StructFormItem/CreateStructModal.vue
0 → 100644
| 1 | +<script setup lang="ts"> | |
| 2 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | |
| 3 | + import { ref, unref } from 'vue'; | |
| 4 | + import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | |
| 5 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | |
| 6 | + import { DataTypeForm } from '../DataTypeForm'; | |
| 7 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 8 | + import { useMessage } from '/@/hooks/web/useMessage'; | |
| 9 | + | |
| 10 | + const props = withDefaults( | |
| 11 | + defineProps<{ value?: StructJSON[]; dataType?: DataTypeEnum[]; mode?: DataActionModeEnum }>(), | |
| 12 | + { | |
| 13 | + value: () => [], | |
| 14 | + dataType: () => Object.values(DataTypeEnum), | |
| 15 | + } | |
| 16 | + ); | |
| 17 | + | |
| 18 | + const dataTypeFormRef = ref<InstanceType<typeof DataTypeForm>>(); | |
| 19 | + | |
| 20 | + const emits = defineEmits(['register', 'complete']); | |
| 21 | + | |
| 22 | + const currentMode = ref(DataActionModeEnum.CREATE); | |
| 23 | + const [registerModal] = useModalInner(({ mode, record }: ModalParamsType<StructJSON>) => { | |
| 24 | + unref(dataTypeFormRef)?.resetFieldsValue?.(); | |
| 25 | + currentMode.value = mode; | |
| 26 | + if (mode === DataActionModeEnum.UPDATE) { | |
| 27 | + unref(dataTypeFormRef)?.setFieldsValue?.(record); | |
| 28 | + } | |
| 29 | + }); | |
| 30 | + | |
| 31 | + const { createMessage } = useMessage(); | |
| 32 | + | |
| 33 | + const handleValidateHasSameIdentifier = (formValue?: StructJSON) => { | |
| 34 | + const { identifier } = formValue || {}; | |
| 35 | + if ( | |
| 36 | + unref(currentMode) === DataActionModeEnum.CREATE && | |
| 37 | + props.value.filter((item) => item.identifier === identifier).length >= 1 | |
| 38 | + ) { | |
| 39 | + const message = '存在一致的标识符'; | |
| 40 | + createMessage.warn(message); | |
| 41 | + return Promise.reject(message); | |
| 42 | + } | |
| 43 | + | |
| 44 | + return Promise.resolve(); | |
| 45 | + }; | |
| 46 | + | |
| 47 | + const handleOk = async () => { | |
| 48 | + await unref(dataTypeFormRef)?.validate?.(); | |
| 49 | + const value = unref(dataTypeFormRef)?.getFieldsValue?.(); | |
| 50 | + await handleValidateHasSameIdentifier(value); | |
| 51 | + emits('complete', value); | |
| 52 | + }; | |
| 53 | +</script> | |
| 54 | + | |
| 55 | +<template> | |
| 56 | + <BasicModal | |
| 57 | + @register="registerModal" | |
| 58 | + title="新增参数" | |
| 59 | + @ok="handleOk" | |
| 60 | + :width="480" | |
| 61 | + :show-ok-btn="mode !== DataActionModeEnum.READ" | |
| 62 | + > | |
| 63 | + <DataTypeForm ref="dataTypeFormRef" :data-type="dataType" show-remark :mode="mode" /> | |
| 64 | + </BasicModal> | |
| 65 | +</template> | ... | ... |
| 1 | +import { FormFieldsEnum, FormFieldsNameEnum } from '../config'; | |
| 2 | +import { findDictItemByCode } from '/@/api/system/dict'; | |
| 3 | +import { FormSchema } from '/@/components/Form'; | |
| 4 | +import { DictEnum } from '/@/enums/dictEnum'; | |
| 5 | +import { DataTypeEnum, FunctionTypeEnum, FunctionTypeNameEnum } from '/@/enums/objectModelEnum'; | |
| 6 | +import { isNullOrUnDef } from '/@/utils/is'; | |
| 7 | + | |
| 8 | +export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => { | |
| 9 | + value = value || {}; | |
| 10 | + const { min, max } = value; | |
| 11 | + if (min > max) { | |
| 12 | + return Promise.reject('最大值小于最小值'); | |
| 13 | + } | |
| 14 | + return Promise.resolve(); | |
| 15 | +}; | |
| 16 | + | |
| 17 | +export const getFormSchemas = (): FormSchema[] => { | |
| 18 | + return [ | |
| 19 | + { | |
| 20 | + field: FormFieldsEnum.FUNCTION_TYPE, | |
| 21 | + label: FormFieldsNameEnum.FUNCTION_TYPE, | |
| 22 | + component: 'Segmented', | |
| 23 | + defaultValue: FunctionTypeEnum.PROPERTIES, | |
| 24 | + required: true, | |
| 25 | + componentProps: ({ formActionType }) => { | |
| 26 | + return { | |
| 27 | + options: Object.keys(FunctionTypeEnum).map((key) => ({ | |
| 28 | + title: FunctionTypeNameEnum[key], | |
| 29 | + value: FunctionTypeEnum[key], | |
| 30 | + })), | |
| 31 | + onChange() { | |
| 32 | + const { setFieldsValue, clearValidate } = formActionType; | |
| 33 | + setFieldsValue({ | |
| 34 | + [FormFieldsEnum.INPUT_DATA]: [], | |
| 35 | + [FormFieldsEnum.OUTPUT_DATA]: [], | |
| 36 | + [FormFieldsEnum.STRUCT_DATA]: [], | |
| 37 | + [FormFieldsEnum.SERVICE_COMMAND]: null, | |
| 38 | + }); | |
| 39 | + clearValidate(); | |
| 40 | + }, | |
| 41 | + }; | |
| 42 | + }, | |
| 43 | + }, | |
| 44 | + { | |
| 45 | + field: FormFieldsEnum.FUNCTION_NAME, | |
| 46 | + label: FormFieldsNameEnum.FUNCTION_NAME, | |
| 47 | + component: 'Input', | |
| 48 | + required: true, | |
| 49 | + dynamicRules: ({ values }) => { | |
| 50 | + return [ | |
| 51 | + { required: true, message: '请输入功能名称' }, | |
| 52 | + { | |
| 53 | + validator: () => { | |
| 54 | + const reg = /[,,]+/; | |
| 55 | + if (reg.test(values?.[FormFieldsEnum.FUNCTION_NAME])) { | |
| 56 | + return Promise.reject(`${FormFieldsNameEnum.FUNCTION_NAME}不能包含逗号`); | |
| 57 | + } | |
| 58 | + return Promise.resolve(); | |
| 59 | + }, | |
| 60 | + }, | |
| 61 | + ]; | |
| 62 | + }, | |
| 63 | + componentProps: { | |
| 64 | + placeholder: `请输入${FormFieldsNameEnum.FUNCTION_NAME}`, | |
| 65 | + }, | |
| 66 | + }, | |
| 67 | + { | |
| 68 | + field: FormFieldsEnum.IDENTIFIER, | |
| 69 | + label: FormFieldsNameEnum.IDENTIFIER, | |
| 70 | + required: true, | |
| 71 | + component: 'Input', | |
| 72 | + componentProps: { | |
| 73 | + maxLength: 128, | |
| 74 | + placeholder: '请输入标识符', | |
| 75 | + }, | |
| 76 | + dynamicRules: ({ values }) => { | |
| 77 | + return [ | |
| 78 | + { required: true, message: '请输入标识符' }, | |
| 79 | + { | |
| 80 | + validator: () => { | |
| 81 | + const reg = /[,,]+/; | |
| 82 | + if (reg.test(values?.[FormFieldsEnum.IDENTIFIER])) { | |
| 83 | + return Promise.reject(`${FormFieldsNameEnum.IDENTIFIER}不能包含逗号`); | |
| 84 | + } | |
| 85 | + return Promise.resolve(); | |
| 86 | + }, | |
| 87 | + }, | |
| 88 | + ]; | |
| 89 | + }, | |
| 90 | + }, | |
| 91 | + { | |
| 92 | + field: FormFieldsEnum.DATA_TYPE, | |
| 93 | + label: FormFieldsNameEnum.DATA_TYPE, | |
| 94 | + required: true, | |
| 95 | + component: 'ApiSelect', | |
| 96 | + defaultValue: DataTypeEnum.NUMBER_INT, | |
| 97 | + ifShow: ({ model }) => | |
| 98 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 99 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 100 | + ), | |
| 101 | + componentProps: () => { | |
| 102 | + return { | |
| 103 | + placeholder: '请选择数据类型', | |
| 104 | + api: async (params: Recordable) => { | |
| 105 | + const result = await findDictItemByCode(params); | |
| 106 | + return result; | |
| 107 | + }, | |
| 108 | + params: { | |
| 109 | + dictCode: DictEnum.DATA_TYPE, | |
| 110 | + }, | |
| 111 | + labelField: 'itemText', | |
| 112 | + valueField: 'itemValue', | |
| 113 | + getPopupContainer: () => document.body, | |
| 114 | + }; | |
| 115 | + }, | |
| 116 | + }, | |
| 117 | + { | |
| 118 | + field: FormFieldsEnum.VALUE_RANGE, | |
| 119 | + label: FormFieldsNameEnum.VALUE_RANGE, | |
| 120 | + component: 'CustomMinMaxInput', | |
| 121 | + valueField: 'value', | |
| 122 | + changeEvent: 'update:value', | |
| 123 | + ifShow: ({ model }) => | |
| 124 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 125 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 126 | + ) && | |
| 127 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | |
| 128 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | |
| 129 | + rules: [{ validator: validateValueRange }], | |
| 130 | + }, | |
| 131 | + { | |
| 132 | + field: FormFieldsEnum.STEP, | |
| 133 | + label: FormFieldsNameEnum.STEP, | |
| 134 | + component: 'InputNumber', | |
| 135 | + componentProps: { | |
| 136 | + maxLength: 255, | |
| 137 | + placeholder: '请输入步长', | |
| 138 | + min: 1, | |
| 139 | + formatter: (value: number | string) => { | |
| 140 | + return value ? Math.floor(Number(value)) : value; | |
| 141 | + }, | |
| 142 | + }, | |
| 143 | + ifShow: ({ model }) => | |
| 144 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 145 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 146 | + ) && | |
| 147 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | |
| 148 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | |
| 149 | + dynamicRules: ({ model }) => { | |
| 150 | + const valueRange = model[FormFieldsEnum.VALUE_RANGE] || {}; | |
| 151 | + const { min, max } = valueRange; | |
| 152 | + const step = model[FormFieldsEnum.STEP]; | |
| 153 | + return [ | |
| 154 | + { | |
| 155 | + validator: () => { | |
| 156 | + if ([min, max].every(isNullOrUnDef)) return Promise.resolve(); | |
| 157 | + if (step > max - min) { | |
| 158 | + return Promise.reject('步长不能大于取值范围的差值'); | |
| 159 | + } | |
| 160 | + return Promise.resolve(); | |
| 161 | + }, | |
| 162 | + }, | |
| 163 | + ]; | |
| 164 | + }, | |
| 165 | + }, | |
| 166 | + { | |
| 167 | + field: FormFieldsEnum.UNIT_NAME, | |
| 168 | + label: FormFieldsNameEnum.UNIT_NAME, | |
| 169 | + component: 'Input', | |
| 170 | + show: false, | |
| 171 | + }, | |
| 172 | + { | |
| 173 | + field: FormFieldsEnum.UNIT, | |
| 174 | + label: FormFieldsNameEnum.UNIT, | |
| 175 | + component: 'ApiSelect', | |
| 176 | + componentProps: ({ formActionType }) => { | |
| 177 | + const { setFieldsValue } = formActionType; | |
| 178 | + return { | |
| 179 | + placeholder: '请选择单位', | |
| 180 | + api: async (params: Recordable) => { | |
| 181 | + const list = await findDictItemByCode(params); | |
| 182 | + list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`)); | |
| 183 | + return list; | |
| 184 | + }, | |
| 185 | + params: { | |
| 186 | + dictCode: DictEnum.ATTRIBUTE_UNIT, | |
| 187 | + }, | |
| 188 | + labelInValue: true, | |
| 189 | + labelField: 'itemText', | |
| 190 | + valueField: 'itemValue', | |
| 191 | + onChange(_, record: Record<'label' | 'value', string>) { | |
| 192 | + if (record) { | |
| 193 | + const { label } = record; | |
| 194 | + setFieldsValue({ [FormFieldsEnum.UNIT_NAME]: label }); | |
| 195 | + } | |
| 196 | + }, | |
| 197 | + getPopupContainer: () => document.body, | |
| 198 | + showSearch: true, | |
| 199 | + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | |
| 200 | + let { label, value } = option; | |
| 201 | + label = label.toLowerCase(); | |
| 202 | + value = value.toLowerCase(); | |
| 203 | + inputValue = inputValue.toLowerCase(); | |
| 204 | + return label.includes(inputValue) || value.includes(inputValue); | |
| 205 | + }, | |
| 206 | + }; | |
| 207 | + }, | |
| 208 | + ifShow: ({ model }) => | |
| 209 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 210 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 211 | + ) && | |
| 212 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | |
| 213 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | |
| 214 | + }, | |
| 215 | + { | |
| 216 | + field: FormFieldsEnum.BOOL_CLOSE, | |
| 217 | + component: 'Input', | |
| 218 | + required: true, | |
| 219 | + label: FormFieldsNameEnum.BOOL_CLOSE, | |
| 220 | + componentProps: { | |
| 221 | + placeholder: '如:关', | |
| 222 | + }, | |
| 223 | + defaultValue: '关', | |
| 224 | + ifShow: ({ model }) => | |
| 225 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 226 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 227 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | |
| 228 | + dynamicRules: ({ model }) => { | |
| 229 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | |
| 230 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | |
| 231 | + return [ | |
| 232 | + { | |
| 233 | + required: true, | |
| 234 | + message: `布尔值不能为空`, | |
| 235 | + }, | |
| 236 | + { | |
| 237 | + validator() { | |
| 238 | + if (open === close) return Promise.reject('布尔值不能相同'); | |
| 239 | + return Promise.resolve(); | |
| 240 | + }, | |
| 241 | + }, | |
| 242 | + ]; | |
| 243 | + }, | |
| 244 | + }, | |
| 245 | + { | |
| 246 | + field: FormFieldsEnum.BOOL_OPEN, | |
| 247 | + component: 'Input', | |
| 248 | + required: true, | |
| 249 | + label: FormFieldsNameEnum.BOOL_OPEN, | |
| 250 | + componentProps: { | |
| 251 | + placeholder: '如:开', | |
| 252 | + }, | |
| 253 | + defaultValue: '开', | |
| 254 | + ifShow: ({ model }) => | |
| 255 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 256 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 257 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | |
| 258 | + dynamicRules: ({ model }) => { | |
| 259 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | |
| 260 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | |
| 261 | + return [ | |
| 262 | + { | |
| 263 | + required: true, | |
| 264 | + message: `布尔值不能为空`, | |
| 265 | + }, | |
| 266 | + { | |
| 267 | + validator() { | |
| 268 | + if (open === close) return Promise.reject('布尔值不能相同'); | |
| 269 | + return Promise.resolve(); | |
| 270 | + }, | |
| 271 | + }, | |
| 272 | + ]; | |
| 273 | + }, | |
| 274 | + }, | |
| 275 | + { | |
| 276 | + field: FormFieldsEnum.LENGTH, | |
| 277 | + component: 'Input', | |
| 278 | + required: true, | |
| 279 | + label: FormFieldsNameEnum.LENGTH, | |
| 280 | + defaultValue: '10240', | |
| 281 | + colProps: { | |
| 282 | + span: 8, | |
| 283 | + }, | |
| 284 | + componentProps: { | |
| 285 | + placeholder: '请输入数据长度', | |
| 286 | + }, | |
| 287 | + renderComponentContent: () => { | |
| 288 | + return { | |
| 289 | + suffix: () => '字节', | |
| 290 | + }; | |
| 291 | + }, | |
| 292 | + ifShow: ({ model }) => | |
| 293 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 294 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 295 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRING, | |
| 296 | + }, | |
| 297 | + { | |
| 298 | + field: FormFieldsEnum.ENUMS_DATA, | |
| 299 | + label: FormFieldsNameEnum.ENUMS_DATA, | |
| 300 | + component: 'Input', | |
| 301 | + slot: FormFieldsEnum.ENUMS_DATA, | |
| 302 | + changeEvent: 'update:value', | |
| 303 | + valueField: 'value', | |
| 304 | + ifShow: ({ model }) => | |
| 305 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 306 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 307 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.ENUM, | |
| 308 | + }, | |
| 309 | + { | |
| 310 | + field: FormFieldsEnum.STRUCT_DATA, | |
| 311 | + label: FormFieldsNameEnum.STRUCT_DATA, | |
| 312 | + component: 'Input', | |
| 313 | + slot: FormFieldsEnum.STRUCT_DATA, | |
| 314 | + changeEvent: 'update:value', | |
| 315 | + valueField: 'value', | |
| 316 | + required: true, | |
| 317 | + ifShow: ({ model }) => | |
| 318 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRUCT && | |
| 319 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 320 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 321 | + ), | |
| 322 | + }, | |
| 323 | + ]; | |
| 324 | +}; | ... | ... |
| 1 | +export { default as StructFormItem } from './index.vue'; | ... | ... |
| 1 | +<script setup lang="ts"> | |
| 2 | + import { Button, Divider } from 'ant-design-vue'; | |
| 3 | + import { cloneDeep } from 'lodash'; | |
| 4 | + import { toRaw, unref } from 'vue'; | |
| 5 | + import CreateStructModal from './CreateStructModal.vue'; | |
| 6 | + import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | |
| 7 | + import { useModal } from '/@/components/Modal'; | |
| 8 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 9 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | |
| 10 | + | |
| 11 | + const props = withDefaults( | |
| 12 | + defineProps<{ | |
| 13 | + buttonName?: string; | |
| 14 | + disabled?: boolean; | |
| 15 | + value?: StructJSON[]; | |
| 16 | + dataType?: DataTypeEnum[]; | |
| 17 | + mode?: DataActionModeEnum; | |
| 18 | + }>(), | |
| 19 | + { | |
| 20 | + buttonName: '+ 新增参数', | |
| 21 | + value: () => [], | |
| 22 | + dataType: () => Object.values(DataTypeEnum), | |
| 23 | + } | |
| 24 | + ); | |
| 25 | + | |
| 26 | + const emits = defineEmits(['update:value']); | |
| 27 | + | |
| 28 | + const [registerModal, { openModal, closeModal }] = useModal(); | |
| 29 | + | |
| 30 | + const handleOpenCreateModal = () => { | |
| 31 | + openModal(true, { | |
| 32 | + mode: DataActionModeEnum.CREATE, | |
| 33 | + } as ModalParamsType); | |
| 34 | + }; | |
| 35 | + | |
| 36 | + const handleEdit = (item: StructJSON) => { | |
| 37 | + openModal(true, { | |
| 38 | + mode: DataActionModeEnum.UPDATE, | |
| 39 | + record: cloneDeep(item), | |
| 40 | + } as ModalParamsType); | |
| 41 | + }; | |
| 42 | + | |
| 43 | + const handleDelete = (item: StructJSON) => { | |
| 44 | + const index = props.value.findIndex((temp) => item.identifier === temp.identifier); | |
| 45 | + | |
| 46 | + if (~index) { | |
| 47 | + const _values = cloneDeep(props.value); | |
| 48 | + _values.splice(index, 1); | |
| 49 | + emits('update:value', _values); | |
| 50 | + } | |
| 51 | + }; | |
| 52 | + | |
| 53 | + const handleEditComplete = (value: StructJSON) => { | |
| 54 | + const _value = cloneDeep(toRaw(unref(props.value))); | |
| 55 | + const index = _value.findIndex((item) => item.identifier === value.identifier); | |
| 56 | + ~index ? _value.splice(index, 1, value) : _value.push(value); | |
| 57 | + emits('update:value', _value); | |
| 58 | + closeModal(); | |
| 59 | + }; | |
| 60 | +</script> | |
| 61 | + | |
| 62 | +<template> | |
| 63 | + <section> | |
| 64 | + <main> | |
| 65 | + <div | |
| 66 | + v-for="item in value" | |
| 67 | + :key="item.identifier" | |
| 68 | + class="flex items-center justify-between px-2 py-1 mb-2 bg-blue-50 text-gray-600" | |
| 69 | + > | |
| 70 | + <div> | |
| 71 | + <span>参数名称:</span> | |
| 72 | + <span class="ml-1">{{ item.functionName }}</span> | |
| 73 | + </div> | |
| 74 | + <div> | |
| 75 | + <Button type="link" @click="handleEdit(item)"> | |
| 76 | + {{ mode === DataActionModeEnum.READ ? '查看' : '编辑' }} | |
| 77 | + </Button> | |
| 78 | + <Divider type="vertical" /> | |
| 79 | + <Button | |
| 80 | + type="link" | |
| 81 | + @click="handleDelete(item)" | |
| 82 | + :disabled="mode === DataActionModeEnum.READ" | |
| 83 | + > | |
| 84 | + 删除 | |
| 85 | + </Button> | |
| 86 | + </div> | |
| 87 | + </div> | |
| 88 | + </main> | |
| 89 | + <Button type="link" @click="handleOpenCreateModal" :disabled="mode === DataActionModeEnum.READ"> | |
| 90 | + {{ buttonName }} | |
| 91 | + </Button> | |
| 92 | + <CreateStructModal | |
| 93 | + :value="value" | |
| 94 | + @register="registerModal" | |
| 95 | + @complete="handleEditComplete" | |
| 96 | + :dataType="dataType" | |
| 97 | + :mode="mode" | |
| 98 | + /> | |
| 99 | + </section> | |
| 100 | +</template> | |
| 101 | +./index.config | ... | ... |
| 1 | +import { unref } from 'vue'; | |
| 2 | +import { createHexCommandRuleValidator } from '.'; | |
| 3 | +import { createFunctionNameFormItem, createIdentifierFormItem } from './DataTypeForm/config'; | |
| 4 | +import { useObjectModelFormContext } from './useObjectModelFormContext'; | |
| 5 | +import { findDictItemByCode } from '/@/api/system/dict'; | |
| 6 | +import { FormSchema } from '/@/components/Form'; | |
| 7 | +import { | |
| 8 | + ServiceCallTypeEnum, | |
| 9 | + ServiceCallTypeNameEnum, | |
| 10 | + TransportTypeEnum, | |
| 11 | +} from '/@/enums/deviceEnum'; | |
| 12 | +import { DictEnum } from '/@/enums/dictEnum'; | |
| 13 | +import { | |
| 14 | + ObjectModelAccessModeEnum, | |
| 15 | + FunctionTypeEnum, | |
| 16 | + FunctionTypeNameEnum, | |
| 17 | + ObjectEventTypeEnum, | |
| 18 | +} from '/@/enums/objectModelEnum'; | |
| 19 | +import { DataActionModeEnum } from '/@/enums/toolEnum'; | |
| 20 | + | |
| 21 | +export enum FormFieldsEnum { | |
| 22 | + FUNCTION_TYPE = 'functionType', | |
| 23 | + FUNCTION_NAME = 'functionName', | |
| 24 | + IDENTIFIER = 'identifier', | |
| 25 | + DATA_TYPE = 'dataType', | |
| 26 | + ACCESS_MODE = 'accessMode', | |
| 27 | + VALUE_RANGE = 'valueRange', | |
| 28 | + STEP = 'step', | |
| 29 | + UNIT = 'unit', | |
| 30 | + UNIT_NAME = 'unitName', | |
| 31 | + REMARK = 'remark', | |
| 32 | + BOOL_CLOSE = 'boolClose', | |
| 33 | + BOOL_OPEN = 'boolOpen', | |
| 34 | + LENGTH = 'length', | |
| 35 | + EXTENSION_DESC = 'extensionDesc', | |
| 36 | + ENUMS_DATA = 'enumsData', | |
| 37 | + STRUCT_DATA = 'structData', | |
| 38 | + | |
| 39 | + CALL_TYPE = 'callType', | |
| 40 | + SERVICE_COMMAND = 'serviceCommand', | |
| 41 | + INPUT_DATA = 'inputData', | |
| 42 | + OUTPUT_DATA = 'outputData', | |
| 43 | + | |
| 44 | + EVENT_TYPE = 'eventType', | |
| 45 | + | |
| 46 | + DATA_TYPE_FORM = 'dataTypeForm', | |
| 47 | +} | |
| 48 | + | |
| 49 | +export enum FormFieldsNameEnum { | |
| 50 | + FUNCTION_TYPE = '功能类型', | |
| 51 | + FUNCTION_NAME = '功能名称', | |
| 52 | + IDENTIFIER = '功能标识符', | |
| 53 | + DATA_TYPE = '数据类型', | |
| 54 | + ACCESS_MODE = '读写类型', | |
| 55 | + VALUE_RANGE = '取值范围', | |
| 56 | + STEP = '步长', | |
| 57 | + UNIT = '单位', | |
| 58 | + UNIT_NAME = '单位名称', | |
| 59 | + REMARK = '备注', | |
| 60 | + BOOL_CLOSE = '0 - 关', | |
| 61 | + BOOL_OPEN = '1 - 开', | |
| 62 | + LENGTH = '数据长度(字节)', | |
| 63 | + EXTENSION_DESC = '拓展描述符', | |
| 64 | + ENUMS_DATA = '枚举项', | |
| 65 | + STRUCT_DATA = 'JSON对象', | |
| 66 | + | |
| 67 | + CALL_TYPE = '调用方式', | |
| 68 | + SERVICE_COMMAND = '输入参数', | |
| 69 | + INPUT_DATA = '输入参数', | |
| 70 | + OUTPUT_DATA = '输出参数', | |
| 71 | + | |
| 72 | + EVENT_TYPE = '事件类型', | |
| 73 | +} | |
| 74 | + | |
| 75 | +export const getFormSchemas = ({ | |
| 76 | + transportType, | |
| 77 | +}: { | |
| 78 | + transportType?: string; | |
| 79 | + mode?: DataActionModeEnum; | |
| 80 | +}): FormSchema[] => { | |
| 81 | + return [ | |
| 82 | + { | |
| 83 | + field: FormFieldsEnum.FUNCTION_TYPE, | |
| 84 | + label: FormFieldsNameEnum.FUNCTION_TYPE, | |
| 85 | + component: 'Segmented', | |
| 86 | + defaultValue: FunctionTypeEnum.PROPERTIES, | |
| 87 | + required: true, | |
| 88 | + dynamicDisabled: () => { | |
| 89 | + const { getModalMode } = useObjectModelFormContext(); | |
| 90 | + return unref(getModalMode) !== DataActionModeEnum.CREATE; | |
| 91 | + }, | |
| 92 | + componentProps: ({ formActionType }) => { | |
| 93 | + return { | |
| 94 | + options: Object.keys(FunctionTypeEnum).map((key) => ({ | |
| 95 | + title: FunctionTypeNameEnum[key], | |
| 96 | + value: FunctionTypeEnum[key], | |
| 97 | + })), | |
| 98 | + onChange() { | |
| 99 | + const { setFieldsValue, clearValidate } = formActionType; | |
| 100 | + setFieldsValue({ | |
| 101 | + [FormFieldsEnum.INPUT_DATA]: [], | |
| 102 | + [FormFieldsEnum.OUTPUT_DATA]: [], | |
| 103 | + [FormFieldsEnum.STRUCT_DATA]: [], | |
| 104 | + [FormFieldsEnum.SERVICE_COMMAND]: null, | |
| 105 | + }); | |
| 106 | + clearValidate(); | |
| 107 | + }, | |
| 108 | + }; | |
| 109 | + }, | |
| 110 | + }, | |
| 111 | + { | |
| 112 | + field: FormFieldsEnum.DATA_TYPE_FORM, | |
| 113 | + component: 'Input', | |
| 114 | + label: '', | |
| 115 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.PROPERTIES, | |
| 116 | + colSlot: FormFieldsEnum.DATA_TYPE_FORM, | |
| 117 | + }, | |
| 118 | + createFunctionNameFormItem({ | |
| 119 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] !== FunctionTypeEnum.PROPERTIES, | |
| 120 | + }), | |
| 121 | + createIdentifierFormItem({ | |
| 122 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] !== FunctionTypeEnum.PROPERTIES, | |
| 123 | + }), | |
| 124 | + { | |
| 125 | + field: FormFieldsEnum.EXTENSION_DESC, | |
| 126 | + component: 'Input', | |
| 127 | + label: FormFieldsNameEnum.EXTENSION_DESC, | |
| 128 | + slot: FormFieldsEnum.EXTENSION_DESC, | |
| 129 | + ifShow: ({ model }) => | |
| 130 | + transportType === TransportTypeEnum.TCP && | |
| 131 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 132 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 133 | + ), | |
| 134 | + }, | |
| 135 | + { | |
| 136 | + field: FormFieldsEnum.ACCESS_MODE, | |
| 137 | + component: 'ApiRadioGroup', | |
| 138 | + label: FormFieldsNameEnum.ACCESS_MODE, | |
| 139 | + required: true, | |
| 140 | + ifShow: ({ model }) => | |
| 141 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 142 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 143 | + ), | |
| 144 | + defaultValue: ObjectModelAccessModeEnum.READ_AND_WRITE, | |
| 145 | + componentProps: { | |
| 146 | + placeholder: '请选择读写类型', | |
| 147 | + api: findDictItemByCode, | |
| 148 | + params: { | |
| 149 | + dictCode: DictEnum.READ_WRITE_TYP, | |
| 150 | + }, | |
| 151 | + labelField: 'itemText', | |
| 152 | + valueField: 'itemValue', | |
| 153 | + }, | |
| 154 | + }, | |
| 155 | + | |
| 156 | + // 服务 | |
| 157 | + { | |
| 158 | + field: FormFieldsEnum.CALL_TYPE, | |
| 159 | + label: FormFieldsNameEnum.CALL_TYPE, | |
| 160 | + component: 'RadioGroup', | |
| 161 | + defaultValue: ServiceCallTypeEnum.ASYNC, | |
| 162 | + required: true, | |
| 163 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE, | |
| 164 | + componentProps: () => { | |
| 165 | + return { | |
| 166 | + options: Object.keys(ServiceCallTypeEnum).map((value) => ({ | |
| 167 | + label: ServiceCallTypeNameEnum[value], | |
| 168 | + value, | |
| 169 | + })), | |
| 170 | + }; | |
| 171 | + }, | |
| 172 | + }, | |
| 173 | + { | |
| 174 | + field: FormFieldsEnum.SERVICE_COMMAND, | |
| 175 | + label: FormFieldsNameEnum.SERVICE_COMMAND, | |
| 176 | + component: 'Input', | |
| 177 | + required: true, | |
| 178 | + rules: [{ message: '输入参数为必填项', required: true }, ...createHexCommandRuleValidator()], | |
| 179 | + ifShow: ({ model }) => | |
| 180 | + model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE && | |
| 181 | + transportType === TransportTypeEnum.TCP, | |
| 182 | + componentProps: () => { | |
| 183 | + return { | |
| 184 | + placeholder: '请输入ASCII或HEX服务命令', | |
| 185 | + }; | |
| 186 | + }, | |
| 187 | + }, | |
| 188 | + { | |
| 189 | + field: FormFieldsEnum.INPUT_DATA, | |
| 190 | + label: FormFieldsNameEnum.INPUT_DATA, | |
| 191 | + component: 'Input', | |
| 192 | + slot: FormFieldsEnum.INPUT_DATA, | |
| 193 | + changeEvent: 'update:value', | |
| 194 | + valueField: 'value', | |
| 195 | + required: true, | |
| 196 | + ifShow: ({ model }) => | |
| 197 | + model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE && | |
| 198 | + transportType !== TransportTypeEnum.TCP, | |
| 199 | + }, | |
| 200 | + | |
| 201 | + // 事件 | |
| 202 | + { | |
| 203 | + field: FormFieldsEnum.EVENT_TYPE, | |
| 204 | + label: FormFieldsNameEnum.EVENT_TYPE, | |
| 205 | + required: true, | |
| 206 | + component: 'ApiRadioGroup', | |
| 207 | + defaultValue: ObjectEventTypeEnum.INFO, | |
| 208 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.EVENTS, | |
| 209 | + componentProps: () => { | |
| 210 | + return { | |
| 211 | + placeholder: '请选择事件类型', | |
| 212 | + api: findDictItemByCode, | |
| 213 | + params: { | |
| 214 | + dictCode: DictEnum.EVENT_TYPE, | |
| 215 | + }, | |
| 216 | + labelField: 'itemText', | |
| 217 | + valueField: 'itemValue', | |
| 218 | + getPopupContainer: () => document.body, | |
| 219 | + }; | |
| 220 | + }, | |
| 221 | + }, | |
| 222 | + { | |
| 223 | + field: FormFieldsEnum.OUTPUT_DATA, | |
| 224 | + label: FormFieldsNameEnum.OUTPUT_DATA, | |
| 225 | + component: 'Input', | |
| 226 | + slot: FormFieldsEnum.OUTPUT_DATA, | |
| 227 | + changeEvent: 'update:value', | |
| 228 | + valueField: 'value', | |
| 229 | + ifShow: ({ model }) => | |
| 230 | + [FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | |
| 231 | + model[FormFieldsEnum.FUNCTION_TYPE] | |
| 232 | + ), | |
| 233 | + }, | |
| 234 | + | |
| 235 | + { | |
| 236 | + field: FormFieldsEnum.REMARK, | |
| 237 | + label: FormFieldsNameEnum.REMARK, | |
| 238 | + component: 'InputTextArea', | |
| 239 | + componentProps: { | |
| 240 | + rows: 4, | |
| 241 | + maxLength: 100, | |
| 242 | + placeholder: '请输入描述', | |
| 243 | + }, | |
| 244 | + }, | |
| 245 | + ]; | |
| 246 | +}; | ... | ... |
| 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 | +} | ... | ... |
| 1 | 1 | <script lang="ts" setup> |
| 2 | 2 | import { computed } from 'vue'; |
| 3 | 3 | import COAPDescription from './COAPDescription.vue'; |
| 4 | - import { TransportTypeEnum } from './const'; | |
| 5 | 4 | import DefaultConfiguration from './DefaultConfiguration.vue'; |
| 6 | 5 | import LWM2MDescription from './LWM2MDescription.vue'; |
| 7 | 6 | import MQTTConfiguration from './MQTTConfiguration.vue'; |
| 8 | 7 | import SNMPDescription from './SNMPDescription.vue'; |
| 9 | 8 | import TCPDescription from './TCPDescription.vue'; |
| 10 | 9 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
| 10 | + import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 11 | 11 | |
| 12 | 12 | const props = defineProps<{ |
| 13 | 13 | record: DeviceRecord; | ... | ... |
| ... | ... | @@ -2,8 +2,7 @@ import { BasicColumn, FormSchema } from '/@/components/Table'; |
| 2 | 2 | import { findDictItemByCode } from '/@/api/system/dict'; |
| 3 | 3 | import { h, unref } from 'vue'; |
| 4 | 4 | import { Tooltip } from 'ant-design-vue'; |
| 5 | - | |
| 6 | -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | |
| 5 | +import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
| 7 | 6 | import { useMessage } from '/@/hooks/web/useMessage'; |
| 8 | 7 | import { useClipboard } from '@vueuse/core'; |
| 9 | 8 | import { DictEnum } from '/@/enums/dictEnum'; |
| ... | ... | @@ -26,10 +25,10 @@ export const columns: BasicColumn[] = [ |
| 26 | 25 | }, |
| 27 | 26 | ]; |
| 28 | 27 | |
| 29 | -export const formatFunctionType: Record<FunctionType, string> = { | |
| 30 | - [FunctionType.PROPERTIES]: '属性', | |
| 31 | - [FunctionType.EVENTS]: '事件', | |
| 32 | - [FunctionType.SERVICE]: '服务', | |
| 28 | +export const formatFunctionType: Record<FunctionTypeEnum, string> = { | |
| 29 | + [FunctionTypeEnum.PROPERTIES]: '属性', | |
| 30 | + [FunctionTypeEnum.EVENTS]: '事件', | |
| 31 | + [FunctionTypeEnum.SERVICE]: '服务', | |
| 33 | 32 | }; |
| 34 | 33 | |
| 35 | 34 | const handleCopy = async (value: string) => { |
| ... | ... | @@ -45,7 +44,7 @@ export const columnsDrawer: BasicColumn[] = [ |
| 45 | 44 | title: '功能类型', |
| 46 | 45 | dataIndex: 'functionType', |
| 47 | 46 | width: 90, |
| 48 | - format: (text: FunctionType) => { | |
| 47 | + format: (text: FunctionTypeEnum) => { | |
| 49 | 48 | return formatFunctionType[text]; |
| 50 | 49 | }, |
| 51 | 50 | }, | ... | ... |
| ... | ... | @@ -5,7 +5,8 @@ import { MessageEnum } from '/@/enums/messageEnum'; |
| 5 | 5 | import { numberRule } from '/@/utils/rules'; |
| 6 | 6 | |
| 7 | 7 | import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi'; |
| 8 | -import { FormField, FunctionType } from './step/cpns/physical/cpns/config'; | |
| 8 | +import { FormField } from './step/cpns/physical/cpns/config'; | |
| 9 | +import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
| 9 | 10 | import { h, unref } from 'vue'; |
| 10 | 11 | import { Tag, Tooltip } from 'ant-design-vue'; |
| 11 | 12 | import { EventType, EventTypeColor, EventTypeName } from '../list/cpns/tabs/EventManage/config'; |
| ... | ... | @@ -68,10 +69,10 @@ export const steps = [ |
| 68 | 69 | }, |
| 69 | 70 | ]; |
| 70 | 71 | |
| 71 | -export const formatFunctionType: Record<FunctionType, string> = { | |
| 72 | - [FunctionType.PROPERTIES]: '属性', | |
| 73 | - [FunctionType.EVENTS]: '事件', | |
| 74 | - [FunctionType.SERVICE]: '服务', | |
| 72 | +export const formatFunctionType: Record<FunctionTypeEnum, string> = { | |
| 73 | + [FunctionTypeEnum.PROPERTIES]: '属性', | |
| 74 | + [FunctionTypeEnum.EVENTS]: '事件', | |
| 75 | + [FunctionTypeEnum.SERVICE]: '服务', | |
| 75 | 76 | }; |
| 76 | 77 | |
| 77 | 78 | export const physicalColumn: BasicColumn[] = [ |
| ... | ... | @@ -79,7 +80,7 @@ export const physicalColumn: BasicColumn[] = [ |
| 79 | 80 | title: '功能类型', |
| 80 | 81 | dataIndex: FormField.FUNCTION_TYPE, |
| 81 | 82 | width: 90, |
| 82 | - format: (text: FunctionType) => { | |
| 83 | + format: (text: FunctionTypeEnum) => { | |
| 83 | 84 | return formatFunctionType[text]; |
| 84 | 85 | }, |
| 85 | 86 | }, |
| ... | ... | @@ -153,9 +154,12 @@ export const modelOfMatterForm: FormSchema[] = [ |
| 153 | 154 | colProps: { span: 8 }, |
| 154 | 155 | componentProps: { |
| 155 | 156 | options: [ |
| 156 | - { label: formatFunctionType[FunctionType.PROPERTIES], value: FunctionType.PROPERTIES }, | |
| 157 | - { label: formatFunctionType[FunctionType.EVENTS], value: FunctionType.EVENTS }, | |
| 158 | - { label: formatFunctionType[FunctionType.SERVICE], value: FunctionType.SERVICE }, | |
| 157 | + { | |
| 158 | + label: formatFunctionType[FunctionTypeEnum.PROPERTIES], | |
| 159 | + value: FunctionTypeEnum.PROPERTIES, | |
| 160 | + }, | |
| 161 | + { label: formatFunctionType[FunctionTypeEnum.EVENTS], value: FunctionTypeEnum.EVENTS }, | |
| 162 | + { label: formatFunctionType[FunctionTypeEnum.SERVICE], value: FunctionTypeEnum.SERVICE }, | |
| 159 | 163 | ], |
| 160 | 164 | }, |
| 161 | 165 | }, | ... | ... |
| ... | ... | @@ -147,7 +147,6 @@ |
| 147 | 147 | getModelList, |
| 148 | 148 | releaseModel, |
| 149 | 149 | } from '/@/api/device/modelOfMatter'; |
| 150 | - import { OpenModelOfMatterModelParams, OpenModelMode } from './cpns/physical/types'; | |
| 151 | 150 | import { |
| 152 | 151 | ModelOfMatterItemRecordType, |
| 153 | 152 | ModelOfMatterParams, |
| ... | ... | @@ -156,8 +155,9 @@ |
| 156 | 155 | import { isObject } from '/@/utils/is'; |
| 157 | 156 | import { useRole } from '/@/hooks/business/useRole'; |
| 158 | 157 | import { ExportModelCategory } from '/@/api/device/modelOfMatter'; |
| 159 | - import { FunctionType } from './cpns/physical/cpns/config'; | |
| 158 | + import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
| 160 | 159 | import SelectImport from '../components/SelectImport.vue'; |
| 160 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | |
| 161 | 161 | |
| 162 | 162 | const { isPlatformAdmin, isSysadmin } = useRole(); |
| 163 | 163 | defineEmits(['register']); |
| ... | ... | @@ -209,23 +209,17 @@ |
| 209 | 209 | if (record) { |
| 210 | 210 | openModal(true, { |
| 211 | 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 | 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 | 225 | const handleDeleteOrBatchDelete = async (record?: ModelOfMatterItemRecordType) => { |
| ... | ... | @@ -287,15 +281,15 @@ |
| 287 | 281 | return Promise.all([ |
| 288 | 282 | ExportModelCategory({ |
| 289 | 283 | deviceProfileId, |
| 290 | - functionType: FunctionType.EVENTS, | |
| 284 | + functionType: FunctionTypeEnum.EVENTS, | |
| 291 | 285 | }), |
| 292 | 286 | ExportModelCategory({ |
| 293 | 287 | deviceProfileId, |
| 294 | - functionType: FunctionType.PROPERTIES, | |
| 288 | + functionType: FunctionTypeEnum.PROPERTIES, | |
| 295 | 289 | }), |
| 296 | 290 | ExportModelCategory({ |
| 297 | 291 | deviceProfileId, |
| 298 | - functionType: FunctionType.SERVICE, | |
| 292 | + functionType: FunctionTypeEnum.SERVICE, | |
| 299 | 293 | }), |
| 300 | 294 | ]); |
| 301 | 295 | }; | ... | ... |
| 1 | 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="FunctionType.PROPERTIES" tab="属性" /> | |
| 27 | - <TabPane :key="FunctionType.SERVICE" :disabled="isTCPGatewaySubDevice" tab="服务" /> | |
| 28 | - <TabPane :key="FunctionType.EVENTS" tab="事件" :disabled="isTCPGatewaySubDevice" /> | |
| 29 | - </Tabs> | |
| 30 | - <Attribute | |
| 31 | - v-if="activeKey === FunctionType.PROPERTIES" | |
| 32 | - :openModalMode="openModalMode" | |
| 33 | - :transportType="record?.transportType" | |
| 34 | - ref="AttrRef" | |
| 35 | - /> | |
| 36 | - <Service | |
| 37 | - v-if="activeKey === FunctionType.SERVICE" | |
| 38 | - :record="$props.record" | |
| 39 | - :openModalMode="openModalMode" | |
| 40 | - ref="ServiceRef" | |
| 41 | - /> | |
| 42 | - <Events | |
| 43 | - v-if="activeKey === FunctionType.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 | 14 | </template> |
| 55 | 15 | <script lang="ts" setup> |
| 56 | - import { ref, unref, nextTick, computed } from 'vue'; | |
| 16 | + import { ref, unref, nextTick } from 'vue'; | |
| 57 | 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 | 19 | import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; |
| 63 | 20 | import { |
| 64 | 21 | createModel, |
| ... | ... | @@ -66,128 +23,77 @@ |
| 66 | 23 | createModelCategory, |
| 67 | 24 | updateModelCategory, |
| 68 | 25 | } from '/@/api/device/modelOfMatter'; |
| 69 | - import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | |
| 26 | + import { DeviceRecord } from '/@/api/device/model/deviceModel'; | |
| 70 | 27 | import { useMessage } from '/@/hooks/web/useMessage'; |
| 71 | - import { OpenModelMode, OpenModelOfMatterModelParams } from './types/index'; | |
| 72 | - import { FunctionType } from './cpns/config'; | |
| 73 | 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 | 33 | const { isPlatformAdmin, isSysadmin } = useRole(); |
| 76 | 34 | |
| 77 | 35 | const emit = defineEmits(['register', 'success']); |
| 78 | 36 | |
| 37 | + const objectModelElRef = ref<InstanceType<typeof ObjectModelForm>>(); | |
| 38 | + | |
| 79 | 39 | const props = defineProps<{ |
| 80 | 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 | 43 | const blockContent = `属性一般是指设备的运行状态,如当前温度等;服务是指设备可被调用的方法,支持定义参数,如执行某项任务;事件则是指设备上报的通知,如告警,需要被及时处理。`; |
| 90 | - const activeKey = ref<FunctionType>(FunctionType.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<FunctionType>(); | |
| 102 | 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 | - [FunctionType.PROPERTIES]: setAttrFormData, | |
| 110 | - [FunctionType.SERVICE]: setServiceFormData, | |
| 111 | - [FunctionType.EVENTS]: setEventsFormData, | |
| 112 | - }; | |
| 113 | - | |
| 114 | - function setFormData(type: FunctionType, value: ModelOfMatterParams) { | |
| 115 | - const setFn = enums[type]; | |
| 116 | - setFn(value); | |
| 117 | - } | |
| 49 | + const currentActionModel = ref<ModelOfMatterParams>(); | |
| 118 | 50 | |
| 119 | 51 | const [register, { closeModal, setModalProps }] = useModalInner( |
| 120 | - async (data: OpenModelOfMatterModelParams) => { | |
| 121 | - openModalParams.value = data; | |
| 52 | + async (data: ModalParamsType<ModelOfMatterParams>) => { | |
| 122 | 53 | const { mode, record } = data; |
| 123 | 54 | openModalMode.value = mode; |
| 124 | 55 | if (record) { |
| 125 | - functionType.value = data.record.functionType; | |
| 126 | - activeKey.value = data.record.functionType; | |
| 127 | 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 = FunctionType.PROPERTIES; | |
| 151 | - if (flag) { | |
| 152 | - closeModal(); | |
| 153 | - } | |
| 154 | - }; | |
| 155 | - | |
| 156 | 71 | const handleSubmit = async () => { |
| 157 | 72 | try { |
| 158 | 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 == FunctionType.PROPERTIES) { | |
| 162 | - params = (await AttrRef.value?.getFormData()) || {}; | |
| 163 | - } else if (activeKey.value == FunctionType.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 | 97 | closeModal(); |
| 192 | 98 | emit('success'); |
| 193 | 99 | } catch (error) { | ... | ... |
| ... | ... | @@ -24,7 +24,7 @@ |
| 24 | 24 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
| 25 | 25 | import { Button } from 'ant-design-vue'; |
| 26 | 26 | import { getModelTsl } from '/@/api/device/modelOfMatter'; |
| 27 | - import { FunctionType } from './cpns/config'; | |
| 27 | + import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
| 28 | 28 | import { isObject } from '/@/utils/is'; |
| 29 | 29 | |
| 30 | 30 | defineEmits(['register']); |
| ... | ... | @@ -75,15 +75,15 @@ |
| 75 | 75 | return Promise.all([ |
| 76 | 76 | getModelTsl({ |
| 77 | 77 | deviceProfileId, |
| 78 | - functionType: FunctionType.EVENTS, | |
| 78 | + functionType: FunctionTypeEnum.EVENTS, | |
| 79 | 79 | }), |
| 80 | 80 | getModelTsl({ |
| 81 | 81 | deviceProfileId, |
| 82 | - functionType: FunctionType.PROPERTIES, | |
| 82 | + functionType: FunctionTypeEnum.PROPERTIES, | |
| 83 | 83 | }), |
| 84 | 84 | getModelTsl({ |
| 85 | 85 | deviceProfileId, |
| 86 | - functionType: FunctionType.SERVICE, | |
| 86 | + functionType: FunctionTypeEnum.SERVICE, | |
| 87 | 87 | }), |
| 88 | 88 | ]); |
| 89 | 89 | }; | ... | ... |
src/views/device/profiles/step/cpns/physical/cpns/Attribute.vue
deleted
100644 → 0
| 1 | -<script lang="ts" setup> | |
| 2 | - import { BasicForm, useForm } from '/@/components/Form'; | |
| 3 | - import { DataType, ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | |
| 4 | - import { StructFormValue } from '/@/components/Form/src/externalCompns/components/StructForm/type'; | |
| 5 | - import { | |
| 6 | - transfromToStructJSON, | |
| 7 | - excludeIdInStructJSON, | |
| 8 | - } from '/@/components/Form/src/externalCompns/components/StructForm/util'; | |
| 9 | - import { FunctionType } from './config'; | |
| 10 | - import { isArray } from 'lodash'; | |
| 11 | - import { OpenModelMode } from '../types'; | |
| 12 | - import { formSchemas } from '/@/components/Form/src/externalCompns/components/StructForm/config'; | |
| 13 | - import { TransportTypeEnum } from '../../../../components/TransportDescript/const'; | |
| 14 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 15 | - import { ref, unref } from 'vue'; | |
| 16 | - import EnumList from '/@/components/Form/src/externalCompns/components/StructForm/EnumList.vue'; | |
| 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: FunctionType.PROPERTIES, | |
| 50 | - remark, | |
| 51 | - identifier, | |
| 52 | - accessMode, | |
| 53 | - extensionDesc: _values.extensionDesc, | |
| 54 | - functionJson: { | |
| 55 | - dataType: dataType, | |
| 56 | - }, | |
| 57 | - } as ModelOfMatterParams; | |
| 58 | - | |
| 59 | - return value; | |
| 60 | - } | |
| 61 | - | |
| 62 | - const resetFormData = () => { | |
| 63 | - resetFields(); | |
| 64 | - }; | |
| 65 | - | |
| 66 | - const setFormData = (record: ModelOfMatterParams) => { | |
| 67 | - const { functionJson } = record; | |
| 68 | - const { dataType } = functionJson!; | |
| 69 | - | |
| 70 | - const { specs } = (dataType! || {}) as DataType; | |
| 71 | - | |
| 72 | - const value = { | |
| 73 | - ...record, | |
| 74 | - ...functionJson, | |
| 75 | - ...dataType, | |
| 76 | - ...(isArray(specs) ? specs : { ...specs }), | |
| 77 | - hasStructForm: (dataType as DataType)?.type === DataTypeEnum.STRUCT, | |
| 78 | - enumList: | |
| 79 | - (dataType as DataType)?.type === DataTypeEnum.ENUM | |
| 80 | - ? unref((dataType as DataType).specsList) | |
| 81 | - : [], | |
| 82 | - }; | |
| 83 | - setFieldsValue(value); | |
| 84 | - }; | |
| 85 | - | |
| 86 | - defineExpose({ | |
| 87 | - resetFormData, | |
| 88 | - getFormData, | |
| 89 | - setFormData, | |
| 90 | - setDisable, | |
| 91 | - }); | |
| 92 | -</script> | |
| 93 | - | |
| 94 | -<template> | |
| 95 | - <BasicForm @register="register" :disabled="disabled"> | |
| 96 | - <template #EnumList="{ field, model }"> | |
| 97 | - <EnumList ref="enumListRef" :value="model[field]" :disabled="disabled" /> | |
| 98 | - </template> | |
| 99 | - </BasicForm> | |
| 100 | -</template> | |
| 101 | - | |
| 102 | -<style lang="less" scoped></style> |
src/views/device/profiles/step/cpns/physical/cpns/Events.vue
deleted
100644 → 0
| 1 | -<template> | |
| 2 | - <BasicForm @register="register" /> | |
| 3 | -</template> | |
| 4 | -<script lang="ts" setup> | |
| 5 | - import { BasicForm, useForm } from '/@/components/Form'; | |
| 6 | - import { eventSchemas, FunctionType } from './config'; | |
| 7 | - import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | |
| 8 | - import { StructFormValue } from '/@/components/Form/src/externalCompns/components/StructForm/type'; | |
| 9 | - import { excludeIdInStructJSON } from '/@/components/Form/src/externalCompns/components/StructForm/util'; | |
| 10 | - import { OpenModelMode } from '../types'; | |
| 11 | - | |
| 12 | - defineProps<{ openModalMode: OpenModelMode }>(); | |
| 13 | - | |
| 14 | - const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({ | |
| 15 | - labelWidth: 100, | |
| 16 | - schemas: eventSchemas, | |
| 17 | - actionColOptions: { | |
| 18 | - span: 14, | |
| 19 | - }, | |
| 20 | - showResetButton: false, | |
| 21 | - submitOnReset: false, | |
| 22 | - showActionButtonGroup: false, | |
| 23 | - }); | |
| 24 | - | |
| 25 | - const setDisable = (flag: boolean) => { | |
| 26 | - setProps({ disabled: flag }); | |
| 27 | - }; | |
| 28 | - | |
| 29 | - //回显数据 | |
| 30 | - const setFormData = (record: ModelOfMatterParams) => { | |
| 31 | - const { functionJson = {}, functionName, identifier, remark, eventType } = record; | |
| 32 | - const { outputData } = functionJson; | |
| 33 | - const value = { | |
| 34 | - functionName, | |
| 35 | - identifier, | |
| 36 | - remark, | |
| 37 | - outputData, | |
| 38 | - eventType, | |
| 39 | - }; | |
| 40 | - setFieldsValue(value); | |
| 41 | - }; | |
| 42 | - | |
| 43 | - async function getFormData() { | |
| 44 | - const _values = (await validate()) as StructFormValue; | |
| 45 | - const { functionName, remark, identifier, outputData: _outputData = [], eventType } = _values; | |
| 46 | - if (!_values) return {} as unknown as ModelOfMatterParams; | |
| 47 | - | |
| 48 | - const outputData = (_outputData as unknown as StructJSON[]).map((item) => { | |
| 49 | - const dataType = excludeIdInStructJSON(item.dataType!); | |
| 50 | - return { | |
| 51 | - ...item, | |
| 52 | - dataType, | |
| 53 | - }; | |
| 54 | - }); | |
| 55 | - | |
| 56 | - const value = { | |
| 57 | - functionName, | |
| 58 | - identifier, | |
| 59 | - remark, | |
| 60 | - functionType: FunctionType.EVENTS, | |
| 61 | - eventType, | |
| 62 | - functionJson: { | |
| 63 | - outputData, | |
| 64 | - }, | |
| 65 | - } as ModelOfMatterParams; | |
| 66 | - | |
| 67 | - return value; | |
| 68 | - } | |
| 69 | - | |
| 70 | - //清空数据 | |
| 71 | - const resetFormData = () => { | |
| 72 | - resetFields(); | |
| 73 | - }; | |
| 74 | - | |
| 75 | - defineExpose({ | |
| 76 | - setFormData, | |
| 77 | - resetFormData, | |
| 78 | - getFormData, | |
| 79 | - setDisable, | |
| 80 | - }); | |
| 81 | -</script> | |
| 82 | -<style lang="less" scoped></style> |
src/views/device/profiles/step/cpns/physical/cpns/Service.vue
deleted
100644 → 0
| 1 | -<template> | |
| 2 | - <BasicForm @register="register" /> | |
| 3 | -</template> | |
| 4 | -<script lang="ts" setup> | |
| 5 | - import { BasicForm, useForm } from '/@/components/Form'; | |
| 6 | - import { FormField, serviceSchemas } from './config'; | |
| 7 | - import { FunctionType } from './config'; | |
| 8 | - import { StructFormValue } from '/@/components/Form/src/externalCompns/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/externalCompns/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: FunctionType.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> |
| ... | ... | @@ -9,9 +9,9 @@ |
| 9 | 9 | </div> |
| 10 | 10 | <div> |
| 11 | 11 | <Tabs type="card" v-model:active-key="activeKey" @change="handleSwitchTsl"> |
| 12 | - <Tabs.TabPane :key="FunctionType.PROPERTIES" tab="属性" /> | |
| 13 | - <Tabs.TabPane :key="FunctionType.SERVICE" tab="服务" /> | |
| 14 | - <Tabs.TabPane :key="FunctionType.EVENTS" tab="事件" /> | |
| 12 | + <Tabs.TabPane :key="FunctionTypeEnum.PROPERTIES" tab="属性" /> | |
| 13 | + <Tabs.TabPane :key="FunctionTypeEnum.SERVICE" tab="服务" /> | |
| 14 | + <Tabs.TabPane :key="FunctionTypeEnum.EVENTS" tab="事件" /> | |
| 15 | 15 | <template #tabBarExtraContent> |
| 16 | 16 | <Button class="ml-2" @click="handleCopy"> |
| 17 | 17 | <template #icon> |
| ... | ... | @@ -38,7 +38,7 @@ |
| 38 | 38 | import { CopyOutlined } from '@ant-design/icons-vue'; |
| 39 | 39 | import { useMessage } from '/@/hooks/web/useMessage'; |
| 40 | 40 | import { Button, Typography, TypographyParagraph, Tabs, Spin, Input } from 'ant-design-vue'; |
| 41 | - import { FunctionType } from './config'; | |
| 41 | + import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
| 42 | 42 | import useCommon from '../hook/useCommon'; |
| 43 | 43 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
| 44 | 44 | import { getModelTsl } from '/@/api/device/modelOfMatter'; |
| ... | ... | @@ -56,7 +56,7 @@ |
| 56 | 56 | |
| 57 | 57 | const jsonValue = ref(); |
| 58 | 58 | |
| 59 | - const activeKey = ref(FunctionType.PROPERTIES); | |
| 59 | + const activeKey = ref(FunctionTypeEnum.PROPERTIES); | |
| 60 | 60 | |
| 61 | 61 | const { copied, copy } = useClipboard({ legacy: true }); |
| 62 | 62 | const handleCopy = async () => { |
| ... | ... | @@ -81,7 +81,7 @@ |
| 81 | 81 | jsonValue.value = null; |
| 82 | 82 | }; |
| 83 | 83 | |
| 84 | - const handleSwitchTsl = async (functionType: FunctionType) => { | |
| 84 | + const handleSwitchTsl = async (functionType: FunctionTypeEnum) => { | |
| 85 | 85 | try { |
| 86 | 86 | loading.value = true; |
| 87 | 87 | const record = await getModelTsl({ |
| ... | ... | @@ -102,8 +102,8 @@ |
| 102 | 102 | }; |
| 103 | 103 | |
| 104 | 104 | onMounted(() => { |
| 105 | - activeKey.value = FunctionType.PROPERTIES; | |
| 106 | - handleSwitchTsl(FunctionType.PROPERTIES); | |
| 105 | + activeKey.value = FunctionTypeEnum.PROPERTIES; | |
| 106 | + handleSwitchTsl(FunctionTypeEnum.PROPERTIES); | |
| 107 | 107 | }); |
| 108 | 108 | |
| 109 | 109 | defineExpose({ | ... | ... |
| ... | ... | @@ -33,17 +33,6 @@ export enum FormField { |
| 33 | 33 | HAS_STRUCT_FROM = 'hasStructForm', |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | -export enum FunctionType { | |
| 37 | - PROPERTIES = 'properties', | |
| 38 | - EVENTS = 'events', | |
| 39 | - SERVICE = 'services', | |
| 40 | -} | |
| 41 | - | |
| 42 | -export enum AssessMode { | |
| 43 | - READ = 'r', | |
| 44 | - WRITE = 'w', | |
| 45 | -} | |
| 46 | - | |
| 47 | 36 | const isNumber = (type: string) => { |
| 48 | 37 | return type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE; |
| 49 | 38 | }; | ... | ... |
| 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 | 1 | export interface IAllData { |
| 15 | 2 | properties: string[]; |
| 16 | 3 | events: string[]; | ... | ... |
| ... | ... | @@ -87,7 +87,10 @@ export const modeForm = (disabled: boolean): FormSchema[] => { |
| 87 | 87 | }, |
| 88 | 88 | }, |
| 89 | 89 | onChange: () => { |
| 90 | - setFieldsValue({ [BasicInfoFormField.DATA_SOURCE_DEVICE]: [] }); | |
| 90 | + setFieldsValue({ | |
| 91 | + [BasicInfoFormField.DATA_SOURCE_DEVICE]: [], | |
| 92 | + [BasicInfoFormField.CONVERT_CONFIG_ID]: null, | |
| 93 | + }); | |
| 91 | 94 | }, |
| 92 | 95 | }; |
| 93 | 96 | }, | ... | ... |
| 1 | +import { toRaw, unref } from 'vue'; | |
| 1 | 2 | import { OperationType } from './type'; |
| 3 | +import { DeviceModelOfMatterAttrs } from '/@/api/device/model/deviceModel'; | |
| 2 | 4 | import { FormSchema } from '/@/components/Form'; |
| 3 | 5 | import { |
| 4 | 6 | BooleanOperationEnum, |
| ... | ... | @@ -11,6 +13,7 @@ import { |
| 11 | 13 | StringOperationNameEnum, |
| 12 | 14 | TriggerValueTypeEnum, |
| 13 | 15 | } from '/@/enums/linkedgeEnum'; |
| 16 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 14 | 17 | |
| 15 | 18 | export enum FormFieldsEnum { |
| 16 | 19 | CONDITION_OPERATION = 'operation', |
| ... | ... | @@ -26,6 +29,7 @@ export enum FormFieldsNameEnum { |
| 26 | 29 | |
| 27 | 30 | interface GetFormSchemasParamsType { |
| 28 | 31 | triggerType: TriggerValueTypeEnum; |
| 32 | + objectModel?: DeviceModelOfMatterAttrs; | |
| 29 | 33 | } |
| 30 | 34 | |
| 31 | 35 | export interface ConditionFormRecordType { |
| ... | ... | @@ -58,7 +62,10 @@ const getOperation = (type: TriggerValueTypeEnum) => { |
| 58 | 62 | return Object.keys(value).map((value) => ({ label: label[value], value })); |
| 59 | 63 | }; |
| 60 | 64 | |
| 61 | -const getOperationValueFormSchemas = (triggerType: TriggerValueTypeEnum): FormSchema[] => { | |
| 65 | +const getOperationValueFormSchemas = ({ | |
| 66 | + triggerType, | |
| 67 | + objectModel, | |
| 68 | +}: GetFormSchemasParamsType): FormSchema[] => { | |
| 62 | 69 | const getNumberSchemas = (): FormSchema[] => { |
| 63 | 70 | return [ |
| 64 | 71 | { |
| ... | ... | @@ -139,17 +146,43 @@ const getOperationValueFormSchemas = (triggerType: TriggerValueTypeEnum): FormSc |
| 139 | 146 | ]; |
| 140 | 147 | }; |
| 141 | 148 | |
| 149 | + const getEnumsSchemas = (): FormSchema[] => { | |
| 150 | + return [ | |
| 151 | + { | |
| 152 | + field: FormFieldsEnum.CONDITION_VALUE, | |
| 153 | + label: FormFieldsNameEnum.CONDITION_VALUE, | |
| 154 | + component: 'Select', | |
| 155 | + required: true, | |
| 156 | + componentProps: () => { | |
| 157 | + return { | |
| 158 | + options: toRaw(unref(objectModel?.detail?.dataType?.specsList))?.map((item) => ({ | |
| 159 | + label: item.name, | |
| 160 | + value: item.value, | |
| 161 | + })), | |
| 162 | + placeholder: `请输入${FormFieldsNameEnum.CONDITION_VALUE}`, | |
| 163 | + }; | |
| 164 | + }, | |
| 165 | + }, | |
| 166 | + ]; | |
| 167 | + }; | |
| 168 | + | |
| 142 | 169 | const schemasMap = { |
| 143 | 170 | [TriggerValueTypeEnum.BOOLEAN]: getBoolSchemas, |
| 144 | 171 | [TriggerValueTypeEnum.DATE_TIME]: getDateSchemas, |
| 145 | - [TriggerValueTypeEnum.NUMERIC]: getNumberSchemas, | |
| 172 | + [TriggerValueTypeEnum.NUMERIC]: | |
| 173 | + objectModel?.detail?.dataType?.type === DataTypeEnum.ENUM | |
| 174 | + ? getEnumsSchemas | |
| 175 | + : getNumberSchemas, | |
| 146 | 176 | [TriggerValueTypeEnum.STRING]: getStringSchemas, |
| 147 | 177 | }; |
| 148 | 178 | |
| 149 | 179 | return schemasMap[triggerType]?.() || []; |
| 150 | 180 | }; |
| 151 | 181 | |
| 152 | -export const getFormSchemas = ({ triggerType }: GetFormSchemasParamsType): FormSchema[] => { | |
| 182 | +export const getFormSchemas = ({ | |
| 183 | + triggerType, | |
| 184 | + objectModel, | |
| 185 | +}: GetFormSchemasParamsType): FormSchema[] => { | |
| 153 | 186 | return [ |
| 154 | 187 | { |
| 155 | 188 | field: FormFieldsEnum.CONDITION_OPERATION, |
| ... | ... | @@ -163,6 +196,6 @@ export const getFormSchemas = ({ triggerType }: GetFormSchemasParamsType): FormS |
| 163 | 196 | }; |
| 164 | 197 | }, |
| 165 | 198 | }, |
| 166 | - ...getOperationValueFormSchemas(triggerType), | |
| 199 | + ...getOperationValueFormSchemas({ triggerType, objectModel }), | |
| 167 | 200 | ]; |
| 168 | 201 | }; | ... | ... |
| 1 | -<script setup lang="ts"> | |
| 1 | +x<script setup lang="ts"> | |
| 2 | 2 | import { ComponentPublicInstance, computed, ref, unref, watch } from 'vue'; |
| 3 | 3 | import { Card, Tag, Button, Tooltip } from 'ant-design-vue'; |
| 4 | 4 | import { Icon } from '/@/components/Icon'; |
| ... | ... | @@ -24,8 +24,8 @@ |
| 24 | 24 | }); |
| 25 | 25 | |
| 26 | 26 | const getSchemas = computed(() => { |
| 27 | - const { triggerType } = props; | |
| 28 | - return getFormSchemas({ triggerType }); | |
| 27 | + const { triggerType, objectModel } = props; | |
| 28 | + return getFormSchemas({ triggerType, objectModel }); | |
| 29 | 29 | }); |
| 30 | 30 | |
| 31 | 31 | const conditionListElRef = ref<ConditionListItemType[]>([getNewConditionFilterItem()]); |
| ... | ... | @@ -40,8 +40,8 @@ |
| 40 | 40 | |
| 41 | 41 | const handleConditionFormValueChange = () => { |
| 42 | 42 | const condition = getConditionFormValues(); |
| 43 | - const { triggerType } = props; | |
| 44 | - const message = useConditionFilterMessage({ triggerType, condition }); | |
| 43 | + const { triggerType, objectModel } = props; | |
| 44 | + const message = useConditionFilterMessage({ triggerType, condition, objectModel }); | |
| 45 | 45 | conditionMessageList.value = message; |
| 46 | 46 | }; |
| 47 | 47 | |
| ... | ... | @@ -84,6 +84,13 @@ |
| 84 | 84 | await validate?.(); |
| 85 | 85 | }; |
| 86 | 86 | |
| 87 | + watch( | |
| 88 | + () => props.objectModel, | |
| 89 | + () => { | |
| 90 | + handleConditionFormValueChange(); | |
| 91 | + } | |
| 92 | + ); | |
| 93 | + | |
| 87 | 94 | defineExpose({ getFieldsValue, setFieldsValue, validate }); |
| 88 | 95 | </script> |
| 89 | 96 | ... | ... |
| 1 | 1 | import { FormFieldsEnum, getOperationMap } from './config'; |
| 2 | +import { DeviceModelOfMatterAttrs } from '/@/api/device/model/deviceModel'; | |
| 2 | 3 | import { |
| 3 | 4 | BooleanOperationValueEnum, |
| 4 | 5 | BooleanOperationValueNameEnum, |
| 5 | 6 | TriggerValueTypeEnum, |
| 6 | 7 | } from '/@/enums/linkedgeEnum'; |
| 8 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 9 | +import { isNullOrUnDef } from '/@/utils/is'; | |
| 7 | 10 | |
| 8 | 11 | export type ConditionMessageItemType = Record< |
| 9 | 12 | Exclude<FormFieldsEnum, FormFieldsEnum.CONDITION_VALUE_IGNORE_CASE>, |
| ... | ... | @@ -13,24 +16,39 @@ export type ConditionMessageItemType = Record< |
| 13 | 16 | interface UseConditionFilterMessageParamsType { |
| 14 | 17 | triggerType: TriggerValueTypeEnum; |
| 15 | 18 | condition: ConditionMessageItemType[]; |
| 19 | + objectModel?: DeviceModelOfMatterAttrs; | |
| 16 | 20 | } |
| 17 | 21 | |
| 18 | 22 | export function useConditionFilterMessage({ |
| 19 | 23 | triggerType, |
| 20 | 24 | condition, |
| 25 | + objectModel, | |
| 21 | 26 | }: UseConditionFilterMessageParamsType) { |
| 22 | 27 | const { label } = getOperationMap(triggerType); |
| 23 | 28 | const list: ConditionMessageItemType[] = []; |
| 29 | + | |
| 30 | + const isEnumsType = objectModel?.detail.dataType?.type === DataTypeEnum.ENUM; | |
| 31 | + | |
| 32 | + const enumsMap: Recordable = {}; | |
| 33 | + if (isEnumsType) { | |
| 34 | + const maps = objectModel.detail.dataType?.specsList?.reduce( | |
| 35 | + (prev, next) => ({ ...prev, [next.value!]: next.name }), | |
| 36 | + {} | |
| 37 | + ); | |
| 38 | + maps && Object.assign(enumsMap, maps); | |
| 39 | + } | |
| 40 | + | |
| 24 | 41 | for (const item of condition) { |
| 25 | 42 | let { defaultValue, operation } = item; |
| 26 | - if (!defaultValue || !operation) continue; | |
| 43 | + if (isNullOrUnDef(defaultValue) || isNullOrUnDef(operation)) continue; | |
| 27 | 44 | operation = label[operation]; |
| 28 | 45 | if (triggerType === TriggerValueTypeEnum.BOOLEAN) |
| 29 | 46 | defaultValue = |
| 30 | 47 | defaultValue === BooleanOperationValueEnum.TRUE |
| 31 | 48 | ? BooleanOperationValueNameEnum.TRUE |
| 32 | 49 | : BooleanOperationValueNameEnum.FALSE; |
| 33 | - list.push({ operation, defaultValue }); | |
| 50 | + | |
| 51 | + list.push({ operation, defaultValue: isEnumsType ? enumsMap[defaultValue] : defaultValue }); | |
| 34 | 52 | } |
| 35 | 53 | return list; |
| 36 | 54 | } | ... | ... |
| 1 | 1 | import { FormSchema, useComponentRegister } from '/@/components/Form'; |
| 2 | 2 | import { |
| 3 | - CommandTypeEnum, | |
| 4 | - CommandTypeNameEnum, | |
| 5 | 3 | ExecutionActionEnum, |
| 6 | 4 | ExecutionActionNameEnum, |
| 7 | 5 | TriggerEntityTypeEnum, |
| 8 | 6 | TriggerEntityTypeNameEnum, |
| 9 | 7 | } from '/@/enums/linkedgeEnum'; |
| 8 | +import { CommandTypeEnum, CommandTypeNameEnum } from '/@/enums/deviceEnum'; | |
| 10 | 9 | import AlarmProfileSelect from './AlarmProfileSelect.vue'; |
| 11 | 10 | import { |
| 12 | 11 | byOrganizationIdGetMasterDevice, |
| ... | ... | @@ -21,15 +20,15 @@ import { findDictItemByCode } from '/@/api/system/dict'; |
| 21 | 20 | import { DictEnum } from '/@/enums/dictEnum'; |
| 22 | 21 | import { getDeviceProfile } from '/@/api/alarm/position'; |
| 23 | 22 | import { createPickerSearch } from '/@/utils/pickerSearch'; |
| 24 | -import { ServiceCallTypeEnum, ServiceCallTypeNameEnum } from '/@/enums/toolEnum'; | |
| 23 | +import { ServiceCallTypeEnum, ServiceCallTypeNameEnum } from '/@/enums/deviceEnum'; | |
| 25 | 24 | import { DeviceTypeEnum } from '../../../dataFlow/cpns/config'; |
| 26 | 25 | import { getModelServices } from '/@/api/device/modelOfMatter'; |
| 27 | 26 | import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
| 28 | 27 | import { DeviceProfileModel } from '/@/api/device/model/deviceModel'; |
| 29 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 30 | 28 | import { JSONEditor } from '/@/components/CodeEditor'; |
| 31 | 29 | import { useJsonParse } from '/@/hooks/business/useJsonParse'; |
| 32 | -import { validateTCPCustomCommand } from '/@/components/Form/src/externalCompns/components/ThingsModelForm'; | |
| 30 | +import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm'; | |
| 31 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 33 | 32 | |
| 34 | 33 | export enum FormFieldsEnum { |
| 35 | 34 | OUT_TARGET = 'outTarget', | ... | ... |
| ... | ... | @@ -5,9 +5,9 @@ import { FlipFlopConditionType } from '../FlipFlop/types'; |
| 5 | 5 | import { DeviceModelOfMatterAttrs, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; |
| 6 | 6 | import { FormActionType, ThingsModelForm } from '/@/components/Form'; |
| 7 | 7 | import { AlarmLevelEnum } from '/@/enums/alarmEnum'; |
| 8 | -import { CommandTypeEnum, ExecutionActionEnum, TriggerEntityTypeEnum } from '/@/enums/linkedgeEnum'; | |
| 9 | -import { ServiceCallTypeEnum } from '/@/enums/toolEnum'; | |
| 10 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 8 | +import { ExecutionActionEnum, TriggerEntityTypeEnum } from '/@/enums/linkedgeEnum'; | |
| 9 | +import { CommandTypeEnum, ServiceCallTypeEnum } from '/@/enums/deviceEnum'; | |
| 10 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 11 | 11 | |
| 12 | 12 | export interface ExecutionActionListRefItemType { |
| 13 | 13 | key: string; | ... | ... |
| ... | ... | @@ -7,12 +7,13 @@ import { |
| 7 | 7 | ExecutionActionFormItemRecordType, |
| 8 | 8 | ExecutionActionListRefItemType, |
| 9 | 9 | } from './type'; |
| 10 | -import { CommandTypeEnum, ExecutionActionEnum } from '/@/enums/linkedgeEnum'; | |
| 11 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 10 | +import { ExecutionActionEnum } from '/@/enums/linkedgeEnum'; | |
| 11 | +import { CommandTypeEnum } from '/@/enums/deviceEnum'; | |
| 12 | 12 | import { useJsonParse } from '/@/hooks/business/useJsonParse'; |
| 13 | 13 | import { createNewExecutionActionItem } from '.'; |
| 14 | 14 | import { FormFieldsEnum } from './config'; |
| 15 | 15 | import { isObject, isString } from '/@/utils/is'; |
| 16 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 16 | 17 | |
| 17 | 18 | export function useExecutionActionData( |
| 18 | 19 | executionActionListRef: Ref<ExecutionActionListRefItemType[]>, | ... | ... |
| ... | ... | @@ -27,11 +27,11 @@ import { DeviceModelOfMatterAttrs, DeviceProfileModel } from '/@/api/device/mode |
| 27 | 27 | import TriggerDurationInput from './TriggerDurationInput.vue'; |
| 28 | 28 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 29 | 29 | import { getModelTsl } from '/@/api/device/modelOfMatter'; |
| 30 | -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | |
| 30 | +import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
| 31 | 31 | import { GetModelTslParams } from '/@/api/device/model/modelOfMatterModel'; |
| 32 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 33 | 32 | import { FlipFlopComponentTypeEnum } from './types'; |
| 34 | 33 | import { OptionsType } from 'ant-design-vue/es/vc-select/interface'; |
| 34 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 35 | 35 | |
| 36 | 36 | export enum FormFieldEnum { |
| 37 | 37 | FLIP_FLOP_TYPE = 'flipFlopType', |
| ... | ... | @@ -77,6 +77,7 @@ function getTriggerValueTypeByThingsModelType(type: DataTypeEnum) { |
| 77 | 77 | [DataTypeEnum.NUMBER_INT]: TriggerValueTypeEnum.NUMERIC, |
| 78 | 78 | [DataTypeEnum.STRING]: TriggerValueTypeEnum.STRING, |
| 79 | 79 | [DataTypeEnum.STRUCT]: TriggerValueTypeEnum.STRING, |
| 80 | + [DataTypeEnum.ENUM]: TriggerValueTypeEnum.NUMERIC, | |
| 80 | 81 | }; |
| 81 | 82 | |
| 82 | 83 | return map[type]; |
| ... | ... | @@ -405,7 +406,7 @@ export const getFormSchemas = ( |
| 405 | 406 | labelField: 'functionName', |
| 406 | 407 | valueField: 'identifier', |
| 407 | 408 | params: { |
| 408 | - functionType: FunctionType.EVENTS, | |
| 409 | + functionType: FunctionTypeEnum.EVENTS, | |
| 409 | 410 | deviceProfileId: formModel[FormFieldEnum.DEVICE_PROFILE_ID], |
| 410 | 411 | }, |
| 411 | 412 | placeholder: `请选择${FormFieldNameEnum.DEVICE_EVENT_TRIGGER_KEY}`, |
| ... | ... | @@ -419,6 +420,7 @@ export const getFormSchemas = ( |
| 419 | 420 | colProps: { span: 24, xxl: 24, xl: 24, lg: 24, md: 24, sm: 24, xs: 24 }, |
| 420 | 421 | ifShow: ({ model }) => |
| 421 | 422 | model[FormFieldEnum.CONDITION_TYPE] === DeviceTriggerTypeEum.TIME_SERIES && |
| 423 | + // model[FormFieldEnum.CONDITION_KEY_DETAIL] && | |
| 422 | 424 | model[FormFieldEnum.CONDITION_KEY] && |
| 423 | 425 | model[FormFieldEnum.CONDITION_VALUE_TYPE], |
| 424 | 426 | colSlot: 'conditionFilter', | ... | ... |
| 1 | -import { ref, unref } from 'vue'; | |
| 1 | +import { ref } from 'vue'; | |
| 2 | 2 | import { BasicColumn, FormSchema } from '/@/components/Table'; |
| 3 | -import { | |
| 4 | - byOrganizationIdGetMasterDevice, | |
| 5 | - screenLinkOrganizationGetApi, | |
| 6 | - getAttribute, | |
| 7 | -} from '/@/api/ruleengine/ruleengineApi'; | |
| 8 | -import { scheduleOptions } from './formatData'; | |
| 3 | +import { screenLinkOrganizationGetApi } from '/@/api/ruleengine/ruleengineApi'; | |
| 9 | 4 | import { copyTransFun } from '/@/utils/fnUtils'; |
| 10 | -import { numberAndNonegativeRule } from '/@/utils/rules'; | |
| 11 | -import { findDictItemByCode } from '/@/api/system/dict'; | |
| 12 | -import { queryDeviceProfileBy } from '/@/api/device/deviceManager'; | |
| 13 | -import { getModelServices } from '/@/api/device/modelOfMatter'; | |
| 14 | -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | |
| 15 | -import useCommonFun from '../hooks/useCommonFun'; | |
| 16 | -import { DeviceProfileModel } from '/@/api/device/model/deviceModel'; | |
| 17 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 18 | 5 | import { useComponentRegister } from '/@/components/Form'; |
| 19 | 6 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; |
| 20 | -import { isArray } from '/@/utils/is'; | |
| 21 | -import { ReadAndWriteEnum } from '/@/enums/toolEnum'; | |
| 22 | 7 | |
| 23 | 8 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
| 24 | 9 | |
| 25 | -const { useByProductGetAttribute } = useCommonFun(); | |
| 26 | 10 | export type TOption = { |
| 27 | 11 | label: string; |
| 28 | 12 | value: string; |
| 29 | 13 | }; |
| 30 | 14 | |
| 31 | -export enum CommandTypeEnum { | |
| 32 | - CUSTOM = 0, | |
| 33 | - SERVICE = 1, | |
| 34 | - ATTRIBUTE = 2, | |
| 35 | -} | |
| 36 | - | |
| 37 | 15 | /** |
| 38 | 16 | * 所使用的枚举值 |
| 39 | 17 | */ |
| ... | ... | @@ -174,733 +152,3 @@ export const searchFormSchema: FormSchema[] = [ |
| 174 | 152 | colProps: { span: 6 }, |
| 175 | 153 | }, |
| 176 | 154 | ]; |
| 177 | -// 持续时间 | |
| 178 | -const isTimeDuration = (type) => { | |
| 179 | - return type === 'DURATION'; | |
| 180 | -}; | |
| 181 | -const isReplace = (type) => { | |
| 182 | - return type === 'REPEATING'; | |
| 183 | -}; | |
| 184 | -// 部分 | |
| 185 | -const isPart = (type: string) => { | |
| 186 | - return type === 'PART'; | |
| 187 | -}; | |
| 188 | -//新增代码2022-11-23 | |
| 189 | -const isPartOrAll = (type: string) => { | |
| 190 | - return type === 'ALL' || type === 'PART'; | |
| 191 | -}; | |
| 192 | -const isService = (type: string) => { | |
| 193 | - return Number(type) === 1; | |
| 194 | -}; | |
| 195 | -const isDefine = (type: string) => { | |
| 196 | - return Number(type) === 0; | |
| 197 | -}; | |
| 198 | -//新增代码2022-11-23 | |
| 199 | - | |
| 200 | -export const trigger_condition_schema: FormSchema[] = [ | |
| 201 | - { | |
| 202 | - field: 'triggered', | |
| 203 | - label: '', | |
| 204 | - component: 'Select', | |
| 205 | - defaultValue: 'SIMPLE', | |
| 206 | - componentProps: { | |
| 207 | - placeholder: '请选择触发类型', | |
| 208 | - options: [ | |
| 209 | - { label: '简单', value: 'SIMPLE' }, | |
| 210 | - { label: '持续时长', value: 'DURATION' }, | |
| 211 | - { label: '重复次数', value: 'REPEATING' }, | |
| 212 | - ], | |
| 213 | - }, | |
| 214 | - colProps: { span: 6 }, | |
| 215 | - }, | |
| 216 | - { | |
| 217 | - field: 'deviceType', | |
| 218 | - label: '', | |
| 219 | - component: 'ApiSelect', | |
| 220 | - colProps: { span: 6 }, | |
| 221 | - defaultValue: 'SENSOR', | |
| 222 | - componentProps: ({ formActionType }) => { | |
| 223 | - const { setFieldsValue } = formActionType; | |
| 224 | - return { | |
| 225 | - api: findDictItemByCode, | |
| 226 | - params: { | |
| 227 | - dictCode: 'device_type', | |
| 228 | - }, | |
| 229 | - placeholder: '请选择类型', | |
| 230 | - labelField: 'itemText', | |
| 231 | - valueField: 'itemValue', | |
| 232 | - getPopupContainer: (triggerNode) => triggerNode.parentNode, | |
| 233 | - onChange(e) { | |
| 234 | - if (e) { | |
| 235 | - setFieldsValue({ deviceProfileId: '' }); | |
| 236 | - } | |
| 237 | - }, | |
| 238 | - }; | |
| 239 | - }, | |
| 240 | - }, | |
| 241 | - { | |
| 242 | - field: 'deviceProfileId', | |
| 243 | - label: '', | |
| 244 | - component: 'ApiSelect', | |
| 245 | - colProps: { span: 6 }, | |
| 246 | - componentProps: ({ formActionType, formModel }) => { | |
| 247 | - const { updateSchema, setFieldsValue } = formActionType; | |
| 248 | - const deviceType = formModel['deviceType']; | |
| 249 | - | |
| 250 | - return { | |
| 251 | - api: queryDeviceProfileBy, | |
| 252 | - params: { | |
| 253 | - deviceType, | |
| 254 | - }, | |
| 255 | - showSearch: true, | |
| 256 | - placeholder: '请选择产品', | |
| 257 | - labelField: 'name', | |
| 258 | - valueField: 'id', | |
| 259 | - getPopupContainer: (triggerNode) => triggerNode.parentNode, | |
| 260 | - onChange: async (e) => { | |
| 261 | - if (e) { | |
| 262 | - setFieldsValue({ type2: '', entityId: [] }); | |
| 263 | - const res = await getAttribute(e); | |
| 264 | - const options = ref<TOption[]>([]); | |
| 265 | - useByProductGetAttribute(res, updateSchema, options); | |
| 266 | - } | |
| 267 | - }, | |
| 268 | - filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | |
| 269 | - let { label, value } = option; | |
| 270 | - label = label.toLowerCase(); | |
| 271 | - value = value.toLowerCase(); | |
| 272 | - inputValue = inputValue.toLowerCase(); | |
| 273 | - return label.includes(inputValue) || value.includes(inputValue); | |
| 274 | - }, | |
| 275 | - }; | |
| 276 | - }, | |
| 277 | - }, | |
| 278 | - { | |
| 279 | - field: 'device', | |
| 280 | - label: '', | |
| 281 | - component: 'Select', | |
| 282 | - componentProps: { | |
| 283 | - placeholder: '请选择设备', | |
| 284 | - options: [ | |
| 285 | - { label: '全部', value: 'ALL' }, | |
| 286 | - { label: '部分', value: 'PART' }, | |
| 287 | - ], | |
| 288 | - }, | |
| 289 | - colProps: { span: 6 }, | |
| 290 | - }, | |
| 291 | - | |
| 292 | - { | |
| 293 | - field: 'entityId', | |
| 294 | - label: '', | |
| 295 | - component: 'ApiSelect', | |
| 296 | - componentProps: ({ formModel }) => { | |
| 297 | - const deviceProfileId = formModel['deviceProfileId']; | |
| 298 | - return { | |
| 299 | - mode: 'multiple', | |
| 300 | - api: async () => { | |
| 301 | - if (unref(organizationId)) { | |
| 302 | - try { | |
| 303 | - const data = await byOrganizationIdGetMasterDevice({ | |
| 304 | - organizationId: unref(organizationId), | |
| 305 | - deviceProfileId, | |
| 306 | - }); | |
| 307 | - if (data) | |
| 308 | - return data.map((item) => ({ | |
| 309 | - ...item, | |
| 310 | - label: item.alias || item.name, | |
| 311 | - value: item.tbDeviceId, | |
| 312 | - })); | |
| 313 | - } catch (error) {} | |
| 314 | - } | |
| 315 | - return []; | |
| 316 | - }, | |
| 317 | - placeholder: '请选择设备', | |
| 318 | - getPopupContainer: (triggerNode) => triggerNode.parentNode, | |
| 319 | - filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | |
| 320 | - let { label, value } = option; | |
| 321 | - label = label.toLowerCase(); | |
| 322 | - value = value.toLowerCase(); | |
| 323 | - inputValue = inputValue.toLowerCase(); | |
| 324 | - return label.includes(inputValue) || value.includes(inputValue); | |
| 325 | - }, | |
| 326 | - }; | |
| 327 | - }, | |
| 328 | - ifShow: ({ values }) => isPart(values.device), | |
| 329 | - colProps: { span: 6 }, | |
| 330 | - }, | |
| 331 | - | |
| 332 | - { | |
| 333 | - field: 'time', | |
| 334 | - label: '', | |
| 335 | - component: 'Input', | |
| 336 | - ifShow: ({ values }) => isTimeDuration(values.triggered), | |
| 337 | - colProps: { span: 6 }, | |
| 338 | - slot: 'time', | |
| 339 | - rules: numberAndNonegativeRule, | |
| 340 | - }, | |
| 341 | - { | |
| 342 | - field: 'timeUnit', | |
| 343 | - label: '', | |
| 344 | - component: 'Select', | |
| 345 | - defaultValue: 'SECONDS', | |
| 346 | - ifShow: ({ values }) => isTimeDuration(values.triggered), | |
| 347 | - show: false, | |
| 348 | - }, | |
| 349 | - { | |
| 350 | - field: 'replaceValue', | |
| 351 | - label: '', | |
| 352 | - component: 'Input', | |
| 353 | - componentProps: { | |
| 354 | - placeholder: '事件计数值', | |
| 355 | - }, | |
| 356 | - ifShow: ({ values }) => isReplace(values.triggered), | |
| 357 | - colProps: { span: 6 }, | |
| 358 | - rules: numberAndNonegativeRule, | |
| 359 | - }, | |
| 360 | - { | |
| 361 | - field: 'triggerType', | |
| 362 | - label: '', | |
| 363 | - component: 'Select', | |
| 364 | - componentProps: { | |
| 365 | - placeholder: '设备触发', | |
| 366 | - options: [ | |
| 367 | - { label: '设备触发', value: 'DEVICE_TRIGGER' }, | |
| 368 | - // { label: '定时触发', value: 'SCHEDULE_TRIGGER' }, | |
| 369 | - // { label: '场景触发', value: 'SCENE_TRIGGER' }, | |
| 370 | - // { label: '手动触发', value: 'HAND_ACT' }, | |
| 371 | - ], | |
| 372 | - }, | |
| 373 | - colProps: { span: 6 }, | |
| 374 | - }, | |
| 375 | - | |
| 376 | - { | |
| 377 | - field: 'type1', | |
| 378 | - label: '', | |
| 379 | - component: 'Select', | |
| 380 | - componentProps: { | |
| 381 | - placeholder: '属性触发方式', | |
| 382 | - options: [{ label: '属性触发', value: 'TIME_SERIES' }], | |
| 383 | - }, | |
| 384 | - ifShow: ({ values }) => isDevice(values.triggerType), | |
| 385 | - colProps: { span: 6 }, | |
| 386 | - }, | |
| 387 | - { | |
| 388 | - field: 'type2', | |
| 389 | - label: '', | |
| 390 | - component: 'Select', | |
| 391 | - componentProps: { | |
| 392 | - placeholder: '请选择属性', | |
| 393 | - }, | |
| 394 | - ifShow: ({ values }) => isDevice(values.triggerType), | |
| 395 | - colProps: { span: 6 }, | |
| 396 | - // rules: numberAndEngLishRule, | |
| 397 | - }, | |
| 398 | - { | |
| 399 | - field: 'operationType', | |
| 400 | - label: '', | |
| 401 | - component: 'Select', | |
| 402 | - slot: 'operationType', | |
| 403 | - colProps: { span: 6 }, | |
| 404 | - }, | |
| 405 | - // { | |
| 406 | - // field: 'detail', | |
| 407 | - // label: '', | |
| 408 | - // component: 'InputTextArea', | |
| 409 | - // componentProps: { | |
| 410 | - // placeholder: '请输入详情', | |
| 411 | - // }, | |
| 412 | - // colProps: { | |
| 413 | - // span: 13, | |
| 414 | - // }, | |
| 415 | - // }, | |
| 416 | -]; | |
| 417 | -// !!!----------------------------------------------------^_^------------------------------------------------------------!!! | |
| 418 | -enum ActionEnum { | |
| 419 | - DEVICE_OUT = 'DEVICE_OUT', | |
| 420 | - ALARM_OUT = 'MSG_NOTIFY', | |
| 421 | -} | |
| 422 | -const isDeviceOut = (type: string) => type === ActionEnum.DEVICE_OUT; | |
| 423 | -const isAlarmOut = (type: string) => type === ActionEnum.ALARM_OUT; | |
| 424 | - | |
| 425 | -export const actionSchema: FormSchema[] = [ | |
| 426 | - { | |
| 427 | - field: 'outTarget', | |
| 428 | - label: '', | |
| 429 | - component: 'Select', | |
| 430 | - required: true, | |
| 431 | - componentProps: { | |
| 432 | - placeholder: '请选择执行动作', | |
| 433 | - }, | |
| 434 | - slot: 'outTarget', | |
| 435 | - colProps: { span: 6 }, | |
| 436 | - }, | |
| 437 | - { | |
| 438 | - field: 'deviceType', | |
| 439 | - label: '', | |
| 440 | - component: 'ApiSelect', | |
| 441 | - colProps: { span: 6 }, | |
| 442 | - defaultValue: 'SENSOR', | |
| 443 | - componentProps: ({ formActionType }) => { | |
| 444 | - const { setFieldsValue } = formActionType; | |
| 445 | - return { | |
| 446 | - api: findDictItemByCode, | |
| 447 | - params: { | |
| 448 | - dictCode: 'device_type', | |
| 449 | - }, | |
| 450 | - placeholder: '请选择类型', | |
| 451 | - labelField: 'itemText', | |
| 452 | - valueField: 'itemValue', | |
| 453 | - getPopupContainer: (triggerNode) => triggerNode.parentNode, | |
| 454 | - onChange(e) { | |
| 455 | - if (e) { | |
| 456 | - setFieldsValue({ deviceProfileId: '' }); | |
| 457 | - } | |
| 458 | - }, | |
| 459 | - }; | |
| 460 | - }, | |
| 461 | - ifShow: ({ values }) => isDeviceOut(values.outTarget), | |
| 462 | - }, | |
| 463 | - { | |
| 464 | - field: 'transportType', | |
| 465 | - label: '', | |
| 466 | - component: 'Input', | |
| 467 | - show: false, | |
| 468 | - }, | |
| 469 | - { | |
| 470 | - field: 'deviceProfileId', | |
| 471 | - label: '', | |
| 472 | - component: 'ApiSelect', | |
| 473 | - colProps: { span: 6 }, | |
| 474 | - componentProps: ({ formActionType, formModel }) => { | |
| 475 | - const { setFieldsValue } = formActionType; | |
| 476 | - const deviceType = formModel['deviceType']; | |
| 477 | - return { | |
| 478 | - api: queryDeviceProfileBy, | |
| 479 | - params: { | |
| 480 | - deviceType, | |
| 481 | - }, | |
| 482 | - placeholder: '请选择产品', | |
| 483 | - labelField: 'name', | |
| 484 | - valueField: 'id', | |
| 485 | - getPopupContainer: (triggerNode) => triggerNode.parentNode, | |
| 486 | - onChange: (_value: string, options = {} as DeviceProfileModel) => { | |
| 487 | - const oldType = formModel['transportType']; | |
| 488 | - | |
| 489 | - const updateFlag = | |
| 490 | - oldType === TransportTypeEnum.TCP || options.transportType === TransportTypeEnum.TCP; | |
| 491 | - | |
| 492 | - setFieldsValue({ | |
| 493 | - thingsModelId: '', | |
| 494 | - deviceId: [], | |
| 495 | - transportType: options.transportType, | |
| 496 | - ...(updateFlag ? { doContext: null } : {}), | |
| 497 | - }); | |
| 498 | - }, | |
| 499 | - onOptionsChange(options: DeviceProfileModel[]) { | |
| 500 | - const deviceProfileId = formModel['deviceProfileId']; | |
| 501 | - if (deviceProfileId) { | |
| 502 | - const index = options.findIndex( | |
| 503 | - (item) => (item as Recordable).value === deviceProfileId | |
| 504 | - ); | |
| 505 | - | |
| 506 | - ~index && setFieldsValue({ transportType: options[index].transportType }); | |
| 507 | - } | |
| 508 | - }, | |
| 509 | - }; | |
| 510 | - }, | |
| 511 | - ifShow: ({ values }) => isDeviceOut(values.outTarget), | |
| 512 | - }, | |
| 513 | - { | |
| 514 | - field: 'device', | |
| 515 | - label: '', | |
| 516 | - component: 'Select', | |
| 517 | - componentProps: ({ formActionType }) => { | |
| 518 | - const { setFieldsValue } = formActionType; | |
| 519 | - return { | |
| 520 | - placeholder: '请选择设备', | |
| 521 | - options: [ | |
| 522 | - { label: '全部', value: 'ALL' }, | |
| 523 | - { label: '部分', value: 'PART' }, | |
| 524 | - ], | |
| 525 | - onChange: () => { | |
| 526 | - setFieldsValue({ deviceId: [], thingsModelId: null }); | |
| 527 | - }, | |
| 528 | - }; | |
| 529 | - }, | |
| 530 | - ifShow: ({ values }) => isDeviceOut(values.outTarget) && values['deviceProfileId'], | |
| 531 | - colProps: { span: 6 }, | |
| 532 | - }, | |
| 533 | - { | |
| 534 | - field: 'deviceId', | |
| 535 | - label: '', | |
| 536 | - component: 'ApiSelect', | |
| 537 | - componentProps: ({ formModel }) => { | |
| 538 | - const deviceProfileId = formModel['deviceProfileId']; | |
| 539 | - return { | |
| 540 | - mode: 'multiple', | |
| 541 | - api: async () => { | |
| 542 | - if (unref(organizationId)) { | |
| 543 | - try { | |
| 544 | - const data = await byOrganizationIdGetMasterDevice({ | |
| 545 | - organizationId: unref(organizationId), | |
| 546 | - deviceProfileId, | |
| 547 | - }); | |
| 548 | - if (data) | |
| 549 | - return data.map((item) => ({ | |
| 550 | - ...item, | |
| 551 | - label: item.alias || item.name, | |
| 552 | - value: item.tbDeviceId, | |
| 553 | - })); | |
| 554 | - } catch (error) {} | |
| 555 | - } | |
| 556 | - return []; | |
| 557 | - }, | |
| 558 | - placeholder: '请选择设备', | |
| 559 | - getPopupContainer: (triggerNode) => triggerNode.parentNode, | |
| 560 | - }; | |
| 561 | - }, | |
| 562 | - ifShow: ({ values }) => isPart(values.device) && isDeviceOut(values.outTarget), | |
| 563 | - colProps: { span: 6 }, | |
| 564 | - }, | |
| 565 | - //新增代码2022-11-23 | |
| 566 | - { | |
| 567 | - field: 'commandType', | |
| 568 | - label: '', | |
| 569 | - required: true, | |
| 570 | - component: 'ApiSelect', | |
| 571 | - colProps: { | |
| 572 | - span: 6, | |
| 573 | - }, | |
| 574 | - defaultValue: '0', | |
| 575 | - componentProps: ({ formActionType }) => { | |
| 576 | - const { setFieldsValue } = formActionType; | |
| 577 | - return { | |
| 578 | - placeholder: '请选择类型', | |
| 579 | - api: async (parmas: Recordable) => { | |
| 580 | - try { | |
| 581 | - const record = await findDictItemByCode(parmas); | |
| 582 | - return record.filter( | |
| 583 | - (item) => | |
| 584 | - item.itemValue !== CommandTypeEnum.ATTRIBUTE.toString() || item.itemText != '属性' | |
| 585 | - ); | |
| 586 | - } catch (error) { | |
| 587 | - return []; | |
| 588 | - } | |
| 589 | - }, | |
| 590 | - labelField: 'itemText', | |
| 591 | - valueField: 'itemValue', | |
| 592 | - params: { | |
| 593 | - dictCode: 'custom_define', | |
| 594 | - }, | |
| 595 | - numberToString: true, | |
| 596 | - getPopupContainer: (triggerNode) => triggerNode.parentNode, | |
| 597 | - onChange: () => { | |
| 598 | - setFieldsValue({ doContext: null, thingsModelId: null }); | |
| 599 | - }, | |
| 600 | - }; | |
| 601 | - }, | |
| 602 | - ifShow: ({ values }) => isDeviceOut(values.outTarget) && isPartOrAll(values.device), | |
| 603 | - }, | |
| 604 | - //新增代码2022-11-23 | |
| 605 | - { | |
| 606 | - field: 'callType', | |
| 607 | - label: '', | |
| 608 | - required: true, | |
| 609 | - component: 'ApiSelect', | |
| 610 | - colProps: { | |
| 611 | - span: 6, | |
| 612 | - }, | |
| 613 | - defaultValue: 'SYNC', | |
| 614 | - componentProps: { | |
| 615 | - placeholder: '请选择类型', | |
| 616 | - api: findDictItemByCode, | |
| 617 | - params: { | |
| 618 | - dictCode: 'call_mode', | |
| 619 | - }, | |
| 620 | - labelField: 'itemText', | |
| 621 | - valueField: 'itemValue', | |
| 622 | - getPopupContainer: (triggerNode) => triggerNode.parentNode, | |
| 623 | - }, | |
| 624 | - ifShow: ({ values }) => | |
| 625 | - isDeviceOut(values.outTarget) && isPartOrAll(values.device) && isDefine(values.commandType), | |
| 626 | - }, | |
| 627 | - { | |
| 628 | - field: 'serviceIdentifier', | |
| 629 | - label: '', | |
| 630 | - component: 'Input', | |
| 631 | - show: false, | |
| 632 | - }, | |
| 633 | - { | |
| 634 | - field: 'thingsModelKeys', | |
| 635 | - label: '', | |
| 636 | - show: false, | |
| 637 | - component: 'Select', | |
| 638 | - componentProps: { | |
| 639 | - mode: 'multiple', | |
| 640 | - }, | |
| 641 | - }, | |
| 642 | - { | |
| 643 | - field: 'thingsModelId', | |
| 644 | - label: '', | |
| 645 | - required: true, | |
| 646 | - component: 'ApiSelect', | |
| 647 | - colProps: { | |
| 648 | - span: 6, | |
| 649 | - }, | |
| 650 | - componentProps: ({ formModel, formActionType }) => { | |
| 651 | - const { updateSchema, setFieldsValue } = formActionType; | |
| 652 | - const deviceProfileId = Reflect.get(formModel, 'deviceProfileId'); | |
| 653 | - const thingsModelId = Reflect.get(formModel, 'thingsModelId'); | |
| 654 | - const transportType = Reflect.get(formModel, 'transportType'); | |
| 655 | - return { | |
| 656 | - placeholder: '请选择服务', | |
| 657 | - api: async (params: Recordable) => { | |
| 658 | - try { | |
| 659 | - if (!Reflect.get(params, 'deviceProfileId')) return []; | |
| 660 | - const record = | |
| 661 | - (await getModelServices(params as Record<'deviceProfileId', string>)) || []; | |
| 662 | - const selected = record.find((item) => item.id === thingsModelId); | |
| 663 | - selected && | |
| 664 | - updateSchema({ | |
| 665 | - field: 'serviceInputValue', | |
| 666 | - componentProps: { | |
| 667 | - inputData: selected?.functionJson.inputData, | |
| 668 | - }, | |
| 669 | - }); | |
| 670 | - selected && | |
| 671 | - setFieldsValue({ | |
| 672 | - callType: selected.callType, | |
| 673 | - serviceIdentifier: selected?.identifier, | |
| 674 | - thingsModelKeys: isArray(selected?.functionJson?.inputData) | |
| 675 | - ? selected?.functionJson?.inputData | |
| 676 | - .filter((item) => item.accessMode === ReadAndWriteEnum.READ_AND_WRITE) | |
| 677 | - .map((item) => item.identifier) | |
| 678 | - : [], | |
| 679 | - }); | |
| 680 | - return record; | |
| 681 | - } catch (error) { | |
| 682 | - console.error(error); | |
| 683 | - return []; | |
| 684 | - } | |
| 685 | - }, | |
| 686 | - params: { | |
| 687 | - deviceProfileId, | |
| 688 | - }, | |
| 689 | - labelField: 'functionName', | |
| 690 | - valueField: 'id', | |
| 691 | - getPopupContainer: (triggerNode) => triggerNode.parentNode, | |
| 692 | - onChange: (_, options: ModelOfMatterParams) => { | |
| 693 | - if (options) { | |
| 694 | - // setFieldsValue({ doContext: { ...options.functionJson, callType: options.callType } }); | |
| 695 | - // if ( ) | |
| 696 | - const record = { | |
| 697 | - callType: options.callType, | |
| 698 | - serviceIdentifier: options.identifier, | |
| 699 | - thingsModelKeys: isArray(options?.functionJson?.inputData) | |
| 700 | - ? options?.functionJson?.inputData | |
| 701 | - .filter((item) => item.accessMode === ReadAndWriteEnum.READ_AND_WRITE) | |
| 702 | - .map((item) => item.identifier) | |
| 703 | - : [], | |
| 704 | - }; | |
| 705 | - transportType === TransportTypeEnum.TCP | |
| 706 | - ? Object.assign(record, { | |
| 707 | - tcpServiceCommand: options.functionJson?.inputData?.[0]?.serviceCommand, | |
| 708 | - }) | |
| 709 | - : updateSchema({ | |
| 710 | - field: 'serviceInputValue', | |
| 711 | - componentProps: { | |
| 712 | - inputData: options.functionJson.inputData, | |
| 713 | - }, | |
| 714 | - }); | |
| 715 | - | |
| 716 | - setFieldsValue(record); | |
| 717 | - } else { | |
| 718 | - setFieldsValue({ serviceIdentifier: null }); | |
| 719 | - } | |
| 720 | - }, | |
| 721 | - }; | |
| 722 | - }, | |
| 723 | - ifShow: ({ values }) => isDeviceOut(values.outTarget) && isService(values.commandType), | |
| 724 | - }, | |
| 725 | - //新增代码2022-11-23 | |
| 726 | - { | |
| 727 | - field: 'alarm_config', | |
| 728 | - label: '', | |
| 729 | - component: 'Input', | |
| 730 | - componentProps: { | |
| 731 | - placeholder: '请选择告警配置', | |
| 732 | - }, | |
| 733 | - slot: 'alarmConfigSlot', | |
| 734 | - ifShow: ({ values }) => values.outTarget === 'MSG_NOTIFY', | |
| 735 | - colProps: { span: 6 }, | |
| 736 | - }, | |
| 737 | - //新增代码2022-11-23 | |
| 738 | - { | |
| 739 | - field: 'doContext', | |
| 740 | - component: 'Input', | |
| 741 | - label: '', | |
| 742 | - slot: 'doContext', | |
| 743 | - show: ({ values }) => { | |
| 744 | - return ( | |
| 745 | - isDeviceOut(values.outTarget) && isPartOrAll(values.device) && isDefine(values.commandType) | |
| 746 | - ); | |
| 747 | - }, | |
| 748 | - colProps: { | |
| 749 | - span: 24, | |
| 750 | - }, | |
| 751 | - }, | |
| 752 | - //新增代码2022-11-23 | |
| 753 | - { | |
| 754 | - field: 'alarm_level', | |
| 755 | - component: 'Select', | |
| 756 | - label: '', | |
| 757 | - show: ({ values }) => isAlarmOut(values.outTarget), | |
| 758 | - // dynamicRules: (params) => { | |
| 759 | - // const { outTarget } = params.values; | |
| 760 | - // return [ | |
| 761 | - // { | |
| 762 | - // required: outTarget == 'MSG_NOTIFY' ? true : false, | |
| 763 | - // validator: (_, value) => { | |
| 764 | - // if (!value) return Promise.reject('请选择告警配置'); | |
| 765 | - // Promise.resolve(); | |
| 766 | - // }, | |
| 767 | - // }, | |
| 768 | - // ]; | |
| 769 | - // }, | |
| 770 | - componentProps: { | |
| 771 | - placeholder: '请选择告警等级', | |
| 772 | - options: [ | |
| 773 | - { | |
| 774 | - label: '紧急', | |
| 775 | - value: 'CRITICAL', | |
| 776 | - }, | |
| 777 | - { | |
| 778 | - label: '重要', | |
| 779 | - value: 'MAJOR', | |
| 780 | - }, | |
| 781 | - { | |
| 782 | - label: '次要', | |
| 783 | - value: 'MINOR', | |
| 784 | - }, | |
| 785 | - { | |
| 786 | - label: '警告', | |
| 787 | - value: 'WARNING', | |
| 788 | - }, | |
| 789 | - { | |
| 790 | - label: '不确定', | |
| 791 | - value: 'INDETERMINATE', | |
| 792 | - }, | |
| 793 | - ], | |
| 794 | - }, | |
| 795 | - colProps: { | |
| 796 | - span: 6, | |
| 797 | - }, | |
| 798 | - }, | |
| 799 | - { | |
| 800 | - field: 'clear_alarm', | |
| 801 | - component: 'Checkbox', | |
| 802 | - label: '', | |
| 803 | - show: ({ values }) => isAlarmOut(values.outTarget), | |
| 804 | - colProps: { | |
| 805 | - span: 8, | |
| 806 | - }, | |
| 807 | - slot: 'clearAlarm', | |
| 808 | - }, | |
| 809 | - { | |
| 810 | - field: 'tcpServiceCommand', | |
| 811 | - component: 'Input', | |
| 812 | - label: '服务命令', | |
| 813 | - colProps: { span: 24 }, | |
| 814 | - dynamicDisabled: true, | |
| 815 | - show: ({ values }) => | |
| 816 | - values['thingsModelId'] && values['transportType'] === TransportTypeEnum.TCP, | |
| 817 | - }, | |
| 818 | - { | |
| 819 | - field: 'serviceInputValue', | |
| 820 | - component: 'ObjectModelValidateForm', | |
| 821 | - label: '', | |
| 822 | - changeEvent: 'update:value', | |
| 823 | - valueField: 'value', | |
| 824 | - show: ({ values }) => | |
| 825 | - values['thingsModelId'] && values['transportType'] !== TransportTypeEnum.TCP, | |
| 826 | - colProps: { | |
| 827 | - span: 24, | |
| 828 | - }, | |
| 829 | - componentProps: {}, | |
| 830 | - }, | |
| 831 | -]; | |
| 832 | - | |
| 833 | -export const alarmScheduleSchemas: FormSchema[] = [ | |
| 834 | - { | |
| 835 | - field: 'schedule', | |
| 836 | - label: '', | |
| 837 | - component: 'Select', | |
| 838 | - componentProps({ formActionType }) { | |
| 839 | - const { resetFields } = formActionType; | |
| 840 | - return { | |
| 841 | - options: scheduleOptions, | |
| 842 | - allowClear: false, | |
| 843 | - onChange() { | |
| 844 | - resetFields(); | |
| 845 | - }, | |
| 846 | - }; | |
| 847 | - }, | |
| 848 | - }, | |
| 849 | - { | |
| 850 | - field: 'timezone', | |
| 851 | - label: '时区', | |
| 852 | - component: 'Select', | |
| 853 | - defaultValue: 'Asia/Shanghai', | |
| 854 | - componentProps: { | |
| 855 | - options: [ | |
| 856 | - { | |
| 857 | - label: 'Asia/Shanghai', | |
| 858 | - value: 'Asia/Shanghai', | |
| 859 | - }, | |
| 860 | - ], | |
| 861 | - allowClear: false, | |
| 862 | - }, | |
| 863 | - ifShow: ({ values }) => values.schedule !== 'ANY_TIME', | |
| 864 | - }, | |
| 865 | - { | |
| 866 | - field: 'daysOfWeek', | |
| 867 | - label: '天', | |
| 868 | - component: 'CheckboxGroup', | |
| 869 | - rules: [ | |
| 870 | - { | |
| 871 | - message: '请至少选择一天', | |
| 872 | - required: true, | |
| 873 | - }, | |
| 874 | - ], | |
| 875 | - componentProps: { | |
| 876 | - options: [ | |
| 877 | - { label: '星期一', value: 1 }, | |
| 878 | - { label: '星期二', value: 2 }, | |
| 879 | - { label: '星期三', value: 3 }, | |
| 880 | - { label: '星期四', value: 4 }, | |
| 881 | - { label: '星期五', value: 5 }, | |
| 882 | - { label: '星期六', value: 6 }, | |
| 883 | - { label: '星期日', value: 7 }, | |
| 884 | - ], | |
| 885 | - }, | |
| 886 | - colProps: { | |
| 887 | - span: 24, | |
| 888 | - }, | |
| 889 | - ifShow: ({ values }) => values.schedule === 'SPECIFIC_TIME', | |
| 890 | - }, | |
| 891 | - { | |
| 892 | - field: 'time', | |
| 893 | - label: '时间', | |
| 894 | - labelWidth: 40, | |
| 895 | - component: 'RangePicker', | |
| 896 | - ifShow: ({ values }) => values.schedule === 'SPECIFIC_TIME', | |
| 897 | - slot: 'timing', | |
| 898 | - }, | |
| 899 | - { | |
| 900 | - field: 'day', | |
| 901 | - label: '天', | |
| 902 | - component: 'CheckboxGroup', | |
| 903 | - slot: 'customEnable', | |
| 904 | - ifShow: ({ values }) => values.schedule === 'CUSTOM', | |
| 905 | - }, | |
| 906 | -]; | ... | ... |
| ... | ... | @@ -8,12 +8,12 @@ import { |
| 8 | 8 | } from '../DevicePicker'; |
| 9 | 9 | import { PollCommandInput, ModeEnum } from '../PollCommandInput'; |
| 10 | 10 | import { DeviceProfileModel } from '/@/api/device/model/deviceModel'; |
| 11 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 12 | 11 | import { JSONEditorValidator } from '/@/components/CodeEditor/src/JSONEditor'; |
| 13 | 12 | import { BooleanStringEnum, TimeUnitEnum, TimeUnitNameEnum } from '/@/enums/toolEnum'; |
| 14 | 13 | import { dateUtil } from '/@/utils/dateUtil'; |
| 15 | 14 | import { ProductPicker, validateProductPicker } from '../ProductPicker'; |
| 16 | 15 | import { useGlobSetting } from '/@/hooks/setting'; |
| 16 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 17 | 17 | |
| 18 | 18 | useComponentRegister('DevicePicker', DevicePicker); |
| 19 | 19 | useComponentRegister('ProductPicker', ProductPicker); | ... | ... |
| ... | ... | @@ -37,5 +37,15 @@ |
| 37 | 37 | </script> |
| 38 | 38 | |
| 39 | 39 | <template> |
| 40 | - <BasicForm @register="register" /> | |
| 40 | + <BasicForm @register="register" class="data-board-source-form" /> | |
| 41 | 41 | </template> |
| 42 | + | |
| 43 | +<style lang="less" scoped> | |
| 44 | + .data-board-source-form { | |
| 45 | + :deep(.ant-form-item-control-input-content) { | |
| 46 | + > div > div { | |
| 47 | + width: 100%; | |
| 48 | + } | |
| 49 | + } | |
| 50 | + } | |
| 51 | +</style> | ... | ... |
| ... | ... | @@ -197,8 +197,7 @@ |
| 197 | 197 | <label class="w-24 text-right pr-2">数据源{{ index + 1 }}</label> |
| 198 | 198 | <component |
| 199 | 199 | :ref="(event) => setDataSourceFormsEl(item.uuid, event, index)" |
| 200 | - class="flex-1 bg-light-50 dark:bg-dark-400" | |
| 201 | - style="max-width: calc(100% - 216px)" | |
| 200 | + class="flex-1 bg-light-50 dark:bg-dark-400 data-board-source-form" | |
| 202 | 201 | :is="getComponent" |
| 203 | 202 | :component-config="componentConfig" |
| 204 | 203 | :values="item" |
| ... | ... | @@ -238,9 +237,11 @@ |
| 238 | 237 | </template> |
| 239 | 238 | |
| 240 | 239 | <style scoped lang="less"> |
| 241 | - :deep(#deviceId) { | |
| 242 | - div { | |
| 243 | - width: 100%; | |
| 240 | + .data-board-source-form { | |
| 241 | + :deep(.ant-form-item-control-input-content) { | |
| 242 | + > div > div { | |
| 243 | + width: 100%; | |
| 244 | + } | |
| 244 | 245 | } |
| 245 | 246 | } |
| 246 | 247 | </style> | ... | ... |
| 1 | 1 | <script lang="ts" setup> |
| 2 | 2 | import { computed } from 'vue'; |
| 3 | - import { PackagesCategoryEnum } from '../../../packages/index.type'; | |
| 4 | 3 | import { SelectedWidgetKeys } from '../../index.type'; |
| 5 | 4 | import { Alert } from 'ant-design-vue'; |
| 6 | 5 | |
| ... | ... | @@ -9,10 +8,10 @@ |
| 9 | 8 | }>(); |
| 10 | 9 | |
| 11 | 10 | const alert = { |
| 12 | - [PackagesCategoryEnum.MAP]: [ | |
| 13 | - '1、绑定数据源为结构体时,可以自行选择结构体里的属性作为经纬度', | |
| 14 | - '2、绑定数据源为非结构体时,第一数据源为经度,第二数据源为纬度,且数据源为同一设备,并同时上报。否则地图组件不能正常显示。', | |
| 15 | - ], | |
| 11 | + // [PackagesCategoryEnum.MAP]: [ | |
| 12 | + // '1、绑定数据源为结构体时,可以自行选择结构体里的属性作为经纬度', | |
| 13 | + // '2、绑定数据源为非结构体时,第一数据源为经度,第二数据源为纬度,且数据源为同一设备,并同时上报。否则地图组件不能正常显示。', | |
| 14 | + // ], | |
| 16 | 15 | }; |
| 17 | 16 | |
| 18 | 17 | const getMessage = computed(() => { | ... | ... |
| 1 | 1 | <script lang="ts" setup> |
| 2 | - import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; | |
| 2 | + import { ComponentMode, ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; | |
| 3 | 3 | import { option } from './config'; |
| 4 | 4 | import { Spin } from 'ant-design-vue'; |
| 5 | 5 | import { computed, ref, unref } from 'vue'; |
| 6 | 6 | import { useComponentScale } from '../../../hook/useComponentScale'; |
| 7 | - import { useSendCommand } from '../../../hook/useSendCommand'; | |
| 8 | 7 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
| 9 | 8 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 10 | 9 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
| 11 | - import { getSendValues } from '../config'; | |
| 12 | 10 | import { useModal } from '/@/components/Modal'; |
| 13 | 11 | import PasswordModal from '../component/PasswordModal.vue'; |
| 12 | + import { useCommandDelivery } from '../../../hook/useCommandDelivery'; | |
| 13 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 14 | 14 | |
| 15 | 15 | const props = defineProps<{ |
| 16 | 16 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -20,48 +20,31 @@ |
| 20 | 20 | |
| 21 | 21 | const currentValue = ref(false); |
| 22 | 22 | |
| 23 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 24 | + | |
| 23 | 25 | const getDesign = computed(() => { |
| 24 | 26 | const { option, persetOption } = props.config; |
| 25 | - const { | |
| 26 | - attribute, | |
| 27 | - attributeRename, | |
| 28 | - attributeName, | |
| 29 | - componentInfo, | |
| 30 | - commandType, | |
| 31 | - extensionDesc, | |
| 32 | - codeType, | |
| 33 | - deviceCode, | |
| 34 | - customCommand, | |
| 35 | - } = option; | |
| 27 | + const { attribute, attributeRename, componentInfo, commandType, deviceProfileId } = option; | |
| 36 | 28 | |
| 37 | 29 | const { fontSize: persetFontSize, password: persetPassword } = persetOption || {}; |
| 38 | 30 | const { fontSize, password } = componentInfo || {}; |
| 31 | + | |
| 32 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 39 | 33 | return { |
| 40 | - attribute: attributeRename || attributeName || attribute, | |
| 34 | + attribute: attributeRename || tsl?.functionName || attribute, | |
| 41 | 35 | fontSize: fontSize || persetFontSize || 14, |
| 42 | 36 | password: password || persetPassword, |
| 43 | - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, | |
| 44 | 37 | commandType, |
| 45 | - codeType, | |
| 46 | - deviceCode, | |
| 47 | - customCommand, | |
| 48 | 38 | }; |
| 49 | 39 | }); |
| 50 | 40 | |
| 51 | - const { sendCommand, loading } = useSendCommand(); | |
| 41 | + const { doCommandDeliver, loading } = useCommandDelivery(); | |
| 52 | 42 | |
| 53 | - const handleSendCommand = async (data) => { | |
| 54 | - const { control: event } = data || {}; | |
| 55 | - const target = event.target as HTMLInputElement; | |
| 56 | - const value = !target.checked; | |
| 43 | + const handleSendCommand = async () => { | |
| 44 | + if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; | |
| 57 | 45 | const { option } = props.config || {}; |
| 58 | - | |
| 59 | - const { values, isModbusCommand, sendValue } = | |
| 60 | - (await getSendValues(option, unref(getDesign), value)) || {}; | |
| 61 | - | |
| 62 | - const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand); | |
| 63 | - if (flag) currentValue.value = value; | |
| 64 | - flag ? (currentValue.value = value) : (target.checked = !value); | |
| 46 | + const result = await doCommandDeliver(option, Number(!unref(currentValue))); | |
| 47 | + currentValue.value = result ? !unref(currentValue) : unref(currentValue); | |
| 65 | 48 | }; |
| 66 | 49 | |
| 67 | 50 | const handleChange = async (event: Event) => { |
| ... | ... | @@ -74,16 +57,7 @@ |
| 74 | 57 | return; |
| 75 | 58 | } |
| 76 | 59 | |
| 77 | - const target = event.target as HTMLInputElement; | |
| 78 | - const value = target.checked; | |
| 79 | - const { option } = props.config || {}; | |
| 80 | - | |
| 81 | - const { values, isModbusCommand, sendValue } = | |
| 82 | - (await getSendValues(option, unref(getDesign), value)) || {}; | |
| 83 | - | |
| 84 | - const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand); | |
| 85 | - if (flag) currentValue.value = value; | |
| 86 | - flag ? (currentValue.value = value) : (target.checked = !value); | |
| 60 | + handleSendCommand(); | |
| 87 | 61 | }; |
| 88 | 62 | |
| 89 | 63 | const updateFn: DataFetchUpdateFn = (message, attribute) => { | ... | ... |
| 1 | 1 | <script lang="ts" setup> |
| 2 | - import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; | |
| 2 | + import { ComponentMode, ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; | |
| 3 | 3 | import { option } from './config'; |
| 4 | 4 | import { SvgIcon } from '/@/components/Icon'; |
| 5 | 5 | import { Switch } from 'ant-design-vue'; |
| 6 | 6 | import { computed, ref } from 'vue'; |
| 7 | 7 | import { useComponentScale } from '../../../hook/useComponentScale'; |
| 8 | - import { useSendCommand } from '../../../hook/useSendCommand'; | |
| 9 | 8 | import { unref } from 'vue'; |
| 10 | 9 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
| 11 | 10 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 12 | 11 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
| 13 | - import { getSendValues } from '../config'; | |
| 14 | 12 | import { useModal } from '/@/components/Modal'; |
| 15 | 13 | import PasswordModal from '../component/PasswordModal.vue'; |
| 14 | + import { useCommandDelivery } from '../../../hook/useCommandDelivery'; | |
| 15 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 16 | 16 | |
| 17 | 17 | const props = defineProps<{ |
| 18 | 18 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -20,19 +20,11 @@ |
| 20 | 20 | |
| 21 | 21 | const checked = ref(false); |
| 22 | 22 | |
| 23 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 24 | + | |
| 23 | 25 | const getDesign = computed(() => { |
| 24 | 26 | const { option, persetOption } = props.config; |
| 25 | - const { | |
| 26 | - componentInfo, | |
| 27 | - attribute, | |
| 28 | - attributeRename, | |
| 29 | - attributeName, | |
| 30 | - commandType, | |
| 31 | - extensionDesc, | |
| 32 | - codeType, | |
| 33 | - deviceCode, | |
| 34 | - customCommand, | |
| 35 | - } = option; | |
| 27 | + const { componentInfo, attribute, deviceProfileId, attributeRename, commandType } = option; | |
| 36 | 28 | const { |
| 37 | 29 | icon: presetIcon, |
| 38 | 30 | iconColor: presetIconColor, |
| ... | ... | @@ -41,21 +33,18 @@ |
| 41 | 33 | } = persetOption || {}; |
| 42 | 34 | const { icon, iconColor, fontSize, password } = componentInfo || {}; |
| 43 | 35 | |
| 36 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 44 | 37 | return { |
| 45 | 38 | icon: icon ?? presetIcon, |
| 46 | 39 | iconColor: iconColor || presetIconColor, |
| 47 | - attribute: attributeRename || attributeName || attribute, | |
| 48 | - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, | |
| 40 | + attribute: attributeRename || tsl?.functionName || attribute, | |
| 49 | 41 | fontSize: fontSize || persetFontSize || 14, |
| 50 | 42 | password: password || persetPassword, |
| 51 | 43 | commandType, |
| 52 | - codeType, | |
| 53 | - deviceCode, | |
| 54 | - customCommand, | |
| 55 | 44 | }; |
| 56 | 45 | }); |
| 57 | 46 | |
| 58 | - const { sendCommand, loading } = useSendCommand(); | |
| 47 | + const { loading, doCommandDeliver } = useCommandDelivery(); | |
| 59 | 48 | |
| 60 | 49 | const handleChange = async () => { |
| 61 | 50 | if (unref(getDesign).password) { |
| ... | ... | @@ -63,32 +52,14 @@ |
| 63 | 52 | checked.value = !unref(checked); |
| 64 | 53 | return; |
| 65 | 54 | } |
| 66 | - const { option } = props.config || {}; | |
| 67 | - | |
| 68 | - const { values, isModbusCommand, sendValue } = | |
| 69 | - (await getSendValues(option, unref(getDesign), unref(checked))) || {}; | |
| 70 | - | |
| 71 | - const flag = await sendCommand( | |
| 72 | - values, | |
| 73 | - isModbusCommand ? sendValue : unref(checked), | |
| 74 | - isModbusCommand | |
| 75 | - ); | |
| 76 | - if (!flag) checked.value = !unref(checked); | |
| 55 | + handleSendCommand(); | |
| 77 | 56 | }; |
| 78 | 57 | |
| 79 | 58 | const handleSendCommand = async () => { |
| 59 | + if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; | |
| 80 | 60 | const { option } = props.config || {}; |
| 81 | - checked.value = !unref(checked); | |
| 82 | - | |
| 83 | - const { values, isModbusCommand, sendValue } = | |
| 84 | - (await getSendValues(option, unref(getDesign), unref(checked))) || {}; | |
| 85 | - | |
| 86 | - const flag = await sendCommand( | |
| 87 | - values, | |
| 88 | - isModbusCommand ? sendValue : unref(checked), | |
| 89 | - isModbusCommand | |
| 90 | - ); | |
| 91 | - if (!flag) checked.value = !unref(checked); | |
| 61 | + const result = await doCommandDeliver(option, Number(unref(checked))); | |
| 62 | + if (!result) checked.value = !unref(checked); | |
| 92 | 63 | }; |
| 93 | 64 | |
| 94 | 65 | const updateFn: DataFetchUpdateFn = (message, attribute) => { | ... | ... |
| 1 | 1 | <script lang="ts" setup> |
| 2 | - import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; | |
| 2 | + import { ComponentMode, ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; | |
| 3 | 3 | import { option } from './config'; |
| 4 | 4 | import { Spin } from 'ant-design-vue'; |
| 5 | 5 | import { computed, ref, unref } from 'vue'; |
| 6 | 6 | import { useComponentScale } from '../../../hook/useComponentScale'; |
| 7 | - import { useSendCommand } from '../../../hook/useSendCommand'; | |
| 8 | 7 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
| 9 | 8 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 10 | 9 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
| 11 | - import { getSendValues } from '../config'; | |
| 12 | 10 | import PasswordModal from '../component/PasswordModal.vue'; |
| 13 | 11 | import { useModal } from '/@/components/Modal'; |
| 12 | + import { useCommandDelivery } from '../../../hook/useCommandDelivery'; | |
| 13 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 14 | 14 | |
| 15 | 15 | const props = defineProps<{ |
| 16 | 16 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -20,34 +20,31 @@ |
| 20 | 20 | |
| 21 | 21 | const currentValue = ref(false); |
| 22 | 22 | |
| 23 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 24 | + | |
| 23 | 25 | const getDesign = computed(() => { |
| 24 | 26 | const { option, persetOption } = props.config; |
| 25 | - const { | |
| 26 | - attribute, | |
| 27 | - attributeRename, | |
| 28 | - attributeName, | |
| 29 | - commandType, | |
| 30 | - extensionDesc, | |
| 31 | - codeType, | |
| 32 | - deviceCode, | |
| 33 | - customCommand, | |
| 34 | - componentInfo, | |
| 35 | - } = option; | |
| 27 | + const { attribute, deviceProfileId, attributeRename, commandType, componentInfo } = option; | |
| 36 | 28 | const { fontSize: persetFontSize, password: persetPassword } = persetOption || {}; |
| 37 | 29 | const { fontSize, password } = componentInfo || {}; |
| 30 | + | |
| 31 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 32 | + | |
| 38 | 33 | return { |
| 39 | - attribute: attributeRename || attributeName || attribute, | |
| 40 | - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, | |
| 34 | + attribute: attributeRename || tsl?.functionName || attribute, | |
| 41 | 35 | password: password || persetPassword, |
| 42 | 36 | commandType, |
| 43 | - codeType, | |
| 44 | - deviceCode, | |
| 45 | - customCommand, | |
| 46 | 37 | fontSize: fontSize || persetFontSize || 14, |
| 47 | 38 | }; |
| 48 | 39 | }); |
| 40 | + const { doCommandDeliver, loading } = useCommandDelivery(); | |
| 49 | 41 | |
| 50 | - const { loading, sendCommand } = useSendCommand(); | |
| 42 | + const handleSendCommand = async () => { | |
| 43 | + if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; | |
| 44 | + const { option } = props.config || {}; | |
| 45 | + const result = await doCommandDeliver(option, Number(!unref(currentValue))); | |
| 46 | + currentValue.value = result ? !unref(currentValue) : unref(currentValue); | |
| 47 | + }; | |
| 51 | 48 | const handleChange = async (event: Event) => { |
| 52 | 49 | if (unref(getDesign).password) { |
| 53 | 50 | const target = event.target as HTMLInputElement; |
| ... | ... | @@ -57,29 +54,7 @@ |
| 57 | 54 | openModal(true, { password: unref(getDesign).password, control: event }); |
| 58 | 55 | return; |
| 59 | 56 | } |
| 60 | - | |
| 61 | - const target = event.target as HTMLInputElement; | |
| 62 | - const value = target.checked; | |
| 63 | - const { option } = props.config || {}; | |
| 64 | - | |
| 65 | - const { values, isModbusCommand, sendValue } = | |
| 66 | - (await getSendValues(option, unref(getDesign), value)) || {}; | |
| 67 | - | |
| 68 | - const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand); | |
| 69 | - flag ? (currentValue.value = value) : (target.checked = !value); | |
| 70 | - }; | |
| 71 | - | |
| 72 | - const handleSendCommand = async (data) => { | |
| 73 | - const { control: event } = data || {}; | |
| 74 | - const target = event.target as HTMLInputElement; | |
| 75 | - const value = !target.checked; | |
| 76 | - const { option } = props.config || {}; | |
| 77 | - | |
| 78 | - const { values, isModbusCommand, sendValue } = | |
| 79 | - (await getSendValues(option, unref(getDesign), value)) || {}; | |
| 80 | - | |
| 81 | - const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand); | |
| 82 | - flag ? (currentValue.value = value) : (target.checked = !value); | |
| 57 | + handleSendCommand(); | |
| 83 | 58 | }; |
| 84 | 59 | |
| 85 | 60 | const updateFn: DataFetchUpdateFn = (message, attribute) => { | ... | ... |
| 1 | 1 | <script lang="ts" setup> |
| 2 | - import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; | |
| 2 | + import { ComponentMode, ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; | |
| 3 | 3 | import { option } from './config'; |
| 4 | 4 | import { Slider, Spin } from 'ant-design-vue'; |
| 5 | 5 | import { computed, ref, unref } from 'vue'; |
| 6 | 6 | import { useComponentScale } from '../../../hook/useComponentScale'; |
| 7 | 7 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
| 8 | - import { useSendCommand } from '../../../hook/useSendCommand'; | |
| 9 | 8 | import { useReceiveValue } from '../../../hook/useReceiveValue'; |
| 10 | 9 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 11 | 10 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
| 12 | - import { TaskTypeEnum } from '/@/views/task/center/config'; | |
| 13 | - import { useMessage } from '/@/hooks/web/useMessage'; | |
| 14 | - import { SingleToHex } from '/@/views/device/list/cpns/tabs/ObjectModelCommandDeliveryModal/config'; | |
| 15 | - import { genModbusCommand } from '/@/api/task'; | |
| 16 | 11 | import PasswordModal from '../component/PasswordModal.vue'; |
| 17 | 12 | import { useModal } from '/@/components/Modal'; |
| 13 | + import { useCommandDelivery } from '../../../hook/useCommandDelivery'; | |
| 14 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 18 | 15 | |
| 19 | 16 | const props = defineProps<{ |
| 20 | 17 | config: ComponentPropsConfigType<typeof option>; |
| 21 | 18 | }>(); |
| 22 | 19 | |
| 23 | 20 | const sliderValue = ref<number>(33); |
| 24 | - const oldSliderValue = ref<number>(33); | |
| 25 | - const noSendValue = ref<number>(0); | |
| 26 | - const sliderEl = ref<Nullable<InstanceType<typeof Slider>>>(null); | |
| 21 | + const sliderElRef = ref<InstanceType<typeof Slider>>(); | |
| 27 | 22 | |
| 28 | - const { loading, sendCommand } = useSendCommand(); | |
| 23 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 29 | 24 | |
| 30 | 25 | const getDesign = computed(() => { |
| 31 | 26 | const { option, persetOption } = props.config; |
| 32 | - const { | |
| 33 | - componentInfo, | |
| 34 | - attribute, | |
| 35 | - attributeRename, | |
| 36 | - attributeName, | |
| 37 | - commandType, | |
| 38 | - extensionDesc, | |
| 39 | - codeType, | |
| 40 | - deviceCode, | |
| 41 | - customCommand, | |
| 42 | - } = option; | |
| 27 | + const { componentInfo, attribute, deviceProfileId, attributeRename, commandType } = option; | |
| 28 | + | |
| 43 | 29 | const { |
| 44 | 30 | controlBarColor: persetControlBarColor, |
| 45 | 31 | fonColor: persetFontColor, |
| ... | ... | @@ -50,6 +36,8 @@ |
| 50 | 36 | fontSize: persetFontSize, |
| 51 | 37 | password: persetPassword, |
| 52 | 38 | } = persetOption || {}; |
| 39 | + | |
| 40 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 53 | 41 | const { |
| 54 | 42 | controlBarColor, |
| 55 | 43 | password, |
| ... | ... | @@ -61,16 +49,12 @@ |
| 61 | 49 | fontSize, |
| 62 | 50 | } = componentInfo || {}; |
| 63 | 51 | return { |
| 64 | - attribute: attributeRename || attributeName || attribute, | |
| 52 | + attribute: attributeRename || tsl?.functionName || attribute, | |
| 65 | 53 | controlBarColor: controlBarColor ?? persetControlBarColor, |
| 66 | 54 | fontColor: fontColor ?? persetFontColor, |
| 67 | 55 | minNumber: minNumber ?? persetMinNumber, |
| 68 | 56 | maxNumber: maxNumber ?? persetMaxNumber, |
| 69 | - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, | |
| 70 | 57 | commandType, |
| 71 | - codeType, | |
| 72 | - deviceCode, | |
| 73 | - customCommand, | |
| 74 | 58 | textColor: textColor || persetTextColor, |
| 75 | 59 | valueSize: valueSize || persetValueSize || 20, |
| 76 | 60 | fontSize: fontSize || persetFontSize || 14, |
| ... | ... | @@ -78,153 +62,28 @@ |
| 78 | 62 | }; |
| 79 | 63 | }); |
| 80 | 64 | |
| 81 | - const index = ref<number>(0); | |
| 82 | - const afterValue = ref<number>(0); //点击Slider获取的值 | |
| 83 | - | |
| 84 | - const handleChange = async (e) => { | |
| 85 | - index.value++; | |
| 86 | - afterValue.value = e; | |
| 87 | - if (index.value == 1) { | |
| 88 | - oldSliderValue.value = unref(sliderValue); | |
| 89 | - return; //这个是因为设置了最大值时,当sliderValue的值大于最大值时只会显示设置的最大值,会执行一次change | |
| 90 | - } | |
| 91 | - sliderValue.value = e; | |
| 92 | - }; | |
| 93 | - | |
| 94 | - const handleAfterChange = async () => { | |
| 95 | - unref(sliderEl)?.blur(); | |
| 96 | - if (unref(sliderValue) == unref(afterValue)) return; | |
| 97 | - if (unref(getDesign).password) return; | |
| 98 | - sliderValue.value = afterValue.value; //这一步是因为当我们是点击不是拖动时候,会让值更新不了,所以需要赋值一次 | |
| 99 | - }; | |
| 100 | - | |
| 101 | - const { createMessage } = useMessage(); | |
| 102 | - | |
| 103 | - // 获取小数 | |
| 104 | - const getFloatPart = (number: string | number) => { | |
| 105 | - const isLessZero = Number(number) < 0; | |
| 106 | - number = number.toString(); | |
| 107 | - const floatPartStartIndex = number.indexOf('.'); | |
| 108 | - const value = ~floatPartStartIndex | |
| 109 | - ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}` | |
| 110 | - : '0'; | |
| 111 | - return Number(value); | |
| 65 | + const sendValue = ref(0); | |
| 66 | + const handleChange = async (value: number) => { | |
| 67 | + sendValue.value = value; | |
| 112 | 68 | }; |
| 113 | 69 | |
| 114 | - const getArray = (values) => { | |
| 115 | - const str = values.replace(/\s+/g, ''); | |
| 116 | - const array: any = []; | |
| 70 | + const { loading, doCommandDeliver } = useCommandDelivery(); | |
| 117 | 71 | |
| 118 | - for (let i = 0; i < str.length; i += 4) { | |
| 119 | - const chunk = parseInt(str.substring(i, i + 4), 16); | |
| 120 | - array.push(chunk); | |
| 121 | - } | |
| 122 | - return array; | |
| 123 | - }; | |
| 124 | - | |
| 125 | - const getSendValue = async (value: number) => { | |
| 126 | - const { extensionDesc, codeType, deviceCode, customCommand } = unref(getDesign) || {}; | |
| 127 | - const { transportType } = customCommand || {}; | |
| 128 | - const { registerAddress, actionType, zoomFactor } = extensionDesc || {}; | |
| 129 | - const newZoomValue = zoomFactor ? Number(zoomFactor) : 1; | |
| 130 | - if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) { | |
| 131 | - if (!deviceCode) { | |
| 132 | - createMessage.warning('当前设备没有设置地址码'); | |
| 133 | - return; | |
| 134 | - } | |
| 135 | - const modbusForm = ref({ | |
| 136 | - crc: 'CRC_16_LOWER', | |
| 137 | - deviceCode: deviceCode, | |
| 138 | - method: actionType == '16' ? '10' : actionType, | |
| 139 | - registerAddress, | |
| 140 | - registerNumber: 1, | |
| 141 | - registerValues: [value], | |
| 142 | - }) as any; | |
| 143 | - if (!actionType) { | |
| 144 | - createMessage.warning('当前物模型扩展描述没有填写'); | |
| 145 | - return; | |
| 146 | - } | |
| 147 | - if (actionType == '06') { | |
| 148 | - const newValue = Math.trunc(value) * newZoomValue + getFloatPart(value) * newZoomValue; | |
| 149 | - if (newValue % 1 != 0) { | |
| 150 | - createMessage.warning(`值必须是整数,缩放因子为${unref(newZoomValue)}`); | |
| 151 | - return; | |
| 152 | - } | |
| 153 | - | |
| 154 | - if (newValue > 65535) { | |
| 155 | - createMessage.warning(`值不能超过65535,缩放因子是${unref(newZoomValue)}`); | |
| 156 | - return; | |
| 157 | - } | |
| 158 | - modbusForm.value.registerValues = [newValue]; | |
| 159 | - } | |
| 160 | - if (actionType == '05') { | |
| 161 | - if (Number(value) != 0 || Number(value) != 1) { | |
| 162 | - createMessage.warning(`当前物模型仅支持值为0和1`); | |
| 163 | - return; | |
| 164 | - } | |
| 165 | - } | |
| 166 | - if (actionType == '16' || actionType == '10') { | |
| 167 | - const regex = /^-?\d+(\.\d{0,2})?$/; | |
| 168 | - const values = | |
| 169 | - Math.trunc(value) * unref(newZoomValue) + getFloatPart(value) * unref(newZoomValue); | |
| 170 | - if (!regex.test(values as any)) { | |
| 171 | - createMessage.warning(`值精确到两位小数,缩放因子是${unref(newZoomValue)}`); | |
| 172 | - return; | |
| 173 | - } | |
| 174 | - const newValue = values == 0 ? [0, 0] : getArray(SingleToHex(values)); | |
| 175 | - modbusForm.value.registerValues = newValue; | |
| 176 | - modbusForm.value.registerNumber = 2; | |
| 177 | - modbusForm.value.method = '10'; | |
| 178 | - } | |
| 179 | - const sendValue = await genModbusCommand(unref(modbusForm)); | |
| 180 | - return sendValue; | |
| 181 | - } | |
| 182 | - }; | |
| 183 | - | |
| 184 | - const handleBlur = async () => { | |
| 72 | + const handleAfterChange = () => { | |
| 185 | 73 | if (unref(getDesign).password) { |
| 186 | - sliderValue.value = oldSliderValue.value; | |
| 187 | - openModal(true, { password: unref(getDesign).password, value: afterValue.value }); | |
| 74 | + openModal(true, { password: unref(getDesign).password }); | |
| 188 | 75 | return; |
| 189 | 76 | } |
| 190 | - | |
| 191 | - if (unref(oldSliderValue) !== unref(sliderValue)) { | |
| 192 | - const { codeType, customCommand } = unref(getDesign) || {}; | |
| 193 | - const { transportType } = customCommand || {}; | |
| 194 | - const sendValue = ref<any>(unref(sliderValue)); | |
| 195 | - const isModbusCommand = ref<boolean>(false); | |
| 196 | - if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) { | |
| 197 | - sendValue.value = await getSendValue(unref(sliderValue)); | |
| 198 | - isModbusCommand.value = true; | |
| 199 | - } | |
| 200 | - const flag = await sendCommand(props.config.option, unref(sendValue), unref(isModbusCommand)); | |
| 201 | - flag | |
| 202 | - ? ((sliderValue.value = unref(sliderValue)), | |
| 203 | - (oldSliderValue.value = sliderValue.value), | |
| 204 | - (index.value = 0)) | |
| 205 | - : (sliderValue.value = unref(oldSliderValue)); | |
| 206 | - } | |
| 77 | + handleSendCommand(); | |
| 207 | 78 | }; |
| 208 | 79 | |
| 209 | - const handleSendCommand = async (data) => { | |
| 210 | - const { value } = data || {}; | |
| 211 | - sliderValue.value = value; | |
| 212 | - if (unref(oldSliderValue) !== unref(sliderValue)) { | |
| 213 | - const { codeType, customCommand } = unref(getDesign) || {}; | |
| 214 | - const { transportType } = customCommand || {}; | |
| 215 | - const sendValue = ref<any>(unref(sliderValue)); | |
| 216 | - const isModbusCommand = ref<boolean>(false); | |
| 217 | - if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) { | |
| 218 | - sendValue.value = await getSendValue(unref(sliderValue)); | |
| 219 | - isModbusCommand.value = true; | |
| 220 | - } | |
| 221 | - const flag = await sendCommand(props.config.option, unref(sendValue), unref(isModbusCommand)); | |
| 222 | - flag | |
| 223 | - ? ((sliderValue.value = unref(sliderValue)), | |
| 224 | - (oldSliderValue.value = sliderValue.value), | |
| 225 | - (index.value = 0)) | |
| 226 | - : (sliderValue.value = unref(oldSliderValue)); | |
| 227 | - } | |
| 80 | + const handleSendCommand = async () => { | |
| 81 | + if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; | |
| 82 | + const value = unref(sendValue); | |
| 83 | + const { option } = props.config || {}; | |
| 84 | + const result = await doCommandDeliver(option, value); | |
| 85 | + unref(sliderElRef)?.blur(); | |
| 86 | + sliderValue.value = result ? value : unref(sliderValue); | |
| 228 | 87 | }; |
| 229 | 88 | |
| 230 | 89 | const { getNumberValue } = useReceiveValue(); |
| ... | ... | @@ -233,7 +92,6 @@ |
| 233 | 92 | const [latest] = data[attribute] || []; |
| 234 | 93 | const [name, value] = latest; |
| 235 | 94 | if (!name && !value) return; |
| 236 | - noSendValue.value = getNumberValue(value); | |
| 237 | 95 | sliderValue.value = getNumberValue(value); |
| 238 | 96 | }; |
| 239 | 97 | |
| ... | ... | @@ -254,10 +112,11 @@ |
| 254 | 112 | fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', |
| 255 | 113 | }" |
| 256 | 114 | class="font-bold text-xl mt-3 text-center" |
| 257 | - >{{ sliderValue }}</span | |
| 258 | 115 | > |
| 116 | + {{ sliderValue }} | |
| 117 | + </span> | |
| 259 | 118 | <Slider |
| 260 | - ref="sliderEl" | |
| 119 | + ref="sliderElRef" | |
| 261 | 120 | :style="{ |
| 262 | 121 | '--slider-color': getDesign.controlBarColor, |
| 263 | 122 | '--slider-handle': (getRatio ? getRatio * 14 : 14) + 'px', |
| ... | ... | @@ -270,7 +129,6 @@ |
| 270 | 129 | :max="getDesign.maxNumber" |
| 271 | 130 | @change="handleChange" |
| 272 | 131 | @afterChange="handleAfterChange" |
| 273 | - @blur="handleBlur" | |
| 274 | 132 | /> |
| 275 | 133 | |
| 276 | 134 | <span | ... | ... |
| ... | ... | @@ -8,6 +8,47 @@ import { |
| 8 | 8 | } from '/@/views/visual/packages/index.type'; |
| 9 | 9 | import { PublicConfigClass, componentInitConfig } from '/@/views/visual/packages/publicConfig'; |
| 10 | 10 | import { ComponentConfigFieldEnum } from '../../../enum'; |
| 11 | +import { buildUUID } from '/@/utils/uuid'; | |
| 12 | + | |
| 13 | +export interface SwitchItemType extends Recordable { | |
| 14 | + id: string; | |
| 15 | + checked: boolean; | |
| 16 | + deviceId?: string; | |
| 17 | + deviceProfileId?: string; | |
| 18 | + attribute?: string; | |
| 19 | + deviceName?: string; | |
| 20 | + attributeName?: string; | |
| 21 | + openCommand?: string; | |
| 22 | + closeCommand?: string; | |
| 23 | + openService?: string; | |
| 24 | + closeService?: string; | |
| 25 | +} | |
| 26 | + | |
| 27 | +export const DEFAULT_VALUE: SwitchItemType[] = [ | |
| 28 | + { | |
| 29 | + deviceName: '光照设备', | |
| 30 | + attributeName: '光照', | |
| 31 | + icon: 'zongfushe', | |
| 32 | + unit: 'kw', | |
| 33 | + iconColor: '#367BFF', | |
| 34 | + fontColor: '#357CFB', | |
| 35 | + checked: false, | |
| 36 | + fontSize: 16, | |
| 37 | + id: buildUUID(), | |
| 38 | + }, | |
| 39 | + { | |
| 40 | + value: 53.7, | |
| 41 | + deviceName: '风机设备', | |
| 42 | + attributeName: '风机', | |
| 43 | + icon: 'guangzhaoqiangdu', | |
| 44 | + unit: '℃', | |
| 45 | + iconColor: '#FFA000', | |
| 46 | + fontColor: '#FFA000', | |
| 47 | + checked: true, | |
| 48 | + fontSize: 16, | |
| 49 | + id: buildUUID(), | |
| 50 | + }, | |
| 51 | +]; | |
| 11 | 52 | |
| 12 | 53 | export const option: PublicPresetOptions = { |
| 13 | 54 | multipleDataSourceComponent: true, | ... | ... |
| 1 | 1 | <script lang="ts" setup> |
| 2 | - import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; | |
| 3 | - import { option } from './config'; | |
| 2 | + import { ComponentMode, ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; | |
| 3 | + import { DEFAULT_VALUE, option, SwitchItemType } from './config'; | |
| 4 | 4 | import { SvgIcon } from '/@/components/Icon'; |
| 5 | 5 | import { Switch } from 'ant-design-vue'; |
| 6 | - import { computed, ref } from 'vue'; | |
| 6 | + import { computed, ref, toRaw } from 'vue'; | |
| 7 | 7 | import { useComponentScale } from '../../../hook/useComponentScale'; |
| 8 | - import { useSendCommand } from '../../../hook/useSendCommand'; | |
| 9 | 8 | import { unref } from 'vue'; |
| 10 | 9 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
| 11 | 10 | import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 12 | 11 | import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; |
| 13 | 12 | import { useReceiveMessage } from '../../../hook/useReceiveMessage'; |
| 14 | 13 | import { useReceiveValue } from '../../../hook/useReceiveValue'; |
| 15 | - import { DataSource } from '/@/views/visual/palette/types'; | |
| 16 | - import { getSendValues, CommandTypeEnumLIst } from '../config'; | |
| 17 | 14 | import { useModal } from '/@/components/Modal'; |
| 18 | 15 | import PasswordModal from '../component/PasswordModal.vue'; |
| 16 | + import { | |
| 17 | + DoCommandDeliverDataSourceType, | |
| 18 | + useCommandDelivery, | |
| 19 | + } from '../../../hook/useCommandDelivery'; | |
| 20 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 19 | 21 | |
| 20 | 22 | const props = defineProps<{ |
| 21 | 23 | config: ComponentPropsConfigType<typeof option>; |
| 22 | 24 | }>(); |
| 23 | 25 | |
| 24 | - const svgList = ref<any>([ | |
| 25 | - { | |
| 26 | - value: 26.2, | |
| 27 | - deviceName: '光照设备', | |
| 28 | - attributeName: '光照', | |
| 29 | - icon: 'zongfushe', | |
| 30 | - unit: 'kw', | |
| 31 | - iconColor: '#367BFF', | |
| 32 | - fontColor: '#357CFB', | |
| 33 | - checked: false, | |
| 34 | - id: 0, | |
| 35 | - }, | |
| 36 | - { | |
| 37 | - value: 53.7, | |
| 38 | - deviceName: '风机设备', | |
| 39 | - attributeName: '风机', | |
| 40 | - icon: 'guangzhaoqiangdu', | |
| 41 | - unit: '℃', | |
| 42 | - iconColor: '#FFA000', | |
| 43 | - fontColor: '#FFA000', | |
| 44 | - checked: true, | |
| 45 | - id: 1, | |
| 46 | - }, | |
| 47 | - ]); | |
| 26 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 48 | 27 | |
| 49 | 28 | const getDesign = computed(() => { |
| 50 | 29 | const { persetOption = {}, option } = props.config; |
| ... | ... | @@ -64,90 +43,66 @@ |
| 64 | 43 | const { |
| 65 | 44 | attribute, |
| 66 | 45 | attributeRename, |
| 67 | - attributeName, | |
| 68 | 46 | deviceId, |
| 69 | 47 | deviceName, |
| 70 | 48 | deviceRename, |
| 71 | 49 | commandType, |
| 72 | - extensionDesc, | |
| 73 | - codeType, | |
| 74 | - deviceCode, | |
| 75 | - customCommand, | |
| 50 | + deviceProfileId, | |
| 51 | + openCommand, | |
| 52 | + closeCommand, | |
| 53 | + openService, | |
| 54 | + closeService, | |
| 76 | 55 | } = item; |
| 56 | + | |
| 57 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 58 | + | |
| 77 | 59 | return { |
| 78 | 60 | unit: unit ?? persetUnit, |
| 61 | + deviceProfileId, | |
| 62 | + deviceId, | |
| 79 | 63 | fontColor: fontColor ?? persetFontColor, |
| 80 | 64 | icon: icon ?? persetIcon, |
| 81 | 65 | iconColor: iconColor ?? persetIconColor, |
| 82 | 66 | attribute: attribute, |
| 83 | - attributeName: attributeRename || attributeName, | |
| 67 | + attributeName: attributeRename || tsl?.functionName || attribute, | |
| 84 | 68 | showDeviceName, |
| 85 | 69 | deviceName: deviceRename || deviceName, |
| 86 | 70 | id: deviceId, |
| 87 | - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, | |
| 88 | 71 | commandType, |
| 89 | - codeType, | |
| 90 | - deviceCode, | |
| 91 | - customCommand, | |
| 92 | 72 | fontSize: fontSize || persetFontSize || 14, |
| 93 | 73 | password: password || persetPassword, |
| 94 | - }; | |
| 74 | + checked: false, | |
| 75 | + openCommand, | |
| 76 | + closeCommand, | |
| 77 | + openService, | |
| 78 | + closeService, | |
| 79 | + } as SwitchItemType; | |
| 95 | 80 | }), |
| 96 | 81 | }; |
| 97 | 82 | }); |
| 98 | 83 | |
| 99 | - const { loading, sendCommand } = useSendCommand(); | |
| 100 | - const handleChange = async (index: number, checked: Boolean, item: any) => { | |
| 84 | + const handleChange = async (item) => { | |
| 101 | 85 | if (item.password) { |
| 102 | - openModal(true, { password: item.password, index, checked }); | |
| 103 | - controlList.value[index].checked = !checked; | |
| 86 | + openModal(true, { password: item.password }); | |
| 104 | 87 | return; |
| 105 | 88 | } |
| 106 | - const { heightPx, itemHeightRatio, itemWidthRatio, mode, widthPx, dataSource } = | |
| 107 | - props.config.option; | |
| 108 | - const data = { | |
| 109 | - ...dataSource?.[index], | |
| 110 | - heightPx, | |
| 111 | - itemHeightRatio, | |
| 112 | - itemWidthRatio, | |
| 113 | - mode, | |
| 114 | - widthPx, | |
| 115 | - } as DataSource; | |
| 116 | - const { values, isModbusCommand, sendValue } = | |
| 117 | - (await getSendValues(data, unref(getDesign).dataSource[index], checked)) || {}; | |
| 118 | - | |
| 119 | - const flag = await sendCommand(values, isModbusCommand ? sendValue : checked, isModbusCommand); | |
| 120 | - if (!flag) controlList.value[index].checked = !checked; | |
| 89 | + handleSendCommand(item); | |
| 121 | 90 | }; |
| 122 | 91 | |
| 123 | 92 | const { forEachGroupMessage } = useReceiveMessage(); |
| 124 | 93 | const { getNumberValue } = useReceiveValue(); |
| 125 | 94 | |
| 126 | 95 | const controlList = ref( |
| 127 | - props.config.option.dataSource | |
| 128 | - ? unref(getDesign).dataSource.map((item) => { | |
| 129 | - return { ...item, checked: false }; | |
| 130 | - }) | |
| 131 | - : svgList | |
| 96 | + props.config.option.dataSource ? unref(getDesign).dataSource : DEFAULT_VALUE | |
| 132 | 97 | ); |
| 133 | - const handleSendCommand = async (modalData) => { | |
| 134 | - const { index, checked } = modalData || {}; | |
| 135 | - const { heightPx, itemHeightRatio, itemWidthRatio, mode, widthPx, dataSource } = | |
| 136 | - props.config.option; | |
| 137 | - const data = { | |
| 138 | - ...dataSource?.[index], | |
| 139 | - heightPx, | |
| 140 | - itemHeightRatio, | |
| 141 | - itemWidthRatio, | |
| 142 | - mode, | |
| 143 | - widthPx, | |
| 144 | - } as DataSource; | |
| 145 | - controlList.value[index].checked = checked; | |
| 146 | - const { values, isModbusCommand, sendValue } = | |
| 147 | - (await getSendValues(data, unref(getDesign).dataSource[index], checked)) || {}; | |
| 148 | 98 | |
| 149 | - const flag = await sendCommand(values, isModbusCommand ? sendValue : checked, isModbusCommand); | |
| 150 | - if (!flag) controlList.value[index].checked = !checked; | |
| 99 | + const { loading, doCommandDeliver } = useCommandDelivery(); | |
| 100 | + const handleSendCommand = async (modalData: SwitchItemType) => { | |
| 101 | + if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; | |
| 102 | + await doCommandDeliver( | |
| 103 | + toRaw(unref(modalData)) as DoCommandDeliverDataSourceType, | |
| 104 | + Number(unref(modalData).checked) | |
| 105 | + ); | |
| 151 | 106 | }; |
| 152 | 107 | |
| 153 | 108 | const updateFn: MultipleDataFetchUpdateFn = async (message, deviceId, attribute) => { |
| ... | ... | @@ -169,34 +124,26 @@ |
| 169 | 124 | <main class="w-full h-full flex flex-col justify-evenly items-center"> |
| 170 | 125 | <DeviceName :config="config" /> |
| 171 | 126 | <div |
| 172 | - style="width: 86%" | |
| 173 | - v-for="(item, index) in controlList" | |
| 127 | + v-for="item in controlList" | |
| 174 | 128 | :key="item.id" |
| 175 | - class="flex justify-between items-center" | |
| 129 | + class="flex justify-between items-center w-full px-4" | |
| 176 | 130 | > |
| 177 | - <div class="flex items-center"> | |
| 178 | - <SvgIcon | |
| 179 | - :name="item.icon!" | |
| 180 | - prefix="iconfont" | |
| 181 | - :size="getRatio ? 30 * getRatio : 30" | |
| 182 | - :style="{ color: item.iconColor }" | |
| 183 | - /> | |
| 184 | - | |
| 185 | - <div | |
| 186 | - class="text-gray-500 truncate ml-6" | |
| 187 | - :style="{ fontSize: (getRatio ? getRatio * item.fontSize : item.fontSize) + 'px' }" | |
| 188 | - >{{ | |
| 189 | - `${item.deviceName} - ${ | |
| 190 | - item.commandType ? CommandTypeEnumLIst[item.commandType].name : item.attributeName | |
| 191 | - }` | |
| 192 | - }}</div | |
| 193 | - > | |
| 131 | + <SvgIcon | |
| 132 | + :name="item.icon!" | |
| 133 | + prefix="iconfont" | |
| 134 | + :size="getRatio ? 30 * getRatio : 30" | |
| 135 | + :style="{ color: item.iconColor }" | |
| 136 | + /> | |
| 137 | + <div | |
| 138 | + class="text-gray-500 truncate mx-2" | |
| 139 | + :style="{ fontSize: (getRatio ? getRatio * item.fontSize : item.fontSize) + 'px' }" | |
| 140 | + > | |
| 141 | + {{ `${item.deviceName} - ${item.attributeName}` }} | |
| 194 | 142 | </div> |
| 195 | - | |
| 196 | 143 | <Switch |
| 197 | 144 | v-model:checked="item.checked" |
| 198 | 145 | :loading="loading" |
| 199 | - @change="handleChange(index, item.checked, item)" | |
| 146 | + @change="handleChange(item)" | |
| 200 | 147 | :style="{ transform: `scale(${getRatio || 1})` }" |
| 201 | 148 | /> |
| 202 | 149 | </div> | ... | ... |
src/views/visual/packages/components/Control/config.ts
deleted
100644 → 0
| 1 | -import { ref, unref } from 'vue'; | |
| 2 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 3 | -import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data'; | |
| 4 | -import { TaskTypeEnum } from '/@/views/task/center/config'; | |
| 5 | -import { genModbusCommand } from '/@/api/task'; | |
| 6 | -import { useMessage } from '/@/hooks/web/useMessage'; | |
| 7 | -import { SingleToHex } from '/@/views/device/list/cpns/tabs/ObjectModelCommandDeliveryModal/config'; | |
| 8 | - | |
| 9 | -const getArray = (values) => { | |
| 10 | - const str = values.replace(/\s+/g, ''); | |
| 11 | - const array: any = []; | |
| 12 | - | |
| 13 | - for (let i = 0; i < str.length; i += 4) { | |
| 14 | - const chunk = parseInt(str.substring(i, i + 4), 16); | |
| 15 | - array.push(chunk); | |
| 16 | - } | |
| 17 | - return array; | |
| 18 | -}; | |
| 19 | - | |
| 20 | -export const getSendValues = async (option, getDesign, checked) => { | |
| 21 | - const values: any = option; | |
| 22 | - const isModbusCommand = ref<boolean>(false); //判断是否是TCP设备-> 且设备标识符是modeBUs,切选择是下发命令类型是属性 | |
| 23 | - const { extensionDesc, commandType, codeType, deviceCode, customCommand } = getDesign || {}; | |
| 24 | - const { registerAddress, actionType } = extensionDesc || {}; | |
| 25 | - const { openCommand, closeCommand, transportType } = customCommand || {}; | |
| 26 | - const modBUS = ref<any>({}); | |
| 27 | - const sendValue = ref(); | |
| 28 | - | |
| 29 | - const { createMessage } = useMessage(); | |
| 30 | - //判断是不是TCP类型设备 | |
| 31 | - if (transportType === TransportTypeEnum.TCP) { | |
| 32 | - if ( | |
| 33 | - //判断TCP下发类型是否是自定义还是服务 | |
| 34 | - commandType === CommandTypeEnum.CUSTOM.toString() || | |
| 35 | - commandType == CommandTypeEnum.SERVICE.toString() | |
| 36 | - ) { | |
| 37 | - values.customCommand.command = checked ? openCommand : closeCommand; | |
| 38 | - values.customCommand.command = values.customCommand.command | |
| 39 | - .replaceAll(/\s/g, '') | |
| 40 | - .toUpperCase(); | |
| 41 | - } | |
| 42 | - if ( | |
| 43 | - //判断命令下发类型是不是属性 且是modbus | |
| 44 | - commandType === CommandTypeEnum.ATTRIBUTE.toString() && | |
| 45 | - codeType === TaskTypeEnum.MODBUS_RTU | |
| 46 | - ) { | |
| 47 | - if (!deviceCode) { | |
| 48 | - createMessage.warning('当前设备没有设置地址码'); | |
| 49 | - return; | |
| 50 | - } | |
| 51 | - if (!actionType) { | |
| 52 | - createMessage.warning('当前物模型扩展描述没有填写'); | |
| 53 | - return; | |
| 54 | - } | |
| 55 | - isModbusCommand.value = true; | |
| 56 | - modBUS.value = { | |
| 57 | - crc: 'CRC_16_LOWER', | |
| 58 | - deviceCode: deviceCode, | |
| 59 | - method: actionType == '16' ? '10' : actionType, | |
| 60 | - registerAddress, | |
| 61 | - registerNumber: 1, | |
| 62 | - registerValues: [Number(checked)], | |
| 63 | - }; | |
| 64 | - | |
| 65 | - if (actionType == '16' || actionType == '10') { | |
| 66 | - const newValue = Number(checked) == 0 ? [0, 0] : getArray(SingleToHex(Number(checked))); | |
| 67 | - modBUS.value.registerValues = newValue; | |
| 68 | - modBUS.value.registerNumber = 2; | |
| 69 | - modBUS.value.method = '10'; | |
| 70 | - } | |
| 71 | - | |
| 72 | - sendValue.value = await genModbusCommand(unref(modBUS)); | |
| 73 | - } | |
| 74 | - } | |
| 75 | - | |
| 76 | - return { values, sendValue: unref(sendValue), isModbusCommand: unref(isModbusCommand) }; | |
| 77 | -}; | |
| 78 | - | |
| 79 | -export const CommandTypeEnumLIst = { | |
| 80 | - '0': { CUSTOM: 0, name: '自定义' }, | |
| 81 | - '1': { CUSTOM: 1, name: '服务' }, | |
| 82 | - '2': { CUSTOM: 2, name: '属性' }, | |
| 83 | -}; |
| ... | ... | @@ -3,11 +3,15 @@ |
| 3 | 3 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
| 4 | 4 | import { formSchema, getHistorySearchParams, SchemaFiled } from './history.config'; |
| 5 | 5 | import { HistoryModalOkEmitParams } from './type'; |
| 6 | - import { ref, unref } from 'vue'; | |
| 6 | + import { computed, ref, unref } from 'vue'; | |
| 7 | 7 | import { getAllDeviceByOrg } from '/@/api/dataBoard'; |
| 8 | 8 | import { getDeviceHistoryInfo } from '/@/api/alarm/position'; |
| 9 | 9 | import { DataSource } from '/@/views/visual/palette/types'; |
| 10 | - import { cloneDeep } from 'lodash-es'; | |
| 10 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 11 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 12 | + import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | |
| 13 | + import { HistoryData } from '/@/api/alarm/position/model'; | |
| 14 | + import { useJsonParse } from '/@/hooks/business/useJsonParse'; | |
| 11 | 15 | |
| 12 | 16 | const emit = defineEmits(['register', 'ok']); |
| 13 | 17 | |
| ... | ... | @@ -20,47 +24,44 @@ |
| 20 | 24 | }); |
| 21 | 25 | |
| 22 | 26 | const loading = ref(false); |
| 23 | - const getDesign = ref(); | |
| 27 | + const dataSourceRef = ref<DataSource[]>([]); | |
| 24 | 28 | |
| 25 | - const getTwoMap = async (dataSource) => { | |
| 26 | - if (dataSource.length < 2) return; | |
| 27 | - dataSource = dataSource.splice(0, 2); | |
| 28 | - const deviceRecord = dataSource?.at(0) || ({} as DataSource); | |
| 29 | + const getPositionRecord = computed<DataSource>(() => unref(dataSourceRef).at(0) as DataSource); | |
| 30 | + | |
| 31 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 32 | + | |
| 33 | + const handleSetForm = async () => { | |
| 34 | + const deviceRecord = unref(getPositionRecord) || {}; | |
| 29 | 35 | |
| 30 | 36 | if (!deviceRecord.organizationId) return; |
| 31 | 37 | const deviceList = await getAllDeviceByOrg( |
| 32 | 38 | deviceRecord.organizationId, |
| 33 | 39 | deviceRecord.deviceProfileId |
| 34 | 40 | ); |
| 41 | + | |
| 35 | 42 | const options = deviceList |
| 36 | 43 | .filter((item) => item.tbDeviceId === deviceRecord.deviceId) |
| 37 | 44 | .map((item) => ({ ...item, label: item.alias || item.name, value: item.tbDeviceId })); |
| 38 | 45 | |
| 39 | - const attKey = dataSource.map((item) => ({ | |
| 40 | - ...item, | |
| 41 | - label: item.attribute, | |
| 42 | - value: item.attribute, | |
| 43 | - })); | |
| 46 | + const { latitudeIdentifier, longitudeIdentifier, deviceProfileId } = deviceRecord; | |
| 44 | 47 | |
| 45 | - updateSchemaMap(options, attKey, deviceRecord); | |
| 46 | - }; | |
| 48 | + const attKey = [latitudeIdentifier, longitudeIdentifier].map((item) => { | |
| 49 | + const [identifier, structIdentifier] = item || []; | |
| 50 | + const detail = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, identifier); | |
| 47 | 51 | |
| 48 | - const getOneMap = async (dataSource) => { | |
| 49 | - const deviceRecord = dataSource?.[0]; | |
| 50 | - if (!deviceRecord.organizationId) return; | |
| 51 | - const deviceList = await getAllDeviceByOrg( | |
| 52 | - deviceRecord.organizationId, | |
| 53 | - deviceRecord.deviceProfileId | |
| 54 | - ); | |
| 55 | - const options = deviceList | |
| 56 | - .filter((item) => item.tbDeviceId === deviceRecord.deviceId) | |
| 57 | - .map((item) => ({ ...item, label: item.alias || item.name, value: item.tbDeviceId })); | |
| 52 | + if (detail?.specs?.dataType.type === DataTypeEnum.STRUCT) { | |
| 53 | + const structIdentifierDetail = (detail.specs.dataType.specs as StructJSON[])?.find( | |
| 54 | + (temp) => temp.identifier === structIdentifier | |
| 55 | + ); | |
| 56 | + return { | |
| 57 | + label: `${detail.functionName} / ${structIdentifierDetail?.functionName}`, | |
| 58 | + value: identifier, | |
| 59 | + }; | |
| 60 | + } | |
| 61 | + | |
| 62 | + return { label: detail?.functionName, value: identifier }; | |
| 63 | + }); | |
| 58 | 64 | |
| 59 | - const attKey = dataSource?.map((item) => ({ | |
| 60 | - ...item, | |
| 61 | - label: item.attributeName, | |
| 62 | - value: item.attribute, | |
| 63 | - })); | |
| 64 | 65 | updateSchemaMap(options, attKey, deviceRecord); |
| 65 | 66 | }; |
| 66 | 67 | |
| ... | ... | @@ -92,81 +93,73 @@ |
| 92 | 93 | |
| 93 | 94 | const [registerModal, { closeModal }] = useModalInner(async (dataSource: DataSource[]) => { |
| 94 | 95 | try { |
| 95 | - getDesign.value = dataSource; | |
| 96 | - dataSource = cloneDeep(dataSource); | |
| 97 | - //判断选择是不是结构体 | |
| 98 | - if (dataSource.length == 1 && dataSource?.[0]?.lal) { | |
| 99 | - getOneMap(dataSource); | |
| 100 | - return; | |
| 101 | - } | |
| 102 | - getTwoMap(dataSource); | |
| 96 | + dataSourceRef.value = dataSource; | |
| 97 | + handleSetForm(); | |
| 103 | 98 | } catch (error) { |
| 104 | 99 | throw error; |
| 105 | 100 | } |
| 106 | 101 | }); |
| 107 | 102 | |
| 108 | - const getMultipleDataSource = async (res) => { | |
| 103 | + const getPositionDataSource = async (res: HistoryData) => { | |
| 104 | + const keys = Object.keys(res); | |
| 105 | + const track: Record<'lng' | 'lat', number>[] = []; | |
| 106 | + | |
| 107 | + const { | |
| 108 | + latitudeIdentifier = [], | |
| 109 | + longitudeIdentifier = [], | |
| 110 | + deviceProfileId, | |
| 111 | + } = unref(getPositionRecord); | |
| 112 | + | |
| 113 | + const [latIdentifier] = latitudeIdentifier || []; | |
| 114 | + const [lngIdentifier] = longitudeIdentifier || []; | |
| 115 | + | |
| 116 | + if (!latIdentifier || !lngIdentifier) return track; | |
| 117 | + | |
| 118 | + const latDetail = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, latIdentifier); | |
| 119 | + const lngDetail = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, lngIdentifier); | |
| 120 | + | |
| 121 | + if (!latDetail || !lngDetail) return track; | |
| 122 | + | |
| 109 | 123 | let timespanList = Object.keys(res).reduce((prev, next) => { |
| 110 | 124 | const ts = res[next].map((item) => item.ts); |
| 111 | 125 | return [...prev, ...ts]; |
| 112 | 126 | }, [] as number[]); |
| 113 | 127 | timespanList = [...new Set(timespanList)]; |
| 114 | 128 | |
| 115 | - const track: Record<'lng' | 'lat', number>[] = []; | |
| 116 | - const keys = Object.keys(res); | |
| 129 | + for (const position of [longitudeIdentifier, latitudeIdentifier]) { | |
| 130 | + const [attribute] = position; | |
| 131 | + const detail = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 132 | + if (!detail) continue; | |
| 133 | + const { identifier } = detail || {}; | |
| 134 | + | |
| 135 | + if (detail?.specs?.dataType.type === DataTypeEnum.STRUCT) { | |
| 136 | + const [, structIdentifier] = position; | |
| 137 | + res[identifier].forEach((temp) => { | |
| 138 | + const structJSON = useJsonParse(temp.value).value; | |
| 139 | + temp.value = structJSON?.[structIdentifier]; | |
| 140 | + }); | |
| 141 | + } | |
| 142 | + } | |
| 117 | 143 | |
| 118 | 144 | for (const ts of timespanList) { |
| 119 | 145 | const list: { ts: number; value: number }[] = []; |
| 120 | 146 | for (const key of keys) { |
| 121 | 147 | const record = res[key].find((item) => ts === item.ts); |
| 122 | - if (!validEffective(record?.value)) { | |
| 123 | - continue; | |
| 124 | - } | |
| 125 | 148 | list.push(record as any); |
| 126 | 149 | } |
| 127 | 150 | |
| 128 | 151 | if (list.length === 2 && list.every(Boolean)) { |
| 129 | - const lng = list.at(0)?.value; | |
| 130 | - const lat = list.at(1)?.value; | |
| 131 | - if (lng && lat) track.push({ lng, lat }); | |
| 152 | + let lng = list.at(0)?.value; | |
| 153 | + let lat = list.at(1)?.value; | |
| 154 | + if (lng && lat) { | |
| 155 | + track.push({ lng: Number(lng), lat: Number(lat) }); | |
| 156 | + } | |
| 132 | 157 | } |
| 133 | 158 | } |
| 134 | - return track; | |
| 135 | - }; | |
| 136 | - | |
| 137 | - // 单数据源选择结构体 | |
| 138 | - const getSingleDataSource = async (res) => { | |
| 139 | - const [keys] = Object.keys(res); | |
| 140 | - const track: Record<'lng' | 'lat', number>[] = []; | |
| 141 | - const dataSource = res[keys]?.map((item) => { | |
| 142 | - return { | |
| 143 | - value: item.value ? JSON.parse(item.value) : {}, | |
| 144 | - }; | |
| 145 | - }); | |
| 146 | - const list: { value: number }[] = []; | |
| 147 | - dataSource?.forEach((item) => { | |
| 148 | - if ( | |
| 149 | - //判断结构体得值是不是数字 | |
| 150 | - Object.values(item.value).every((item1) => { | |
| 151 | - return validEffective(item1 as any); | |
| 152 | - }) | |
| 153 | - ) { | |
| 154 | - list.push(item.value); | |
| 155 | - } | |
| 156 | - }); | |
| 157 | - const { latitude, longitude } = unref(getDesign)?.[0]; //获取选择的经纬度选项 | |
| 158 | 159 | |
| 159 | - list.forEach((item) => { | |
| 160 | - const lng = item[longitude]; | |
| 161 | - const lat = item[latitude]; | |
| 162 | - if (lng && lat) track.push({ lng, lat }); | |
| 163 | - }); | |
| 164 | 160 | return track; |
| 165 | 161 | }; |
| 166 | 162 | |
| 167 | - const validEffective = (value = '') => { | |
| 168 | - return !!(value && !isNaN(value as unknown as number)); | |
| 169 | - }; | |
| 170 | 163 | const handleOk = async () => { |
| 171 | 164 | try { |
| 172 | 165 | await validate(); |
| ... | ... | @@ -180,9 +173,8 @@ |
| 180 | 173 | ...value, |
| 181 | 174 | [SchemaFiled.KEYS]: value[SchemaFiled.KEYS].join(','), |
| 182 | 175 | }); |
| 183 | - const ifSingle = unref(getDesign)?.length === 1 && unref(getDesign)?.[0]?.lal; | |
| 184 | 176 | |
| 185 | - const track = ifSingle ? await getSingleDataSource(res) : await getMultipleDataSource(res); | |
| 177 | + const track = await getPositionDataSource(res); | |
| 186 | 178 | emit('ok', { track, value } as HistoryModalOkEmitParams); |
| 187 | 179 | closeModal(); |
| 188 | 180 | } catch (error) { | ... | ... |
| 1 | +import { CategoryEnum } from '../..'; | |
| 1 | 2 | import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys'; |
| 2 | 3 | import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type'; |
| 3 | 4 | |
| ... | ... | @@ -6,5 +7,6 @@ const componentKeys = useComponentKeys('MapComponentTrackHistory'); |
| 6 | 7 | export const MapComponentTrackHistoryConfig: ConfigType = { |
| 7 | 8 | ...componentKeys, |
| 8 | 9 | title: '历史轨迹', |
| 10 | + category: CategoryEnum.MAP, | |
| 9 | 11 | package: PackagesCategoryEnum.MAP, |
| 10 | 12 | }; | ... | ... |
| 1 | +import { CategoryEnum } from '../..'; | |
| 1 | 2 | import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys'; |
| 2 | 3 | import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type'; |
| 3 | 4 | |
| ... | ... | @@ -6,5 +7,6 @@ const componentKeys = useComponentKeys('MapComponentTrackReal'); |
| 6 | 7 | export const MapComponentTrackRealConfig: ConfigType = { |
| 7 | 8 | ...componentKeys, |
| 8 | 9 | title: '实时轨迹', |
| 10 | + category: CategoryEnum.MAP, | |
| 9 | 11 | package: PackagesCategoryEnum.MAP, |
| 10 | 12 | }; | ... | ... |
| ... | ... | @@ -9,6 +9,7 @@ |
| 9 | 9 | import { useMapTrackPlayBack } from './useMapTrackPlayback'; |
| 10 | 10 | import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; |
| 11 | 11 | import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 12 | + import get from 'lodash-es/get'; | |
| 12 | 13 | |
| 13 | 14 | const props = defineProps<{ |
| 14 | 15 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -24,44 +25,36 @@ |
| 24 | 25 | return props.config.option.dataSource?.at(0)?.deviceId; |
| 25 | 26 | }); |
| 26 | 27 | |
| 27 | - const getDesign = computed(() => { | |
| 28 | - const { option } = props.config; | |
| 29 | - const { dataSource } = option || {}; | |
| 30 | - return { | |
| 31 | - dataSource: dataSource, | |
| 32 | - }; | |
| 33 | - }); | |
| 34 | - | |
| 35 | 28 | /** |
| 36 | 29 | * @description 经度key |
| 37 | 30 | */ |
| 38 | 31 | const getLngKey = computed(() => { |
| 39 | - return props.config.option.dataSource?.at(0)?.attribute || ''; | |
| 32 | + return props.config.option.dataSource?.at(0)?.longitudeIdentifier || []; | |
| 40 | 33 | }); |
| 41 | 34 | |
| 42 | 35 | /** |
| 43 | 36 | * @description 纬度key |
| 44 | 37 | */ |
| 45 | 38 | const getLatKey = computed(() => { |
| 46 | - return props.config.option.dataSource?.at(1)?.attribute || ''; | |
| 39 | + return props.config.option.dataSource?.at(0)?.latitudeIdentifier || []; | |
| 47 | 40 | }); |
| 48 | 41 | |
| 49 | 42 | const validEffective = (value = '') => { |
| 50 | 43 | return !!(value && !isNaN(value as unknown as number)); |
| 51 | 44 | }; |
| 52 | 45 | |
| 53 | - const getTwoMap = (message, deviceId) => { | |
| 46 | + const handleMessage = (message, deviceId) => { | |
| 54 | 47 | if (unref(getDeviceId) !== deviceId) return; |
| 55 | 48 | |
| 56 | 49 | const { data = {} } = message; |
| 57 | 50 | |
| 58 | 51 | const bindMessage = data[deviceId]; |
| 59 | 52 | |
| 60 | - const lngData = bindMessage[unref(getLngKey)] || []; | |
| 53 | + const lngData = get(bindMessage, unref(getLngKey)) || []; | |
| 61 | 54 | const [lngLatest] = lngData; |
| 62 | 55 | const [, lng] = lngLatest || []; |
| 63 | 56 | |
| 64 | - const latData = bindMessage[unref(getLatKey)] || []; | |
| 57 | + const latData = get(bindMessage, unref(getLatKey)) || []; | |
| 65 | 58 | const [latLatest] = latData; |
| 66 | 59 | const [, lat] = latLatest || []; |
| 67 | 60 | |
| ... | ... | @@ -70,32 +63,8 @@ |
| 70 | 63 | } |
| 71 | 64 | }; |
| 72 | 65 | |
| 73 | - const getOneMap = (message, deviceId) => { | |
| 74 | - if (unref(getDeviceId) !== deviceId) return; | |
| 75 | - | |
| 76 | - const { longitude, latitude, attribute } = unref(getDesign)?.dataSource?.[0] || {}; | |
| 77 | - const { data } = message || {}; | |
| 78 | - const bindMessage = data[deviceId]; | |
| 79 | - const [, values] = attribute && bindMessage?.[attribute][0]; | |
| 80 | - | |
| 81 | - const mapValues = values ? JSON.parse(values) : {}; | |
| 82 | - const lng = longitude && mapValues[longitude]; | |
| 83 | - const lat = latitude && mapValues[latitude]; | |
| 84 | - | |
| 85 | - if (validEffective(lng) && validEffective(lat)) { | |
| 86 | - drawLine({ lng: Number(lng), lat: Number(lat) }); | |
| 87 | - } | |
| 88 | - }; | |
| 89 | - | |
| 90 | 66 | const updateFn: MultipleDataFetchUpdateFn = (message, deviceId) => { |
| 91 | - const { lal } = unref(getDesign)?.dataSource?.[0] || {}; | |
| 92 | - // 属性选择结构体类型时 | |
| 93 | - if (lal && unref(getDesign)?.dataSource?.length == 1) { | |
| 94 | - getOneMap(message, deviceId); | |
| 95 | - return; | |
| 96 | - } | |
| 97 | - // 选择两个属性时 | |
| 98 | - getTwoMap(message, deviceId); | |
| 67 | + handleMessage(message, deviceId); | |
| 99 | 68 | }; |
| 100 | 69 | |
| 101 | 70 | useMultipleDataFetch(props, updateFn); | ... | ... |
src/views/visual/packages/components/Text/SwitchSignalLight/config.ts
deleted
100644 → 0
| 1 | -import cloneDeep from 'lodash-es/cloneDeep'; | |
| 2 | -import { SwitchSignalLightConfig } from '.'; | |
| 3 | -import { | |
| 4 | - ConfigType, | |
| 5 | - CreateComponentType, | |
| 6 | - PublicComponentOptions, | |
| 7 | - PublicPresetOptions, | |
| 8 | -} from '/@/views/visual/packages/index.type'; | |
| 9 | -import { PublicConfigClass, componentInitConfig } from '/@/views/visual/packages/publicConfig'; | |
| 10 | -import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum'; | |
| 11 | - | |
| 12 | -export const option: PublicPresetOptions = { | |
| 13 | - [ComponentConfigFieldEnum.OPEN_COLOR]: '#30f230', | |
| 14 | - [ComponentConfigFieldEnum.CLOSE_COLOR]: '#eeeeee', | |
| 15 | - [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: true, | |
| 16 | -}; | |
| 17 | - | |
| 18 | -export default class Config extends PublicConfigClass implements CreateComponentType { | |
| 19 | - public key: string = SwitchSignalLightConfig.key; | |
| 20 | - | |
| 21 | - public attr = { ...componentInitConfig }; | |
| 22 | - | |
| 23 | - public componentConfig: ConfigType = cloneDeep(SwitchSignalLightConfig); | |
| 24 | - | |
| 25 | - public persetOption = cloneDeep(option); | |
| 26 | - | |
| 27 | - public option: PublicComponentOptions; | |
| 28 | - | |
| 29 | - constructor(option: PublicComponentOptions) { | |
| 30 | - super(); | |
| 31 | - this.option = { ...option }; | |
| 32 | - } | |
| 33 | -} |
src/views/visual/packages/components/Text/SwitchSignalLight/config.vue
deleted
100644 → 0
| 1 | -<script lang="ts" setup> | |
| 2 | - import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum'; | |
| 3 | - import { useForm, BasicForm } from '/@/components/Form'; | |
| 4 | - import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; | |
| 5 | - import { option } from './config'; | |
| 6 | - | |
| 7 | - const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ | |
| 8 | - schemas: [ | |
| 9 | - { | |
| 10 | - field: ComponentConfigFieldEnum.OPEN_COLOR, | |
| 11 | - label: 'ON颜色', | |
| 12 | - component: 'ColorPicker', | |
| 13 | - changeEvent: 'update:value', | |
| 14 | - componentProps: { | |
| 15 | - defaultValue: option.openColor, | |
| 16 | - }, | |
| 17 | - }, | |
| 18 | - { | |
| 19 | - field: ComponentConfigFieldEnum.CLOSE_COLOR, | |
| 20 | - label: 'OFF颜色', | |
| 21 | - component: 'ColorPicker', | |
| 22 | - changeEvent: 'update:value', | |
| 23 | - componentProps: { | |
| 24 | - defaultValue: option.closeColor, | |
| 25 | - }, | |
| 26 | - }, | |
| 27 | - { | |
| 28 | - field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | |
| 29 | - label: '显示设备名称', | |
| 30 | - component: 'Checkbox', | |
| 31 | - defaultValue: option.showDeviceName, | |
| 32 | - }, | |
| 33 | - ], | |
| 34 | - showActionButtonGroup: false, | |
| 35 | - labelWidth: 120, | |
| 36 | - baseColProps: { | |
| 37 | - span: 12, | |
| 38 | - }, | |
| 39 | - }); | |
| 40 | - | |
| 41 | - const getFormValues = () => { | |
| 42 | - return getFieldsValue(); | |
| 43 | - }; | |
| 44 | - | |
| 45 | - const setFormValues = (data: Recordable) => { | |
| 46 | - return setFieldsValue(data); | |
| 47 | - }; | |
| 48 | - | |
| 49 | - defineExpose({ | |
| 50 | - getFormValues, | |
| 51 | - setFormValues, | |
| 52 | - resetFormValues: resetFields, | |
| 53 | - } as PublicFormInstaceType); | |
| 54 | -</script> | |
| 55 | - | |
| 56 | -<template> | |
| 57 | - <BasicForm @register="register" /> | |
| 58 | -</template> |
src/views/visual/packages/components/Text/SwitchSignalLight/datasource.vue
deleted
100644 → 0
| 1 | -<script lang="ts" setup> | |
| 2 | - import { commonDataSourceSchemas } from '../../../config/common.config'; | |
| 3 | - import { BasicForm, useForm } from '/@/components/Form'; | |
| 4 | - import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; | |
| 5 | - | |
| 6 | - const [register, { getFieldsValue, setFieldsValue, validate, resetFields }] = useForm({ | |
| 7 | - labelWidth: 0, | |
| 8 | - showActionButtonGroup: false, | |
| 9 | - layout: 'horizontal', | |
| 10 | - labelCol: { span: 0 }, | |
| 11 | - schemas: commonDataSourceSchemas(), | |
| 12 | - }); | |
| 13 | - | |
| 14 | - const getFormValues = () => { | |
| 15 | - return getFieldsValue(); | |
| 16 | - }; | |
| 17 | - | |
| 18 | - const setFormValues = (record: Recordable) => { | |
| 19 | - return setFieldsValue(record); | |
| 20 | - }; | |
| 21 | - | |
| 22 | - defineExpose({ | |
| 23 | - getFormValues, | |
| 24 | - setFormValues, | |
| 25 | - validate, | |
| 26 | - resetFormValues: resetFields, | |
| 27 | - } as PublicFormInstaceType); | |
| 28 | -</script> | |
| 29 | - | |
| 30 | -<template> | |
| 31 | - <BasicForm @register="register" /> | |
| 32 | -</template> |
src/views/visual/packages/components/Text/SwitchSignalLight/index.ts
deleted
100644 → 0
| 1 | -// import { ComponentEnum, ComponentNameEnum } from '../index.type'; | |
| 2 | -import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys'; | |
| 3 | -import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type'; | |
| 4 | - | |
| 5 | -const componentKeys = useComponentKeys('SwitchSignalLight'); | |
| 6 | -export const SwitchSignalLightConfig: ConfigType = { | |
| 7 | - ...componentKeys, | |
| 8 | - // title: ComponentNameEnum.TEXT_COMPONENT_1, | |
| 9 | - title: '开关量信号灯', | |
| 10 | - package: PackagesCategoryEnum.TEXT, | |
| 11 | -}; |
src/views/visual/packages/components/Text/SwitchSignalLight/index.vue
deleted
100644 → 0
| 1 | -<script lang="ts" setup> | |
| 2 | - import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; | |
| 3 | - import { option } from './config'; | |
| 4 | - import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; | |
| 5 | - import { ref, onMounted, unref } from 'vue'; | |
| 6 | - import { useIntervalFn } from '@vueuse/core'; | |
| 7 | - import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; | |
| 8 | - import { useReceiveValue } from '../../../hook/useReceiveValue'; | |
| 9 | - import { useDataFetch } from '../../../hook/socket/useSocket'; | |
| 10 | - import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; | |
| 11 | - | |
| 12 | - const props = defineProps<{ | |
| 13 | - config: ComponentPropsConfigType<typeof option>; | |
| 14 | - }>(); | |
| 15 | - | |
| 16 | - const isOpenClose = ref<boolean>(true); | |
| 17 | - | |
| 18 | - const randomFn = () => { | |
| 19 | - useIntervalFn(() => { | |
| 20 | - isOpenClose.value = !unref(isOpenClose); | |
| 21 | - }, 2000); | |
| 22 | - }; | |
| 23 | - const { getNumberValue } = useReceiveValue(); | |
| 24 | - const updateFn: DataFetchUpdateFn = (message, attribute) => { | |
| 25 | - const { data = {} } = message; | |
| 26 | - const [latest] = data[attribute] || []; | |
| 27 | - const [_, value] = latest; | |
| 28 | - isOpenClose.value = Boolean(getNumberValue(value)); | |
| 29 | - }; | |
| 30 | - | |
| 31 | - onMounted(() => { | |
| 32 | - !props.config.option.uuid && randomFn(); | |
| 33 | - }); | |
| 34 | - | |
| 35 | - useDataFetch(props, updateFn); | |
| 36 | - | |
| 37 | - const { getScale } = useComponentScale(props); | |
| 38 | -</script> | |
| 39 | - | |
| 40 | -<template> | |
| 41 | - <main :style="getScale" class="w-full h-full flex flex-col justify-center items-center"> | |
| 42 | - <DeviceName :config="config" class="text-center" /> | |
| 43 | - <div | |
| 44 | - style="--open-color: `${getDesign.openColor}`; --close-color: `${getDesign.closeColor}`" | |
| 45 | - :class="isOpenClose ? 'switch_open' : 'switch_close'" | |
| 46 | - > | |
| 47 | - </div> | |
| 48 | - </main> | |
| 49 | -</template> | |
| 50 | -<style lang="less" scoped> | |
| 51 | - .switch_open { | |
| 52 | - background: var(--open-color); | |
| 53 | - box-shadow: var(--open-color) 0 0 10px 3px; | |
| 54 | - width: 60px; | |
| 55 | - height: 60px; | |
| 56 | - border-radius: 50%; | |
| 57 | - margin: 10px 0; | |
| 58 | - } | |
| 59 | - | |
| 60 | - .switch_close { | |
| 61 | - background-color: var(--close-color); | |
| 62 | - box-shadow: none; | |
| 63 | - width: 42.023px; | |
| 64 | - height: 42.023px; | |
| 65 | - border-radius: 50%; | |
| 66 | - margin: 10px 0; | |
| 67 | - } | |
| 68 | -</style> |
| ... | ... | @@ -2,11 +2,13 @@ |
| 2 | 2 | import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; |
| 3 | 3 | import { option } from './config'; |
| 4 | 4 | import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; |
| 5 | - import { computed } from 'vue'; | |
| 5 | + import { computed, unref } from 'vue'; | |
| 6 | 6 | import { ref } from 'vue'; |
| 7 | 7 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
| 8 | 8 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
| 9 | 9 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 10 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 11 | + import { useThingsModelValueTransform } from '/@/views/visual/palette/hooks/useThingsModelValueTransform'; | |
| 10 | 12 | |
| 11 | 13 | const props = defineProps<{ |
| 12 | 14 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -14,6 +16,15 @@ |
| 14 | 16 | |
| 15 | 17 | const currentValue = ref<string | number>(123); |
| 16 | 18 | |
| 19 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 20 | + | |
| 21 | + const getThingModelTsl = computed(() => { | |
| 22 | + const { option } = props.config; | |
| 23 | + | |
| 24 | + const { deviceProfileId, attribute } = option; | |
| 25 | + return getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 26 | + }); | |
| 27 | + | |
| 17 | 28 | const getDesign = computed(() => { |
| 18 | 29 | const { persetOption = {}, option } = props.config; |
| 19 | 30 | |
| ... | ... | @@ -23,14 +34,14 @@ |
| 23 | 34 | valueSize: persetValueSize, |
| 24 | 35 | } = persetOption; |
| 25 | 36 | |
| 26 | - const { componentInfo, attribute, attributeRename, attributeName } = option; | |
| 37 | + const { componentInfo, attribute, attributeRename } = option; | |
| 27 | 38 | |
| 28 | 39 | const { fontColor, fontSize, valueSize } = componentInfo || {}; |
| 29 | 40 | return { |
| 30 | 41 | fontColor: fontColor || persetFontColor, |
| 31 | 42 | fontSize: fontSize || persetFontSize || 14, |
| 32 | 43 | valueSize: valueSize || persetValueSize || 20, |
| 33 | - attribute: attributeRename || attributeName || attribute, | |
| 44 | + attribute: attributeRename || unref(getThingModelTsl)?.functionName || attribute, | |
| 34 | 45 | }; |
| 35 | 46 | }); |
| 36 | 47 | |
| ... | ... | @@ -39,7 +50,10 @@ |
| 39 | 50 | const [latest] = data[attribute] || []; |
| 40 | 51 | if (!latest.length) return; |
| 41 | 52 | const [_, value] = latest; |
| 42 | - currentValue.value = value; | |
| 53 | + currentValue.value = useThingsModelValueTransform( | |
| 54 | + value, | |
| 55 | + unref(getThingModelTsl)?.specs?.dataType | |
| 56 | + ); | |
| 43 | 57 | }; |
| 44 | 58 | |
| 45 | 59 | useDataFetch(props, updateFn); |
| ... | ... | @@ -52,7 +66,7 @@ |
| 52 | 66 | <DeviceName :config="config" /> |
| 53 | 67 | |
| 54 | 68 | <h1 |
| 55 | - class="my-4 font-bold !my-2 truncate" | |
| 69 | + class="my-4 font-bold !my-2 truncate w-full text-center" | |
| 56 | 70 | :style="{ |
| 57 | 71 | color: getDesign.fontColor, |
| 58 | 72 | fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', |
| ... | ... | @@ -63,10 +77,8 @@ |
| 63 | 77 | <div |
| 64 | 78 | class="text-gray-500 truncate" |
| 65 | 79 | :style="{ fontSize: (getRatio ? getRatio * getDesign.fontSize : getDesign.fontSize) + 'px' }" |
| 66 | - >{{ getDesign.attribute || '温度' }}</div | |
| 67 | 80 | > |
| 68 | - <!-- <div v-if="config.option.componentInfo.showDeviceName"> | |
| 69 | - {{ config.option.deviceRename || config.option.deviceName }} | |
| 70 | - </div> --> | |
| 81 | + {{ getDesign.attribute || '温度' }} | |
| 82 | + </div> | |
| 71 | 83 | </main> |
| 72 | 84 | </template> | ... | ... |
| ... | ... | @@ -2,12 +2,14 @@ |
| 2 | 2 | import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; |
| 3 | 3 | import { option } from './config'; |
| 4 | 4 | import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; |
| 5 | - import { computed } from 'vue'; | |
| 5 | + import { computed, unref } from 'vue'; | |
| 6 | 6 | import { ref } from 'vue'; |
| 7 | 7 | import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; |
| 8 | 8 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
| 9 | 9 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
| 10 | 10 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 11 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 12 | + import { useThingsModelValueTransform } from '/@/views/visual/palette/hooks/useThingsModelValueTransform'; | |
| 11 | 13 | |
| 12 | 14 | const props = defineProps<{ |
| 13 | 15 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -16,6 +18,15 @@ |
| 16 | 18 | const currentValue = ref<string | number>(123); |
| 17 | 19 | const time = ref<Nullable<number>>(null); |
| 18 | 20 | |
| 21 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 22 | + | |
| 23 | + const getThingModelTsl = computed(() => { | |
| 24 | + const { option } = props.config; | |
| 25 | + | |
| 26 | + const { deviceProfileId, attribute } = option; | |
| 27 | + return getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 28 | + }); | |
| 29 | + | |
| 19 | 30 | const getDesign = computed(() => { |
| 20 | 31 | const { persetOption = {}, option } = props.config; |
| 21 | 32 | |
| ... | ... | @@ -25,7 +36,7 @@ |
| 25 | 36 | fontSize: persetFontSize, |
| 26 | 37 | } = persetOption; |
| 27 | 38 | |
| 28 | - const { componentInfo, attribute, attributeName, attributeRename } = option; | |
| 39 | + const { componentInfo, attribute, attributeRename } = option; | |
| 29 | 40 | |
| 30 | 41 | const { fontColor, valueSize, fontSize } = componentInfo || {}; |
| 31 | 42 | |
| ... | ... | @@ -33,7 +44,7 @@ |
| 33 | 44 | fontColor: fontColor || persetFontColor, |
| 34 | 45 | valueSize: valueSize || persetValueSize || 20, |
| 35 | 46 | fontSize: fontSize || persetFontSize || 14, |
| 36 | - attribute: attributeRename || attributeName || attribute, | |
| 47 | + attribute: attributeRename || unref(getThingModelTsl)?.functionName || attribute, | |
| 37 | 48 | }; |
| 38 | 49 | }); |
| 39 | 50 | |
| ... | ... | @@ -42,7 +53,10 @@ |
| 42 | 53 | const [info] = data[attribute] || []; |
| 43 | 54 | if (!info.length) return; |
| 44 | 55 | const [timespan, value] = info; |
| 45 | - currentValue.value = value; | |
| 56 | + currentValue.value = useThingsModelValueTransform( | |
| 57 | + value, | |
| 58 | + unref(getThingModelTsl)?.specs?.dataType | |
| 59 | + ); | |
| 46 | 60 | time.value = timespan; |
| 47 | 61 | }; |
| 48 | 62 | |
| ... | ... | @@ -54,9 +68,9 @@ |
| 54 | 68 | <template> |
| 55 | 69 | <main class="w-full h-full flex flex-col justify-center items-center"> |
| 56 | 70 | <DeviceName :config="config" /> |
| 57 | - <div class="flex-1 flex justify-center items-center flex-col" :style="getScale"> | |
| 71 | + <div class="flex-1 flex justify-center items-center flex-col w-full" :style="getScale"> | |
| 58 | 72 | <h1 |
| 59 | - class="my-4 font-bold !my-2 truncate" | |
| 73 | + class="my-4 font-bold !my-2 truncate w-full text-center" | |
| 60 | 74 | :style="{ |
| 61 | 75 | color: getDesign.fontColor, |
| 62 | 76 | fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', | ... | ... |
| ... | ... | @@ -2,13 +2,15 @@ |
| 2 | 2 | import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; |
| 3 | 3 | import { option } from './config'; |
| 4 | 4 | import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; |
| 5 | - import { ref } from 'vue'; | |
| 5 | + import { ref, unref } from 'vue'; | |
| 6 | 6 | import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; |
| 7 | 7 | import { SvgIcon } from '/@/components/Icon'; |
| 8 | 8 | import { computed } from 'vue'; |
| 9 | 9 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
| 10 | 10 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
| 11 | 11 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 12 | + import { useThingsModelValueTransform } from '/@/views/visual/palette/hooks/useThingsModelValueTransform'; | |
| 13 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 12 | 14 | |
| 13 | 15 | const props = defineProps<{ |
| 14 | 16 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -18,6 +20,15 @@ |
| 18 | 20 | |
| 19 | 21 | const time = ref<Nullable<number>>(null); |
| 20 | 22 | |
| 23 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 24 | + | |
| 25 | + const getThingModelTsl = computed(() => { | |
| 26 | + const { option } = props.config; | |
| 27 | + | |
| 28 | + const { deviceProfileId, attribute } = option; | |
| 29 | + return getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 30 | + }); | |
| 31 | + | |
| 21 | 32 | const getDesign = computed(() => { |
| 22 | 33 | const { persetOption = {}, option } = props.config; |
| 23 | 34 | |
| ... | ... | @@ -49,7 +60,10 @@ |
| 49 | 60 | const [latest] = data[attribute] || []; |
| 50 | 61 | if (!latest.length) return; |
| 51 | 62 | const [timespan, value] = latest; |
| 52 | - currentValue.value = value; | |
| 63 | + currentValue.value = useThingsModelValueTransform( | |
| 64 | + value, | |
| 65 | + unref(getThingModelTsl)?.specs?.dataType | |
| 66 | + ); | |
| 53 | 67 | time.value = timespan; |
| 54 | 68 | }; |
| 55 | 69 | |
| ... | ... | @@ -61,7 +75,7 @@ |
| 61 | 75 | <template> |
| 62 | 76 | <main :style="getScale" class="w-full h-full flex flex-col justify-center items-center"> |
| 63 | 77 | <DeviceName :config="config" /> |
| 64 | - <div class="flex-1 flex justify-center items-center flex-col"> | |
| 78 | + <div class="flex-1 flex justify-center items-center flex-col w-full"> | |
| 65 | 79 | <SvgIcon |
| 66 | 80 | :name="getDesign.icon!" |
| 67 | 81 | prefix="iconfont" |
| ... | ... | @@ -69,7 +83,7 @@ |
| 69 | 83 | :style="{ color: getDesign.iconColor }" |
| 70 | 84 | /> |
| 71 | 85 | <h1 |
| 72 | - class="font-bold m-2 truncate" | |
| 86 | + class="font-bold m-2 truncate w-full text-center" | |
| 73 | 87 | :style="{ |
| 74 | 88 | color: getDesign.fontColor, |
| 75 | 89 | fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', | ... | ... |
| ... | ... | @@ -2,12 +2,14 @@ |
| 2 | 2 | import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; |
| 3 | 3 | import { option } from './config'; |
| 4 | 4 | import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; |
| 5 | - import { computed } from 'vue'; | |
| 5 | + import { computed, unref } from 'vue'; | |
| 6 | 6 | import { ref } from 'vue'; |
| 7 | 7 | import { SvgIcon } from '/@/components/Icon'; |
| 8 | 8 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
| 9 | 9 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
| 10 | 10 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 11 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 12 | + import { useThingsModelValueTransform } from '/@/views/visual/palette/hooks/useThingsModelValueTransform'; | |
| 11 | 13 | |
| 12 | 14 | const props = defineProps<{ |
| 13 | 15 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -15,6 +17,15 @@ |
| 15 | 17 | |
| 16 | 18 | const currentValue = ref<string | number>(123); |
| 17 | 19 | |
| 20 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 21 | + | |
| 22 | + const getThingModelTsl = computed(() => { | |
| 23 | + const { option } = props.config; | |
| 24 | + | |
| 25 | + const { deviceProfileId, attribute } = option; | |
| 26 | + return getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 27 | + }); | |
| 28 | + | |
| 18 | 29 | const getDesign = computed(() => { |
| 19 | 30 | const { persetOption = {}, option } = props.config; |
| 20 | 31 | |
| ... | ... | @@ -27,7 +38,7 @@ |
| 27 | 38 | fontSize: persetFontSize, |
| 28 | 39 | } = persetOption; |
| 29 | 40 | |
| 30 | - const { componentInfo, attribute, attributeRename, attributeName } = option; | |
| 41 | + const { componentInfo, attribute, attributeRename } = option; | |
| 31 | 42 | |
| 32 | 43 | const { icon, iconColor, fontColor, unit, valueSize, fontSize } = componentInfo || {}; |
| 33 | 44 | return { |
| ... | ... | @@ -35,7 +46,7 @@ |
| 35 | 46 | unit: unit ?? perseUnit, |
| 36 | 47 | icon: icon || persetIcon, |
| 37 | 48 | fontColor: fontColor || persetFontColor, |
| 38 | - attribute: attributeRename || attributeName || attribute, | |
| 49 | + attribute: attributeRename || unref(getThingModelTsl)?.functionName || attribute, | |
| 39 | 50 | valueSize: valueSize || persetValueSize || 20, |
| 40 | 51 | fontSize: fontSize || persetFontSize || 14, |
| 41 | 52 | }; |
| ... | ... | @@ -46,7 +57,10 @@ |
| 46 | 57 | const [info] = data[attribute] || []; |
| 47 | 58 | if (!info.length) return; |
| 48 | 59 | const [_, value] = info; |
| 49 | - currentValue.value = value; | |
| 60 | + currentValue.value = useThingsModelValueTransform( | |
| 61 | + value, | |
| 62 | + unref(getThingModelTsl)?.specs?.dataType | |
| 63 | + ); | |
| 50 | 64 | }; |
| 51 | 65 | |
| 52 | 66 | useDataFetch(props, updateFn); |
| ... | ... | @@ -57,7 +71,7 @@ |
| 57 | 71 | <template> |
| 58 | 72 | <main class="w-full h-full flex flex-col justify-center items-center"> |
| 59 | 73 | <DeviceName :config="config" /> |
| 60 | - <div :style="getScale" class="flex-1 flex justify-center items-center flex-col"> | |
| 74 | + <div :style="getScale" class="flex-1 flex justify-center items-center flex-col w-full"> | |
| 61 | 75 | <SvgIcon |
| 62 | 76 | :name="getDesign.icon!" |
| 63 | 77 | prefix="iconfont" |
| ... | ... | @@ -65,7 +79,7 @@ |
| 65 | 79 | :style="{ color: getDesign.iconColor }" |
| 66 | 80 | /> |
| 67 | 81 | <h1 |
| 68 | - class="my-4 font-bold !my-2 truncate" | |
| 82 | + class="my-4 font-bold !my-2 truncate w-full text-center" | |
| 69 | 83 | :style="{ |
| 70 | 84 | color: getDesign.fontColor, |
| 71 | 85 | fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', | ... | ... |
| ... | ... | @@ -10,6 +10,7 @@ |
| 10 | 10 | import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; |
| 11 | 11 | import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 12 | 12 | import { useComponentScale } from '../../../hook/useComponentScale'; |
| 13 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 13 | 14 | |
| 14 | 15 | const props = defineProps<{ |
| 15 | 16 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -56,6 +57,8 @@ |
| 56 | 57 | }, |
| 57 | 58 | ]; |
| 58 | 59 | |
| 60 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 61 | + | |
| 59 | 62 | const getDesign = computed(() => { |
| 60 | 63 | const { persetOption = {}, option } = props.config; |
| 61 | 64 | const { dataSource = [] } = option || {}; |
| ... | ... | @@ -71,15 +74,17 @@ |
| 71 | 74 | dataSource: dataSource.map((item) => { |
| 72 | 75 | const { unit, fontColor, backgroundColor, maxNumber, valueSize, fontSize } = |
| 73 | 76 | item.componentInfo || {}; |
| 74 | - const { attribute, attributeRename, deviceName, deviceRename, deviceId, attributeName } = | |
| 77 | + const { attribute, attributeRename, deviceProfileId, deviceName, deviceRename, deviceId } = | |
| 75 | 78 | item; |
| 79 | + | |
| 80 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 76 | 81 | return { |
| 77 | 82 | unit: unit ?? presetUnit, |
| 78 | 83 | fontColor: fontColor ?? presetFontColor, |
| 79 | 84 | backgroundColor: backgroundColor ?? persetBackgroundColor, |
| 80 | 85 | deviceName: deviceRename || deviceName, |
| 81 | 86 | attribute, |
| 82 | - attributeName: attributeRename || attributeName, | |
| 87 | + attributeName: attributeRename || tsl?.functionName, | |
| 83 | 88 | maxNumber: maxNumber || persetMaxNumber, |
| 84 | 89 | id: deviceId, |
| 85 | 90 | valueSize: valueSize || persetValueSize || 20, | ... | ... |
| ... | ... | @@ -10,6 +10,8 @@ |
| 10 | 10 | import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; |
| 11 | 11 | import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 12 | 12 | import { useComponentScale } from '../../../hook/useComponentScale'; |
| 13 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
| 14 | + import { buildUUID } from '/@/utils/uuid'; | |
| 13 | 15 | |
| 14 | 16 | const props = defineProps<{ |
| 15 | 17 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -17,6 +19,8 @@ |
| 17 | 19 | |
| 18 | 20 | const time = ref<Nullable<number>>(null); |
| 19 | 21 | |
| 22 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 23 | + | |
| 20 | 24 | const getDesign = computed(() => { |
| 21 | 25 | const { persetOption = {}, option } = props.config; |
| 22 | 26 | const { dataSource = [] } = option || {}; |
| ... | ... | @@ -28,12 +32,13 @@ |
| 28 | 32 | valueSize: persetValueSize, |
| 29 | 33 | fontSize: persetFontSize, |
| 30 | 34 | } = persetOption || {}; |
| 35 | + | |
| 31 | 36 | return { |
| 32 | 37 | dataSource: dataSource.map((item) => { |
| 33 | 38 | const { fontColor, icon, iconColor, unit, valueSize, fontSize } = item.componentInfo; |
| 34 | - const { attribute, attributeRename, deviceName, deviceRename, deviceId, attributeName } = | |
| 39 | + const { attribute, attributeRename, deviceName, deviceRename, deviceId, deviceProfileId } = | |
| 35 | 40 | item; |
| 36 | - | |
| 41 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 37 | 42 | return { |
| 38 | 43 | unit: unit ?? persetUnit, |
| 39 | 44 | fontColor: fontColor ?? persetFontColor, |
| ... | ... | @@ -41,7 +46,7 @@ |
| 41 | 46 | iconColor: iconColor ?? persetIconColor, |
| 42 | 47 | attribute, |
| 43 | 48 | deviceName: deviceRename || deviceName, |
| 44 | - attributeName: attributeRename || attributeName, | |
| 49 | + attributeName: attributeRename || tsl?.functionName, | |
| 45 | 50 | id: deviceId, |
| 46 | 51 | valueSize: valueSize || persetValueSize || 20, |
| 47 | 52 | fontSize: fontSize || persetFontSize || 14, |
| ... | ... | @@ -49,8 +54,10 @@ |
| 49 | 54 | }), |
| 50 | 55 | }; |
| 51 | 56 | }); |
| 52 | - const defaultSvgList = ref<any>([ | |
| 57 | + | |
| 58 | + const defaultSvgList = ref([ | |
| 53 | 59 | { |
| 60 | + id: buildUUID(), | |
| 54 | 61 | value: 26.2, |
| 55 | 62 | deviceName: '温湿度', |
| 56 | 63 | attributeName: '温度', |
| ... | ... | @@ -58,8 +65,11 @@ |
| 58 | 65 | unit: '℃', |
| 59 | 66 | iconColor: '#367BFF', |
| 60 | 67 | fontColor: '#357CFB', |
| 68 | + fontSize: 16, | |
| 69 | + valueSize: 16, | |
| 61 | 70 | }, |
| 62 | 71 | { |
| 72 | + id: buildUUID(), | |
| 63 | 73 | value: 53.7, |
| 64 | 74 | deviceName: '温湿度', |
| 65 | 75 | attributeName: '湿度', |
| ... | ... | @@ -67,6 +77,8 @@ |
| 67 | 77 | unit: '℃', |
| 68 | 78 | iconColor: '#FFA000', |
| 69 | 79 | fontColor: '#FFA000', |
| 80 | + fontSize: 16, | |
| 81 | + valueSize: 16, | |
| 70 | 82 | }, |
| 71 | 83 | ]); |
| 72 | 84 | |
| ... | ... | @@ -108,7 +120,6 @@ |
| 108 | 120 | :size="getRatio ? 30 * getRatio : 30" |
| 109 | 121 | :style="{ color: item.iconColor }" |
| 110 | 122 | /> |
| 111 | - | |
| 112 | 123 | <div |
| 113 | 124 | class="text-gray-500 ml-6" |
| 114 | 125 | :style="{ fontSize: (getRatio ? getRatio * item.fontSize : item.fontSize) + 'px' }" |
| ... | ... | @@ -122,10 +133,9 @@ |
| 122 | 133 | fontSize: (getRatio ? getRatio * item.valueSize : item.valueSize) + 'px', |
| 123 | 134 | }" |
| 124 | 135 | > |
| 125 | - <span> {{ item.value || 0 }}</span> | |
| 136 | + <span> {{ (item as any).value || 0 }}</span> | |
| 126 | 137 | <span>{{ item.unit }} </span> |
| 127 | 138 | </span> |
| 128 | 139 | </div> |
| 129 | - <!-- <UpdateTime :time="time" /> --> | |
| 130 | 140 | </main> |
| 131 | 141 | </template> | ... | ... |
| ... | ... | @@ -3,21 +3,25 @@ import { useSelectWidgetKeys, useSelectWidgetMode } from '../../dataSourceBindPa |
| 3 | 3 | import { PackagesCategoryEnum } from '../index.type'; |
| 4 | 4 | import { getDeviceProfile } from '/@/api/alarm/position'; |
| 5 | 5 | import { getDeviceAttributes, getMeetTheConditionsDevice } from '/@/api/dataBoard'; |
| 6 | -import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model'; | |
| 7 | -import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | |
| 8 | -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | |
| 6 | +import { DeviceAttributeParams } from '/@/api/dataBoard/model'; | |
| 9 | 7 | import { getModelServices } from '/@/api/device/modelOfMatter'; |
| 10 | 8 | import { findDictItemByCode } from '/@/api/system/dict'; |
| 11 | -import { FormSchema, useComponentRegister } from '/@/components/Form'; | |
| 9 | +import { ApiCascader, FormSchema, useComponentRegister } from '/@/components/Form'; | |
| 12 | 10 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; |
| 13 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 14 | -import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data'; | |
| 15 | 11 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
| 16 | 12 | import { TaskTypeEnum } from '/@/views/task/center/config'; |
| 17 | 13 | import { createPickerSearch } from '/@/utils/pickerSearch'; |
| 18 | 14 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 15 | +import { CommandTypeEnum, CommandTypeNameEnum } from '/@/enums/deviceEnum'; | |
| 16 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
| 17 | +import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | |
| 18 | +import { ControlComponentEnum } from '../components/Control'; | |
| 19 | +import { isNullOrUnDef } from '/@/utils/is'; | |
| 20 | +import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm'; | |
| 21 | +import { CategoryEnum } from '../components'; | |
| 19 | 22 | |
| 20 | 23 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
| 24 | +useComponentRegister('ApiCascader', ApiCascader); | |
| 21 | 25 | |
| 22 | 26 | export interface CommonDataSourceBindValueType extends Record<DataSourceField, string> { |
| 23 | 27 | customCommand?: { |
| ... | ... | @@ -31,30 +35,51 @@ export interface CommonDataSourceBindValueType extends Record<DataSourceField, s |
| 31 | 35 | } |
| 32 | 36 | |
| 33 | 37 | export enum DataSourceField { |
| 34 | - IS_GATEWAY_DEVICE = 'gatewayDevice', | |
| 38 | + // IS_GATEWAY_DEVICE = 'gatewayDevice', | |
| 35 | 39 | DEVICE_TYPE = 'deviceType', |
| 36 | 40 | TRANSPORT_TYPE = 'transportType', |
| 37 | 41 | ORIGINATION_ID = 'organizationId', |
| 38 | 42 | DEVICE_ID = 'deviceId', |
| 39 | - DEVICE_CODE = 'deviceCode', //设备地址码 | |
| 43 | + // DEVICE_CODE = 'deviceCode', //设备地址码 | |
| 40 | 44 | DEVICE_PROFILE_ID = 'deviceProfileId', |
| 41 | 45 | ATTRIBUTE = 'attribute', |
| 42 | - ATTRIBUTE_NAME = 'attributeName', | |
| 46 | + // ATTRIBUTE_NAME = 'attributeName', | |
| 43 | 47 | ATTRIBUTE_RENAME = 'attributeRename', |
| 44 | 48 | DEVICE_NAME = 'deviceName', |
| 45 | 49 | DEVICE_RENAME = 'deviceRename', |
| 46 | - LONGITUDE_ATTRIBUTE = 'longitudeAttribute', | |
| 47 | - LATITUDE_ATTRIBUTE = 'latitudeAttribute', | |
| 50 | + | |
| 48 | 51 | CODE_TYPE = 'codeType', |
| 49 | - COMMAND = 'command', | |
| 52 | + // COMMAND = 'command', | |
| 53 | + | |
| 50 | 54 | OPEN_COMMAND = 'openCommand', |
| 51 | 55 | CLOSE_COMMAND = 'closeCommand', |
| 56 | + | |
| 52 | 57 | COMMAND_TYPE = 'commandType', |
| 53 | - SERVICE = 'service', | |
| 58 | + | |
| 59 | + // SERVICE = 'service', | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * @description 关服务标识符 | |
| 63 | + */ | |
| 54 | 64 | OPEN_SERVICE = 'openService', |
| 65 | + | |
| 66 | + /** | |
| 67 | + * @description 开服务标识符 | |
| 68 | + */ | |
| 55 | 69 | CLOSE_SERVICE = 'closeService', |
| 56 | - EXTENSION_DESC = 'extensionDesc', | |
| 57 | - CALL_TYPE = 'callType', | |
| 70 | + | |
| 71 | + /** | |
| 72 | + * @description 经度标识符 | |
| 73 | + */ | |
| 74 | + LONGITUDE_IDENTIFIER = 'longitudeIdentifier', | |
| 75 | + | |
| 76 | + /** | |
| 77 | + * @description 纬度标识符号 | |
| 78 | + */ | |
| 79 | + LATITUDE_IDENTIFIER = 'latitudeIdentifier', | |
| 80 | + | |
| 81 | + // EXTENSION_DESC = 'extensionDesc', | |
| 82 | + // CALL_TYPE = 'callType', | |
| 58 | 83 | } |
| 59 | 84 | |
| 60 | 85 | const isTcpProfile = (transportType: string) => transportType === TransportTypeEnum.TCP; |
| ... | ... | @@ -69,15 +94,6 @@ const isBooleanComponent = (componentKeys: { categoryKey?: string; componentKey? |
| 69 | 94 | ); |
| 70 | 95 | }; |
| 71 | 96 | |
| 72 | -const getDeviceService = async (deviceProfileId: string) => { | |
| 73 | - try { | |
| 74 | - const data = await getModelServices({ deviceProfileId }); | |
| 75 | - if (data) | |
| 76 | - return data.map((item) => ({ ...item, label: item.functionName, value: item.identifier })); | |
| 77 | - } catch (error) {} | |
| 78 | - return []; | |
| 79 | -}; | |
| 80 | - | |
| 81 | 97 | const getDeviceAttribute = async (params: DeviceAttributeParams) => { |
| 82 | 98 | try { |
| 83 | 99 | const data = await getDeviceAttributes(params); |
| ... | ... | @@ -94,24 +110,6 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 94 | 110 | |
| 95 | 111 | return [ |
| 96 | 112 | { |
| 97 | - field: DataSourceField.IS_GATEWAY_DEVICE, | |
| 98 | - component: 'Switch', | |
| 99 | - label: '是否是网关设备', | |
| 100 | - show: false, | |
| 101 | - }, | |
| 102 | - { | |
| 103 | - field: DataSourceField.DEVICE_NAME, | |
| 104 | - component: 'Input', | |
| 105 | - label: '设备名', | |
| 106 | - show: false, | |
| 107 | - }, | |
| 108 | - { | |
| 109 | - field: DataSourceField.TRANSPORT_TYPE, | |
| 110 | - component: 'Input', | |
| 111 | - label: '设备配置类型', | |
| 112 | - show: false, | |
| 113 | - }, | |
| 114 | - { | |
| 115 | 113 | field: DataSourceField.DEVICE_TYPE, |
| 116 | 114 | component: 'ApiSelect', |
| 117 | 115 | label: '设备类型', |
| ... | ... | @@ -127,13 +125,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 127 | 125 | valueField: 'itemValue', |
| 128 | 126 | labelField: 'itemText', |
| 129 | 127 | placeholder: '请选择设备类型', |
| 130 | - onChange: (value: DeviceTypeEnum) => { | |
| 128 | + onChange: () => { | |
| 131 | 129 | setFieldsValue({ |
| 132 | - [DataSourceField.IS_GATEWAY_DEVICE]: value === DeviceTypeEnum.GATEWAY, | |
| 133 | 130 | [DataSourceField.DEVICE_PROFILE_ID]: null, |
| 134 | 131 | [DataSourceField.DEVICE_ID]: null, |
| 135 | 132 | [DataSourceField.ATTRIBUTE]: null, |
| 136 | - [DataSourceField.ATTRIBUTE_NAME]: null, | |
| 137 | 133 | [DataSourceField.TRANSPORT_TYPE]: null, |
| 138 | 134 | }); |
| 139 | 135 | }, |
| ... | ... | @@ -142,6 +138,12 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 142 | 138 | }, |
| 143 | 139 | }, |
| 144 | 140 | { |
| 141 | + field: DataSourceField.TRANSPORT_TYPE, | |
| 142 | + label: '传输协议', | |
| 143 | + component: 'Input', | |
| 144 | + ifShow: false, | |
| 145 | + }, | |
| 146 | + { | |
| 145 | 147 | field: DataSourceField.DEVICE_PROFILE_ID, |
| 146 | 148 | component: 'ApiSelect', |
| 147 | 149 | label: '产品', |
| ... | ... | @@ -164,12 +166,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 164 | 166 | labelField: 'name', |
| 165 | 167 | valueField: 'id', |
| 166 | 168 | placeholder: '请选择产品', |
| 167 | - onChange: (_, option = {} as Record<'transportType', string>) => { | |
| 169 | + onChange: (_, option: Record<'transportType', string>) => { | |
| 168 | 170 | setFieldsValue({ |
| 169 | 171 | [DataSourceField.DEVICE_ID]: null, |
| 170 | 172 | [DataSourceField.ATTRIBUTE]: null, |
| 171 | - [DataSourceField.ATTRIBUTE_NAME]: null, | |
| 172 | - [DataSourceField.TRANSPORT_TYPE]: option[DataSourceField.TRANSPORT_TYPE], | |
| 173 | + [DataSourceField.TRANSPORT_TYPE]: option?.[DataSourceField.TRANSPORT_TYPE], | |
| 173 | 174 | [DataSourceField.COMMAND_TYPE]: null, |
| 174 | 175 | }); |
| 175 | 176 | }, |
| ... | ... | @@ -201,10 +202,10 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 201 | 202 | }, |
| 202 | 203 | }, |
| 203 | 204 | { |
| 204 | - field: DataSourceField.DEVICE_PROFILE_ID, | |
| 205 | + field: DataSourceField.DEVICE_NAME, | |
| 205 | 206 | component: 'Input', |
| 206 | - label: '', | |
| 207 | - show: false, | |
| 207 | + label: '设备名称', | |
| 208 | + ifShow: false, | |
| 208 | 209 | }, |
| 209 | 210 | { |
| 210 | 211 | field: DataSourceField.DEVICE_ID, |
| ... | ... | @@ -227,23 +228,32 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 227 | 228 | deviceProfileId, |
| 228 | 229 | deviceType, |
| 229 | 230 | }); |
| 230 | - if (data) | |
| 231 | - return data.map((item) => ({ | |
| 231 | + if (data) { | |
| 232 | + const result = data.map((item) => ({ | |
| 232 | 233 | ...item, |
| 233 | 234 | label: item.alias || item.name, |
| 234 | 235 | value: item.tbDeviceId, |
| 235 | 236 | deviceType: item.deviceType, |
| 236 | 237 | })); |
| 238 | + | |
| 239 | + if ( | |
| 240 | + unref(selectWidgetKeys).componentKey === | |
| 241 | + ControlComponentEnum.LATERAL_NUMERICAL_CONTROL | |
| 242 | + ) { | |
| 243 | + return result.filter((item) => item.codeType === TaskTypeEnum.MODBUS_RTU); | |
| 244 | + } | |
| 245 | + | |
| 246 | + return result; | |
| 247 | + } | |
| 237 | 248 | } catch (error) {} |
| 238 | 249 | } |
| 239 | 250 | return []; |
| 240 | 251 | }, |
| 241 | - onChange(_value, record: MasterDeviceList) { | |
| 252 | + onChange(_value, options: Record<'label' | 'codeType', string>) { | |
| 242 | 253 | setFieldsValue({ |
| 243 | - [DataSourceField.DEVICE_NAME]: record?.label, | |
| 244 | - [DataSourceField.CODE_TYPE]: record?.codeType, | |
| 245 | - [DataSourceField.DEVICE_CODE]: record?.code, | |
| 246 | 254 | [DataSourceField.COMMAND_TYPE]: null, |
| 255 | + [DataSourceField.DEVICE_NAME]: options?.label, | |
| 256 | + [DataSourceField.CODE_TYPE]: options?.codeType, | |
| 247 | 257 | }); |
| 248 | 258 | }, |
| 249 | 259 | placeholder: '请选择设备', |
| ... | ... | @@ -253,91 +263,9 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 253 | 263 | }, |
| 254 | 264 | { |
| 255 | 265 | field: DataSourceField.CODE_TYPE, |
| 266 | + label: '设备标识符类型', | |
| 256 | 267 | component: 'Input', |
| 257 | - label: '设备标识符', | |
| 258 | - show: false, | |
| 259 | - }, | |
| 260 | - | |
| 261 | - { | |
| 262 | - field: DataSourceField.DEVICE_CODE, | |
| 263 | - component: 'Input', | |
| 264 | - show: false, | |
| 265 | - label: '设备地址码', | |
| 266 | - }, | |
| 267 | - | |
| 268 | - { | |
| 269 | - field: DataSourceField.ATTRIBUTE_NAME, | |
| 270 | - component: 'Input', | |
| 271 | - label: '属性名', | |
| 272 | - show: false, | |
| 273 | - }, | |
| 274 | - { | |
| 275 | - field: DataSourceField.COMMAND_TYPE, | |
| 276 | - component: 'ApiSelect', | |
| 277 | - label: '命令类型', | |
| 278 | - defaultValue: CommandTypeEnum.CUSTOM.toString(), | |
| 279 | - rules: [{ required: true, message: '请选择命令类型' }], | |
| 280 | - colProps: { span: 8 }, | |
| 281 | - ifShow: ({ model }) => { | |
| 282 | - return isControlComponent(category!) && isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]); | |
| 283 | - }, | |
| 284 | - componentProps: ({ formActionType, formModel }) => { | |
| 285 | - const { setFieldsValue } = formActionType; | |
| 286 | - const { codeType, deviceType, transportType } = formModel || {}; | |
| 287 | - return { | |
| 288 | - // api: findDictItemByCode, | |
| 289 | - api: async (params: Recordable) => { | |
| 290 | - try { | |
| 291 | - const record = await findDictItemByCode(params); | |
| 292 | - if (unref(selectWidgetKeys).componentKey == 'LateralNumericalControl') { | |
| 293 | - return record.filter( | |
| 294 | - (item) => item.itemValue == CommandTypeEnum.ATTRIBUTE.toString() | |
| 295 | - ); | |
| 296 | - } | |
| 297 | - // TCP网关子 --> 不能要服务命令类型 | |
| 298 | - if (deviceType == 'SENSOR' && transportType == 'TCP') { | |
| 299 | - return codeType == 'MODBUS_RTU' | |
| 300 | - ? record.filter((item) => item.itemValue == CommandTypeEnum.ATTRIBUTE.toString()) | |
| 301 | - : codeType == 'CUSTOM' | |
| 302 | - ? record.filter((item) => item.itemValue == CommandTypeEnum.CUSTOM.toString()) | |
| 303 | - : []; | |
| 304 | - } | |
| 305 | - | |
| 306 | - if (codeType == TaskTypeEnum.MODBUS_RTU) { | |
| 307 | - // setFieldsValue({ [DataSourceField.COMMAND_TYPE]: undefined }); | |
| 308 | - return record.filter( | |
| 309 | - (item) => item.itemValue !== CommandTypeEnum.CUSTOM.toString() | |
| 310 | - ); | |
| 311 | - } else { | |
| 312 | - return record.filter( | |
| 313 | - (item) => item.itemValue !== CommandTypeEnum.ATTRIBUTE.toString() | |
| 314 | - ); | |
| 315 | - } | |
| 316 | - } catch (error) { | |
| 317 | - return []; | |
| 318 | - } | |
| 319 | - }, | |
| 320 | - params: { | |
| 321 | - dictCode: 'custom_define', | |
| 322 | - }, | |
| 323 | - labelField: 'itemText', | |
| 324 | - valueField: 'itemValue', | |
| 325 | - placeholder: '请选择命令类型', | |
| 326 | - getPopupContainer: () => document.body, | |
| 327 | - onChange() { | |
| 328 | - setFieldsValue({ | |
| 329 | - [DataSourceField.COMMAND]: null, | |
| 330 | - [DataSourceField.SERVICE]: null, | |
| 331 | - [DataSourceField.OPEN_COMMAND]: null, | |
| 332 | - [DataSourceField.CLOSE_COMMAND]: null, | |
| 333 | - [DataSourceField.OPEN_SERVICE]: null, | |
| 334 | - [DataSourceField.CLOSE_SERVICE]: null, | |
| 335 | - [DataSourceField.CALL_TYPE]: null, | |
| 336 | - [DataSourceField.ATTRIBUTE]: null, | |
| 337 | - }); | |
| 338 | - }, | |
| 339 | - }; | |
| 340 | - }, | |
| 268 | + ifShow: false, | |
| 341 | 269 | }, |
| 342 | 270 | { |
| 343 | 271 | field: DataSourceField.ATTRIBUTE, |
| ... | ... | @@ -345,9 +273,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 345 | 273 | label: '属性', |
| 346 | 274 | colProps: { span: 8 }, |
| 347 | 275 | rules: [{ required: true, message: '请选择属性' }], |
| 348 | - ifShow: ({ model }) => | |
| 349 | - !(isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && isControlComponent(category!)) || | |
| 350 | - model[DataSourceField.COMMAND_TYPE] == 2, | |
| 276 | + ifShow: () => category !== CategoryEnum.MAP, | |
| 351 | 277 | componentProps({ formModel, formActionType }) { |
| 352 | 278 | const { setFieldsValue } = formActionType; |
| 353 | 279 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; |
| ... | ... | @@ -359,20 +285,25 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 359 | 285 | deviceProfileId, |
| 360 | 286 | dataType: |
| 361 | 287 | (isControlComponent(category!) && |
| 362 | - unref(selectWidgetKeys).componentKey !== 'LateralNumericalControl') || | |
| 288 | + unref(selectWidgetKeys).componentKey !== | |
| 289 | + ControlComponentEnum.LATERAL_NUMERICAL_CONTROL) || | |
| 363 | 290 | isBooleanComponent(unref(selectWidgetKeys)) |
| 364 | 291 | ? DataTypeEnum.BOOL |
| 365 | 292 | : undefined, |
| 366 | 293 | }); |
| 367 | 294 | |
| 368 | 295 | // 选择控制组件4的时候只能选择属性且是(int double类型) |
| 369 | - if (unref(selectWidgetKeys).componentKey == 'LateralNumericalControl') { | |
| 296 | + if ( | |
| 297 | + unref(selectWidgetKeys).componentKey === | |
| 298 | + ControlComponentEnum.LATERAL_NUMERICAL_CONTROL | |
| 299 | + ) { | |
| 370 | 300 | setFieldsValue({ |
| 371 | 301 | [DataSourceField.COMMAND_TYPE]: CommandTypeEnum.ATTRIBUTE.toString(), |
| 372 | 302 | }); |
| 373 | 303 | return option.filter( |
| 374 | 304 | (item) => |
| 375 | - item.detail.dataType.type == 'INT' || item.detail.dataType.type == 'DOUBLE' | |
| 305 | + item.detail.dataType.type === DataTypeEnum.NUMBER_INT || | |
| 306 | + item.detail.dataType.type == DataTypeEnum.NUMBER_DOUBLE | |
| 376 | 307 | ); |
| 377 | 308 | } |
| 378 | 309 | return option; |
| ... | ... | @@ -381,76 +312,133 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 381 | 312 | return []; |
| 382 | 313 | }, |
| 383 | 314 | placeholder: '请选择属性', |
| 384 | - onChange(value: string, option: Record<'label' | 'value' | any, string>) { | |
| 385 | - const { detail }: any = option || {}; | |
| 386 | - setFieldsValue({ | |
| 387 | - [DataSourceField.ATTRIBUTE_NAME]: value ? option.label : null, | |
| 388 | - [DataSourceField.EXTENSION_DESC]: value ? JSON.stringify(option.extensionDesc) : '', | |
| 389 | - // 地图组件结构体 | |
| 390 | - lal: | |
| 391 | - category === 'MAP' && value && detail?.dataType.type === 'STRUCT' | |
| 392 | - ? JSON.stringify(detail?.dataType.specs) | |
| 393 | - : null, | |
| 394 | - latitude: null, | |
| 395 | - longitude: null, | |
| 396 | - }); | |
| 397 | - }, | |
| 398 | 315 | ...createPickerSearch(), |
| 399 | 316 | }; |
| 400 | 317 | }, |
| 401 | 318 | }, |
| 402 | 319 | { |
| 403 | - field: 'lal', | |
| 404 | - label: '经纬度存储的值', | |
| 405 | - component: 'Input', | |
| 406 | - show: false, | |
| 407 | - }, | |
| 408 | - { | |
| 409 | - field: 'longitude', | |
| 320 | + field: DataSourceField.LONGITUDE_IDENTIFIER, | |
| 410 | 321 | label: '经度', |
| 322 | + component: 'ApiCascader', | |
| 323 | + ifShow: ({}) => category === CategoryEnum.MAP, | |
| 411 | 324 | colProps: { span: 8 }, |
| 412 | - component: 'Select', | |
| 413 | - required: true, | |
| 414 | - ifShow: ({ model }) => category === 'MAP' && model.lal, | |
| 415 | - componentProps({ formModel }) { | |
| 416 | - const { lal } = formModel || {}; | |
| 325 | + changeEvent: 'update:value', | |
| 326 | + valueField: 'value', | |
| 327 | + componentProps: ({ formModel }) => { | |
| 328 | + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | |
| 417 | 329 | return { |
| 330 | + api: async () => { | |
| 331 | + try { | |
| 332 | + if (deviceProfileId) { | |
| 333 | + const options = await getDeviceAttributes({ | |
| 334 | + deviceProfileId, | |
| 335 | + }); | |
| 336 | + | |
| 337 | + const _options = options.map((item) => { | |
| 338 | + item = Object.assign(item, { functionName: item.name }); | |
| 339 | + if (item.detail.dataType.type === DataTypeEnum.STRUCT) { | |
| 340 | + return Object.assign(item, { specs: item.detail.dataType.specs }); | |
| 341 | + } | |
| 342 | + return item; | |
| 343 | + }); | |
| 344 | + return _options; | |
| 345 | + } | |
| 346 | + } catch (error) {} | |
| 347 | + return []; | |
| 348 | + }, | |
| 418 | 349 | placeholder: '请选择经度', |
| 419 | - options: lal | |
| 420 | - ? JSON.parse(lal)?.map((item) => ({ label: item.functionName, value: item.identifier })) | |
| 421 | - : [], | |
| 350 | + getPopupContainer: () => document.body, | |
| 351 | + fieldNames: { label: 'functionName', value: 'identifier', children: 'specs' }, | |
| 422 | 352 | }; |
| 423 | 353 | }, |
| 424 | 354 | }, |
| 425 | 355 | { |
| 426 | - field: 'latitude', | |
| 356 | + field: DataSourceField.LATITUDE_IDENTIFIER, | |
| 427 | 357 | label: '纬度', |
| 358 | + component: 'ApiCascader', | |
| 359 | + ifShow: ({}) => category === CategoryEnum.MAP, | |
| 428 | 360 | colProps: { span: 8 }, |
| 429 | - required: true, | |
| 430 | - component: 'Select', | |
| 431 | - ifShow: ({ model }) => category === 'MAP' && model.lal, | |
| 432 | - componentProps({ formModel }) { | |
| 433 | - const { lal } = formModel || {}; | |
| 361 | + changeEvent: 'update:value', | |
| 362 | + valueField: 'value', | |
| 363 | + componentProps: ({ formModel }) => { | |
| 364 | + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | |
| 365 | + | |
| 434 | 366 | return { |
| 367 | + api: async () => { | |
| 368 | + try { | |
| 369 | + if (deviceProfileId) { | |
| 370 | + const options = await getDeviceAttributes({ | |
| 371 | + deviceProfileId, | |
| 372 | + }); | |
| 373 | + | |
| 374 | + const _options = options.map((item) => { | |
| 375 | + item = Object.assign(item, { functionName: item.name }); | |
| 376 | + | |
| 377 | + if (item.detail.dataType.type === DataTypeEnum.STRUCT) { | |
| 378 | + return Object.assign(item, { specs: item.detail.dataType.specs }); | |
| 379 | + } | |
| 380 | + return item; | |
| 381 | + }); | |
| 382 | + | |
| 383 | + return _options; | |
| 384 | + } | |
| 385 | + } catch (error) {} | |
| 386 | + return []; | |
| 387 | + }, | |
| 435 | 388 | placeholder: '请选择纬度', |
| 436 | - options: lal | |
| 437 | - ? JSON.parse(lal)?.map((item) => ({ label: item.functionName, value: item.identifier })) | |
| 438 | - : [], | |
| 389 | + getPopupContainer: () => document.body, | |
| 390 | + fieldNames: { label: 'functionName', value: 'identifier', children: 'specs' }, | |
| 439 | 391 | }; |
| 440 | 392 | }, |
| 441 | 393 | }, |
| 442 | 394 | { |
| 443 | - field: DataSourceField.EXTENSION_DESC, | |
| 444 | - component: 'Input', | |
| 445 | - show: false, | |
| 446 | - label: '扩展描述符', | |
| 447 | - }, | |
| 395 | + field: DataSourceField.COMMAND_TYPE, | |
| 396 | + component: 'Select', | |
| 397 | + label: '命令类型', | |
| 398 | + defaultValue: CommandTypeEnum.CUSTOM, | |
| 399 | + rules: [ | |
| 400 | + { | |
| 401 | + required: true, | |
| 402 | + validator(_, value: number | string) { | |
| 403 | + if (isNullOrUnDef(value)) return Promise.reject('请选择命令类型'); | |
| 404 | + return Promise.resolve(); | |
| 405 | + }, | |
| 406 | + }, | |
| 407 | + ], | |
| 408 | + colProps: { span: 8 }, | |
| 409 | + ifShow: ({ model }) => { | |
| 410 | + return ( | |
| 411 | + isControlComponent(category!) && | |
| 412 | + unref(selectWidgetKeys).componentKey !== ControlComponentEnum.LATERAL_NUMERICAL_CONTROL && | |
| 413 | + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && | |
| 414 | + model[DataSourceField.CODE_TYPE] === TaskTypeEnum.CUSTOM | |
| 415 | + ); | |
| 416 | + }, | |
| 417 | + componentProps: ({ formActionType, formModel }) => { | |
| 418 | + const { setFieldsValue } = formActionType; | |
| 419 | + const deviceType = formModel[DataSourceField.DEVICE_TYPE]; | |
| 420 | + const options: Record<'label' | 'value', string | number>[] = [ | |
| 421 | + { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM }, | |
| 422 | + ]; | |
| 448 | 423 | |
| 449 | - { | |
| 450 | - field: DataSourceField.CALL_TYPE, | |
| 451 | - component: 'Input', | |
| 452 | - ifShow: false, | |
| 453 | - label: 'callType', | |
| 424 | + // 网关子设备无服务 | |
| 425 | + if (deviceType !== DeviceTypeEnum.SENSOR) | |
| 426 | + options.push({ label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE }); | |
| 427 | + | |
| 428 | + return { | |
| 429 | + options, | |
| 430 | + placeholder: '请选择命令类型', | |
| 431 | + getPopupContainer: () => document.body, | |
| 432 | + onChange() { | |
| 433 | + setFieldsValue({ | |
| 434 | + [DataSourceField.OPEN_COMMAND]: null, | |
| 435 | + [DataSourceField.CLOSE_COMMAND]: null, | |
| 436 | + [DataSourceField.OPEN_SERVICE]: null, | |
| 437 | + [DataSourceField.CLOSE_SERVICE]: null, | |
| 438 | + }); | |
| 439 | + }, | |
| 440 | + }; | |
| 441 | + }, | |
| 454 | 442 | }, |
| 455 | 443 | { |
| 456 | 444 | field: DataSourceField.OPEN_SERVICE, |
| ... | ... | @@ -460,40 +448,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 460 | 448 | rules: [{ required: true, message: '请选择开服务' }], |
| 461 | 449 | ifShow: ({ model }) => |
| 462 | 450 | isControlComponent(category!) && |
| 463 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() && | |
| 451 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE && | |
| 464 | 452 | isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), |
| 465 | - componentProps({ formModel, formActionType }) { | |
| 466 | - const { setFieldsValue } = formActionType; | |
| 453 | + componentProps({ formModel }) { | |
| 467 | 454 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; |
| 468 | - const transportType = formModel[DataSourceField.TRANSPORT_TYPE]; | |
| 469 | - if (isUpdate && ![deviceProfileId, transportType].every(Boolean)) | |
| 470 | - return { placeholder: '请选择开服务', getPopupContainer: () => document.body }; | |
| 471 | 455 | return { |
| 472 | - api: async () => { | |
| 473 | - try { | |
| 474 | - if (deviceProfileId) { | |
| 475 | - const services = await getDeviceService(deviceProfileId); | |
| 476 | - const value = formModel[DataSourceField.SERVICE]; | |
| 477 | - if (value) { | |
| 478 | - const selected = services.find((item) => item.value === value); | |
| 479 | - selected && setFieldsValue({ [DataSourceField.CALL_TYPE]: selected.callType }); | |
| 480 | - } | |
| 481 | - return services; | |
| 482 | - } | |
| 483 | - } catch (error) {} | |
| 484 | - return []; | |
| 456 | + api: getModelServices, | |
| 457 | + params: { | |
| 458 | + deviceProfileId, | |
| 485 | 459 | }, |
| 486 | 460 | placeholder: '请选择开服务', |
| 461 | + labelField: 'functionName', | |
| 462 | + valueField: 'identifier', | |
| 487 | 463 | getPopupContainer: () => document.body, |
| 488 | - onChange(value: string, options: ModelOfMatterParams) { | |
| 489 | - const command = value | |
| 490 | - ? (options.functionJson.inputData || [])[0]?.serviceCommand | |
| 491 | - : null; | |
| 492 | - setFieldsValue({ | |
| 493 | - [DataSourceField.OPEN_COMMAND]: command, | |
| 494 | - [DataSourceField.CALL_TYPE]: value ? options.callType : null, | |
| 495 | - }); | |
| 496 | - }, | |
| 497 | 464 | }; |
| 498 | 465 | }, |
| 499 | 466 | }, |
| ... | ... | @@ -505,40 +472,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 505 | 472 | rules: [{ required: true, message: '请选择关服务' }], |
| 506 | 473 | ifShow: ({ model }) => |
| 507 | 474 | isControlComponent(category!) && |
| 508 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() && | |
| 475 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE && | |
| 509 | 476 | isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), |
| 510 | - componentProps({ formModel, formActionType }) { | |
| 511 | - const { setFieldsValue } = formActionType; | |
| 477 | + componentProps({ formModel }) { | |
| 512 | 478 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; |
| 513 | - const transportType = formModel[DataSourceField.TRANSPORT_TYPE]; | |
| 514 | - if (isUpdate && ![deviceProfileId, transportType].every(Boolean)) | |
| 515 | - return { placeholder: '请选择关服务', getPopupContainer: () => document.body }; | |
| 516 | 479 | return { |
| 517 | - api: async () => { | |
| 518 | - try { | |
| 519 | - if (deviceProfileId) { | |
| 520 | - const services = await getDeviceService(deviceProfileId); | |
| 521 | - const value = formModel[DataSourceField.SERVICE]; | |
| 522 | - if (value) { | |
| 523 | - const selected = services.find((item) => item.value === value); | |
| 524 | - selected && setFieldsValue({ [DataSourceField.CALL_TYPE]: selected.callType }); | |
| 525 | - } | |
| 526 | - return services; | |
| 527 | - } | |
| 528 | - } catch (error) {} | |
| 529 | - return []; | |
| 480 | + api: getModelServices, | |
| 481 | + params: { | |
| 482 | + deviceProfileId, | |
| 530 | 483 | }, |
| 531 | 484 | placeholder: '请选择关服务', |
| 485 | + labelField: 'functionName', | |
| 486 | + valueField: 'identifier', | |
| 532 | 487 | getPopupContainer: () => document.body, |
| 533 | - onChange(value: string, options: ModelOfMatterParams) { | |
| 534 | - const command = value | |
| 535 | - ? (options.functionJson.inputData || [])[0]?.serviceCommand | |
| 536 | - : null; | |
| 537 | - setFieldsValue({ | |
| 538 | - [DataSourceField.CLOSE_COMMAND]: command, | |
| 539 | - [DataSourceField.CALL_TYPE]: value ? options.callType : null, | |
| 540 | - }); | |
| 541 | - }, | |
| 542 | 488 | }; |
| 543 | 489 | }, |
| 544 | 490 | }, |
| ... | ... | @@ -547,11 +493,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 547 | 493 | component: 'Input', |
| 548 | 494 | label: '命令', |
| 549 | 495 | colProps: { span: 8 }, |
| 550 | - rules: [{ required: true, message: '请输入开下发命令' }], | |
| 496 | + rules: [{ validator: validateTCPCustomCommand }], | |
| 551 | 497 | // 是控制组件 && 自定义命令 && 传输协议为TCP |
| 552 | 498 | ifShow: ({ model }) => |
| 553 | 499 | isControlComponent(category!) && |
| 554 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM.toString() && | |
| 500 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM && | |
| 555 | 501 | model[DataSourceField.TRANSPORT_TYPE] && |
| 556 | 502 | isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), |
| 557 | 503 | componentProps: { |
| ... | ... | @@ -563,11 +509,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 563 | 509 | component: 'Input', |
| 564 | 510 | label: '命令', |
| 565 | 511 | colProps: { span: 8 }, |
| 566 | - rules: [{ required: true, message: '请输入关下发命令' }], | |
| 512 | + rules: [{ validator: validateTCPCustomCommand }], | |
| 567 | 513 | // 是控制组件 && 自定义命令 && 传输协议为TCP |
| 568 | 514 | ifShow: ({ model }) => |
| 569 | 515 | isControlComponent(category!) && |
| 570 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM.toString() && | |
| 516 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM && | |
| 571 | 517 | model[DataSourceField.TRANSPORT_TYPE] && |
| 572 | 518 | isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), |
| 573 | 519 | componentProps: { | ... | ... |
| ... | ... | @@ -24,6 +24,7 @@ import { |
| 24 | 24 | } from './useSocket.type'; |
| 25 | 25 | import { ComponentPropsConfigType } from '../../index.type'; |
| 26 | 26 | import { isNullOrUnDef } from '/@/utils/is'; |
| 27 | +import { CategoryEnum } from '../../components'; | |
| 27 | 28 | |
| 28 | 29 | interface DeviceGroupMapType { |
| 29 | 30 | subscriptionId: number; |
| ... | ... | @@ -410,8 +411,20 @@ export const useMultipleDataFetch = ( |
| 410 | 411 | Object.keys(unref(getDataSourceGroup)).forEach((key) => { |
| 411 | 412 | const item = unref(getDataSourceGroup)[key]; |
| 412 | 413 | const attributes = [...new Set(item.map((item) => item.attribute))]; |
| 414 | + | |
| 415 | + if (props.config.componentConfig.category === CategoryEnum.MAP) { | |
| 416 | + const positionIdentifier = item.reduce((prev, next) => { | |
| 417 | + const [lat] = next.latitudeIdentifier || []; | |
| 418 | + const [lng] = next.longitudeIdentifier || []; | |
| 419 | + | |
| 420 | + return [...prev, lat, lng]; | |
| 421 | + }, []); | |
| 422 | + | |
| 423 | + attributes.push(...new Set(positionIdentifier)); | |
| 424 | + } | |
| 425 | + | |
| 413 | 426 | subscriber.trackUpdateGroup(key, { |
| 414 | - attributes, | |
| 427 | + attributes: attributes.filter(Boolean), | |
| 415 | 428 | fn: updateFn, |
| 416 | 429 | }); |
| 417 | 430 | }); | ... | ... |
| 1 | +import { ref } from 'vue'; | |
| 2 | +import { getDeviceDetail } from '/@/api/device/deviceManager'; | |
| 3 | +import { useDeviceProfileQueryContext } from '../../palette/hooks/useDeviceProfileQueryContext'; | |
| 4 | +import { | |
| 5 | + CommandTypeEnum, | |
| 6 | + RPCCommandMethodEnum, | |
| 7 | + ServiceCallTypeEnum, | |
| 8 | + TransportTypeEnum, | |
| 9 | +} from '/@/enums/deviceEnum'; | |
| 10 | +import { RpcCommandType } from '/@/api/device/model/deviceConfigModel'; | |
| 11 | +import { useMessage } from '/@/hooks/web/useMessage'; | |
| 12 | +import { useGetModbusCommand } from './useGetModbusCommand'; | |
| 13 | +import { TaskTypeEnum } from '/@/views/task/center/config'; | |
| 14 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 15 | +import { sendCommandOneway, sendCommandTwoway } from '/@/api/dataBoard'; | |
| 16 | +import { isNullOrUnDef } from '/@/utils/is'; | |
| 17 | + | |
| 18 | +export interface DoCommandDeliverDataSourceType { | |
| 19 | + deviceId: string; | |
| 20 | + deviceProfileId: string; | |
| 21 | + attribute: string; | |
| 22 | + commandType?: CommandTypeEnum; | |
| 23 | + openCommand?: string; | |
| 24 | + closeCommand?: string; | |
| 25 | + openService?: string; | |
| 26 | + closeService?: string; | |
| 27 | +} | |
| 28 | + | |
| 29 | +export function useCommandDelivery() { | |
| 30 | + const loading = ref(false); | |
| 31 | + | |
| 32 | + const { createMessage } = useMessage(); | |
| 33 | + | |
| 34 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
| 35 | + | |
| 36 | + const doGetDeviceId = async (deviceId: string) => { | |
| 37 | + return await getDeviceDetail(deviceId); | |
| 38 | + }; | |
| 39 | + | |
| 40 | + const { validateCanGetCommand, getModbusCommand } = useGetModbusCommand(); | |
| 41 | + | |
| 42 | + const doCommandDeliver = async ( | |
| 43 | + dataSource: DoCommandDeliverDataSourceType, | |
| 44 | + value: any | |
| 45 | + ): Promise<boolean> => { | |
| 46 | + try { | |
| 47 | + const { deviceId, deviceProfileId, attribute, commandType } = dataSource; | |
| 48 | + | |
| 49 | + loading.value = true; | |
| 50 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
| 51 | + const deviceDetail = await doGetDeviceId(deviceId); | |
| 52 | + | |
| 53 | + let sendCommandFn = sendCommandOneway; | |
| 54 | + | |
| 55 | + let params: string | Recordable | undefined = { | |
| 56 | + [attribute]: value, | |
| 57 | + }; | |
| 58 | + | |
| 59 | + if (deviceDetail.transportType === TransportTypeEnum.TCP) { | |
| 60 | + if (deviceDetail.codeType === TaskTypeEnum.MODBUS_RTU) { | |
| 61 | + if (!validateCanGetCommand(tsl?.extensionDesc, deviceDetail.code)) return false; | |
| 62 | + params = await getModbusCommand(value, tsl!.extensionDesc, deviceDetail.code!); | |
| 63 | + } else if (deviceDetail.codeType === TaskTypeEnum.CUSTOM) { | |
| 64 | + params = value; | |
| 65 | + if (commandType === CommandTypeEnum.CUSTOM) { | |
| 66 | + if (tsl?.specs?.dataType.type === DataTypeEnum.BOOL) { | |
| 67 | + const { openCommand, closeCommand } = dataSource; | |
| 68 | + params = !!Number(value) ? openCommand : closeCommand; | |
| 69 | + } | |
| 70 | + } else if (commandType === CommandTypeEnum.SERVICE) { | |
| 71 | + if (tsl?.specs?.dataType.type === DataTypeEnum.BOOL) { | |
| 72 | + const { openService, closeService } = dataSource; | |
| 73 | + const serviceIdentifier = !!Number(value) ? openService : closeService; | |
| 74 | + const serviceTsl = getDeviceProfileTslByIdWithIdentifier?.( | |
| 75 | + deviceProfileId, | |
| 76 | + serviceIdentifier! | |
| 77 | + ); | |
| 78 | + params = serviceTsl?.inputData?.[0].serviceCommand; | |
| 79 | + sendCommandFn = | |
| 80 | + serviceTsl?.callType === ServiceCallTypeEnum.SYNC | |
| 81 | + ? sendCommandTwoway | |
| 82 | + : sendCommandOneway; | |
| 83 | + } | |
| 84 | + } | |
| 85 | + } | |
| 86 | + } | |
| 87 | + | |
| 88 | + if (isNullOrUnDef(params)) return false; | |
| 89 | + | |
| 90 | + const rpcCommand: RpcCommandType = { | |
| 91 | + additionalInfo: { | |
| 92 | + cmdType: CommandTypeEnum.API, | |
| 93 | + }, | |
| 94 | + params, | |
| 95 | + method: RPCCommandMethodEnum.THINGSKIT, | |
| 96 | + persistent: true, | |
| 97 | + }; | |
| 98 | + | |
| 99 | + await sendCommandFn({ deviceId: deviceId, value: rpcCommand }); | |
| 100 | + | |
| 101 | + createMessage.success('命令下发成功'); | |
| 102 | + | |
| 103 | + return true; | |
| 104 | + } catch (e) { | |
| 105 | + return false; | |
| 106 | + } finally { | |
| 107 | + loading.value = false; | |
| 108 | + } | |
| 109 | + }; | |
| 110 | + | |
| 111 | + return { | |
| 112 | + loading, | |
| 113 | + doCommandDeliver, | |
| 114 | + }; | |
| 115 | +} | ... | ... |
| 1 | +import { ExtensionDesc } from '/@/api/device/model/modelOfMatterModel'; | |
| 2 | +import { genModbusCommand } from '/@/api/task'; | |
| 3 | +import { GenModbusCommandType } from '/@/api/task/model'; | |
| 4 | +import { ModbusCRCEnum, RegisterActionTypeEnum } from '/@/enums/objectModelEnum'; | |
| 5 | +import { useMessage } from '/@/hooks/web/useMessage'; | |
| 6 | + | |
| 7 | +export const InsertString = (t: any, c: any, n: any) => { | |
| 8 | + const r: string | number[] = []; | |
| 9 | + | |
| 10 | + for (let i = 0; i * 2 < t.length; i++) r.push(t.substr(i * 2, n)); | |
| 11 | + | |
| 12 | + return r.join(c); | |
| 13 | +}; | |
| 14 | + | |
| 15 | +export const FillString = (t: any, c: any, n: any, b: any) => { | |
| 16 | + if (t === '' || c.length !== 1 || n <= t.length) return t; | |
| 17 | + | |
| 18 | + const l = t.length; | |
| 19 | + | |
| 20 | + for (let i = 0; i < n - l; i++) { | |
| 21 | + if (b === true) t = c + t; | |
| 22 | + else t += c; | |
| 23 | + } | |
| 24 | + return t; | |
| 25 | +}; | |
| 26 | + | |
| 27 | +export const SingleToHex = (t: any) => { | |
| 28 | + if (t === '') return ''; | |
| 29 | + | |
| 30 | + t = parseFloat(t); | |
| 31 | + | |
| 32 | + if (isNaN(t) === true) return 'Error'; | |
| 33 | + | |
| 34 | + if (t === 0) return '00000000'; | |
| 35 | + | |
| 36 | + let s, e, m; | |
| 37 | + | |
| 38 | + if (t > 0) { | |
| 39 | + s = 0; | |
| 40 | + } else { | |
| 41 | + s = 1; | |
| 42 | + | |
| 43 | + t = 0 - t; | |
| 44 | + } | |
| 45 | + m = t.toString(2); | |
| 46 | + | |
| 47 | + if (m >= 1) { | |
| 48 | + if (m.indexOf('.') === -1) m = `${m}.0`; | |
| 49 | + | |
| 50 | + e = m.indexOf('.') - 1; | |
| 51 | + } else { | |
| 52 | + e = 1 - m.indexOf('1'); | |
| 53 | + } | |
| 54 | + if (e >= 0) m = m.replace('.', ''); | |
| 55 | + else m = m.substring(m.indexOf('1')); | |
| 56 | + | |
| 57 | + if (m.length > 24) m = m.substr(0, 24); | |
| 58 | + else m = FillString(m, '0', 24, false); | |
| 59 | + | |
| 60 | + m = m.substring(1); | |
| 61 | + | |
| 62 | + e = (e + 127).toString(2); | |
| 63 | + | |
| 64 | + e = FillString(e, '0', 8, true); | |
| 65 | + | |
| 66 | + let r = parseInt(s + e + m, 2).toString(16); | |
| 67 | + | |
| 68 | + r = FillString(r, '0', 8, true); | |
| 69 | + | |
| 70 | + return InsertString(r, ' ', 2).toUpperCase(); | |
| 71 | +}; | |
| 72 | + | |
| 73 | +export const FormatHex = (t: any, n: any, ie: any) => { | |
| 74 | + const r: string[] = []; | |
| 75 | + | |
| 76 | + let s = ''; | |
| 77 | + | |
| 78 | + let c = 0; | |
| 79 | + | |
| 80 | + for (let i = 0; i < t.length; i++) { | |
| 81 | + if (t.charAt(i) !== ' ') { | |
| 82 | + s += t.charAt(i); | |
| 83 | + | |
| 84 | + c += 1; | |
| 85 | + | |
| 86 | + if (c === n) { | |
| 87 | + r.push(s); | |
| 88 | + | |
| 89 | + s = ''; | |
| 90 | + | |
| 91 | + c = 0; | |
| 92 | + } | |
| 93 | + } | |
| 94 | + if (ie === false) { | |
| 95 | + if (i === t.length - 1 && s !== '') r.push(s); | |
| 96 | + } | |
| 97 | + } | |
| 98 | + return r.join('\n'); | |
| 99 | +}; | |
| 100 | + | |
| 101 | +export const FormatHexBatch = (t: any, n: any, ie: any) => { | |
| 102 | + const a = t.split('\n'); | |
| 103 | + | |
| 104 | + const r: string[] = []; | |
| 105 | + | |
| 106 | + for (let i = 0; i < a.length; i++) r[i] = FormatHex(a[i], n, ie); | |
| 107 | + | |
| 108 | + return r.join('\n'); | |
| 109 | +}; | |
| 110 | + | |
| 111 | +export const SingleToHexBatch = (t: any) => { | |
| 112 | + const a = t.split('\n'); | |
| 113 | + | |
| 114 | + const r: string[] = []; | |
| 115 | + | |
| 116 | + for (let i = 0; i < a.length; i++) r[i] = SingleToHex(a[i]); | |
| 117 | + | |
| 118 | + return r.join('\r\n'); | |
| 119 | +}; | |
| 120 | + | |
| 121 | +const getArray = (values: any) => { | |
| 122 | + const str = values.replace(/\s+/g, ''); | |
| 123 | + const array: any = []; | |
| 124 | + | |
| 125 | + for (let i = 0; i < str.length; i += 4) { | |
| 126 | + const chunk = parseInt(str.substring(i, i + 4), 16); | |
| 127 | + array.push(chunk); | |
| 128 | + } | |
| 129 | + return array; | |
| 130 | +}; | |
| 131 | + | |
| 132 | +const getFloatPart = (number: string | number) => { | |
| 133 | + const isLessZero = Number(number) < 0; | |
| 134 | + number = number.toString(); | |
| 135 | + const floatPartStartIndex = number.indexOf('.'); | |
| 136 | + const value = ~floatPartStartIndex | |
| 137 | + ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}` | |
| 138 | + : '0'; | |
| 139 | + return Number(value); | |
| 140 | +}; | |
| 141 | + | |
| 142 | +const REGISTER_MAX_VALUE = Number(0xffff); | |
| 143 | + | |
| 144 | +export function useGetModbusCommand() { | |
| 145 | + const { createMessage } = useMessage(); | |
| 146 | + | |
| 147 | + const getModbusCommand = async ( | |
| 148 | + value: number, | |
| 149 | + extensionDesc: ExtensionDesc, | |
| 150 | + deviceAddressCode: string | |
| 151 | + ) => { | |
| 152 | + const { registerAddress, actionType, zoomFactor } = extensionDesc as Required<ExtensionDesc>; | |
| 153 | + | |
| 154 | + const params: GenModbusCommandType = { | |
| 155 | + crc: ModbusCRCEnum.CRC_16_LOWER, | |
| 156 | + registerNumber: 1, | |
| 157 | + deviceCode: deviceAddressCode, | |
| 158 | + registerAddress, | |
| 159 | + method: actionType, | |
| 160 | + registerValues: [value], | |
| 161 | + }; | |
| 162 | + | |
| 163 | + if (actionType === RegisterActionTypeEnum.INT) { | |
| 164 | + const newValue = Math.trunc(value) * zoomFactor + getFloatPart(value) * zoomFactor; | |
| 165 | + | |
| 166 | + if (newValue % 1 !== 0) { | |
| 167 | + createMessage.warning(`属性下发类型必须是整数,缩放因子为${zoomFactor}`); | |
| 168 | + return; | |
| 169 | + } | |
| 170 | + | |
| 171 | + if (value * zoomFactor > REGISTER_MAX_VALUE) { | |
| 172 | + createMessage.warning(`属性下发值不能超过${REGISTER_MAX_VALUE},缩放因子为${zoomFactor}`); | |
| 173 | + return; | |
| 174 | + } | |
| 175 | + | |
| 176 | + params.registerValues = [newValue]; | |
| 177 | + } else if (actionType === RegisterActionTypeEnum.DOUBLE) { | |
| 178 | + const regex = /^-?\d+(\.\d{0,2})?$/; | |
| 179 | + const values = Math.trunc(value) * zoomFactor + getFloatPart(value) * zoomFactor; | |
| 180 | + | |
| 181 | + if (!regex.test(values.toString())) { | |
| 182 | + createMessage.warning(`属性下发值精确到两位小数,缩放因子为${zoomFactor}`); | |
| 183 | + return; | |
| 184 | + } | |
| 185 | + | |
| 186 | + const newValue = values === 0 ? [0, 0] : getArray(SingleToHex(values)); | |
| 187 | + params.registerValues = newValue; | |
| 188 | + params.registerNumber = 2; | |
| 189 | + params.method = '10'; | |
| 190 | + } | |
| 191 | + | |
| 192 | + return await genModbusCommand(params); | |
| 193 | + }; | |
| 194 | + | |
| 195 | + /** | |
| 196 | + * | |
| 197 | + * @param extensionDesc 物模型拓展描述符 | |
| 198 | + * @param deviceAddressCode 设备地址码 | |
| 199 | + */ | |
| 200 | + const validateCanGetCommand = ( | |
| 201 | + extensionDesc?: ExtensionDesc, | |
| 202 | + deviceAddressCode?: string, | |
| 203 | + createValidateMessage = true | |
| 204 | + ) => { | |
| 205 | + const result = { flag: true, message: '' }; | |
| 206 | + if (!extensionDesc) { | |
| 207 | + result.flag = false; | |
| 208 | + result.message = '当前物模型扩展描述没有填写'; | |
| 209 | + } | |
| 210 | + | |
| 211 | + if (!deviceAddressCode) { | |
| 212 | + result.flag = false; | |
| 213 | + result.message = '当前设备未绑定设备地址码'; | |
| 214 | + } | |
| 215 | + | |
| 216 | + if (result.message && createValidateMessage) createMessage.warning(result.message); | |
| 217 | + | |
| 218 | + return result; | |
| 219 | + }; | |
| 220 | + | |
| 221 | + return { | |
| 222 | + getModbusCommand, | |
| 223 | + validateCanGetCommand, | |
| 224 | + }; | |
| 225 | +} | ... | ... |
src/views/visual/packages/hook/useSendCommand.ts
deleted
100644 → 0
| 1 | -import { ref } from 'vue'; | |
| 2 | -import { DataSource } from '../../palette/types'; | |
| 3 | -import { sendCommandOneway, sendCommandTwoway } from '/@/api/dataBoard'; | |
| 4 | -import { useMessage } from '/@/hooks/web/useMessage'; | |
| 5 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
| 6 | -import { ServiceCallTypeEnum } from '/@/enums/toolEnum'; | |
| 7 | - | |
| 8 | -const { createMessage } = useMessage(); | |
| 9 | -export function useSendCommand() { | |
| 10 | - const loading = ref(false); | |
| 11 | - | |
| 12 | - const error = () => { | |
| 13 | - // createMessage.error('下发指令失败'); | |
| 14 | - return false; | |
| 15 | - }; | |
| 16 | - | |
| 17 | - const sendCommand = async (record: DataSource, value: any, isModbusCommand = false) => { | |
| 18 | - if (!record) return false; | |
| 19 | - const { customCommand, attribute } = record || {}; | |
| 20 | - const { deviceId } = record; | |
| 21 | - if (!deviceId) return false; | |
| 22 | - | |
| 23 | - try { | |
| 24 | - loading.value = true; | |
| 25 | - let params: string | Recordable = { | |
| 26 | - [attribute!]: Number(value), | |
| 27 | - }; | |
| 28 | - if (isModbusCommand) { | |
| 29 | - params = value; | |
| 30 | - } | |
| 31 | - | |
| 32 | - let sendCommandFn = sendCommandOneway; | |
| 33 | - // 如果是TCP设备从物模型中获取下发命令(TCP网关子设备无物模型服务与事件) | |
| 34 | - if (customCommand?.transportType === TransportTypeEnum.TCP && !isModbusCommand) { | |
| 35 | - params = customCommand.command!; | |
| 36 | - if (customCommand.callType === ServiceCallTypeEnum.SYNC) { | |
| 37 | - sendCommandFn = sendCommandTwoway; | |
| 38 | - } | |
| 39 | - } | |
| 40 | - // 控制按钮下发命令为0 或 1 | |
| 41 | - await sendCommandFn({ | |
| 42 | - deviceId, | |
| 43 | - value: { | |
| 44 | - params: params || null, | |
| 45 | - persistent: true, | |
| 46 | - additionalInfo: { | |
| 47 | - cmdType: customCommand.commandType || 'API', | |
| 48 | - }, | |
| 49 | - method: 'methodThingskit', | |
| 50 | - }, | |
| 51 | - }); | |
| 52 | - createMessage.success('命令下发成功'); | |
| 53 | - return true; | |
| 54 | - } catch (msg) { | |
| 55 | - console.error(msg); | |
| 56 | - return error(); | |
| 57 | - } finally { | |
| 58 | - loading.value = false; | |
| 59 | - } | |
| 60 | - }; | |
| 61 | - return { | |
| 62 | - loading, | |
| 63 | - sendCommand, | |
| 64 | - }; | |
| 65 | -} |
| ... | ... | @@ -9,7 +9,7 @@ export const useApp = () => { |
| 9 | 9 | |
| 10 | 10 | const isPhone = () => { |
| 11 | 11 | const values = location?.pathname.split('/') || []; |
| 12 | - return values[values?.length - 2] === 'phone' ? true : false; | |
| 12 | + return values[values?.length - 2] === 'phone'; | |
| 13 | 13 | }; |
| 14 | 14 | |
| 15 | 15 | return { getIsAppPage, isPhone }; | ... | ... |
| ... | ... | @@ -11,6 +11,8 @@ import { |
| 11 | 11 | } from '../types'; |
| 12 | 12 | import { buildUUID } from '/@/utils/uuid'; |
| 13 | 13 | import { PublicComponentOptions } from '../../packages/index.type'; |
| 14 | +import { batchGetObjectModel } from '/@/api/device/modelOfMatter'; | |
| 15 | +import { BatchGetObjectModelItemType } from '/@/api/device/model/modelOfMatterModel'; | |
| 14 | 16 | |
| 15 | 17 | export interface WidgetDataType extends Layout, ComponentDataType, PublicComponentOptions {} |
| 16 | 18 | |
| ... | ... | @@ -23,6 +25,17 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { |
| 23 | 25 | |
| 24 | 26 | const dataSource = ref<WidgetDataType[]>([]); |
| 25 | 27 | |
| 28 | + const deviceProfilesMapRef = ref<Map<string, BatchGetObjectModelItemType>>(); | |
| 29 | + | |
| 30 | + const getAllDeviceProfileIdsByDataSource = computed(() => { | |
| 31 | + const result = unref(rawDataSource)?.componentData?.reduce((prev, next) => { | |
| 32 | + const ids = next.dataSource.map((temp) => temp.deviceProfileId); | |
| 33 | + return [...prev, ...ids]; | |
| 34 | + }, []); | |
| 35 | + | |
| 36 | + return [...new Set(result)]; | |
| 37 | + }); | |
| 38 | + | |
| 26 | 39 | const getSharePageData = computed(() => { |
| 27 | 40 | return unref(propsRef).value!; |
| 28 | 41 | }); |
| ... | ... | @@ -64,19 +77,8 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { |
| 64 | 77 | loading.value = true; |
| 65 | 78 | const { data } = await getDataBoradDetail(); |
| 66 | 79 | rawDataSource.value = data; |
| 67 | - // const result: WidgetDataType[] = (data.componentData || []).map((item) => { | |
| 68 | - // const { id } = item; | |
| 69 | - | |
| 70 | - // const layout = | |
| 71 | - // (data.componentLayout || []).find((item) => item.id === id) || | |
| 72 | - // ({} as ComponentLayoutType); | |
| 73 | - | |
| 74 | - // return { | |
| 75 | - // i: buildUUID(), | |
| 76 | - // ...layout, | |
| 77 | - // ...item, | |
| 78 | - // } as WidgetDataType; | |
| 79 | - // }); | |
| 80 | + await getAllDeviceProfiles(); | |
| 81 | + | |
| 80 | 82 | const result: WidgetDataType[] = data.componentLayout.map((item) => { |
| 81 | 83 | const { id } = item; |
| 82 | 84 | const dataSource = |
| ... | ... | @@ -129,8 +131,35 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { |
| 129 | 131 | } catch (error) {} |
| 130 | 132 | }; |
| 131 | 133 | |
| 132 | - onMounted(() => { | |
| 133 | - getDataSource(); | |
| 134 | + const getAllDeviceProfiles = async () => { | |
| 135 | + if (!unref(getAllDeviceProfileIdsByDataSource).length) return; | |
| 136 | + const result = await batchGetObjectModel({ | |
| 137 | + deviceProfileIds: unref(getAllDeviceProfileIdsByDataSource), | |
| 138 | + }); | |
| 139 | + | |
| 140 | + const map = new Map(); | |
| 141 | + | |
| 142 | + result.forEach((item) => { | |
| 143 | + map.set(item.id, item); | |
| 144 | + }); | |
| 145 | + | |
| 146 | + deviceProfilesMapRef.value = map; | |
| 147 | + }; | |
| 148 | + | |
| 149 | + function getDeviceProfileDetailById(id: string) { | |
| 150 | + return unref(deviceProfilesMapRef)?.get(id); | |
| 151 | + } | |
| 152 | + | |
| 153 | + function getDeviceProfileTslByIdWithIdentifier(id: string, identifier: string) { | |
| 154 | + const detail = getDeviceProfileDetailById(id); | |
| 155 | + if (!detail) return; | |
| 156 | + for (const item of detail.tsl) { | |
| 157 | + if (item.identifier === identifier) return item; | |
| 158 | + } | |
| 159 | + } | |
| 160 | + | |
| 161 | + onMounted(async () => { | |
| 162 | + await getDataSource(); | |
| 134 | 163 | }); |
| 135 | 164 | |
| 136 | 165 | return { |
| ... | ... | @@ -138,8 +167,11 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { |
| 138 | 167 | draggable: !unref(getIsSharePage) && !unref(getIsAppPage), |
| 139 | 168 | resizable: !unref(getIsSharePage) && !unref(getIsAppPage), |
| 140 | 169 | dataSource, |
| 170 | + deviceProfilesMapRef, | |
| 141 | 171 | rawDataSource, |
| 142 | 172 | getDataSource, |
| 143 | 173 | setLayoutInfo, |
| 174 | + getDeviceProfileDetailById, | |
| 175 | + getDeviceProfileTslByIdWithIdentifier, | |
| 144 | 176 | }; |
| 145 | 177 | }; | ... | ... |
| 1 | +import { inject, provide } from 'vue'; | |
| 2 | +import { BatchGetObjectModelItemType, Tsl } from '/@/api/device/model/modelOfMatterModel'; | |
| 3 | + | |
| 4 | +const SymbolKey = Symbol('data-board'); | |
| 5 | + | |
| 6 | +export interface ContextOptionsType { | |
| 7 | + getDeviceProfileDetailById: (id: string) => BatchGetObjectModelItemType | undefined; | |
| 8 | + getDeviceProfileTslByIdWithIdentifier: (id: string, identifier?: string) => Tsl | undefined; | |
| 9 | +} | |
| 10 | + | |
| 11 | +export const createDeviceProfileQueryContext = (options: ContextOptionsType) => { | |
| 12 | + provide(SymbolKey, options); | |
| 13 | +}; | |
| 14 | + | |
| 15 | +export const useDeviceProfileQueryContext = () => { | |
| 16 | + return inject<ContextOptionsType>(SymbolKey) || ({} as Partial<ContextOptionsType>); | |
| 17 | +}; | ... | ... |
| 1 | +import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | |
| 2 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
| 3 | +import { useJsonParse } from '/@/hooks/business/useJsonParse'; | |
| 4 | +function getBoolTypeValue(value: number, Specs: Specs) { | |
| 5 | + const { boolOpen, boolClose } = Specs; | |
| 6 | + | |
| 7 | + return Number(value) ? boolOpen : boolClose; | |
| 8 | +} | |
| 9 | + | |
| 10 | +function getEnumTypeValue(value: number, specsList: Specs[]) { | |
| 11 | + const res = specsList.find((item) => item.value === Number(value)); | |
| 12 | + | |
| 13 | + return res?.name; | |
| 14 | +} | |
| 15 | + | |
| 16 | +function getStructTypeValue(value: string, specs: StructJSON[]): string { | |
| 17 | + const res = useJsonParse(value).value; | |
| 18 | + | |
| 19 | + function generateStruct(specs: StructJSON[], value: Recordable) { | |
| 20 | + if (!value) return {}; | |
| 21 | + | |
| 22 | + return specs.reduce((prev, next) => { | |
| 23 | + return { | |
| 24 | + ...prev, | |
| 25 | + [next.functionName!]: getValueByType( | |
| 26 | + next.dataType!.type, | |
| 27 | + value[next.identifier], | |
| 28 | + next.dataType! | |
| 29 | + ), | |
| 30 | + }; | |
| 31 | + }, {}); | |
| 32 | + } | |
| 33 | + | |
| 34 | + return JSON.stringify(generateStruct(specs, res)); | |
| 35 | +} | |
| 36 | + | |
| 37 | +function getValueByType(type: string, value: any, dataType: DataType) { | |
| 38 | + switch (type) { | |
| 39 | + case DataTypeEnum.BOOL: | |
| 40 | + return getBoolTypeValue(value, dataType.specs as Specs); | |
| 41 | + case DataTypeEnum.STRUCT: | |
| 42 | + return getStructTypeValue(value, dataType.specs as StructJSON[]); | |
| 43 | + case DataTypeEnum.ENUM: | |
| 44 | + return getEnumTypeValue(value, dataType.specsList as Specs[]); | |
| 45 | + default: | |
| 46 | + return value; | |
| 47 | + } | |
| 48 | +} | |
| 49 | +export function useThingsModelValueTransform(value: any, thingsModelDataType?: DataType) { | |
| 50 | + if (!thingsModelDataType) return value; | |
| 51 | + | |
| 52 | + const { type } = thingsModelDataType; | |
| 53 | + return getValueByType(type, value, thingsModelDataType); | |
| 54 | +} | ... | ... |
| ... | ... | @@ -40,6 +40,7 @@ |
| 40 | 40 | import SIGNALSVG from '/@/assets/svg/signal.svg'; |
| 41 | 41 | import BATTERYSVG from '/@/assets/svg/battery.svg'; |
| 42 | 42 | import { useRoute } from 'vue-router'; |
| 43 | + import { createDeviceProfileQueryContext } from './hooks/useDeviceProfileQueryContext'; | |
| 43 | 44 | |
| 44 | 45 | const props = defineProps<{ |
| 45 | 46 | value?: Recordable; |
| ... | ... | @@ -53,8 +54,17 @@ |
| 53 | 54 | |
| 54 | 55 | const ROUTE = useRoute(); |
| 55 | 56 | |
| 56 | - const { loading, draggable, resizable, dataSource, rawDataSource, setLayoutInfo, getDataSource } = | |
| 57 | - useDataSource(getProps); | |
| 57 | + const { | |
| 58 | + loading, | |
| 59 | + draggable, | |
| 60 | + resizable, | |
| 61 | + dataSource, | |
| 62 | + rawDataSource, | |
| 63 | + setLayoutInfo, | |
| 64 | + getDataSource, | |
| 65 | + getDeviceProfileDetailById, | |
| 66 | + getDeviceProfileTslByIdWithIdentifier, | |
| 67 | + } = useDataSource(getProps); | |
| 58 | 68 | |
| 59 | 69 | const { resize, resized, moved, containerResized } = useDragGridLayout(dataSource, setLayoutInfo); |
| 60 | 70 | |
| ... | ... | @@ -157,6 +167,11 @@ |
| 157 | 167 | |
| 158 | 168 | createDataBoardContext({ send, close }); |
| 159 | 169 | |
| 170 | + createDeviceProfileQueryContext({ | |
| 171 | + getDeviceProfileDetailById, | |
| 172 | + getDeviceProfileTslByIdWithIdentifier, | |
| 173 | + }); | |
| 174 | + | |
| 160 | 175 | const { getDarkMode } = useRootSetting(); |
| 161 | 176 | watch( |
| 162 | 177 | getIsSharePage, | ... | ... |
| 1 | 1 | import { PublicComponentOptions } from '../../packages/index.type'; |
| 2 | +import { CommandTypeEnum } from '/@/enums/deviceEnum'; | |
| 2 | 3 | |
| 3 | 4 | export interface ComponentDataType<T = ExtraDataSource> { |
| 4 | 5 | id: string; |
| ... | ... | @@ -19,19 +20,26 @@ export interface DataSource { |
| 19 | 20 | deviceId: string; |
| 20 | 21 | deviceType: string; |
| 21 | 22 | attribute: string; |
| 22 | - attributeName: string; | |
| 23 | - deviceName: string; | |
| 24 | - gatewayDevice: boolean; | |
| 25 | - slaveDeviceId: string; | |
| 23 | + commandType?: CommandTypeEnum; | |
| 24 | + openCommand?: string; | |
| 25 | + closeCommand?: string; | |
| 26 | + openService?: string; | |
| 27 | + closeService?: string; | |
| 28 | + // attributeName: string; | |
| 29 | + // deviceName: string; | |
| 30 | + // gatewayDevice: boolean; | |
| 31 | + // slaveDeviceId: string; | |
| 26 | 32 | deviceRename: string; |
| 27 | 33 | attributeRename: string; |
| 28 | 34 | componentInfo: ComponentInfo; |
| 29 | - customCommand: CustomCommand; | |
| 30 | 35 | videoConfig?: VideoConfigType; |
| 31 | 36 | [key: string]: any; |
| 32 | - lal?: string; | |
| 33 | - latitude?: string | number; | |
| 34 | - longitude?: string | number; | |
| 37 | + | |
| 38 | + latitudeIdentifier?: string[]; | |
| 39 | + longitudeIdentifier?: string[]; | |
| 40 | + // lal?: string; | |
| 41 | + // latitude?: string | number; | |
| 42 | + // longitude?: string | number; | |
| 35 | 43 | } |
| 36 | 44 | |
| 37 | 45 | export interface ExtraDataSource extends DataSource, PublicComponentOptions { | ... | ... |