Showing
9 changed files
with
157 additions
and
69 deletions
| 1 | <script lang="ts"> | 1 | <script lang="ts"> |
| 2 | export default { | 2 | export default { |
| 3 | + components: { Spin }, | ||
| 3 | inheritAttrs: false, | 4 | inheritAttrs: false, |
| 4 | }; | 5 | }; |
| 5 | </script> | 6 | </script> |
| 6 | <script lang="ts" setup> | 7 | <script lang="ts" setup> |
| 8 | + import { Spin } from 'ant-design-vue'; | ||
| 7 | import { RadioRecord } from '../../detail/config/util'; | 9 | import { RadioRecord } from '../../detail/config/util'; |
| 8 | import { ControlComponentDefaultConfig, ControlComponentValue } from './control.config'; | 10 | import { ControlComponentDefaultConfig, ControlComponentValue } from './control.config'; |
| 9 | import { useSendCommand } from './useSendCommand'; | 11 | import { useSendCommand } from './useSendCommand'; |
| 12 | + import { ref } from 'vue'; | ||
| 10 | 13 | ||
| 11 | interface VisualComponentProps<Layout = Recordable, Value = ControlComponentValue> { | 14 | interface VisualComponentProps<Layout = Recordable, Value = ControlComponentValue> { |
| 12 | value?: Value; | 15 | value?: Value; |
| @@ -23,33 +26,45 @@ | @@ -23,33 +26,45 @@ | ||
| 23 | const emit = defineEmits(['update:value', 'change']); | 26 | const emit = defineEmits(['update:value', 'change']); |
| 24 | 27 | ||
| 25 | const { sendCommand } = useSendCommand(); | 28 | const { sendCommand } = useSendCommand(); |
| 26 | - const handleChange = (event: Event) => { | 29 | + |
| 30 | + const loading = ref(false); | ||
| 31 | + const handleChange = async (event: Event) => { | ||
| 27 | const _value = (event.target as HTMLInputElement).checked; | 32 | const _value = (event.target as HTMLInputElement).checked; |
| 33 | + if (props.value) { | ||
| 34 | + loading.value = true; | ||
| 35 | + const flag = await sendCommand(props.value, _value); | ||
| 36 | + loading.value = false; | ||
| 37 | + if (!flag) { | ||
| 38 | + (event.target as HTMLInputElement).checked = !_value; | ||
| 39 | + return; | ||
| 40 | + } | ||
| 41 | + } | ||
| 28 | emit('update:value', _value); | 42 | emit('update:value', _value); |
| 29 | emit('change', _value); | 43 | emit('change', _value); |
| 30 | - sendCommand(props.value!, _value); | ||
| 31 | }; | 44 | }; |
| 32 | </script> | 45 | </script> |
| 33 | 46 | ||
| 34 | <template> | 47 | <template> |
| 35 | <div class="flex flex-col justify-center"> | 48 | <div class="flex flex-col justify-center"> |
| 36 | - <label class="sliding-switch"> | ||
| 37 | - <input | ||
| 38 | - :value="!!Number(props.value?.value)" | ||
| 39 | - type="checkbox" | ||
| 40 | - :checked="!!Number(props.value?.value)" | ||
| 41 | - @change="handleChange" | ||
| 42 | - /> | ||
| 43 | - <span class="slider"></span> | ||
| 44 | - <span class="on">ON</span> | ||
| 45 | - <span class="off">OFF</span> | ||
| 46 | - </label> | ||
| 47 | - <div | ||
| 48 | - class="text-center mt-2 text-gray-700" | ||
| 49 | - :style="{ color: props?.value?.fontColor || ControlComponentDefaultConfig.fontColor }" | ||
| 50 | - > | ||
| 51 | - {{ props.value?.attributeRename || props.value?.attribute }}</div | ||
| 52 | - > | 49 | + <Spin :spinning="loading"> |
| 50 | + <label class="sliding-switch"> | ||
| 51 | + <input | ||
| 52 | + :value="!!Number(props.value?.value)" | ||
| 53 | + type="checkbox" | ||
| 54 | + :checked="!!Number(props.value?.value)" | ||
| 55 | + @change="handleChange" | ||
| 56 | + /> | ||
| 57 | + <span class="slider"></span> | ||
| 58 | + <span class="on">ON</span> | ||
| 59 | + <span class="off">OFF</span> | ||
| 60 | + </label> | ||
| 61 | + <div | ||
| 62 | + class="text-center mt-2 text-gray-700" | ||
| 63 | + :style="{ color: props?.value?.fontColor || ControlComponentDefaultConfig.fontColor }" | ||
| 64 | + > | ||
| 65 | + {{ props.value?.attributeRename || props.value?.attribute }}</div | ||
| 66 | + > | ||
| 67 | + </Spin> | ||
| 53 | </div> | 68 | </div> |
| 54 | </template> | 69 | </template> |
| 55 | 70 |
| @@ -31,8 +31,14 @@ | @@ -31,8 +31,14 @@ | ||
| 31 | const checked = ref(!!Number(props.value.value)); | 31 | const checked = ref(!!Number(props.value.value)); |
| 32 | 32 | ||
| 33 | const { sendCommand } = useSendCommand(); | 33 | const { sendCommand } = useSendCommand(); |
| 34 | - const handleChange = (value: boolean) => { | ||
| 35 | - sendCommand(props.value, value); | 34 | + const loading = ref(false); |
| 35 | + const handleChange = async (value: boolean) => { | ||
| 36 | + loading.value = true; | ||
| 37 | + const flag = await sendCommand(props.value, value); | ||
| 38 | + loading.value = false; | ||
| 39 | + if (!flag) { | ||
| 40 | + checked.value = !value; | ||
| 41 | + } | ||
| 36 | }; | 42 | }; |
| 37 | 43 | ||
| 38 | watchEffect(() => { | 44 | watchEffect(() => { |
| @@ -59,6 +65,6 @@ | @@ -59,6 +65,6 @@ | ||
| 59 | {{ props.value.attributeRename || props.value.attribute }} | 65 | {{ props.value.attributeRename || props.value.attribute }} |
| 60 | </span> | 66 | </span> |
| 61 | </div> | 67 | </div> |
| 62 | - <Switch v-model:checked="checked" @change="handleChange" /> | 68 | + <Switch v-model:checked="checked" :loading="loading" @change="handleChange" /> |
| 63 | </div> | 69 | </div> |
| 64 | </template> | 70 | </template> |
| 1 | <script lang="ts"> | 1 | <script lang="ts"> |
| 2 | export default { | 2 | export default { |
| 3 | + components: { Spin }, | ||
| 3 | inheritAttrs: false, | 4 | inheritAttrs: false, |
| 4 | }; | 5 | }; |
| 5 | </script> | 6 | </script> |
| @@ -8,6 +9,8 @@ | @@ -8,6 +9,8 @@ | ||
| 8 | import { DEFAULT_RADIO_RECORD, fontSize, RadioRecord } from '../../detail/config/util'; | 9 | import { DEFAULT_RADIO_RECORD, fontSize, RadioRecord } from '../../detail/config/util'; |
| 9 | import { ControlComponentDefaultConfig, ControlComponentValue } from './control.config'; | 10 | import { ControlComponentDefaultConfig, ControlComponentValue } from './control.config'; |
| 10 | import { useSendCommand } from './useSendCommand'; | 11 | import { useSendCommand } from './useSendCommand'; |
| 12 | + import { ref } from 'vue'; | ||
| 13 | + import { Spin } from 'ant-design-vue'; | ||
| 11 | 14 | ||
| 12 | const props = defineProps<{ | 15 | const props = defineProps<{ |
| 13 | value?: ControlComponentValue; | 16 | value?: ControlComponentValue; |
| @@ -18,11 +21,20 @@ | @@ -18,11 +21,20 @@ | ||
| 18 | const emit = defineEmits(['update:value', 'change']); | 21 | const emit = defineEmits(['update:value', 'change']); |
| 19 | 22 | ||
| 20 | const { sendCommand } = useSendCommand(); | 23 | const { sendCommand } = useSendCommand(); |
| 21 | - const handleChange = (event: Event) => { | 24 | + const loading = ref(false); |
| 25 | + const handleChange = async (event: Event) => { | ||
| 22 | const _value = (event.target as HTMLInputElement).checked; | 26 | const _value = (event.target as HTMLInputElement).checked; |
| 27 | + if (props.value) { | ||
| 28 | + loading.value = true; | ||
| 29 | + const flag = await sendCommand(props.value, _value); | ||
| 30 | + loading.value = false; | ||
| 31 | + if (!flag) { | ||
| 32 | + (event.target as HTMLInputElement).checked = !_value; | ||
| 33 | + return; | ||
| 34 | + } | ||
| 35 | + } | ||
| 23 | emit('update:value', _value); | 36 | emit('update:value', _value); |
| 24 | emit('change', _value); | 37 | emit('change', _value); |
| 25 | - sendCommand(props.value!, _value); | ||
| 26 | }; | 38 | }; |
| 27 | 39 | ||
| 28 | const getRadio = computed(() => { | 40 | const getRadio = computed(() => { |
| @@ -32,35 +44,37 @@ | @@ -32,35 +44,37 @@ | ||
| 32 | 44 | ||
| 33 | <template> | 45 | <template> |
| 34 | <div class="flex flex-col"> | 46 | <div class="flex flex-col"> |
| 35 | - <div | ||
| 36 | - class="toggle-switch" | ||
| 37 | - :style="{ | ||
| 38 | - width: fontSize({ radioRecord: getRadio, basic: 75, max: 75, min: 60 }), | ||
| 39 | - height: fontSize({ radioRecord: getRadio, basic: 97.5, max: 97.5, min: 80 }), | ||
| 40 | - }" | ||
| 41 | - > | ||
| 42 | - <label class="switch"> | ||
| 43 | - <input | ||
| 44 | - :value="!!Number(props.value?.value)" | ||
| 45 | - type="checkbox" | ||
| 46 | - :checked="!!Number(props.value?.value)" | ||
| 47 | - @change="handleChange" | ||
| 48 | - /> | ||
| 49 | - <div class="button"> | ||
| 50 | - <div class="light"></div> | ||
| 51 | - <div class="dots"></div> | ||
| 52 | - <div class="characters"></div> | ||
| 53 | - <div class="shine"></div> | ||
| 54 | - <div class="shadow"></div> | ||
| 55 | - </div> | ||
| 56 | - </label> | ||
| 57 | - </div> | ||
| 58 | - <div | ||
| 59 | - class="text-center mt-2 text-gray-700" | ||
| 60 | - :style="{ color: props?.value?.fontColor || ControlComponentDefaultConfig.fontColor }" | ||
| 61 | - > | ||
| 62 | - {{ props.value?.attributeRename || props.value?.attribute }}</div | ||
| 63 | - > | 47 | + <Spin :spinning="loading"> |
| 48 | + <div | ||
| 49 | + class="toggle-switch" | ||
| 50 | + :style="{ | ||
| 51 | + width: fontSize({ radioRecord: getRadio, basic: 75, max: 75, min: 60 }), | ||
| 52 | + height: fontSize({ radioRecord: getRadio, basic: 97.5, max: 97.5, min: 80 }), | ||
| 53 | + }" | ||
| 54 | + > | ||
| 55 | + <label class="switch"> | ||
| 56 | + <input | ||
| 57 | + :value="!!Number(props.value?.value)" | ||
| 58 | + type="checkbox" | ||
| 59 | + :checked="!!Number(props.value?.value)" | ||
| 60 | + @change="handleChange" | ||
| 61 | + /> | ||
| 62 | + <div class="button"> | ||
| 63 | + <div class="light"></div> | ||
| 64 | + <div class="dots"></div> | ||
| 65 | + <div class="characters"></div> | ||
| 66 | + <div class="shine"></div> | ||
| 67 | + <div class="shadow"></div> | ||
| 68 | + </div> | ||
| 69 | + </label> | ||
| 70 | + </div> | ||
| 71 | + <div | ||
| 72 | + class="text-center mt-2 text-gray-700" | ||
| 73 | + :style="{ color: props?.value?.fontColor || ControlComponentDefaultConfig.fontColor }" | ||
| 74 | + > | ||
| 75 | + {{ props.value?.attributeRename || props.value?.attribute }}</div | ||
| 76 | + > | ||
| 77 | + </Spin> | ||
| 64 | </div> | 78 | </div> |
| 65 | </template> | 79 | </template> |
| 66 | 80 |
| @@ -5,33 +5,44 @@ import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | @@ -5,33 +5,44 @@ import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | ||
| 5 | import { getModelServices } from '/@/api/device/modelOfMatter'; | 5 | import { getModelServices } from '/@/api/device/modelOfMatter'; |
| 6 | import { useMessage } from '/@/hooks/web/useMessage'; | 6 | import { useMessage } from '/@/hooks/web/useMessage'; |
| 7 | import { isString } from '/@/utils/is'; | 7 | import { isString } from '/@/utils/is'; |
| 8 | +import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | ||
| 8 | 9 | ||
| 9 | const { createMessage } = useMessage(); | 10 | const { createMessage } = useMessage(); |
| 10 | export function useSendCommand() { | 11 | export function useSendCommand() { |
| 12 | + const error = () => { | ||
| 13 | + createMessage.error('下发指令失败'); | ||
| 14 | + return false; | ||
| 15 | + }; | ||
| 11 | const sendCommand = async (record: ControlComponentValue, value: any) => { | 16 | const sendCommand = async (record: ControlComponentValue, value: any) => { |
| 12 | - if (!record) return; | 17 | + if (!record) return error(); |
| 13 | const { deviceProfileId, attribute, deviceType } = record; | 18 | const { deviceProfileId, attribute, deviceType } = record; |
| 14 | let { deviceId } = record; | 19 | let { deviceId } = record; |
| 15 | - if (!deviceId) return; | 20 | + if (!deviceId) return error(); |
| 16 | try { | 21 | try { |
| 17 | const list = await getDeviceProfile(); | 22 | const list = await getDeviceProfile(); |
| 18 | const deviceProfile = list.find((item) => item.id === deviceProfileId); | 23 | const deviceProfile = list.find((item) => item.id === deviceProfileId); |
| 19 | - if (!deviceProfile) return; | 24 | + if (!deviceProfile) return error(); |
| 25 | + | ||
| 20 | let params: string | Recordable = { | 26 | let params: string | Recordable = { |
| 21 | [attribute!]: Number(value), | 27 | [attribute!]: Number(value), |
| 22 | }; | 28 | }; |
| 23 | - if (deviceProfile.transportType === 'TCP') { | ||
| 24 | - const serviceList = await getModelServices({ deviceProfileId: deviceProfileId! }); | 29 | + |
| 30 | + // 如果是TCP设备从物模型中获取下发命令(TCP网关子设备无物模型服务与事件) | ||
| 31 | + if (deviceProfile!.transportType === TransportTypeEnum.TCP) { | ||
| 32 | + const serviceList = (await getModelServices({ deviceProfileId: deviceProfileId! })) || []; | ||
| 25 | const record = serviceList.find((item) => item.identifier === attribute); | 33 | const record = serviceList.find((item) => item.identifier === attribute); |
| 26 | const sendCommand = record?.functionJson.inputData?.at(0)?.serviceCommand || ''; | 34 | const sendCommand = record?.functionJson.inputData?.at(0)?.serviceCommand || ''; |
| 27 | params = isString(sendCommand) ? sendCommand : JSON.stringify(sendCommand); | 35 | params = isString(sendCommand) ? sendCommand : JSON.stringify(sendCommand); |
| 28 | } | 36 | } |
| 37 | + | ||
| 29 | if (deviceType === DeviceTypeEnum.SENSOR) { | 38 | if (deviceType === DeviceTypeEnum.SENSOR) { |
| 30 | deviceId = await getDeviceRelation({ | 39 | deviceId = await getDeviceRelation({ |
| 31 | deviceId, | 40 | deviceId, |
| 32 | isSlave: deviceType === DeviceTypeEnum.SENSOR, | 41 | isSlave: deviceType === DeviceTypeEnum.SENSOR, |
| 33 | }); | 42 | }); |
| 34 | } | 43 | } |
| 44 | + | ||
| 45 | + // 控制按钮下发命令为0 或 1 | ||
| 35 | await sendCommandOneway({ | 46 | await sendCommandOneway({ |
| 36 | deviceId, | 47 | deviceId, |
| 37 | value: { | 48 | value: { |
| @@ -44,7 +55,11 @@ export function useSendCommand() { | @@ -44,7 +55,11 @@ export function useSendCommand() { | ||
| 44 | }, | 55 | }, |
| 45 | }); | 56 | }); |
| 46 | createMessage.success('命令下发成功'); | 57 | createMessage.success('命令下发成功'); |
| 47 | - } catch (error) {} | 58 | + } catch (msg) { |
| 59 | + return error(); | ||
| 60 | + } finally { | ||
| 61 | + return true; | ||
| 62 | + } | ||
| 48 | }; | 63 | }; |
| 49 | return { | 64 | return { |
| 50 | sendCommand, | 65 | sendCommand, |
| @@ -57,6 +57,13 @@ | @@ -57,6 +57,13 @@ | ||
| 57 | } | 57 | } |
| 58 | }; | 58 | }; |
| 59 | 59 | ||
| 60 | + const resetFormFields = async () => { | ||
| 61 | + const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]); | ||
| 62 | + for (const id of hasExistEl) { | ||
| 63 | + await dataSourceEl[id]?.resetFields(); | ||
| 64 | + } | ||
| 65 | + }; | ||
| 66 | + | ||
| 60 | const validate = async () => { | 67 | const validate = async () => { |
| 61 | await basicMethod.validate(); | 68 | await basicMethod.validate(); |
| 62 | await validateDataSourceField(); | 69 | await validateDataSourceField(); |
| @@ -258,6 +265,16 @@ | @@ -258,6 +265,16 @@ | ||
| 258 | return isControlComponent(props.frontId as FrontComponent); | 265 | return isControlComponent(props.frontId as FrontComponent); |
| 259 | }); | 266 | }); |
| 260 | 267 | ||
| 268 | + watch( | ||
| 269 | + () => props.frontId, | ||
| 270 | + async (target, oldTarget) => { | ||
| 271 | + if (isControlComponent(oldTarget!)) return; | ||
| 272 | + if (isControlComponent(target!)) { | ||
| 273 | + await resetFormFields(); | ||
| 274 | + } | ||
| 275 | + } | ||
| 276 | + ); | ||
| 277 | + | ||
| 261 | onMounted(() => handleSort()); | 278 | onMounted(() => handleSort()); |
| 262 | 279 | ||
| 263 | defineExpose({ | 280 | defineExpose({ |
| @@ -293,7 +310,7 @@ | @@ -293,7 +310,7 @@ | ||
| 293 | 310 | ||
| 294 | <section ref="formListEl"> | 311 | <section ref="formListEl"> |
| 295 | <div v-for="item in dataSource" :data-id="item.id" :key="item.id" class="flex bg-light-50"> | 312 | <div v-for="item in dataSource" :data-id="item.id" :key="item.id" class="flex bg-light-50"> |
| 296 | - <div class="w-24 text-right flex justify-end"> 选择设备 </div> | 313 | + <div class="w-24 text-right flex justify-end" style="flex: 0 0 96px"> 选择设备 </div> |
| 297 | <div class="pl-2 flex-auto"> | 314 | <div class="pl-2 flex-auto"> |
| 298 | <component | 315 | <component |
| 299 | :frontId="$props.frontId" | 316 | :frontId="$props.frontId" |
| @@ -9,6 +9,8 @@ import { getModelServices } from '/@/api/device/modelOfMatter'; | @@ -9,6 +9,8 @@ import { getModelServices } from '/@/api/device/modelOfMatter'; | ||
| 9 | import { findDictItemByCode } from '/@/api/system/dict'; | 9 | import { findDictItemByCode } from '/@/api/system/dict'; |
| 10 | import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | 10 | import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; |
| 11 | import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; | 11 | import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; |
| 12 | +import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | ||
| 13 | +import { nextTick } from 'vue'; | ||
| 12 | 14 | ||
| 13 | export enum BasicConfigField { | 15 | export enum BasicConfigField { |
| 14 | NAME = 'name', | 16 | NAME = 'name', |
| @@ -66,7 +68,7 @@ export const isMapComponent = (frontId: FrontComponent) => { | @@ -66,7 +68,7 @@ export const isMapComponent = (frontId: FrontComponent) => { | ||
| 66 | }; | 68 | }; |
| 67 | 69 | ||
| 68 | const isTcpProfile = (transportType: string) => { | 70 | const isTcpProfile = (transportType: string) => { |
| 69 | - return transportType === 'TCP'; | 71 | + return transportType === TransportTypeEnum.TCP; |
| 70 | }; | 72 | }; |
| 71 | 73 | ||
| 72 | export const basicSchema: FormSchema[] = [ | 74 | export const basicSchema: FormSchema[] = [ |
| @@ -117,6 +119,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For | @@ -117,6 +119,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For | ||
| 117 | component: 'ApiSelect', | 119 | component: 'ApiSelect', |
| 118 | label: '设备类型', | 120 | label: '设备类型', |
| 119 | colProps: { span: 8 }, | 121 | colProps: { span: 8 }, |
| 122 | + rules: [{ message: '请选择设备类型', required: true }], | ||
| 120 | // defaultValue: DeviceTypeEnum.SENSOR, | 123 | // defaultValue: DeviceTypeEnum.SENSOR, |
| 121 | componentProps: ({ formActionType }) => { | 124 | componentProps: ({ formActionType }) => { |
| 122 | const { setFieldsValue } = formActionType; | 125 | const { setFieldsValue } = formActionType; |
| @@ -180,7 +183,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For | @@ -180,7 +183,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For | ||
| 180 | colProps: { span: 8 }, | 183 | colProps: { span: 8 }, |
| 181 | rules: [{ required: true, message: '组织为必填项' }], | 184 | rules: [{ required: true, message: '组织为必填项' }], |
| 182 | componentProps({ formActionType }) { | 185 | componentProps({ formActionType }) { |
| 183 | - const { setFieldsValue } = formActionType; | 186 | + const { setFieldsValue, getFieldsValue } = formActionType; |
| 184 | return { | 187 | return { |
| 185 | placeholder: '请选择组织', | 188 | placeholder: '请选择组织', |
| 186 | api: async () => { | 189 | api: async () => { |
| @@ -192,6 +195,9 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For | @@ -192,6 +195,9 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For | ||
| 192 | setFieldsValue({ | 195 | setFieldsValue({ |
| 193 | [DataSourceField.DEVICE_ID]: null, | 196 | [DataSourceField.DEVICE_ID]: null, |
| 194 | }); | 197 | }); |
| 198 | + nextTick(() => { | ||
| 199 | + console.log('org change', getFieldsValue()); | ||
| 200 | + }); | ||
| 195 | }, | 201 | }, |
| 196 | getPopupContainer: () => document.body, | 202 | getPopupContainer: () => document.body, |
| 197 | }; | 203 | }; |
| @@ -250,11 +256,24 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For | @@ -250,11 +256,24 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For | ||
| 250 | component: 'ApiSelect', | 256 | component: 'ApiSelect', |
| 251 | label: '属性', | 257 | label: '属性', |
| 252 | colProps: { span: 8 }, | 258 | colProps: { span: 8 }, |
| 253 | - rules: [{ required: true, message: '属性为必填项' }], | 259 | + dynamicRules: ({ model }) => { |
| 260 | + const transportType = model[DataSourceField.TRANSPORT_TYPE]; | ||
| 261 | + return [ | ||
| 262 | + { | ||
| 263 | + required: true, | ||
| 264 | + message: `${ | ||
| 265 | + isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType) | ||
| 266 | + ? '服务' | ||
| 267 | + : '属性' | ||
| 268 | + }为必填项`, | ||
| 269 | + }, | ||
| 270 | + ]; | ||
| 271 | + }, | ||
| 254 | componentProps({ formModel }) { | 272 | componentProps({ formModel }) { |
| 255 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | 273 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; |
| 256 | const transportType = formModel[DataSourceField.TRANSPORT_TYPE]; | 274 | const transportType = formModel[DataSourceField.TRANSPORT_TYPE]; |
| 257 | - if (isEdit && ![deviceProfileId, transportType].every(Boolean)) return {}; | 275 | + if (isEdit && ![deviceProfileId, transportType].every(Boolean)) |
| 276 | + return { placeholder: '请选择属性', getPopupContainer: () => document.body }; | ||
| 258 | return { | 277 | return { |
| 259 | api: async () => { | 278 | api: async () => { |
| 260 | try { | 279 | try { |
| @@ -47,7 +47,7 @@ | @@ -47,7 +47,7 @@ | ||
| 47 | import { BAI_DU_MAP_GL_LIB, BAI_DU_MAP_TRACK_ANIMATION } from '/@/utils/fnUtils'; | 47 | import { BAI_DU_MAP_GL_LIB, BAI_DU_MAP_TRACK_ANIMATION } from '/@/utils/fnUtils'; |
| 48 | 48 | ||
| 49 | const props = defineProps<{ | 49 | const props = defineProps<{ |
| 50 | - value: Recordable; | 50 | + value?: Recordable; |
| 51 | }>(); | 51 | }>(); |
| 52 | 52 | ||
| 53 | const ROUTE = useRoute(); | 53 | const ROUTE = useRoute(); |
| @@ -126,7 +126,7 @@ export function useSocketConnect(dataSourceRef: Ref<DataBoardLayoutInfo[]>) { | @@ -126,7 +126,7 @@ export function useSocketConnect(dataSourceRef: Ref<DataBoardLayoutInfo[]>) { | ||
| 126 | const { subscriptionId, data = {} } = res; | 126 | const { subscriptionId, data = {} } = res; |
| 127 | if (isNullAndUnDef(subscriptionId)) return; | 127 | if (isNullAndUnDef(subscriptionId)) return; |
| 128 | const mappingRecord = cmdIdMapping.get(subscriptionId); | 128 | const mappingRecord = cmdIdMapping.get(subscriptionId); |
| 129 | - if (!mappingRecord) return; | 129 | + if (!mappingRecord || !data) return; |
| 130 | mappingRecord.forEach((item) => { | 130 | mappingRecord.forEach((item) => { |
| 131 | const { attribute, recordIndex, dataSourceIndex } = item; | 131 | const { attribute, recordIndex, dataSourceIndex } = item; |
| 132 | const [[timespan, value]] = data[attribute]; | 132 | const [[timespan, value]] = data[attribute]; |
| @@ -249,17 +249,19 @@ | @@ -249,17 +249,19 @@ | ||
| 249 | </div> | 249 | </div> |
| 250 | </div> | 250 | </div> |
| 251 | <div class="flex justify-between mt-4 text-sm" style="color: #999"> | 251 | <div class="flex justify-between mt-4 text-sm" style="color: #999"> |
| 252 | - <div> | 252 | + <div class="flex min-w-20 mr-3"> |
| 253 | <span> | 253 | <span> |
| 254 | {{ item.viewType === ViewType.PRIVATE_VIEW ? '私有看板' : '公共看板' }} | 254 | {{ item.viewType === ViewType.PRIVATE_VIEW ? '私有看板' : '公共看板' }} |
| 255 | </span> | 255 | </span> |
| 256 | <span v-if="item.viewType === ViewType.PUBLIC_VIEW"> | 256 | <span v-if="item.viewType === ViewType.PUBLIC_VIEW"> |
| 257 | <Tooltip title="点击复制分享链接"> | 257 | <Tooltip title="点击复制分享链接"> |
| 258 | - <ShareAltOutlined class="ml-2" @click.stop="handleCopyShareUrl(item)" /> | 258 | + <ShareAltOutlined class="ml-1" @click.stop="handleCopyShareUrl(item)" /> |
| 259 | </Tooltip> | 259 | </Tooltip> |
| 260 | </span> | 260 | </span> |
| 261 | </div> | 261 | </div> |
| 262 | - <div>{{ item.createTime }}</div> | 262 | + <Tooltip placement="topLeft" :title="item.createTime"> |
| 263 | + <div class="truncate">{{ item.createTime }}</div> | ||
| 264 | + </Tooltip> | ||
| 263 | </div> | 265 | </div> |
| 264 | </section> | 266 | </section> |
| 265 | </Card> | 267 | </Card> |