<!-- 计量检定分析 --数据看板 --> <script name="meteringVerification" lang="ts" setup> import dayjs from 'dayjs' import draggable from 'vuedraggable' import { colors } from '../environment/colors' import { meteringVerification } from './img' import { getRangeAllTime } from '@/utils/date' import type { deptType, dictType } from '@/global' import { getDictByCode } from '@/api/system/dict' import ScrollTable from '@/components/ScrollTable/index.vue' // import { getEquipmentList } from '@/api/equipment/info/book' import { getAlreadyDeviceRank, getBaseInfo, getDeviceAnalysis, getTimelyAnalysis, getVerificationResult, } from '@/api/dataManagement/data/verificationAnalysis' // import { getCompanyList } from '@/api/system/dept' import { getCustomerInfoList } from '@/api/resource/customer' import { getEquipmentList } from '@/api/business/manager/order' import { uniqueMultiArray } from '@/utils/Array' const props = defineProps({ lab: { type: String, default: '', }, }) watch(() => props.lab, (newVal) => { if (newVal) { searchQueryResult.value.labCode = props.lab fetchData() } }) // 检定结果分析查询条件 const searchQueryResult = ref({ customerId: '', groupCode: '', helpInstruction: '', labCode: '', model: '', sampleName: '', timeEnd: '', timeStart: '', }) const datetimerange = ref() watch(() => datetimerange.value, (newVal) => { searchQueryResult.value.timeStart = '' searchQueryResult.value.timeEnd = '' if (Array.isArray(newVal)) { if (newVal.length) { searchQueryResult.value.timeStart = `${newVal[0]}` searchQueryResult.value.timeEnd = `${newVal[1]}` } } }) // 实验室 const labCodeList = ref<dictType[]>([]) // 部门 const groupCodeList = ref<dictType[]>([]) // 委托方 const customerList = ref<any[]>([]) // 受检设备 const sampleNameList = ref<any[]>([]) // 规格型号 const modelList = ref<any[]>([]) // 辅助字段 const helpInstructionList = ref<any[]>([]) // 所有受检设备 const allDeviceList = ref<any[]>([]) // 查询检定结果分析 const searchList = () => { // 检定结果分析 const current = showChart.value.filter(item => item.name === '检定结果分析')[0] current.loading = true getVerificationResult(searchQueryResult.value).then((res) => { const data = [ { name: '合格', value: 'qualified', data: '', }, { name: '限用', value: 'restrict', data: '', }, { name: '停用', value: 'deactivate', data: '', }, ] data.forEach((item) => { item.data = res.data[item.value] // item.data = String(Math.floor(Math.random() * 100) + 1) }) current.data = [ { name: '数量', data: data.map(item => item.data), }, ] current.xAxisData = data.map(item => item.name) // 判断是否为空 if (current.data[0].data.every((item: any) => String(item) === '0')) { current.isEmpty = true } else { current.isEmpty = false } current.loading = false }).catch(() => { current.loading = false }) } // 重置查询 const clearList = () => { searchQueryResult.value = { customerId: '', groupCode: '', helpInstruction: '', labCode: props.lab, model: '', sampleName: '', timeEnd: '', timeStart: '', } searchList() } const baseInfo = ref([ { name: '在检设备总量', value: 'measuringAmount', data: '', icon: meteringVerification['在检设备总量'], color: '#02A7F0', }, { name: '超期未检设备总量', value: 'overdueAmount', data: '', icon: meteringVerification['超期未检设备总量'], color: '#D81E06', }, { name: '已检设备总量(本年)', value: 'measuredAmount', data: '', icon: meteringVerification['已检设备总量(本年)'], color: '#2AA515', }, { name: '检定合格率(本年)', value: 'passRate', data: '', icon: meteringVerification['检定合格率(本年)'], color: '#02A7F0', }, { name: '停用率(本年)', value: 'stopRate', data: '', icon: meteringVerification['停用率(本年)'], color: '#D81E06', }, { name: '计量室检定率', value: 'insideRate', data: '', icon: meteringVerification['计量室检定率(本年)'], color: '#02A7F0', }, ]) onMounted(() => { if (props.lab) { searchQueryResult.value.labCode = props.lab // 实验室 getDictByCode('bizGroupCodeEquipment').then((response) => { labCodeList.value = response.data fetchData() }) // 部门 getDictByCode('bizGroupCode').then((response) => { const tempMenu = ['电学电源组', '热工力学组', '无线电脉冲组'] tempMenu.forEach((item) => { const tempFindData = response.data.find( (e: { name: string; value: string }) => e.name === item, ) if (tempFindData) { groupCodeList.value.push({ name: tempFindData.name, id: `${tempFindData.id}`, value: `${tempFindData.value}`, }) } }) }) } }) const showChart = ref<any[]>([ { name: '在检设备分析', type: 'pie', showType: 'pie', source: 'system', center: ['50%', '60%'], labelPosition: 'outside', radius: '70%', data: [], columns: [ { text: '名称', value: 'name', align: 'center', }, { text: '数量', value: 'value', align: 'center', }, ], loading: false, // 加载中 isEmpty: false, // 是否为空 }, { name: '当年已检设备排行TOP10', type: 'rank-table', source: 'system', data: [], columns: [ { text: '设备名称', value: 'name', align: 'center', }, { text: '已检数量', value: 'count', align: 'center', }, ], loading: false, // 加载中 isEmpty: false, // 是否为空 }, { name: '检定及时分析', type: 'pie', source: 'system', showType: 'pie', center: ['50%', '60%'], labelPosition: 'outside', radius: '70%', data: [], columns: [ { text: '名称', value: 'name', align: 'center', }, { text: '数量', value: 'value', align: 'center', }, ], loading: false, // 加载中 isEmpty: false, // 是否为空 }, { name: '检定结果分析', type: 'bar-horizontal', source: 'system', xAxisData: [], data: [], smooth: false, gradient: false, loading: false, // 加载中 isEmpty: false, // 是否为空 }, ]) watch(() => showChart.value.length, () => { setTimeout(() => { const resize = new Event('resize') window.dispatchEvent(resize) }) }) const loadingBase = ref(false) function fetchData() { // 获取基础数据 loadingBase.value = true getBaseInfo({ labCode: props.lab }).then((res) => { console.log(res.data, '获取基础数据') baseInfo.value.forEach((item) => { item.data = res.data[item.value] }) loadingBase.value = false }).catch(() => { loadingBase.value = false }) // 在检设备分析 const current1 = showChart.value.filter(item => item.name === '在检设备分析')[0] current1.loading = true getDeviceAnalysis({ labCode: props.lab }).then((res) => { const data = [ { name: '分包方', value: 'outsourceAmount', data: '', }, { name: '电学电源组', value: 'ddAmount', data: '', }, { name: '热工力学组', value: 'rlAmount', data: '', }, { name: '无线电脉冲组', value: 'wmAmount', data: '', }, { name: '设备分发中', value: 'distributeAmount', data: '', }, ] data.forEach((item) => { item.data = res.data[item.value] }) current1.data = data.map(item => ({ name: item.name, value: String(item.data) })) current1.loading = false // 判断是否为空 if(current1.data.every((item: any)=> item.value === '0')) { current1.isEmpty = true } else { current1.isEmpty = false } }).catch(() => { current1.loading = false }) // 当年已检设备排行 const current2 = showChart.value.filter(item => item.name === '当年已检设备排行TOP10')[0] current2.loading = true getAlreadyDeviceRank({ labCode: props.lab }).then((res) => { current2.data = [] for (const i in res.data) { current2.data.push({ name: i, count: res.data[i], }) } current2.loading = false }).catch(() => { current2.loading = false }) // 检定及时分析 const current3 = showChart.value.filter(item => item.name === '检定及时分析')[0] current3.loading = true getTimelyAnalysis({ labCode: props.lab }).then((res) => { const data = [ { name: '提前检定', value: 'advanceAmount', data: '', }, { name: '滞后检定', value: 'delayAmount', data: '', }, { name: '按时检定', value: 'onTimeAmount', 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 }) // 检定结果分析 searchList() } defineExpose({ showChart, }) // 获取委托方列表 const fetchCompanyList = () => { getCustomerInfoList({ limit: 9999, offset: 1, approvalStatus: '0', formId: 'zyglwtfml' }).then((res) => { customerList.value = res.data.rows.map((item: any) => ({ name: item.customerName, id: item.id, value: item.id, })) }) } fetchCompanyList() // 获取受检设备 const fetchDevice = () => { getEquipmentList({ customerId: searchQueryResult.value.customerId, equipmentType: '1', offset: 1, limit: 9999 }).then((res) => { // console.log(res.data, '受检设备') allDeviceList.value = res.data.rows sampleNameList.value = uniqueMultiArray(res.data.rows.map((item: any) => ({ sampleName: item.equipmentName, model: item.model, helpInstruction: item.helpInstruction, })), 'sampleName',) }) } fetchDevice() // 改变委托方查询条件 const changeCustomer = (value: string) => { console.log(value, '委托方') // 清空设备相关 allDeviceList.value = [] sampleNameList.value = [] modelList.value = [] helpInstructionList.value = [] searchQueryResult.value.sampleName = '' searchQueryResult.value.model = '' searchQueryResult.value.helpInstruction = '' // 重新获取设备相关 fetchDevice() } // 改变设备名称查询条件 const changeSampleName = (value: string) => { // console.log(value, '设备名称') modelList.value = [] helpInstructionList.value = [] searchQueryResult.value.model = '' searchQueryResult.value.helpInstruction = '' modelList.value = uniqueMultiArray(allDeviceList.value.filter((item: any) => item.equipmentName === value).map((item: any) => ({ sampleName: item.equipmentName, model: item.model, helpInstruction: item.helpInstruction, })), 'model',) helpInstructionList.value = modelList.value // console.log(modelList.value, 'modelList.value') } // 改变规格型号查询条件 const changeHelpInstruction = (value: string) => { // console.log(value, '规格型号') helpInstructionList.value = [] searchQueryResult.value.helpInstruction = '' helpInstructionList.value = modelList.value.filter(item => item.model === value) } // 拖拽结束 const onEnd = () => { console.log('拖拽结束') const resize = new Event('resize') window.dispatchEvent(resize) } // 切换显示 const changeShowType = (row: any) => { row.showType = row.showType === 'pie' ? 'table' : 'pie' } </script> <template> <div class="container"> <!-- 展示内容 --> <div class="content-count"> <!-- 统计数据 --> <div v-loading="loadingBase" class="count"> <div v-for="item in baseInfo" :key="item.value" class="count-item"> <div class="header-icon"> <!-- <svg-icon name="icon-env-device-online" class="icon-button-icon" /> --> <img :src="item.icon"> </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="showChart" 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: `${showChart.length === 4 ? '48%' : index === 0 || index === 1 ? '48%' : '31.5%'}` }" class="chart-item"> <div class="chart-name"> <!-- {{ element.name }} --> </div> <div v-loading="element.loading" class="chart-page"> <div v-if="element.type === 'pie'" style="width: 100%; height: 100%;"> <div class="chart-name" style="width: 100%; height: 10%;" @click="changeShowType(element)"> {{ element.name }} </div> <el-empty v-show="element.isEmpty" :image-size="100" description="暂无数据" /> <div v-show="!element.isEmpty" style="width: 100%; height: 90%;"> <pie-chart v-show="element.showType == 'pie'" :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', }" /> <scroll-table v-show="element.showType == 'table'" style="width: 100%;" :height="300" :data="element.data" :columns="element.columns" /> </div> </div> <div v-if="element.type === 'rank-table'" style="width: 100%; height: 100%;"> <div class="chart-name" style="width: 100%; height: 10%;" @click="changeShowType(element)"> {{ element.name }} </div> <!-- <el-empty v-show="element.isEmpty" :image-size="100" description="暂无数据" /> --> <div style="width: 100%; height: 90%;"> <scroll-table style="width: 100%;" :height="300" :data="element.data" :columns="element.columns" /> </div> </div> <div v-if="element.type === 'bar-horizontal'" style="width: 100%; height: 100%;"> <div class="chart-name" style="width: 100%; height: 10%;" @click="changeShowType(element)"> {{ element.name }} </div> <!-- 查询条件 --> <search-area :need-clear="true" @search="searchList" @clear="clearList"> <!-- 实验室 --> <search-item> <el-select v-model="searchQueryResult.labCode" style="width: 130px;" class="short-input" placeholder="实验室" clearable> <el-option v-for="item in labCodeList" :key="item.id" :label="item.name" :value="item.value" /> </el-select> </search-item> <!-- 部门 --> <search-item> <el-select v-model="searchQueryResult.groupCode" style="width: 130px;" class="short-input" placeholder="部门" clearable> <el-option v-for="item in groupCodeList" :key="item.id" :label="item.name" :value="item.value" /> </el-select> </search-item> <!-- 委托方 --> <search-item> <el-select v-model="searchQueryResult.customerId" filterable style="width: 130px;" class="short-input" placeholder="委托方" clearable @change="changeCustomer"> <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.value" /> </el-select> </search-item> <!-- 受检设备名称 --> <search-item> <el-select v-model="searchQueryResult.sampleName" filterable style="width: 130px;" class="short-input" placeholder="受检设备名称" clearable @change="changeSampleName"> <el-option v-for="item in sampleNameList" :key="item.sampleName" :label="item.sampleName" :value="item.sampleName" /> </el-select> </search-item> <!-- 规格型号 --> <search-item> <el-select v-model="searchQueryResult.model" filterable style="width: 130px;" class="short-input" placeholder="规格型号" clearable @change="changeHelpInstruction"> <el-option v-for="item in modelList" :key="item.model" :label="item.model" :value="item.model" /> </el-select> </search-item> <!-- 辅助字段 --> <search-item> <el-select v-model="searchQueryResult.helpInstruction" filterable style="width: 130px;" class="short-input" placeholder="辅助字段" clearable> <el-option v-for="item in helpInstructionList" :key="item.helpInstruction" :label="item.helpInstruction" :value="item.helpInstruction" /> </el-select> </search-item> <!-- 时间 --> <search-item> <div style="width: 350px !important;"> <el-date-picker v-model="datetimerange" type="daterange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" range-separator="至" start-placeholder="检定开始时间" end-placeholder="检定结束时间" clearable /> </div> </search-item> </search-area> <div style="width: 100%; height: 70%;"> <el-empty v-show="element.isEmpty" :image-size="100" description="暂无数据" /> <bar-chart-horizontal v-show="!element.isEmpty" bar-width="25" :bar-coner="0" :data="element.data" :x-axis-data="element.xAxisData" :legend="{ itemWidth: 8, itemHeight: 8, type: 'scroll', orient: 'horizontal', icon: 'roundRect', left: '0', top: '10', }" :grid="{ top: 20, left: 20, right: 60, bottom: 30, containLabel: true, }"/> </div> </div> <!-- --> <!-- <line-chart v-if="element.type === 'line'" :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-vertical v-if="element.type === 'bar-vertical'" :bar-coner="0" :data="element.data" :x-axis-data="element.xAxisData" :legend="{ itemWidth: 8, itemHeight: 8, type: 'scroll', orient: 'horizontal', icon: 'roundRect', left: '0', top: '10' }" /> <bar-chart-horizontal v-if="element.type === 'bar-horizontal'" bar-width="10" :bar-coner="0" :data="element.data" :x-axis-data="element.xAxisData" :legend="{ itemWidth: 8, itemHeight: 8, type: 'scroll', orient: 'horizontal', icon: 'roundRect', left: '0', top: '10' }" /> --> </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: 50%; // left: 50%; display: flex; justify-content: space-between; padding: 0 60px; } .count { display: flex; width: 80%; margin: 0 auto; position: absolute; top: -40px; left: 50%; transform: translateX(-50%); .count-item { margin-left: 20px; width: 18%; height: 75px; display: flex; justify-content: space-around; // padding: 0 10px; .header-icon { width: 20%; display: flex; flex-direction: column; justify-content: center; } .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; &:hover { cursor: pointer; } } .chart-page { height: 330px; overflow: hidden; // box-sizing: content-box; // padding: 15px; } } } } </style>