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>

相关推荐
boy快快长大7 分钟前
【VUE】day01-vue基本使用、调试工具、指令与过滤器
前端·javascript·vue.js
JYeontu2 小时前
实现一个带@功能的输入框组件
前端·javascript·vue.js
一颗奇趣蛋2 小时前
vue-router的query和params的区别(附实际用法)
前端·vue.js
lh_12543 小时前
VUE2脚手架的下载与安装
vue.js
cypking3 小时前
vue实现一个pdf在线预览,pdf选择文本并提取复制文字触发弹窗效果
前端·vue.js·pdf
飘逸飘逸3 小时前
若依前后端分离版使用Electron打包前端Vue为Exe文件
前端·vue.js·electron·vue·ruoyi
双口馋猫4 小时前
cesium+vite demo
前端·vue.js
树上有只程序猿5 小时前
3分钟,了解一下Vue3中的插槽到底是个啥
vue.js