2025-06-30 10:51:31 +00:00
|
|
|
|
<template>
|
2025-07-02 11:48:50 +00:00
|
|
|
|
<div class="app-container">
|
|
|
|
|
|
<!-- 查询 -->
|
|
|
|
|
|
<SearchForm slot="pageHeader" :model="queryParams" ref="queryForm" size="mini" :maxShow="30" label-width="68px"
|
|
|
|
|
|
v-show="showSearch" @search="handleQuery" @reset="resetQuery">
|
|
|
|
|
|
<!-- 线路查询 -->
|
|
|
|
|
|
<el-form-item label="线路" prop="lineCode">
|
2025-07-10 10:05:32 +00:00
|
|
|
|
<TransLineAllPicker v-model="queryParams.lineCode" isInitiated="true"></TransLineAllPicker>
|
2025-07-02 11:48:50 +00:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="产品类型" prop="productType">
|
2025-07-10 10:05:32 +00:00
|
|
|
|
<TransProductAllPicker placeholder="产品类型" v-model="queryParams.productType"
|
|
|
|
|
|
:transLineCode="queryParams.lineCode" @onChange="onProductChange"></TransProductAllPicker>
|
2025-07-02 11:48:50 +00:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="始发网点" prop="startSiteCode">
|
|
|
|
|
|
<EmisSiteSimplePicker placeholder="始发网点" v-model="queryParams.startSiteCode"></EmisSiteSimplePicker>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</SearchForm>
|
2025-07-10 10:05:32 +00:00
|
|
|
|
<div slot="toolbar">
|
|
|
|
|
|
<el-row :gutter="10" class="mb8">
|
|
|
|
|
|
<el-col :span="1.5">
|
|
|
|
|
|
<el-button type="warning" plain icon="el-icon-download" size="mini"
|
|
|
|
|
|
@click="exportImage">导 出</el-button>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div id="container">
|
|
|
|
|
|
<!-- mini 地图 -->
|
|
|
|
|
|
<div class="minimap-container" id="minimap"></div>
|
|
|
|
|
|
</div>
|
2025-07-02 11:48:50 +00:00
|
|
|
|
</div>
|
2025-06-30 10:51:31 +00:00
|
|
|
|
</template>
|
2025-07-02 11:48:50 +00:00
|
|
|
|
|
2025-06-30 10:51:31 +00:00
|
|
|
|
<script>
|
|
|
|
|
|
import G6 from "@antv/g6";
|
2025-07-10 10:05:32 +00:00
|
|
|
|
import TransLineAllPicker from '@/views/emis/EmisBaseTools/TransLineAllPicker.vue';
|
|
|
|
|
|
import TransProductAllPicker from '@/views/emis/EmisBaseTools/TransProductAllPicker.vue';
|
|
|
|
|
|
|
2025-07-02 11:48:50 +00:00
|
|
|
|
import EmisSiteSimplePicker from '@/views/emis/EmisBaseTools/EmisSiteSimplePicker.vue';
|
|
|
|
|
|
|
2025-06-30 10:51:31 +00:00
|
|
|
|
import { emisTransPlanRuleTree } from "@/api/emis/emisTransPlanRule";
|
|
|
|
|
|
export default {
|
2025-07-02 11:48:50 +00:00
|
|
|
|
name: "EmisTransPlanRuleTopoGraph",
|
|
|
|
|
|
components: {
|
2025-07-10 10:05:32 +00:00
|
|
|
|
TransLineAllPicker,
|
|
|
|
|
|
TransProductAllPicker,
|
2025-07-02 11:48:50 +00:00
|
|
|
|
EmisSiteSimplePicker
|
|
|
|
|
|
},
|
|
|
|
|
|
data() {
|
2025-06-30 10:51:31 +00:00
|
|
|
|
return {
|
2025-07-02 11:48:50 +00:00
|
|
|
|
treeData: {},
|
|
|
|
|
|
graph: null,
|
|
|
|
|
|
queryParams: {
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
pageSize: 50,
|
|
|
|
|
|
lineCode: "",
|
|
|
|
|
|
productType: "",
|
|
|
|
|
|
productName: '',
|
|
|
|
|
|
startSiteCode: ''
|
|
|
|
|
|
},
|
|
|
|
|
|
// 显示搜索条件
|
|
|
|
|
|
showSearch: true,
|
|
|
|
|
|
//节点操作菜单
|
|
|
|
|
|
descriptionDiv: null,
|
2025-06-30 10:51:31 +00:00
|
|
|
|
};
|
|
|
|
|
|
},
|
2025-07-02 11:48:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mounted() {
|
|
|
|
|
|
// 注册自定义缩放行为
|
|
|
|
|
|
G6.registerBehavior("fixed-zoom-canvas", {
|
|
|
|
|
|
getEvents() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
wheel: "onWheel",
|
|
|
|
|
|
mousewheel: "onWheel",
|
|
|
|
|
|
touchmove: "onTouchMove",
|
|
|
|
|
|
touchstart: "onTouchStart",
|
|
|
|
|
|
touchend: "onTouchEnd",
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 passive 为 false
|
|
|
|
|
|
getDefaultCfg() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
direction: "both",
|
|
|
|
|
|
sensitivity: 1,
|
|
|
|
|
|
minZoom: 0.2,
|
|
|
|
|
|
maxZoom: 10,
|
|
|
|
|
|
// 关键配置:禁用被动事件监听器
|
|
|
|
|
|
wheelEvent: { passive: false },
|
|
|
|
|
|
touchEvent: { passive: false },
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 处理滚轮事件
|
|
|
|
|
|
onWheel(e) {
|
|
|
|
|
|
// 阻止默认行为
|
|
|
|
|
|
// e.preventDefault();
|
|
|
|
|
|
|
|
|
|
|
|
// 调用原始缩放逻辑
|
|
|
|
|
|
const self = this;
|
|
|
|
|
|
const graph = self.graph;
|
|
|
|
|
|
const point = { x: e.clientX, y: e.clientY };
|
|
|
|
|
|
const canvasPoint = graph.getPointByClient(point.x, point.y);
|
|
|
|
|
|
|
|
|
|
|
|
// 缩放逻辑...
|
|
|
|
|
|
const zoom = e.deltaY > 0 ? 0.9 : 1.1;
|
|
|
|
|
|
graph.zoom(zoom, { x: canvasPoint.x, y: canvasPoint.y });
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 处理触摸事件
|
|
|
|
|
|
onTouchMove(e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
// 触摸移动逻辑...
|
|
|
|
|
|
},
|
2025-06-30 10:51:31 +00:00
|
|
|
|
});
|
2025-07-02 11:48:50 +00:00
|
|
|
|
|
|
|
|
|
|
// this.splitChild(this.treeData);
|
|
|
|
|
|
this.drawTreeGraph();
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-07-08 02:07:23 +00:00
|
|
|
|
},
|
|
|
|
|
|
beforeRouteEnter(to, from, next) {
|
|
|
|
|
|
// 组件创建前 保存当前背景颜色
|
|
|
|
|
|
document.documentElement.style.overflow = 'hidden';
|
|
|
|
|
|
next();
|
|
|
|
|
|
},
|
|
|
|
|
|
beforeRouteLeave(to, from, next) {
|
|
|
|
|
|
// 组件销毁前 恢复样式
|
|
|
|
|
|
document.documentElement.style.overflow = '';
|
|
|
|
|
|
next();
|
2025-06-30 10:51:31 +00:00
|
|
|
|
},
|
2025-07-02 11:48:50 +00:00
|
|
|
|
methods: {
|
|
|
|
|
|
/** 搜索按钮操作 */
|
|
|
|
|
|
handleQuery() {
|
|
|
|
|
|
this.getTreeData();
|
|
|
|
|
|
},
|
|
|
|
|
|
/** 重置按钮操作 */
|
|
|
|
|
|
resetQuery() {
|
|
|
|
|
|
this.queryParams = {
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
pageSize: 50,
|
|
|
|
|
|
lineCode: "",
|
|
|
|
|
|
productType: "",
|
|
|
|
|
|
productName: ''
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-07-10 10:05:32 +00:00
|
|
|
|
onProductChange(item) {
|
|
|
|
|
|
if (item) {
|
|
|
|
|
|
this.queryParams.productName = item.prodName;
|
|
|
|
|
|
}
|
2025-07-02 11:48:50 +00:00
|
|
|
|
},
|
|
|
|
|
|
drawTreeGraph() {
|
2025-07-10 10:05:32 +00:00
|
|
|
|
// 节点操作菜单
|
|
|
|
|
|
const contextMenu = new G6.Menu({
|
|
|
|
|
|
getContent(evt) {
|
|
|
|
|
|
return `
|
|
|
|
|
|
<h3>操作节点</h3>
|
|
|
|
|
|
<button class='edit-btn'>编辑</button>
|
|
|
|
|
|
<button class='del-btn'>删除</button>
|
|
|
|
|
|
`;
|
|
|
|
|
|
},
|
|
|
|
|
|
handleMenuClick: (target, item) => {
|
|
|
|
|
|
console.log(target, item);
|
|
|
|
|
|
},
|
|
|
|
|
|
// offsetX and offsetY include the padding of the parent container
|
|
|
|
|
|
// 需要加上父级容器的 padding-left 16 与自身偏移量 10
|
|
|
|
|
|
offsetX: 16 + 10,
|
|
|
|
|
|
// 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10
|
|
|
|
|
|
offsetY: 0,
|
|
|
|
|
|
// the types of items that allow the menu show up
|
|
|
|
|
|
// 在哪些类型的元素上响应
|
|
|
|
|
|
itemTypes: ['node', 'edge', 'canvas'],
|
2025-06-30 10:51:31 +00:00
|
|
|
|
});
|
2025-07-02 11:48:50 +00:00
|
|
|
|
|
2025-07-10 10:05:32 +00:00
|
|
|
|
const minimap = new G6.Minimap({
|
|
|
|
|
|
container:'minimap',
|
|
|
|
|
|
size: [190, 190],
|
|
|
|
|
|
type: 'delegate',
|
|
|
|
|
|
delegateStyle: {
|
|
|
|
|
|
// 代理元素样式配置
|
|
|
|
|
|
fill: '#b12d29', // 填充色(半透明白色)
|
|
|
|
|
|
// stroke: 'black', // 描边色(蓝色)
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-07-02 11:48:50 +00:00
|
|
|
|
this.graph = new G6.TreeGraph({
|
|
|
|
|
|
container,
|
2025-07-10 10:05:32 +00:00
|
|
|
|
width: document.getElementById('container').clientWidth, //画布宽度等于浏览器宽度
|
|
|
|
|
|
height: document.getElementById('container').clientHeight, //画布高度等于浏览器高度
|
2025-07-02 11:55:07 +00:00
|
|
|
|
fitView: false,
|
2025-07-02 11:48:50 +00:00
|
|
|
|
// fitViewPadding: [10, 50, 10, 50],
|
|
|
|
|
|
animate: true,
|
|
|
|
|
|
// plugins: [this.treeTooltip(), this.contextMenu],
|
2025-07-10 10:05:32 +00:00
|
|
|
|
plugins: [this.treeTooltip(), minimap],
|
2025-07-02 11:48:50 +00:00
|
|
|
|
defaultNode: {
|
|
|
|
|
|
type: "rect",
|
|
|
|
|
|
size: [250, 80],
|
2025-07-10 10:05:32 +00:00
|
|
|
|
collapsed: false, // false 展开 true 收起
|
2025-07-02 11:48:50 +00:00
|
|
|
|
style: {
|
|
|
|
|
|
// fill: "#fff",
|
|
|
|
|
|
// lineWidth: 2,
|
|
|
|
|
|
// cursor: "pointer",
|
|
|
|
|
|
},
|
|
|
|
|
|
// labelCfg: {
|
|
|
|
|
|
// position: "right",
|
|
|
|
|
|
// offset: 10,
|
|
|
|
|
|
// style: {
|
|
|
|
|
|
// fill: "#333",
|
|
|
|
|
|
// fontSize: 20,
|
|
|
|
|
|
// stroke: "#fff",
|
|
|
|
|
|
// background: {
|
|
|
|
|
|
// fill: "#ffffff",
|
|
|
|
|
|
// padding: [2, 2, 2, 2],
|
|
|
|
|
|
// },
|
|
|
|
|
|
// },
|
|
|
|
|
|
// },
|
|
|
|
|
|
anchorPoints: [
|
|
|
|
|
|
[0, 0.5],
|
|
|
|
|
|
[1, 0.5],
|
|
|
|
|
|
],
|
|
|
|
|
|
icon: {
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
width: 25,
|
|
|
|
|
|
height: 25,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
defaultEdge: {
|
|
|
|
|
|
// type: "cubic-horizontal",
|
|
|
|
|
|
type: "polyline",
|
|
|
|
|
|
style: {
|
|
|
|
|
|
endArrow: true
|
|
|
|
|
|
},
|
|
|
|
|
|
labelCfg: {
|
|
|
|
|
|
position: "end",
|
|
|
|
|
|
refX: -15,
|
|
|
|
|
|
style: {
|
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
|
background: {
|
|
|
|
|
|
fill: "#ffffff",
|
|
|
|
|
|
padding: [2, 2, 2, 2],
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2025-07-08 02:07:23 +00:00
|
|
|
|
nodeStateStyles: {
|
|
|
|
|
|
closest: {
|
|
|
|
|
|
fill: '#f00',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2025-07-02 11:48:50 +00:00
|
|
|
|
modes: {
|
|
|
|
|
|
default: [
|
|
|
|
|
|
{
|
|
|
|
|
|
type: "collapse-expand",
|
|
|
|
|
|
onChange: function onChange(item, collapsed) {
|
|
|
|
|
|
const data = item?.get("model");
|
|
|
|
|
|
data.collapsed = collapsed;
|
|
|
|
|
|
const model = {
|
|
|
|
|
|
id: data.id,
|
|
|
|
|
|
// labelCfg: { position: !collapsed ? "top" : "right" },
|
|
|
|
|
|
};
|
|
|
|
|
|
item.update(model);
|
|
|
|
|
|
item.refresh();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
"drag-canvas",
|
|
|
|
|
|
"fixed-zoom-canvas",
|
2025-07-08 02:07:23 +00:00
|
|
|
|
"drag-node",
|
|
|
|
|
|
// {
|
|
|
|
|
|
// type: 'drag-node',
|
|
|
|
|
|
// enableDelegate: true,
|
|
|
|
|
|
// }
|
2025-07-02 11:48:50 +00:00
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
layout: {
|
|
|
|
|
|
type: "compactBox",
|
|
|
|
|
|
direction: "LR",
|
|
|
|
|
|
getHeight: function getHeight() {
|
|
|
|
|
|
return 30;
|
|
|
|
|
|
},
|
|
|
|
|
|
getWidth: function getWidth() {
|
|
|
|
|
|
return 16;
|
|
|
|
|
|
},
|
|
|
|
|
|
getVGap: function getVGap() {
|
|
|
|
|
|
return 30;
|
|
|
|
|
|
},
|
|
|
|
|
|
getHGap: function getHGap() {
|
|
|
|
|
|
return 150; // 节点水平间距
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
//选中节点
|
|
|
|
|
|
nodeStateStyles: {
|
|
|
|
|
|
active: {
|
|
|
|
|
|
// fill: "l(0) 0:#FF4500 1:#32CD32",
|
|
|
|
|
|
// stroke: "l(0) 0:#FF4500 1:#32CD32",
|
|
|
|
|
|
// fill: "#B12D29",
|
|
|
|
|
|
// stroke:"#B12D29",
|
|
|
|
|
|
// lineWidth: 5,
|
|
|
|
|
|
},
|
|
|
|
|
|
selected: {
|
|
|
|
|
|
// fill: "l(0) 0:#FF4500 1:#32CD32",
|
|
|
|
|
|
// fill: "#B12D29",
|
|
|
|
|
|
stroke: "#B12D29",
|
|
|
|
|
|
lineWidth: 3,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
this.graph.node((node) => {
|
|
|
|
|
|
if (!node.id.includes("expand")) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
label: node.label || this.formatLabel(node),
|
|
|
|
|
|
icon: this.formatIcon(node),
|
|
|
|
|
|
size: node.size || 40,
|
|
|
|
|
|
// labelCfg: { position: this.setLabelPos(node) },
|
|
|
|
|
|
style: {
|
|
|
|
|
|
// fill: this.getNodeColor(),
|
|
|
|
|
|
stroke: '#B12D29',
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return {
|
|
|
|
|
|
// label: node.label || this.formatLabel(node),
|
|
|
|
|
|
label: '更多...',
|
|
|
|
|
|
icon: this.formatIcon(node),
|
|
|
|
|
|
size: 50,
|
|
|
|
|
|
type: 'circle',
|
|
|
|
|
|
// labelCfg: { position: this.setLabelPos(node) },
|
|
|
|
|
|
labelCfg: { position: 'right' },
|
|
|
|
|
|
style: {
|
|
|
|
|
|
// fill: this.getNodeColor(),
|
|
|
|
|
|
fill: '#B12D29',
|
|
|
|
|
|
stroke: '#B12D29',
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
let selfGrowthNum = 0;
|
|
|
|
|
|
this.graph.edge((edge) => {
|
|
|
|
|
|
// let {source, target } = edge // 也可以根据 link的属性不同自定义 连线颜色和label颜色,因为是测试数据,因此就用一个自增长的数判断奇偶性来进行区分,以便明白其中定制化的方法
|
|
|
|
|
|
selfGrowthNum++;
|
|
|
|
|
|
return {
|
|
|
|
|
|
style: {
|
|
|
|
|
|
opacity: 0.5,
|
|
|
|
|
|
// stroke: selfGrowthNum % 2 ? "#ADD8E6" : "#FFDEAD",
|
|
|
|
|
|
stroke: '#B12D29',
|
|
|
|
|
|
lineWidth: 2,
|
|
|
|
|
|
},
|
|
|
|
|
|
labelCfg: {
|
|
|
|
|
|
position: "end",
|
|
|
|
|
|
style: {
|
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
|
fill: selfGrowthNum % 2 ? "#ADD8E6" : "#FFDEAD",
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
// label: selfGrowthNum % 2 ? "even" : "odd", //边标签
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
this.graph.on("node:mouseenter", (evt) => {
|
|
|
|
|
|
const { item } = evt;
|
|
|
|
|
|
// this.graph.setItemState(item, "active", true);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
this.graph.on("node:mouseleave", (evt) => {
|
|
|
|
|
|
const { item } = evt;
|
|
|
|
|
|
// this.graph.setItemState(item, "active", false);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const animateCfg = { duration: 200, easing: "easeCubic" };
|
|
|
|
|
|
this.graph.on("node:click", (evt) => {
|
|
|
|
|
|
const { item } = evt;
|
|
|
|
|
|
const node = item?.get("model");
|
|
|
|
|
|
//点击更多 显示锁起来的下一层节点
|
|
|
|
|
|
if (node.id.includes("expand")) {
|
|
|
|
|
|
const parentNode = this.graph.getNeighbors(item, "source")[0].get("model");
|
|
|
|
|
|
this.graph.updateChildren(parentNode.childrenBak, parentNode.id);
|
|
|
|
|
|
}
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
if (!node.id.includes("expand")) {
|
|
|
|
|
|
this.graph.focusItem(item, true, animateCfg);
|
|
|
|
|
|
this.graph.getNodes().forEach((node) => {
|
|
|
|
|
|
this.graph.clearItemStates(node);
|
|
|
|
|
|
});
|
|
|
|
|
|
this.graph.setItemState(item, "selected", true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 500);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
this.graph.on("canvas:click", () => {
|
|
|
|
|
|
this.graph.getNodes().forEach((node) => {
|
|
|
|
|
|
this.graph.clearItemStates(node);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-07-08 02:07:23 +00:00
|
|
|
|
let minDisNode;
|
|
|
|
|
|
this.graph.on('node:dragstart', (e) => {
|
|
|
|
|
|
console.log('dragstart')
|
|
|
|
|
|
minDisNode = undefined;
|
|
|
|
|
|
});
|
|
|
|
|
|
this.graph.on('node:drag', (e) => {
|
|
|
|
|
|
console.log('node:drag')
|
|
|
|
|
|
minDisNode = undefined;
|
|
|
|
|
|
const item = e.item;
|
|
|
|
|
|
const model = item.getModel();
|
|
|
|
|
|
const nodes = this.graph.getNodes();
|
|
|
|
|
|
let minDis = Infinity;
|
|
|
|
|
|
nodes.forEach((inode) => {
|
|
|
|
|
|
this.graph.setItemState(inode, 'closest', false);
|
|
|
|
|
|
const node = inode.getModel();
|
|
|
|
|
|
if (node.id === model.id) return;
|
|
|
|
|
|
const dis = (node.x - e.x) * (node.x - e.x) + (node.y - e.y) * (node.y - e.y);
|
|
|
|
|
|
if (dis < minDis) {
|
|
|
|
|
|
minDis = dis;
|
|
|
|
|
|
minDisNode = inode;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
// console.log('minDisNode1', minDisNode);
|
|
|
|
|
|
// if (minDis < 2000)
|
|
|
|
|
|
this.graph.setItemState(minDisNode, 'closest', true);
|
|
|
|
|
|
// else minDisNode = undefined;
|
|
|
|
|
|
// console.log('minDisNode2',minDisNode)
|
|
|
|
|
|
});
|
2025-07-10 10:05:32 +00:00
|
|
|
|
// 节点拖动
|
|
|
|
|
|
// this.graph.on('node:dragend', (e) => {
|
|
|
|
|
|
// // console.log('dragend')
|
|
|
|
|
|
// const descriptionDiv = document.createElement('div');
|
|
|
|
|
|
// descriptionDiv.innerHTML =
|
|
|
|
|
|
// 'Move a subtree to a new parent by dragging the root node of the subtree.';
|
|
|
|
|
|
// container.appendChild(descriptionDiv);
|
|
|
|
|
|
// // console.log('minDisNode', minDisNode)
|
|
|
|
|
|
// if (!minDisNode) {
|
|
|
|
|
|
// descriptionDiv.innerHTML = 'Failed. No node close to the dragged node.';
|
|
|
|
|
|
// return;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// const item = e.item;
|
|
|
|
|
|
// const id = item.getID();
|
|
|
|
|
|
// const data = this.graph.findDataById(id);
|
|
|
|
|
|
// // if the minDisNode is a descent of the dragged node, return
|
|
|
|
|
|
// let isDescent = false;
|
|
|
|
|
|
// const minDisNodeId = minDisNode.getID();
|
|
|
|
|
|
// // console.log('dragend', minDisNodeId, isDescent, data, id);
|
|
|
|
|
|
// console.log('目的节点==》',this.graph.findDataById(minDisNodeId)) //目的节点
|
|
|
|
|
|
// console.log('拖动节点==》', this.graph.findDataById(id)) //拖动节点
|
|
|
|
|
|
// G6.Util.traverseTree(data, (d) => {
|
|
|
|
|
|
// if (d.id === minDisNodeId) isDescent = true;
|
|
|
|
|
|
// });
|
|
|
|
|
|
// if (isDescent) {
|
|
|
|
|
|
// descriptionDiv.innerHTML = 'Failed. The target node is a descendant of the dragged node.';
|
|
|
|
|
|
// return;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// this.graph.removeChild(id);
|
|
|
|
|
|
|
|
|
|
|
|
// setTimeout(() => {
|
|
|
|
|
|
// const newParentData = this.graph.findDataById(minDisNodeId);
|
|
|
|
|
|
// let newChildren = newParentData.children;
|
|
|
|
|
|
// if (newChildren) newChildren.push(data);
|
|
|
|
|
|
// else newChildren = [data];
|
|
|
|
|
|
// this.graph.updateChildren(newChildren, minDisNodeId);
|
|
|
|
|
|
// descriptionDiv.innerHTML = 'Success.';
|
|
|
|
|
|
// }, 600);
|
|
|
|
|
|
// });
|
2025-07-02 11:48:50 +00:00
|
|
|
|
// this.graph.data(this.treeData);
|
|
|
|
|
|
// this.graph.render();
|
|
|
|
|
|
this.graph.zoom(0.9);
|
|
|
|
|
|
this.graph.fitCenter();
|
|
|
|
|
|
this.graph.get("canvas").set("localRefresh", false);
|
|
|
|
|
|
},
|
|
|
|
|
|
// label 过长截断 显示...
|
|
|
|
|
|
formatLabel(node) {
|
|
|
|
|
|
const hasChildren = node.childrenBak?.length || node.children?.length;
|
|
|
|
|
|
// const ellipsis = node.id.length > 15 ? "..." : "";
|
|
|
|
|
|
return `${this.labelStr(node)}`;
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 叶子节点 图标处理 截取ID 前两个字符串
|
|
|
|
|
|
formatIcon(node) {
|
|
|
|
|
|
if (node.id) {
|
|
|
|
|
|
node.icon = {
|
|
|
|
|
|
// text: node.id.slice(0, 2),
|
|
|
|
|
|
text: '+',
|
|
|
|
|
|
fill: "#fff",
|
|
|
|
|
|
stroke: "#fff",
|
|
|
|
|
|
textBaseline: "middle",
|
|
|
|
|
|
fontSize: 20,
|
|
|
|
|
|
width: 25,
|
|
|
|
|
|
height: 25,
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 叶子节点 背景颜色随机填充
|
|
|
|
|
|
getNodeColor() {
|
|
|
|
|
|
const colors = ["#8470FF", "#A020F0", "#C0FF3E", "#FF4500", "#66d6d1"];
|
|
|
|
|
|
return colors[Math.floor(Math.random() * colors.length)];
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 根据节点展开收起状态 动态变更 label 显示位置,展开时在上,收起时在右
|
|
|
|
|
|
setLabelPos(node) {
|
|
|
|
|
|
return !node.collapsed && node.children?.length ? "top" : "right";
|
|
|
|
|
|
},
|
|
|
|
|
|
// label 显示... 时,显示提示 tip
|
|
|
|
|
|
treeTooltip() {
|
|
|
|
|
|
return new G6.Tooltip({
|
|
|
|
|
|
offsetX: 10,
|
|
|
|
|
|
offsetY: 20,
|
|
|
|
|
|
shouldBegin(e) {
|
|
|
|
|
|
// return e.item?.get("model")?.label?.includes("...");
|
|
|
|
|
|
return e.item._cfg.model.label;
|
|
|
|
|
|
},
|
|
|
|
|
|
getContent(e) {
|
2025-07-08 02:07:23 +00:00
|
|
|
|
if (e.item._cfg.model.startSiteName) {
|
|
|
|
|
|
return `<div class="tooltip">
|
|
|
|
|
|
<div>${e.item._cfg.model.startSiteName}->${e.item._cfg.model.nextSiteName}</div>
|
|
|
|
|
|
<div>${e.item._cfg.model.supplierName || ''}</div>
|
|
|
|
|
|
</div>`
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
2025-07-02 11:48:50 +00:00
|
|
|
|
},
|
|
|
|
|
|
itemTypes: ["node"],
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 叶子节点超过 5(xxx)条, 折叠叶子节点,显示展开更多
|
|
|
|
|
|
splitChild(node) {
|
|
|
|
|
|
node.childrenBak = node.children ? [...node.children] : [];
|
|
|
|
|
|
let result = [];
|
|
|
|
|
|
|
|
|
|
|
|
if (node.children) {
|
|
|
|
|
|
result = node.children.slice(0, 2);
|
|
|
|
|
|
if (node.children.length > 2) {
|
|
|
|
|
|
// result.push({ id: `expand-${node.id}`, label: " 展开更多..." });
|
|
|
|
|
|
result.push({ id: `expand-${node.id}`, label: "" });
|
|
|
|
|
|
}
|
|
|
|
|
|
node.children = result;
|
|
|
|
|
|
node.children.forEach((child) => {
|
|
|
|
|
|
this.splitChild(child);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
processTree(nodes) {
|
|
|
|
|
|
return nodes.map(node => {
|
|
|
|
|
|
// const currentPath = parentPath ? `${parentPath}/${node.name}` : node.name;
|
|
|
|
|
|
const newNode = { ...node, id: node.treeId };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (node.children) {
|
|
|
|
|
|
newNode.children = this.processTree(node.children);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return newNode;
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
//获取树形数据
|
|
|
|
|
|
async getTreeData() {
|
|
|
|
|
|
const loadingInstance = this.$loading({
|
|
|
|
|
|
text: "正在加载...",
|
|
|
|
|
|
});
|
|
|
|
|
|
this.list = [];
|
|
|
|
|
|
this.edges = [];
|
|
|
|
|
|
const res = await emisTransPlanRuleTree(this.queryParams); //测试数据
|
|
|
|
|
|
this.treeData = res.rows;
|
|
|
|
|
|
|
|
|
|
|
|
//获取产品下的网点
|
|
|
|
|
|
// let { list: newlist, edges: newEdges } = this.treeDataToNodelist(
|
|
|
|
|
|
// this.treeData,
|
|
|
|
|
|
// this.queryParams.productType,
|
|
|
|
|
|
// );
|
|
|
|
|
|
this.treeData = { "id": '1', "label": this.queryParams.productName, "x": 500, "y": 500, "children": this.processTree(this.treeData) }
|
|
|
|
|
|
// 折叠节点
|
|
|
|
|
|
this.splitChild(this.treeData);
|
|
|
|
|
|
this.graph.data(this.treeData);
|
|
|
|
|
|
this.graph.render();
|
2025-07-04 09:11:11 +00:00
|
|
|
|
this.graph.fitCenter();
|
2025-07-02 11:48:50 +00:00
|
|
|
|
// setTimeout(() => {
|
|
|
|
|
|
// this.list = this.list.concat(newlist);
|
|
|
|
|
|
// this.edges = this.edges.concat(newEdges);
|
|
|
|
|
|
// this.graph.data({
|
|
|
|
|
|
// nodes: this.list,
|
|
|
|
|
|
// edges: this.edges,
|
|
|
|
|
|
// });
|
|
|
|
|
|
// this.graph.render();
|
|
|
|
|
|
// }, 500);
|
|
|
|
|
|
loadingInstance.close();
|
|
|
|
|
|
},
|
|
|
|
|
|
//格式化标签
|
|
|
|
|
|
labelStr(node) {
|
|
|
|
|
|
let str;
|
|
|
|
|
|
const hasChildren = node.childrenBak?.length || node.children?.length || 0;
|
|
|
|
|
|
if (node.startSiteName) {
|
|
|
|
|
|
str = `${node.startSiteName}->${node.nextSiteName}\n${node.supplierName ? node.supplierName.replace(/(.{20})/g, '$1\n') : ''} \n (${hasChildren})`
|
|
|
|
|
|
} else {
|
|
|
|
|
|
str = node.label;
|
|
|
|
|
|
}
|
|
|
|
|
|
return str;
|
2025-07-10 10:05:32 +00:00
|
|
|
|
},
|
|
|
|
|
|
async exportImage() {
|
|
|
|
|
|
|
|
|
|
|
|
this.graph.toFullDataURL((res) => {
|
|
|
|
|
|
// 为了防止下载的图片影响到G6显示,这里作一个深度拷贝
|
|
|
|
|
|
const canvas = document.getElementById('container').childNodes[1].cloneNode(true);
|
|
|
|
|
|
console.log('canvas',document.getElementById('container').childNodes);
|
|
|
|
|
|
const img = new Image();
|
|
|
|
|
|
img.onload = function () {
|
|
|
|
|
|
canvas.width = img.width + 32;
|
|
|
|
|
|
canvas.height = img.height + 32;
|
|
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
|
|
ctx.fillStyle = '#fff';
|
|
|
|
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
|
ctx.drawImage(img, 16, 16);
|
|
|
|
|
|
// 下载
|
|
|
|
|
|
const oA = document.createElement('a');
|
|
|
|
|
|
oA.download = '组批次规则图';
|
|
|
|
|
|
oA.href = canvas.toDataURL('image/png');
|
|
|
|
|
|
document.body.appendChild(oA);
|
|
|
|
|
|
oA.click();
|
|
|
|
|
|
oA.remove(); // 下载之后把创建的元素删除
|
|
|
|
|
|
};
|
|
|
|
|
|
img.src = res;
|
|
|
|
|
|
}, 'image/png', {
|
|
|
|
|
|
imageConfig: {
|
|
|
|
|
|
padding: 16
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-06-30 10:51:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2025-07-02 11:48:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-06-30 10:51:31 +00:00
|
|
|
|
</script>
|
2025-07-02 11:48:50 +00:00
|
|
|
|
<style scoped>
|
2025-06-30 10:51:31 +00:00
|
|
|
|
#container {
|
2025-07-02 11:48:50 +00:00
|
|
|
|
touch-action: none;
|
|
|
|
|
|
/* 禁用默认触摸滚动 */
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: calc(100vh - 150px);
|
|
|
|
|
|
border: 1px solid #ccc;
|
2025-07-10 10:05:32 +00:00
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
.minimap-container {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
width: 200px;
|
|
|
|
|
|
height: 200px;
|
|
|
|
|
|
border: 1px solid #ccc;
|
2025-07-02 11:48:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#container>>>.g6-component-tooltip {
|
|
|
|
|
|
background: #333;
|
|
|
|
|
|
color: #fff;
|
2025-07-08 02:07:23 +00:00
|
|
|
|
padding: 10px;
|
2025-07-02 11:48:50 +00:00
|
|
|
|
}
|
2025-07-10 10:05:32 +00:00
|
|
|
|
.minimap-container >>>.g6-minimap-viewport {
|
|
|
|
|
|
outline: 2px solid black !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* canvas {
|
2025-07-02 11:48:50 +00:00
|
|
|
|
cursor: pointer !important;
|
2025-07-10 10:05:32 +00:00
|
|
|
|
} */
|
|
|
|
|
|
.minimap-container {
|
|
|
|
|
|
margin: 0;
|
2025-06-30 10:51:31 +00:00
|
|
|
|
}
|
2025-07-02 11:48:50 +00:00
|
|
|
|
</style>
|