123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- <template>
- <a-spin :spinning="spinning">
- <a-form
- ref="baseFormRef"
- :model="baseFormState"
- :rules="rules"
- :label-col="{ style: { width: '100px' } }"
- :wrapper-col="{ style: { width: '330px' } }"
- >
- <a-form-item label="设备名称" name="deviceName">
- <a-input
- v-model:value.trim="baseFormState.deviceName"
- placeholder="请输入设备名称"
- :maxlength="10"
- show-count
- :style="inputStyle"
- />
- </a-form-item>
- <a-form-item label="安装方式" name="installWay">
- <a-select
- v-model:value="baseFormState.installWay"
- placeholder="请选择安装方式"
- :style="inputStyle"
- >
- <a-select-option value="Wall">墙装</a-select-option>
- <a-select-option value="Ceiling">顶装</a-select-option>
- </a-select>
- </a-form-item>
- <a-form-item label="安装位置" name="installPosition">
- <a-select
- v-model:value="baseFormState.installPosition"
- placeholder="请选择安装位置"
- :style="inputStyle"
- :options="installPositionOptions"
- >
- </a-select>
- </a-form-item>
- <a-form-item label="X范围" class="outSideInput">
- <div class="rangeInput">
- <a-form-item name="xRangeStart">
- <a-input
- v-model:value.trim="baseFormState.xRangeStart"
- name="xRangeStart"
- :style="rangeInputStyle"
- placeholder="请输入"
- >
- <template #suffix>
- <a-tooltip>
- <template #title>
- <div>范围:-200 - 200 cm</div>
- </template>
- <info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
- </a-tooltip>
- </template>
- </a-input>
- </a-form-item>
- <span class="line"> — </span>
- <a-form-item name="xRangeEnd">
- <a-input
- v-model:value.trim="baseFormState.xRangeEnd"
- name="xRangeEnd"
- :style="rangeInputStyle"
- placeholder="请输入"
- >
- <template #suffix>
- <a-tooltip>
- <template #title>
- <div>范围:-200 - 200 cm</div>
- </template>
- <info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
- </a-tooltip>
- </template>
- </a-input>
- </a-form-item>
- </div>
- </a-form-item>
- <a-form-item label="Y范围" class="outSideInput">
- <div class="rangeInput">
- <a-form-item name="yRangeStart">
- <a-input
- v-model:value.trim="baseFormState.yRangeStart"
- name="yRangeStart"
- :style="rangeInputStyle"
- placeholder="请输入"
- >
- <template #suffix>
- <a-tooltip>
- <template #title>
- <div>范围:-250 - 250 cm</div>
- </template>
- <info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
- </a-tooltip>
- </template>
- </a-input>
- </a-form-item>
- <span class="line"> — </span>
- <a-form-item name="yRangeEnd">
- <a-input
- v-model:value.trim="baseFormState.yRangeEnd"
- name="yRangeEnd"
- :style="rangeInputStyle"
- placeholder="请输入"
- >
- <template #suffix>
- <a-tooltip>
- <template #title>
- <div>范围:-250 - 250 cm</div>
- </template>
- <info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
- </a-tooltip>
- </template>
- </a-input>
- </a-form-item>
- </div>
- </a-form-item>
- <a-form-item label="Z范围" class="outSideInput">
- <div class="rangeInput">
- <a-form-item name="zRangeStart">
- <a-input
- v-model:value.trim="baseFormState.zRangeStart"
- name="zRangeStart"
- :style="rangeInputStyle"
- placeholder="请输入"
- >
- <template #suffix>
- <a-tooltip>
- <template #title>
- <div>范围:0 - 5 cm</div>
- </template>
- <info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
- </a-tooltip>
- </template>
- </a-input>
- </a-form-item>
- <span class="line"> — </span>
- <a-form-item name="zRangeEnd">
- <a-input
- v-model:value.trim="baseFormState.zRangeEnd"
- name="zRangeEnd"
- :style="rangeInputStyle"
- placeholder="请输入"
- >
- <template #suffix>
- <a-tooltip>
- <template #title>
- <div>范围:200 - 300 cm</div>
- </template>
- <info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
- </a-tooltip>
- </template>
- </a-input>
- </a-form-item>
- </div>
- </a-form-item>
- <a-form-item label="安装高度" name="installHeight">
- <a-input
- v-model:value.trim="baseFormState.installHeight"
- placeholder="请输入安装高度"
- :style="inputStyle"
- >
- <template #suffix>
- <a-tooltip>
- <template #title>
- <div>范围:250 - 370 cm</div>
- </template>
- <info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
- </a-tooltip>
- </template>
- </a-input>
- </a-form-item>
- <a-form-item label="电源灯朝向" name="northAngle">
- <a-select ref="select" v-model:value="baseFormState.northAngle" :style="inputStyle">
- <a-select-option :value="0">北(0度)</a-select-option>
- <a-select-option :value="90">东(90度)</a-select-option>
- <a-select-option :value="180">南(180度)</a-select-option>
- <a-select-option :value="270">西(270度)</a-select-option>
- </a-select>
- </a-form-item>
- <a-form-item label="归属租户" name="tenantId">
- <a-select
- v-model:value="baseFormState.tenantId"
- placeholder="请选择归属租户"
- :options="tenantOptions"
- :style="inputStyle"
- allow-clear
- >
- </a-select>
- </a-form-item>
- <a-form-item label="跌倒确认时间" name="fallingConfirm">
- <a-input
- v-model:value.trim="baseFormState.fallingConfirm"
- placeholder="请输入跌倒确认时间"
- :style="inputStyle"
- >
- <template #suffix>
- <a-tooltip>
- <template #title>
- <div>需大于0,默认: 53秒</div>
- </template>
- <info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
- </a-tooltip>
- </template>
- </a-input>
- </a-form-item>
- <a-form-item label="监护对象年龄" name="guardAge">
- <a-input
- v-model:value.trim="baseFormState.guardAge"
- placeholder="请输入监护对象年龄"
- :style="inputStyle"
- >
- <template #suffix>
- <a-tooltip>
- <template #title>
- <div>年龄范围: 1 - 120</div>
- </template>
- <info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
- </a-tooltip>
- </template>
- </a-input>
- </a-form-item>
- <a-form-item label="监护对象类型" name="guardType">
- <a-select
- v-model:value="baseFormState.guardType"
- placeholder="请选择监护对象类型"
- :options="guardTypeOptions"
- :style="inputStyle"
- allow-clear
- >
- </a-select>
- </a-form-item>
- <div class="footer" :style="{ marginLeft: '100px' }">
- <a-space>
- <a-button
- type="primary"
- :loading="saveBaseLoading"
- :disabled="props.online === 0"
- @click="saveBaseConfig"
- >保存配置</a-button
- >
- <span v-if="props.online === 0" style="color: red">⚠️设备离线,不允许编辑保存</span>
- </a-space>
- </div>
- </a-form>
- </a-spin>
- </template>
- <script setup lang="ts">
- import { ref, reactive } from 'vue'
- import { message, type FormInstance } from 'ant-design-vue'
- import { InfoCircleOutlined } from '@ant-design/icons-vue'
- import type { Rule } from 'ant-design-vue/es/form'
- import * as deviceApi from '@/api/device'
- import * as tenantAPI from '@/api/tenant'
- import type { TenantItem } from '@/api/tenant/types'
- import { useUserStore } from '@/stores/user'
- import { useDict } from '@/hooks/useDict'
- defineOptions({
- name: 'deviceBaseConfig',
- })
- type Props = {
- devId: string // 设备id 查询使用
- clientId: string // 设备id 更新使用
- online: SwitchType | 9 // 设备是否在线
- }
- const emit = defineEmits<{
- (e: 'success', value: void): void
- }>()
- const props = withDefaults(defineProps<Props>(), {
- devId: '',
- clientId: '',
- online: 0,
- })
- const userStore = useUserStore()
- // 基础配置表单
- interface BaseFormState {
- deviceName: string // 设备名称
- installWay: InstallWay // 安装方式
- installPosition: InstallPosition // 安装位置
- xRangeStart: ID // x范围
- xRangeEnd: ID // x范围
- yRangeStart: ID // y范围
- yRangeEnd: ID // y范围
- zRangeStart: ID // z范围
- zRangeEnd: ID // z范围
- installHeight: ID // 安装高度
- northAngle: NorthAngle // 正北向夹角
- tenantId: ID // 租户id
- fallingConfirm: ID // 跌倒确认
- guardAge?: ID // 监护对象年龄
- guardType?: ID // 监护对象类型
- }
- const spinning = ref(false)
- const baseFormState = reactive<BaseFormState>({
- deviceName: '',
- installWay: 'Wall',
- installPosition: 'Toilet',
- xRangeStart: '',
- xRangeEnd: '',
- yRangeStart: '',
- yRangeEnd: '',
- zRangeStart: '',
- zRangeEnd: '',
- installHeight: '',
- northAngle: 0,
- tenantId: null,
- fallingConfirm: 53,
- guardAge: null,
- guardType: null,
- })
- // 范围输入框尺寸
- const rangeInputStyle = {
- width: '150px',
- }
- // 普通输入框尺寸
- const inputStyle = {
- width: '330px',
- }
- const baseFormRef = ref<FormInstance>()
- const saveBaseLoading = ref(false)
- // 保存基础配置
- const saveBaseConfig = async () => {
- baseFormRef.value
- ?.validate()
- .then(async () => {
- saveBaseLoading.value = true
- console.log('saveBaseConfig 校验通过', baseFormState, props)
- try {
- await deviceApi.updateDevice({
- clientId: props.clientId,
- userId: userStore?.userInfo?.userId,
- devName: baseFormState.deviceName,
- mountPlain: baseFormState.installWay,
- installPosition: baseFormState.installPosition,
- xxStart: baseFormState.xRangeStart,
- xxEnd: baseFormState.xRangeEnd,
- yyStart: baseFormState.yRangeStart,
- yyEnd: baseFormState.yRangeEnd,
- zzStart: baseFormState.zRangeStart,
- zzEnd: baseFormState.zRangeEnd,
- height: baseFormState.installHeight,
- northAngle: baseFormState.northAngle,
- tenantId: baseFormState?.tenantId,
- fallingConfirm:
- Number(baseFormState?.fallingConfirm) > 0
- ? Number(baseFormState?.fallingConfirm)
- : null,
- age: baseFormState?.guardAge ? Number(baseFormState?.guardAge) : null,
- guardianshipType: baseFormState?.guardType ? String(baseFormState?.guardType) : null,
- })
- saveBaseLoading.value = false
- message.success('保存成功')
- emit('success')
- } catch (error) {
- console.error('saveBaseConfig 保存失败', error)
- saveBaseLoading.value = false
- }
- })
- .catch((err) => {
- console.error('saveBaseConfig 校验失败', err)
- saveBaseLoading.value = false
- })
- }
- // 校验输入的范围大小
- // const createRangeValidator = (min: number, max: number) => async (_rule: Rule, value: string) => {
- // if (!value) {
- // return Promise.reject(new Error('不能为空'))
- // }
- // if (!/^-?\d+(\.\d+)?$/.test(value)) {
- // return Promise.reject(new Error('必须为数字'))
- // }
- // const num = parseFloat(value)
- // if (num < min || num > max) {
- // return Promise.reject(new Error(`范围:${min} ~ ${max}`))
- // }
- // return Promise.resolve()
- // }
- // 校验起始值和结束值的大小关系
- // const validateRangeOrder = (startField: string, endField: string) => {
- // return async () => {
- // const startVal = parseFloat(baseFormState[startField as keyof BaseFormState] as string)
- // const endVal = parseFloat(baseFormState[endField as keyof BaseFormState] as string)
- // if (startVal >= endVal) {
- // return Promise.reject(new Error('结束值须大于起始值'))
- // }
- // return Promise.resolve()
- // }
- // }
- // 统一校验规则
- const rules: Record<string, Rule[]> = {
- installWay: [
- {
- required: true,
- message: '请选择安装方式',
- trigger: ['change', 'blur'],
- },
- ],
- guardAge: [
- {
- validator: (_rule: Rule, value: string) => {
- if (!value) {
- return Promise.resolve()
- }
- if (!/^\d+$/.test(value)) {
- return Promise.reject(new Error('必须为整数'))
- }
- const num = parseInt(value, 10)
- if (num < 1 || num > 120) {
- return Promise.reject(new Error('年龄范围: 1 - 120'))
- }
- return Promise.resolve()
- },
- trigger: ['change', 'blur'],
- },
- ],
- // xRangeStart: [
- // {
- // required: true,
- // validator: createRangeValidator(-200, 200),
- // trigger: ['change', 'blur'],
- // },
- // ],
- // xRangeEnd: [
- // {
- // validator: createRangeValidator(-200, 200),
- // trigger: ['change', 'blur'],
- // },
- // {
- // validator: validateRangeOrder('xRangeStart', 'xRangeEnd'),
- // trigger: ['change', 'blur'],
- // },
- // ],
- // yRangeStart: [
- // {
- // validator: createRangeValidator(-250, 250),
- // trigger: ['change', 'blur'],
- // },
- // ],
- // yRangeEnd: [
- // {
- // validator: createRangeValidator(-250, 250),
- // trigger: ['change', 'blur'],
- // },
- // {
- // validator: validateRangeOrder('yRangeStart', 'yRangeEnd'),
- // trigger: ['change', 'blur'],
- // },
- // ],
- // zRangeStart: [
- // {
- // validator: createRangeValidator(0, 5),
- // trigger: ['change', 'blur'],
- // },
- // ],
- // zRangeEnd: [
- // {
- // validator: createRangeValidator(200, 300),
- // trigger: ['change', 'blur'],
- // },
- // ],
- }
- const tenantOptions = ref<{ label: string; value: string }[]>([])
- // 获取租户下拉列表
- const fetchTenantList = async () => {
- try {
- const res = await tenantAPI.queryTenant({
- pageNo: 1,
- pageSize: 10000,
- })
- const { rows } = res.data
- tenantOptions.value = rows.map((item: TenantItem) => ({
- label: item.tenantName,
- value: item?.tenantId as string,
- }))
- } catch (err) {
- console.log('❌ 获取数据失败', err)
- }
- }
- fetchTenantList()
- const { dictList: guardTypeOptions, fetchDict: fetchGuardTypeOptions } =
- useDict('guardianship_type')
- fetchGuardTypeOptions()
- const { dictList: installPositionOptions, fetchDict: fetchDictInstallPosition } =
- useDict('install_position')
- fetchDictInstallPosition()
- // 获取设备回显数据
- const fetchDeviceBaseInfo = async () => {
- console.log('fetchDeviceDetail', props)
- if (!props.devId) {
- message.error('设备ID不能为空')
- return
- }
- try {
- spinning.value = true
- const res = await deviceApi.getDeviceDetailByDevId({
- devId: props.devId,
- })
- console.log('✅获取到设备详情', res)
- baseFormState.deviceName = res.data.devName
- baseFormState.installWay = res.data.mountPlain
- baseFormState.installPosition = res.data.installPosition
- baseFormState.xRangeStart = res.data.xxStart
- baseFormState.xRangeEnd = res.data.xxEnd
- baseFormState.yRangeStart = res.data.yyStart
- baseFormState.yRangeEnd = res.data.yyEnd
- baseFormState.zRangeStart = res.data.zzStart
- baseFormState.zRangeEnd = res.data.zzEnd
- baseFormState.installHeight = res.data.height
- baseFormState.northAngle = res.data.northAngle
- baseFormState.tenantId = res.data.tenantId
- baseFormState.fallingConfirm = res.data.fallingConfirm
- baseFormState.guardAge = res.data.age
- baseFormState.guardType = res.data.guardianshipType
- } catch (error) {
- console.error('❌获取设备详情失败', error)
- }
- spinning.value = false
- }
- fetchDeviceBaseInfo()
- </script>
- <style scoped lang="less">
- // 取消嵌套的a-form-item的margin-bottom
- :deep(.outSideInput.ant-form-item) {
- margin-bottom: 0;
- }
- .rangeInput {
- display: flex;
- align-items: baseline;
- gap: 8px;
- }
- </style>
|