流程管理,可视化一棵树

useTreeLight.js:

import React, { useEffect, useState, useMemo } from 'react'
import TreeCard from './TreeCard'
import { deepClone } from '../../../utils/tools'
import TreeLine from './TreeLine'
import useTreeLightList from './useTreeLightList'
let timer
let historyPosition = {}
export default function useTreeLight(props) {
  const { dataSource, isToCenter, onAddChild, onDelete, onEdit } = props
  const [scaleValue, setScaleValue] = useState(1)
  const [isDrag, setIsDrag] = useState(false)
  const [domHistoryPositon, setDomHistoryPositon] = useState({
    clientX: 0,
    clientY: 0,
  })
  //添加position和lines
  const { treeData, treeBoundary, processEndNode, endNodeLines } =
    useTreeLightList({ dataSource })

  //查找行列值和position值一致的元素
  const findTreeNode = ({ treeData, position }) => {
    let result
    const findTreeNodeLoop = (arr, parentId = '') => {
      for (let i = 0; i < arr.length; i++) {
        if (
          position.rolIndex === arr[i].position.rolIndex &&
          position.colIndex === arr[i].position.colIndex
        ) {
          result = arr[i]
        }
        if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
          findTreeNodeLoop(arr[i].children, `${parentId}${i + 1}`)
        }
      }
    }
    const treeDataCopy = deepClone(treeData)
    findTreeNodeLoop(treeDataCopy)
    return result
  }

  //查找行列值等于lines数组包含的行列值,对应的type
  const findLineType = ({ treeData, position }) => {
    let result
    const findLineTypeLoop = (arr, parentId = '') => {
      for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i].lines)) {
          const lineItem = arr[i].lines.find(
            (item) =>
              item.rolIndex === position.rolIndex &&
              item.colIndex === position.colIndex
          )
          if (lineItem) {
            result = lineItem.lineType
          }
        }
        if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
          findLineTypeLoop(arr[i].children, `${parentId}${i + 1}`)
        }
      }
    }
    const treeDataCopy = deepClone(treeData)
    findLineTypeLoop(treeDataCopy)
    return result
  }

  //根据树结构判断是否应该渲染TreeCard组件
  const renderTreeCol = ({ rolIndex, colIndex }) => {
    const treeNode = findTreeNode({
      treeData,
      position: { rolIndex, colIndex },
    })
    const lineType = findLineType({
      treeData,
      position: { rolIndex, colIndex },
    })
    const endNodeLine = endNodeLines.find(
      (item) => item.rolIndex === rolIndex && item.colIndex === colIndex
    )
    return (
      <>
        {treeNode && (
          <TreeCard
            title={treeNode.name}
            color={treeNode.color}
            item={treeNode}
            treeNodeContent={treeNode.content}
            onAddChild={onAddChild}
            onDelete={onDelete}
            onEdit={onEdit}
          ></TreeCard>
        )}
        {lineType && <TreeLine lineType={lineType}></TreeLine>}
        {processEndNode.rolIndex === rolIndex &&
          processEndNode.colIndex === colIndex && (
            <div className="m-tree-end-node">流程结束</div>
          )}
        {endNodeLine && <TreeLine lineType={endNodeLine.lineType}></TreeLine>}
      </>
    )
  }

  //根节点回到可视区域内
  const handleResetTreeToCenter = () => {
    setDomHistoryPositon({
      clientX: 0,
      clientY: 0,
    })
    setTimeout(() => {
      const treeRoot = document.getElementById('m-tree-root')
      if (treeRoot) {
        treeRoot.scrollIntoView({
          behavior: 'smooth',
          block: 'center',
          inline: 'center',
        })
      }
    }, 0)
  }

  //调解大小
  const handleScale = (value) => {
    setScaleValue((value / 100).toFixed(2))
    clearTimeout(timer)
    timer = setTimeout(() => {
      handleResetTreeToCenter()
    }, 200)
  }

  //鼠标按下
  const handleMouseDown = (e) => {
    console.log('down', e.clientX, e.clientY, e)
    historyPosition = {
      clientX: e.clientX,
      clientY: e.clientY,
    }
    setIsDrag(true)
    // clearTimeout(timer2)
    // timer2 = setTimeout(function () {
    //   setIsDrag(true)
    // }, 500)
  }

  //鼠标抬起
  const handleMouseUp = (e) => {
    console.log('up', e.clientX, e.clientY, e)
    setIsDrag(false)
  }

  //拖拽
  const handleMouseMove = (e) => {
    if (e.buttons === 1) {
      //console.log('move', e.clientX, e.clientY, e)
      setIsDrag(true)
      setDomHistoryPositon({
        clientX:
          domHistoryPositon.clientX + (e.clientX - historyPosition.clientX),
        clientY:
          domHistoryPositon.clientY + (e.clientY - historyPosition.clientY),
      })
      historyPosition = {
        clientX: e.clientX,
        clientY: e.clientY,
      }
    } else {
      setIsDrag(false)
    }
  }

  const renderTree = useMemo(() => {
    console.log('tree')
    const dataArr = []
    const { rolIndexEnd = 10, colIndexEnd = 10 } = treeBoundary
    const rolCount = rolIndexEnd + 6 < 10 ? 10 : rolIndexEnd + 6
    const colCount = colIndexEnd + 2 < 10 ? 10 : colIndexEnd + 2

    for (let i = 0; i < rolCount; i++) {
      let dataRow = []
      for (let j = 0; j < colCount; j++) {
        dataRow.push({
          row: i,
          col: j,
        })
      }
      dataArr.push(dataRow)
    }
    const isDev = localStorage.getItem('isDev') === 'true' ? true : false
    return (
      <>
        {dataArr.map((colList, rolIndex) => (
          <div className="m-tree-row" key={rolIndex}>
            {colList.map((item, colIndex) => (
              <div
                key={`${rolIndex}-${colIndex}`}
                className={`m-tree-col ${isDev ? 'active' : ''}`}
              >
                {renderTreeCol({ rolIndex, colIndex })}
              </div>
            ))}
          </div>
        ))}
      </>
    )
    // eslint-disable-next-line
  }, [dataSource])

  //渲染dom
  const renderDom = () => {
    return (
      <div className={`m-tree-wrap`}>
        <div className="m-tree-container" onMouseDown={handleMouseDown}>
          <div
            className="m-tree-inner"
            style={{
              transform: `scale(${scaleValue})`,
              left: domHistoryPositon.clientX + 'px',
              top: domHistoryPositon.clientY + 'px',
            }}
          >
            {renderTree}
          </div>
        </div>
        <div
          className={`m-drag-level ${isDrag ? 'active' : ''}`}
          onMouseMove={handleMouseMove}
          onMouseUp={handleMouseUp}
        ></div>
      </div>
    )
  }

  useEffect(() => {
    if (isToCenter) {
      handleResetTreeToCenter()
    }
    // eslint-disable-next-line
  }, [isToCenter])

  return {
    getTreeDom: renderDom,
    handleResetTreeToCenter,
    handleScale,
  }
}

useTreeLightList.js:

import { deepClone } from '../../../utils/tools'

export default function useTreeLightList({ dataSource }) {
  //统计一个棵树各层的元素的个数,并求出最大值
  const getMaxTreeLevelNodeCount = ({ treeDataSource }) => {
    let levelObj = {}
    const find = (arr, level = 0) => {
      for (let i = 0; i < arr.length; i++) {
        arr[i].level = level
        levelObj[level] = levelObj[level] ? levelObj[level] + 1 : 1
        if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
          find(arr[i].children, level + 1)
        }
      }
    }
    const treeDataSourceCopy = deepClone(treeDataSource)
    find(treeDataSourceCopy)

    let levelCountArr = []
    for (let key in levelObj) {
      levelCountArr.push(levelObj[key])
    }
    let maxLevelCount = 0
    if (Array.isArray(levelCountArr) && levelCountArr.length > 0) {
      maxLevelCount = Math.max.apply(null, levelCountArr)
    }

    return maxLevelCount
  }

  // const count = getMaxTreeLevelNodeCount({ treeDataSource: dataSource })
  // console.log(count)

  //添加position
  const handleAddPosition = ({ treeDataSource }) => {
    //rolIndex计算方式:孩子节点rolIndex = 父节点rolIndex + 2
    //colIndex计算方式:
    //(1)startColIndex标识起始位置,
    //(2)如果是第一个节点,需要加上一个levelMove偏移量,这个levelMove偏移量是
    //根据该节点的children计算出来的,和最大层节点数有关
    //(3)如果不是第一个节点,除了和levelMove偏移量有关外,和紧邻的上一个兄弟节点的位置也有关,
    //和紧邻的上一个兄弟节点的levelMove也有关,
    const setPosition = (arr, { rolIndex, startColIndex, isRoot }) => {
      for (let i = 0; i < arr.length; i++) {
        const maxTreeLevelNodeCount = getMaxTreeLevelNodeCount({
          treeDataSource: [arr[i]],
        })
        let levelMove = 0
        if (maxTreeLevelNodeCount % 2 === 0) {
          levelMove = ((maxTreeLevelNodeCount - 2) / 2) * 2 + 1
        } else {
          levelMove = ((maxTreeLevelNodeCount - 1) / 2) * 2
        }
        let fatherColIndex
        if (i === 0) {
          fatherColIndex = startColIndex + (isRoot ? levelMove : 0)
        } else if (i > 0 && arr[i - 1].position) {
          fatherColIndex =
            arr[i - 1].position.colIndex +
            2 +
            arr[i - 1].position.levelMove +
            levelMove
        }
        arr[i].position = {
          rolIndex: rolIndex + 2,
          colIndex: fatherColIndex,
          levelMove,
        }
        if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
          let childrenLength = arr[i].children.length
          //奇数偶数处理方式不同
          let startColIndex

          if (childrenLength % 2 === 1) {
            startColIndex = fatherColIndex - ((childrenLength - 1) / 2) * 2
          } else {
            startColIndex = fatherColIndex - ((childrenLength - 2) / 2) * 2 - 1
          }
          setPosition(arr[i].children, {
            rolIndex: rolIndex + 2,
            startColIndex,
          })
        }
      }
    }
    const treeDataSourceCopy = deepClone(treeDataSource)
    //起始行数: -1 + 2 = 1
    //起始列: 2
    //初步设置position
    setPosition(treeDataSourceCopy, {
      rolIndex: -1,
      startColIndex: 2,
      isRoot: true,
    })
    //找出最小的colIndex
    //平移这个树

    return treeDataSourceCopy
  }

  //添加lines
  const handleAddLines = ({ treeDataSource }) => {
    const setLines = (arr) => {
      for (let i = 0; i < arr.length; i++) {
        const rolIndex = arr[i].position.rolIndex + 1
        arr[i].lines = []

        if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
          const fatherColIndex = arr[i].position.colIndex
          const childrenColIndexArr = arr[i].children.map(
            (item) => item.position.colIndex
          )

          for (
            let j = arr[i].children[0].position.colIndex;
            j < arr[i].children[arr[i].children.length - 1].position.colIndex;
            j++
          ) {
            //设置不合孩子节点及父节点在同一列的lineType
            if (!childrenColIndexArr.includes(j) && j !== fatherColIndex) {
              arr[i].lines.push({
                rolIndex,
                colIndex: j,
                lineType: [2, 4],
              })
            }
            //设置父节点正下方的lineType
            if (!childrenColIndexArr.includes(j) && j === fatherColIndex) {
              if (arr[i].children.length > 1) {
                arr[i].lines.push({
                  rolIndex,
                  colIndex: j,
                  lineType: [1, 2, 4],
                })
              }
            }
          }

          //设置孩子节点正上方的lineType
          for (let j = 0; j < arr[i].children.length; j++) {
            const tempColIndex = arr[i].children[j].position.colIndex
            if (tempColIndex < fatherColIndex && j === 0) {
              arr[i].lines.push({
                rolIndex,
                colIndex: tempColIndex,
                lineType: [2, 3],
              })
            } else if (
              tempColIndex > fatherColIndex &&
              j === arr[i].children.length - 1
            ) {
              arr[i].lines.push({
                rolIndex,
                colIndex: tempColIndex,
                lineType: [4, 3],
              })
            } else if (
              arr[i].children.length === 1 &&
              fatherColIndex === tempColIndex
            ) {
              arr[i].lines.push({
                rolIndex,
                colIndex: tempColIndex,
                lineType: [1, 3],
              })
            } else if (
              arr[i].children.length > 1 &&
              fatherColIndex === tempColIndex
            ) {
              arr[i].lines.push({
                rolIndex,
                colIndex: tempColIndex,
                lineType: [1, 2, 3, 4],
              })
            } else if (
              arr[i].children.length > 1 &&
              fatherColIndex > tempColIndex &&
              j !== 0
            ) {
              arr[i].lines.push({
                rolIndex,
                colIndex: tempColIndex,
                lineType: [2, 3, 4],
              })
            } else if (
              arr[i].children.length > 1 &&
              fatherColIndex < tempColIndex &&
              j !== arr[i].children.length - 1
            ) {
              arr[i].lines.push({
                rolIndex,
                colIndex: tempColIndex,
                lineType: [2, 3, 4],
              })
            }
          }

          setLines(arr[i].children)
        }
      }
    }
    const treeDataSourceCopy = deepClone(treeDataSource)
    setLines(treeDataSourceCopy)
    return treeDataSourceCopy
  }

  //求树坐标的边界值
  const getTreeBoundary = ({ treeDataSource }) => {
    let rolIndexArr = []
    let colIndexArr = []
    const find = (arr, level = 0) => {
      for (let i = 0; i < arr.length; i++) {
        rolIndexArr.push(arr[i].position.rolIndex)
        colIndexArr.push(arr[i].position.colIndex)
        if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
          find(arr[i].children, level + 1)
        }
      }
    }
    const treeDataSourceCopy = deepClone(treeDataSource)
    find(treeDataSourceCopy)

    rolIndexArr.sort((a, b) => a - b)
    colIndexArr.sort((a, b) => a - b)
    return {
      rolIndexStart: rolIndexArr[0],
      rolIndexEnd: rolIndexArr[rolIndexArr.length - 1],
      colIndexStart: colIndexArr[0],
      colIndexEnd: colIndexArr[colIndexArr.length - 1],
    }
  }

  //流程结束节点位置
  const getPorcessEndNode = ({ treeDataSource, treeBoundary }) => {
    return {
      rolIndex: treeBoundary.rolIndexEnd + 4,
      colIndex: treeDataSource[0].position.colIndex,
    }
  }

  //叶子节点连接到流程结束节点的线
  const getEndNodeLines = ({ treeDataSource, processEndNode }) => {
    let leafNodeArr = []
    let endNodeLines = []
    const find = (arr, level = 0) => {
      for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
          find(arr[i].children, level + 1)
        } else {
          leafNodeArr.push(arr[i].position)
        }
      }
    }
    const treeDataSourceCopy = deepClone(treeDataSource)
    find(treeDataSourceCopy)

    leafNodeArr.forEach((item) => {
      for (let i = item.rolIndex + 1; i < processEndNode.rolIndex - 1; i++) {
        endNodeLines.push({
          rolIndex: i,
          colIndex: item.colIndex,
          lineType: [1, 3],
        })
      }
    })
    for (
      let i = leafNodeArr[0].colIndex;
      i < leafNodeArr[leafNodeArr.length - 1].colIndex + 1;
      i++
    ) {
      if (leafNodeArr.length === 1) {
        endNodeLines.push({
          rolIndex: processEndNode.rolIndex - 1,
          colIndex: i,
          lineType: [1, 3],
        })
      } else if (i === leafNodeArr[0].colIndex) {
        endNodeLines.push({
          rolIndex: processEndNode.rolIndex - 1,
          colIndex: i,
          lineType: [1, 2],
        })
      } else if (i === leafNodeArr[leafNodeArr.length - 1].colIndex) {
        endNodeLines.push({
          rolIndex: processEndNode.rolIndex - 1,
          colIndex: i,
          lineType: [1, 4],
        })
      } else if (i === processEndNode.colIndex && leafNodeArr.find(item => item.colIndex === i)) {
        endNodeLines.push({
          rolIndex: processEndNode.rolIndex - 1,
          colIndex: i,
          lineType: [1, 2, 3, 4],
        })
      } else if (i === processEndNode.colIndex && !leafNodeArr.find(item => item.colIndex === i)) {
        endNodeLines.push({
          rolIndex: processEndNode.rolIndex - 1,
          colIndex: i,
          lineType: [2, 3, 4],
        })
      } else if (i !== processEndNode.colIndex && leafNodeArr.find(item => item.colIndex === i)) {
        endNodeLines.push({
          rolIndex: processEndNode.rolIndex - 1,
          colIndex: i,
          lineType: [ 1, 2, 4],
        })
      } else {
        endNodeLines.push({
          rolIndex: processEndNode.rolIndex - 1,
          colIndex: i,
          lineType: [2, 4],
        })
      }

    }
    return endNodeLines
  }

  //切换数据源
  let treeDataResult
  let treeDataSource = dataSource
  let treeBoundary = {}
  let processEndNode = []
  let endNodeLines = []
  if (Array.isArray(treeDataSource) && treeDataSource.length > 0) {
    treeDataResult = handleAddPosition({ treeDataSource })
    treeDataResult = handleAddLines({ treeDataSource: treeDataResult })
    treeBoundary = getTreeBoundary({ treeDataSource: treeDataResult })
    processEndNode = getPorcessEndNode({
      treeDataSource: treeDataResult,
      treeBoundary,
    })
    endNodeLines = getEndNodeLines({
      treeDataSource: treeDataResult,
      processEndNode,
    })
  }

  //切换真正用于渲染的treeData
  let treeData = treeDataResult ? treeDataResult : treeDataSource
  return {
    treeData,
    treeBoundary,
    processEndNode,
    endNodeLines,
  }
}

TreeCard.js:

import React from 'react'
import Icon from '../Icon'

export default function TreeCard(props) {
  const { title, treeNodeContent, color = 'gray', item } = props
  const handleAdd = (e) => {
    e.stopPropagation()
    props.onAddChild(item)
  }
  const handleDelete = (e) => {
    e.stopPropagation()
    props.onDelete(item)
  }
  const handleMouseDown = (e) => {
    e.stopPropagation()
  }

  const getShortContent = () => {
    let shortContent
    if (treeNodeContent && treeNodeContent.length > 50) {
      shortContent = treeNodeContent.slice(0, 50) + '...'
    } else {
      shortContent = treeNodeContent
    }
    return shortContent
  }

  return (
    <div
      className={`m-tree-card ${color}`}
      onClick={() => props.onEdit(item)}
      id={`${item.belongCategory === '0' ? 'm-tree-root' : ''}`}
      onMouseDown={handleMouseDown}
    >
      <div className={`m-tree-card-header ${color}`}>
        <div className="m-tree-card-header-title" title={title}>{title}</div>
        {item.belongCategory !== '0' && (
          <Icon
            className="m-tree-card-header-delete"
            name="delete"
            onClick={handleDelete}
          ></Icon>
        )}
      </div>
      <div className="m-tree-card-content">
        <div className="m-tree-card-content-text" title={treeNodeContent}>
          {getShortContent()}
        </div>
        <span className="m-tree-card-add-wrap" >
          <Icon
            name="bg-add"
            onClick={handleAdd}
            className="m-tree-card-add"
          ></Icon>
          <span className="m-tree-card-add-bg"></span>
        </span>
      </div>
    </div>
  )
}

TreeLight.js:

import React from 'react'

export default function TreeLine(props) {
  const { lineType } = props
  return (
    <div className="m-tree-line-wrap">
      {/* {JSON.stringify(lineType)} */}
      <div className="m-tree-line-row">
        <div className={`m-tree-line-item ${lineType.includes(1) ? 'right' : ' '} ${lineType.includes(4) ? 'bottom' : ' '}`}></div>
        <div className={`m-tree-line-item ${lineType.includes(1) ? 'left' : ' '} ${lineType.includes(2) ? 'bottom' : ' '}`}></div>
      </div>
      <div className="m-tree-line-row">
        <div className={`m-tree-line-item ${lineType.includes(3) ? 'right' : ' '} ${lineType.includes(4) ? 'top' : ' '}`}></div>
        <div className={`m-tree-line-item ${lineType.includes(2) ? 'top' : ' '} ${lineType.includes(3) ? 'left' : ' '}` }></div>
      </div>
    </div>
  )
}

css:

.m-tree-wrap{display: flex; position: relative; flex: 1; margin: 5px 10px 5px; }
.m-tree-container{position: relative; flex: 1;padding: 16px;background: white;overflow: auto;}
.m-drag-level{position: absolute;top: 0;left: 0;right: 0;bottom: 0; pointer-events: none;}
.m-drag-level.active{pointer-events: unset;}
.m-tree-inner{white-space: nowrap;position: absolute;top: 0;left: 0;user-select: none;}
.m-tree-col{display: inline-block;position: relative;width: 240px;height: 120px;vertical-align: top;}
.m-tree-col.active{border: 1px solid #ddd;}
.m-tree-card{display: flex; height: 100%; flex-direction: column; border-radius: 4px;cursor: pointer;box-shadow: 0 1px 8px 0 #ddd;}
.m-tree-card-header{display: flex; padding: 5px 0 5px 5px;}
.m-tree-card-header-title{flex:1;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}
.m-tree-card-header-delete{opacity: 0;;width: 30px;cursor: pointer;text-align: center;vertical-align: top;}
.m-tree-card:hover .m-tree-card-header-delete{opacity: 1;}
.m-tree-card-header-delete:hover{color: #1890ff;font-weight: bold;}
.m-tree-card-content{display: flex; flex:1;flex-direction: column; padding: 5px; background: white;border-bottom-left-radius: 4px;border-bottom-right-radius: 4px;}
.m-tree-card-content-text{flex:1;word-wrap:break-word;word-break:break-all;white-space: normal}
.m-tree-card.gray{border-top: 4px solid #88939F;background: #EEF4FB;}
.m-tree-card.gray:hover{box-shadow: 0 1px 8px 0 #88939F;}
.m-tree-card.blue{border-top: 4px solid #4A94FF;background: #E9F2FF;}
.m-tree-card.blue:hover{box-shadow: 0 1px 8px 0 #4a94ff;}
.m-tree-card.orange{border-top: 4px solid #FCAD22;background: #FFF9EE;}
.m-tree-card.orange:hover{box-shadow: 0 1px 8px 0 #FCAD22;}
.m-tree-card.green{border-top: 4px solid #3CB4B2;background: #E6F8F8;}
.m-tree-card.green:hover{box-shadow: 0 1px 8px 0 #3CB4B2;}
.m-tree-card-add-wrap{position: absolute;display: inline-block; transform: translateY(115px) translateX(99px);width: 30px;height: 30px;z-index: 999;}
.m-tree-card-add{position: absolute;top: 0;left: 0;  display: inline-block;width: 30px;height: 30px;font-size: 30px;color: #A0B5C8;cursor: pointer;z-index: 99;}
.m-tree-card-add:hover{color: #8599AA;}
.m-tree-card-add-bg{position: absolute;top: 16px; left: 8px; width: 15px; height: 15px; background: white;}
.m-tree-line-wrap{display: flex;flex-direction: column;height: 100%;}
.m-tree-line-row{display: flex; flex:1;}
.m-tree-line-item{flex:1;}
.m-tree-line-item.top{border-top: 1px solid #333}
.m-tree-line-item.right{border-right: 1px solid #333}
.m-tree-line-item.bottom{border-bottom: 1px solid #333}
.m-tree-line-item.left{border-left: 1px solid #333}
.m-tree-slider{display: inline-block; width: 100px;}
.m-tree-end-node{display: flex; align-items: center;justify-content: center;height:60px;border-radius: 4px;box-shadow: 0 1px 8px 0 #ddd;}

后端node.js: 

 data.js:

let dataArr = [
  {
    id: '0',
    key: '0',
    path: '/light/index/content?id=0',
    title: '请假',
    tree: [
      {
        name: '申请人',
        content: '商之讯',
        belongCategory: '0',
        status: true,
        id: 1622771045562,
        color: 'blue',
        position: {
          rolIndex: 1,
          colIndex: 2
        },
        children: [
          {
            name: '审批人',
            content: '申请人自选 不限范围 多选 依次审批',
            belongCategory: 1622771045562,
            status: false,
            id: 1622771052842,
            color: 'orange',
            position: {
              rolIndex: 3,
              colIndex: 2
            },
            children: [
              {
                name: '抄送人',
                content: '申请人自选',
                belongCategory: 1622771052842,
                status: false,
                id: 1636424256035,
                color: 'green',
                position: {
                  rolIndex: 5,
                  colIndex: 2
                },
              }
            ]
          }
        ]
      }
    ]
  },
  {
    id: '1',
    key: '1',
    path: '/light/index/content?id=1',
    title: '报销',
    tree: [
      {
        name: '申请人',
        content: '商之讯',
        belongCategory: '0',
        status: true,
        id: 1622771045562,
        color: 'blue',
        position: {
          rolIndex: 1,
          colIndex: 2
        },
        children: [
          {
            name: '审批人',
            content: '申请人自选 不限范围 多选 依次审批',
            belongCategory: 1622771045562,
            status: false,
            id: 1622771052842,
            color: 'orange',
            position: {
              rolIndex: 3,
              colIndex: 1
            },
            children: [
              {
                name: '抄送人',
                content: '申请人自选',
                belongCategory: 1622771052842,
                status: false,
                id: 1636424256035,
                color: 'green',
                position: {
                  rolIndex: 5,
                  colIndex: 1
                },
              }
            ]
          },
          {
            name: '审批人',
            content: '申请人自选 不限范围 多选 依次审批',
            belongCategory: 1622771045562,
            status: false,
            id: 1622771052843,
            color: 'orange',
            position: {
              rolIndex: 3,
              colIndex: 3
            },
            children: [
              {
                name: '抄送人',
                content: '申请人自选',
                belongCategory: 1622771052843,
                status: false,
                id: 1636424256034,
                color: 'green',
                position: {
                  rolIndex: 5,
                  colIndex: 3
                }
              }
            ]
          }
        ]
      }
    ]
  },
  {
    id: '2',
    key: '2',
    path: '/light/index/content?id=2',
    title: '出差',
    tree: [
      {
        name: '申请人',
        content: '商之讯',
        belongCategory: '0',
        status: true,
        id: 1622771045562,
        color: 'blue',
        position: {
          rolIndex: 1,
          colIndex: 4
        },
        children: [
          {
            name: '审批人',
            content: '申请人自选 不限范围 多选 依次审批',
            belongCategory: 1622771045562,
            status: false,
            id: 1622771052842,
            color: 'orange',
            position: {
              rolIndex: 3,
              colIndex: 2
            },
            children: [
              {
                name: '抄送人',
                content: '申请人自选',
                belongCategory: 1622771052842,
                status: false,
                id: 1636424256035,
                color: 'green',
                position: {
                  rolIndex: 5,
                  colIndex: 2
                }
              }
            ]
          },
          {
            name: '审批人',
            content: '申请人自选 不限范围 多选 依次审批',
            belongCategory: 1622771045562,
            status: false,
            id: 1622771052843,
            color: 'orange',
            position: {
              rolIndex: 3,
              colIndex: 4
            },
            children: [
              {
                name: '抄送人',
                content: '申请人自选',
                belongCategory: 1622771052843,
                status: false,
                id: 1636424256034,
                color: 'green',
                position: {
                  rolIndex: 5,
                  colIndex: 4
                }
              }
            ]
          },
          {
            name: '审批人',
            content: '申请人自选 不限范围 多选 依次审批',
            belongCategory: 1622771045562,
            status: false,
            id: 1622771052844,
            color: 'orange',
            position: {
              rolIndex: 3,
              colIndex: 6
            },
            children: [
              {
                name: '抄送人',
                content: '申请人自选',
                belongCategory: 1622771052844,
                status: false,
                id: 1636424256036,
                color: 'green',
                position: {
                  rolIndex: 5,
                  colIndex: 6
                }
              }
            ]
          }
        ]
      }
    ]
  },
  {
    id: '3',
    key: '3',
    path: '/light/index/content?id=3',
    title: '活动',
    tree: [
      {
        name: '申请人',
        content: '商之讯',
        belongCategory: '0',
        status: true,
        id: 1622771045562,
        color: 'blue',
        position: {
          rolIndex: 1,
          colIndex: 6
        },
        children: [
          {
            name: '审批人',
            content: '申请人自选 不限范围 多选 依次审批',
            belongCategory: 1622771045562,
            status: false,
            id: 1622771052842,
            color: 'orange',
            position: {
              rolIndex: 3,
              colIndex: 3
            },
            children: [
              {
                name: '抄送人',
                content: '申请人自选',
                belongCategory: 1622771052842,
                status: false,
                id: 1636424256066,
                color: 'green',
                position: {
                  rolIndex: 5,
                  colIndex: 3
                }
              }
            ]
          },
          {
            name: '审批人',
            content: '申请人自选 不限范围 多选 依次审批',
            belongCategory: 1622771045562,
            status: false,
            id: 1622771052843,
            color: 'orange',
            position: {
              rolIndex: 3,
              colIndex: 5
            },
            children: [
              {
                name: '抄送人',
                content: '申请人自选',
                belongCategory: 1622771052843,
                status: false,
                id: 1636424256034,
                color: 'green',
                position: {
                  rolIndex: 5,
                  colIndex: 5
                }
              }
            ]
          },
          {
            name: '审批人',
            content: '申请人自选 不限范围 多选 依次审批',
            belongCategory: 1622771045562,
            status: false,
            id: 1622771052844,
            color: 'orange',
            position: {
              rolIndex: 3,
              colIndex: 7
            },
            children: [
              {
                name: '抄送人',
                content: '申请人自选',
                belongCategory: 1622771052844,
                status: false,
                id: 1636424256036,
                color: 'green',
                position: {
                  rolIndex: 5,
                  colIndex: 7
                }
              }
            ]
          },
          {
            name: '审批人',
            content: '申请人自选 不限范围 多选 依次审批',
            belongCategory: 1622771045562,
            status: false,
            id: 1622771052847,
            color: 'orange',
            position: {
              rolIndex: 3,
              colIndex: 9
            },
            children: [
              {
                name: '抄送人',
                content: '申请人自选',
                belongCategory: 1622771052847,
                status: false,
                id: 1636424256038,
                color: 'green',
                position: {
                  rolIndex: 5,
                  colIndex: 9
                }
              }
            ]
          }
        ]
      }
    ]
  }
]

module.exports = { dataArr }

list.js:

let { dataArr } = require('../data')
const { v4: uuidv4 } = require('uuid')

//调试用,返回全部数据
const dataSearchAll = (req, res) => {
  console.log('air dataSearchAll')
  res.send({
    code: 200,
    data: dataArr,
    message: '搜索成功'
  })
}

//搜索
const dataSearch = (req, res) => {
  const data = dataArr.map((item) => {
    return {
      id: item.id,
      key: item.key,
      path: item.path,
      title: item.title
    }
  })
  console.log('air dataSearch')
  res.send({
    code: 200,
    data,
    message: '搜索成功'
  })
}

//添加
const dataAdd = (req, res) => {
  const { dataItem } = req.body
  dataItem.id = uuidv4()
  dataItem.path += `?id=${dataItem.id}`
  dataItem.key = dataItem.id
  dataItem.tree = [
    {
      name: '流程起点',
      belongCategory: '0',
      status: true,
      id: 1622771045569
    }
  ]
  dataArr.push({ ...dataItem })
  res.send({
    code: 200,
    data: dataItem,
    message: '添加成功'
  })
}

//删除
const dataDelete = (req, res) => {
  let { ids } = req.body
  console.log(ids)
  dataArr = dataArr.filter((item) => !ids.includes(item.id))
  res.send({
    code: 200,
    data: ids,
    message: '删除成功'
  })
}

//编辑
const dataEdit = (req, res) => {
  let { id, dataItem } = req.body
  let index = dataArr.findIndex((item) => item.id === id)
  if (index >= 0) {
    dataArr[index].title = dataItem.title
    //dataArr[index] = { id, ...dataItem }
    res.send({
      code: 200,
      data: dataItem,
      message: '编辑成功'
    })
  } else {
    res.send({
      code: 400,
      data: dataItem,
      message: '编辑失败,id不存在'
    })
  }
}

module.exports = {
  processListSearchAll: dataSearchAll,
  processListSearch: dataSearch,
  processListAdd: dataAdd,
  processListDelete: dataDelete,
  processListEdit: dataEdit
}

fields.js:

let { dataArr } = require('../data')


//添加函数,如果不是一级分类,就递归遍历所有children,找到他的所属分类并添加
const addFunWrap = (dataItem) => {
  //belongCategory==='0'代表一级分类
  if (dataItem.belongCategory === '0') {
    dataArr.push({ ...dataItem })
    return () => {}
  } else {
    const addFun = (arr, belongCategory) => {
      for (let i = 0; i < arr.length; i++) {
        if (arr[i].children) {
          addFun(arr[i].children, belongCategory)
        }
        if (arr[i].id === belongCategory) {
          if (arr[i].children && arr[i].children.length > 0) {
            arr[i].children.push(dataItem)
          } else {
            arr[i].children = [dataItem]
          }
        }
      }
    }

    return addFun
  }
}


//编辑或删除后,children可能为空,及时删除children
const deleteEmptyChildrenFunWrap = (tree) => {
  const deleteEmptyChildrenFun = (arr) => {
    for (let i = 0; i < arr.length; i++) {
      if (arr[i].children) {
        deleteEmptyChildrenFun(arr[i].children)
      }
      if (arr[i].children && arr[i].children.length === 0) {
        delete arr[i].children
      }
    }
  }

  return deleteEmptyChildrenFun(tree)
}

//编辑函数,递归遍历所有children,找到他的对应的id并编辑
const editFunWrap = (dataItem, tree) => {
  const editFun = (arr, id) => {
    for (let i = 0; i < arr.length; i++) {
      if (arr[i].children) {
        editFun(arr[i].children, id)
      }
      if (arr[i].id === id) {
        //编辑时没有修改所属分类
        if (arr[i].belongCategory === dataItem.belongCategory) {
          arr[i] = { id, ...dataItem, updateTime: Date.now(), children: arr[i].children }
        } else {
          //删除当前的,这块需要改一改,不能把children删了
          const deleteItem = arr.splice(i, 1)
          if (arr.length === 0) {
            deleteEmptyChildrenFunWrap(tree)
          }
          if (Array.isArray(deleteItem) && deleteItem.length > 0 && Array.isArray(deleteItem[0].children) && deleteItem[0].children.length > 0) {
            dataItem.children = deleteItem[0].children
          }
          //重新添加一个新的
          addFunWrap(dataItem)(tree, dataItem.belongCategory)
        }
      }
    }
  }
  return editFun
}

//删除函数,递归遍历所有children,找到他的对应的id并编辑
const deleteFunWrap = (id, tree) => {
  const deleteFun = (arr) => {
    for (let i = 0; i < arr.length; i++) {
      if (arr[i].children) {
        deleteFun(arr[i].children)
      }
      if (arr[i].id === id) {
        //删除当前的
        arr.splice(i, 1)
        if (arr.length === 0) {
          deleteEmptyChildrenFunWrap(tree)
        }
      }
    }
  }
  deleteFun(tree)
}


//调试用,返回全部数据
const dataSearchAll = (req, res) => {
  res.send({
    code: 200,
    data: dataArr,
    message: '搜索成功',
  })
}

//搜索
const dataSearch = (req, res) => {
  const { tableId } = req.body
  const application = dataArr.find((item) => item.id === tableId)
  res.send({
    code: 200,
    data: {
      ...application
    },
    message: '搜索成功',
  })
}

//添加
const dataAdd = (req, res) => {
  const { tableId, dataItem } = req.body
  console.log('add')
  const index = dataArr.findIndex((item) => item.id === tableId)
  dataItem.id = Date.now()
  const tree = dataArr[index].tree

  addFunWrap(dataItem)(tree, dataItem.belongCategory)

  res.send({
    code: 200,
    data: dataItem,
    message: '添加成功',
  })
}

//删除
const dataDelete = (req, res) => {
  let { tableId, id } = req.body
  const tableIndex = dataArr.findIndex((item) => item.id === tableId)
  console.log(id)
  // dataArr[tableIndex].table.fields = dataArr[tableIndex].table.fields.filter(
  //   (item) => !ids.includes(item.id)
  // )
  deleteFunWrap(id, dataArr[tableIndex].tree)
  res.send({
    code: 200,
    data: id,
    message: '删除成功',
  })
}

//编辑
const dataEdit = (req, res) => {
  let { tableId, id, dataItem } = req.body
  const tableIndex = dataArr.findIndex((item) => item.id === tableId)
  const tree = dataArr[tableIndex].tree
  editFunWrap(dataItem, tree)(tree, id)
  res.send({
    code: 200,
    data: dataItem,
    message: '编辑成功',
  })
}

//编辑全部
const dataEditAll = (req, res) => {
  const { tableId, dataItem, skin } = req.body
  const tableIndex = dataArr.findIndex((item) => item.id === tableId)
  dataArr[tableIndex].table.fields = [
    ...dataArr[tableIndex].table.fields.filter((item) => item.isSystem),
    ...dataItem,
  ]
  dataArr[tableIndex].table.skin = skin ? skin : {}

  res.send({
    code: 200,
    data: dataArr,
    message: '保存成功',
  })
}

module.exports = {
  processFieldsSearchAll: dataSearchAll,
  processFieldsSearch: dataSearch,
  processFieldsAdd: dataAdd,
  processFieldsDelete: dataDelete,
  processFieldsEdit: dataEdit,
  processFieldsEditAll: dataEditAll,
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐同保

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值