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

1 month ago
<!--
图件的图层树
add by RYG
-->
<template>
<div class="tree-component">
<!-- 加载状态由父组件控制 -->
1 month ago
<div v-if="loading" class="loading">
1 month ago
加载中
1 month ago
<!-- <el-skeleton :rows="3" animated /> -->
</div>
<div v-else class="tree-container">
1 month ago
<div class="operation" v-if="!props.isPopup">
1 month ago
<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)"
/>
1 month ago
<el-divider direction="vertical" />
1 month ago
<el-button
class="square-icon-button"
:icon="Delete"
type="danger"
circle
title="删除"
text
@click="deleteLayer"
/>
1 month ago
</div>
<!-- 树形结构使用外部传入的数据 -->
1 month ago
<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"
>
1 month ago
<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> -->
1 month ago
<el-icon
v-if="data.status"
:size="treeConfig.iconSize"
:class="'icon-orange'"
>
1 month ago
<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>
1 month ago
<div class="popup-operation" v-if="props.isPopup">
1 month ago
<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";
1 month ago
import { ElTree, ElSkeleton, ElButton } from "element-plus";
1 month ago
import { Hide, Lock, Edit, Delete, Finished } from "@element-plus/icons-vue";
import { useRoute } from 'vue-router';
const route = useRoute();
1 month ago
const { message, status, send } = useWebSocket();
1 month ago
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([]);
// 加载状态
1 month ago
const loading = ref(true);
1 month ago
// 得到的原始图层数据
const sourceData = ref([]);
// 响应式数据
const treeRef = ref(null);
// 选中的节点 ID 数组
const selectedKeys = ref([]);
// 树形配置(节点唯一标识、标签、子节点字段)
const defaultProps = {
children: "children",
label: "label",
};
1 month ago
watchEffect(() => {
1 month ago
try {
1 month ago
if (!message.value) return; // 如果没有新消息,直接返回
const json = JSON.parse(message.value);
// console.info(json);
1 month ago
const evtType = json.type;
1 month ago
// if (evtType != "Pong") {
// emitter.emit("evtType", { type: evtType, data: message.value });
// }
// }
1 month ago
switch (evtType) {
1 month ago
case "ReloadLayer":
1 month ago
let layerData = json.data;
showLayers(layerData);
1 month ago
loading.value = false;
console.log("加载:", loading.value);
1 month ago
break;
}
} finally {
}
1 month ago
});
1 month ago
// 组装树的数据
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";
}
1 month ago
send("SetLayersStatus", { layerData: statusData });
1 month ago
// 处理图层状态设置逻辑
1 month ago
console.log("设置图层状态为:", status);
1 month ago
};
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);
}
1 month ago
send("DeleteLayers", { layers: statusData, widthChild: 1 });
1 month ago
};
// 删除指定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(() => {
1 month ago
// 页面加载完成后发送获取图层命令
// 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);
}
1 month ago
});
const getNodeIcon = (status) => {
switch (status) {
case 1:
return Edit;
case 2:
return Lock;
case 3:
return Hide;
default:
return Edit; // 默认图标
}
};
// 递归处理节点和子节点
const processNode = (node) => {
1 month ago
let nodeIcon = getNodeIcon(node.Status);
1 month ago
const treeNode = {
1 month ago
id: node.Id.toString(),
1 month ago
pId: "Layer:", // node.ParentId.toString(),
1 month ago
label: node.Name || "",
1 month ago
open: false,
icon: nodeIcon,
1 month ago
status: node.Status || 0,
fullPath: node.FullPath || "",
1 month ago
children: [],
};
// 如果有子节点,递归处理
1 month ago
if (node.Children && node.Children.length > 0) {
treeNode.children = node.Children.map((child) => {
1 month ago
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) {
1 month ago
return;
1 month ago
}
let statusData = "";
for (let i = 0; i < nCount; i++) {
1 month ago
// statusData += (nodes[i].id + ",");
1 month ago
statusData += nodes[i].fullPath + ",";
1 month ago
// treeRef.value.removeNode(nodes[i]);
1 month ago
}
1 month ago
// console.log("格式化的数据:", statusData);
1 month ago
1 month ago
emitter.emit("selectedLayer", statusData);
// 得到数据之后清空选择
clearSelection();
};
1 month ago
</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%;
1 month ago
height: calc(100vh - 75px);
1 month ago
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 {
1 month ago
height: 40px;
1 month ago
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;
1 month ago
padding: 0 !important; /* 去掉内边距 */
border-radius: 4px; /* 设置圆角为4px若要直角则设为0 */
1 month ago
display: inline-flex;
justify-content: center;
align-items: center;
}
.square-icon-button:hover {
1 month ago
color: #ec0a0a; /* 鼠标悬停时的背景色 */
1 month ago
}
.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);
}
1 month ago
.custom-tree .el-tree-node.is-current > .el-tree-node__content {
1 month ago
background-color: rgba(52, 152, 219, 0.15);
}
1 month ago
.custom-tree .el-tree-node.is-current > .el-tree-node__content .el-tree-node__label {
1 month ago
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>