手写顺序流程图组件

效果图

完整代码

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>
相关推荐
软件技术NINI2 天前
html流程图
流程图
充气大锤2 天前
Vue-flow中动态流程图的实现
前端·javascript·vue.js·笔记·流程图·vue-flow
_DCG_2 天前
快速生成viso流程图图片形式
流程图·viso
小逗比r3 天前
时序/流程/各种图ai自动生成:deepseek+‌Mermaid
ai·流程图
空空隆隆4 天前
【自荐】一款简洁、开源的在线白板工具 Drawnix
开源·流程图·思维导图·白板工具
树毅vs素忆8 天前
使用mermaid查看cursor程序生成的流程图
流程图
谁刺我心8 天前
deepseek+mermaid【自动生成流程图】
流程图·mermaid·deepseek
wd2099889 天前
DeepSeek能画流程图吗?分享一种我正在使用的DeepSeek画流程图教程
人工智能·信息可视化·powerpoint·流程图·ppt·亿图图示
爱分享的淘金达人12 天前
2025年证券从业资格考试报名全流程图解✅
java·python·小程序·tomcat·流程图
所得皆惊喜13 天前
SPRING10_SPRING的生命周期流程图
java·spring·流程图