feat(新增物模型模块及优化部门树): 新增物模型模块及优化其它模块代码

This commit is contained in:
Kven 2025-01-08 11:13:44 +08:00
parent daf3f540a4
commit 1cbd436f73
18 changed files with 1699 additions and 182 deletions

View File

@ -14,7 +14,7 @@ export default mergeConfig(
proxy: { proxy: {
'/api': { '/api': {
// target: 'http://8.134.75.234:8081', // target: 'http://8.134.75.234:8081',
target: 'http://127.0.0.1:8081', target: 'http://192.168.3.238:8081',
changeOrigin: true, changeOrigin: true,
}, },
}, },

View File

@ -11,6 +11,17 @@ export interface DeptRecord extends DeptCreateRecord {
id: string; id: string;
} }
// 获取部门树
export function getAllDeptTree(id: number | string) {
return axios({
method: 'get',
url: '/api/rest/dept/tree',
params: {
id,
}
});
}
// 添加区域 // 添加区域
export function create(data: DeptCreateRecord) { export function create(data: DeptCreateRecord) {
return axios.post(`/api/rest/dept`, data); return axios.post(`/api/rest/dept`, data);

View File

@ -24,8 +24,6 @@ export interface MessageCreateRecord {
attachmentIds?: string[]; attachmentIds?: string[];
} }
// 查看详情 // 查看详情
export function queryMessage(userId: number, messageId: number) { export function queryMessage(userId: number, messageId: number) {
return axios.get(`/api/rest/message/${userId}/${messageId}`); return axios.get(`/api/rest/message/${userId}/${messageId}`);

75
src/api/tsl.ts Normal file
View File

@ -0,0 +1,75 @@
import axios from 'axios';
export interface Record {
current: number;
size: number;
}
export interface ServeRecord extends Record {
name?: string;
identifier?: string;
productId: number;
}
export interface PropertyRecord extends Record {
name?: string;
identifier?: string;
productId: number;
dataType?: string;
ioType?: string;
}
export interface eventRecord extends Record {
name?: string;
level?: string;
identifier?: string;
productId?: number;
}
export function queryServeList(data: ServeRecord) {
return axios({
url: '/api/rest/tsl/serve',
method: 'get',
params: data,
});
}
export function queryPropertyList(data: PropertyRecord) {
return axios({
url: '/api/rest/tsl/property',
method: 'get',
params: data,
});
}
export function queryEventList(data: eventRecord) {
return axios({
url: '/api/rest/tsl/event',
method: 'get',
params: data,
});
}
export function createServe(data: any) {
return axios.post(`/api/rest/tsl/serve`, data);
}
export function createProperty(data: any) {
return axios.post(`/api/rest/tsl/property`, data);
}
export function createEvent(data: any) {
return axios.post(`/api/rest/tsl/event`, data);
}
export function deleteServe(data: any) {
return axios.delete(`/api/rest/tsl/serve/${data}`, );
}
export function deleteProperty(data: any) {
return axios.delete(`/api/rest/tsl/property/${data}`);
}
export function deleteEvent(data: any) {
return axios.delete(`/api/rest/tsl/event/${data}`);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

34
src/mock/deptTree.ts Normal file
View File

@ -0,0 +1,34 @@
import Mock from 'mockjs';
import setupMock, { successResponseWrap } from '@/utils/setup-mock';
// 生成部门树数据
const deptTree = Mock.mock({
'totalDept': {
'dept': '总部门',
'children': [
{
'dept': '部门1',
'members': [
{'name': '@cname'},
{'name': '@cname'}
]
},
{
'dept': '部门2',
'members': [
{'name': '@cname'},
{'name': '@cname'}
]
}
]
}
});
setupMock({
setup: () => {
Mock.mock(new RegExp('/api/deptTree'), () => {
return successResponseWrap(deptTree);
})
}
})
// 假设你需要将这个数据作为一个 API 的响应

View File

@ -46,6 +46,29 @@ const IOT: AppRouteRecordRaw = {
permissions: ['*'], permissions: ['*'],
}, },
}, },
{
path: 'device/:id',
name: 'deviceDetail',
component: () => import('@/views/iot/device/components/device-detail.vue'),
meta: {
title: '设备详情',
requiresAuth: true,
showInMenu: false,
permissions: ['*'],
},
},
{
path: 'product/tsl/:id',
name: 'productTsl',
component: () => import('@/views/iot/product/components/product-tsl.vue'),
meta: {
title: '物模型',
requiresAuth: true,
showInMenu: false,
permissions: ['*'],
},
}
], ],
}; };

View File

@ -0,0 +1,94 @@
<template>
<a-card
class="general-card"
title='公告'
:header-style="{ paddingBottom: '0' }"
:body-style="{ padding: '15px 20px 13px 20px' }"
>
<template #extra>
<a-link @click="$router.push({ name: 'Bulletins' })">查看更多</a-link>
</template>
<div>
<div v-for="(item, idx) in renderData" :key="idx" class="item">
<a-tag color=blue size="small">通知</a-tag>
<span class="item-content" @click="$router.push({name:'Details',params:{id:item.id}})">
{{ item.title }}
</span>
</div>
</div>
</a-card>
</template>
<script lang="ts" setup>
//
import { BulletinsRecord } from '@/api/bulletins';
import { useBulletinsStore } from '@/store';
import { ref } from 'vue';
const bulletinsStore = useBulletinsStore();
const renderData = ref<BulletinsRecord[]>([]);
//
const fetchData = async (
params: BulletinsRecord = { current: 1 ,size: 5}
) => {
try {
const res = await bulletinsStore.getBulletinsList(params);
renderData.value = res.data.records;
} catch (err) {
// you can report use errorHandler or other
}
};
fetchData();
const list = [
{
type: 'orangered',
label: '活动',
content: '内容最新优惠活动',
},
{
type: 'cyan',
label: '消息',
content: '新增内容尚未通过审核,详情请点击查看。',
},
{
type: 'blue',
label: '通知',
content: '当前产品试用期即将结束,如需续费请点击查看。',
},
{
type: 'blue',
label: '通知',
content: '1月新系统升级计划通知',
},
{
type: 'cyan',
label: '消息',
content: '新增内容已经通过审核,详情请点击查看。',
},
];
</script>
<style scoped lang="less">
.item {
display: flex;
align-items: center;
width: 100%;
height: 24px;
margin-bottom: 4px;
.item-content {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 4px;
color: var(--color-text-2);
text-decoration: none;
font-size: 13px;
cursor: pointer;
}
}
</style>

View File

@ -0,0 +1,35 @@
<template>
<a-card
class="general-card"
:title="$t('workplace.quick.operation')"
:header-style="{ paddingBottom: '0' }"
:body-style="{ padding: '24px 20px 0 20px' }"
>
<template #extra>
<a-link>{{ $t('workplace.quickOperation.setup') }}</a-link>
</template>
<a-row :gutter="8">
<a-col v-for="link in links" :key="link.text" :span="8" class="wrapper">
<div class="icon">
<component :is="link.icon" />
</div>
<a-typography-paragraph class="text">
{{ $t(link.text) }}
</a-typography-paragraph>
</a-col>
</a-row>
<a-divider class="split-line" style="margin: 0" />
</a-card>
</template>
<script lang="ts" setup>
const links = [
{ text: 'workplace.contentManagement', icon: 'icon-file' },
{ text: 'workplace.contentStatistical', icon: 'icon-storage' },
{ text: 'workplace.advanced', icon: 'icon-settings' },
{ text: 'workplace.onlinePromotion', icon: 'icon-mobile' },
{ text: 'workplace.contentPutIn', icon: 'icon-fire' },
];
</script>
<style scoped lang="less"></style>

View File

@ -0,0 +1,44 @@
<template>
<a-card
class="general-card"
:title="$t('workplace.recently.visited')"
:header-style="{ paddingBottom: '0' }"
:body-style="{ paddingTop: '26px' }"
>
<div style="margin-bottom: -1rem">
<a-row :gutter="8">
<a-col v-for="link in links" :key="link.text" :span="8" class="wrapper">
<div class="icon">
<component :is="link.icon" />
</div>
<a-typography-paragraph class="text">
{{ $t(link.text) }}
</a-typography-paragraph>
</a-col>
</a-row>
</div>
</a-card>
</template>
<script lang="ts" setup>
const links = [
{
text: 'workplace.contentManagement',
icon: 'icon-storage',
},
{
text: 'workplace.contentStatistical',
icon: 'icon-file',
},
{
text: 'workplace.advanced',
icon: 'icon-settings',
},
];
</script>
<style lang="less" scoped>
:deep(.arco-card-header-title) {
line-height: inherit;
}
</style>

View File

@ -1,70 +1,102 @@
<template> <template>
<a-button <div class="container">
type="outline" <Breadcrumb :items="['物联网管理', '设备管理', '设备详情']" />
size="small" <a-card class="general-card" title=" ">
status="success" <a-descriptions v-model="renderData" size="large">
style="padding: 7px; margin-right: 10px" <template #title><h3 style="margin-top: -15px">设备详情</h3></template>
@click="handleClick" <a-descriptions-item label="设备名称">{{renderData.name }}</a-descriptions-item>
> <a-descriptions-item label="硬件版本">{{renderData.hardwareVersion }}</a-descriptions-item>
<template #icon><icon-list /></template> <a-descriptions-item label="固件版本">{{renderData.firmwareVersion }}</a-descriptions-item>
详情 <a-descriptions-item label="所属产品">{{renderData.productId }}</a-descriptions-item>
</a-button> <!-- <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-modal </a-descriptions>
width="600px" </a-card>
:visible="visible" <a-card class="general-card" style="margin-top: 10px">
@cancel="handleCancel" <a-tabs :active-key="activeKey" @tab-click="handleMenuClick" style="padding-top: 20px">
> <a-tab-pane key="1" tab="参数" title="扩展属性">
<template #title>设备详情</template> <a-table :columns="columns" :data="renderData.extendParams" />
<a-descriptions style="margin-top: 20px" :data="formData" size="large" :title=formData.name :column="1"> </a-tab-pane>
<a-descriptions-item label="产品名称">{{formData.productId}}</a-descriptions-item> <a-tab-pane key="2" tab="基本信息" title="基本信息">
<a-descriptions-item label="硬件版本">{{formData.hardwareVersion}}</a-descriptions-item> 基本信息
<a-descriptions-item label="固件版本">{{formData.firmwareVersion}}</a-descriptions-item> </a-tab-pane>
<a-descriptions-item label="扩展属性">{{formData.extendParams}}</a-descriptions-item> <a-tab-pane key="3" tab="执行服务" title="执行服务"> 执行服务内容 </a-tab-pane>
</a-descriptions> </a-tabs>
</a-card>
<template #footer> </div>
<!-- <a-button class="editor-button" @click="handleCancel">取消</a-button>-->
<a-button class="editor-button" type="primary" @click="handleCancel">确定</a-button>
</template>
</a-modal>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import useVisible from '@/hooks/visible'; import dayjs from 'dayjs';
import { defineEmits, PropType, ref } from 'vue'; import { useRoute } from 'vue-router';
import { DeviceCreateRecord } from '@/api/device'; import { onMounted, ref } from 'vue';
import '@wangeditor/editor/dist/css/style.css' import { queryDeviceDetail } from '@/api/device';
const route = useRoute();
const props = defineProps({ const id = Number(route.params.id);
prem: { const columns = [
type: Object as PropType<DeviceCreateRecord>, {
title: '参数名称',
dataIndex: 'name',
slotName: 'name',
}, },
isCreate: Boolean, {
}); title: '参数标识',
const emit = defineEmits(['refresh']); dataIndex: 'identifier',
const { visible, setVisible } = useVisible(false); slotName: 'identifier',
},
const formData = ref<any>({ {
...props.prem, title: '数据类型',
}); dataIndex: 'dataType',
slotName: 'dataType',
},
// {
const handleClick = () => { title: '参数类型',
setVisible(true); dataIndex: 'type',
slotName: 'type',
},
];
const activeKey = ref('1');
const renderData = ref([]);
const fetchData = async (Id: number) => {
const res = await queryDeviceDetail(Id);
renderData.value = res.data;
}; };
const handleMenuClick = (e: any) => {
// activeKey.value = e;
const handleCancel = async () => {
setVisible(false);
}; };
onMounted(() => {
fetchData(id);
});
</script> </script>
<style scoped> <style scoped>
.editor-button{ .container {
position: static padding: 0 20px 20px 20px;
}
h1 {
font-size: 24px;
margin-bottom: 10px;
}
.meta span {
margin-right: 20px;
}
.attachments li {
margin-bottom: 10px;
}
.attachments a {
display: flex;
align-items: center;
font-size: 14px;
color: #1890ff;
text-decoration: none;
}
.attachments a:hover {
text-decoration: underline;
} }
</style> </style>

View File

@ -82,13 +82,25 @@
<a-form-item <a-form-item
field="extendParams" field="extendParams"
label="扩展属性" label="扩展属性"
:rules="[{ required: true, message: '扩展属性不能为空' }]"
:validate-trigger="['change']"
> >
<a-input <!-- <a-input-->
v-model="formData.extendParams" <!-- v-model="formData.extendParams"-->
placeholder='请输入扩展属性' <!-- placeholder='请输入扩展属性'-->
/> <!-- />-->
<div style="width: 100%">
<div style="width: 100%;margin-bottom: 5px; ">
<a-space v-for="(param,index) in paramsData" :key="index" style="margin-bottom: 5px">
<a-input v-model="param.name" placeholder="名称" allow-clear />
<a-input v-model="param.identifier" placeholder="标识" allow-clear />
<a-select v-model="param.dataType" :options="dataTypeOptions" allow-search />
<a-select v-model="param.type" :options="typeOptions" allow-search />
<a-button type="text" @click="handleDeleteParams(index)"><icon-minus-circle /></a-button>
</a-space>
</div>
<a-button @click="handleAddParams" style="width: 100%" >
<icon-plus />
</a-button>
</div>
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -108,6 +120,7 @@
import { useMessageStore } from '@/store'; import { useMessageStore } from '@/store';
import '@wangeditor/editor/dist/css/style.css' import '@wangeditor/editor/dist/css/style.css'
import { createDevice, updateDevice } from '@/api/device'; import { createDevice, updateDevice } from '@/api/device';
import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
const props = defineProps({ const props = defineProps({
prem: { prem: {
@ -115,6 +128,58 @@
}, },
isCreate: Boolean, isCreate: Boolean,
}); });
const paramsData = ref([
{
name: '',
identifier: '',
dataType: '',
type: '',
},
]);
const dataTypeOptions = computed<SelectOptionData[]>(() => [
{
label: '整型',
value: 'INT',
},
{
label: '单精度浮点型',
value: 'FLOAT',
},
{
label: '双精度浮点型',
value: 'DOUBLE',
},
{
label: '布尔型',
value: 'BOOLEAN',
},
{
label: '字符串',
value: 'STRING',
},
{
label: '日期型',
value: 'DATE',
},
{
label: '透传',
value: 'RAW',
},
]);
const typeOptions = computed<SelectOptionData[]>(() => [
{
label: '物模型输入',
value: 'INPUT',
},
{
label: '物模型输出',
value: 'OUTPUT',
},
{
label: '读写属性',
value: 'RW',
},
]);
const emit = defineEmits(['refresh']); const emit = defineEmits(['refresh']);
const modalTitle = computed(() => { const modalTitle = computed(() => {
return props.isCreate ? '创建设备' : '编辑设备'; return props.isCreate ? '创建设备' : '编辑设备';
@ -142,7 +207,22 @@
options.value = [] options.value = []
} }
}; };
//
const handleAddParams = () => {
// paramsVisible.value = true;
paramsData.value.push({
name: '',
identifier: '',
dataType: '',
type: '',
});
};
//
const handleDeleteParams = (record: any) => {
if (record !== -1) {
paramsData.value.splice(record, 1);
}
};
// //
const handleClick = () => { const handleClick = () => {
setVisible(true); setVisible(true);
@ -153,6 +233,7 @@
const valid = await CreateRef.value?.validate(); const valid = await CreateRef.value?.validate();
if (!valid) { if (!valid) {
// //
formData.value.extendParams = paramsData.value;
if (props.isCreate) { if (props.isCreate) {
// formData.value.username = formData.value.email; // formData.value.username = formData.value.email;
const res = await createDevice(formData.value); const res = await createDevice(formData.value);

View File

@ -162,17 +162,16 @@
{{ record.online == true? '是' : '否' }} {{ record.online == true? '是' : '否' }}
</template> </template>
<template #operations="{ record }"> <template #operations="{ record }">
<!-- <a-button--> <a-button
<!-- type="outline"--> type="outline"
<!-- size="small"--> size="small"
<!-- status="success"--> status="success"
<!-- style="padding: 7px; margin-right: 10px"--> style="padding: 7px; margin-right: 10px"
<!-- @click="openDetail(record.id)"--> @click="openDetail(record.id)"
<!-- >--> >
<!-- <template #icon><icon-list /></template>--> <template #icon><icon-list /></template>
<!-- 详情--> 详情
<!-- </a-button>--> </a-button>
<DeviceDetail :prem="record" />
<DeviceEdit <DeviceEdit
ref="editUserRef" ref="editUserRef"
:prem="record" :prem="record"
@ -338,7 +337,7 @@
// //
function openDetail(id:number): void{ function openDetail(id:number): void{
const url = router.resolve({ const url = router.resolve({
name: 'Detail', name: 'deviceDetail',
params: {id} params: {id}
}).href; }).href;
router.push(url); router.push(url);

View File

@ -1,20 +1,27 @@
<template> <template>
<div class="container"> <div class="container">
<Breadcrumb :items="['物联网管理', '产品管理','产品详情']" /> <Breadcrumb :items="['物联网管理', '产品管理', '产品详情']" />
<a-card class="general-card" title=" "> <a-card class="general-card" title=" ">
<div class="announcement-detail"> <a-descriptions v-model="renderData" size="large">
<a-descriptions :data="renderData" size="large" title="产品详情" bordered > <template #title><h3 style="margin-top: -15px">产品详情</h3></template>
<a-descriptions-item label="产品名称">{{renderData.name}}</a-descriptions-item> <a-descriptions-item label="产品名称">{{renderData.name }}</a-descriptions-item>
<a-descriptions-item label="产品类型">{{renderData.productType}}</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.model }}</a-descriptions-item>
<a-descriptions-item label="通讯协议">{{renderData.link}}</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="备注">{{renderData.remark }}</a-descriptions-item>
<a-descriptions-item label="创建时间">{{dayjs(renderData.createTime).format('YYYY-MM-DD HH:mm:ss')}}</a-descriptions-item> <a-descriptions-item label="创建时间">{{dayjs(renderData.createTime).format('YYYY-MM-DD HH:mm:ss') }}</a-descriptions-item>
<a-descriptions-item label="扩展属性"> </a-descriptions>
<a-table :columns="columns" :data="renderData.params" row-key="id" :pagination="false"/> </a-card>
</a-descriptions-item> <a-card class="general-card" style="margin-top: 10px">
</a-descriptions> <a-tabs :active-key="activeKey" @tab-click="handleMenuClick" style="padding-top: 20px">
</div> <a-tab-pane key="1" tab="参数" title="参数">
<a-table :columns="columns" :data="renderData.params" />
</a-tab-pane>
<a-tab-pane key="2" tab="基本信息" title="基本信息">
基本信息
</a-tab-pane>
<a-tab-pane key="3" tab="执行服务" title="执行服务"> 执行服务内容 </a-tab-pane>
</a-tabs>
</a-card> </a-card>
</div> </div>
</template> </template>
@ -27,7 +34,7 @@
const route = useRoute(); const route = useRoute();
const id = Number(route.params.id); const id = Number(route.params.id);
const columns=[ const columns = [
{ {
title: '参数名称', title: '参数名称',
dataIndex: 'name', dataIndex: 'name',
@ -49,72 +56,34 @@
slotName: 'type', slotName: 'type',
}, },
]; ];
const renderData = ref<any>( const activeKey = ref('1');
{ const renderData = ref([]);
name: '产品名称', const fetchData = async (Id: number) => {
productType: '设备', const res = await queryProductDetail(Id);
model: '123456', renderData.value = res.data;
link: 'TCP', };
remark: '123456', const handleMenuClick = (e: any) => {
createTime: '2023-08-08 10:10:10', activeKey.value = e;
params:[ };
{ onMounted(() => {
name:'设备名称', fetchData(id);
identifier:'1.0.0', });
dataType:'1.0.0',
type:'123456',
},
{
name:'设备名称',
identifier:'1.0.0',
dataType:'1.0.0',
type:'123456',
},
{
name:'设备名称',
identifier:'1.0.0',
dataType:'1.0.0',
type:'123456',
},
]
},
);
// const fetchData = async (Id: number) => {
// const res = await queryProductDetail(Id);
// renderData.value = res.data;
// };
// onMounted(() => {
// fetchData(id);
// });
</script> </script>
<style scoped> <style scoped>
.container { .container {
padding: 0 20px 20px 20px; padding: 0 20px 20px 20px;
} }
.announcement-detail {
min-width: 800px;
max-width: 800px;
margin: auto;
padding: 20px;
border: 1px solid #ddd;
background-color: #fff; /* 白色背景,与淡蓝色背景区分开来 */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
h1 { h1 {
font-size: 24px; font-size: 24px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.meta span { .meta span {
margin-right: 20px; margin-right: 20px;
} }
.attachments li { .attachments li {
margin-bottom: 10px; margin-bottom: 10px;
} }
@ -131,5 +100,3 @@
text-decoration: underline; text-decoration: underline;
} }
</style> </style>

View File

@ -0,0 +1,445 @@
<template>
<a-button v-if="props.isCreate" type="primary" @click="handleClick">
<template #icon><icon-plus /></template>
新建
</a-button>
<a-button
v-if="!props.isCreate"
type="outline"
size="small"
:style="{ marginRight: '10px', padding: '7px' }"
@click="handleClick"
>
<template #icon><icon-edit /></template>
修改
</a-button>
<a-modal
width="900px"
height="500px"
:visible="visible"
@cancel="handleCancel"
>
<template #title>{{ modalTitle }}</template>
<a-form
ref="CreateRef"
:model="formData"
:style="{ width: '800px',height: '420px' }"
>
<!-- 产品名称 -->
<a-form-item
field="name"
label='产品名称'
:rules="[{ required: true, message: '产品名称不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="formData.name"
placeholder='请输入产品名称'
/>
</a-form-item>
<!-- 产品分类-->
<a-form-item
field="productType"
label="产品分类"
:rules="[{ required: true, message: '产品分类不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="formData.productType"
placeholder='请选择产品分类'
/>
</a-form-item>
<!-- 产品类型 -->
<a-form-item
field="model"
label="类型"
:rules="[{ required: true, message: '产品类型不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="formData.model"
placeholder='请输入产品类型'
/>
</a-form-item>
<!-- 接入方式 -->
<a-form-item
field="link"
label="接入"
:rules="[{ required: true, message: '接入方式不能为空' }]"
:validate-trigger="['change']"
>
<a-select
v-model="formData.link"
placeholder='请输入接入方式'
:options="linkOptions"
allow-search
/>
</a-form-item>
<!-- 备注 -->
<a-form-item
field="remark"
label="备注"
>
<a-input
v-model="formData.remark"
placeholder='请输入备注'
/>
</a-form-item>
<!-- 参数 -->
<a-form-item
field="params"
label="参数"
>
<!-- <a-table-->
<!-- :columns="columns"-->
<!-- :data="paramsData"-->
<!-- :bordered="false"-->
<!-- :pagination="false"-->
<!-- :table-layout-fixed="true"-->
<!-- :show-header="false"-->
<!-- :hoverable="false"-->
<!-- :column-resizable="true"-->
<!-- :filter-icon-align-left="true"-->
<!-- :body-cell-class="{ padding: '0px',marginLeft: '-10px' }"-->
<!-- >-->
<!-- <template #name="{ record }">-->
<!-- <a-input-->
<!-- v-model="record.name"-->
<!-- placeholder="名称"-->
<!-- />-->
<!-- </template>-->
<!-- <template #identifier="{ record }">-->
<!-- <a-input-->
<!-- v-model="record.identifier"-->
<!-- placeholder="标识"-->
<!-- />-->
<!-- </template>-->
<!-- <template #dataType="{ record }">-->
<!-- <a-select-->
<!-- v-model="record.dataType"-->
<!-- :options="dataTypeOptions"-->
<!-- allow-search-->
<!-- />-->
<!-- </template>-->
<!-- <template #type="{ record }">-->
<!-- <a-select-->
<!-- v-model="record.type"-->
<!-- :options="typeOptions"-->
<!-- allow-search-->
<!-- />-->
<!-- </template>-->
<!-- <template #operation="{ record }">-->
<!-- <a-button type="text" @click="handleDeleteParams(record)"><icon-minus-circle /></a-button>-->
<!-- </template>-->
<!-- <template #footer>-->
<!-- <a-button @click="handleAddParams" style="width: 100%" >-->
<!-- <icon-plus />-->
<!-- </a-button>-->
<!-- </template>-->
<!-- </a-table>-->
<div style="width: 100%">
<div style="width: 100%;margin-bottom: 5px; ">
<a-space v-for="(param,index) in paramsData" :key="index" style="margin-bottom: 5px">
<a-input v-model="param.name" placeholder="名称" allow-clear />
<a-input v-model="param.identifier" placeholder="标识" allow-clear />
<a-select v-model="param.dataType" :options="dataTypeOptions" allow-search />
<a-select v-model="param.type" :options="typeOptions" allow-search />
<a-button type="text" @click="handleDeleteParams(index)"><icon-minus-circle /></a-button>
</a-space>
</div>
<a-button @click="handleAddParams" style="width: 100%" type="outline">
<icon-plus />
</a-button>
</div>
</a-form-item>
</a-form>
<template #footer>
<a-button class="editor-button" @click="handleCancel">取消</a-button>
<a-button class="editor-button" type="primary" @click="handleSubmit">确定</a-button>
</template>
</a-modal>
<a-modal
width="900px"
:visible="paramsVisible"
@cancel="paramsCancel"
>
<template #title>添加参数</template>
<a-form
ref="CreateRef"
:model="paramsData"
:style="{ width: '650px' }"
>
<!-- 添加参数 -->
<a-form-item
field="name"
label="参数名称"
:rules="[{ required: true, message: '参数名称不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="paramsData.name"
placeholder='请选择参数名称'
/>
</a-form-item>
<!-- 参数标识 -->
<a-form-item
field="identifier"
label="参数标识"
:rules="[{ required: true, message: '参数标识不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="paramsData.identifier"
placeholder='请选择参数标识'
/>
</a-form-item>
<a-form-item
field="dataType"
label="数据类型"
:rules="[{ required: true, message: '数据类型不能为空' }]"
:validate-trigger="['change']"
>
<a-select
v-model="paramsData.dataType"
placeholder='请输入数据类型'
:options="dataTypeOptions"
allow-search
/>
</a-form-item>
<a-form-item
field="type"
label="参数类型"
:rules="[{ required: true, message: '参数类型不能为空' }]"
:validate-trigger="['change']"
>
<a-select
v-model="paramsData.type"
placeholder='请输入参数类型'
:options="typeOptions"
allow-search
/>
</a-form-item>
</a-form>
<template #footer>
<a-button class="editor-button" @click="paramsCancel">取消</a-button>
<a-button class="editor-button" type="primary" @click="paramsSubmit">确定</a-button>
</template>
</a-modal>
</template>
<script lang="ts" setup>
import useVisible from '@/hooks/visible';
import { computed, defineEmits, onMounted, PropType, ref } from 'vue';
import { FormInstance } from '@arco-design/web-vue/es/form';
import { Message } from '@arco-design/web-vue';
import '@wangeditor/editor/dist/css/style.css'
import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
import { createProduct, ProductCreateRecord, queryProductDetail, updateProduct } from '@/api/product';
const props = defineProps({
prem: {
type: Object as PropType<ProductCreateRecord>,
},
isCreate: Boolean,
id: Number,
});
const emit = defineEmits(['refresh']);
const modalTitle = computed(() => {
return props.isCreate ? '创建产品' : '编辑产品';
});
const { visible, setVisible } = useVisible(false);
const CreateRef = ref<FormInstance>();
const formData = ref<any>({
// params: [],
// ...props.prem,
});
const columns=[
{
title: '参数名称',
dataIndex: 'name',
slotName: 'name',
width: '120',
},
{
title: '参数标识',
dataIndex: 'identifier',
slotName: 'identifier',
width: '120',
align: 'left'
},
{
title: '数据类型',
dataIndex: 'dataType',
slotName: 'dataType',
width: '150',
align: 'left'
},
{
title: '参数类型',
dataIndex: 'type',
slotName: 'type',
width: '180',
align: 'left'
},
{
title: '操作',
dataIndex: 'operation',
slotName: 'operation',
align: 'left'
}
];
const paramsData = ref([
{
name: '',
identifier: '',
dataType: '',
type: '',
},
]);
const linkOptions = computed<SelectOptionData[]>(() => [
{
label: 'TCP',
value: 'TCP',
},
{
label: 'HTTP',
value: 'HTTP',
},
{
label: 'MQTT',
value: 'MQTT',
},
]);
const dataTypeOptions = computed<SelectOptionData[]>(() => [
{
label: '整型',
value: 'INT',
},
{
label: '单精度浮点型',
value: 'FLOAT',
},
{
label: '双精度浮点型',
value: 'DOUBLE',
},
{
label: '布尔型',
value: 'BOOLEAN',
},
{
label: '字符串',
value: 'STRING',
},
{
label: '日期型',
value: 'DATE',
},
{
label: '透传',
value: 'RAW',
},
]);
const typeOptions = computed<SelectOptionData[]>(() => [
{
label: '物模型输入',
value: 'INPUT',
},
{
label: '物模型输出',
value: 'OUTPUT',
},
{
label: '读写属性',
value: 'RW',
},
]);
const paramsVisible = ref(false);
const paramsName = ref([]);
const fetchData = async (Id: number | undefined) => {
const res = await queryProductDetail(Id);
formData.value = res.data;
};
//
const handleClick = () => {
setVisible(true);
if (!props.isCreate) {
fetchData(props.id);
}
};
//
const handleSubmit = async () => {
const valid = await CreateRef.value?.validate();
if (!valid) {
//
if (props.isCreate) {
formData.value.params = paramsData.value;
// formData.value.username = formData.value.email;
const res = await createProduct(formData.value);
if (res.status === 200) {
Message.success({
content: '新建成功',
duration: 5 * 1000,
});
emit('refresh');
setVisible(false);
}
CreateRef.value?.resetFields();
} else {
//
const res = await updateProduct(formData.value);
if (res.status === 200) {
Message.success({
content: '修改成功',
duration: 5 * 1000,
});
emit('refresh');
setVisible(false);
}
}
}
};
//
const handleAddParams = () => {
// paramsVisible.value = true;
paramsData.value.push({
name: '',
identifier: '',
dataType: '',
type: '',
});
};
//
const handleDeleteParams = (record: any) => {
if (record !== -1) {
paramsData.value.splice(record, 1);
}
};
//
const paramsCancel = () => {
paramsVisible.value = false;
};
//
const paramsSubmit = () => {
paramsVisible.value = false;
formData.value.params.push(paramsData.value);
paramsName.value.push(paramsData.value.name);
}
//
const handleCancel = async () => {
setVisible(false);
};
</script>
<style scoped>
.editor-button{
position: static
}
</style>

View File

@ -0,0 +1,648 @@
<template>
<div class="container">
<Breadcrumb :items="['物联网管理', '产品管理', '物模型']" />
<a-card class="general-card" title=" " style="height: 50px">
<h3 style="margin-top: -24px">物模型</h3>
</a-card>
<a-card class="general-card" style="margin-top: 10px;padding-top: 20px">
<a-tabs type="rounded" position="left" size="large" @change="changeKey" animation style="">
<a-tab-pane key="1" title="属性">
<a-button type="primary" style="margin-bottom: 10px" @click="handleClick">
<template #icon><icon-plus /></template>
新建
</a-button>
<a-table :columns="columns" :data="propertyData" >
<template #operation="{ record }">
<a-button
type="outline"
status="danger"
size="small"
style="padding: 7px;margin-right: 10px"
@click="handleDeleteProperty(record)"
>
<template #icon><icon-delete /></template>
删除
</a-button>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="2" title="服务">
<a-button type="primary" style="margin-bottom: 10px" @click="handleClick">
<template #icon><icon-plus /></template>
新建
</a-button>
<a-table :columns="columns" :data="serveData" >
<template #operation="{ record }">
<a-button
type="outline"
status="danger"
size="small"
style="padding: 7px;margin-right: 10px"
@click="handleDeleteServe(record)"
>
<template #icon><icon-delete /></template>
删除
</a-button>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="3" title="事件">
<a-button type="primary" style="margin-bottom: 10px" @click="handleClick">
<template #icon><icon-plus /></template>
新建
</a-button>
<a-table :columns="columns" :data="eventData" >
<template #operation="{ record }">
<a-button
type="outline"
status="danger"
size="small"
style="padding: 7px;margin-right: 10px"
@click="handleDeleteEvent(record)"
>
<template #icon><icon-delete /></template>
删除
</a-button>
</template>
</a-table>
</a-tab-pane>
</a-tabs>
</a-card>
</div>
<a-modal
width="900px"
height="500px"
:visible="visible && keyValue==='1'"
@cancel="handleCancel"
>
<template #title>新建属性</template>
<a-form
ref="propertyCreateRef"
:model="propertyAddData"
:style="{ width: '650px' }"
>
<!-- 设备名称 -->
<a-form-item
field="name"
label="属性名称"
:rules="[{ required: true, message: '设备名称不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="propertyAddData.name"
placeholder='请输入设备名称'
/>
</a-form-item>
<!-- 标识 -->
<a-form-item
field="identifier"
label="标识"
:rules="[{ required: true, message: '标识不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="propertyAddData.identifier"
placeholder='请输入标识'
/>
</a-form-item>
<!-- 数据类型 -->
<a-form-item
field="dataType"
label="数据类型"
:rules="[{ required: true, message: '数据类型不能为空' }]"
:validate-trigger="['change']"
>
<a-select v-model="propertyAddData.dataType" :options="dataTypeOptions" allow-search placeholder="请选择数据类型"/>
</a-form-item>
<!-- 读写类型 -->
<a-form-item
field="ioType"
label="读写类型"
>
<a-radio-group v-model="propertyAddData.ioType">
<a-radio value="1">读写</a-radio>
<a-radio value="2">只读</a-radio>
</a-radio-group>
</a-form-item>
<!-- 备注 -->
<a-form-item
field="remark"
label="备注"
>
<a-textarea
v-model="propertyAddData.remark"
placeholder='请输入备注'
/>
</a-form-item>
</a-form>
<template #footer>
<a-button class="editor-button" @click="handleCancel">取消</a-button>
<a-button class="editor-button" type="primary" @click="handleSubmit">确定</a-button>
</template>
</a-modal>
<a-modal
width="900px"
height="500px"
:visible="visible && keyValue==='2'"
@cancel="handleCancel"
>
<template #title>新建服务</template>
<a-form
ref="serveCreateRef"
:model="serveAddData"
:style="{ width: '650px' }"
>
<!-- 服务名称 -->
<a-form-item
field="name"
label="服务名称"
:rules="[{ required: true, message: '服务名称不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="serveAddData.name"
placeholder='请输入服务名称'
/>
</a-form-item>
<!-- 标识 -->
<a-form-item
field="identifier"
label="标识"
:rules="[{ required: true, message: '标识不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="serveAddData.identifier"
placeholder='请输入标识'
/>
</a-form-item>
<!-- 输入参数 -->
<a-form-item
field="inputs"
label="输入参数"
>
<div style="width: 100%">
<div style="width: 100%;margin-bottom: 5px; ">
<a-space v-for="(param,index) in serveInputData" :key="index" style="margin-bottom: 5px">
<a-input v-model="param.name" placeholder="名称" allow-clear />
<a-input v-model="param.identifier" placeholder="标识" allow-clear />
<a-select v-model="param.dataType" :options="dataTypeOptions" allow-search placeholder="数据类型" />
<a-select v-model="param.type" :options="typeOptions" allow-search placeholder="类型"/>
<a-button type="text" @click="handleDeleteParams(index,'serveInputData')"><icon-minus-circle /></a-button>
</a-space>
</div>
<a-button @click="handleAddParams('serveInputData')" style="width: 100%" type="outline">
<icon-plus />
</a-button>
</div>
</a-form-item>
<!-- 输出参数 -->
<a-form-item
field="outputs"
label="输出参数"
>
<div style="width: 100%">
<div style="width: 100%;margin-bottom: 5px; ">
<a-space v-for="(param,index) in serveOutputData" :key="index" style="margin-bottom: 5px">
<a-input v-model="param.name" placeholder="名称" allow-clear />
<a-input v-model="param.identifier" placeholder="标识" allow-clear />
<a-select v-model="param.dataType" :options="dataTypeOptions" allow-search placeholder="数据类型"/>
<a-select v-model="param.type" :options="typeOptions" allow-search placeholder="类型"/>
<a-button type="text" @click="handleDeleteParams(index,'serveOutputData')"><icon-minus-circle /></a-button>
</a-space>
</div>
<a-button @click="handleAddParams('serveOutputData')" style="width: 100%" type="outline">
<icon-plus />
</a-button>
</div>
</a-form-item>
<!-- 备注 -->
<a-form-item
field="remark"
label="备注"
>
<a-textarea
v-model="serveInputData.remark"
placeholder='请输入备注'
/>
</a-form-item>
</a-form>
<template #footer>
<a-button class="editor-button" @click="handleCancel">取消</a-button>
<a-button class="editor-button" type="primary" @click="handleSubmit">确定</a-button>
</template>
</a-modal>
<a-modal
width="900px"
height="500px"
:visible="visible && keyValue==='3'"
@cancel="handleCancel"
>
<template #title>新建事件</template>
<a-form
ref="eventCreateRef"
:model="eventAddData"
:style="{ width: '650px' }"
>
<!-- 事件名称 -->
<a-form-item
field="name"
label="服务名称"
:rules="[{ required: true, message: '服务名称不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="eventAddData.name"
placeholder='请输入服务名称'
/>
</a-form-item>
<!-- 标识 -->
<a-form-item
field="identifier"
label="标识"
:rules="[{ required: true, message: '标识不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="eventAddData.identifier"
placeholder='请输入标识'
/>
</a-form-item>
<!-- 类型 -->
<a-form-item
field="type"
label="类型"
:rules="[{ required: true, message: '类型不能为空' }]"
:validate-trigger="['change']"
>
<a-input
v-model="eventAddData.type"
placeholder='请输入类型'
/>
</a-form-item>
<!-- 输出参数 -->
<a-form-item
field="outputs"
label="输出参数"
>
<div style="width: 100%">
<div style="width: 100%;margin-bottom: 5px; ">
<a-space v-for="(param,index) in eventOutputData" :key="index" style="margin-bottom: 5px">
<a-input v-model="param.name" placeholder="名称" allow-clear />
<a-input v-model="param.identifier" placeholder="标识" allow-clear />
<a-select v-model="param.dataType" :options="dataTypeOptions" allow-search placeholder="数据类型"/>
<a-select v-model="param.type" :options="typeOptions" allow-search placeholder="类型"/>
<a-button type="text" @click="handleDeleteParams(index,'eventOutputData')"><icon-minus-circle /></a-button>
</a-space>
</div>
<a-button @click="handleAddParams('eventOutputData')" style="width: 100%" type="outline">
<icon-plus />
</a-button>
</div>
</a-form-item>
<!-- 备注 -->
<a-form-item
field="remark"
label="备注"
>
<a-textarea
v-model="eventAddData.remark"
placeholder='请输入备注'
/>
</a-form-item>
</a-form>
<template #footer>
<a-button class="editor-button" @click="handleCancel">取消</a-button>
<a-button class="editor-button" type="primary" @click="handleSubmit">确定</a-button>
</template>
</a-modal>
</template>
<script lang="ts" setup>
import { useRoute } from 'vue-router';
import { computed, onMounted, ref } from 'vue';
import useVisible from '@/hooks/visible';
import {
createEvent,
createProperty,
createServe,
deleteEvent,
deleteProperty,
deleteServe,
queryEventList,
queryPropertyList,
queryServeList
} from '@/api/tsl';
import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
import { FormInstance } from '@arco-design/web-vue/es/form';
import { Message } from '@arco-design/web-vue';
const { visible, setVisible } = useVisible();
const route = useRoute();
const id = Number(route.params.id);
const columns = [
{
title: '名称',
dataIndex: 'name',
slotName: 'name',
},
{
title: '标识',
dataIndex: 'identifier',
slotName: 'identifier',
},
{
title: '备注',
dataIndex: 'remark',
slotName: 'remark',
},
{
title: '操作',
dataIndex: 'operation',
slotName: 'operation',
},
];
const serveData = ref([]);
const propertyData = ref([]);
const eventData = ref([]);
const keyValue = ref('1');
const paramsData = ref([]);
const dataTypeOptions = computed<SelectOptionData[]>(() => [
{
label: '整型',
value: 'INT',
},
{
label: '单精度浮点型',
value: 'FLOAT',
},
{
label: '双精度浮点型',
value: 'DOUBLE',
},
{
label: '布尔型',
value: 'BOOLEAN',
},
{
label: '字符串',
value: 'STRING',
},
{
label: '日期型',
value: 'DATE',
},
{
label: '透传',
value: 'RAW',
},
]);
const typeOptions = computed<SelectOptionData[]>(() => [
{
label: '物模型输入',
value: 'INPUT',
},
{
label: '物模型输出',
value: 'OUTPUT',
},
{
label: '读写属性',
value: 'RW',
},
]);
const propertyAddData = ref({
productId:id,
});
const eventAddData = ref({
productId:id,
outputs: [{
name: '',
identifier: '',
dataType: '',
type: '',
}],
});
const serveAddData = ref({
productId:id,
inputs: [{
name: '',
identifier: '',
dataType: '',
type: '',
}],
outputs: [{
name: '',
identifier: '',
dataType: '',
type: '',
}],
});
const serveInputData = ref([{
name: '',
identifier: '',
dataType: '',
type: '',
}]);
const serveOutputData = ref([{
name: '',
identifier: '',
dataType: '',
type: '',
}]);
const eventOutputData = ref([{
name: '',
identifier: '',
dataType: '',
type: '',
}]);
const propertyCreateRef = ref<FormInstance>();
const eventCreateRef = ref<FormInstance>();
const serveCreateRef = ref<FormInstance>();
const fetchData = async (Id: number) => {
const params={
size:10,
current:1,
productId:Id,
}
const res1 = await queryServeList(params);
serveData.value = res1.data.records;
const res2 = await queryPropertyList(params);
propertyData.value = res2.data.records;
const res3 = await queryEventList(params);
eventData.value = res3.data.records;
};
const changeKey= (e:any) =>{
keyValue.value = e;
}
//
const handleClick = () => {
setVisible(true);
};
//
const handleCancel = async () => {
setVisible(false);
};
//
const handleSubmit = async () => {
if(keyValue.value==='1'){
const valid = await propertyCreateRef.value?.validate();
if (!valid) {
const res = await createProperty(propertyAddData.value);
if (res.status === 200) {
Message.success({
content: '新建成功',
duration: 5 * 1000,
});
await fetchData(id);
setVisible(false);
}
}
} else if(keyValue.value==='2') {
const valid = await serveCreateRef.value?.validate();
if (!valid) {
serveAddData.value.inputs = serveInputData.value;
serveAddData.value.outputs = serveOutputData.value;
const res = await createServe(serveAddData.value);
if (res.status === 200) {
Message.success({
content: '新建成功',
duration: 5 * 1000,
});
await fetchData(id);
setVisible(false);
}
}
} else if(keyValue.value==='3') {
const valid = await eventCreateRef.value?.validate();
if (!valid) {
eventAddData.value.outputs = eventOutputData.value;
const res = await createEvent(eventAddData.value);
if (res.status === 200) {
Message.success({
content: '新建成功',
duration: 5 * 1000,
});
await fetchData(id);
setVisible(false);
}
}
}
};
//
const handleDeleteParams = (record: any,data: string) => {
console.log(record);
if (record !== -1 && data==='serveInputData') {
serveInputData.value.splice(record, 1);
} else if (record!== -1 && data==='serveOutputData') {
serveOutputData.value.splice(record, 1);
} else if (record!== -1 && data==='eventOutputData') {
eventOutputData.value.splice(record, 1);
}
};
//
const handleAddParams = (data:string) => {
console.log(data);
if(data==='serveInputData'){
serveInputData.value.push({
name: '',
identifier: '',
dataType: '',
type: '',
});
}else if(data==='serveOutputData') {
serveOutputData.value.push({
name: '',
identifier: '',
dataType: '',
type: '',
});
} else if(data==='eventOutputData') {
eventOutputData.value.push({
name: '',
identifier: '',
dataType: '',
type: '',
});
}
};
//
const handleDeleteProperty = async (record: any) => {
const res = await deleteProperty(record.id);
console.log(res);
if (res.status === 200) {
Message.success({
content: '删除成功',
duration: 5 * 1000,
});
await fetchData(id);
}
};
//
const handleDeleteServe = async (record: any) => {
console.log(record);
const res = await deleteServe(record.id);
if (res.status === 200) {
Message.success({
content: '删除成功',
duration: 5 * 1000,
});
await fetchData(id);
}
};
//
const handleDeleteEvent = async (record: any) => {
const res = await deleteEvent(record.id);
if (res.status === 200) {
Message.success({
content: '删除成功',
duration: 5 * 1000,
});
await fetchData(id);
}
};
onMounted(() => {
fetchData(id);
});
</script>
<style scoped>
.container {
padding: 0 20px 20px 20px;
}
h1 {
font-size: 24px;
margin-bottom: 10px;
}
.meta span {
margin-right: 20px;
}
.attachments li {
margin-bottom: 10px;
}
.attachments a {
display: flex;
align-items: center;
font-size: 14px;
color: #1890ff;
text-decoration: none;
}
.attachments a:hover {
text-decoration: underline;
}
</style>

View File

@ -172,6 +172,16 @@
<template #icon><icon-list /></template> <template #icon><icon-list /></template>
详情 详情
</a-button> </a-button>
<a-button
type="outline"
size="small"
status="warning"
style="padding: 7px; margin-right: 10px"
@click="openTsl(record.id)"
>
<template #icon><icon-list /></template>
物模型
</a-button>
<ProductEdit <ProductEdit
:id="record.id" :id="record.id"
ref="editUserRef" ref="editUserRef"
@ -339,6 +349,15 @@
router.push(url); router.push(url);
}; };
//
function openTsl(id:number): void{
const url = router.resolve({
name: 'productTsl',
params: {id}
}).href;
router.push(url);
};
// //
const handleDelete = async (id: number) => { const handleDelete = async (id: number) => {
const res = await deleteProduct(id); const res = await deleteProduct(id);

View File

@ -134,11 +134,12 @@
<a-card style="margin-right: 10px;width: 30%"> <a-card style="margin-right: 10px;width: 30%">
<a-tree <a-tree
:data="deptTreeData" :data="deptTreeData"
checkable="true" :field-names="{ title: 'name', key: 'id', children: 'children' }"
style="margin-bottom: 8px; max-width: 240px" style="margin-bottom: 8px; max-width: 240px"
@select="handleClickTree" @select="handleClickTree"
@check="handleCheckTree" @check="handleCheckTree"
/> >
</a-tree>
</a-card> </a-card>
<a-card style="flex: 1"> <a-card style="flex: 1">
<a-button type="primary" >全选</a-button> <a-button type="primary" >全选</a-button>
@ -146,11 +147,11 @@
:data="selectedDepartmentMembers" :data="selectedDepartmentMembers"
:columns="columns" :columns="columns"
style="height: 520px"> style="height: 520px">
<template #key="{ record }"> <template #id="{ record }">
<a-checkbox v-model="selectedIds" :value="record.key"> </a-checkbox> <a-checkbox v-model="selectedIds" :value="record.id"> </a-checkbox>
</template> </template>
<template #title="{ record }"> <template #username="{ record }">
{{ record.title }} {{ record.username }}
</template> </template>
</a-table> </a-table>
</a-card> </a-card>
@ -165,7 +166,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import useVisible from '@/hooks/visible'; import useVisible from '@/hooks/visible';
import { computed, defineEmits, PropType, ref, shallowRef, onBeforeUnmount, reactive } from 'vue'; import { computed, defineEmits, PropType, ref, shallowRef, onBeforeUnmount, reactive, onMounted } from 'vue';
import { CreateRecord } from '@/api/user'; import { CreateRecord } from '@/api/user';
import { FormInstance } from '@arco-design/web-vue/es/form'; import { FormInstance } from '@arco-design/web-vue/es/form';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
@ -174,6 +175,7 @@
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'; import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
import { IEditorConfig } from '@wangeditor/editor' import { IEditorConfig } from '@wangeditor/editor'
import '@wangeditor/editor/dist/css/style.css' import '@wangeditor/editor/dist/css/style.css'
import { getAllDeptTree } from '@/api/dept';
const props = defineProps({ const props = defineProps({
prem: { prem: {
@ -205,12 +207,12 @@
const columns = computed<any[]>(()=>[ const columns = computed<any[]>(()=>[
{ {
title: '操作', title: '操作',
dataIndex: 'key', dataIndex: 'id',
slotName: 'key', slotName: 'id',
}, },
{ {
title: '用户', title: '用户',
dataIndex: 'title', dataIndex: 'username',
}, },
]) ])
@ -253,33 +255,33 @@
// }, // },
// }, // },
// ); // );
const deptTreeData = reactive([ const deptTreeData = ref([
{ {
key: '1', id: '1',
title: '总部门', name: '总部门',
children: [ children: [
{ {
key: '2', id: '2',
title: '部门1', name: '部门1',
members: [ members: [
{ key: '101', title: '成员1' }, { id: '101', username: '成员1' },
{ key: '102', title: '成员2' } { id: '102', username: '成员2' }
] ]
}, },
{ {
key: '3', id: '3',
title: '部门2', name: '部门2',
members: [ members: [
{ key: '201', title: '成员3' }, { id: '201', username: '成员3' },
{ key: '202', title: '成员4' } { id: '202', username: '成员4' }
] ]
}, },
{ {
key: '4', id: '4',
title: '部门3', name: '部门3',
members: [ members: [
{ key: '203', title: '成员5' }, { id: '203', username: '成员5' },
{ key: '204', title: '成员6' } { id: '204', username: '成员6' }
] ]
} }
] ]
@ -287,6 +289,13 @@
]); ]);
const selectedDepartmentMembers: any = ref([]); const selectedDepartmentMembers: any = ref([]);
const renderData = ref<any[]>([]); const renderData = ref<any[]>([]);
//
const getDeptTree = async () => {
const res = await getAllDeptTree(1);
if (res.status === 200) {
deptTreeData.value = res.data;
}
}
// //
const queryDeptTree= async ()=>{ const queryDeptTree= async ()=>{
deptVisible.value = true; deptVisible.value = true;
@ -299,7 +308,7 @@
while (queue.length > 0) { while (queue.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const node = queue.shift()!; const node = queue.shift()!;
if (selectedKeys.includes(node.key)) { if (selectedKeys.includes(node.id)) {
if (node.members) { if (node.members) {
selectedMembers.push(...node.members); selectedMembers.push(...node.members);
} }
@ -313,12 +322,12 @@
// //
const handleClickTree = (key: string[]) => { const handleClickTree = (id: string[]) => {
// '1' // '1'
if (key[0] === '1' || key.length === 0) { if (id[0] === '1' || id.length === 0) {
// 广 // 广
const allMembers: any[] = []; const allMembers: any[] = [];
const queue:any = [...deptTreeData]; const queue:any = [...deptTreeData.value];
while (queue.length > 0) { while (queue.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const node = queue.shift()!; const node = queue.shift()!;
@ -333,17 +342,17 @@
renderData.value = selectedDepartmentMembers.value; renderData.value = selectedDepartmentMembers.value;
return; return;
} }
const Members = getSelectedMembers(deptTreeData, key); const Members = getSelectedMembers(deptTreeData.value, id);
selectedDepartmentMembers.value = Members; selectedDepartmentMembers.value = Members;
} }
// //
const handleCheckTree = (key: string[]) => { const handleCheckTree = (id: string[]) => {
if (key[0] === '1' ) { // '1' if (id[0] === '1' ) { // '1'
const allKeys: string[] = []; const allKeys: string[] = [];
const traverseTree = (node: any) => { const traverseTree = (node: any) => {
if (node.members) { if (node.members) {
node.members.forEach((member: any) => { node.members.forEach((member: any) => {
allKeys.push(member.key); allKeys.push(member.id);
}); });
} }
if (node.children) { if (node.children) {
@ -352,14 +361,14 @@
}); });
} }
}; };
deptTreeData.forEach((node: any) => { deptTreeData.value.forEach((node: any) => {
traverseTree(node); traverseTree(node);
}); });
selectedIds.value = allKeys; selectedIds.value = allKeys;
} else { } else {
const selectedKeys = getSelectedMembers(deptTreeData, key); const selectedKeys = getSelectedMembers(deptTreeData.value, id);
selectedIds.value = selectedKeys.map(item => item.key); selectedIds.value = selectedKeys.map(item => item.id);
} }
}; };
@ -423,6 +432,9 @@
const deptTreeCancel = () => { const deptTreeCancel = () => {
deptVisible.value = false; deptVisible.value = false;
}; };
onMounted(async () => {
await getDeptTree();
});
</script> </script>
<style scoped> <style scoped>