vue3 html版本的审批流状态回显

414 阅读1分钟

1.上图

image.png

2.代码

  <div class="content">
    <div class="box start"></div>
    <div class="task-box">
      <template v-for="(item, index) in data" :key="index">
        <div
          :class="[
            'col',
            { center: item.length == 1 || item.length < colMaxLen,
            'col-green': item.find(i=>i.status == 1), 'col-red': item.every(i=>i.status == 2),
            'col1': index == 0 },
          ]"
        >
          <template v-for="(item2, index2) in item" :key="index2">
            <div
              class="item"
              :class="{
                audit: index == 0,
                approval: index == 1,
                green: item2.status == 1,
                red: item2.status == 2,
              }"
            >
              <div class="item-c task-name">{{ item2.taskName }}</div>
              <div class="item-c user">
                <h4>张三</h4>
                <div>{{ statusMap[item2.status] }}</div>
              </div>
              <span class="status">{{ statusMap[item2.status] }}</span>
              <span class="time">2022-07-26 11:09:27</span>
              <template v-if="!((item.length - 1) / 2 == index2)">
                <span
                  :class="[
                    'left-line',
                    {
                      'is-half': isHalf(item, index2),
                      'right-line_top': index2 < (item.length - 1) / 2,
                      'right-line_bot': index2 > (item.length - 1) / 2,
                    },
                  ]"
                ></span>
                <span
                  :class="[
                    'right-line',
                    {
                      'is-half': isHalf(item, index2),
                      'must-green': mustGreen(item, index2),
                      'right-line_top': index2 < (item.length - 1) / 2,
                      'right-line_bot': index2 > (item.length - 1) / 2,
                    },
                  ]"
                ></span>
              </template>
            </div>
          </template>
        </div>
        <div
          class="mid-line"
          :class="{'mid-green': item.find(i=>i.status == 1), 'mid-red': item.every(i=>i.status == 2)}"
          v-if="item.length > 1 && data[index + 1] && data[index + 1].length > 1"
        ></div>
      </template>
    </div>
    <div class="box end" :class="[getEndStatus(data)]"></div>
  </div>
</template>
<script setup lang="ts">
// @ts-nocheck
import { ref, computed } from "vue";
interface IProps {
  statusMap?: {[key:string]: any};
  data?: any[]; 
}

const props = withDefaults(defineProps<IProps>(),{
  statusMap: ()=>({
    0: "待处理",
    1: "同意",
    2: "不同意",
  }),
  data: ()=>[]
});
const firstGreenIndex = (arr: any[]) => {
  return arr.findIndex((i) => i.status == 1);
};

const lastGreenIndex = (arr: any[]) => {
  return arr.findLastIndex((i) => i.status == 1);
};

//是否是偶数 切是中间两个
const isHalf = (arr: any[], index: number) => {
  if (arr.length % 2 !== 0) return false;
  let index1 = arr.length / 2;
  let index2 = index1 - 1;
  return index == index1 || index == index2;
};

const mustGreen = (arr: any[], index: number) => {
  const fI = firstGreenIndex(arr);
  const lI = lastGreenIndex(arr);
  let result1 = fI != -1 && index >= fI && index < (arr.length - 1) / 2;
  let result2 = lI != -1 && index <= lI && index > (arr.length - 1) / 2;
  if (result1 || result2) {
    return "must-green";
  } else {
    return "";
  }
};

const getEndStatus = (arr: any)=>{
  if(!arr.length) return '';
  let lastArr = arr[arr.length-1];
  let hasGreen = lastArr.find((i:any)=>i.status == 1);
  if(hasGreen) {
    return 'green';
  }
  let allRed = lastArr.every((i:any)=>i.status == 2);
  if(allRed) {
    return 'red';
  }
}

const colMaxLen = computed(()=>{
  let max = 0;
  props.data.forEach((i:any) => {
    if(i.length > max) {
      max = i.length;
    }
  });
  return max;
})
</script>
<style lang="scss" scoped>
* {
  margin: 0;
  padding: 0;
}
$itemLen: 50px;
$rightLineLen: 100px;
$leftLineLen: 40px;
$startEndLineLen: 30px;
.content {
  display: flex;
  align-items: center;
  min-height: 400px;
  margin-left: 50px;
}

.task-box {
  display: flex;
  .mid-line {
    width: 20px;
    position: relative;
    &::before {
      content: "";
      display: block;
      width: 100%;
      height: 1px;
      position: absolute;
      left: 0;
      top: 50%;
      background: #1177e5;
    }
    &.mid-green {
      &::before {
        background: #03d94f;
      }
      & + .col {
        .left-line {
          background: #03d94f;
        }

        .item::before {
          background: #03d94f;
        }
      }
    }
    &.mid-red {
      &::before {
        background: red;
      }
      & + .col {
        .col-left {
          &::before {
            background: red;
          }
          &::after {
            background: red;
          }
        }

        .item::before {
          background: red;
        }
      }
    }
  }
  
  .col {
    padding: 0 $rightLineLen 0 $leftLineLen;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    position: relative;
    &.center {
      justify-content: center;
    }
    &.col1 {
      .left-line,.item::before {
        background: #03d94f;
      }
    }
  }
  .col-green + .col {
    .left-line {
      background: #03d94f;
    }

    .item::before {
      background: #03d94f;
    }
  }
  .col-red + .col {
    .left-line {
      background: red;
    }

    .item::before {
      background: red;
    }
  }
}

.audit {
  background: #03d94f;
}

.approval {
  background: #1177e5;
}

.item {
  width: calc($itemLen * 2);
  background: #03d94f;
  height: $itemLen;
  position: relative;
  display: flex;
  border: 1px solid #1177e5;
  border-radius: 4px;
  .item-c {
    flex: 1;
    width: 0;
    &.user {
      background: #fff;
      border-top-right-radius: 4px;
      border-bottom-right-radius: 4px;
    }
  }
  .status {
    position: absolute;
    display: block;
    padding: 0 5px;
    border: 1px solid #1177e5;
    border-radius: 4px;
    background: #fff;
    left: 100%;
    top: 50%;
    white-space: nowrap;
    transform: translate3d(20px, -50%, 0);
    z-index: 9;
  }
  .time {
    position: absolute;
    left: 50%;
    top: 100%;
    transform: translateX(-50%);
    white-space: nowrap;
  }
  .left-line {
    width: 1px;
    height: calc($itemLen + 40px + 2px);
    position: absolute;
    left: - calc($leftLineLen + 1px);
    background-color: #1177e5;
    &.is-half {
      height: calc(($itemLen + 40px + 2px) / 2);
    }
    &.right-line_top {
      top: calc($itemLen / 2);
    }

    &.right-line_bot {
      bottom: calc($itemLen / 2);
    }
  }
  .right-line {
    width: 1px;
    height: calc($itemLen + 40px + 2px);
    position: absolute;
    left: calc(100% + $rightLineLen);
    background-color: #1177e5;
    &.is-half {
      height: calc(($itemLen + 40px + 2px) / 2);
    }
    &.right-line_top {
      top: calc($itemLen / 2);
    }

    &.right-line_bot {
      bottom: calc($itemLen / 2);
    }
    &.must-green {
      background: #03d94f !important;
    }
  }
  & + .item {
    margin-top: 40px;
  }
  &::before {
    content: "";
    display: block;
    position: absolute;
    right: calc(100% + 1px);
    top: 50%;
    width: $leftLineLen;
    height: 1px;
    background: #1177e5;
  }
  &::after {
    content: "";
    display: block;
    position: absolute;
    left: calc(100% + 1px);
    top: 50%;
    width: $rightLineLen;
    height: 1px;
    background: #1177e5;
  }
  // 处理状态 颜色
  &.green {
    &::after {
      background: #03d94f;
    }
    .status {
      color: #03d94f;
      border-color: #03d94f;
    }
    .right-line {
      background: #03d94f;
    }
  }
  &.red {
    &::after {
      background: red;
    }
    .status {
      color: red;
      border-color: red;
    }
    .right-line {
      background: red;
    }
  }
}
.start {
  width: $itemLen;
  height: $itemLen;
  border-radius: 4px;
  // background: url("~@assets/img/bpmn/start.png") no-repeat left top /100%;
  background: red;
  margin-right: $startEndLineLen;
  position: relative;
  &::after {
    content: "";
    display: block;
    height: 1px;
    width: $startEndLineLen;
    position: absolute;
    left: 100%;
    top: 50%;
    background: #03d94f;
  }
}

.end {
  width: $itemLen;
  height: $itemLen;
  background: red;
  border-radius: 4px;
  margin-left: $startEndLineLen;
  position: relative;
  &::before {
    content: "";
    display: block;
    height: 1px;
    width: $startEndLineLen;
    position: absolute;
    right: 100%;
    top: 50%;
    background: #1177e5;
  }
  &.green {
    &::before {
      background: #03d94f;
    }
  }
  &.red {
    &::before {
      background: red;
    }
  }
  // background: url("~@assets/img/bpmn/end.png") no-repeat left top /100%;
}
</style>