feat(iot): 增加设备所属产品详情展示并优化搜索功能

- 在设备详情页面添加所属产品信息展示,包括产品名称、类型、型号、通讯协议等
- 实现设备和产品名称的模糊搜索功能
- 优化设备列表查询,改用名称搜索替代原有列表查询方式
-修复产品 TSL 页面权限控制错误
This commit is contained in:
Kven 2025-02-25 22:00:47 +08:00
parent 2bc05aaea8
commit 827bb69f22
6 changed files with 106 additions and 41 deletions

View File

@ -16,22 +16,12 @@
设备详情</h3 设备详情</h3
> >
</template> </template>
<a-descriptions-item label="设备名称">{{ <a-descriptions-item label="设备名称">{{ renderData.name }}</a-descriptions-item>
renderData.name <a-descriptions-item label="硬件版本">{{ renderData.hardwareVersion }}</a-descriptions-item>
}}</a-descriptions-item> <a-descriptions-item label="固件版本">{{ renderData.firmwareVersion }}</a-descriptions-item>
<a-descriptions-item label="硬件版本">{{ <a-descriptions-item label="所属产品">{{ renderData.productId }}</a-descriptions-item>
renderData.hardwareVersion
}}</a-descriptions-item>
<a-descriptions-item label="固件版本">{{
renderData.firmwareVersion
}}</a-descriptions-item>
<a-descriptions-item label="所属产品">{{
renderData.productId
}}</a-descriptions-item>
<!-- <a-descriptions-item label="备注">{{renderData.remark }}</a-descriptions-item>--> <!-- <a-descriptions-item label="备注">{{renderData.remark }}</a-descriptions-item>-->
<a-descriptions-item label="创建时间">{{ <a-descriptions-item label="创建时间">{{dayjs(renderData.createTime).format('YYYY-MM-DD HH:mm:ss') }}</a-descriptions-item>
dayjs(renderData.createTime).format('YYYY-MM-DD HH:mm:ss')
}}</a-descriptions-item>
</a-descriptions> </a-descriptions>
</a-card> </a-card>
<a-card class="general-card" style="margin-top: 10px"> <a-card class="general-card" style="margin-top: 10px">
@ -44,7 +34,31 @@
<a-table :columns="columns" :data="renderData.params" /> <a-table :columns="columns" :data="renderData.params" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="2" tab="基本信息" title="基本信息"> <a-tab-pane key="2" tab="基本信息" title="基本信息">
基本信息 <a-card class="general-card" title=" ">
<a-descriptions size="large">
<template #title>
<h3 style="margin-top: -15px">所属产品详情</h3>
</template>
<a-descriptions-item label="产品名称">{{
renderData.productName
}}</a-descriptions-item>
<a-descriptions-item label="产品类型">{{
renderData.productType
}}</a-descriptions-item>
<a-descriptions-item label="产品型号">{{
renderData.model
}}</a-descriptions-item>
<a-descriptions-item label="通讯协议">{{
renderData.link
}}</a-descriptions-item>
<a-descriptions-item label="备注">{{
renderData.remark
}}</a-descriptions-item>
<a-descriptions-item label="创建时间">{{
dayjs(renderData.createTime).format('YYYY-MM-DD HH:mm:ss')
}}</a-descriptions-item>
</a-descriptions>
</a-card>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="3" tab="执行服务" title="执行服务"> <a-tab-pane key="3" tab="执行服务" title="执行服务">
执行服务内容 执行服务内容

View File

@ -32,7 +32,7 @@
<a-col :span="9"> <a-col :span="9">
<a-form-item field="productName" label="所属产品"> <a-form-item field="productName" label="所属产品">
<a-select <a-select
v-model="formModel.productName" v-model="formModel.productId"
:loading="loading" :loading="loading"
:filter-option="false" :filter-option="false"
allow-search allow-search
@ -40,7 +40,7 @@
placeholder="请选择所属产品" placeholder="请选择所属产品"
@search="handleSearch" @search="handleSearch"
> >
<a-option v-for="item of options" :key="item.id" :value="item.name">{{ <a-option v-for="item of options" :key="item.id" :value="item.id">{{
item.name item.name
}}</a-option> }}</a-option>
</a-select> </a-select>
@ -242,7 +242,7 @@
name: '', name: '',
status: '', status: '',
isOnline: '', isOnline: '',
productName: '', productId: '',
}; };
}; };
@ -338,10 +338,8 @@
window.setTimeout(async () => { window.setTimeout(async () => {
const res = await queryDeviceByName({ const res = await queryDeviceByName({
name: value, name: value,
page: 1,
size: 5,
}); });
options.value = res.data.records.map((item: any) => { options.value = res.data.map((item: any) => {
return { return {
id: item.id, id: item.id,
name: item.name, name: item.name,

View File

@ -43,7 +43,31 @@
<a-table :columns="columns" :data="renderData.params" /> <a-table :columns="columns" :data="renderData.params" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="2" tab="基本信息" title="基本信息"> <a-tab-pane key="2" tab="基本信息" title="基本信息">
基本信息 <a-card class="general-card" title=" ">
<a-descriptions size="large">
<template #title>
<h3 style="margin-top: -15px">所属产品详情</h3>
</template>
<a-descriptions-item label="产品名称">{{
renderData.productName
}}</a-descriptions-item>
<a-descriptions-item label="产品类型">{{
renderData.productType
}}</a-descriptions-item>
<a-descriptions-item label="产品型号">{{
renderData.model
}}</a-descriptions-item>
<a-descriptions-item label="通讯协议">{{
renderData.link
}}</a-descriptions-item>
<a-descriptions-item label="备注">{{
renderData.remark
}}</a-descriptions-item>
<a-descriptions-item label="创建时间">{{
dayjs(renderData.createTime).format('YYYY-MM-DD HH:mm:ss')
}}</a-descriptions-item>
</a-descriptions>
</a-card>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="3" tab="执行服务" title="执行服务"> <a-tab-pane key="3" tab="执行服务" title="执行服务">
执行服务内容 执行服务内容

View File

@ -30,13 +30,20 @@
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="9"> <a-col :span="9">
<a-form-item field="status" label="所属产品"> <a-form-item field="productName" label="所属产品">
<a-select <a-select
v-model="formModel.productName" v-model="formModel.productId"
:loading="loading"
:filter-option="false"
allow-search
style="width: 360px" style="width: 360px"
placeholder="请选择所属产品" placeholder="请选择所属产品"
:options="productOptions" @search="handleSearch"
/> >
<a-option v-for="item of options" :key="item.id" :value="item.id">{{
item.name
}}</a-option>
</a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="10"> <a-col :span="10">
@ -131,7 +138,7 @@
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import usePagination from '@/hooks/pagination'; import usePagination from '@/hooks/pagination';
import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface'; import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
import { DeviceRecord, queryDeviceList } from '@/api/device'; import { DeviceRecord, queryDeviceByName, queryDeviceList } from '@/api/device';
import DeviceEdit from '@/views/iot/deviceCard/components/device-edit.vue'; import DeviceEdit from '@/views/iot/deviceCard/components/device-edit.vue';
import CardWrap from './components/card-wrap.vue'; import CardWrap from './components/card-wrap.vue';
@ -140,6 +147,7 @@
name: '', name: '',
status: '', status: '',
isOnline: '', isOnline: '',
productId: '',
}; };
}; };
@ -147,6 +155,7 @@
const { pagination, setPagination } = usePagination(); const { pagination, setPagination } = usePagination();
const renderData = ref<[]>([]); const renderData = ref<[]>([]);
const formModel = ref(generateFormModel()); const formModel = ref(generateFormModel());
const options = ref<any>([]);
const statusOptions = computed<SelectOptionData[]>(() => [ const statusOptions = computed<SelectOptionData[]>(() => [
{ {
@ -190,6 +199,28 @@
} as unknown as DeviceRecord); } as unknown as DeviceRecord);
}; };
//
const handleSearch = (value: any) => {
if (value) {
loading.value = true;
options.value = [];
window.setTimeout(async () => {
const res = await queryDeviceByName({
name: value,
});
options.value = res.data.map((item: any) => {
return {
id: item.id,
name: item.name,
};
});
loading.value = false;
}, 1000);
} else {
options.value = [];
}
};
// //
const onPageChange = (current: number) => { const onPageChange = (current: number) => {
pagination.value.page = current; pagination.value.page = current;

View File

@ -41,7 +41,7 @@
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="2" title="服务"> <a-tab-pane key="2" title="服务">
<a-button <a-button
v-permission="['iot:server:create']" v-permission="['iot:serve:create']"
type="primary" type="primary"
style="margin-bottom: 10px" style="margin-bottom: 10px"
@click="handleClick" @click="handleClick"
@ -52,7 +52,7 @@
<a-table :columns="columns" :data="serveData"> <a-table :columns="columns" :data="serveData">
<template #operation="{ record }"> <template #operation="{ record }">
<a-button <a-button
v-permission="['iot:server:delete']" v-permission="['iot:serve:delete']"
type="outline" type="outline"
status="danger" status="danger"
size="small" size="small"

View File

@ -227,11 +227,6 @@
excludeKeys: ['uploadVideo', 'insertImage', 'insertVideo'], excludeKeys: ['uploadVideo', 'insertImage', 'insertVideo'],
}; };
const columns = computed<any[]>(() => [ const columns = computed<any[]>(() => [
// {
// title: '',
// dataIndex: 'id',
// slotName: 'id',
// },
{ {
title: '用户', title: '用户',
dataIndex: 'username', dataIndex: 'username',
@ -244,11 +239,11 @@
const deptTreeData = ref([ const deptTreeData = ref([
{ {
id: '1', id: 1,
name: '总部门', name: '总部门',
children: [ children: [
{ {
id: '2', id: 2,
name: '部门1', name: '部门1',
members: [ members: [
{ id: '101', username: '成员1' }, { id: '101', username: '成员1' },
@ -256,7 +251,7 @@
], ],
}, },
{ {
id: '3', id: 3,
name: '部门2', name: '部门2',
members: [ members: [
{ id: '201', username: '成员3' }, { id: '201', username: '成员3' },
@ -264,7 +259,7 @@
], ],
}, },
{ {
id: '4', id: 4,
name: '部门3', name: '部门3',
members: [ members: [
{ id: '203', username: '成员5' }, { id: '203', username: '成员5' },
@ -275,6 +270,7 @@
}, },
]); ]);
const renderData = ref<any[]>([]); const renderData = ref<any[]>([]);
const selectData = ref<any[]>([]);
const messageType = computed(() => [ const messageType = computed(() => [
{ {
label: '消息', label: '消息',
@ -304,6 +300,7 @@
deptId, deptId,
}); });
renderData.value = res.data.records; renderData.value = res.data.records;
selectData.value = selectData.value.concat(res.data.records);
} catch (err) { } catch (err) {
// you can report use errorHandler or other // you can report use errorHandler or other
} }
@ -377,14 +374,15 @@
const getNamesByIds = (ids: number[]) => { const getNamesByIds = (ids: number[]) => {
return ids return ids
.map((id) => renderData.value.find((dept) => dept.id === id)?.username) .map((id) => selectData.value.find((dept) => dept.id === id)?.username)
.filter((username) => username !== undefined) as string[]; .filter((username) => username !== undefined) as string[];
}; };
// //
const deptTreeSubmit = () => { const deptTreeSubmit = () => {
deptVisible.value = false; deptVisible.value = false;
formData.value.userIds = selectedIds.value; formData.value.userIds = [...selectedIds.value];
selectedNames.value = getNamesByIds(selectedIds.value); selectedNames.value = getNamesByIds(selectedIds.value);
}; };