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 (newValue0 != oldValue0) {

securityId1.value = newValue0

}

if (newValue1 != oldValue1) {

symbol1.value = newValue1

}

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 (newValue0 != oldValue0) {

symbol1.value = newValue0

}

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>

相关推荐
她说可以呀2 小时前
Vue插槽
前端·javascript·vue.js
qq_448011163 小时前
Vue + Jinja2两套模版与法冲突造成undefinedError
前端·javascript·vue.js
志尊宝4 小时前
AI原生前端开发(039):AI辅助的Nuxt 4全栈开发
前端·vue.js·ai-native
兰亭妙微UI设计公司7 小时前
兰亭妙微ui设计公司分享:B端组件命名如何支持团队协作
前端·javascript·vue.js
小大宇8 小时前
Vue知识点-ElementPlus-03提示框+询问+通知
前端·javascript·vue.js
潇小墨11 小时前
我给自己造了一个桌面效率助手:剪贴板、截图提单、全盘文件搜索,全都塞进快捷键里
vue.js
jktantan12 小时前
前端也需要事务:Vue Router 乐观更新、回滚与“过期事务”
前端·javascript·vue.js
biu_biubiu13 小时前
我开源了 Codebook
前端·vue.js
小李科技14 小时前
【性能优化】事件委托:目标定位与 React/Vue 实战
前端·vue.js·react.js·面试·性能优化·前端框架
蓝莓味的口香糖14 小时前
【VUE】【CSS】Vue移动端适配主流方案总结
前端·css·vue.js