Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce1738daf0 | ||
|
|
a41d824ca3 | ||
|
|
ac5a8450e8 | ||
|
|
01e817a5d6 | ||
|
|
01bf07fc42 | ||
|
|
633e914c9a | ||
|
|
37e69e7bda | ||
|
|
19fb90432a | ||
|
|
4a3c81a792 | ||
|
|
12d3073241 | ||
|
|
72838462ad | ||
|
|
327addf625 |
@@ -33,9 +33,9 @@ mybatis-plus:
|
||||
#驼峰命名
|
||||
map-underscore-to-camel-case: true
|
||||
#配置sql日志输出
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
#关闭日志输出
|
||||
# log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
||||
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
||||
global-config:
|
||||
db-config:
|
||||
#指定主键生成策略
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/extraResources/mysql/data/#innodb_redo/#ib_redo42
Normal file
BIN
build/extraResources/mysql/data/#innodb_redo/#ib_redo42
Normal file
Binary file not shown.
BIN
build/extraResources/mysql/data/#innodb_redo/#ib_redo43
Normal file
BIN
build/extraResources/mysql/data/#innodb_redo/#ib_redo43
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
11900
|
||||
95428
|
||||
|
||||
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000036
Normal file
BIN
build/extraResources/mysql/data/binlog.000036
Normal file
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000037
Normal file
BIN
build/extraResources/mysql/data/binlog.000037
Normal file
Binary file not shown.
BIN
build/extraResources/mysql/data/binlog.000038
Normal file
BIN
build/extraResources/mysql/data/binlog.000038
Normal file
Binary file not shown.
@@ -11,3 +11,6 @@
|
||||
.\binlog.000033
|
||||
.\binlog.000034
|
||||
.\binlog.000035
|
||||
.\binlog.000036
|
||||
.\binlog.000037
|
||||
.\binlog.000038
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -24,4 +24,4 @@ VITE_PROXY=[["/api","http://127.0.0.1:18093/"]]
|
||||
#VITE_PROXY=[["/api","http://192.168.2.125:18092/"]]
|
||||
# VITE_PROXY=[["/api","http://192.168.1.138:8080/"]]张文
|
||||
# 开启激活验证
|
||||
VITE_ACTIVATE_OPEN=true
|
||||
VITE_ACTIVATE_OPEN=false
|
||||
@@ -25,4 +25,4 @@ VITE_PWA=true
|
||||
#VITE_API_URL="/api" # 打包时用
|
||||
VITE_API_URL="http://127.0.0.1:18093/"
|
||||
# 开启激活验证
|
||||
VITE_ACTIVATE_OPEN=true
|
||||
VITE_ACTIVATE_OPEN=false
|
||||
@@ -69,5 +69,31 @@ export namespace Plan {
|
||||
maxTime: number;
|
||||
}
|
||||
|
||||
export interface PlanStatisticsItem {
|
||||
itemId: string;
|
||||
itemName: string;
|
||||
totalCount: number;
|
||||
qualifiedCount: number;
|
||||
unqualifiedCount: number;
|
||||
passRate: number;
|
||||
}
|
||||
|
||||
export interface PlanStatistics {
|
||||
planId: string;
|
||||
planName: string;
|
||||
totalCheckCount: number;
|
||||
checkedDeviceCount: number;
|
||||
firstQualifiedDeviceCount: number;
|
||||
secondQualifiedDeviceCount: number;
|
||||
thirdOrMoreQualifiedDeviceCount: number;
|
||||
unqualifiedDeviceCount: number;
|
||||
unqualifiedItemCount: number;
|
||||
firstPassRate: number;
|
||||
secondPassRate: number;
|
||||
thirdOrMorePassRate: number;
|
||||
unqualifiedRate: number;
|
||||
itemDistributions: PlanStatisticsItem[];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,10 @@ export const staticsAnalyse = (params: { id: string[] }) => {
|
||||
return http.download('/adPlan/analyse', params)
|
||||
}
|
||||
|
||||
export const getPlanStatistics = (params: { planId: string }) => {
|
||||
return http.post<Plan.PlanStatistics>(`/adPlan/statistics`, params)
|
||||
}
|
||||
|
||||
//根据计划id分页查询被检设
|
||||
export const getDevListByPlanId = (params: any) => {
|
||||
return http.post(`/adPlan/listDevByPlanId`, params)
|
||||
@@ -159,4 +163,4 @@ export const importAndMergePlanCheckData = (params: Plan.ResPlan) => {
|
||||
return http.upload(`/adPlan/importAndMergePlanCheckData`, params, {
|
||||
timeout: 60000 * 20
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
type="primary"
|
||||
icon="Clock"
|
||||
@click="handleTest('手动检测')"
|
||||
v-if="form.activeTabs === 0 && modeStore.currentMode == '模拟式'"
|
||||
v-if="form.activeTabs === 0 && modeStore.currentMode != '比对式'"
|
||||
>
|
||||
手动检测
|
||||
</el-button>
|
||||
@@ -483,7 +483,7 @@ const columns = reactive<ColumnProps<Device.ResPqDev>[]>([
|
||||
sortable: true,
|
||||
isShow: checkStateShow,
|
||||
render: scope => {
|
||||
return scope.row.checkState === 0 ? '未检' : scope.row.checkState === 1 ? '检测中' : '检测完成'
|
||||
return scope.row.checkState === 0 ? '未检' : scope.row.checkState === 1 ? '检测中' : scope.row.checkState === 2 ? '检测完成':'归档'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -494,10 +494,12 @@ const columns = reactive<ColumnProps<Device.ResPqDev>[]>([
|
||||
render: scope => {
|
||||
if (scope.row.checkResult === 0) {
|
||||
return <el-tag type="danger">不符合</el-tag>
|
||||
} else if (scope.row.checkResult === 0) {
|
||||
return '不符合'
|
||||
} else if (scope.row.checkResult === 1) {
|
||||
return '符合'
|
||||
} else if (scope.row.checkResult === 2) {
|
||||
return '未检'
|
||||
}else if(scope.row.checkResult === 2) {
|
||||
return '未检'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
@@ -1087,7 +1089,7 @@ const openDrawer = async (title: string, row: any) => {
|
||||
|
||||
if (title === '检测数据查询') {
|
||||
checkStore.setShowDetailType(0)
|
||||
if (modeStore.currentMode == '模拟式') {
|
||||
if (modeStore.currentMode == '模拟式'||modeStore.currentMode == '数字式') {
|
||||
dataCheckPopupRef.value?.open(row.id, '-1', null)
|
||||
} else if (modeStore.currentMode == '比对式') {
|
||||
dataCheckSingleChannelSingleTestPopupRef.value?.open(row, null, row.id, 2)
|
||||
@@ -1095,7 +1097,7 @@ const openDrawer = async (title: string, row: any) => {
|
||||
}
|
||||
if (title === '误差体系更换') {
|
||||
checkStore.setShowDetailType(1)
|
||||
if (modeStore.currentMode == '模拟式') {
|
||||
if (modeStore.currentMode == '模拟式'||modeStore.currentMode == '数字式') {
|
||||
dataCheckPopupRef.value?.open(row.id, '-1', null)
|
||||
} else if (modeStore.currentMode == '比对式') {
|
||||
dataCheckSingleChannelSingleTestPopupRef.value?.open(row, null, row.id, 2)
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node" style="display: flex; align-items: center;">
|
||||
<span class="custom-tree-node">
|
||||
<!-- 父节点图标 -->
|
||||
<Platform
|
||||
v-if="!data.pid"
|
||||
@@ -39,50 +39,52 @@
|
||||
}"
|
||||
/>
|
||||
<!-- 节点名称 -->
|
||||
<span>{{ node.label }}</span>
|
||||
<!-- 子节点右侧图标 + tooltip -->
|
||||
<el-tooltip
|
||||
v-if="
|
||||
node.label != '未检' &&
|
||||
node.label != '检测中' &&
|
||||
node.label != '检测完成' &&
|
||||
hasChildrenInPlanTable(node.data)
|
||||
"
|
||||
placement="top"
|
||||
:manual="true"
|
||||
content="子计划信息"
|
||||
>
|
||||
<List
|
||||
@click.stop="childDetail(node.data)"
|
||||
style="
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
color: var(--el-color-primary);
|
||||
"
|
||||
<span class="node-label">{{ node.label }}</span>
|
||||
<span class="node-actions">
|
||||
<PieChart
|
||||
v-if="isCompletedPlanNode(node.data)"
|
||||
class="node-action-icon"
|
||||
@click.stop="openStatistics(node.data)"
|
||||
style="margin-right: 8px"
|
||||
/>
|
||||
</el-tooltip>
|
||||
<!-- 子节点右侧图标 + tooltip -->
|
||||
<el-tooltip
|
||||
v-if="
|
||||
node.label != '未检' &&
|
||||
node.label != '检测中' &&
|
||||
node.label != '检测完成' &&
|
||||
hasChildrenInPlanTable(node.data)
|
||||
"
|
||||
placement="top"
|
||||
:manual="true"
|
||||
content="子计划信息"
|
||||
>
|
||||
<List class="node-action-icon" @click.stop="childDetail(node.data)" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
<SourceOpen ref="openSourceView" :width="width" :height="height + 175"></SourceOpen>
|
||||
<PlanStatisticsPopup ref="planStatisticsPopupRef" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { type Plan } from '@/api/plan/interface'
|
||||
import { List, Menu, Platform } from '@element-plus/icons-vue'
|
||||
import { List, Menu, PieChart, Platform } from '@element-plus/icons-vue'
|
||||
import { nextTick, onMounted, ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useCheckStore } from '@/stores/modules/check'
|
||||
import { ElTooltip } from 'element-plus'
|
||||
import SourceOpen from '@/views/plan/planList/components/childrenPlan.vue'
|
||||
import PlanStatisticsPopup from '@/views/plan/planList/components/planStatisticsPopup.vue'
|
||||
import { getPlanList } from '@/api/plan/plan.ts'
|
||||
import { useModeStore } from '@/stores/modules/mode' // 引入模式 store
|
||||
import { useDictStore } from '@/stores/modules/dict'
|
||||
|
||||
const openSourceView = ref()
|
||||
const planStatisticsPopupRef = ref<InstanceType<typeof PlanStatisticsPopup> | null>(null)
|
||||
const router = useRouter()
|
||||
const checkStore = useCheckStore()
|
||||
const filterText = ref('')
|
||||
@@ -211,6 +213,14 @@ const childDetail = (data: Plan.ResPlan) => {
|
||||
}
|
||||
}
|
||||
|
||||
const isCompletedPlanNode = (data: Partial<Plan.ResPlan>) => {
|
||||
return Number(data.testState) === 2
|
||||
}
|
||||
|
||||
const openStatistics = (data: Partial<Plan.ResPlan>) => {
|
||||
planStatisticsPopupRef.value?.open(data)
|
||||
}
|
||||
|
||||
function buildTree(flatList: any[]): any[] {
|
||||
const map = new Map()
|
||||
const tree: any[] = []
|
||||
@@ -293,6 +303,40 @@ defineExpose({ getTreeData, clickTableToTree })
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__content) {
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.custom-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.node-label {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.node-actions {
|
||||
flex: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.node-action-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
//.filter-tree span {
|
||||
// font-size: 16px;
|
||||
// display:block;
|
||||
|
||||
@@ -228,7 +228,7 @@ const unit = [
|
||||
},
|
||||
{
|
||||
label: '功率',
|
||||
unit: 'W'
|
||||
unit: props.valueCode == 'Absolute' ? 'W' : '%Un*In'
|
||||
},
|
||||
{
|
||||
label: '电压偏差',
|
||||
|
||||
@@ -933,7 +933,7 @@ const open = async (sign: string, data: Plan.ReqPlan, currentMode: string, plan:
|
||||
const datasourceDicts = dictStore.getDictData('Datasource')
|
||||
|
||||
formContent.datasourceIds = datasourceDicts
|
||||
.filter(item => ['real', 'wave_data'].includes(item.code))
|
||||
.filter(item => ['real'].includes(item.code))
|
||||
.map(item => item.code)
|
||||
|
||||
realTimeSetting.value = true
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="`检测计划统计 - ${planName || '/'}`"
|
||||
width="min(1280px, 92vw)"
|
||||
class="plan-statistics-dialog"
|
||||
destroy-on-close
|
||||
draggable
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<div v-loading="loading" class="plan-statistics">
|
||||
<el-empty v-if="loadFailed" description="统计数据加载失败" />
|
||||
<template v-else>
|
||||
<div class="summary-grid">
|
||||
<div v-for="item in summaryItems" :key="item.label" class="summary-item">
|
||||
<span class="summary-label">{{ item.label }}</span>
|
||||
<strong class="summary-value">{{ item.value }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isEmpty" class="empty-area">
|
||||
<el-empty description="暂无统计数据" />
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="chart-grid">
|
||||
<div class="chart-panel">
|
||||
<div class="panel-title">合格率</div>
|
||||
<div ref="rateChartRef" class="chart"></div>
|
||||
</div>
|
||||
<div class="chart-panel">
|
||||
<div class="panel-title">检测大项不合格分布</div>
|
||||
<div ref="itemChartRef" class="chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table :data="statisticsData.itemDistributions" height="260" border>
|
||||
<el-table-column prop="itemName" label="检测大项" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="totalCount" label="执行次数" width="110" align="center" />
|
||||
<el-table-column prop="qualifiedCount" label="合格次数" width="110" align="center" />
|
||||
<el-table-column prop="unqualifiedCount" label="不合格次数" width="120" align="center" />
|
||||
<el-table-column label="合格率" width="110" align="center">
|
||||
<template #default="scope">{{ formatRate(scope.row.passRate) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getPlanStatistics } from '@/api/plan/plan'
|
||||
import type { Plan } from '@/api/plan/interface'
|
||||
|
||||
const emptyStatistics = (): Plan.PlanStatistics => ({
|
||||
planId: '',
|
||||
planName: '',
|
||||
totalCheckCount: 0,
|
||||
checkedDeviceCount: 0,
|
||||
firstQualifiedDeviceCount: 0,
|
||||
secondQualifiedDeviceCount: 0,
|
||||
thirdOrMoreQualifiedDeviceCount: 0,
|
||||
unqualifiedDeviceCount: 0,
|
||||
unqualifiedItemCount: 0,
|
||||
firstPassRate: 0,
|
||||
secondPassRate: 0,
|
||||
thirdOrMorePassRate: 0,
|
||||
unqualifiedRate: 0,
|
||||
itemDistributions: []
|
||||
})
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const loading = ref(false)
|
||||
const loadFailed = ref(false)
|
||||
const planName = ref('')
|
||||
const rateChartRef = ref<HTMLDivElement>()
|
||||
const itemChartRef = ref<HTMLDivElement>()
|
||||
const statisticsData = reactive<Plan.PlanStatistics>(emptyStatistics())
|
||||
let rateChart: echarts.ECharts | null = null
|
||||
let itemChart: echarts.ECharts | null = null
|
||||
|
||||
const isEmpty = computed(() => {
|
||||
return (
|
||||
!loading.value &&
|
||||
statisticsData.totalCheckCount === 0 &&
|
||||
statisticsData.checkedDeviceCount === 0 &&
|
||||
statisticsData.itemDistributions.length === 0
|
||||
)
|
||||
})
|
||||
|
||||
const summaryItems = computed(() => [
|
||||
{ label: '总次数', value: statisticsData.totalCheckCount },
|
||||
{ label: '已检设备', value: statisticsData.checkedDeviceCount },
|
||||
{ label: '一次合格率', value: formatRate(statisticsData.firstPassRate) },
|
||||
{ label: '二次合格率', value: formatRate(statisticsData.secondPassRate) },
|
||||
{ label: '3次+合格率', value: formatRate(statisticsData.thirdOrMorePassRate) },
|
||||
{ label: '不合格率', value: formatRate(statisticsData.unqualifiedRate) },
|
||||
{ label: '不合格次数', value: statisticsData.unqualifiedItemCount }
|
||||
])
|
||||
|
||||
const resetData = () => {
|
||||
Object.assign(statisticsData, emptyStatistics())
|
||||
}
|
||||
|
||||
const formatRate = (value: number | string | null | undefined) => {
|
||||
const numberValue = Number(value)
|
||||
if (!Number.isFinite(numberValue)) return '0%'
|
||||
return `${numberValue.toFixed(2)}%`
|
||||
}
|
||||
|
||||
const normalizeRate = (value: number | string | null | undefined) => {
|
||||
const numberValue = Number(value)
|
||||
return Number.isFinite(numberValue) ? numberValue : 0
|
||||
}
|
||||
|
||||
const open = async (row: Partial<Plan.ReqPlan>) => {
|
||||
if (!row.id) {
|
||||
ElMessage.error('计划信息缺失,无法统计')
|
||||
return
|
||||
}
|
||||
resetData()
|
||||
disposeCharts()
|
||||
loadFailed.value = false
|
||||
planName.value = row.name || ''
|
||||
dialogVisible.value = true
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const { data } = await getPlanStatistics({ planId: row.id })
|
||||
Object.assign(statisticsData, {
|
||||
...emptyStatistics(),
|
||||
...data,
|
||||
itemDistributions: data?.itemDistributions || []
|
||||
})
|
||||
await nextTick()
|
||||
renderCharts()
|
||||
} catch (error) {
|
||||
loadFailed.value = true
|
||||
ElMessage.error('统计数据加载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const renderCharts = () => {
|
||||
if (!dialogVisible.value || loadFailed.value || isEmpty.value) return
|
||||
renderRateChart()
|
||||
renderItemChart()
|
||||
resizeCharts()
|
||||
}
|
||||
|
||||
const renderRateChart = () => {
|
||||
if (!rateChartRef.value) return
|
||||
rateChart?.dispose()
|
||||
rateChart = echarts.init(rateChartRef.value)
|
||||
const rateData = [
|
||||
{
|
||||
name: '一次合格率',
|
||||
value: normalizeRate(statisticsData.firstPassRate),
|
||||
count: statisticsData.firstQualifiedDeviceCount
|
||||
},
|
||||
{
|
||||
name: '二次合格率',
|
||||
value: normalizeRate(statisticsData.secondPassRate),
|
||||
count: statisticsData.secondQualifiedDeviceCount
|
||||
},
|
||||
{
|
||||
name: '三次及以上合格率',
|
||||
value: normalizeRate(statisticsData.thirdOrMorePassRate),
|
||||
count: statisticsData.thirdOrMoreQualifiedDeviceCount
|
||||
},
|
||||
{
|
||||
name: '不合格率',
|
||||
value: normalizeRate(statisticsData.unqualifiedRate),
|
||||
count: statisticsData.unqualifiedDeviceCount
|
||||
}
|
||||
]
|
||||
rateChart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: (params: any) => {
|
||||
return `${params.name}<br/>${formatRate(params.value)}<br/>设备数:${params.data?.count || 0}`
|
||||
}
|
||||
},
|
||||
legend: { bottom: 0, left: 'center' },
|
||||
color: ['#67c23a', '#409eff', '#e6a23c', '#f56c6c'],
|
||||
series: [
|
||||
{
|
||||
name: '合格率',
|
||||
type: 'pie',
|
||||
radius: ['42%', '68%'],
|
||||
center: ['50%', '43%'],
|
||||
avoidLabelOverlap: true,
|
||||
data: rateData,
|
||||
label: {
|
||||
formatter: ({ name, value }: any) => `${name}\n${formatRate(value)}`
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
const renderItemChart = () => {
|
||||
if (!itemChartRef.value) return
|
||||
itemChart?.dispose()
|
||||
itemChart = echarts.init(itemChartRef.value)
|
||||
const topItems = [...statisticsData.itemDistributions]
|
||||
.sort((a, b) => (b.unqualifiedCount || 0) - (a.unqualifiedCount || 0))
|
||||
.slice(0, 8)
|
||||
itemChart.setOption({
|
||||
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
||||
grid: { left: 48, right: 20, top: 36, bottom: 48 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: topItems.map(item => item.itemName || '/'),
|
||||
axisLabel: { interval: 0, rotate: 28 }
|
||||
},
|
||||
yAxis: { type: 'value', minInterval: 1 },
|
||||
series: [
|
||||
{
|
||||
name: '不合格次数',
|
||||
type: 'bar',
|
||||
barWidth: 30,
|
||||
data: topItems.map(item => item.unqualifiedCount || 0),
|
||||
itemStyle: { color: '#f56c6c' }
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
const disposeCharts = () => {
|
||||
rateChart?.dispose()
|
||||
itemChart?.dispose()
|
||||
rateChart = null
|
||||
itemChart = null
|
||||
}
|
||||
|
||||
const resizeCharts = () => {
|
||||
rateChart?.resize()
|
||||
itemChart?.resize()
|
||||
}
|
||||
|
||||
const handleClosed = () => {
|
||||
disposeCharts()
|
||||
resetData()
|
||||
loadFailed.value = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', resizeCharts)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', resizeCharts)
|
||||
disposeCharts()
|
||||
})
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.plan-statistics-dialog) {
|
||||
max-width: 92vw;
|
||||
}
|
||||
|
||||
:deep(.plan-statistics-dialog .el-dialog__body) {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.plan-statistics {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
min-height: 64px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 6px;
|
||||
background: var(--el-fill-color-lighter);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
display: block;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.summary-value {
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.chart-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.chart-panel {
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.empty-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 320px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.summary-grid {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.summary-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.chart-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: 260px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
:deep(.plan-statistics-dialog) {
|
||||
width: 96vw;
|
||||
max-width: 96vw;
|
||||
}
|
||||
|
||||
:deep(.plan-statistics-dialog .el-dialog__body) {
|
||||
max-height: calc(92vh - 110px);
|
||||
overflow-y: auto;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.summary-grid {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.summary-value {
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -99,6 +99,16 @@
|
||||
被检设备
|
||||
</el-button>
|
||||
<!-- <el-button type='primary' link :icon='List' @click='showDeviceOpen(scope.row)'>设备绑定</el-button> -->
|
||||
<el-button
|
||||
type="primary"
|
||||
v-auth.plan="'analysis'"
|
||||
link
|
||||
icon="PieChart"
|
||||
v-if="scope.row.testState == '2' && modeStore.currentMode != '比对式'"
|
||||
@click="openStatistics(scope.row)"
|
||||
>
|
||||
统计
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
v-auth.plan="'analysis'"
|
||||
@@ -136,6 +146,7 @@
|
||||
|
||||
<ImportExcel ref="planImportExcel" />
|
||||
<ImportZip ref="planImportZip" @result="importResult" />
|
||||
<PlanStatisticsPopup ref="planStatisticsPopupRef" />
|
||||
|
||||
<ChildrenPlan
|
||||
:refresh-table="refreshTable"
|
||||
@@ -163,6 +174,7 @@ import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import type { Plan } from '@/api/plan/interface'
|
||||
import PlanPopup from '@/views/plan/planList/components/planPopup.vue' // 导入子组件
|
||||
import ChildrenPlan from '@/views/plan/planList/components/childrenPlan.vue'
|
||||
import PlanStatisticsPopup from '@/views/plan/planList/components/planStatisticsPopup.vue'
|
||||
import { useViewSize } from '@/hooks/useViewSize'
|
||||
import { useDictStore } from '@/stores/modules/dict'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
@@ -187,6 +199,7 @@ const proTable = ref<ProTableInstance>()
|
||||
const errorStandardPopup = ref()
|
||||
const testSourcePopup = ref()
|
||||
const planPopup = ref()
|
||||
const planStatisticsPopupRef = ref<InstanceType<typeof PlanStatisticsPopup> | null>(null)
|
||||
|
||||
const modeStore = useModeStore()
|
||||
const tableData = ref<any[]>([])
|
||||
@@ -530,7 +543,7 @@ const columns = reactive<ColumnProps<Plan.ReqPlan>[]>([
|
||||
isShow: modeStore.currentMode == '比对式'
|
||||
},
|
||||
|
||||
{ prop: 'operation', label: '操作', fixed: 'right', minWidth: 250 }
|
||||
{ prop: 'operation', label: '操作', fixed: 'right', minWidth: 320 }
|
||||
])
|
||||
|
||||
function isVisible(row: Plan.ReqPlan) {
|
||||
@@ -654,6 +667,10 @@ const statisticalAnalysis = async (row: Partial<Plan.ReqPlan> = {}) => {
|
||||
useDownload(staticsAnalyse, '分析结果', [row.id], false, '.xlsx')
|
||||
}
|
||||
|
||||
const openStatistics = (row: Partial<Plan.ReqPlan> = {}) => {
|
||||
planStatisticsPopupRef.value?.open(row)
|
||||
}
|
||||
|
||||
const importSubClick = () => {
|
||||
const params = {
|
||||
title: '导入检测计划',
|
||||
@@ -671,4 +688,4 @@ const importResult = async (success: boolean | undefined) => {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped></style>
|
||||
|
||||
Reference in New Issue
Block a user