You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

520 lines
12 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!--
图件的图层树
add by RYG
-->
<template>
<div class="tree-component">
<!-- 加载状态由父组件控制 -->
<div v-if="loading" class="loading">
加载中……
<!-- <el-skeleton :rows="3" animated /> -->
</div>
<div v-else class="tree-container">
<div class="operation" v-if="!props.isPopup">
<el-button
v-for="(btn, index) in iconButtons"
:key="index"
class="square-icon-button"
:icon="getNodeIcon(btn.id)"
:type="btn.type"
:title="btn.title"
text
@click="() => setLayerStatus(btn.id)"
/>
<el-divider direction="vertical" />
<el-button
class="square-icon-button"
:icon="Delete"
type="danger"
circle
title="删除"
text
@click="deleteLayer"
/>
</div>
<!-- 树形结构(使用外部传入的数据) -->
<el-tree
ref="treeRef"
v-model="selectedKeys"
:data="treeData"
show-checkbox
node-key="id"
default-expand-all
:highlight-current="true"
:props="defaultProps"
placeholder="点击展开加载子节点..."
clearable
@check="handleCheck"
class="custom-tree"
>
<template #default="{ node, data }">
<div class="tree-node">
<span class="icon-container">
<!-- <el-icon :size="treeConfig.iconSize" :class="'icon-orange'">
<component :is="data.icon" />
</el-icon> -->
<el-icon
v-if="data.status"
:size="treeConfig.iconSize"
:class="'icon-orange'"
>
<component :is="getNodeIcon(data.status)" />
</el-icon>
</span>
<span>{{ node.label }}</span>
<!-- <span v-if="data.count" class="node-count">{{ data.count }}</span> -->
</div>
</template>
</el-tree>
<div class="popup-operation" v-if="props.isPopup">
<el-button :icon="Finished" @click="handleConfirm" type="primary">确定</el-button>
</div>
</div>
<!-- 操作按钮 -->
<!-- <div class="actions" v-if="treeData.length">
<el-button type="primary" @click="getSelectedNodes">获取选中项</el-button>
<el-button @click="clearSelection"></el-button>
</div> -->
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { emitter } from "@/utils/eventBus";
import { ElTree, ElSkeleton, ElButton } from "element-plus";
import { Hide, Lock, Edit, Delete, Finished } from "@element-plus/icons-vue";
import { useRoute } from 'vue-router';
const route = useRoute();
const { message, status, send } = useWebSocket();
const props = defineProps({
isPopup: { type: Boolean, default: false },
});
const isPopup = computed(() => {
return props.isPopup;
});
const iconButtons = [
{ icon: "edit", title: "可编辑", type: "warning", id: 1 },
{ icon: "lock", title: "只读", type: "warning", id: 2 },
{ icon: "hide", title: "隐藏", type: "warning", id: 3 },
// { icon: Delete, title: "删除", type: "danger" },
];
// 树配置
const treeConfig = ref({
fontSize: 13,
iconSize: 13,
nodeHeight: 25,
currentHighlight: true,
showIcons: true,
});
// 树的数据
const treeData = ref([]);
// 加载状态
const loading = ref(true);
// 得到的原始图层数据
const sourceData = ref([]);
// 响应式数据
const treeRef = ref(null);
// 选中的节点 ID 数组
const selectedKeys = ref([]);
// 树形配置(节点唯一标识、标签、子节点字段)
const defaultProps = {
children: "children",
label: "label",
};
watchEffect(() => {
try {
if (!message.value) return; // 如果没有新消息,直接返回
const json = JSON.parse(message.value);
// console.info(json);
const evtType = json.type;
// if (evtType != "Pong") {
// emitter.emit("evtType", { type: evtType, data: message.value });
// }
// }
switch (evtType) {
case "ReloadLayer":
let layerData = json.data;
showLayers(layerData);
loading.value = false;
console.log("加载:", loading.value);
break;
}
} finally {
}
});
// 组装树的数据
const showLayers = (layerData) => {
// 确保layerData是有效的JSON字符串
if (typeof layerData === "string") {
layerData = JSON.parse(layerData);
}
// 确保转换后是数组
if (!Array.isArray(layerData)) {
console.error("Invalid layer data format - expected array");
return;
}
sourceData.value = layerData; // 假设data是树形结构数据
// 创建图层根节点
let layerNodes = [
{
id: "Layer:",
pId: "-1",
label: "图层",
open: true,
halfCheck: false,
status: 1,
icon: getNodeIcon(1), // 默认图标为可编辑
children: [],
},
];
layerNodes[0].children = layerData.map((node) => processNode(node));
treeData.value = layerNodes;
};
const setLayerStatus = (status) => {
let nodes = getSelectedNodes();
nodes = nodes.filter((node) => node.name !== "图层");
let nCount = nodes.length;
if (nCount === 0) {
return;
}
let strStatus = "10";
if (status === 2) {
strStatus = "11";
} else if (status === 3) {
strStatus = "12";
}
let statusData = "";
for (let i = 0; i < nCount; i++) {
// var imgUrl = iconButtons.value[status].icon;
// nodes[i].status = status;
// treeObj.updateNode(nodes[i]);
editNodeById(treeData.value, nodes[i].id, status);
statusData += strStatus + "|Layer:\\" + nodes[i].fullPath + "\r\n";
}
send("SetLayersStatus", { layerData: statusData });
// 处理图层状态设置逻辑
console.log("设置图层状态为:", status);
};
const deleteLayer = () => {
let nodes = getSelectedNodes();
nodes = nodes.filter((node) => node.name !== "图层");
let nCount = nodes.length;
if (nCount === 0) {
return;
}
let statusData = "";
for (let i = 0; i < nCount; i++) {
// statusData += (nodes[i].id + ",");
statusData += "Layer:\\" + nodes[i].fullPath + ",";
// treeRef.value.removeNode(nodes[i]);
removeNodeById(treeData.value, nodes[i].id);
}
send("DeleteLayers", { layers: statusData, widthChild: 1 });
};
// 删除指定ID的节点
const removeNodeById = (tree, id) => {
for (let i = 0; i < tree.length; i++) {
if (tree[i].id === id) {
const removedNode = tree.splice(i, 1)[0];
return { removed: true, removedNode };
}
if (tree[i].children && tree[i].children.length > 0) {
const result = removeNodeById(tree[i].children, id);
if (result.removed) {
return result;
}
}
}
return { removed: false };
};
// 修改节点的状态
const editNodeById = (tree, id, status) => {
for (let i = 0; i < tree.length; i++) {
if (tree[i].id === id) {
// 在这里可以修改节点的属性
tree[i].status = status;
tree[i].icon = getNodeIcon(status); // 更新图标
return true; // 找到并修改节点返回true
}
if (tree[i].children && tree[i].children.length > 0) {
const found = editNodeById(tree[i].children, id, status);
if (found) {
return true; // 如果在子节点中找到并修改返回true
}
}
}
return false; // 没有找到节点返回false
};
onMounted(() => {
// 页面加载完成后发送获取图层命令
// const isView = localStorage.getItem('isView');
// const nodeId = route.query.nodeId;
const id = route.query.id;
if (checkEmpty(id)) {
send("GetLayers");
} else {
const viewerToken = localStorage.getItem("viewerToken");
send("GetLayers",null,viewerToken);
}
});
const getNodeIcon = (status) => {
switch (status) {
case 1:
return Edit;
case 2:
return Lock;
case 3:
return Hide;
default:
return Edit; // 默认图标
}
};
// 递归处理节点和子节点
const processNode = (node) => {
let nodeIcon = getNodeIcon(node.Status);
const treeNode = {
id: node.Id.toString(),
pId: "Layer:", // node.ParentId.toString(),
label: node.Name || "",
open: false,
icon: nodeIcon,
status: node.Status || 0,
fullPath: node.FullPath || "",
children: [],
};
// 如果有子节点,递归处理
if (node.Children && node.Children.length > 0) {
treeNode.children = node.Children.map((child) => {
const childNode = processNode(child);
// 确保子节点的pId指向父节点id
childNode.pId = treeNode.id;
return childNode;
});
// 有子节点时默认展开
treeNode.open = true;
}
return treeNode;
};
onBeforeUnmount(() => {
// emitter.off("ReloadLayer");
});
// 选中节点事件处理
const handleCheck = (currentNode, checkStatus) => {
selectedKeys.value = checkStatus.checkedKeys;
};
// 获取选中的节点
const getSelectedNodes = () => {
// 通过 Tree 实例获取选中节点的完整数据
const selectedNodes = treeRef.value?.getCheckedNodes() || [];
return selectedNodes;
};
// 清空选择
const clearSelection = () => {
treeRef.value?.setCheckedKeys([]);
selectedKeys.value = [];
};
const handleConfirm = () => {
let nodes = getSelectedNodes();
nodes = nodes.filter((node) => node.name !== "图层");
let nCount = nodes.length;
if (nCount === 0) {
return;
}
let statusData = "";
for (let i = 0; i < nCount; i++) {
// statusData += (nodes[i].id + ",");
statusData += nodes[i].fullPath + ",";
// treeRef.value.removeNode(nodes[i]);
}
// console.log("格式化的数据:", statusData);
emitter.emit("selectedLayer", statusData);
// 得到数据之后清空选择
clearSelection();
};
</script>
<style scoped>
.tree-component {
width: 100%;
height: 100%;
margin: 0 auto;
padding: 0;
border: 0;
background: #fff;
max-width: 240px;
}
.tree-container {
width: 100%;
height: calc(100vh - 75px);
overflow: auto;
/* background: lightblue; */
padding: 0;
vertical-align: middle;
/* background: #2c3e50; */
}
.operation {
height: 40px;
vertical-align: middle;
padding-top: 5px;
padding-left: 10px;
border: 1px solid #eaeaea;
}
.popup-operation {
height: 40px;
vertical-align: middle;
padding-top: 10px;
padding-right: 10px;
border-top: 1px solid #eaeaea;
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.square-icon-button {
width: 25px;
height: 25px;
padding: 0 !important; /* 去掉内边距 */
border-radius: 4px; /* 设置圆角为4px若要直角则设为0 */
display: inline-flex;
justify-content: center;
align-items: center;
}
.square-icon-button:hover {
color: #ec0a0a; /* 鼠标悬停时的背景色 */
}
.loading {
width: 100%;
height: 100%;
display: flex;
background: #333;
vertical-align: middle;
justify-content: center;
padding: 0;
font-size: 14px;
color: #3498db;
}
.actions {
margin-top: 15px;
display: flex;
gap: 5px;
}
.custom-tree {
/* 自定义文字大小 */
--tree-font-size: 13px;
--tree-icon-size: 13px;
/* overflow: auto; */
font-size: var(--tree-font-size);
}
.custom-tree .el-tree-node__content {
height: 25px !important;
transition: background-color 0.2s;
padding: 0;
border-radius: 6px;
}
.custom-tree .el-tree-node__content:hover {
background-color: rgba(52, 152, 219, 0.1);
}
.custom-tree .el-tree-node__label {
/* font-weight: 300; */
color: #2c3e50;
white-space: nowrap;
margin-left: 0;
}
.custom-tree .el-tree-node__expand-icon {
font-size: var(--tree-icon-size);
transition: transform 0.3s;
}
.custom-tree .el-tree-node__expand-icon.expanded {
transform: rotate(90deg);
}
.custom-tree .el-tree-node.is-current > .el-tree-node__content {
background-color: rgba(52, 152, 219, 0.15);
}
.custom-tree .el-tree-node.is-current > .el-tree-node__content .el-tree-node__label {
color: #2980b9;
/* font-weight: 400; */
}
.icon-container {
display: inline-flex;
align-items: center;
justify-content: center;
margin-right: 2px;
width: 16px;
height: 16px;
}
.icon-blue {
color: #3498db;
}
.icon-green {
color: #27ae60;
}
.icon-orange {
color: #e67e22;
}
.icon-purple {
color: #9b59b6;
}
.el-tree {
--el-tree-node-hover-bg-color: rgba(52, 152, 219, 0.1);
--el-tree-text-color: #2c3e50;
--el-tree-expand-icon-color: #e4d61b;
/* background: #333; */
display: flex;
}
</style>