Newer
Older
smart-metering-front / src / views / measure / bench / bench.vue
dutingting on 27 Feb 17 KB 需求更改、excel在线编辑
<script lang="ts" setup name="MeasureBench">
import { getCurrentInstance, ref } from 'vue'
import type { Ref } from 'vue'
import dayjs from 'dayjs'
import { stringLiteral } from '@babel/types'
import type { ICertificatReturn, IPlanReturn, IValidReturn } from './bench-interface'
import { getCertificateStatistic, getStaffSStatistic, getStatistic, getTrainLogSStatistic } from '@/api/system/bench'
import { getPlanList } from '@/api/measure/plan'
import { listPageApi } from '@/api/measure/file'
import BenchCol from '@/components/benchCol/index.vue'
import type { lineDataI, pieDataI } from '@/components/Echart/echart-interface'
import { SCHEDULE } from '@/utils/scheduleDict'
import useUserStore from '@/store/modules/user'
const { proxy } = getCurrentInstance() as any

// 每个展示块高度
const blockHeight = ref(300)
const blockWidth = ref(400)
const { username } = useUserStore() // 账户
const buttomTypes = ref([
  { id: '1', text: '培训记录', url: '/train/trainLog' },
  { id: '2', text: '证书到期提醒', url: '/person/remind' },
  { id: '3', text: '溯源供方', url: '/source/list' },
  { id: '4', text: '实验室', url: '/measureDept/ks' },
  { id: '5', text: '工程组', url: '/measureDept/gcz' },
  { id: '6', text: '计量人员', url: '/person/list' },
]) // 按钮组
const tableData = ref([]) // 培训计划表格数据
const meterAdmin = ref(false) // 是否计量负责人
const meterageTableData = ref([]) // 计量人间表格数据
const columns = ref([
  { text: '培训名称', value: 'planName' },
  { text: '负责人', value: 'director', width: '120' },
  { text: '培训时间', value: 'trainTime', width: '180' },
])// 培训计划
const meterageColumns = ref([
  { text: '文件名称', value: 'fileName' },
  { text: '类别', value: 'fileTypeName', width: '110' },
  { text: '发布时间', value: 'publishTime', width: '180' },
])// 计量文件
const CertificateColumns = ref([
  { text: '证书名称', value: 'certificateName' },
  { text: '到期时间', value: 'validDate', width: '180' },
])// 我的证书
const CertificatesColumns = ref([
  { text: '证书名称', value: 'certificateName' },
  { text: '人员名称', value: 'name', width: '110' },
  { text: '到期时间', value: 'validDate', width: '180' },
])// 证书预警
const StatisticxAxis: Ref<string[]> = ref([]) // 培训计划x轴
const StatisticData: Ref<lineDataI[]> = ref([]) // 培训计划数据
const StaffSStatisticData: Ref<lineDataI[]> = ref([]) // 计量人员数据
const maxStaffCount = ref(10) // 计量人员最大值
const StaffSStatisticxAxis: Ref<string[]> = ref([]) // 计量人员x轴
const TrainLogSList: Ref<pieDataI[]> = ref([]) // 培训考核合格率以及不合格率
const TrainLogTitle = ref(0) // 培训考核饼图中间的考核人次
const CertificateList = ref([]) // 证书列表表格数据

const CertificateObject = ref<IValidReturn>({
  certificateName:	'', // certificateName
  count:	'', // 人数
  date:	'', // 时间
  lastValidDate:	0, // 最近到期时间
  name:	'', // 人员名称
  notQualified:	0, // 不合格
  qualified:	0, // 合格
  qualifiedCount:	0, // 合格总人次
  trainCount:	0, // 考核总人次
  validDate:	'', // validDate
})

// 培训计划列表
const getStatisticList = () => {
  const params = {
    createStartTime: '', // 创建开始时间
    createEndTime: '', // 创建结束时间
    trainStartTime: '', // 培训开始时间
    trainEndTime: '', // 培训结束时间
    deptId: '', // 部门id
    formId: SCHEDULE.TRAIN_APPROVAL, // 表单id
    director: '', // 负责人
    effectiveCompany: '', // 实施单位
    offset: 1,
    limit: 20,
  }
  getPlanList(params).then((res) => {
    tableData.value = res.data.rows
  })

  getStatistic().then((res) => {
    StatisticxAxis.value = res.data.map((item: IPlanReturn) => {
      return item.date
    })
    const yValue = res.data.map((item: IPlanReturn) => {
      return Number(item.count)
    })
    StatisticData.value = [{ name: '培训计划', data: yValue }]
  })
}

// 计量人员统计
function fetchStaffStatistic() {
  getStaffSStatistic().then((res) => {
    StaffSStatisticxAxis.value = res.data.map((item: IPlanReturn) => item.date)
    const yValue = res.data.map((item: IPlanReturn) => Number(item.count))
    maxStaffCount.value = Math.max(yValue) > 10 ? Math.max(yValue) : 10
    StaffSStatisticData.value = [{ name: '人数', data: yValue }]
  })
}

// 证书列表
function fetchCertificates() {
  const param = {
    account: username, // 账户
  }
  getCertificateStatistic(param).then((res) => {
    if (res.data.length) {
      CertificateList.value = res.data // 表格数据
      meterAdmin.value = res.data[0].meterAdmin // 是否是计量负责人
      if (!meterAdmin.value) { // 非计量负责人找最近到期时间
        // expireList数组中均为已到期的证书记录
        const expireList = res.data.filter((item: ICertificatReturn) => typeof item.lastValidDate === 'number')
        // 按照到期时间排序-找到最近的到期时间
        if (expireList.length) {
          CertificateObject.value = expireList.sort((nex: ICertificatReturn, max: ICertificatReturn) => {
            if (`${nex.lastValidDate}` && `${max.lastValidDate}`) {
              return nex.lastValidDate - max.lastValidDate
            }
          })[0]
        }
      }
    }
  })
}

// 我的培训考核
function fetchMyTrainLog() {
  const param = {
    account: username,
  }
  getTrainLogSStatistic(param).then((res) => {
    const result = res.data
    let total = 0 // 总人数
    let notQualifiedCount = 0// 不合格人数
    let qualifiedCount = 0 // 合格人数
    if (meterAdmin.value) { // 是计量负责人--计算全部人次的考核合格率
      total = result.trainCount // 总人数
      // 不合格人数
      notQualifiedCount = total - result.qualifiedCount
      // 合格人数
      qualifiedCount = result.qualifiedCount
      TrainLogTitle.value = total // 考核总人次
    }
    else {
      // 总人数
      total = result.notQualified + result.qualified
      // 不合格人数
      notQualifiedCount = result.notQualified
      // 合格人数
      qualifiedCount = result.qualified
      TrainLogTitle.value = total // 考核总人次
    }
    // 合格率:合格总人次/考核总人次
    const qualifiedRate = qualifiedCount !== 0 ? ((qualifiedCount / total) * 100).toFixed(2) : '0'
    // 不合格率:不合格总人次/考核总人次
    const notQualifiedRate = notQualifiedCount !== 0 ? ((notQualifiedCount / total) * 100).toFixed(2) : '0'
    TrainLogSList.value = [
      { name: `合   格   ${qualifiedCount}人次 - ${qualifiedRate}%`, value: qualifiedCount },
      { name: `不合格   ${notQualifiedCount}人次 - ${notQualifiedRate}%`, value: notQualifiedCount },
    ]
  }).catch((_) => {
    // 处理异常
    TrainLogSList.value = [
      { name: '合   格   0 - 50%', value: 0 },
      { name: '不合格   0 - 50%', value: 0 },
    ]
    TrainLogTitle.value = 0
  })
}

// 计量文件列表
const getFileList = () => {
  const params = {
    ids: [],
    fileNo: '', // 编号
    fileName: '', // 名称
    formId: SCHEDULE.FILE_APPROVAL, //  表单id
    publishStartTime: '', // 发布时间
    publishEndTime: '', // 发布时间
    fileCode: '', // 文件号
    effectiveStartTime: '', // 实施时间
    effectiveEndTime: '', // 实施时间
    effectiveStatus: '', // 实施状态
    fileType: '',
    limit: 20,
    offset: 1,
  }
  listPageApi(params).then((res) => {
    meterageTableData.value = res.data.rows
  })
}

function calcBlockSize() {
  // 计算工作台区域高度 - 顶部-面包屑-边距
  const bodyHeight = document.body.clientHeight - 60 - 50 - 20
  blockHeight.value = bodyHeight > 610 ? (bodyHeight - 10) / 2 : 300
  blockWidth.value = (document.body.clientWidth - 180 - 20 - 20) / 3
  console.log(blockHeight.value, blockWidth.value - 20)
}
window.addEventListener('resize', () => {
  calcBlockSize()
})

onMounted(async () => {
  await fetchCertificates() // 证书列表
  calcBlockSize() // 计算各区域宽高
  fetchMyTrainLog() // 获取我的培训考核
  getFileList() // 计量文件
  getStatisticList() // 培训计划列表
  fetchStaffStatistic() // 计量人员统计
})
</script>

<template>
  <app-container>
    <div class="bench">
      <el-row :gutter="10">
        <el-col :span="8">
          <bench-col
            v-if="proxy.hasPerm('/workbench/meterTrainStatistic')"
            icon="icon-book"
            title="培训计划"
            path-url="/train/plan"
            :style="{ height: blockHeight }"
            :height="blockHeight"
          >
            <el-table
              :data="tableData"
              style="width: 100%; height: 100%;"
              :max-height="blockHeight - 60"
              stripe
              header-row-class-name="bench-table-header"
              row-class-name="bench-table-row"
              class="bench-table"
            >
              <el-table-column
                v-for="item in columns"
                :key="item.value"
                :prop="item.value"
                align="center"
                :label="item.text"
                :width="item.width"
                show-overflow-tooltip
              />
            </el-table>
          </bench-col>
        </el-col>
        <el-col :span="8">
          <bench-col
            v-if="proxy.hasPerm('/workbench/meterTrain/line')"
            icon="icon-line"
            title="培训计划"
            :height="blockHeight"
          >
            <line-chart
              :x-axis-data="StatisticxAxis"
              :data="StatisticData"
              :width="`${blockWidth - 20}px`"
              unit="个"
            />
          </bench-col>
        </el-col>
        <el-col :span="8">
          <div
            class="bench-right block"
            :style="{ height: `${blockHeight}px` }"
          >
            <bench-col height="40%">
              <div class="bench-right-top">
                <div
                  v-for="item of buttomTypes"
                  :key="item.text"
                  class="right-top-box"
                  @click="$router.push(item.url)"
                >
                  {{ item.text }}
                </div>
              </div>
            </bench-col>
            <div style="flex: 1; margin-top: 10px;">
              <bench-col
                v-if="proxy.hasPerm('/workbench/meterTrain/train')"
                :title="meterAdmin ? '培训考核' : '我的培训考核'"
                icon="icon-book"
                height="100%"
              >
                <div class="pie-chart">
                  <div class="pie-chart-info">
                    <pie-chart
                      :data="TrainLogSList"
                      :radius="['50%', '70%']"
                      right="45%"
                      :width="`${blockWidth - 20}px`"
                      :label-formatter="`${TrainLogTitle}`"
                    />
                  </div>
                </div>
              </bench-col>
            </div>
          </div>
        </el-col>
      </el-row>
    </div>
    <div class="bench">
      <el-row :gutter="10">
        <el-col :span="8">
          <bench-col
            v-if="proxy.hasPerm('/measure/file/quality')"
            title="计量文件"
            icon="icon-file"
            path-url="/file/quality"
            :height="blockHeight"
            class="flex-full block"
          >
            <el-table
              :data="meterageTableData"
              style="width: 100%; height: 100%;"
              stripe
              header-row-class-name="bench-table-header"
              row-class-name="bench-table-row"
              class="bench-table"
              :max-height="blockHeight - 60"
            >
              <el-table-column
                v-for="item in meterageColumns"
                :key="item.value"
                :prop="item.value"
                align="center"
                :label="item.text"
                :width="item.width"
                show-overflow-tooltip
              />
            </el-table>
          </bench-col>
        </el-col>
        <el-col :span="8">
          <bench-col
            v-if="!meterAdmin"
            title="我的证书"
            icon="icon-certi"
            :height="blockHeight"
            path-url="/person/remind"
            class="flex-full block"
          >
            <div style="display: flex;">
              <el-table
                :data="CertificateList"
                style="width: 65%; height: 100%;"
                stripe
                header-row-class-name="bench-table-header"
                row-class-name="bench-table-row"
                class="bench-table"
                :max-height="blockHeight - 60"
              >
                <el-table-column
                  v-for="item in CertificateColumns"
                  :key="item.value"
                  :prop="item.value"
                  align="center"
                  :label="item.text"
                  :width="item.width"
                  show-overflow-tooltip
                />
              </el-table>
              <div class="validDate">
                <div class="validDate-top">
                  <span class="validate-text" :style="`width:${blockWidth - 20}px`">
                    {{ CertificateObject.lastValidDate }}
                    <span style="font-size: 12px;">天</span>
                  </span>
                </div>
                <div style="font-size: 14px; font-weight: 600;">
                  最近到期时间
                </div>
              </div>
            </div>
          </bench-col>
          <bench-col
            v-if="meterAdmin"
            title="证书预警"
            icon="icon-certi"
            path-url="/person/remind"
            :height="blockHeight"
            class="flex-full block"
          >
            <el-table
              :data="CertificateList"
              style="width: 100%; height: 100%;"
              stripe
              header-row-class-name="bench-table-header"
              row-class-name="bench-table-row"
              class="bench-table"
              :max-height="blockHeight - 60"
            >
              <el-table-column
                v-for="item in CertificatesColumns"
                :key="item.value"
                :prop="item.value"
                align="center"
                :label="item.text"
                :width="item.width"
              />
            </el-table>
          </bench-col>
        </el-col>
        <el-col :span="8">
          <bench-col
            v-if="proxy.hasPerm('/workbench/person/list')"
            title="计量人员"
            icon="icon-line"
            :height="blockHeight"
            class="flex-full block"
          >
            <line-chart
              :x-axis-data="StaffSStatisticxAxis"
              :width="`${blockWidth - 20}px`"
              :max="maxStaffCount"
              :data="StaffSStatisticData"
              unit="人"
            />
          </bench-col>
        </el-col>
      </el-row>
    </div>
  </app-container>
</template>

<style lang="scss" scoped>
.bench {
  // display: flex;
  // justify-content: flex-start;
  width: 100%;
  box-sizing: border-box;

  .flex-full {
    flex: 1;
  }

  .block + .block {
    margin-left: 10px;
  }

  .bench-right {
    flex: 1;
    display: flex;
    flex-direction: column;
    height: 40vh;

    .bench-right-top {
      width: 100%;
      height: 100%;
      padding: 0 10px;
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      box-sizing: border-box;

      .right-top-box {
        width: calc(33% - 10px);
        height: calc(50% - 10px);
        padding: 10px;
        margin: 5px;
        border-radius: 5px;
        text-align: center;
        font-size: 14px;
        color: #fff;
        cursor: pointer;
        background-color: #3d7eff;
        display: flex;
        justify-content: center;
        align-items: center;

        &:hover {
          background-color: #286ffd;
        }
      }
    }
  }
}

.bench + .bench {
  margin-top: 10px;
}

.validDate {
  width: 35%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;

  .validDate-top {
    position: relative;
    width: 100px;
    height: 100px;
    margin-bottom: 20px;
    background:
      url("../../../assets/images/bench/clock.png") no-repeat center /
      cover;

    .validate-text {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      font-size: 20px;
    }
  }

  .validDate-bottom {
    font-size: 16px;
    font-weight: 500;

    span {
      font-size: 14px;
      line-height: 2;
      font-weight: 400;
    }
  }
}

.pie-chart {
  display: flex;
  align-items: center;
  width: 100%;
  height: 100%;

  .pie-chart-info {
    flex: 1;
    font-size: 12px;
    width: 100%;
    height: 100%;

    .chart-info-title {
      display: flex;
      align-items: center;
      margin-top: 10px;

      .chart-info-bg {
        width: 10px;
        height: 10px;
        border-radius: 50%;
        margin-right: 8px;
        background-color: #3d7eff;
      }
    }
  }
}
</style>

<style lang="scss">
.bench-table {
  .el-table__header-wrapper {
    border-radius: 8px;
  }
}

.bench-table-header {
  th {
    font-weight: normal;
    font-size: 14px;
  }
}

.bench-table-row {
  border-radius: 8px;
}
</style>