<template>
|
<div class="tree-container">
|
<div class="search-box">
|
<p>公司产业链图谱</p>
|
<textarea v-model="message" placeholder="请输入公司名称(必填)"></textarea>
|
<input type="text" v-model="mainproducts" placeholder="请输入主营产品(非必填)">
|
<input type="text" v-model="upstreamsuppliers" placeholder="请输入上游供应商(非必填)">
|
<input type="text" v-model="keyaccount" placeholder="请输入主要客户(非必填)">
|
<input type="text" v-model="downstreamindustry" placeholder="请输入下游行业(非必填)">
|
<input type="text" v-model="othercategories" placeholder="请输入其他信息(非必填)">
|
|
<button @click="generateChart" :disabled="disableButton"
|
:class="{ 'disabled-button': disableButton }">一键生成</button>
|
<div class="checkbox-container">
|
<label class="custom-checkbox">
|
<input type="checkbox" id="latest" v-model="getLatest" />
|
<span class="checkmark"></span>
|
获取最新
|
</label>
|
</div>
|
</div>
|
|
<div class="tree-box" v-show="!loadingSub">
|
<div class="ent-structure" ref="ent-structure">
|
<div class="ent-tree" ref="ent-tree"></div>
|
</div>
|
</div>
|
|
<div class="loading-box" v-if="loadingSub">
|
<div style="font-size: 32px;font-weight: 600;line-height: 32px;">生成中</div>
|
<Spin size="small" style="margin-top: 10px;margin-left: 20px" />
|
<Spin style="margin-top: 6px;margin-left: 20px" />
|
<Spin style="margin-left: 20px" size="large" />
|
</div>
|
|
<div class="no-data" v-if="!hasTreeData && !loadingSub">
|
<img src="../../assets/images/less.png" alt="">
|
<p>暂无数据</p>
|
</div>
|
|
</div>
|
</template>
|
|
<script>
|
import * as echarts from 'echarts'
|
import axios from '@/libs/api.request'
|
|
export default {
|
data() {
|
return {
|
loadingSub: false,
|
disableButton: false,
|
getLatest: false,
|
message: '',
|
mainproducts: '',
|
upstreamsuppliers: '',
|
keyaccount: '',
|
downstreamindustry: '',
|
othercategories: '',
|
treeData: {},
|
hasTreeData: false,
|
Echarts: null,
|
childLength: []
|
}
|
},
|
|
mounted() {
|
// this.formatData(this.treeData)
|
console.log(this.treeData.length);
|
|
this.$nextTick(() => {
|
this.initTree();
|
});
|
window.onresize = () => {
|
this.$nextTick(() => {
|
this.Echarts.resize()
|
})
|
}
|
},
|
|
methods: {
|
initTree() {
|
this.Echarts = echarts.init(this.$refs['ent-tree']);
|
// this.Echarts.setOption(this.getTreeOptions());
|
},
|
getTreeOptions() {
|
return {
|
tooltip: {
|
trigger: 'item',
|
triggerOn: 'mousemove'
|
},
|
series: [
|
{
|
type: 'tree',
|
data: [this.treeData],
|
nodePadding: 30,
|
layerPadding: 10,
|
name: '树图',
|
top: '1%',
|
left: '1%',
|
bottom: '1%',
|
right: '1%',
|
layout: 'orthogonal',
|
orient: 'horizontal',
|
zoom: 0.8,
|
roam: true,
|
scaleLimit: {
|
min: 0.2,
|
max: Infinity
|
},
|
initialTreeDepth: undefined,
|
symbol: 'circle',
|
symbolSize: 6,
|
itemStyle: {
|
color: '#0780ED',
|
borderCap: 'round'
|
},
|
label: {
|
show: true,
|
distance: 8,
|
position: ['50%', '50%'],
|
verticalAlign: 'middle',
|
align: 'center',
|
fontSize: 16,
|
color: '#333',
|
backgroundColor: '#F8F9FA',
|
borderColor: '#CED4DA',
|
borderWidth: 1,
|
borderType: 'solid',
|
borderRadius: 2,
|
padding: [6, 12],
|
shadowColor: 'rgba(0,121,221,0.3)',
|
shadowBlur: 6
|
},
|
labelLayout: {
|
dy: -20,
|
verticalAlign: 'top'
|
},
|
lineStyle: {
|
width: 1,
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
},
|
blur: {
|
itemStyle: {},
|
lineStyle: {},
|
label: {}
|
},
|
leaves: {
|
label: {
|
distance: 0,
|
position: 'bottom',
|
verticalAlign: 'middle'
|
},
|
itemStyle: {},
|
emphasis: {},
|
blur: {},
|
select: {}
|
},
|
animation: true,
|
expandAndCollapse: true,
|
animationDuration: 550,
|
animationEasing: 'linear',
|
animationDelay: 0,
|
animationDurationUpdate: 750,
|
animationEasingUpdate: 'cubicInOut',
|
animationDelayUpdate: 0
|
}
|
]
|
};
|
},
|
generateChart() {
|
if (!this.message) {
|
alert('请输入正确的公司名称!');
|
} else {
|
// 禁用按钮
|
this.disableButton = true;
|
console.log('获取最新', this.getLatest);
|
if (this.getLatest) {
|
//勾选获取最新按钮
|
this.getNewTreeData(this.message)
|
} else {
|
this.getTreeData(this.message)
|
}
|
}
|
},
|
getTreeData(name) {
|
this.loadingSub = true;
|
|
const params = {
|
CompanyName: name,
|
};
|
|
axios.request({
|
url: `/TreeDiagramRecord/GetTreeDiagramRecordIsName`,
|
method: 'POST',
|
data: params
|
}).then(response => {
|
console.log('Response:', response.data.Data);
|
if (response.data.Data == null) {
|
this.getNewTreeData(name)
|
} else {
|
const res = response.data.Data[0].Content
|
const jsonString1 = JSON.parse(JSON.stringify(res))
|
const jsonString2 = "[" + jsonString1.replace(/json/g, "").replace(/```/g, '').replace(/[\n\t\r]/g, "").replace(/\\/g, '').replace(/[ ]/g, "").replace(/^\"|\"$/g, '').replace(/,$/, '') + "]"
|
// const jsonString2 = "[" + jsonString1.replace('json', '').replace(/```/g, '').replace(/[\n\t\r]/g, "").replace(/\\/g, '').replace(/[ ]/g, "").replace(/^\"|\"$/g, '') + "]"
|
|
this.treeData = {
|
"name": name,
|
"relationType": 0,
|
"children": JSON.parse(jsonString2)
|
};
|
console.log('Response:', this.treeData);
|
this.formatData(this.treeData); // 重新渲染树图
|
this.loadingSub = false;
|
this.hasTreeData = true;
|
// alert('已生成:\n' + this.message + '公司产业链图谱');
|
// 重新启用按钮
|
this.disableButton = false;
|
}
|
}).catch(error => {
|
console.error('Error:', error);
|
this.loadingSub = false;
|
this.hasTreeData = true;
|
alert('提交时发生错误,请重试。');
|
// 重新启用按钮
|
this.disableButton = false;
|
});
|
},
|
addTreeData(name, content) {
|
this.loadingSub = true;
|
|
const params = {
|
CompanyName: name,
|
Content: content
|
};
|
|
axios.request({
|
url: `/TreeDiagramRecord/AddTreeDiagramRecord`,
|
method: 'POST',
|
data: params
|
}).then(response => {
|
console.log('Response:', response.data.Data);
|
|
}).catch(error => {
|
console.error('Error:', error);
|
alert('提交时发生错误,请重试。');
|
});
|
},
|
UpdataTreeData(name, content) {
|
this.loadingSub = true;
|
|
const params = {
|
CompanyName: name,
|
Content: content
|
};
|
|
axios.request({
|
url: `/TreeDiagramRecord/UpdateTreeDiagramRecord`,
|
method: 'POST',
|
data: params
|
}).then(response => {
|
console.log('Response:', response.data.Data);
|
|
}).catch(error => {
|
console.error('Error:', error);
|
alert('提交时发生错误,请重试。');
|
});
|
},
|
getNewTreeData(name) {
|
this.loadingSub = true;
|
const apiUrl = 'http://122.51.217.202/v1/workflows/run';
|
const headers = {
|
'Authorization': 'Bearer app-u8deQihzgArYUad4Tl7bV2xi',
|
'Content-Type': 'application/json'
|
};
|
const params = {
|
inputs: {
|
company_name: name,
|
mainproducts: this.mainproducts,
|
upstreamsuppliers: this.upstreamsuppliers,
|
keyaccount: this.keyaccount,
|
downstreamindustry: this.downstreamindustry,
|
othercategories: this.othercategories,
|
},
|
response_mode: "blocking",
|
user: "rensofter"
|
};
|
|
axios.request({
|
url: apiUrl,
|
method: 'POST',
|
headers: headers,
|
data: params
|
}).then(response => {
|
console.log('Response1111:', response.data.data.outputs.output);
|
const res = response.data.data.outputs.output
|
|
if (this.getLatest) {
|
//勾选获取最新按钮
|
this.UpdataTreeData(name, res)
|
} else {
|
this.addTreeData(name, res)
|
}
|
|
// const jsonStringRes = res.replace(/[\n\t\r]/g, "").replace(/\"/g,'').replace(/\\/g,'');
|
// const jsonStringRes = res.replace(/[\n\t\r]/g, "").replace(/^\"|\"$/g, '').replace(/\\/g, '').replace(/[ ]/g, "");
|
const jsonString1 = JSON.parse(JSON.stringify(res))
|
// const jsonString2 = "[" + jsonString1.replace(/[\n\t\r]/g, "").replace(/\\/g, '').replace(/[ ]/g, "").replace(/^\"|\"$/g, '') + "]"
|
const jsonString2 = "[" + jsonString1.replace(/json/g, "").replace(/```/g, '').replace(/[\n\t\r]/g, "").replace(/\\/g, '').replace(/[ ]/g, "").replace(/^\"|\"$/g, '').replace(/,$/, '') + "]"
|
console.log('jsonString2:', jsonString2);
|
|
this.treeData = {
|
"name": name,
|
"relationType": 0,
|
"children": JSON.parse(jsonString2)
|
}; // 更新 treeData
|
// this.treeData.children.push(jsonStringRes);
|
// const jsonString = JSON.stringify(this.treeData, null, 2);
|
// const jsonString = JSON.parse(JSON.stringify(res))
|
// const jsonString1 = JSON.parse(res)
|
|
console.log('Response2222:', this.treeData);
|
this.formatData(this.treeData); // 重新渲染树图
|
this.loadingSub = false;
|
this.hasTreeData = true;
|
// alert('已生成:\n' + this.message + '公司产业链图谱');
|
// 重新启用按钮
|
this.disableButton = false;
|
|
|
|
}).catch(error => {
|
console.error('Error:', error);
|
this.loadingSub = false;
|
this.hasTreeData = true;
|
alert('提交时发生错误,请重试。');
|
// 重新启用按钮
|
this.disableButton = false;
|
});
|
},
|
formatData(data) {
|
this.$nextTick(() => {
|
this.childLength = []; // 清空 childLength 数组
|
this.recursive(data)
|
this.setCanvasWidth()
|
this.renderTree(data)
|
})
|
},
|
|
// 递归-给每个子节点中添加 children
|
recursive(data) {
|
if (data.childList) {
|
this.childLength.push(data.childList.length)
|
data.children = data.childList
|
data.childList.forEach((item) => {
|
this.recursive(item)
|
})
|
} else {
|
return
|
}
|
},
|
|
// 根据树的childList设置容器宽度
|
setCanvasWidth() {
|
let container = document.getElementsByClassName('ent-structure')[0]
|
container.style.width = `100%`
|
},
|
|
// 绘制企业结构的echarts
|
renderTree(data) {
|
this.childLength = []
|
this.Echarts = echarts.init(this.$refs['ent-tree'])
|
|
|
// 定义自定义图形
|
// const customSymbol = {
|
// type: 'custom',
|
// shape: {
|
// x: 0,
|
// y: 0,
|
// width: 100,
|
// height: 2
|
// },
|
// style: {
|
// stroke: '#000',
|
// lineWidth: 2
|
// },
|
// render: function (params, api) {
|
// const x = api.value(0);
|
// const y = api.value(1);
|
// const width = api.size([1, 0])[0];
|
// const height = api.size([0, 1])[1];
|
|
// const points = [
|
// [x - width / 2, y],
|
// [x + width / 2, y]
|
// ];
|
|
// const line = echarts.graphic.clipPointsByRect(points, {
|
// x: params.coordSys.x,
|
// y: params.coordSys.y,
|
// width: params.coordSys.width,
|
// height: params.coordSys.height
|
// });
|
|
// if (line.length >= 2) {
|
// return {
|
// type: 'line',
|
// shape: {
|
// x1: line[0][0],
|
// y1: line[0][1],
|
// x2: line[1][0],
|
// y2: line[1][1]
|
// },
|
// style: api.style()
|
// };
|
// }
|
// }
|
// };
|
|
const options = {
|
tooltip: {
|
// 提示框浮层设置
|
trigger: 'item',
|
triggerOn: 'mousemove' // 提示框触发条件
|
},
|
series: [
|
{
|
type: 'tree',
|
data: [data],
|
nodePadding: 30, //结点间距 (发现没用)
|
layerPadding: 10, //连接线长度 (发现没用)
|
name: '树图',
|
top: '1%',
|
left: '7%',
|
bottom: '1%',
|
right: '20%',
|
symbolSize: 10,
|
label: {
|
position: 'top',
|
verticalAlign: 'middle',
|
align: 'center',
|
fontSize: 18, // 放大字体
|
fontWeight: 'bold', // 加粗字体
|
offset: [0, -5] // 向上偏移10个像素
|
},
|
leaves: {
|
label: {
|
position: 'top',
|
verticalAlign: 'middle',
|
align: 'center',
|
fontSize: 18, // 放大字体
|
fontWeight: 'bold',// 加粗字体
|
offset: [0, -5] // 向上偏移10个像素
|
}
|
},
|
lineStyle: {
|
width: 3, // 加粗线条
|
color: '#555' // 设置线条颜色
|
},
|
expandAndCollapse: true,
|
animationDuration: 550,
|
animationDurationUpdate: 750,
|
zoom: 0.8, //当前视角的缩放比例
|
roam: true, //是否开启平游或缩放 // 是否开启鼠标缩放或平移,默认false
|
scaleLimit: {
|
//滚轮缩放的极限控制
|
min: 0.2,
|
max: Infinity
|
},
|
initialTreeDepth: undefined, // 树图初始的展开层级(深度),根节点是0,不设置时全部展开
|
|
// animation: true, // 是否开启动画
|
// expandAndCollapse: true, // 子树折叠和展开的交互,默认打开
|
// animationDuration: 550, // 初始动画的时长
|
// animationEasing: 'linear', // 初始动画的缓动效果
|
// animationDelay: 0, // 初始动画的延迟
|
// animationDurationUpdate: 750, // 数据更新动画的时长
|
// animationEasingUpdate: 'cubicInOut', // 数据更新动画的缓动效果
|
// animationDelayUpdate: 0 // 数据更新动画的延迟
|
}
|
]
|
}
|
|
// const options =this.getTreeOptions()
|
/**
|
* 遍历数节点
|
*/
|
function nodesStutes(nodes) {
|
if (nodes.children && nodes.children.length) {
|
for (let i = 0; i < nodes.children.length; i++) {
|
if (nodes.children[i].relationType == 1 || nodes.children[i].relationType == '1') {
|
// 修改连线颜色
|
nodes.children[i].lineStyle = {
|
color: '#6DD400'
|
}
|
} else if (nodes.children[i].relationType == 2 || nodes.children[i].relationType == "2") {
|
nodes.children[i].lineStyle = {
|
color: '#0780ED'
|
}
|
}
|
else if (nodes.children[i].relationType == 3 || nodes.children[i].relationType == "3") {
|
nodes.children[i].lineStyle = {
|
color: '#ff7f00'
|
}
|
} else if (nodes.children[i].relationType == 4 || nodes.children[i].relationType == "4") {
|
nodes.children[i].lineStyle = {
|
color: '#ff0000'
|
}
|
} else if (nodes.children[i].relationType == 5 || nodes.children[i].relationType == "5") {
|
nodes.children[i].lineStyle = {
|
color: '#800080'
|
}
|
} else if (nodes.children[i].relationType == 6 || nodes.children[i].relationType == "6") {
|
nodes.children[i].lineStyle = {
|
color: '#4B0082'
|
}
|
}
|
else if (nodes.children[i].relationType == 7 || nodes.children[i].relationType == "7") {
|
nodes.children[i].lineStyle = {
|
color: '#0000FF'
|
}
|
} else {
|
nodes.children[i].lineStyle = {
|
color: '#bce6ff'
|
}
|
}
|
nodesStutes(nodes.children[i])
|
}
|
}
|
}
|
nodesStutes(data)
|
this.Echarts.setOption(options, true)
|
// 监听窗口大小变化,动态调整图表大小
|
window.addEventListener('resize', () => {
|
this.Echarts.resize();
|
});
|
|
// 监听点击事件
|
this.Echarts.off('click'); // 先解绑所有 click 事件
|
this.Echarts.on('click', (params) => {
|
if (!params.data.children || params.data.children.length === 0) {
|
const data = params.data;
|
// 如果点击的是叶子节点,调用新接口
|
console.log("===是根节点===", data);
|
if (data.typeId == 1) {
|
this.toNewPage(data)
|
} else if (data.typeId == 2 || data.typeId == 3) {
|
this.callNewApi(data);
|
}
|
}
|
});
|
},
|
toNewPage(data) {
|
window.open(data.url, '_blank')
|
},
|
callNewApi(data) {
|
const params = {
|
KeywordProduct: '',//产品
|
KeywordIndustry: '',//行业
|
PageNum: 1,
|
PageSize: 10
|
};
|
if (data.typeId == 2) {
|
params.KeywordProduct = data.name.replace('系统', "").replace('解决方案', "").slice(-2);
|
console.log(params.KeywordProduct);
|
} else if (data.typeId == 3) {
|
params.KeywordIndustry = data.name
|
}
|
|
axios.request({
|
url: `/CompanyInfo/GetCompanyInfoList`,
|
method: 'POST',
|
data: params
|
}).then(response => {
|
console.log('Response:', response.data.Data.Data);
|
const childList = response.data.Data.Data
|
|
childList.forEach((item) => {
|
item.name = item.CompanyName
|
})
|
|
// 更新树图数据
|
data.children = childList;
|
// console.log('Response:', childList, data, this.treeData);
|
// 递归查找并更新树图数据中的相应节点
|
this.updateTreeData(this.treeData, data.id, childList);
|
|
this.formatData(this.treeData); // 重新渲染树图
|
// alert('已添加新节点:\n' + data.name);
|
|
|
}).catch(error => {
|
console.error('Error:', error);
|
alert('提交时发生错误,请重试。');
|
});
|
},
|
updateTreeData(treeData, targetId, newChildren) {
|
if (treeData.id === targetId) {
|
treeData.children = newChildren;
|
return true;
|
}
|
|
if (treeData.children) {
|
for (let i = 0; i < treeData.children.length; i++) {
|
if (this.updateTreeData(treeData.children[i], targetId, newChildren)) {
|
return true;
|
}
|
}
|
}
|
|
return false;
|
}
|
|
}
|
}
|
</script>
|
|
<style lang="less" scoped>
|
.tree-container {
|
margin: 0;
|
padding: 0;
|
width: 100%;
|
height: 100%;
|
background-color: #f4f4f4;
|
display: flex;
|
// justify-content: center;
|
// align-items: center;
|
}
|
|
.search-box {
|
width: 500px;
|
padding: 100px;
|
|
p {
|
font-size: 30px;
|
margin-bottom: 20px;
|
}
|
|
textarea,
|
input[type="text"] {
|
width: 100%;
|
|
resize: none;
|
/* 禁止用户调整大小 */
|
padding: 10px;
|
border: 1px solid #ccc;
|
border-radius: 4px;
|
font-family: Arial, sans-serif;
|
font-size: 14px;
|
box-sizing: border-box;
|
margin-bottom: 10px;
|
}
|
|
textarea {
|
height: 6em;
|
/* 设置高度为三行 */
|
}
|
|
textarea:focus,
|
input[type="text"]:focus {
|
outline: none;
|
border-color: #2d8cf0;
|
box-shadow: 0 0 5px rgba(108, 99, 255, 0.5);
|
}
|
|
button {
|
width: 100%;
|
padding: 10px;
|
background-color: #2d8cf0;
|
color: #fff;
|
border: none;
|
border-radius: 4px;
|
font-size: 16px;
|
cursor: pointer;
|
transition: background-color 0.3s ease;
|
}
|
|
button:hover {
|
background-color: #2d8cf0;
|
}
|
|
button:focus {
|
outline: none;
|
box-shadow: 0 0 5px rgba(108, 99, 255, 0.5);
|
}
|
|
.disabled-button {
|
background-color: #ccc;
|
cursor: not-allowed;
|
}
|
|
.checkbox-container {
|
display: inline-block;
|
float: right;
|
margin-top: 15px;
|
|
/* 自定义复选框样式 */
|
.custom-checkbox {
|
display: inline-block;
|
position: relative;
|
padding-left: 30px;
|
/* 为复选框留出空间 */
|
cursor: pointer;
|
font-size: 14px;
|
color: #2d8cf0;
|
/* 文字颜色 */
|
user-select: none;
|
}
|
|
/* 隐藏原生复选框 */
|
.custom-checkbox input {
|
position: absolute;
|
opacity: 0;
|
cursor: pointer;
|
height: 0;
|
width: 0;
|
}
|
|
/* 创建自定义复选框 */
|
.checkmark {
|
position: absolute;
|
top: 0;
|
left: 0;
|
height: 20px;
|
width: 20px;
|
background-color: #eee;
|
border: 2px solid #2d8cf0;
|
/* 红色边框 */
|
border-radius: 4px;
|
}
|
|
/* 当复选框被选中时,背景变为红色 */
|
.custom-checkbox input:checked~.checkmark {
|
background-color: #2d8cf0;
|
}
|
|
/* 创建选中时的勾号 */
|
.checkmark:after {
|
content: "";
|
position: absolute;
|
display: none;
|
}
|
|
/* 显示勾号 */
|
.custom-checkbox input:checked~.checkmark:after {
|
display: block;
|
}
|
|
/* 勾号的样式 */
|
.custom-checkbox .checkmark:after {
|
left: 6px;
|
top: 2px;
|
width: 5px;
|
height: 10px;
|
border: solid white;
|
border-width: 0 2px 2px 0;
|
transform: rotate(45deg);
|
}
|
}
|
|
|
}
|
|
.loading-box {
|
width: calc( 100vw - 500px);
|
height: 100%;
|
position: absolute;
|
left: 500px;
|
line-height: 20%;
|
display: flex;
|
padding-left: calc(50% - 250px);
|
padding-top: 50vh;
|
background: white;
|
color: #2d8cf0;
|
|
/deep/ .ivu-spin-dot {
|
background-color: #2d8cf0;
|
}
|
}
|
|
.no-data {
|
position: absolute;
|
left: 50%;
|
top: 20%;
|
text-align: center;
|
padding-bottom: 100px;
|
color: #999;
|
}
|
|
.no-data img {
|
width: 500px;
|
}
|
|
.tree-box {
|
// width: auto;
|
flex: 1;
|
min-width: 800px;
|
}
|
|
.ent-structure {
|
width: 100%;
|
height: 100%;
|
min-width: 800px;
|
min-height: 800px;
|
background: #fff;
|
}
|
|
.ent-tree {
|
width: 100%;
|
min-width: 800px;
|
min-height: 800px;
|
height: 100%;
|
overflow: auto;
|
}
|
</style>
|