Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
f8b5f7aa
提交
f8b5f7aa
authored
3月 31, 2026
作者:
yanpeng
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
关系图
上级
8e94a51d
显示空白字符变更
内嵌
并排
正在显示
3 个修改的文件
包含
374 行增加
和
34 行删除
+374
-34
index.vue
src/components/base/GraphChart/index.vue
+29
-20
RelationGraph.vue
...nction/components/deepMining/components/RelationGraph.vue
+175
-14
index.vue
...ontrol/v2.0SingleSanction/components/deepMining/index.vue
+170
-0
没有找到文件。
src/components/base/GraphChart/index.vue
浏览文件 @
f8b5f7aa
<
template
>
<div
class=
"graph-chart-wrapper"
id=
"graph"
>
</div>
<div
class=
"graph-chart-wrapper"
id=
"graph"
></div>
</
template
>
<
script
setup
>
import
{
onMounted
,
onBeforeUnmount
}
from
'vue'
;
import
setChart
from
'@/utils/setChart'
;
import
getGraphChart
from
'./graphChart'
;
import
{
onMounted
,
onBeforeUnmount
,
watch
}
from
"vue"
;
import
setChart
from
"@/utils/setChart"
;
import
getGraphChart
from
"./graphChart"
;
const
emits
=
defineEmits
([
"handleClickNode"
])
const
emits
=
defineEmits
([
"handleClickNode"
])
;
const
props
=
defineProps
({
nodes
:
{
type
:
Array
,
...
...
@@ -21,33 +19,44 @@ const props = defineProps({
},
layoutType
:
{
type
:
String
,
default
:
'force'
default
:
"force"
},
width
:
{
type
:
String
,
default
:
'force'
default
:
"force"
},
height
:
{
type
:
String
,
default
:
'force'
default
:
"force"
}
})
})
;
let
chart
=
null
let
chart
=
null
;
onMounted
(()
=>
{
const
graph
=
getGraphChart
(
props
.
nodes
,
props
.
links
,
props
.
layoutType
)
chart
=
setChart
(
graph
,
'graph'
)
chart
.
on
(
"click"
,
(
event
)
=>
{
emits
(
"handleClickNode"
,
event
)
})
})
const
graph
=
getGraphChart
(
props
.
nodes
,
props
.
links
,
props
.
layoutType
);
chart
=
setChart
(
graph
,
"graph"
);
chart
.
on
(
"click"
,
event
=>
{
emits
(
"handleClickNode"
,
event
);
});
});
onBeforeUnmount
(()
=>
{
chart
.
off
(
"click"
)
chart
.
dispose
()
})
chart
.
off
(
"click"
);
chart
.
dispose
();
})
;
watch
(
()
=>
[
props
.
nodes
,
props
.
links
],
()
=>
{
if
(
chart
)
{
const
graph
=
getGraphChart
(
props
.
nodes
,
props
.
links
,
props
.
layoutType
);
chart
.
setOption
(
graph
,
true
);
}
},
{
deep
:
true
}
);
</
script
>
<
style
lang=
"scss"
scoped
>
.graph-chart-wrapper
{
width
:
100%
;
...
...
src/views/exportControl/v2.0SingleSanction/components/deepMining/components/RelationGraph.vue
浏览文件 @
f8b5f7aa
...
...
@@ -107,22 +107,185 @@ const initGraph = (layoutType = 1) => {
});
};
// const initNormalGraph = (layoutType, width, height) => {
// const data = processGraphData(props.graphData);
// console.log("初始数据", props.graphData);
// if (!data.nodes || data.nodes.length === 0) return;
// const layout = {
// type: "none",
// center: [width / 2, height / 2],
// preventOverlap: true,
// nodeSpacing: 80,
// linkDistance: 250,
// nodeStrength: -800,
// edgeStrength: 0.1,
// collideStrength: 0.8,
// alphaDecay: 0.01,
// alphaMin: 0.001
// };
// graphInstance.value = new G6.Graph({
// container: containerRef.value,
// width,
// height,
// fitView: true,
// fitViewPadding: 100,
// fitCenter: true,
// animate: true,
// animateCfg: {
// duration: 300,
// easing: "easeLinear"
// },
// minZoom: 0.1,
// maxZoom: 10,
// modes: {
// default: [
// "drag-canvas",
// "zoom-canvas",
// "drag-node",
// {
// type: "activate-relations",
// trigger: "mouseenter",
// resetSelected: true
// }
// ]
// },
// layout,
// defaultNode: {
// type: "image",
// size: 40,
// clipCfg: {
// show: true,
// type: "circle",
// r: 20
// },
// labelCfg: {
// position: "bottom",
// offset: 10,
// style: {
// fill: "#333",
// fontSize: 11,
// fontFamily: "Microsoft YaHei",
// textAlign: "center",
// background: {
// fill: "rgba(255, 255, 255, 0.95)",
// padding: [4, 6, 4, 6],
// radius: 4
// }
// }
// }
// // 注意:节点边框样式在 processGraphData 中单独设置,不在这里设置
// },
// defaultEdge: {
// type: "quadratic",
// style: {
// stroke: "red",
// lineWidth: 3,
// opacity: 0.9,
// shadowColor: "rgba(231, 243, 255, 1)",
// shadowBlur: 4,
// endArrow: {
// path: "M 0,0 L 12,6 L 12,-6 Z",
// fill: "#5B8FF9"
// }
// },
// labelCfg: {
// autoRotate: true,
// style: {
// fill: "rgba(137, 193, 255, 1)",
// fontSize: 10,
// fontFamily: "Microsoft YaHei",
// background: {
// fill: "rgba(231, 243, 255, 1)",
// padding: [2, 4, 2, 4],
// radius: 5
// }
// }
// }
// },
// nodeStateStyles: {
// active: {
// shadowColor: "#1459BB",
// shadowBlur: 15,
// stroke: "#1459BB",
// lineWidth: 3
// },
// inactive: {
// opacity: 0.3
// }
// },
// edgeStateStyles: {
// active: {
// stroke: "#1459BB",
// lineWidth: 4
// },
// inactive: {
// opacity: 0.15
// }
// }
// });
// graphInstance.value.data(data);
// graphInstance.value.render();
// bindGraphEvents();
// };
const
initNormalGraph
=
(
layoutType
,
width
,
height
)
=>
{
const
data
=
processGraphData
(
props
.
graphData
);
console
.
log
(
"初始数据"
,
props
.
graphData
);
if
(
!
data
.
nodes
||
data
.
nodes
.
length
===
0
)
return
;
// 中心节点坐标
const
centerX
=
width
/
2
;
const
centerY
=
height
/
2
;
const
upperY
=
centerY
-
200
;
// 上方节点 Y 坐标
const
lowerY
=
centerY
+
200
;
// 下方节点 Y 坐标
const
nodeSpacing
=
100
;
// 节点水平间距
// 分离中心节点和其他节点
const
centerNode
=
data
.
nodes
.
find
(
n
=>
n
.
isCenter
);
const
otherNodes
=
data
.
nodes
.
filter
(
n
=>
!
n
.
isCenter
);
const
totalNodes
=
otherNodes
.
length
;
const
upperCount
=
Math
.
ceil
(
totalNodes
/
2
);
// 向上取整,上方多一个
const
lowerCount
=
totalNodes
-
upperCount
;
// 为上方节点分配坐标
const
upperNodes
=
otherNodes
.
slice
(
0
,
upperCount
);
upperNodes
.
forEach
((
node
,
index
)
=>
{
const
totalWidth
=
(
upperCount
-
1
)
*
nodeSpacing
;
const
startX
=
centerX
-
totalWidth
/
2
;
node
.
x
=
startX
+
index
*
nodeSpacing
;
node
.
y
=
upperY
;
// 固定位置,防止力导向布局移动
node
.
fx
=
node
.
x
;
node
.
fy
=
node
.
y
;
});
// 为下方节点分配坐标
const
lowerNodes
=
otherNodes
.
slice
(
upperCount
);
lowerNodes
.
forEach
((
node
,
index
)
=>
{
const
totalWidth
=
(
lowerCount
-
1
)
*
nodeSpacing
;
const
startX
=
centerX
-
totalWidth
/
2
;
node
.
x
=
startX
+
index
*
nodeSpacing
;
node
.
y
=
lowerY
;
// 固定位置,防止力导向布局移动
node
.
fx
=
node
.
x
;
node
.
fy
=
node
.
y
;
});
// 设置中心节点坐标
if
(
centerNode
)
{
centerNode
.
x
=
centerX
;
centerNode
.
y
=
centerY
;
// 固定中心节点位置
centerNode
.
fx
=
centerX
;
centerNode
.
fy
=
centerY
;
}
const
layout
=
{
type
:
"force"
,
center
:
[
width
/
2
,
height
/
2
],
preventOverlap
:
true
,
nodeSpacing
:
80
,
linkDistance
:
250
,
nodeStrength
:
-
800
,
edgeStrength
:
0.1
,
collideStrength
:
0.8
,
alphaDecay
:
0.01
,
alphaMin
:
0.001
type
:
"none"
,
// 使用预设坐标,不进行力导向布局
center
:
[
centerX
,
centerY
]
};
graphInstance
.
value
=
new
G6
.
Graph
({
...
...
@@ -175,10 +338,9 @@ const initNormalGraph = (layoutType, width, height) => {
}
}
}
// 注意:节点边框样式在 processGraphData 中单独设置,不在这里设置
},
defaultEdge
:
{
type
:
"
quadratic
"
,
type
:
"
line
"
,
style
:
{
stroke
:
"red"
,
lineWidth
:
3
,
...
...
@@ -230,7 +392,6 @@ const initNormalGraph = (layoutType, width, height) => {
graphInstance
.
value
.
render
();
bindGraphEvents
();
};
const
initCircularGraph
=
(
width
,
height
)
=>
{
const
data
=
processGraphData
(
props
.
graphData
);
...
...
@@ -307,7 +468,7 @@ const initCircularGraph = (width, height) => {
}
},
defaultEdge
:
{
type
:
"
quadratic
"
,
type
:
"
line
"
,
style
:
{
stroke
:
"#5B8FF9"
,
lineWidth
:
3
,
...
...
src/views/exportControl/v2.0SingleSanction/components/deepMining/index.vue
浏览文件 @
f8b5f7aa
...
...
@@ -110,6 +110,7 @@
@
node-click=
"handleNodeClick"
@
layout-change=
"handleLayoutChange"
/>
<!-- <GraphChart :nodes="nodes" :links="links" layoutType="none" /> -->
</div>
</div>
</div>
...
...
@@ -134,6 +135,7 @@ import {
}
from
"@/api/exportControlV2.0"
;
import
RelationGraph
from
"./components/RelationGraph.vue"
;
import
AnalysisBox
from
"@/components/base/boxBackground/analysisBox.vue"
;
import
GraphChart
from
"@/components/base/GraphChart/index.vue"
;
const
sanRecordId
=
ref
(
""
);
const
activeTab
=
ref
([
"实体穿透分析"
]);
...
...
@@ -157,6 +159,9 @@ const graphData = ref({ nodes: [], links: [] });
const
treeData
=
ref
(
null
);
const
selectedNode
=
ref
(
null
);
const
nodes
=
ref
([]);
const
links
=
ref
([]);
const
singleSanctionEntityEquityData
=
ref
(
null
);
const
singleSanctionEntitySupplyChainData
=
ref
(
null
);
const
singleSanctionEntityList
=
ref
([]);
...
...
@@ -204,6 +209,7 @@ const updateGraphData = () => {
const
data
=
rightActiveTab
.
value
===
"supplyChain"
?
singleSanctionEntitySupplyChainData
.
value
:
singleSanctionEntityEquityData
.
value
;
console
.
log
(
"图谱数据 =>"
,
data
);
if
(
!
data
)
return
;
const
nodes
=
[];
...
...
@@ -252,8 +258,172 @@ const updateGraphData = () => {
});
graphData
.
value
=
{
nodes
,
links
};
// nodes.value = nodes;
// links.value = links;
};
const
links1
=
[
{
source
:
1
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"合作"
}
},
{
source
:
2
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"持股"
}
},
{
source
:
3
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"合作"
}
},
{
source
:
4
,
target
:
7
,
lineStyle
:
{
type
:
"dashed"
,
color
:
"#d32f2f"
},
label
:
{
show
:
true
,
formatter
:
"从属"
}
},
{
source
:
5
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"合作"
}
},
{
source
:
6
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"持股"
}
},
{
source
:
0
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"持股"
}
},
{
source
:
8
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"合作"
}
},
{
source
:
9
,
target
:
7
,
lineStyle
:
{
type
:
"dashed"
,
color
:
"#d32f2f"
},
label
:
{
show
:
true
,
formatter
:
"从属"
}
},
{
source
:
10
,
target
:
7
,
lineStyle
:
{
type
:
"dashed"
,
color
:
"#d32f2f"
},
label
:
{
show
:
true
,
formatter
:
"合作"
}
},
{
source
:
11
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"合作"
}
},
{
source
:
12
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"合作"
}
},
{
source
:
13
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"合作"
}
},
{
source
:
14
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"合作"
}
},
{
source
:
15
,
target
:
7
,
label
:
{
show
:
true
,
formatter
:
"合作"
,
color
:
"red"
,
borderColor
:
"red"
}
}
];
// const updateGraphData = () => {
// const data =
// rightActiveTab.value === "supplyChain" ? singleSanctionEntitySupplyChainData.value : singleSanctionEntityEquityData.value;
// console.log("图谱数据 =>", data);
// if (!data) return;
// const newNodes = [];
// const newLinks = [];
// // 容器尺寸(根据 .right-echarts 的高度 calc(100% - 56px) ≈ 772px)
// const containerWidth = 1000;
// const containerHeight = 700;
// // 中心节点坐标(居中)
// const centerX = containerWidth / 2;
// const centerY = containerHeight / 2;
// // 上下节点分布参数
// const upperY = centerY - 200; // 上方节点 Y 坐标
// const lowerY = centerY + 200; // 下方节点 Y 坐标
// const nodeSpacing = 100; // 节点水平间距
// // 合并所有节点列表(上游 + 下游)
// const allItems = [];
// const parentList = data.parentOrgList || [];
// const childList = data.childrenOrgList || [];
// // 添加上游节点
// parentList.forEach((item, index) => {
// allItems.push({
// ...item,
// linkType: rightActiveTab.value === "supplyChain" ? "供应商" : item.type || "持股",
// direction: "source" // 链接方向:指向中心节点
// });
// });
// // 添加下游节点
// childList.forEach((item, index) => {
// allItems.push({
// ...item,
// linkType: rightActiveTab.value === "supplyChain" ? "客户" : item.description || "投资",
// direction: "target" // 链接方向:从中心节点指出
// });
// });
// // 中心节点
// newNodes.push({
// id: 0,
// name: data.orgName || "中心节点",
// symbol: `image://${companyActive}`,
// symbolSize: 60,
// value: 10,
// isSanctioned: true,
// x: centerX,
// y: centerY
// });
// // 计算上下分配
// const totalNodes = allItems.length;
// const upperCount = Math.ceil(totalNodes / 2); // 向上取整,上方多一个
// const lowerCount = totalNodes - upperCount;
// // 上方节点(前一半)
// const upperItems = allItems.slice(0, upperCount);
// upperItems.forEach((item, index) => {
// const totalWidth = (upperCount - 1) * nodeSpacing;
// const startX = centerX - totalWidth / 2;
// const x = startX + index * nodeSpacing;
// const y = upperY;
// const nodeId = `n-${index}`;
// newNodes.push({
// id: nodeId,
// name: item.name || `节点${index}`,
// symbol: `image://${item.isSanctioned ? companyActive : company}`,
// symbolSize: 40,
// value: 5,
// isSanctioned: item.isSanctioned,
// x,
// y
// });
// // 根据 direction 决定链接方向
// if (item.direction === "source") {
// newLinks.push({
// source: nodeId,
// target: 0,
// name: item.linkType,
// label: { show: true, formatter: item.description }
// });
// } else {
// newLinks.push({
// source: 0,
// target: nodeId,
// name: item.linkType,
// label: { show: true, formatter: item.description }
// });
// }
// });
// // 下方节点(后一半)
// const lowerItems = allItems.slice(upperCount);
// lowerItems.forEach((item, index) => {
// const totalWidth = (lowerCount - 1) * nodeSpacing;
// const startX = centerX - totalWidth / 2;
// const x = startX + index * nodeSpacing;
// const y = lowerY;
// const nodeId = `n-${upperCount + index}`;
// newNodes.push({
// id: nodeId,
// name: item.name || `节点${upperCount + index}`,
// symbol: `image://${item.isSanctioned ? companyActive : company}`,
// symbolSize: 40,
// value: 5,
// isSanctioned: item.isSanctioned,
// x,
// y
// });
// // 根据 direction 决定链接方向
// if (item.direction === "source") {
// newLinks.push({
// source: nodeId,
// target: 0,
// name: item.linkType,
// label: { show: true, formatter: "{c}" }
// });
// } else {
// newLinks.push({
// source: 0,
// target: nodeId,
// name: item.linkType,
// label: { show: true, formatter: "{c}" }
// });
// }
// });
// console.log("最终节点数:", newNodes, "最终链接数:", newLinks, "上方节点:", upperCount, "下方节点:", lowerCount);
// // 更新响应式数据
// nodes.value = newNodes;
// links.value = newLinks;
// };
const
updateTreeData
=
data
=>
{
if
(
!
data
)
return
;
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论