详情页面

This commit is contained in:
Flow 2025-05-30 15:26:21 +08:00
parent 67dce31b56
commit d6f433bd84
4 changed files with 246 additions and 19 deletions

View File

@ -0,0 +1,181 @@
<template>
<Dialog v-model="dialogVisible" :title="dialogTitle">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
label-width="100px"
:disabled="true"
>
<el-form-item label="设备ID">
<span>{{ formData.deviceKey }}</span>
</el-form-item>
<el-form-item label="设备名称">
<span>{{ formData.deviceName }}</span>
</el-form-item>
<el-form-item label="所属机构">
<span>{{ getOrgName(formData.productId) }}</span>
</el-form-item>
<el-form-item label="设备类型">
<span>{{ getDeviceTypeName(formData.deviceType) }}</span>
</el-form-item>
<el-form-item label="设备位置">
<span>{{ formData.address }}</span>
</el-form-item>
<el-form-item label="设备状态">
<span>{{ getDeviceStateName(formData.state) }}</span>
</el-form-item>
<el-form-item label="设备描述">
<span>{{ formData.description || '-' }}</span>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as DeviceApi from '@/api/iot/device/device'
import * as ProductApi from '@/api/iot/product/product'
import { DeviceStateEnum } from '@/api/iot/device/device'
defineOptions({ name: 'DeviceDetailsForm' })
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) //
const formData = ref<DeviceApi.DeviceVO>({
id: 0,
deviceKey: '',
deviceName: '',
productId: 0,
productKey: '',
deviceType: 0,
nickname: '',
gatewayId: 0,
state: DeviceStateEnum.INACTIVE,
onlineTime: new Date(),
offlineTime: new Date(),
activeTime: new Date(),
createTime: new Date(),
ip: '',
firmwareVersion: '',
deviceSecret: '',
mqttClientId: '',
mqttUsername: '',
mqttPassword: '',
authType: '',
latitude: 0,
longitude: 0,
areaId: 0,
address: '',
serialNumber: '',
config: '',
description: '',
groupIds: []
})
const productList = ref<ProductApi.ProductVO[]>([]) //
//
const getOrgName = (productId: number) => {
const product = productList.value.find(item => item.id === productId)
return product?.name || '-'
}
//
const getDeviceTypeName = (type: number) => {
const dict = getIntDictOptions(DICT_TYPE.IOT_DEVICE_TYPE).find(item => item.value === type)
return dict?.label || '-'
}
//
const getDeviceStateName = (state: number) => {
const dict = getIntDictOptions(DICT_TYPE.IOT_DEVICE_STATE).find(item => item.value === state)
return dict?.label || '-'
}
/** 打开弹窗 */
const open = async (id: number) => {
dialogVisible.value = true
dialogTitle.value = '设备详情'
resetForm()
//
if (id) {
formLoading.value = true
try {
// API
const mockDeviceData = {
id: id,
deviceKey: 'Device' + id,
deviceName: '测试设备' + id,
deviceType: 1,
state: DeviceStateEnum.INACTIVE,
address: '北京市/海淀区/中关村',
description: '这是一个测试设备的描述信息',
productId: 1,
productKey: 'product_key_' + id,
nickname: '设备昵称' + id,
gatewayId: 1,
onlineTime: new Date(),
offlineTime: new Date(),
activeTime: new Date(),
createTime: new Date(),
ip: '192.168.1.1',
firmwareVersion: '1.0.0',
deviceSecret: 'secret_' + id,
mqttClientId: 'client_' + id,
mqttUsername: 'username_' + id,
mqttPassword: 'password_' + id,
authType: 'password',
latitude: 39.123456,
longitude: 117.123456,
areaId: 1,
serialNumber: 'SN' + id,
config: '{}',
groupIds: []
}
formData.value = mockDeviceData
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: 0,
deviceKey: '',
deviceName: '',
productId: 0,
productKey: '',
deviceType: 0,
nickname: '',
gatewayId: 0,
state: DeviceStateEnum.INACTIVE,
onlineTime: new Date(),
offlineTime: new Date(),
activeTime: new Date(),
createTime: new Date(),
ip: '',
firmwareVersion: '',
deviceSecret: '',
mqttClientId: '',
mqttUsername: '',
mqttPassword: '',
authType: '',
latitude: 0,
longitude: 0,
areaId: 0,
address: '',
serialNumber: '',
config: '',
description: '',
groupIds: []
}
}
</script>

View File

@ -6,10 +6,14 @@
:model="formData"
:rules="formRules"
label-width="100px"
:disabled="isDetail"
>
<el-form-item label="设备ID" prop="deviceId">
<el-input v-model="formData.deviceId" placeholder="请输入设备ID" />
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="formData.deviceName" placeholder="请输入设备名称" />
</el-form-item>
<el-form-item label="所属机构" prop="orgId">
<el-select v-model="formData.orgId" placeholder="请选择所属机构" @change="handleProductChange">
<el-option
@ -67,8 +71,8 @@
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
<el-button v-if="!isDetail" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false">{{ isDetail ? '关 闭' : '取 消' }}</el-button>
</template>
</Dialog>
</template>
@ -97,9 +101,10 @@ const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const isDetail = ref(false) //
const formData = ref<DeviceApi.DeviceVO>({
id: undefined,
deviceKey: undefined,
deviceId: undefined,
deviceName: undefined,
productId: undefined,
productKey: undefined,
@ -122,14 +127,16 @@ const formData = ref<DeviceApi.DeviceVO>({
longitude: undefined,
areaId: undefined,
address: undefined,
detailAddress: undefined,
serialNumber: undefined,
config: undefined,
description: undefined,
groupIds: []
})
const formRules = reactive<FormRules>({
deviceId: [{ required: true, message: '设备ID不能为空', trigger: 'blur' }],
orgId: [{ required: true, message: '所属机构不能为空', trigger: 'blur' }],
deviceName: [{ required: true, message: '设备名称不能为空', trigger: 'blur' }],
deviceType: [{ required: true, message: '设备类型不能为空', trigger: 'blur' }],
address: [{ required: true, message: '设备位置不能为空', trigger: 'blur' }],
})
@ -145,12 +152,46 @@ const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
isDetail.value = type === 'details' //
resetForm()
//
if (id) {
formLoading.value = true
try {
formData.value = await DeviceApi.getDevice(id)
// API
const mockDeviceData = {
id: id,
deviceId: 'Device' + id,
deviceName: '测试设备' + id,
deviceType: 1,
state: DeviceStateEnum.INACTIVE,
address: '',
detailAddress: '测试地址123号',
description: '这是一个测试设备的描述信息',
productId: 1,
productKey: 'product_key_' + id,
nickname: '设备昵称' + id,
gatewayId: 1,
onlineTime: new Date(),
offlineTime: new Date(),
activeTime: new Date(),
createTime: new Date(),
ip: '192.168.1.1',
firmwareVersion: '1.0.0',
deviceSecret: 'secret_' + id,
mqttClientId: 'client_' + id,
mqttUsername: 'username_' + id,
mqttPassword: 'password_' + id,
authType: 'password',
latitude: 39.123456,
longitude: 117.123456,
areaId: 1,
serialNumber: 'SN' + id,
config: '{}',
groupIds: []
}
formData.value = mockDeviceData
//
if (formData.value.address) {
const addressParts = formData.value.address.split('/')
@ -176,10 +217,6 @@ const open = async (type: string, id?: number) => {
formLoading.value = false
}
}
//
productList.value = await ProductApi.getSimpleProductList()
//
groupList.value = await DeviceGroupApi.getSimpleDeviceGroupList()
}
defineExpose({ open }) // open
@ -213,7 +250,7 @@ const submitForm = async () => {
const resetForm = () => {
formData.value = {
id: undefined,
deviceKey: undefined,
deviceId: undefined,
deviceName: undefined,
productId: undefined,
productKey: undefined,
@ -236,8 +273,10 @@ const resetForm = () => {
longitude: undefined,
areaId: undefined,
address: undefined,
detailAddress: undefined,
serialNumber: undefined,
config: undefined,
description: undefined,
groupIds: []
}
selectedOptions.value = []

View File

@ -29,7 +29,7 @@
</p>
<p class="detail-item">
<span class="label">位置</span>
<span class="value">{{ deviceInfo.location }}</span>
<span class="value">{{ deviceInfo.address }}</span>
</p>
</div>
<div class="divider"></div>
@ -82,7 +82,7 @@ const props = defineProps({
id: '',
name: '',
type: '',
location: '',
address: '',
status: 'normal',
statusText: '正常'
})

View File

@ -76,6 +76,7 @@
<!-- 设备表单 -->
<DeviceForm ref="deviceFormRef" @success="handleQuery" />
<DetailsForm ref="detailsFormRef" @success="handleQuery" />
</template>
<script setup lang="ts">
@ -85,6 +86,7 @@ import { getIntDictOptions } from '@/utils/dict'
import { DICT_TYPE } from '@/utils/dict'
import DeviceCard from '../devices/devices_cards.vue'
import DeviceForm from './DevFrom.vue'
import DetailsForm from './DetailsForm.vue'
//
interface QueryParams {
@ -105,7 +107,7 @@ const deviceList = ref([
id: 'Blood001',
name: '血压仪',
type: '血压仪',
location: '天津市/西青区/精武镇',
address: '天津市/市辖区/西青区/精武镇',
status: 'normal',
statusText: '在线'
},
@ -113,7 +115,7 @@ const deviceList = ref([
id: 'Glucose001',
name: '血糖仪',
type: '血糖仪',
location: '河北省/廊坊市/安次区',
address: '河北省/廊坊市/安次区',
status: 'warning',
statusText: '待激活'
},
@ -121,7 +123,7 @@ const deviceList = ref([
id: 'Oxygen001',
name: '血氧仪',
type: '血氧仪',
location: '四川省/绵阳市/涪城区',
address: '四川省/绵阳市/涪城区',
status: 'error',
statusText: '离线'
}
@ -131,6 +133,8 @@ const deviceList = ref([
const queryFormRef = ref()
//
const deviceFormRef = ref()
//
const detailsFormRef = ref()
//
const handleQuery = () => {
@ -145,14 +149,17 @@ const resetQuery = () => {
}
//
const openForm = (type: string, id?: number) => {
deviceFormRef.value?.open(type, id)
const openForm = (type: string) => {
deviceFormRef.value?.open(type)
}
//
const handleDeviceAction = (action: any) => {
if (action.type === 'detail') {
openForm('update', action.device.id)
if (action.action === 'details') {
//
detailsFormRef.value?.open(action.deviceId)
} else {
console.log('设备操作:', action)
}
}