一、下载第三方包
使用npm下载第三方包:npm install @antv/g6
二、父组件代码
<template>
<div id="financialMonitoring" v-loading="loading">
<retainedProfitsMap
:securityId="securityId1"
:symbol="symbol1"
id="retainedProfitsMap"
class="mt10"
/>
</div>
</template>
<script setup lang="ts">
import retainedProfitsMap from './retainedProfitsMap.vue'
const props = defineProps({
symbol: {
type: String,
default: ''
},
securityId: {
type: [String, Number],
required: true
}
})
const loading = ref(false) // 加载状态
const securityId1 = ref(props.securityId) // 证券id
const symbol1 = ref(props.symbol) // 证券代码
// 监听参数重新赋值
watch(
() => [props.securityId, props.symbol],
(newValue: any, oldValue: any) => {
if (newValue[0] != oldValue[0]) {
securityId1.value = newValue[0]
}
if (newValue[1] != oldValue[1]) {
symbol1.value = newValue[1]
}
editIndex.value = 1
dateRange.value = []
getFinanciaMonitoringData()
},
{ deep: true }
)
</script>
三、子组件代码
<template>
<ItemCard title="脑图" v-loading="loading">
<template #rightPart>
<el-date-picker
v-model="dateYear"
type="year"
value-format="YYYY"
placeholder="请选择年份"
:editable="false"
:clearable="false"
:disabled-date="disabledDate"
@change="changeDate"
class="year-select"
ref="datePicker"
/>
</template>
<template #otherContent>
<div id="container"></div>
</template>
</ItemCard>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import ItemCard from './../RiskWarn/ItemCard.vue'
import G6 from '@antv/g6'
import { queryRetainedProfitsData } from '@/pages/monitor/monitorApi/stockMonitor'
const props = defineProps({
symbol: {
// 证券代码
type: String,
default: ''
},
editIndex: {
// 变更前后索引
type: Number,
default: 1
}
})
const loading = ref(false) // 加载状态
const dateYear = ref<any>('') // 年份
const symbol1 = ref(props.symbol) // 证券代码
const graphMap = ref() // 脑图dom
const mapData = ref({}) // 脑图数据
const datePicker = ref() // 时间筛选框ref
// 监听参数重新赋值
watch(
() => [props.symbol],
(newValue: any, oldValue: any) => {
if (newValue[0] != oldValue[0]) {
symbol1.value = newValue[0]
}
handleClear()
getRetainedProfitsData()
},
{ deep: true }
)
// 获取脑图数据
const getRetainedProfitsData = async () => {
loading.value = true
await queryRetainedProfitsData(
symbol1.value,
dateYear.value
).then((res: any) => {
if (res.code === 200) {
dateYear.value = String(res.data.year)
mapData.value = res.data.mindMap
initData()
} else {
ElMessage({
type: 'error',
message: res.message
})
}
loading.value = false
})
}
// 注册自定义节点
G6.registerNode('custom-node', {
draw(cfg: any, group: any) {
const {
labelCfg = {},
id,
num,
sameRatio,
numRank,
sameRatioRank,
children
} = cfg
let keyShape = null
// 绘制矩形作为节点主体
const rect = group.addShape('rect', {
attrs: {
fill: '#0088FE',
stroke: '#ffffff',
radius: 3,
cursor: children && children.length > 0 ? 'pointer' : '',
text: id,
width: Math.max(220, id.length * 4),
height: 60,
x: 0,
y: 0
}
})
group.addShape('text', {
attrs: {
...labelCfg.style,
cursor: children && children.length > 0 ? 'pointer' : '',
text: id,
x: Math.max(220, id.length * 4) / 2,
y: 60 / 2,
fontSize: 24,
fill: '#ffffff'
}
})
group.addShape('rect', {
attrs: {
width: 380,
height: 70,
x: -90,
y: 80,
lineWidth: 1,
stroke: '#ccc',
fill: '#ffffff',
radius: 3,
fillOpacity: 0
},
name: 'num-node'
})
group.addShape('text', {
attrs: {
text: `数值:${num}`,
fill: '#333',
fontSize: 15,
x: -Math.max(80, id.length * 4) / 4 - 62,
y: 105
},
name: 'num-text'
})
group.addShape('text', {
attrs: {
text: `行业排名(数值):${numRank}`,
fill: '#333',
fontSize: 15,
x: -Math.max(80, id.length * 4) / 4 + 105,
y: 105
},
name: 'num-text'
})
group.addShape('text', {
attrs: {
text: `同比:${sameRatio}`,
fill: '#333',
fontSize: 15,
x: -Math.max(80, id.length * 4) / 4 - 62,
y: 142
},
name: 'num-text'
})
group.addShape('text', {
attrs: {
text: `行业排名(同比):${sameRatioRank}`,
fill: '#333',
fontSize: 15,
x: -Math.max(80, id.length * 4) / 4 + 105,
y: 142
},
name: 'num-text'
})
return rect
}
})
// 控制当前时间之后不能选
const disabledDate = (time: any) => {
const year = time.getFullYear()
return year < 2019 || year > new Date().getFullYear()
}
// 改变时间
const changeDate = (value: any) => {
if (value) {
dateYear.value = value
handleClear()
getRetainedProfitsData()
datePicker.value.blur()
}
}
const initData = () => {
const graph = new G6.TreeGraph({
container: 'container',
width: 1410,
height: 790,
modes: {
default: ['drag-canvas', 'zoom-canvas']
},
defaultNode: {
type: 'custom-node',
anchorPoints: [
[0, 0.5],
[1, 0.5]
],
labelCfg: {
style: {
fill: '#ffffff',
fontSize: 20,
fontWeight: 'normal',
textAlign: 'center',
textBaseline: 'middle'
}
}
},
defaultEdge: {
type: 'cubic-horizontal',
style: {
stroke: '#0088FE',
lineWidth: 2,
startArrow: {
path: 'M 0,0 L 4,3 L 4,-3 Z', // 开始箭头
fill: '#0088FE'
}
}
},
layout: {
type: 'compactBox',
direction: 'H',
getVGap: () => {
return 68
},
getHGap: () => {
return 308
},
getSide: () => {
return 'right'
}
}
})
graph.data(mapData.value)
graph.render()
graph.fitView()
graphMap.value = graph
// 脑图点击事件
graph.on('node:click', (evt: any) => {
const { item } = evt
const model = item.getModel()
haneldCollapse(item, model)
})
}
// 处理节点展开折叠
const haneldCollapse = (item: any, model: any) => {
const hasChildren = model.children && model.children.length > 0 ? true : false
if (!hasChildren) {
return
}
let collapsed = model.collapsed
collapsed = !collapsed
model.collapsed = collapsed
graphMap.value.updateChild(model, model.id)
const updateParams = {
collapsed
}
graphMap.value.updateItem(item, updateParams)
}
// 清除画布
const handleClear = () => {
graphMap.value && graphMap.value.destroy()
graphMap.value = null
}
onMounted(() => {
getRetainedProfitsData()
})
onBeforeUnmount(() => {
handleClear()
})
</script>
<style lang="less" scoped>
:deep(.right-part) {
display: flex;
align-items: center;
margin-left: auto;
.year-select {
width: 182px;
height: 28px;
--el-text-color-regular: #333333;
}
}
#container {
width: 1410px;
height: 790px;
border: 1px solid #ebeef8;
}
</style>