当前位置: 首页 > news >正文

Vue项目中实现自定义连线图

需求描述

在vue项目中实现由自定义块元素组成的连线图。

效果图

连线图

实现思路

Leader-Line 是一个用于 Web 的轻量级 JavaScript 库,专为创建从一个元素指向另一个元素的引导线而设计。它提供了高度自定义的能力,使得开发者能够轻松地在网页上实现各种指引用例,如表单验证指示、界面导航辅助等。该库支持现代浏览器,且不依赖任何大型框架,保证了其灵活性和性能。
利用Leader-Line组件可以实现把不同dom元素进行连接,且连线及箭头可以灵活自定义。

具体步骤

1、环境版本:

Node版本:16.20.2
Leader-Line版本: 1.0.8

2、使用npm或yarn安装leader-line依赖包

npm install leader-line  或  yarn add leader-line

3、找到装好的依赖包,把leader-line.min.js文件复制到自己的拓展工具类函数文件夹下面
例如放置路径:src\plugins\leader-line.min.js

4、在使用的地方引入leader-line.min.js文件

import LeaderLine from "@/plugins/leader-line.min.js";

5、创建连线
在这里插入图片描述

完整代码

<!-- 经营全景看板-产业链经营全景 -->
<template><div class="chain-chart" :class="theme"><TabHeader title="产业链经营全景"><template #leftBtn><div class="right-select"><template><el-selectclass="common-simple-select"v-model="searchForm.keyProduct"placeholder="选择重点产品":popper-append-to-body="true"popper-class="common-search-popper"clearable><el-optionv-for="item in keyProductOptions":key="item.value":label="item.label":value="item.value"></el-option></el-select></template></div></template><div class="content-container"><!-- 销售计划 --><div ref="box1" class="box sales-plan-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">计划执行</div><div class="title">销售计划</div><div class="sub-title"></div></div><div class="stats"><div class="stat-row"><div>本年计划</div><div class="text-box"><span class="blue-text">5400</span><span class="unit">万只</span></div></div><div class="stat-row"><div>较去年实际</div><div class="arrow"><img src="@/assets/icons/up_arrow.png" alt="" /><img src="@/assets/icons/down_arrow.png" alt="" v-if="false" />3.24%</div></div><div class="stat-row"><div>上期计划准确率</div><div class="arrow"><img src="@/assets/icons/up_arrow.png" alt="" /><img src="@/assets/icons/down_arrow.png" alt="" v-if="false" />84.22%</div></div></div><div class="switch-btns"><span :class="{ active: switchBtnState.salesPlan === 'quantity' }" @click="toggleSwitchBtn('salesPlan', 'quantity')"></span><span :class="{ active: switchBtnState.salesPlan === 'amount' }" @click="toggleSwitchBtn('salesPlan', 'amount')"></span></div></div><!-- 销售目标 --><div ref="box2" class="box sales-target-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">计划执行</div><div class="title">销售目标</div><div class="sub-title" @click="toPath('/strategeAnalysis/pages/842?catalogId=8')">专题分析</div></div><div class="stats"><div class="stat-row"><div>本年达成率</div><div class="text-box"><span class="red-text">75%</span><!-- <imgsrc="@/assets/icons/tips_icon.png"alt=""class="tips-icon"/> --><i class="el-icon-warning tips-icon"></i></div></div><div class="stat-row"><div>本年实际量</div><div class="text-box"><span class="blue-text">5400</span><span class="unit">万只</span></div></div><div class="stat-row"><div>同比</div><div class="arrow"><img src="@/assets/icons/up_arrow.png" alt="" v-if="false" /><img src="@/assets/icons/down_arrow.png" alt="" />4.22%</div></div></div><div class="switch-btns"><span :class="{ active: switchBtnState.salesTarget === 'quantity' }" @click="toggleSwitchBtn('salesTarget', 'quantity')"></span><span :class="{ active: switchBtnState.salesTarget === 'amount' }" @click="toggleSwitchBtn('salesTarget', 'amount')"></span></div></div><!-- 供应商履约 --><div ref="box3" class="box supplier-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">计划执行</div><div class="title">供应商履约</div><div class="sub-title"></div></div><div class="stats"><div class="stat-row"><div>准时交付率</div><div class="text-box"><span class="blue-text">85%</span></div></div><div class="stat-row"><div>质量合格率</div><div class="text-box"><span class="blue-text">90%</span></div></div><div class="stat-row"><div>原料总用量</div><div class="text-box"><span>5400</span><span class="unit">万只</span></div></div></div></div><!-- 协议签订 --><div ref="box4" class="box order-sign-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">计划执行</div><div class="title">协议签订</div><div class="sub-title"></div></div><div class="stats"><div class="stat-row"><div>本年履约率</div><div class="text-box"><span class="blue-text">75%</span></div></div><div class="stat-row"><div>本年协议量</div><div class="text-box"><span>4400</span><span class="unit">万只</span></div></div><div class="stat-row"><div>本年实际量</div><div class="text-box"><span>3400</span><span class="unit">万只</span></div></div></div><div class="switch-btns"><span :class="{ active: switchBtnState.orderSign === 'quantity' }" @click="toggleSwitchBtn('orderSign', 'quantity')"></span><span :class="{ active: switchBtnState.orderSign === 'amount' }" @click="toggleSwitchBtn('orderSign', 'amount')"></span></div></div><!-- 生产计划 --><div ref="box5" class="box prod-plan-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">计划执行</div><div class="title">生产计划</div><div class="sub-title"></div></div><div class="stats"><div class="stat-row"><div>生产成本</div><div class="text-box"><span class="blue-text">XX万元</span></div></div><div class="stat-row"><div>生产计划量</div><div class="text-box"><span>600</span><span class="unit">万只</span></div></div><div class="stat-row"><div>排产准确率</div><div class="text-box"><span>100%</span></div></div></div></div><!-- 订单情况 --><div ref="box6" class="box order-detail-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">销售执行</div><div class="title">订单情况</div><div class="sub-title"></div></div><div class="stats"><div class="stat-row"><div>订单满足率</div><div class="text-box"><span class="blue-text">75%</span></div></div><div class="stat-row"><div>紧急订单占比</div><div class="text-box"><span class="red-text">40%</span></div></div><div class="stat-row"><div>未满足订单量</div><div class="text-box"><span>3400</span><span class="unit">万只</span></div></div></div></div><!-- 采购合同 --><div ref="box7" class="box purchase-contract-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">采购执行</div><div class="title">采购合同</div><div class="sub-title"></div></div><div class="stats"><div class="stat-row"><div>生产采购成本</div><div class="text-box"><span class="blue-text">XX万元</span></div></div><div class="stat-row"><div>采购成本同比</div><div class="text-box"><span>4.22%</span></div></div><div class="stat-row"><div>采购成本环比</div><div class="text-box"><span>2.31%</span></div></div></div></div><!-- 回款情况 --><div ref="box8" class="box collect-situation-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">销售执行</div><div class="title">回款情况</div><div class="sub-title" @click="toPath('/strategeAnalysis/pages/856?catalogId=25')">专题分析</div></div><div class="stats"><div class="stat-row"><div>应收账款逾期率</div><div class="text-box"><span class="red-text">20%</span></div></div><div class="stat-row"><div>回款周期</div><div class="text-box"><span>85</span></div></div><div class="stat-row"><div>预期总金额</div><div class="text-box"><span>5000</span></div></div></div></div><!-- 发货情况 --><div ref="box9" class="box send-situation-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">物流执行</div><div class="title">发货情况</div><div class="sub-title"></div></div><div class="stats"><div class="stat-row"><div>准时发货率</div><div class="text-box"><span class="blue-text">90%</span></div></div><div class="stat-row"><div>已发货总量</div><div class="text-box"><span>11</span><span class="unit">万只</span></div></div><div class="stat-row"><div>已发货总金额</div><div class="text-box"><span>500</span></div></div></div></div><!-- 库存管理 --><div ref="box10" class="box inventory-manage-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">物流执行</div><div class="title">库存管理</div><div class="sub-title" @click="toPath('/strategeAnalysis/pages/863?catalogId=41')">专题分析</div></div><div class="stats"><div class="stat-row"><div>可用库存可销月</div><div class="text-box"><span class="blue-text">3个月</span></div></div><div class="stat-row"><div>库存周转天数</div><div class="text-box"><span>80</span></div></div><div class="stat-row"><div>半年内近效期库存占比</div><div class="text-box"><span class="red-text">10%</span></div></div></div></div><!-- 生产执行 --><div ref="box11" class="box prod-execution-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">生产执行</div><div class="title">生产执行</div><div class="sub-title"></div></div><div class="stats"><div class="stat-row"><div>生产批次合格率</div><div class="text-box"><span class="blue-text">100%</span></div></div><div class="stat-row"><div>已生产总量</div><div class="text-box"><span>10</span><span class="unit">万只</span></div></div><div class="stat-row"><div>紧急生产占比</div><div class="text-box"><span>10%</span></div></div></div></div><!-- 商业库存 --><div ref="box12" class="box business-inventory-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">商业流通</div><div class="title">商业库存</div><div class="sub-title"></div></div><div class="stats"><div class="stat-row"><div>商业库存总量</div><div class="text-box"><span class="blue-text">50</span><span class="unit">万只</span></div></div><div class="stat-row"><div>库存过低</div><div class="text-box"><span class="red-text">16</span></div></div><div class="stat-row"><div>库存过高</div><div class="text-box"><span>13</span></div></div></div></div><!-- 终端覆盖 --><div ref="box13" class="box terminal-coverage-box common-tab-header-border"><div class="corner-decoration top-left"></div><div class="corner-decoration top-right"></div><div class="corner-decoration bottom-left"></div><div class="corner-decoration bottom-right"></div><div class="header"><div class="type">终端分析</div><div class="title">终端覆盖</div><div class="sub-title"  @click="toPath()">专题分析</div></div><div class="stats"><div class="stat-row"><div>目标终端覆盖率</div><div class="text-box"><span class="blue-text">75%</span></div></div><div class="stat-row"><div>纯销总量</div><div class="text-box"><span>10</span><span class="unit">万只</span></div></div><div class="stat-row"><div>未开发终端总量</div><div class="text-box"><span>XX</span></div></div></div></div></div></TabHeader></div>
</template><script>
import TabHeader from "@/components/common_components/tabHeader.vue";
import LeaderLine from "@/plugins/leader-line.min.js";
import { Notification } from 'element-ui'export default {components: {TabHeader,},props: {width: {type: Number,default: null,},height: {type: Number,default: null,},theme: {type: String,default: "theme-dark",},},data() {return {lines: [],switchBtnState: {salesPlan: 'quantity', // 销售计划salesTarget: 'quantity', // 销售目标orderSign: 'quantity', // 协议签订},keyProductOptions: [{label: "重点产品1",value: "1",},{label: "重点产品2",value: "2",},{label: "重点产品3",value: "3",},],searchForm: {keyProduct: ''}};},computed: {},mounted() {// id为当前页id的时候才做初始化查询if (this.$route.params.id == 865) {this.$nextTick(() => {this.initLines();});}// 添加窗口大小变化的监听window.addEventListener('resize', this.debounceResize);},beforeDestroy() {// 组件销毁前清除所有连线this.lines.forEach((line) => line.remove());// 移除窗口大小变化的监听window.removeEventListener('resize', this.debounceResize);},methods: {toggleSwitchBtn(boxName, value) {this.switchBtnState[boxName] = value;},// 防抖处理debounceResize: function() {if (this.resizeTimer) clearTimeout(this.resizeTimer);this.resizeTimer = setTimeout(() => {this.handleResize();}, 200);},// 处理窗口大小变化handleResize() {// 清除现有的连线if (this.lines && this.lines.length > 0) {this.lines.forEach((line) => {if (line && typeof line.remove === 'function') {line.remove();}});this.lines = [];}// 重新初始化连线this.$nextTick(() => {this.initLines();});},initLines() {// 确保所有ref都存在const refs = ['box1', 'box2', 'box3', 'box4', 'box5', 'box6', 'box7', 'box8', 'box9', 'box10', 'box11', 'box12', 'box13'];const missingRefs = refs.filter(ref => !this.$refs[ref]);if (missingRefs.length > 0) {return;}try {// 创建连线// 销售计划-销售目标const line1 = new LeaderLine(this.$refs.box1, this.$refs.box2, {color: "#0e75ed", // 连线颜色size: 1, // 连线粗细path: "straight", // 连线类型startSocket: "bottom", // 起点位置endSocket: "top", // 终点位置endPlug: 'behind' // 终点插件});// 销售计划-协议签订const line2 = new LeaderLine(this.$refs.box1, this.$refs.box4, {color: "#0e75ed",size: 1,path: "grid",startSocket: "right",endSocket: "top",endPlug: 'behind' });// 销售计划-订单情况const line3 = new LeaderLine(this.$refs.box1, this.$refs.box6, {color: "#0e75ed",size: 1,path: "grid",startSocket: "right",endSocket: "top",endPlug: 'behind' });// 订单情况-生产计划const line4 = new LeaderLine(this.$refs.box6, this.$refs.box5, {color: "#0e75ed",size: 1,path: "straight",startSocket: "bottom",endSocket: "top",endPlug: 'behind' });// 生产计划-供应商履约const line5 = new LeaderLine(this.$refs.box5, this.$refs.box3, {color: "#0e75ed",size: 1,path: "straight",startSocket: "left",endSocket: "right",endPlug: 'behind' });// 采购合同-生产计划const line6 = new LeaderLine(this.$refs.box7, this.$refs.box5, {color: "#0e75ed",size: 1,path: "straight",startSocket: "left",endSocket: "right",endPlug: 'behind'});// 回款情况-商业库存const line7 = new LeaderLine(this.$refs.box8, this.$refs.box12, {color: "#0e75ed",size: 1,path: "grid",startSocket: "top",endSocket: "top",endPlug: 'behind'});// 发货情况-商业库存const line8 = new LeaderLine(this.$refs.box9, this.$refs.box12, {color: "#0e75ed",size: 1,path: "straight",startSocket: "right",endSocket: "left",endPlug: 'behind'});// 发货情况-库存管理const line9 = new LeaderLine(this.$refs.box9, this.$refs.box10, {color: "#0e75ed",size: 1,path: "straight",startSocket: "bottom",endSocket: "top",endPlug: 'behind'});// 库存管理-生产执行const line10 = new LeaderLine(this.$refs.box10, this.$refs.box11, {color: "#0e75ed",size: 1,path: "straight",startSocket: "bottom",endSocket: "top",endPlug: 'behind'});// 生产执行-采购合同const line11 = new LeaderLine(this.$refs.box11, this.$refs.box7, {color: "#0e75ed",size: 1,path: "straight",startSocket: "left",endSocket: "right",endPlug: 'behind'});// 商业库存-终端覆盖const line12 = new LeaderLine(this.$refs.box12, this.$refs.box13, {color: "#0e75ed",size: 1,path: "straight",startSocket: "bottom",endSocket: "top",endPlug: 'behind'});this.lines = [line1, line2, line3, line4, line5, line6, line7, line8, line9, line10, line11, line12];} catch (error) {console.error('Error initializing lines:', error);}},// 路由跳转toPath(url) {if(!url) return  Notification.warning({title: '提示',message: '功能开发中',})this.$router.push(url)}},
};
</script><style lang="scss">
@import "~@/assets/css/variables.scss";
.chain-chart {height: 100%;.content-container {padding: vw(5) vw(10);height: calc(100% - vw(10));position: relative;}.box {background: rgba(12, 51, 101, 0.6);box-shadow: inset 0px 0px vw(25) 0px rgba(10, 77, 154, 0.6);// border: 1px solid #0e75ed;padding: vw(8);width: vw(240);color: #fff;font-family: "Microsoft YaHei", Arial, sans-serif;.header {display: flex;flex-direction: row;flex-wrap: nowrap;align-content: center;justify-content: space-between;border-bottom: 1px solid #0e75ed;padding-bottom: vw(5);align-items: center;.type {font-size: vw(11);border: 1px solid #0e75ed;padding: vw(2) vw(8);border-radius: vw(8);}.title {font-size: vw(15);font-weight: 600;margin-right: vw(12);}.sub-title {font-size: vw(11);color: #b0c4de;margin-right: vw(5);min-width: vw(35);cursor: pointer;}}.stats {margin-top: vw(5);.stat-row {display: flex;justify-content: space-between;align-items: center;font-size: vw(13);margin-bottom: vw(4);.blue-text {font-size: vw(16);color: #1bb7c7;font-weight: bold;margin-right: vw(3);}.unit {font-size: vw(12);color: #fff;margin-left: vw(2);}.red-text {font-size: vw(16);color: $defaultRedColor;font-weight: bold;margin-right: vw(3);}.text-box {display: flex;align-items: center;}.tips-icon {font-size: vw(18);color: $defaultYellowColor;margin-left: vw(5);}.arrow img {height: vw(12);}}}.switch-btns {margin-top: vw(6);display: flex;gap: vw(8);justify-content: flex-end;span {padding: vw(2) vw(10);border-radius: vw(8);border: 1px solid #0e75ed;color: #1bb7c7;cursor: pointer;font-size: vw(12);&.active {background: #0e75ed;color: #fff;}}}}// 销售计划.sales-plan-box {position: absolute;left: vw(10);top: vw(5);}// 销售目标.sales-target-box {position: absolute;left: vw(10);top: 37%;}// 供应商履约.supplier-box {position: absolute;left: vw(10);top: 74.5%;}// 协议签订.order-sign-box {position: absolute;left: 17.5%;top: 37%;}// 生产计划.prod-plan-box {position: absolute;left: 34%;top: 75%;}// 订单情况.order-detail-box {position: absolute;left: 34%;top: 37%;}// 采购合同.purchase-contract-box {position: absolute;left: 50.5%;top: 75%;}// 回款情况.collect-situation-box {position: absolute;left: 50.5%;top: 37%;}// 发货情况.send-situation-box {position: absolute;left: 67.5%;top: 10%;}// 库存管理.inventory-manage-box {position: absolute;left: 67.5%;top: 42%;}// 生产执行.prod-execution-box {position: absolute;left: 67.5%;top: 75%;}// 商业库存.business-inventory-box {position: absolute;right: vw(30);top: 9.5%;}// 终端覆盖.terminal-coverage-box {position: absolute;right: vw(30);top: 42%;}
}
</style>

注意

leader-line组件目前不支持直接引入依赖,需要下载对应leader-line.min.js文件,通过import文件才能正常使用。

报错排查方向

在这里插入图片描述
1、检查引入方式;
2、下载下来的leader-line.min.js该文件仅仅定义了一个LeaderLine 函数 ,并没有使用任何模块导出语句 (即没有module.export 或者 es6 export 导出语句)

针对第2个原因造成的报错,直接在leader-line.min.js后面增加导出语句即可
在这里插入图片描述

http://www.xdnf.cn/news/367309.html

相关文章:

  • 硬件实操技巧记录
  • Edu教育邮箱2025年5月亲测有效
  • 解锁蜘蛛池 SEO 优化:网站流量增长的高效引擎
  • 初等数论--欧拉函数及其性质
  • TLS 加密通信介绍
  • 机器学习 期末考试题
  • 鞋样设计软件
  • 【库(Library)、包(Package)和模块(Module)解析】
  • iOS App 下架了无法下载 ? 推荐个软件——IPADown
  • 【时时三省】(C语言基础)二维数组举例
  • 什么是硅二极管温度传感器
  • OptiStruct实例:声振耦合超单元应用
  • wordpress自学笔记 第二节: 3种独立站商城横幅的制作
  • linux0.11内核源码修仙传第十六章——获取硬盘信息
  • 【技术突破】CAN转Profinet:破解堆垛起重机智能互联的“密钥”
  • Python爬虫抓取Bilibili弹幕并生成词云
  • Qt 系统相关
  • 元强化学习
  • Django项目中不同app使用不同数据库的实现
  • MySQL主从同步(主从复制)
  • PPL困惑度的计算
  • 使用 NSSM 安装 Tomcat 11.0.6 为 Windows 服务
  • loop对象
  • 根据文件路径获取base64照片
  • 具身智能数据集解析
  • LVGL的核心:lv_timer_handler
  • 【AI入门】CherryStudio入门7:引入魔搭中的MCP服务
  • WDG看门狗(独立看门狗和窗口看门狗)
  • Babylon.js学习之路《二、开发环境搭建与第一个Hello World》
  • Windows11开机后黑屏,且任务管理器点击无反应