373 lines
10 KiB
Vue
373 lines
10 KiB
Vue
<template>
|
|
<a-card :bordered="false" style="height: 100%">
|
|
<a-spin :spinning="syncoading">
|
|
<div class="j-table-operator" style="width: 100%">
|
|
<a-button preIcon="ant-design:sync-outlined" @click="loadRootTreeData">刷新</a-button>
|
|
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreateProject">新增</a-button>
|
|
<a-button preIcon="ant-design:sync-outlined" @click="syncProjectInfo">同步</a-button>
|
|
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreateRegion">新增下级</a-button>
|
|
<template v-if="currentRegion !=null">
|
|
<a-button preIcon="ant-design:sync-outlined" @click="syncRegionInfo">同步下级</a-button>
|
|
</template>
|
|
</div>
|
|
<a-spin :spinning="loading">
|
|
<!-- <a-input-search placeholder="按名称搜索…" style="margin-bottom: 10px" @search="onSearch" />-->
|
|
<!--分组树-->
|
|
<template v-if="treeData.length > 0">
|
|
<a-tree
|
|
v-if="!treeReloading"
|
|
:clickRowToExpand="false"
|
|
:treeData="treeData"
|
|
:selectedKeys="selectedKeys"
|
|
:checkStrictly="checkStrictly"
|
|
:load-data="loadChildrenTreeData"
|
|
:checkedKeys="checkedKeys"
|
|
v-model:expandedKeys="expandedKeys"
|
|
@select="onSelect"
|
|
>
|
|
<template #title="{ key: treeKey, title, dataRef }">
|
|
<a-dropdown :trigger="['contextmenu']">
|
|
<span>{{ title }}</span>
|
|
<template #overlay>
|
|
<a-menu @click="">
|
|
<a-menu-item key="1" @click="onAddChild(dataRef)">添加子级</a-menu-item>
|
|
<a-menu-item key="2" @click="handleDeleteNode(dataRef)">
|
|
<span style="color: red">删除</span>
|
|
</a-menu-item>
|
|
</a-menu>
|
|
</template>
|
|
</a-dropdown>
|
|
</template>
|
|
</a-tree>
|
|
</template>
|
|
<a-empty v-else description="暂无数据" />
|
|
</a-spin>
|
|
</a-spin>
|
|
</a-card>
|
|
<!-- 表单分组 -->
|
|
<ProjectInfoDrawer @register="registerProjectDrawer" @success="handleSuccess" />
|
|
<RegionInfoDrawer @register="registerRegionDrawer" @success="handleSuccess" />
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import {createVNode, nextTick, ref} from 'vue';
|
|
import { useModal } from '/@/components/Modal';
|
|
import { useMessage } from '/@/hooks/web/useMessage';
|
|
import { useMethods } from '/@/hooks/system/useMethods';
|
|
import {
|
|
deleteRegion,
|
|
queryProjectTreeSync,
|
|
queryRegionTreeSync,
|
|
syncProject,
|
|
syncRegion,
|
|
syncRegionChildren
|
|
} from '../RegionInfo.api';
|
|
const { createMessage } = useMessage();
|
|
import {useDrawer} from "@/components/Drawer";
|
|
import ProjectInfoDrawer from '@/views/iot/tplink/project/components/ProjectInfoDrawer.vue';
|
|
import RegionInfoDrawer from './RegionInfoDrawer.vue';
|
|
import {Modal} from "ant-design-vue";
|
|
import {ExclamationCircleOutlined} from "@ant-design/icons-vue";
|
|
import {deletePrject} from "@/views/iot/tplink/project/ProjectInfo.api";
|
|
|
|
const emit = defineEmits(['select', 'rootTreeData']);
|
|
const syncoading = ref<boolean>(false);
|
|
const loading = ref<boolean>(false);
|
|
// 分组树列表数据
|
|
const treeData = ref<any[]>([]);
|
|
// 当前选中的项
|
|
const checkedKeys = ref<any[]>([]);
|
|
// 当前展开的项
|
|
const expandedKeys = ref<any[]>([]);
|
|
// 当前选中的项
|
|
const selectedKeys = ref<any[]>([]);
|
|
// 树组件重新加载
|
|
const treeReloading = ref<boolean>(false);
|
|
// 树父子是否关联
|
|
const checkStrictly = ref<boolean>(true);
|
|
// 当前选中的分组
|
|
const currentRegion = ref<any>(null);
|
|
//注册drawer
|
|
const [registerProjectDrawer, { openDrawer : openProjectDrawer }] = useDrawer();
|
|
const [registerRegionDrawer, { openDrawer : openRegionDrawer }] = useDrawer();
|
|
|
|
// 加载顶级分组信息
|
|
async function loadRootTreeData() {
|
|
try {
|
|
loading.value = true;
|
|
treeData.value = [];
|
|
const result = await queryProjectTreeSync();
|
|
if (Array.isArray(result)) {
|
|
treeData.value = result;
|
|
}
|
|
if (expandedKeys.value.length === 0) {
|
|
autoExpandParentNode();
|
|
} else {
|
|
if (selectedKeys.value.length === 0) {
|
|
let item = treeData.value[0];
|
|
if (item) {
|
|
// 默认选中第一个
|
|
setSelectedKey(item.id, item);
|
|
}
|
|
} else {
|
|
emit('select', currentRegion.value);
|
|
}
|
|
}
|
|
emit('rootTreeData', treeData.value);
|
|
} finally {
|
|
loading.value = false;
|
|
syncoading.value = false;
|
|
}
|
|
}
|
|
|
|
loadRootTreeData();
|
|
|
|
/**
|
|
* 加载子级分组信息
|
|
*/
|
|
async function loadChildrenTreeData(treeNode) {
|
|
try {
|
|
const result = await queryRegionTreeSync({
|
|
parentId: treeNode.dataRef.id,
|
|
projectId: treeNode.dataRef.projectId,
|
|
});
|
|
if (result.length == 0) {
|
|
treeNode.dataRef.isLeaf = true;
|
|
} else {
|
|
treeNode.dataRef.children = result;
|
|
if (expandedKeys.value.length > 0) {
|
|
// 判断获取的子级是否有当前展开的项
|
|
let subKeys: any[] = [];
|
|
for (let key of expandedKeys.value) {
|
|
if (result.findIndex((item) => item.id === key) !== -1) {
|
|
subKeys.push(key);
|
|
}
|
|
}
|
|
if (subKeys.length > 0) {
|
|
expandedKeys.value = [...expandedKeys.value];
|
|
}
|
|
}
|
|
}
|
|
treeData.value = [...treeData.value];
|
|
emit('rootTreeData', treeData.value);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
/**
|
|
* 自动展开父节点,只展开一级
|
|
*/
|
|
function autoExpandParentNode() {
|
|
let item = treeData.value[0];
|
|
if (item) {
|
|
if (!item.isLeaf) {
|
|
expandedKeys.value = [item.key];
|
|
}
|
|
// 默认选中第一个
|
|
setSelectedKey(item.id, item);
|
|
reloadTree();
|
|
} else {
|
|
emit('select', null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 重新加载树组件,防止无法默认展开数据
|
|
*/
|
|
async function reloadTree() {
|
|
await nextTick();
|
|
treeReloading.value = true;
|
|
await nextTick();
|
|
treeReloading.value = false;
|
|
}
|
|
|
|
/**
|
|
* 设置当前选中的行
|
|
*/
|
|
function setSelectedKey(key: string, data?: object) {
|
|
selectedKeys.value = [key];
|
|
if (data) {
|
|
currentRegion.value = data;
|
|
emit('select', data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 树选择事件
|
|
*/
|
|
function onSelect(selKeys, event) {
|
|
if (selKeys.length > 0 && selectedKeys.value[0] !== selKeys[0]) {
|
|
setSelectedKey(selKeys[0], event.selectedNodes[0]);
|
|
} else {
|
|
// 这样可以防止用户取消选择
|
|
setSelectedKey(selectedKeys.value[0]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 同步项目
|
|
*/
|
|
async function syncProjectInfo(){
|
|
syncoading.value = true;
|
|
await syncProject();
|
|
await loadRootTreeData();
|
|
}
|
|
|
|
/**
|
|
* 同步分组
|
|
*/
|
|
async function syncRegionInfo(){
|
|
let data = currentRegion.value;
|
|
if (data == null) {
|
|
createMessage.warning('请先选择一个节点');
|
|
return;
|
|
}
|
|
const record = {
|
|
projectId: data.projectId,
|
|
regionId: data.regionId
|
|
};
|
|
if(data.regionId == null){
|
|
await syncRegion(record);
|
|
}else{
|
|
await syncRegionChildren(record);
|
|
}
|
|
syncoading.value = true;
|
|
|
|
await loadRootTreeData();
|
|
}
|
|
|
|
/**
|
|
* 新增
|
|
*/
|
|
function handleCreateProject() {
|
|
openProjectDrawer(true, {
|
|
isUpdate: false,
|
|
showFooter: true,
|
|
tenantSaas: false,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 新增下级
|
|
*/
|
|
function handleCreateRegion() {
|
|
let data = currentRegion.value;
|
|
if (data == null) {
|
|
createMessage.warning('请先选择一个节点');
|
|
return;
|
|
}
|
|
let record = {
|
|
projectId: data.projectId,
|
|
projectName: data.projectName,
|
|
parentId: data.regionId,
|
|
parentName: data.regionName,
|
|
};
|
|
if(data.regionId != null){
|
|
record["institutionId"] = data.areaId;
|
|
}else{
|
|
record["institutionId"] = data.institutionId;
|
|
}
|
|
openRegionDrawer(true, {
|
|
record,
|
|
isUpdate: false,
|
|
showFooter: true,
|
|
tenantSaas: false,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 添加子级
|
|
*/
|
|
function onAddChild(data = currentRegion.value){
|
|
if (data == null) {
|
|
createMessage.warning('请先选择一个节点');
|
|
return;
|
|
}
|
|
let record = {
|
|
projectId: data.projectId,
|
|
projectName: data.projectName,
|
|
parentId: data.regionId,
|
|
parentName: data.regionName,
|
|
};
|
|
if(data.regionId != null){
|
|
record["institutionId"] = data.areaId;
|
|
}else{
|
|
record["institutionId"] = data.institutionId;
|
|
}
|
|
openRegionDrawer(true, {
|
|
record,
|
|
isUpdate: false,
|
|
showFooter: true,
|
|
tenantSaas: false,
|
|
});
|
|
}
|
|
|
|
/**
|
|
*删除节点
|
|
*/
|
|
function handleDeleteNode(data = currentRegion.value){
|
|
if (data == null) {
|
|
createMessage.warning('请先选择一个节点');
|
|
return;
|
|
}
|
|
if(data.regionId == null){
|
|
handleDeleteProject(data);
|
|
}else{
|
|
handleDelete(data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除项目
|
|
*/
|
|
function handleDeleteProject(record: Recordable) {
|
|
Modal.confirm({
|
|
title: '删除项目',
|
|
width: '500px',
|
|
icon: createVNode(ExclamationCircleOutlined),
|
|
content: createVNode('div', { style: 'color:red;' }, '项目删除后,与之相关信息将失效,确定要删除该项目吗?'),
|
|
okText: '确定',
|
|
onOk() {
|
|
deletePrject(record, handleSuccess);
|
|
},
|
|
onCancel() {
|
|
},
|
|
class: 'test',
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 删除分组
|
|
*/
|
|
function handleDelete(record: Recordable) {
|
|
Modal.confirm({
|
|
title: '删除分组',
|
|
width: '500px',
|
|
icon: createVNode(ExclamationCircleOutlined),
|
|
content: createVNode('div', { style: 'color:red;' }, '分组删除后,该分组下的所有设备将被转移,确定要删除该分组吗?'),
|
|
okText: '确定',
|
|
onOk() {
|
|
deleteRegion(record, handleSuccess);
|
|
},
|
|
onCancel() {
|
|
},
|
|
class: 'test',
|
|
});
|
|
}
|
|
|
|
function handleSuccess(){
|
|
loadRootTreeData();
|
|
}
|
|
|
|
defineExpose({
|
|
loadRootTreeData,
|
|
});
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
|
|
|
|
</style>
|