antdv a-timeline自定义流程节点、审批节点,历史节点

ant 的时间节点组件支持自定义节点内容, 以及节点状态。下面封装了几个业务组件,供应不同业务场景。流程审批节点的设置,查看历史节点信息,代办已办审批流程的状态查看。
如下图所示:
在这里插入图片描述
在这里插入图片描述
从ant官方不难找到如下代码

<template>
  <a-timeline>
    <a-timeline-item>Create a services site 2015-09-01</a-timeline-item>
    <a-timeline-item>Solve initial network problems 2015-09-01</a-timeline-item>
    <a-timeline-item>Technical testing 2015-09-01</a-timeline-item>
    <a-timeline-item>Network problems being solved 2015-09-01</a-timeline-item>
  </a-timeline>
</template>

a-timeline-item 就是每个节点的内容, 使用slot dot可以自定义节点 圆点图标。
实际中我们的代码框架是这样的

<a-timeline class="eg-time-line-todo">
      <a-timeline-item class="line-item" v-for="(line, index) in flowListArr" :key="index">
        <div>content</div>
        <div slot="dot">
        </div>
      </a-timeline-item>
    </a-timeline>

以上组件部分实现

EgTimelineParallelTodo:
<template>
  <div>
    <a-timeline class="eg-timeline-parallel-todo">
      <a-timeline-item class="line-item" v-for="line in flowListArr" :key="line.nodeId">
        <div class="line-right">
          <parallel-node
            :ref="'route' + line.nodeId"
            v-if="line.type == 'route'"
            :line="line"
            :curNodeIDs="curNodeIDs"
            :source="'todo'"
          ></parallel-node>
          <p class="list-node" v-else>
            {{ line.name }}
          </p>
          <p class="list-node-name" v-if="line.type == 'start'">{{ line.properties.contentLabel }}</p>
          <!-- 单人 显示名称 -->
          <p class="list-node-name" v-if="line.type !== 'start' && getApprovalList(line).length == 1">
            {{ getApprovalList(line)[0].RY_JBXX__NAME }}
            <span
              >{{ getstatus(line) }}
              <eg-button
                v-if="line.status == 1 && curNodeIDs.includes(line.nodeId)"
                :attrObject="attrObject"
                @clickButton="cbClick"
              ></eg-button
            ></span>
          </p>
          <!-- 时间 -->
          <p class="node-handle-time" v-if="line.status != '1'">{{ line.handleTime }}</p>
          <span v-if="getApproveNode(line)">
            <span v-if="getApprovalList(line).length == 0 && line.noneActionerAction == 'autoPass'">{{
              getNoneActionerAction(line) == "autoPass" ? "(自动通过)" : "(转交管理员)"
            }}</span>
          </span>
          <span v-if="line.status == '7'">(转审批)</span>
          <span v-if="line.type == 1"
            >{{ getApprovalList(line).length + getActType(line, "or") ? "人或签" : "人会签" }}
            <eg-button
              v-if="line.status == 1 && curNodeIDs.includes(line.nodeId)"
              :attrObject="attrObject"
              @clickButton="cbClick"
            ></eg-button
          ></span>
          <span v-if="getAppproveOriNode(line)">(发起人自己)</span>

          <!-- 多人头像 -->
          <eg-head-group
            v-if="getApprovalList(line).length > 1"
            :photoList="getApprovalList(line)"
            :isName="true"
            :hasMaxLength="false"
            :isphotoDel="getRuleType(line) == 'target_select'"
            :isAdd="false"
            :ellipsis="false"
            :linerGrident="false"
          ></eg-head-group>

          <!-- 审批意见 -->
          <p
            class="node-opinion"
            v-if="
              (line.handleOpinion && sourceName == 'todo') ||
                (line.handleOpinion && opinionVisible == '0' && sourceName == 'application')
            "
          >
            {{ line.handleOpinion }}
          </p>
        </div>
        <template slot="dot">
          <!-- 发起人头像 -->
          <eg-photo type="after" :width="32" :height="32" v-if="line.type == 'start'" :photo="startPhoto"></eg-photo>
          <!-- 并行分支头像 -->
          <span class="circle" v-else-if="line.type == 'route'">
            <eg-svg
              :svgId="'bingxingfenzhi'"
              :width="20"
              :height="20"
              :color="'#ffffff'"
              :backgroundColor="'#3F8FFF'"
              :verticalAlign="'-2px'"
              :mright="0"
            />
          </span>
          <!-- 抄送头像 -->
          <span class="circle" v-else-if="line.type == 'notifier'">
            <eg-svg
              :svgId="'icon_notifier'"
              :width="32"
              :height="32"
              :color="'#ffffff'"
              :backgroundColor="'#3F8FFF'"
              :verticalAlign="'-2px'"
              :mright="0"
            />
          </span>
          <!-- 单人头像 -->
          <template v-else>
            <template v-if="getApprovalList(line).length <= 1">
              <eg-photo :width="32" :height="32" type="after" :photo="getApprovalList(line)[0]"></eg-photo>
            </template>
            <!-- 多人头像 -->
            <template v-else>
              <span class="circle">
                <eg-svg
                  :svgId="'icon_manypeople'"
                  :width="32"
                  :height="32"
                  :color="'#ffffff'"
                  :backgroundColor="'#3F8FFF'"
                  :verticalAlign="'-2px'"
                  :mright="0"
                />
              </span>
            </template>
          </template>
          <!-- 状态图标 -->
          <eg-svg
            :svgId="getIconName(line)"
            :backgroundColor="'transparent'"
            :width="14"
            :height="14"
            class="status-icon"
          />
        </template>
      </a-timeline-item>
    </a-timeline>
  </div>
</template>
<script>
import Vue from "vue";
import { Timeline } from "ant-design-vue";
import "ant-design-vue/lib/timeline/style/css";
import ParallelNode from "./ParallelNode.vue";
Vue.use(Timeline);
/**
 * @module EgTimelineParallelTodo
 * @author 何志强
 * @description 流程平行分支节点申请单,配置审批流程节点
 */
export default {
  components: { ParallelNode },
  name: "EgTimelineParallelTodo",
  data() {
    return {
      attrObject: {
        type: "link",
        size: "small",
        text: "催办"
      },
      token: sessionStorage.getItem("Authorization"),
      defaultImg: 'this.src="' + require("../../commonImg/loading-failed-img.png") + '"', //默认图地址
      treeType: "orgTree",
      addIndex: "",
      selctmodel: "",
      orgIds: [],
      multiple: false,
      flowListArr: [],
      handlerNode: null,
      cbfunck: null,
      startPhoto: {}
    };
  },
  /**
   * Props 接受父组件的传值 重要参数
   * @prop {Array} flowList 流程节点数据源
   */
  props: {
    flowList: {
      type: Object,
      default: function() {
        return {
          nextId: "7954f7a941704b65a029ce250c8adb28,c6eeb77f3dc242aebc6ec96852b28ee3",
          auth: [
            {
              name: "金额",
              value: "1",
              key: "7ba099ea42604ce7a0666e8f447ec38d"
            },
            {
              name: "多行文本",
              value: "1",
              key: "b3a8b3894cf042baa744d7efa189592a"
            }
          ],
          name: "发起人",
          type: "start",
          nodeId: "4c1e626646e8487687411d044d0b21f2",
          properties: {
            contentLabel: "所有人",
            type: "starter",
            category: "0",
            content: ""
          },
          childNode: {
            nextId:
              "139df798003444bcacee7e56a0707adc,bf1021ab34024051a4994635d1c832bf,b29755d09fd543468bc949cfdc7f9ef3",
            name: "分支1",
            prevId: "4c1e626646e8487687411d044d0b21f2",
            type: "route",
            nodeId: "c6eeb77f3dc242aebc6ec96852b28ee3",
            childNode: {
              nextId: "",
              auth: [
                {
                  name: "金额",
                  value: "2",
                  key: "7ba099ea42604ce7a0666e8f447ec38d"
                },
                {
                  name: "多行文本",
                  value: "2",
                  key: "b3a8b3894cf042baa744d7efa189592a"
                }
              ],
              name: "审批人",
              prevId: "c6eeb77f3dc242aebc6ec96852b28ee3",
              type: "approver",
              nodeId: "b29755d09fd543468bc949cfdc7f9ef3",
              properties: {
                activateType: "ONE_BY_ONE",
                actionerRules: [
                  {
                    approvals: [],
                    actType: "or",
                    subType: "3",
                    type: "target_select"
                  }
                ],
                type: "approver",
                agreeAll: false
              },
              childNode: ""
            },
            parallelismNodes: [
              {
                nextId: "fd5c709ac465469bbe59cb433d454199",
                name: "",
                prevId: "c6eeb77f3dc242aebc6ec96852b28ee3",
                type: "parallelism",
                priority: "1.0",
                nodeId: "139df798003444bcacee7e56a0707adc",
                childNode: {
                  nextId: "",
                  auth: [
                    {
                      name: "金额",
                      value: "2",
                      key: "7ba099ea42604ce7a0666e8f447ec38d"
                    },
                    {
                      name: "多行文本",
                      value: "2",
                      key: "b3a8b3894cf042baa744d7efa189592a"
                    }
                  ],
                  name: "审批人1_1",
                  prevId: "139df798003444bcacee7e56a0707adc",
                  type: "approver",
                  nodeId: "fd5c709ac465469bbe59cb433d454199",
                  properties: {
                    activateType: "ONE_BY_ONE",
                    actionerRules: [
                      {
                        approvals: [],
                        actType: "or",
                        subType: "3",
                        type: "target_select"
                      }
                    ],
                    type: "approver",
                    agreeAll: false
                  },
                  childNode: {
                    nextId: "",
                    auth: [
                      {
                        name: "金额",
                        value: "2",
                        key: "7ba099ea42604ce7a0666e8f447ec38d"
                      },
                      {
                        name: "多行文本",
                        value: "2",
                        key: "b3a8b3894cf042baa744d7efa189592a"
                      }
                    ],
                    name: "审批人1_2",
                    prevId: "4c1e626646e8487687411d044d0b21f2",
                    type: "approver",
                    nodeId: "7954f7a941704b65a029ce250c8adb28",
                    properties: {
                      activateType: "ONE_BY_ONE",
                      actionerRules: [
                        {
                          approvals: [],
                          actType: "or",
                          subType: "3",
                          type: "target_select"
                        }
                      ],
                      type: "approver",
                      agreeAll: false
                    },
                    childNode: ""
                  }
                }
              },
              {
                nextId: "8dad356f6e0a479d890326c647dc77cd,450149b8ced943f3849c96caf8bff90d",
                name: "",
                prevId: "c6eeb77f3dc242aebc6ec96852b28ee3",
                type: "parallelism",
                nodeId: "bf1021ab34024051a4994635d1c832bf",
                childNode: {
                  nextId:
                    "e4e6fe160d7d4ed2afc08bd9f07fa3cf,c7203875b9a84d54b955800e311c3e56,0b0acd8add74465eab2d69c088c57ad5",
                  name: "分支2",
                  prevId: "bf1021ab34024051a4994635d1c832bf",
                  type: "route",
                  nodeId: "450149b8ced943f3849c96caf8bff90d",
                  childNode: "",
                  parallelismNodes: [
                    {
                      nextId: "f321be68edbd4880b9e1688c74df7424",
                      name: "",
                      prevId: "450149b8ced943f3849c96caf8bff90d",
                      type: "parallelism",
                      priority: "1.0",
                      nodeId: "e4e6fe160d7d4ed2afc08bd9f07fa3cf",
                      childNode: {
                        nextId: "",
                        auth: [
                          {
                            name: "金额",
                            value: "2",
                            key: "7ba099ea42604ce7a0666e8f447ec38d"
                          },
                          {
                            name: "多行文本",
                            value: "2",
                            key: "b3a8b3894cf042baa744d7efa189592a"
                          }
                        ],
                        name: "审批人2_1",
                        prevId: "e4e6fe160d7d4ed2afc08bd9f07fa3cf",
                        type: "approver",
                        nodeId: "f321be68edbd4880b9e1688c74df7424",
                        properties: {
                          activateType: "ONE_BY_ONE",
                          actionerRules: [
                            {
                              approvals: [],
                              actType: "or",
                              subType: "3",
                              type: "target_select"
                            }
                          ],
                          type: "approver",
                          agreeAll: false
                        },
                        childNode: {
                          nextId: "",
                          auth: [
                            {
                              name: "金额",
                              value: "2",
                              key: "7ba099ea42604ce7a0666e8f447ec38d"
                            },
                            {
                              name: "多行文本",
                              value: "2",
                              key: "b3a8b3894cf042baa744d7efa189592a"
                            }
                          ],
                          name: "审批人2_2",
                          prevId: "bf1021ab34024051a4994635d1c832bf",
                          type: "approver",
                          nodeId: "8dad356f6e0a479d890326c647dc77cd",
                          properties: {
                            activateType: "ONE_BY_ONE",
                            actionerRules: [
                              {
                                approvals: [],
                                actType: "or",
                                subType: "3",
                                type: "target_select"
                              }
                            ],
                            type: "approver",
                            agreeAll: false
                          },
                          childNode: ""
                        }
                      }
                    },
                    {
                      nextId: "3e5d85f5fdc2430abffc68b4b86eeaa8",
                      name: "",
                      prevId: "450149b8ced943f3849c96caf8bff90d",
                      type: "parallelism",
                      nodeId: "c7203875b9a84d54b955800e311c3e56",
                      childNode: {
                        nextId: "",
                        auth: [
                          {
                            name: "金额",
                            value: "2",
                            key: "7ba099ea42604ce7a0666e8f447ec38d"
                          },
                          {
                            name: "多行文本",
                            value: "2",
                            key: "b3a8b3894cf042baa744d7efa189592a"
                          }
                        ],
                        name: "审批人3_1",
                        prevId: "c7203875b9a84d54b955800e311c3e56",
                        type: "approver",
                        nodeId: "3e5d85f5fdc2430abffc68b4b86eeaa8",
                        properties: {
                          activateType: "ONE_BY_ONE",
                          actionerRules: [
                            {
                              approvals: [],
                              actType: "or",
                              subType: "3",
                              type: "target_select"
                            }
                          ],
                          type: "approver",
                          agreeAll: false
                        },
                        childNode: ""
                      }
                    },
                    {
                      nextId: "56b8ea405c6943ca86e79edb904c9a5a",
                      name: "分支3",
                      prevId: "450149b8ced943f3849c96caf8bff90d",
                      type: "parallelism",
                      nodeId: "0b0acd8add74465eab2d69c088c57ad5",
                      childNode: {
                        nextId: "",
                        auth: [
                          {
                            name: "金额",
                            value: "2",
                            key: "7ba099ea42604ce7a0666e8f447ec38d"
                          },
                          {
                            name: "多行文本",
                            value: "2",
                            key: "b3a8b3894cf042baa744d7efa189592a"
                          }
                        ],
                        name: "审批人4_1",
                        prevId: "0b0acd8add74465eab2d69c088c57ad5",
                        type: "approver",
                        nodeId: "56b8ea405c6943ca86e79edb904c9a5a",
                        properties: {
                          activateType: "ONE_BY_ONE",
                          actionerRules: [
                            {
                              approvals: [],
                              actType: "or",
                              subType: "3",
                              type: "target_select"
                            }
                          ],
                          type: "approver",
                          agreeAll: false
                        },
                        childNode: ""
                      }
                    }
                  ]
                }
              }
            ]
          }
        };
      }
    },
    nodeId: {
      default: ""
    },
    applyUser: {
      type: String
    },
    creator: {
      type: String
    },
    instanceId: {
      type: String
    },
    sourceName: {
      default: "todo"
    },
    opinionVisible: {
      type: String,
      default: "0"
    }
  },
  computed: {
    curNodeIDs() {
      if (this.nodeId) {
        return this.nodeId.split(",");
      } else return [];
    }
  },
  mounted() {
    this.getListArr(this.flowList);
  },
  methods: {
    getRuleType(line) {
      if (line.properties && line.properties.actionerRules) return line.properties.actionerRules[0].type;
      else return "";
    },
    getApprovalList(line) {
      if (line.properties && line.properties.actionerRules) return line.properties.actionerRules[0].approvals;
      else return [];
    },
    getNoneActionerAction(line) {
      return line.properties.actionerRules[0].noneActionerAction;
    },
    getAutoPass(line, text) {
      if (line.type == "approver") {
        let approveList = line.properties.actionerRules[0].approvals.length;
        let noneActionerAction = this.getNoneActionerAction(line);
        if (approveList == 0 && noneActionerAction == text) return true;
        else return false;
      } else return false;
    },
    getActType(line, text) {
      if (line.type == "approver") {
        let actT = line.properties.actionerRules[0].actType;
        let approveList = line.properties.actionerRules[0].approvals.length;
        if (actT == text && approveList > 1) return true;
        else return false;
      } else return false;
    },
    getAppproveOriNode(line) {
      if (line.type == "approver") {
        let ruleT = line.properties.actionerRules[0].type;
        if (ruleT == "target_originator") return true;
        else return false;
      } else return false;
    },
    getApproveNode(line) {
      if (line.type == "approver") {
        let ruleT = line.properties.actionerRules[0].type;
        if (ruleT == "target_post" || ruleT == "target_pos" || ruleT == "target_charge") return true;
        else return false;
      } else return false;
    },
    getListArr(flow) {
      this.flowListArr.push({
        nextId: flow.nextId,
        name: flow.name,
        type: flow.type,
        nodeId: flow.nodeId,
        prevId: flow.prevId,
        properties: flow.properties,
        parallelismNodes: flow.parallelismNodes
      });
      if (flow.childNode) this.getListArr(flow.childNode);
    },
    /**
     * @function cbClick
     * @description: 催办接口
     * @param {*}
     */
    cbClick() {
      let par = {
        serviceName: "plugin",
        servicePath: "grid",
        instanceId: this.instanceId
      };
      this.$request(par).then(result => {
        if (result.status == "1") {
          this.$message.success(result.msgInfo);
        } else {
          this.$message.error(result.msgInfo);
        }
      });
    },
    /**
     * @function getIconName
     * @description: 根据节点状态设置回显图标
     * @param {*}
     */
    getIconName(line) {
      if (line.type === "start") return "icon-full-flow-agree";
      if (line.type === "route") return this.getRouteApproveStatus(line);
      let item = this.getApprovalList(line)[0];
      return item.status === "1" && item.nodeId == this.nodeId
        ? "icon-full-flow-doing"
        : item.status === "2"
        ? "icon-full-flow-agree"
        : item.status === "3"
        ? "icon-full-flow-disagree"
        : item.status == "4"
        ? "icon-full-flow-back"
        : item.status == "5"
        ? "icon-full-flow-agree"
        : item.status == "6"
        ? "icon-full-flow-agree"
        : item.status == "7"
        ? "icon-full-flow-share"
        : "";
    },
    // 判断平行分支是否全部审批完成
    getRouteApproveStatus(line) {
      let { poeList, routeNodeId } = this.getRoutePeople(line);
      let status = poeList.map(p => p.status);
      let uniA = Array.from(new Set(status));
      if (uniA.length == 1 && uniA[0] == "2") return "icon-full-flow-agree";
      let svgID = "";
      this.curNodeIDs.forEach(id => {
        if (routeNodeId.indexOf(id) > 0) {
          svgID = "icon-full-flow-doing";
        }
      });
      return svgID;
    },
    /**
     * @function getstatus
     * @description: 回显状态名称, type: 0发起人, 1审批人, 2抄送人
     * @param {*}
     */
    getstatus(line) {
      let item = this.getApprovalList(line)[0];
      //  1: 有效 2: 同意 3. 不同意 4: 退回 5:自动同意 6:自动通过
      return item.status == "1" && line.nodeId == this.nodeId
        ? "(审批中)"
        : item.status == "2"
        ? "(已同意)"
        : item.status == "3"
        ? "(不同意)"
        : item.status == "4"
        ? "(退回)"
        : item.status == "5"
        ? "(自动同意)"
        : item.status == "6"
        ? "(自动通过)"
        : item.status == "7"
        ? "(转审批)"
        : "";
    },
    // 获取平行分支节点的 审批人列表
    getRoutePeople(line) {
      let _this = this;
      let poeList = [];
      let routeNodeId = [];
      line.parallelismNodes.forEach(parall => {
        _this.getChildNodePeo(parall.childNode, poeList, routeNodeId);
      });
      return { poeList, routeNodeId };
    },
    // 对平行分支的一个递归操作
    getChildNodePeo(childNode, poeList, routeNodeId) {
      let _this = this;
      if (childNode.type === "route" && childNode.parallelismNodes) {
        childNode.parallelismNodes.forEach(parall => {
          _this.getChildNodePeo(parall.childNode, poeList, routeNodeId);
        });
      } else {
        let pl = childNode.properties.actionerRules[0].approvals;
        routeNodeId.push(childNode.nodeId);
        pl.forEach(p => {
          poeList.push(p);
        });
      }
      if (childNode.childNode) {
        _this.getChildNodePeo(childNode.childNode, poeList, routeNodeId);
      }
    }
  }
};
</script>

<style lang="scss">
.eg-timeline-parallel-todo {
  .list-node {
    color: rgba(0, 0, 0, 0.9);
    font-size: 12px;
    margin: 0;
  }
  .line-item .ant-timeline-item-head {
    padding: 0 !important;
    .status-icon {
      position: absolute;
      bottom: 0px;
      right: 0px;
    }
    .circle {
      border-radius: 50%;
      border: 1px solid #e8e8e8;
      display: inline-block;
      overflow: hidden;
      width: 32px;
      height: 32px;
      background-color: rgb(63, 143, 255);
      .svg-icon {
        margin-top: 5px;
      }
    }
    .eg-photo {
      border-radius: 50%;
      overflow: hidden;
      display: inline-block;
      border: 1px solid #e8e8e8;
      img {
        width: 100%;
        height: 100%;
        -o-object-fit: cover;
        object-fit: cover;
        border-radius: 50%;
        border: 1px solid #e8e8e8;
      }
    }
  }
  .ant-timeline-item {
    padding-bottom: $spacing-xxl !important;
  }

  & > .line-item {
    & > .ant-timeline-item-content {
      left: 8px;
      margin: 0 0 0 22px;
      top: -15px;

      .line-right {
        width: 100%;
        display: inline-block;
        .eg-head-group {
          margin-top: 13px;
        }
        .parallel-node {
          background: #f5f5f5;
          border-radius: 4px;
        }
        .ant-btn {
          vertical-align: top;
          margin-left: 12px;
          margin-right: 12px;
        }
      }
    }
  }
}
</style>

ParallelNode:
<template>
  <div class="parallel-node">
    <!-- 面包屑 -->
    <eg-breadcrumb
      :breadcrumbDatas="breadcrumbDatas"
      :attrObject="attrObject"
      @breadcrumbItemClick="breadcrumbItemClick"
    >
      <span slot="separator">></span>
    </eg-breadcrumb>
    <!-- 平行分支几点展示 -->
    <div style="display: flex; margin-top: 4px;">
      <div class="parallel-line" v-for="parallel in renderLine.parallelismNodes" :key="parallel.nodeId">
        <ParallelTimeLine
          :source="'todo'"
          :curNodeIDs="curNodeIDs"
          :parallelNode="parallel.childNode"
          @route-node="routeNode"
          @select-person="selectPerson"
        ></ParallelTimeLine>
      </div>
    </div>
  </div>
</template>
<script>
/**
 * @module ParallelNode
 * @author 何志强
 * @description 流程平行分支节点
 */
import ParallelTimeLine from "./ParallelTimeLine.vue";
export default {
  components: { ParallelTimeLine },
  name: "ParallelNode",
  data() {
    return {
      attrObject: {
        separatorSlot: true,
        itemSlot: false
      },
      breadcrumbDatas: [],
      renderLine: null,
      poeList: []
    };
  },
  /**
   * Props 接受父组件的传值 重要参数
   * @prop {Object} line 流程节点数据源
   */
  props: {
    line: {
      type: Object
    },
    source: {
      type: String,
      default: "applicate"
    },
    curNodeIDs: Array
  },
  watch: {
    line: {
      deep: true,
      immediate: true,
      handler(va) {
        if (va) {
          if (this.breadcrumbDatas.length == 0) {
            this.renderLine = va;
            this.breadcrumbDatas = [];
            this.breadcrumbDatas.push({
              name: `${this.line.name}(${this.line.parallelismNodes.length}条)`,
              nodeId: this.line.nodeId,
              routeNode: va
            });
          }
        }
      }
    }
  },
  computed: {},
  mounted() {},
  methods: {
    getRoutePeople() {
      this.poeList = [];
      let _this = this;
      this.line.parallelismNodes.forEach(parall => {
        _this.getChildNodePeo(parall.childNode);
      });
      return this.poeList;
    },
    getChildNodePeo(childNode) {
      let _this = this;
      if (childNode.type === "route" && childNode.parallelismNodes) {
        childNode.parallelismNodes.forEach(parall => {
          _this.getChildNodePeo(parall.childNode);
        });
      } else {
        _this.poeList = [
          ..._this.poeList,
          { nodeId: childNode.nodeId, type: _this.getRuleType(childNode), approvals: _this.getApprovalList(childNode) }
        ];
      }
      if (childNode.childNode) {
        _this.getChildNodePeo(childNode.childNode);
      }
    },
    // 自身后面的面包屑 清除
    breadcrumbItemClick(bread, index) {
      this.breadcrumbDatas = this.breadcrumbDatas.filter((item, findex) => findex <= index);
      this.renderLine = bread.routeNode;
    },
    getRuleType(line) {
      if (line.properties && line.properties.actionerRules) return line.properties.actionerRules[0].type;
      else return "";
    },
    getApprovalList(line) {
      if (line.properties && line.properties.actionerRules) return line.properties.actionerRules[0].approvals;
      else return [];
    },
    getAutoPass(line, text) {
      if (line.type == "approver") {
        line.approvalList.length == 0 && line.noneActionerAction == "admin";
        let approveList = line.properties.actionerRules[0].approvals.length;
        if (approveList == 0 && line.noneActionerAction == text) return true;
        else return false;
      } else return false;
    },
    getActType(line) {
      let act = line.properties.actionerRules[0].actType;
      if (act == "or") return "或签";
      else return "会签";
    },
    getAppproveOriNode(line) {
      if (line.type == "approver") {
        let ruleT = line.properties.actionerRules[0].type;
        if (ruleT == "target_originator") {
          return true;
        } else return false;
      } else return false;
    },
    getApproveNode(line) {
      if (line.type == "approver") {
        let ruleT = line.properties.actionerRules[0].type;
        if (ruleT == "target_post" || ruleT == "target_pos" || ruleT == "target_charge") {
          return true;
        } else return false;
      } else return false;
    },
    routeNode(routeNode) {
      this.renderLine = routeNode;
      this.breadcrumbDatas.push({
        name: `${routeNode.name}(${routeNode.parallelismNodes.length}条)`,
        nodeId: routeNode.nodeId,
        routeNode: routeNode
      });
    },
    selectPerson(approveNode, cb) {
      this.$emit("select-person", approveNode, cb);
    }
  }
};
</script>

<style lang="scss">
.parallel-node {
  padding: 4px 0 8px 8px;
  .parallel-line {
    width: 174px;
    background: #ffffff;
    border-radius: 4px;
    margin-right: 8px;
    // padding: 8px;
    padding: 12px 8px 0 8px;
    .ant-timeline-item {
      padding-bottom: 8px !important;
    }
    .ant-timeline-item-last {
      padding-bottom: 0px !important;
    }
    .ant-timeline-item-content {
      min-height: 24px;
    }
  }
  .eg-breadcrumb .ant-breadcrumb > span:last-child {
    font-size: 12px !important;
    color: rgba(0, 0, 0, 0.9);
    font-weight: 600;
    line-height: 20px !important;
  }
  .eg-breadcrumb {
    .ant-breadcrumb {
      .ant-breadcrumb-link {
        font-size: 12px;
        font-weight: 400;
        line-height: 20px;
      }
    }
    .ant-breadcrumb-separator {
      font-size: 14px;
      line-height: 20px;
    }
  }
}
</style>

EgTimelineInner:
<template>
  <div class="eg-timeline-inner">
    <a-timeline class="eg-time-line-inner">
      <a-timeline-item class="line-item" v-for="(line, index) in manyPeoFlowList" :key="index">
        <div class="line-right">
          <!-- 节点名称 -->
          <p class="list-node">
            {{ line.name || "" }}
          </p>
          <!-- 单人 显示名称 -->
          <p class="list-node-name" v-if="line.approvals.length == 1">
            {{ line.approvals[0].RY_JBXX__NAME }}
            <span>{{ getstatus(line) }} </span>
          </p>
          <!-- 时间 -->
          <p class="node-handle-time" v-if="line.status != '1'">{{ line.handleTime }}</p>
          <span v-if="line.type == 'target_post' || line.type == 'target_pos' || line.type == 'target_charge'">
            <span v-if="line.approvals.length == 0 && line.noneActionerAction == 'autoPass'">(自动通过)</span>
            <span v-if="line.approvals.length == 0 && line.noneActionerAction == 'admin'">(转交管理员)</span>
          </span>
          <!-- <span v-if="line.status == '7'">(转审批)</span> -->
          <span v-if="line.activateType == 'or' && line.approvals.length > 1"
            >{{ line.approvals.length + "人或签" }}
          </span>
          <span v-if="line.activateType == 'and' && line.approvals.length > 1"
            >{{ line.approvals.length + "人会签" }}
          </span>
          <span v-if="line.type == 'target_originator'">(发起人自己)</span>

          <!-- // 多人的时候, 显示头像 -->
          <p v-if="line.approvals.length > 1">
            <span class="list-persin" v-for="ava in line.approvals" :key="ava.RY_JBXX__PERSONID">
              <span class="circle" v-if="ava.RY_JBXX__PHOTO">
                <img :src="'api/thumbnail/' + ava.RY_JBXX__PHOTO + '?' + token" :onerror="defaultImg" alt />
              </span>
              <span class="circle" v-else>
                <img v-if="ava.RY_JBXX__GENDER !== '1'" src="../../commonImg/female.png" alt />
                <img v-else-if="ava.RY_JBXX__GENDER !== '2'" src="../../commonImg/male.png" alt />
                <img v-else src="../../commonImg/default-emp.png" />
              </span>
              <p class="list-name">{{ ava.RY_JBXX__NAME }}</p>
            </span>
          </p>

          <!-- 审批意见 -->
          <p class="node-opinion" v-if="line.handleOpinion && sourceName == 'todo'">
            {{ line.handleOpinion }}
          </p>
        </div>
        <div slot="dot">
          <!-- 单人头像 -->
          <template v-if="line.approvals.length <= 1">
            <span class="circle" v-if="line.approvals[0] && line.approvals[0].RY_JBXX__PHOTO">
              <img :src="'api/thumbnail/' + line.approvals[0].RY_JBXX__PHOTO + '?' + token" :onerror="defaultImg" alt />
            </span>
            <span class="circle" v-else>
              <img
                v-if="line.approvals[0] && line.approvals[0].RY_JBXX__GENDER !== '1'"
                src="../../commonImg/female.png"
                alt
              />
              <img
                v-else-if="line.approvals[0] && line.approvals[0].RY_JBXX__GENDER !== '2'"
                src="../../commonImg/male.png"
                alt
              />
              <img v-else src="../../commonImg/default-emp.png" />
            </span>
          </template>
          <!-- 多人头像 -->
          <template v-else>
            <span class="circle">
              <eg-svg
                :svgId="'icon_manypeople'"
                :width="32"
                :height="32"
                :color="'#ffffff'"
                :backgroundColor="'#3F8FFF'"
                :verticalAlign="'-2px'"
                :mright="0"
              />
            </span>
          </template>
          <!-- 状态图标 -->
          <eg-svg
            :svgId="getIconName(line)"
            :backgroundColor="'transparent'"
            :width="14"
            :height="14"
            class="status-icon"
          />
        </div>
      </a-timeline-item>
    </a-timeline>
  </div>
</template>
<script>
/**
 * @module EgTimelineInner
 * @author 何志强
 * @description 代办流程节点展示, 自带业务功能催办
 */
export default {
  name: "EgTimelineInner",
  data() {
    return {
      attrObject: {
        type: "link",
        size: "small",
        text: "催办"
      },
      token: sessionStorage.getItem("Authorization"),
      userId: "83133C71402F45D7A64DE68D3D1064CD",
      defaultImg: 'this.src="' + require("../../commonImg/loading-failed-img.png") + '"' //默认图地址
    };
  },
  computed: {
    manyPeoFlowList() {
      let arr = new Array();
      let obj = {
        name: this.flowNode.name,
        nodeId: this.flowNode.nodeId,
        // type: this.flowNode.type,
        activateType: this.flowNode.properties.actionerRules[0].actType,
        agreeAll: this.flowNode.properties.agreeAll
      };

      let approved = this.flowNode.properties.actionerRules[0].approvals.filter(item => item.status !== "1");
      let noneApprove = this.flowNode.properties.actionerRules[0].approvals.filter(item => item.status == "1");
      approved.forEach(element => {
        let O = {
          // 代办有审批意见, 需要按照时间排序
          status: element.status,
          handleOpinion: element.handleOpinion,
          handleTime: element.handleTime,
          type: this.flowNode.properties.actionerRules[0].type,
          approvals: [element],
          ...obj
        };
        arr.push(O);
      });
      arr = this.handleTimeSort(arr, "handleTime");
      if (noneApprove.length > 0) {
        arr.push({
          // 代办有审批意见
          status: "1",
          handleOpinion: "",
          handleTime: "",
          type: this.flowNode.properties.actionerRules[0].type,
          approvals: noneApprove,
          ...obj
        });
      }
      return arr;
    }
  },
  /**
   * Props 接受父组件的传值 重要参数
   * @prop {String} curNodeIDs 当前审批中节点id
   * @prop {String} applyUser 申请人, 原始发起人
   * @prop {String} creator 代理的发起人
   * @prop {Object} flowNode 流程节点数据源
   * @prop {String} instanceId 催办接口使用
   * @prop {String} sourceName 组件引用来源 可为 todo(代办)、application(申请)  默认 todo
   * @prop {String} opinionVisible 审批意见是否显示 (为0 且当 sourceName == application) 时 或者 (sourceName == todo)时 显示
   */
  props: {
    applyUser: {
      type: String
    },
    creator: {
      type: String
    },
    flowNode: {
      type: Object
    },
    instanceId: {
      type: String
    },
    sourceName: {
      default: "todo"
    },
    opinionVisible: {
      type: String,
      default: "0"
    },
    curNodeIDs: Array
  },
  methods: {
    // 根据时间排序
    handleTimeSort(arr, name) {
      arr.sort((a, b) => {
        let t1 = new Date(a[name]);
        let t2 = new Date(b[name]);
        return t2.getTime() - t1.getTime();
      });
      return arr;
    },
    /**
     * @function getIconBgColor
     * @description: 设置icon 颜色 弃用
     * @param {*}
     */
    getIconBgColor(item) {
      return item.status === "1" && this.curNodeIDs.includes(item.nodeId)
        ? "#F5A60A"
        : item.status === "2"
        ? "#1FC48D"
        : item.status === "3"
        ? "#F53F2E"
        : item.status == "4"
        ? "#7C00F0"
        : item.status == "5"
        ? "#1FC48D"
        : item.status == "6"
        ? "#1FC48D"
        : "";
    },
    /**
     * @function getIconName
     * @description: 根据节点状态设置回显图标
     * @param {*}
     */
    getIconName(item) {
      return item.status === "1" && this.curNodeIDs.includes(item.nodeId)
        ? "icon-full-flow-doing"
        : item.status === "2"
        ? "icon-full-flow-agree"
        : item.status === "3"
        ? "icon-full-flow-disagree"
        : item.status == "4"
        ? "icon-full-flow-back"
        : item.status == "5"
        ? "icon-full-flow-agree"
        : item.status == "6"
        ? "icon-full-flow-agree"
        : item.status == "7"
        ? "icon-full-flow-share"
        : "";
    },
    /**
     * @function getstatus
     * @description: 回显状态名称, type: start发起人, approver审批人, notifier抄送人
     * @param {*}
     */
    getstatus(item) {
      //  1: 有效 2: 同意 3. 不同意 4: 退回 5:自动同意 6:自动通过
      return item.status == "1" && this.curNodeIDs.includes(item.nodeId)
        ? "(审批中)"
        : item.status == "2" && item.type != "0"
        ? "(已同意)"
        : item.status == "3"
        ? "(不同意)"
        : item.status == "4"
        ? "(退回)"
        : item.status == "5"
        ? "(自动同意)"
        : item.status == "6"
        ? "(自动通过)"
        : item.status == "7"
        ? "(转审批)"
        : "";
    }
  }
};
</script>
<style lang="scss">
.eg-timeline-inner {
  .ant-timeline-item-last {
    .ant-timeline-item-content {
      padding-bottom: 0px !important;
      margin-left: 24px !important;
    }
  }
  .line-item {
    padding: 0px;
    .circle {
      width: 32px;
      height: 32px;
      border-radius: 50%;
      overflow: hidden;
      display: inline-block;
      border: 1px solid #e8e8e8;
      img {
        width: 100%;
        height: 100%;
        object-fit: cover;
      }
    }
    .ant-timeline-item-content {
      left: $spacing-m;
      margin: 0 0 0 22px;
      padding-bottom: 36px;
      top: -15px;
      .line-right {
        .circle {
          margin-left: 12px;
          margin-right: 12px;
        }
        .list-node {
          margin-bottom: 0;
        }
        .list-node-name {
          margin-bottom: $spacing-m;
        }
        .list-node {
          @include text-02;
        }
        .list-node-name {
          @include explain-01;
        }
        .node-handle-time {
          top: 0;
          position: absolute;
          right: 0;
          @include explain-03;
        }
        .node-opinion {
          @include text-02;
          padding: $spacing-m $spacing-l;
          background-color: $bg-light;
          margin: 0;
        }
        .list-persin {
          display: inline-block;
          height: 32px;
          .list-name {
            text-align: center;
          }
        }
      }
    }
    .ant-timeline-item-head {
      padding: 0 !important;
      .status-icon {
        position: absolute;
        bottom: 0px;
        right: 0px;
      }
    }
  }
}
</style>

ParallelTimeLine:
<template>
  <a-timeline>
    <a-timeline-item v-for="(node, index) in childNodes" :key="index" :color="colorText(node)">
      <!-- 分支节点 -->
      <div class="line-content-flex" v-if="node.type == 'route'">
        <div>{{ node.name }}</div>
        <div class="node-people-num-text" @click="getNestNode(node)">
          {{ node.parallelismNodes.length + "条" }} <span class="arrow-icon">></span>
        </div>
      </div>
      <div class="line-content-flex" v-else>
        <div>{{ node.name }}</div>
        <!-- 多人 -->
        <!-- //  1: 有效 2: 同意 3. 不同意 4: 退回 5:自动同意 6:自动通过 -->
        <div class="line-content-right" v-if="getApprovalList(node).length > 1">
          <!-- 不在当前审批节点范围 -->
          <div class="line-content-right many-people-underline" v-if="!curNodeIDs.includes(node.nodeId)">
            <!-- 显示会签或签 -->
            {{ `${getApprovalList(node).length}人${getActType(node)}` }}
          </div>
          <!-- 多人 会签, 要判断每一个人的审批状态 -->
          <eg-popover overlayClassName="time-line-simple" v-else>
            <template slot="popoverContent">
              <div style="margin-bottom: 24px;">
                {{ `${node.name}(${getApprovalList(node).length}人${getActType(node)})` }}
              </div>
              <EgTimelineInner :flowNode="node" :sourceName="'todo'" :curNodeIDs="curNodeIDs" />
            </template>
            <div class="line-content-right many-people-underline" slot="main">
              <!-- 显示会签或签 -->
              {{ `${getApprovalList(node).length}人${getActType(node)}` }}
            </div>
          </eg-popover>
        </div>
        <!-- 单人 -->
        <template v-if="getApprovalList(node).length == 1">
          <eg-popover v-if="getApprovalList(node)[0].status !== '1'" overlayClassName="time-line-simple">
            <template slot="popoverContent">
              <div class="popover-left-name">
                审批状态 <span class="popover-right-text">{{ getstatus(node) }}</span>
              </div>
              <div class="popover-left-name">
                审批意见 <span class="popover-right-text">{{ getApprovalList(node)[0].handleOpinion }}</span>
              </div>
              <div class="popover-left-name">
                审批时间 <span class="popover-right-text">{{ getApprovalList(node)[0].handleTime }}</span>
              </div>
            </template>
            <div class="line-content-right" slot="main">
              {{ getApprovalList(node)[0].RY_JBXX__NAME }}
            </div>
          </eg-popover>
          <div v-else class="line-content-right">
            {{ getApprovalList(node)[0].RY_JBXX__NAME }}
          </div>
        </template>
      </div>
    </a-timeline-item>
  </a-timeline>
</template>
<script>
import EgTimelineInner from "./EgTimelineInner.vue";
/**
 * @module ParallelTimeLine
 * @author 何志强
 * @description 流程平行分支节点
 */
export default {
  name: "ParallelTimeLine",
  components: { EgTimelineInner },
  data() {
    return {
      childNodes: []
    };
  },
  /**
   * Props 接受父组件的传值 重要参数
   * @prop {Object} line 流程节点数据源
   */
  props: {
    parallelNode: {
      type: Object
    },
    source: {
      type: String,
      default: "applicate"
    },
    curNodeIDs: {
      type: Array
    }
  },
  watch: {
    parallelNode: {
      deep: true,
      immediate: true,
      handler(va) {
        if (va) {
          this.childNodes = [];
          this.getListArr(va);
        }
      }
    }
  },
  computed: {},
  mounted() {},
  methods: {
    //  <!-- //  1: 有效 2: 同意 3. 不同意 4: 退回 5:自动同意 6:自动通过 -->
    // 节点圆圈的 颜色, 根据 status 是否等于 1 未审批, 或者2 审批通过, 当前节点, 会签是否全部完成等判断审批节点颜色
    // #EAA83E 当前审批节点 黄色
    // "#1890ff" 默认节点颜色 蓝色
    // #5CC190 审批通过颜色 绿色
    colorText(node) {
      let approvals = this.getApprovalList(node);
      if (node.type == "route" && approvals.length == 0) {
        let colorArr = [];
        let filteredArr = [];
        node.parallelismNodes.forEach(item => {
          colorArr.push(this.handlerApprovals(item.childNode, this.getApprovalList(item.childNode)));
        });
        filteredArr = [...new Set(colorArr)];
        return filteredArr.length == 1 ? filteredArr[0] : "#EAA83E";
      } else {
        return this.handlerApprovals(node, approvals);
      }
    },
    handlerApprovals(node, approvals) {
      if (approvals && approvals.length == 1) {
        if (approvals[0].status == 1) {
          if (this.curNodeIDs.includes(node.nodeId)) return "#EAA83E";
          else return "#1890ff";
        } else if (approvals[0].status == 2) return "#5CC190";
        else return "#1890ff";
      } else if (approvals.length > 1) {
        if (this.getActType(node) == "会签") {
          let arr = approvals.map(item => item.status);
          // 存在未审批
          if (arr.includes("1")) {
            if (this.curNodeIDs.includes(node.nodeId)) return "#EAA83E";
            else return "#1890ff";
          } else {
            let narr = [...new Set(arr)];
            if (narr[0] == "2" && narr.length == 1) return "#5CC190";
            else return "#1890ff";
          }
        } else {
          // 或签
          if (this.curNodeIDs.includes(node.nodeId)) return "#EAA83E";
          else return "#1890ff";
        }
      }
      return "#1890ff";
    },
    // 判断会签是否全部通过
    checkAnd(node) {
      let approvals = this.getApprovalList(node);
      if (this.getActType(node) == "会签") {
        let arr = approvals.map(item => item.status);
        // 存在未审批
        if (arr.includes("1")) {
          return false;
        } else {
          // 有过审批, 可以是拒绝 通过等
          return true;
        }
      }
    },
    // 自身后面的面包屑 清除
    breadcrumbItemClick(bread) {
      console.log(bread);
    },
    getRuleType(line) {
      if (line.properties && line.properties.actionerRules) return line.properties.actionerRules[0].type;
      else return "";
    },
    getApprovalList(line) {
      if (line.properties && line.properties.actionerRules) return line.properties.actionerRules[0].approvals;
      else return [];
    },
    getAutoPass(line, text) {
      if (line.type == "approver") {
        line.approvalList.length == 0 && line.noneActionerAction == "admin";
        let approveList = line.properties.actionerRules[0].approvals.length;
        if (approveList == 0 && line.noneActionerAction == text) return true;
        else return false;
      } else return false;
    },
    getActType(line) {
      let act = line.properties.actionerRules[0].actType;
      if (act == "or") return "或签";
      else return "会签";
    },
    getAppproveOriNode(line) {
      if (line.type == "approver") {
        let ruleT = line.properties.actionerRules[0].type;
        if (ruleT == "target_originator") {
          return true;
        } else return false;
      } else return false;
    },
    getApproveNode(line) {
      if (line.type == "approver") {
        let ruleT = line.properties.actionerRules[0].type;
        if (ruleT == "target_post" || ruleT == "target_pos" || ruleT == "target_charge") {
          return true;
        } else return false;
      } else return false;
    },
    getListArr(flow) {
      this.childNodes.push({
        nextId: flow.nextId,
        name: flow.name,
        type: flow.type,
        nodeId: flow.nodeId,
        prevId: flow.prevId,
        properties: flow.properties,
        parallelismNodes: flow.parallelismNodes
      });
      if (flow.childNode) {
        this.getListArr(flow.childNode);
      }
    },
    getNestNode(routeNode) {
      this.$emit("route-node", routeNode);
    },
    selectPerson(approveNode) {
      if (this.source == "todo") return false;
      this.$emit("select-person", approveNode, approvals => {
        approveNode.properties.actionerRules[0].approvals = approvals;
      });
    },
    getstatus(line) {
      let item = this.getApprovalList(line)[0];
      //  1: 有效 2: 同意 3. 不同意 4: 退回 5:自动同意 6:自动通过
      return item.status == "1" && line.nodeId == this.nodeId
        ? "审批中"
        : item.status == "2" && item.type != "0"
        ? "已同意"
        : item.status == "3"
        ? "不同意"
        : item.status == "4"
        ? "退回"
        : item.status == "5"
        ? "自动同意"
        : item.status == "6"
        ? "自动通过"
        : item.status == "7"
        ? "转审批"
        : "";
    }
  }
};
</script>

<style lang="scss">
.time-line-simple {
  min-width: 240px;
  .popover-left-name {
    color: #191919;
    font-size: 14px;
    line-height: 22px;
    margin-bottom: 8px;
  }
  .popover-right-text {
    display: inline-block;
    width: 207px;
    margin-left: 24px;
    color: #4c4c4c;
  }
}
.line-content-flex {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  line-height: 24px;
  .line-content-right {
    cursor: pointer;
    &:hover {
      color: #3f8fff;
    }
  }
  .select-people-tips {
    color: #eaa83e;
  }
  .node-people-num-text {
    font-size: 12px;
    color: #3f8fff;
    line-height: 20px;
    cursor: pointer;
    .arrow-icon {
      color: #7f7f7f;
    }
  }
}
</style>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值