手写顺序流程图组件

效果图

完整代码

html 复制代码
<template>
  <div>
    <div class="container" :style="{ width: `${spacingX * (colNum - 1) + itemWidth * colNum}px` }">
      <div
        v-for="(item, i) in recordList"
        :key="i"
        class="list-box"
        :style="{
          marginTop: i < colNum ? '0' : `${spacingY}px`,
          marginRight: i % (2 * colNum) === colNum - 1 || i % (2 * colNum) === colNum ? '0' : `${spacingX}px`,
          order: orderList[i] && orderList[i].order,
          visibility: orderList[i] && orderList[i].itemHide ? 'hidden' : 'visible'
        }"
      >
        <div class="cont-box" :style="{ width: itemWidth + 'px', height: itemHeight + 'px', backgroundColor: '#16a085' }">{{ item }}</div>
        <div v-if="i !== listLen - 1" class="arrow-box" :style="arrowStyle[orderList[i] && orderList[i].arrow]">
          <div class="line-tip" />
          <div class="arrow-tip" />
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'FlowPath',
  data() {
    return {
      itemWidth: 75, // item宽度
      itemHeight: 75, // item高度
      colNum: 1, // 显示的列数
      spacingX: 40, // 列间距
      spacingY: 40, // 行间距
      rawList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], // 原始数据
      recordList: [], // 列表数据
      arrowStyle: { right: {}, down: {}, left: {}}, // 箭头样式
      orderList: [], // 列表排序序号
      listLen: '' // 列表数据长度
    }
  },
  mounted() {
    this.listLen = this.rawList.length
    this.initFun() // 初始化方法
    window.addEventListener('resize', this.initFun) // 页面宽度变化监听器
  },
  beforeDestroy() {
    window.removeEventListener('resize', this.initFun) // 组件销毁时移除 resize 事件监听器,避免内存泄漏
  },
  methods: {
    /* 初始化方法 */
    initFun() {
      const pageWidth = document.documentElement.clientWidth // 获取页面宽度(可视区域宽度)
      const minTotalWidth = this.itemWidth + this.spacingX // 每个 item(包含间距) 期望的最小总宽度为 minTotalWidth(单位:px)
      const newNum = Math.floor(pageWidth / minTotalWidth) // 计算 colNum,向下取整
      this.colNum = Math.max(newNum, 1) // 限制 colNum 的最小值,比如至少为 1 列
      // 更新箭头样式和列表样式,因为 colNum 变化了,相关布局依赖 colNum 列数
      this.setArrowStyle() // 设置箭头样式
      this.setOrderList() // 设置列表样式
    },
    /* 设置箭头样式 */
    setArrowStyle() {
      const left = {
        width: this.spacingX + 'px',
        top: this.itemHeight / 2 + 'px',
        left: -this.spacingX + 'px'
      }
      const right = {
        width: this.spacingX + 'px',
        top: this.itemHeight / 2 + 'px',
        right: -this.spacingX + 'px',
        transform: 'rotate(180deg)'
      }
      const down = {
        width: this.spacingY + 'px',
        left: this.itemWidth / 2 + 'px',
        bottom: -this.spacingY + 'px',
        transform: 'rotate(-90deg)',
        transformOrigin: 0
      }
      this.arrowStyle = { right, left, down }
    },
    /* 设置列表样式 */
    setOrderList() {
      this.recordList = JSON.parse(JSON.stringify(this.rawList))
      this.orderList = [] // 列表排序序号
      const n = this.colNum // 显示的列数
      const dbn = n * 2 // 列数 * 2
      // 添加占位的 item 项
      const arrLen = this.listLen
      const remainder = (arrLen - 1) % dbn
      if (remainder >= n && remainder < dbn) {
        const diff = dbn - 1 - remainder
        for (let i = 0; i < diff; i++) {
          this.orderList[arrLen + i] = {
            itemHide: true,
            order: arrLen + i
          }
          this.recordList[arrLen + i] = null
        }
      }
      // 设置 item 的箭头方向和顺序
      this.recordList.map((item, index) => {
        const i = index % dbn	// 余数
        if (i >= 0 && i < n) {
          this.orderList[index] = {
            order: index,
            arrow: i !== n - 1 ? 'right' : 'down'
          } // 不用改变顺序
        } else {
          this.orderList[index] = {
            order: index + ((n - 1) - 2 * (i - n)), // i - n 是与最近一侧的距离
            arrow: i !== dbn - 1 ? 'left' : 'down',
            itemHide: this.orderList[index]?.itemHide
          } // 需要改变顺序
        }
      })
    }
  }
}
</script>

<style scoped lang="scss">
.container {
  display: flex;
  flex-wrap: wrap;
  box-sizing: border-box;
  overflow: hidden;
  .list-box {
    position: relative;
    font-size: 20px;
    box-sizing: border-box;
    .cont-box {
    }
  }
}
/* 箭头区域 */
.arrow-box {
  $bgColor: #303133;
  position: absolute;
  // 线条样式
  .line-tip {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    left: 3px;
    width: 90%;
    height: 2px;
    background-color: $bgColor;
  }
  // 箭头样式
  .arrow-tip {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    left: 1px;
    width: 0;
    height: 0;
    border-top: 8px solid transparent;
    border-bottom: 8px solid transparent;
    border-right: 8px solid $bgColor;
  }
}
</style>
相关推荐
zzywxc7872 天前
自动化测试框架是软件测试的核心基础设施,通过预设规则和脚本自动执行测试用例,显著提高测试效率和覆盖率。
运维·人工智能·自动化·prompt·测试用例·流程图
TextIn智能文档云平台2 天前
产品无法正确解析复杂表格和流程图,有什么替代方案或优化方法?
流程图
coder_lorraine2 天前
Drawnix:开源一体化白板工具,让你的创意无限流动!
开源·流程图
m0_555762903 天前
项目1——单片机程序审查,控制系统流程图和时序图
单片机·mongodb·流程图
zzywxc7875 天前
AI在金融、医疗、教育、制造业等领域的落地案例
人工智能·机器学习·金融·prompt·流程图
趣学习6 天前
A043基于博途西门子1200PLC自动售货机系统仿真A043自动售货机+S71200+HMI+主电路图+外部接线图+流程图+IO分配表
毕业设计·流程图·课程设计·plc·西门子
kebeiovo8 天前
项目必备流程图,类图,E-R图实例速通
开发语言·r语言·流程图
晋人在秦 老K8 天前
入梦工具箱怎么检测硬件?3步完成CPU-Z跑分测试 硬件检测总出错?图吧工具箱免费功能实测 draw.io 部署指南:私有化流程图服务搭建教程
测试工具·流程图·工具·draw.io
zzywxc7878 天前
AI在金融、医疗、教育、制造业等领域的落地案例(含代码、流程图、Prompt示例与图表)
人工智能·spring·机器学习·金融·数据挖掘·prompt·流程图
ui小新11 天前
什么是流程图:流程六要素、三大结构及绘制规范总结
流程图·设计工具·流程图设计