vue3+antV G6节点与文本框实现

一、下载第三方包

使用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>

相关推荐
橘子味的冰淇淋~13 分钟前
【解决】Vue + Vite + TS 配置路径别名成功仍爆红
前端·javascript·vue.js
火星思想2 小时前
深入浅出Vue3 Diff算法:从简单到复杂的演进之路
前端·vue.js
Json_3 小时前
vue2练习项目 家乡特色网站—前端静态网站模板
前端·vue.js·html
萌萌哒草头将军3 小时前
尤雨溪 VoidZero 公司推出新一代ts文件打包工具 tsdown 🚀🚀🚀
vue.js·webpack·typescript
大海海海3 小时前
在 Vue3 中优雅封装 Element Plus Dialog:打造实用的 UseDialog Hooks
前端·vue.js
10年前端老司机4 小时前
使用uni-app 一天时间快速开发豆瓣电影微信小程序和h5应用(仿写)
前端·javascript·vue.js
DC...4 小时前
vue使用语音识别
前端·vue.js·语音识别
天才首富科学家4 小时前
Web前端(11)-vue刷新网页404、登录状态页面路由处理
前端·vue.js
天才首富科学家4 小时前
Web前端(12)-vue代码读取package.json中的version
前端·vue.js
爱生活的前端狗4 小时前
配置 eslint 也太容易了
前端·javascript·vue.js