<!-- 检定证书分析 --数据看板 --> <script name="verificationCertificate" lang="ts" setup> import dayjs from 'dayjs' import draggable from 'vuedraggable' import { colors } from '../environment/colors' import { getRangeAllTime } from '@/utils/date' import { verificationCertificate } from './img' import { getBaseInfo, getStaffRank, getCategoryAnalysis, getDeptRate, getDeptAnalysis, getOnlyRecord } from '@/api/dataManagement/data/verificationCertificate' const props = defineProps({ lab: { type: String, default: '', }, }) // 日期查询条件 const dateRef = ref() const current = ref('') setTimeout(() => { current.value = '近7日' }) const searchQueryResult = ref({ labCode: '', timeStart: '', timeEnd: '', contentType: '1' }) const selectCurrentTime = ref() const menu = ref(['当日', '近7日', '近14天', '自定义时间']) // 时间筛选条件 // const startTime = ref() // const calendarChange = (e: any) => { // startTime.value = e[0].getTime() // } // const pickerOptions = (time: any) => { // if (startTime.value) { // const timeRange = 1 * 24 * 60 * 60 * 1000 // 1天时间戳 // const minTime = startTime.value - timeRange * 2 // const maxTime = startTime.value + timeRange * 15 // return time.getTime() <= minTime || time.getTime() >= maxTime || time.getTime() === startTime.value // } // else { // return false // } // } // 点击自定义时间 // const clickBtn = (event: any) => { // const select = event.target.value // if (select && select === '自定义时间') { // dateRef.value.focus() // } // else if (select && select !== '自定义时间') { // selectCurrentTime.value = [] // startTime.value = '' // } // } const currentBak = ref('') const handlerFocus = () => { console.log('获取焦点') // 记录之前 currentBak.value = current.value current.value = '自定义时间' } const handlerBlur = () => { console.log('失去焦点') if (!selectCurrentTime.value.length) { current.value = currentBak.value || '近7日' } } const timeDate = ref<string[]>([]) watch(() => current.value, (newVal) => { if (newVal) { if (newVal === '当日') { timeDate.value = [dayjs().format('YYYY-MM-DD')] } else if (newVal === '近7日') { timeDate.value = getRangeAllTime( dayjs().subtract(7, 'day').format('YYYY-MM-DD'), dayjs().format('YYYY-MM-DD') ) } else if (newVal === '近14天') { timeDate.value = getRangeAllTime( dayjs().subtract(14, 'day').format('YYYY-MM-DD'), dayjs().format('YYYY-MM-DD') ) } if (!newVal.includes('自定义')) { searchQueryResult.value.labCode = props.lab === '全站' ? '' : props.lab fetchData() selectCurrentTime.value = [] } } }) watch(() => selectCurrentTime.value, newVal => { if (newVal.length === 2) { console.log('确定时间') timeDate.value = getRangeAllTime(newVal[0], newVal[1]) fetchData() } }) watch(() => props.lab, (newVal) => { if (newVal) { searchQueryResult.value.labCode = props.lab === '全站' ? '' : props.lab setTimeout(() => { fetchData() }) } }) watch(() => searchQueryResult.value.contentType, (newVal) => { if (!newVal) { return } console.log(newVal, 'newVal') if (newVal !== '3') { showChartBase.value = showChart.value // 修改基本信息 baseInfo.value = baseInfo.value.map((item: any) => ({ ...item, name: newVal=== '1' && item.name.includes('原始记录') ? item.name.replace('原始记录', '检定证书') : newVal === '2' && item.name.includes('检定证书') ? item.name.replace('检定证书', '原始记录') : item.name })) // 修改标题 showChartBase.value = showChartBase.value.map((item: any) => ({ ...item, name: newVal=== '1' && item.name.includes('原始记录') ? item.name.replace('原始记录', '检校证书') : newVal === '2' && item.name.includes('检校证书') ? item.name.replace('检校证书', '原始记录') : item.name })) } else { showChartBase.value = showChartNoNeed.value } fetchData() }) // 基础信息展示数据 const baseInfo = ref([ { name: '检定证书总量', value: 'certificateAmount', data: '', icon: verificationCertificate['检定证书总量'], color: '#1AAF8B' }, { name: '检定证书总量(分包方)', value: 'outCertificateAmount', data: '', icon: verificationCertificate['检定证书总量(分包方)'], color: '#1AAF8B' }, { name: '检定及时率', value: 'timelyRate', data: '', icon: verificationCertificate['检定及时率'], color: '#02A7F0' }, { name: '证书差错率', value: 'errorRate', data: '', icon: verificationCertificate['证书差错率'], color: '#D9001B' }, ]) const showChartBase = ref<any[]>([]) const showChart = ref<any[]>([ { name: '检校类别分析', type: 'bar-vertical', source: 'system', gradient: false, }, { name: '部门检校证书比例', type: 'pie', source: 'system', center: ['50%', '60%'], labelPosition: 'outside', radius: '70%', data: [], }, { name: '部门检校证书分析', type: 'bar-horizontal', source: 'system', gradient: false }, { name: '检定人员排行榜', type: 'rank-table', source: 'system', columns: [ { text: '检定员', value: 'staffName', align: 'center', }, { text: '部门', value: 'groupCodeName', align: 'center', }, { text: '检定证书数量', value: 'certificateAmount', align: 'center', }, ], data: [], }, ]) const showChartNoNeed = ref<any[]>([ { name: '设备列表', type: 'rank-table', source: 'system', columns: [ { text: '设备名称', value: 'equipmentName', align: 'center', }, { text: '型号规格', value: 'model', align: 'center', }, { text: '辅助字段', value: 'helpInstruction', align: 'center', }, { text: '生产厂家', value: 'manufacturer', align: 'center', }, { text: '出厂编号', value: 'manufactureNo', align: 'center', }, { text: '负责人', value: 'directorName', align: 'center', }, { text: '检定机构', value: 'checkOrganization', align: 'center', }, { text: '证书有效期', value: 'certificateValid', align: 'center', }, ], data: [], }, ]) showChartBase.value = showChart.value watch(() => showChart.value.length, () => { setTimeout(() => { const resize = new Event('resize') window.dispatchEvent(resize) }) }) // 模拟数据 const loadingBase = ref(false) function fetchData() { if (current.value !== '自定义时间') { // console.log(timeDate.value, 'timeDate') if (current.value === '当日') { searchQueryResult.value.timeStart = `${timeDate.value[0]} 00:00:00` searchQueryResult.value.timeEnd = `${timeDate.value[0]} 23:59:59` } else { searchQueryResult.value.timeStart = `${timeDate.value[0]} 00:00:00` searchQueryResult.value.timeEnd = `${timeDate.value[timeDate.value.length - 1]} 23:59:59` } } else { searchQueryResult.value.timeStart = `${selectCurrentTime.value[0]} 00:00:00` searchQueryResult.value.timeEnd = `${selectCurrentTime.value[1]} 23:59:59` } if (searchQueryResult.value.contentType !== '3') { // 获取基础信息 loadingBase.value = true getBaseInfo(searchQueryResult.value).then(res => { baseInfo.value.forEach(item => { item.data = res.data[item.value] }) loadingBase.value = false }).catch(() => { loadingBase.value = false }) // 检定人员排行榜 const current1 = showChartBase.value.filter(item => item.name === '检定人员排行榜')[0] current1.loading = true getStaffRank(searchQueryResult.value).then(res => { // console.log(res.data, '检定人员排行榜') current1.data = res.data.rows current1.loading = false }).catch(() => { current1.loading = false }) // 检校类别分析 const current2 = showChartBase.value.filter(item => item.name === '检校类别分析')[0] current2.loading = true getCategoryAnalysis(searchQueryResult.value).then(res => { // console.log(res.data, '检校类别分析') current2.xAxisData = res.data.map((item: any) => item.measureCategoryName) const data = [ { name: '合格', value: 'qualifiedAmount' }, { name: '不合格', value: 'unqualifiedAmount' }, { name: '除*外合格', value: 'otherAmount' }, ] current2.data = data.map((item: any, index: number) => ({ name: item.name, stack: 'Ad', color: colors[index], data: res.data.map((citem: any) => String(citem[data[index].value])) // data: data.map(item => Math.floor(Math.random() * 100) + 1), })) console.log(current2.data, 'current2.data') // 判断是否为空 if(current2.data.every((item: any) => item.data.every((item: string) => item === '0'))) { current2.isEmpty = true } else { current2.isEmpty = false } current2.loading = false }).catch(() => { current2.loading = false }) // 部门检校证书比例 const current3 = showChartBase.value.filter(item => item.name === '部门检校证书比例' || item.name === '部门原始记录比例')[0] current3.loading = true getDeptRate(searchQueryResult.value).then(res => { // console.log(res.data, '部门检校证书比例') const data = [ { name: '电学电源组', value: 'ddAmount', data: '' }, { name: '热工压力组', value: 'rlAmount', data: '' }, { name: '无线电脉冲组', value: 'wmAmount', data: '' } ] data.forEach(item => { item.data = res.data[item.value] }) current3.data = data.map(item => ({ name: item.name, value: String(item.data) })) // 判断是否为空 if(current3.data.every((item: any) => item.value === '0')) { current3.isEmpty = true } else { current3.isEmpty = false } current3.loading = false }).catch(() => { current3.loading = false }) // 部门检校证书分析 const current4 = showChartBase.value.filter(item => item.name === '部门检校证书分析' || item.name === '部门原始记录分析')[0] current4.loading = true getDeptAnalysis(searchQueryResult.value).then(res => { // console.log(res.data, '部门检校证书分析') const data = [ { name: '校准', value: 'calibrationAmount', data: '' }, { name: '校验', value: 'checkAmount', data: '' }, { name: '检定', value: 'measureAmount', data: '' }, { name: '测试', value: 'testAmount', data: '' }, ] current4.xAxisData = res.data.map((item: any) => item.groupCodeName) current4.data = data.map((item: any, index: number) => ({ name: item.name, data: res.data.map((citem: any) => String(citem[item.value])), color: JSON.parse(JSON.stringify(colors)).reverse()[index] })) console.log(current4.data, 'current4.data') current4.loading = false }).catch(() => { current4.loading = false }) } else { const current = showChartBase.value.filter(item => item.name === '设备列表')[0] current.loading = true getOnlyRecord(searchQueryResult.value).then(res => { current.data = res.data.rows current.loading = false }).catch(() => { current.loading = false }) } // loading.value = true // // 检校类别分析 // const name = ['检定', '校准', '测试', '校验'] // const name1 = ['合格', '不合格', '除*外合格'] // showChart.value.filter((item: any) => item.name === '检校类别分析')[0].data = name1.map((item, index) => ({ // name: item, // stack: 'Ad', // data: name.map(item => Math.floor(Math.random() * 100) + 1), // color: colors[index], // })) // showChart.value.filter((item: any) => item.name === '检校类别分析')[0].xAxisData = name // // 部门检校证书比例 // const name2 = ['电学电源组', '热工压力组', '无线脉冲组'] // showChart.value.filter((item: any) => item.name === '部门检校证书比例')[0].data = name2.map(item => ({ // name: item, // value: Math.floor(Math.random() * 100) + 1, // })) // // 部门检校证书分析 // const name3 = ['检定', '校准', '测试', '校验'] // showChart.value.filter((item: any) => item.name === '部门检校证书分析')[0].xAxisData = name2 // showChart.value.filter((item: any) => item.name === '部门检校证书分析')[0].data = name3.map(item => ({ // name: item, // data: name2.map(item => Math.floor(Math.random() * 100) + 1), // })) // // 检定人员排行榜 // const name4 = ['李响', '张超', '王立勇', '张浩', '孙子涵', '楚瑜', '张月月', '刘涵', '李渊', '王瑞'] // const name5 = ['电学电源组', '电学电源组', '热工压力组', '热工压力组', '热工压力组', '电学电源组', '无线脉冲组', '无线脉冲组', '电学电源组', '热工压力组'] // showChart.value.filter((item: any) => item.name === '检定人员排行榜')[0].data = name4.map((item, index) => ({ // name: item, // count: Math.floor(Math.random() * 100) + 1, // dept: name5[index], // })) // setTimeout(() => { // loading.value = false // }, 3000) } defineExpose({ showChart, }) // 拖拽结束 const onEnd = () => { console.log('拖拽结束') const resize = new Event('resize') window.dispatchEvent(resize) } </script> <template> <div class="container"> <!-- 查询条件 --> <div class="btns"> <div /> <div style="position: relative;display: flex;align-items: center;"> <el-radio-group v-model="current"> <el-radio-button v-for="item in menu" :key="item" :label="item"> {{ item }} </el-radio-button> </el-radio-group> <!-- 自定义时间选择 --移花接木 --> <el-date-picker ref="dateRef" v-model="selectCurrentTime" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" class="time-daterange" style="width: 101px; position: absolute; top: 0; right: 130px; opacity: 0" format="YYYY-MM-DD" value-format="YYYY-MM-DD" @focus="handlerFocus" @blur="handlerBlur" /> <!-- 证书类型 --> <el-select v-model="searchQueryResult.contentType" style="width: 130px" class="short-input" placeholder="证书类型"> <el-option label="证书报告" value="1" /> <el-option label="原始记录" value="2" /> <el-option label="无需出证书" value="3" /> </el-select> </div> </div> <!-- 展示内容 --> <div class="content-count"> <!-- 统计数据 --> <div v-loading="loadingBase" v-show="searchQueryResult.contentType !== '3'" class="count"> <div class="count-item" v-for="item in baseInfo" :key="item.value"> <div class="header-icon" style="width: 55px"> <img :src="item.icon" width="55px" height="50px" /> </div> <div class="header-content"> <div class="content-title"> {{ item.name }} </div> <div class="content-count" :style="{ color: item.color }"> {{ item.data }} </div> </div> </div> </div> <!-- 图表区域 --> <!-- <div class="chart"> --> <draggable v-model="showChartBase" item-key="name" class="chart" animation="300" drag-class="dragClass" ghost-class="ghostClass" chosen-class="chosenClass" @end="onEnd"> <template #item="{ element, index }"> <div :style="{ width: `${'48%'}` }" class="chart-item"> <div class="chart-name"> <!-- {{ element.name }} --> </div> <div v-loading="element.loading" class="chart-page"> <div style="width: 100%; height: 100%"> <div class="chart-name" style="width: 100%; height: 10%; display: flex"> {{ element.name }} </div> <div style="width: 100%; height: 90%"> <pie-chart v-if="element.type === 'pie' && !element.isEmpty" :title="element.title" :show-total="element.showTotal" :data="element.data" :colors="colors" :center="element.center" label-formatter="{style1|{c}}" :label-position="element.labelPosition" :radius="element.radius" :grid="{ top: 50, left: 15, right: 15, bottom: 10, containLabel: true }" :legend="{ itemWidth: 8, itemHeight: 8, type: 'scroll', orient: 'horizontal', icon: 'roundRect', left: '0', top: '10' }" /> <line-chart v-if="element.type === 'line' && !element.isEmpty" :colors="colors" :gradient="element.gradient" :x-axis-data="element.xAxisData" :data="element.data" :smooth="element.smooth" :grid="{ top: 47, left: 5, right: 5, bottom: 10, containLabel: true }" :legend="{ itemWidth: 8, itemHeight: 2, type: 'scroll', orient: 'horizontal', icon: 'roundRect', left: '0', top: '5' }" /> <bar-chart-double v-if="element.type === 'bar-double' && !element.isEmpty" :legend-data="element.legendData" :data1="element.data1" :data2="element.data2" :x-axis-data="element.xAxisData" :legend="{ itemWidth: 8, itemHeight: 8, type: 'scroll', orient: 'horizontal', icon: 'roundRect', left: '0', top: '10' }" /> <bar-chart-vertical v-if="element.type === 'bar-vertical' && !element.isEmpty" :bar-coner="0" :data="element.data" :x-axis-data="element.xAxisData" :gradient="element.gradient" :show-label="false" :legend="{ itemWidth: 8, itemHeight: 8, type: 'scroll', orient: 'horizontal', icon: 'roundRect', left: '0', top: '10' }" /> <bar-chart-horizontal v-if="element.type === 'bar-horizontal' && !element.isEmpty" bar-width="10" :bar-coner="0" :data="element.data" :x-axis-data="element.xAxisData" :gradient="element.gradient" :legend="{ itemWidth: 8, itemHeight: 8, type: 'scroll', orient: 'horizontal', icon: 'roundRect', left: '0', top: '10' }" /> <scroll-table v-if="element.type === 'rank-table'" style="width: 100%" :height="300" :data="element.data" :columns="element.columns" /> <el-empty v-if="element.isEmpty" description="暂无数据" /> </div> </div> </div> </div> </template> </draggable> <!-- </div> --> </div> </div> </template> <style lang="scss" scoped> .dragClass { /* background-color: blueviolet !important; */ opacity: 1 !important; box-shadow: none !important; outline: none !important; background-image: none !important; color: #6ff !important; } .ghostClass { /* background-color: blue !important; */ } .chosenClass { color: #6ff !important; /* background-color: #ccc !important; */ /* opacity: 1 !important; */ /* width: 32.5% !important; */ } .icon-button-icon { width: 50px; height: 50px; } .container { position: relative; // height: 500px; .btns { width: 100%; position: absolute; top: -36px; right: 60%; // left: 50%; display: flex; justify-content: space-between; padding: 0 120px; } .count { display: flex; width: 50%; margin: 0 auto; position: absolute; top: -40px; left: 40%; transform: translateX(-50%); justify-content: space-around; .count-item { // margin-left: 20px; // width: 44%; height: 75px; display: flex; padding: 0 10px; justify-content: space-around; .header-icon { width: 20%; display: flex; flex-direction: column; justify-content: center; margin-right: 15px; } .header-content { // width: 60%; padding: 10px; display: flex; flex-direction: column; justify-content: space-around; .content-title { font-weight: 700; } .content-count { font-weight: 700; font-size: 24px; color: #1aaf8b; text-align: center; } } } } .chart { display: flex; flex-wrap: wrap; justify-content: space-around; // margin-top: 15px; padding-top: 20px; .chart-item { width: 33%; height: 348px; // border: 1px solid red; margin-top: 10px; .chart-name { color: #3d6fb6; font-size: 18px; font-weight: 700; } .chart-page { height: 330px; } } } } </style>