From d5d3f57f11d829909bd2b55bc1bab331b69a1607 Mon Sep 17 00:00:00 2001 From: gaoluyang <2820782392@qq.com> Date: 星期一, 18 八月 2025 15:28:24 +0800 Subject: [PATCH] 中强恒兴质量管理页面添加 --- src/views/reportAnalysis/reportManagement/index.vue | 716 +++++++++++++++++++++++ src/views/qualityManagement/visualization/qualityDashboard.vue | 307 ++++++++++ src/views/qualityManagement/nonconformingManagement/index.vue | 7 src/views/reportAnalysis/reportManagement.vue | 733 ++++++++++++++++++++++++ 4 files changed, 1,760 insertions(+), 3 deletions(-) diff --git a/src/views/qualityManagement/nonconformingManagement/index.vue b/src/views/qualityManagement/nonconformingManagement/index.vue index d3ac667..169a2f3 100644 --- a/src/views/qualityManagement/nonconformingManagement/index.vue +++ b/src/views/qualityManagement/nonconformingManagement/index.vue @@ -4,7 +4,7 @@ <div style="display: flex;flex-direction: row;align-items: center;"> <div> <span class="search_title">绫诲瀷锛�</span> - <el-select v-model="searchForm.inspectType" clearable style="width: 240px" @change="handleQuery"> + <el-select v-model="searchForm.inspectType" clearable style="width: 200px" @change="handleQuery"> <el-option label="鍘熸潗鏂欐楠�" :value="0" /> <el-option label="杩囩▼妫�楠�" :value="1" /> <el-option label="鍑哄巶妫�楠�" :value="2" /> @@ -12,7 +12,7 @@ </div> <div style="margin-left: 10px"> <span class="search_title">鐘舵�侊細</span> - <el-select v-model="searchForm.inspectState" clearable style="width: 240px" @change="handleQuery"> + <el-select v-model="searchForm.inspectState" clearable style="width: 200px" @change="handleQuery"> <el-option label="寰呭鐞�" :value="0" /> <el-option label="宸插鐞�" :value="1" /> </el-select> @@ -21,7 +21,7 @@ <span class="search_title">浜у搧鍚嶇О锛�</span> <el-input v-model="searchForm.productName" - style="width: 240px" + style="width: 200px" placeholder="璇疯緭鍏ヤ骇鍝佸悕绉版悳绱�" @change="handleQuery" clearable @@ -30,6 +30,7 @@ </div> <span style="margin-left: 10px" class="search_title">妫�娴嬫棩鏈燂細</span> <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" + style="width: 300px" placeholder="璇烽�夋嫨" clearable @change="changeDaterange" /> <el-button type="primary" @click="handleQuery" style="margin-left: 10px">鎼滅储</el-button> </div> diff --git a/src/views/qualityManagement/visualization/qualityDashboard.vue b/src/views/qualityManagement/visualization/qualityDashboard.vue new file mode 100644 index 0000000..57462b7 --- /dev/null +++ b/src/views/qualityManagement/visualization/qualityDashboard.vue @@ -0,0 +1,307 @@ +<template> + <div class="quality-dashboard"> + <el-row :gutter="16"> + <el-col :xs="24" :sm="12"> + <el-card shadow="hover" class="panel"> + <template #header> + <div class="panel-title"> + 妫�娴嬫牱鍝佸姩鎬佺姸鎬� + <div class="actions"> + <el-switch v-model="voiceEnabled" active-text="璇煶棰勮" inactive-text="闈欓煶" /> + </div> + </div> + </template> + <div class="status-list"> + <div v-for="item in sampleStatus" :key="item.id" class="status-item" :class="item.status"> + <div class="left"> + <span class="dot" :class="item.status"></span> + <span class="name">{{ item.name }}</span> + </div> + <div class="right"> + <el-tag :type="statusTagType(item.status)" size="small">{{ statusLabel(item.status) }}</el-tag> + <span class="time">{{ item.time }}</span> + </div> + </div> + </div> + </el-card> + </el-col> + <el-col :xs="24" :sm="12"> + <el-card shadow="hover" class="panel"> + <template #header> + <div class="panel-title">浠诲姟鎺掕锛圱op 10锛�</div> + </template> + <EChart :xAxis="tasksXAxis" :yAxis="[{ type: 'value' }]" :series="tasksSeries" :grid="{ left: 40, right: 20, top: 20, bottom: 40 }" :tooltip="{ trigger: 'axis' }" :barColors="['#3b82f6']" :chartStyle="{ height: '320px', width: '100%' }" /> + </el-card> + </el-col> + </el-row> + + <el-row :gutter="16" style="margin-top: 16px;"> + <el-col :xs="24" :sm="14"> + <el-card shadow="hover" class="panel"> + <template #header> + <div class="panel-title">鍘嗗彶瓒嬪娍</div> + </template> + <EChart :xAxis="[{ type: 'category', data: trendXAxis }]" :yAxis="[{ type: 'value', name: '鏁伴噺' }]" :series="trendSeries" :tooltip="{ trigger: 'axis' }" :legend="{ top: 0 }" :lineColors="['#10b981', '#f59e0b']" :chartStyle="{ height: '340px', width: '100%' }" /> + </el-card> + </el-col> + <el-col :xs="24" :sm="10"> + <el-card shadow="hover" class="panel"> + <template #header> + <div class="panel-title">鍚堟牸鐜囧垎鏋�</div> + </template> + <EChart :series="passRateSeries" :legend="{ show: false }" :chartStyle="{ height: '340px', width: '100%' }" /> + <div class="passrate-text"> + 褰撳墠鍚堟牸鐜囷細<b>{{ (passRate * 100).toFixed(1) }}%</b> + </div> + </el-card> + </el-col> + </el-row> + + <el-row :gutter="16" style="margin-top: 16px;"> + <el-col :xs="24"> + <el-card shadow="hover" class="panel"> + <template #header> + <div class="panel-title">SPC 鎺у埗鍥撅紙Xbar锛�</div> + </template> + <EChart :xAxis="[{ type: 'category', data: spcXAxis }]" :yAxis="[{ type: 'value', name: '娴嬮噺鍊�' }]" :series="spcSeries" :legend="{ top: 0 }" :tooltip="{ trigger: 'axis' }" :lineColors="['#2563eb', '#ef4444', '#f97316', '#22c55e']" :chartStyle="{ height: '380px', width: '100%' }" /> + </el-card> + </el-col> + </el-row> + </div> + +</template> + +<script setup> +import { onMounted, onBeforeUnmount, reactive, ref } from 'vue' +import EChart from '@/components/Echarts/echarts.vue' + +const voiceEnabled = ref(true) +let dataTimer = null + +// 1) 鏍峰搧鍔ㄦ�佺姸鎬侊紙婊氬姩鏇存柊锛� +const sampleStatus = ref([]) +const statusPool = ['processing', 'warning', 'error', 'success'] +function statusLabel(s) { + return s === 'processing' ? '妫�娴嬩腑' : s === 'warning' ? '棰勮' : s === 'error' ? '涓嶅悎鏍�' : '鍚堟牸' +} +function statusTagType(s) { + return s === 'processing' ? 'info' : s === 'warning' ? 'warning' : s === 'error' ? 'danger' : 'success' +} +function randomSample() { + const id = Math.random().toString(36).slice(2, 8) + const status = statusPool[Math.floor(Math.random() * statusPool.length)] + const name = `鏍峰搧-${Math.floor(Math.random() * 900 + 100)}` + const time = new Date().toLocaleTimeString('zh-CN', { hour12: false }) + return { id, name, status, time } +} + +// 2) 浠诲姟鎺掕锛堟煴鐘跺浘锛� +const tasksXAxis = reactive([{ type: 'category', data: [] }]) +const tasksSeries = ref([ + { + type: 'bar', + data: [], + label: { show: true, position: 'inside', align: 'center', verticalAlign: 'middle', color: '#fff' }, + encode: undefined, + }, +]) + +// 3) 鍘嗗彶瓒嬪娍锛堟姌绾匡級 +const trendXAxis = ref([]) +const trendSeries = ref([ + { name: '鏉ユ牱鏁�', type: 'line', smooth: true, data: [] }, + { name: '瀹屾垚鏁�', type: 'line', smooth: true, data: [] }, +]) + +// 4) 鍚堟牸鐜囧垎鏋愶紙浠〃鐩橈級 +const passRate = ref(0.92) +const passRateSeries = ref([ + { + type: 'gauge', + progress: { show: true, width: 12 }, + axisLine: { lineStyle: { width: 12 } }, + pointer: { show: true }, + detail: { valueAnimation: true, formatter: (v) => `${(v * 100).toFixed(1)}%` }, + data: [{ value: passRate.value }], + }, +]) + +// 5) SPC 鎺у埗鍥� +const spcXAxis = ref([]) +const spcData = ref([]) // 瀹為檯娴嬮噺鍊� +const CL = ref(50) +const UCL = ref(55) +const LCL = ref(45) +const spcSeries = ref([ + { + name: '娴嬮噺鍧囧��', + type: 'line', + smooth: false, + symbol: 'circle', + data: [], + markLine: { + symbol: 'none', + lineStyle: { type: 'dashed', color: '#999' }, + data: [ + { yAxis: () => UCL.value, name: 'UCL' }, + { yAxis: () => CL.value, name: 'CL' }, + { yAxis: () => LCL.value, name: 'LCL' }, + ], + label: { formatter: ({ name }) => name }, + }, + }, + { name: 'UCL', type: 'line', data: [], symbol: 'none', lineStyle: { type: 'dashed', color: '#ef4444' } }, + { name: 'CL', type: 'line', data: [], symbol: 'none', lineStyle: { type: 'dashed', color: '#f97316' } }, + { name: 'LCL', type: 'line', data: [], symbol: 'none', lineStyle: { type: 'dashed', color: '#22c55e' } }, +]) + +// 璇煶鎾姤 +function speak(text) { + if (!voiceEnabled.value) return + if (!('speechSynthesis' in window)) return + const utter = new SpeechSynthesisUtterance(text) + utter.lang = 'zh-CN' + try { + window.speechSynthesis.cancel() + window.speechSynthesis.speak(utter) + } catch (e) { + // ignore + } +} + +function refreshFakeData() { + // 鏍峰搧鐘舵�佹粴鍔� + const next = randomSample() + sampleStatus.value = [next, ...sampleStatus.value].slice(0, 8) + + // 浠诲姟鎺掕 + const tasks = Array.from({ length: 10 }).map((_, i) => ({ name: `浠诲姟-${i + 1}`, count: Math.floor(Math.random() * 100 + 20) })) + tasks.sort((a, b) => a.count - b.count) + tasksXAxis.data = tasks.map(t => t.name) + tasksSeries.value[0].data = tasks.map(t => t.count) + + // 鍘嗗彶瓒嬪娍锛堣拷鍔犵偣锛� + const nowLabel = new Date().toLocaleTimeString('zh-CN', { minute: '2-digit', second: '2-digit' }) + if (trendXAxis.value.length > 15) { + trendXAxis.value.shift() + trendSeries.value[0].data.shift() + trendSeries.value[1].data.shift() + } + trendXAxis.value.push(nowLabel) + const incoming = Math.floor(Math.random() * 30 + 20) + const finished = Math.max(0, incoming - Math.floor(Math.random() * 10)) + trendSeries.value[0].data.push(incoming) + trendSeries.value[1].data.push(finished) + + // 鍚堟牸鐜囷紙杞诲井娉㈠姩锛� + const delta = (Math.random() - 0.5) * 0.02 + passRate.value = Math.min(0.99, Math.max(0.6, passRate.value + delta)) + passRateSeries.value[0].data[0].value = passRate.value + + // SPC 鏁版嵁锛堢獥鍙gЩ鍔級 + const nextVal = CL.value + (Math.random() - 0.5) * 8 // 娉㈠姩 + if (spcXAxis.value.length > 30) { + spcXAxis.value.shift() + spcData.value.shift() + } + spcXAxis.value.push(`${spcXAxis.value.length + 1}`) + spcData.value.push(parseFloat(nextVal.toFixed(2))) + spcSeries.value[0].data = [...spcData.value] + spcSeries.value[1].data = new Array(spcData.value.length).fill(UCL.value) + spcSeries.value[2].data = new Array(spcData.value.length).fill(CL.value) + spcSeries.value[3].data = new Array(spcData.value.length).fill(LCL.value) + + // 瑙﹀彂鎾姤锛氬悎鏍肩巼杩囦綆鎴� SPC 瓒呴檺 + if (passRate.value < 0.8) { + speak(`棰勮锛屽綋鍓嶅悎鏍肩巼涓� ${(passRate.value * 100).toFixed(0)}%锛屼綆浜� 80% 闃堝�糮) + } + const last = spcData.value[spcData.value.length - 1] + if (last > UCL.value) { + speak(`棰勮锛屾渶鏂版祴閲忓�� ${last.toFixed(2)} 瓒呰繃涓婇檺`) + } + if (last < LCL.value) { + speak(`棰勮锛屾渶鏂版祴閲忓�� ${last.toFixed(2)} 浣庝簬涓嬮檺`) + } +} + +onMounted(() => { + // 鍒濆鍖栧嚑鏉″亣鏁版嵁 + sampleStatus.value = Array.from({ length: 5 }).map(() => randomSample()) + for (let i = 0; i < 10; i++) { + trendXAxis.value.push(`T-${i}`) + trendSeries.value[0].data.push(Math.floor(Math.random() * 30 + 20)) + trendSeries.value[1].data.push(Math.floor(Math.random() * 25 + 15)) + } + for (let i = 0; i < 20; i++) { + spcXAxis.value.push(`${i + 1}`) + const v = CL.value + (Math.random() - 0.5) * 6 + spcData.value.push(parseFloat(v.toFixed(2))) + } + spcSeries.value[0].data = [...spcData.value] + spcSeries.value[1].data = new Array(spcData.value.length).fill(UCL.value) + spcSeries.value[2].data = new Array(spcData.value.length).fill(CL.value) + spcSeries.value[3].data = new Array(spcData.value.length).fill(LCL.value) + + dataTimer = setInterval(refreshFakeData, 10000) +}) + +onBeforeUnmount(() => { + if (dataTimer) clearInterval(dataTimer) + try { window.speechSynthesis && window.speechSynthesis.cancel() } catch (e) {} +}) +</script> + +<style scoped> +.quality-dashboard { + padding: 8px; +} +.panel-title { + display: flex; + align-items: center; + justify-content: space-between; + font-weight: 600; +} +.status-list { + display: flex; + flex-direction: column; + gap: 8px; + max-height: 320px; + overflow: auto; +} +.status-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 10px; + border-radius: 6px; + background: var(--el-fill-color-light); +} +.status-item .left { + display: flex; + align-items: center; + gap: 8px; +} +.status-item .right { + display: flex; + align-items: center; + gap: 10px; +} +.status-item .name { font-weight: 500; } +.status-item .time { color: var(--el-text-color-secondary); font-size: 12px; } +.dot { + width: 8px; + height: 8px; + border-radius: 50%; + display: inline-block; +} +.dot.processing { background: #60a5fa; } +.dot.warning { background: #f59e0b; } +.dot.error { background: #ef4444; } +.dot.success { background: #10b981; } +.passrate-text { + text-align: center; + margin-top: 8px; +} +</style> + + diff --git a/src/views/reportAnalysis/reportManagement.vue b/src/views/reportAnalysis/reportManagement.vue new file mode 100644 index 0000000..02babb5 --- /dev/null +++ b/src/views/reportAnalysis/reportManagement.vue @@ -0,0 +1,733 @@ +<template> + <div class="report-management"> + <!-- 椤甸潰鏍囬 --> + <div class="page-header"> + <h2>鎶ヨ〃绠$悊</h2> + <p>鎻愪緵鏍峰搧杩涘害銆佽澶囦娇鐢ㄣ�佹娴嬮」鐩�侀鐢ㄨ褰曠瓑鍚勭被鑷姩缁熻鎶ヨ〃</p> + </div> + + <!-- 绛涢�夋潯浠� --> + <el-card class="filter-card" shadow="never"> + <el-form :model="filterForm" inline> + <el-form-item label="鏃堕棿鑼冨洿"> + <el-date-picker + v-model="filterForm.dateRange" + type="daterange" + range-separator="鑷�" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + format="YYYY-MM-DD" + value-format="YYYY-MM-DD" + @change="handleFilterChange" + /> + </el-form-item> + <el-form-item label="鎶ヨ〃绫诲瀷"> + <el-select v-model="filterForm.reportType" placeholder="璇烽�夋嫨鎶ヨ〃绫诲瀷" @change="handleFilterChange"> + <el-option label="鏍峰搧杩涘害鎶ヨ〃" value="sample" /> + <el-option label="璁惧浣跨敤鎶ヨ〃" value="equipment" /> + <el-option label="妫�娴嬮」鐩姤琛�" value="inspection" /> + <el-option label="棰嗙敤璁板綍鎶ヨ〃" value="usage" /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="handleFilterChange">鏌ヨ</el-button> + <el-button @click="resetFilter">閲嶇疆</el-button> + <el-button type="success" @click="exportReport">瀵煎嚭鎶ヨ〃</el-button> + </el-form-item> + </el-form> + </el-card> + + <!-- 缁熻鍗$墖 --> + <div class="statistics-cards"> + <el-row :gutter="20"> + <el-col :span="6"> + <el-card class="stat-card" shadow="hover"> + <div class="stat-content"> + <div class="stat-icon"> + <el-icon><Box /></el-icon> + </div> + <div class="stat-info"> + <div class="stat-number">{{ statistics.totalSamples }}</div> + <div class="stat-label">鎬绘牱鍝佹暟</div> + </div> + </div> + </el-card> + </el-col> + <el-col :span="6"> + <el-card class="stat-card" shadow="hover"> + <div class="stat-content"> + <div class="stat-icon"> + <el-icon><Tools /></el-icon> + </div> + <div class="stat-info"> + <div class="stat-number">{{ statistics.activeEquipment }}</div> + <div class="stat-label">鍦ㄧ敤璁惧</div> + </div> + </div> + </el-card> + </el-col> + <el-col :span="6"> + <el-card class="stat-card" shadow="hover"> + <div class="stat-content"> + <div class="stat-icon"> + <el-icon><Document /></el-icon> + </div> + <div class="stat-info"> + <div class="stat-number">{{ statistics.completedInspections }}</div> + <div class="stat-label">宸插畬鎴愭娴�</div> + </div> + </div> + </el-card> + </el-col> + <el-col :span="6"> + <el-card class="stat-card" shadow="hover"> + <div class="stat-content"> + <div class="stat-icon"> + <el-icon><ShoppingCart /></el-icon> + </div> + <div class="stat-info"> + <div class="stat-number">{{ statistics.totalUsage }}</div> + <div class="stat-label">鎬婚鐢ㄦ鏁�</div> + </div> + </div> + </el-card> + </el-col> + </el-row> + </div> + + <!-- 鍥捐〃鍖哄煙 --> + <div class="charts-container"> + <el-row :gutter="20"> + <!-- 鏍峰搧杩涘害鍥捐〃 --> + <el-col :span="12"> + <el-card class="chart-card" shadow="hover"> + <template #header> + <div class="card-header"> + <span>鏍峰搧杩涘害缁熻</span> + <el-button type="text" @click="refreshSampleChart">鍒锋柊</el-button> + </div> + </template> + <div ref="sampleChartRef" class="chart-container"></div> + </el-card> + </el-col> + + <!-- 璁惧浣跨敤鍥捐〃 --> + <el-col :span="12"> + <el-card class="chart-card" shadow="hover"> + <template #header> + <div class="card-header"> + <span>璁惧浣跨敤鐜囩粺璁�</span> + <el-button type="text" @click="refreshEquipmentChart">鍒锋柊</el-button> + </div> + </template> + <div ref="equipmentChartRef" class="chart-container"></div> + </el-card> + </el-col> + </el-row> + + <el-row :gutter="20" style="margin-top: 20px;"> + <!-- 妫�娴嬮」鐩粺璁� --> + <el-col :span="12"> + <el-card class="chart-card" shadow="hover"> + <template #header> + <div class="card-header"> + <span>妫�娴嬮」鐩垎甯�</span> + <el-button type="text" @click="refreshInspectionChart">鍒锋柊</el-button> + </div> + </template> + <div ref="inspectionChartRef" class="chart-container"></div> + </el-card> + </el-col> + + <!-- 棰嗙敤璁板綍瓒嬪娍 --> + <el-col :span="12"> + <el-card class="chart-card" shadow="hover"> + <template #header> + <div class="card-header"> + <span>棰嗙敤璁板綍瓒嬪娍</span> + <el-button type="text" @click="refreshUsageChart">鍒锋柊</el-button> + </div> + </template> + <div ref="usageChartRef" class="chart-container"></div> + </el-card> + </el-col> + </el-row> + </div> + + <!-- 璇︾粏鏁版嵁琛ㄦ牸 --> + <el-card class="table-card" shadow="hover"> + <template #header> + <div class="card-header"> + <span>璇︾粏鏁版嵁</span> + <div> + <el-button type="primary" size="small" @click="refreshTable">鍒锋柊</el-button> + <el-button type="success" size="small" @click="exportTable">瀵煎嚭</el-button> + </div> + </div> + </template> + + <el-table + :data="tableData" + style="width: 100%" + v-loading="tableLoading" + stripe + border + > + <el-table-column prop="id" label="缂栧彿" width="80" /> + <el-table-column prop="name" label="鍚嶇О" /> + <el-table-column prop="type" label="绫诲瀷" width="120" /> + <el-table-column prop="status" label="鐘舵��" width="100"> + <template #default="scope"> + <el-tag :type="getStatusType(scope.row.status)"> + {{ scope.row.status }} + </el-tag> + </template> + </el-table-column> + <el-table-column prop="progress" label="杩涘害" width="120"> + <template #default="scope"> + <el-progress :percentage="scope.row.progress" :status="getProgressStatus(scope.row.progress)" /> + </template> + </el-table-column> + <el-table-column prop="createTime" label="鍒涘缓鏃堕棿" width="180" /> + <el-table-column prop="updateTime" label="鏇存柊鏃堕棿" width="180" /> + <el-table-column label="鎿嶄綔" width="150" fixed="right"> + <template #default="scope"> + <el-button type="text" size="small" @click="viewDetail(scope.row)">鏌ョ湅</el-button> + <el-button type="text" size="small" @click="editItem(scope.row)">缂栬緫</el-button> + </template> + </el-table-column> + </el-table> + + <div class="pagination-container"> + <el-pagination + v-model:current-page="pagination.currentPage" + v-model:page-size="pagination.pageSize" + :page-sizes="[10, 20, 50, 100]" + :total="pagination.total" + layout="total, sizes, prev, pager, next, jumper" + @size-change="handleSizeChange" + @current-change="handleCurrentChange" + /> + </div> + </el-card> + </div> +</template> + +<script setup> +import { ref, reactive, onMounted, nextTick } from 'vue' +import { ElMessage, ElMessageBox } from 'element-plus' +import * as echarts from 'echarts' +import { Box, Tools, Document, ShoppingCart } from '@element-plus/icons-vue' + +// 鍝嶅簲寮忔暟鎹� +const filterForm = reactive({ + dateRange: [], + reportType: 'sample' +}) + +const statistics = reactive({ + totalSamples: 1250, + activeEquipment: 45, + completedInspections: 890, + totalUsage: 2340 +}) + +const tableData = ref([]) +const tableLoading = ref(false) +const pagination = reactive({ + currentPage: 1, + pageSize: 20, + total: 0 +}) + +// 鍥捐〃寮曠敤 +const sampleChartRef = ref(null) +const equipmentChartRef = ref(null) +const inspectionChartRef = ref(null) +const usageChartRef = ref(null) + +// 鍥捐〃瀹炰緥 +let sampleChart = null +let equipmentChart = null +let inspectionChart = null +let usageChart = null + +// 鍒濆鍖栨暟鎹� +const initData = () => { + // 妯℃嫙琛ㄦ牸鏁版嵁 + tableData.value = [ + { + id: 'SP001', + name: '鏍峰搧A-001', + type: '閲戝睘鏉愭枡', + status: '妫�娴嬩腑', + progress: 75, + createTime: '2024-01-15 09:30:00', + updateTime: '2024-01-20 14:20:00' + }, + { + id: 'SP002', + name: '鏍峰搧B-002', + type: '濉戞枡鍒跺搧', + status: '宸插畬鎴�', + progress: 100, + createTime: '2024-01-10 10:15:00', + updateTime: '2024-01-18 16:45:00' + }, + { + id: 'SP003', + name: '鏍峰搧C-003', + type: '鐢靛瓙鍏冧欢', + status: '寰呮娴�', + progress: 0, + createTime: '2024-01-22 08:45:00', + updateTime: '2024-01-22 08:45:00' + }, + { + id: 'EQ001', + name: '妫�娴嬭澶嘇', + type: '鍏夎氨浠�', + status: '浣跨敤涓�', + progress: 60, + createTime: '2024-01-05 14:20:00', + updateTime: '2024-01-20 11:30:00' + }, + { + id: 'EQ002', + name: '妫�娴嬭澶嘊', + type: '鏄惧井闀�', + status: '绌洪棽', + progress: 0, + createTime: '2024-01-08 16:10:00', + updateTime: '2024-01-19 09:15:00' + } + ] + + pagination.total = tableData.value.length +} + +// 鍒濆鍖栨牱鍝佽繘搴﹀浘琛� +const initSampleChart = () => { + if (sampleChartRef.value) { + sampleChart = echarts.init(sampleChartRef.value) + const option = { + title: { + text: '鏍峰搧杩涘害鍒嗗竷', + left: 'center' + }, + tooltip: { + trigger: 'item', + formatter: '{a} <br/>{b}: {c} ({d}%)' + }, + legend: { + orient: 'vertical', + left: 'left' + }, + series: [ + { + name: '鏍峰搧鐘舵��', + type: 'pie', + radius: ['40%', '70%'], + avoidLabelOverlap: false, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true, + fontSize: '18', + fontWeight: 'bold' + } + }, + labelLine: { + show: false + }, + data: [ + { value: 450, name: '宸插畬鎴�' }, + { value: 320, name: '妫�娴嬩腑' }, + { value: 280, name: '寰呮娴�' }, + { value: 200, name: '宸叉殏鍋�' } + ] + } + ] + } + sampleChart.setOption(option) + } +} + +// 鍒濆鍖栬澶囦娇鐢ㄥ浘琛� +const initEquipmentChart = () => { + if (equipmentChartRef.value) { + equipmentChart = echarts.init(equipmentChartRef.value) + const option = { + title: { + text: '璁惧浣跨敤鐜�', + left: 'center' + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow' + } + }, + xAxis: { + type: 'category', + data: ['鍏夎氨浠�', '鏄惧井闀�', '纭害璁�', '鎷夊姏鏈�', '鍐插嚮鏈�', '閲戠浉浠�'] + }, + yAxis: { + type: 'value', + name: '浣跨敤鐜�(%)' + }, + series: [ + { + name: '浣跨敤鐜�', + type: 'bar', + data: [85, 60, 75, 90, 45, 70], + label: { + show: true, + position: 'inside', + align: 'center', + verticalAlign: 'middle', + formatter: '{c}%', + color: '#fff' + }, + itemStyle: { + color: function(params) { + const colors = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#9C27B0'] + return colors[params.dataIndex] + } + } + } + ] + } + equipmentChart.setOption(option) + } +} + +// 鍒濆鍖栨娴嬮」鐩浘琛� +const initInspectionChart = () => { + if (inspectionChartRef.value) { + inspectionChart = echarts.init(inspectionChartRef.value) + const option = { + title: { + text: '妫�娴嬮」鐩垎甯�', + left: 'center' + }, + tooltip: { + trigger: 'item' + }, + legend: { + orient: 'vertical', + left: 'left' + }, + series: [ + { + name: '妫�娴嬮」鐩�', + type: 'pie', + radius: '50%', + data: [ + { value: 335, name: '鐗╃悊鎬ц兘' }, + { value: 310, name: '鍖栧鍒嗘瀽' }, + { value: 234, name: '灏哄娴嬮噺' }, + { value: 135, name: '澶栬妫�鏌�' }, + { value: 148, name: '鍏朵粬妫�娴�' } + ], + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + } + } + ] + } + inspectionChart.setOption(option) + } +} + +// 鍒濆鍖栭鐢ㄨ褰曞浘琛� +const initUsageChart = () => { + if (usageChartRef.value) { + usageChart = echarts.init(usageChartRef.value) + const option = { + title: { + text: '棰嗙敤璁板綍瓒嬪娍', + left: 'center' + }, + tooltip: { + trigger: 'axis' + }, + legend: { + data: ['棰嗙敤娆℃暟', '褰掕繕娆℃暟'] + }, + xAxis: { + type: 'category', + boundaryGap: false, + data: ['1鏈�', '2鏈�', '3鏈�', '4鏈�', '5鏈�', '6鏈�', '7鏈�', '8鏈�', '9鏈�', '10鏈�', '11鏈�', '12鏈�'] + }, + yAxis: { + type: 'value' + }, + series: [ + { + name: '棰嗙敤娆℃暟', + type: 'line', + stack: 'Total', + data: [120, 132, 101, 134, 90, 230, 210, 182, 191, 234, 290, 330] + }, + { + name: '褰掕繕娆℃暟', + type: 'line', + stack: 'Total', + data: [110, 125, 95, 128, 85, 220, 200, 175, 185, 225, 280, 320] + } + ] + } + usageChart.setOption(option) + } +} + +// 浜嬩欢澶勭悊鍑芥暟 +const handleFilterChange = () => { + ElMessage.success('绛涢�夋潯浠跺凡鏇存柊') + // 杩欓噷鍙互鏍规嵁绛涢�夋潯浠堕噸鏂板姞杞芥暟鎹� +} + +const resetFilter = () => { + filterForm.dateRange = [] + filterForm.reportType = 'sample' + ElMessage.info('绛涢�夋潯浠跺凡閲嶇疆') +} + +const exportReport = () => { + ElMessage.success('鎶ヨ〃瀵煎嚭鍔熻兘寮�鍙戜腑...') +} + +const refreshSampleChart = () => { + initSampleChart() + ElMessage.success('鏍峰搧杩涘害鍥捐〃宸插埛鏂�') +} + +const refreshEquipmentChart = () => { + initEquipmentChart() + ElMessage.success('璁惧浣跨敤鍥捐〃宸插埛鏂�') +} + +const refreshInspectionChart = () => { + initInspectionChart() + ElMessage.success('妫�娴嬮」鐩浘琛ㄥ凡鍒锋柊') +} + +const refreshUsageChart = () => { + initUsageChart() + ElMessage.success('棰嗙敤璁板綍鍥捐〃宸插埛鏂�') +} + +const refreshTable = () => { + tableLoading.value = true + setTimeout(() => { + tableLoading.value = false + ElMessage.success('琛ㄦ牸鏁版嵁宸插埛鏂�') + }, 1000) +} + +const exportTable = () => { + ElMessage.success('琛ㄦ牸瀵煎嚭鍔熻兘寮�鍙戜腑...') +} + +const handleSizeChange = (val) => { + pagination.pageSize = val + // 閲嶆柊鍔犺浇鏁版嵁 +} + +const handleCurrentChange = (val) => { + pagination.currentPage = val + // 閲嶆柊鍔犺浇鏁版嵁 +} + +const getStatusType = (status) => { + const statusMap = { + '宸插畬鎴�': 'success', + '妫�娴嬩腑': 'warning', + '寰呮娴�': 'info', + '宸叉殏鍋�': 'danger', + '浣跨敤涓�': 'primary', + '绌洪棽': 'info' + } + return statusMap[status] || 'info' +} + +const getProgressStatus = (progress) => { + if (progress === 100) return 'success' + if (progress >= 80) return 'warning' + if (progress >= 50) return '' + return 'exception' +} + +const viewDetail = (row) => { + ElMessage.info(`鏌ョ湅璇︽儏: ${row.name}`) +} + +const editItem = (row) => { + ElMessage.info(`缂栬緫椤圭洰: ${row.name}`) +} + +// 鐢熷懡鍛ㄦ湡 +onMounted(() => { + initData() + nextTick(() => { + initSampleChart() + initEquipmentChart() + initInspectionChart() + initUsageChart() + }) + + // 鐩戝惉绐楀彛澶у皬鍙樺寲锛岄噸鏂拌皟鏁村浘琛ㄥぇ灏� + window.addEventListener('resize', () => { + sampleChart?.resize() + equipmentChart?.resize() + inspectionChart?.resize() + usageChart?.resize() + }) +}) +</script> + +<style scoped> +.report-management { + padding: 20px; + background-color: #f5f5f5; + min-height: 100vh; +} + +.page-header { + margin-bottom: 20px; + text-align: center; +} + +.page-header h2 { + color: #303133; + margin-bottom: 8px; + font-size: 24px; + font-weight: 600; +} + +.page-header p { + color: #909399; + font-size: 14px; + margin: 0; +} + +.filter-card { + margin-bottom: 20px; +} + +.statistics-cards { + margin-bottom: 20px; +} + +.stat-card { + height: 120px; +} + +.stat-content { + display: flex; + align-items: center; + height: 100%; +} + +.stat-icon { + width: 60px; + height: 60px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-right: 20px; + font-size: 24px; + color: white; +} + +.stat-card:nth-child(1) .stat-icon { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +} + +.stat-card:nth-child(2) .stat-icon { + background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); +} + +.stat-card:nth-child(3) .stat-icon { + background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); +} + +.stat-card:nth-child(4) .stat-icon { + background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); +} + +.stat-info { + flex: 1; +} + +.stat-number { + font-size: 28px; + font-weight: bold; + color: #303133; + margin-bottom: 8px; +} + +.stat-label { + font-size: 14px; + color: #909399; +} + +.charts-container { + margin-bottom: 20px; +} + +.chart-card { + margin-bottom: 20px; +} + +.card-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.chart-container { + height: 300px; + width: 100%; +} + +.table-card { + margin-bottom: 20px; +} + +.pagination-container { + margin-top: 20px; + text-align: right; +} + +:deep(.el-card__header) { + padding: 15px 20px; + border-bottom: 1px solid #ebeef5; + background-color: #fafafa; +} + +:deep(.el-card__body) { + padding: 20px; +} + +:deep(.el-table) { + margin-bottom: 20px; +} + +:deep(.el-progress) { + margin: 0; +} + +:deep(.el-tag) { + margin: 0; +} +</style> diff --git a/src/views/reportAnalysis/reportManagement/index.vue b/src/views/reportAnalysis/reportManagement/index.vue new file mode 100644 index 0000000..b25619c --- /dev/null +++ b/src/views/reportAnalysis/reportManagement/index.vue @@ -0,0 +1,716 @@ +<template> + <div class="report-management"> + <!-- 绛涢�夋潯浠� --> + <el-card class="filter-card" shadow="never"> + <el-form :model="filterForm" inline> + <el-form-item label="鏃堕棿鑼冨洿"> + <el-date-picker + style="width: 300px" + v-model="filterForm.dateRange" + type="daterange" + range-separator="鑷�" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + format="YYYY-MM-DD" + value-format="YYYY-MM-DD" + @change="handleFilterChange" + /> + </el-form-item> + <el-form-item label="鎶ヨ〃绫诲瀷"> + <el-select v-model="filterForm.reportType" placeholder="璇烽�夋嫨鎶ヨ〃绫诲瀷" @change="handleFilterChange" style="width: 300px"> + <el-option label="鏍峰搧杩涘害鎶ヨ〃" value="sample" /> + <el-option label="璁惧浣跨敤鎶ヨ〃" value="equipment" /> + <el-option label="妫�娴嬮」鐩姤琛�" value="inspection" /> + <el-option label="棰嗙敤璁板綍鎶ヨ〃" value="usage" /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="handleFilterChange">鏌ヨ</el-button> + <el-button @click="resetFilter">閲嶇疆</el-button> + <el-button type="success" @click="exportReport">瀵煎嚭鎶ヨ〃</el-button> + </el-form-item> + </el-form> + </el-card> + + <!-- 缁熻鍗$墖 --> + <div class="statistics-cards"> + <el-row :gutter="20"> + <el-col :span="6"> + <el-card class="stat-card" shadow="hover"> + <div class="stat-content"> + <div class="stat-icon"> + <el-icon><Box /></el-icon> + </div> + <div class="stat-info"> + <div class="stat-number">{{ statistics.totalSamples }}</div> + <div class="stat-label">鎬绘牱鍝佹暟</div> + </div> + </div> + </el-card> + </el-col> + <el-col :span="6"> + <el-card class="stat-card" shadow="hover"> + <div class="stat-content"> + <div class="stat-icon"> + <el-icon><Tools /></el-icon> + </div> + <div class="stat-info"> + <div class="stat-number">{{ statistics.activeEquipment }}</div> + <div class="stat-label">鍦ㄧ敤璁惧</div> + </div> + </div> + </el-card> + </el-col> + <el-col :span="6"> + <el-card class="stat-card" shadow="hover"> + <div class="stat-content"> + <div class="stat-icon"> + <el-icon><Document /></el-icon> + </div> + <div class="stat-info"> + <div class="stat-number">{{ statistics.completedInspections }}</div> + <div class="stat-label">宸插畬鎴愭娴�</div> + </div> + </div> + </el-card> + </el-col> + <el-col :span="6"> + <el-card class="stat-card" shadow="hover"> + <div class="stat-content"> + <div class="stat-icon"> + <el-icon><ShoppingCart /></el-icon> + </div> + <div class="stat-info"> + <div class="stat-number">{{ statistics.totalUsage }}</div> + <div class="stat-label">鎬婚鐢ㄦ鏁�</div> + </div> + </div> + </el-card> + </el-col> + </el-row> + </div> + + <!-- 鍥捐〃鍖哄煙 --> + <div class="charts-container"> + <el-row :gutter="20"> + <!-- 鏍峰搧杩涘害鍥捐〃 --> + <el-col :span="12"> + <el-card class="chart-card" shadow="hover"> + <template #header> + <div class="card-header"> + <span>鏍峰搧杩涘害缁熻</span> + <el-button link @click="refreshSampleChart">鍒锋柊</el-button> + </div> + </template> + <div ref="sampleChartRef" class="chart-container"></div> + </el-card> + </el-col> + + <!-- 璁惧浣跨敤鍥捐〃 --> + <el-col :span="12"> + <el-card class="chart-card" shadow="hover"> + <template #header> + <div class="card-header"> + <span>璁惧浣跨敤鐜囩粺璁�</span> + <el-button link @click="refreshEquipmentChart">鍒锋柊</el-button> + </div> + </template> + <div ref="equipmentChartRef" class="chart-container"></div> + </el-card> + </el-col> + </el-row> + + <el-row :gutter="20" style="margin-top: 20px;"> + <!-- 妫�娴嬮」鐩粺璁� --> + <el-col :span="12"> + <el-card class="chart-card" shadow="hover"> + <template #header> + <div class="card-header"> + <span>妫�娴嬮」鐩垎甯�</span> + <el-button link @click="refreshInspectionChart">鍒锋柊</el-button> + </div> + </template> + <div ref="inspectionChartRef" class="chart-container"></div> + </el-card> + </el-col> + + <!-- 棰嗙敤璁板綍瓒嬪娍 --> + <el-col :span="12"> + <el-card class="chart-card" shadow="hover"> + <template #header> + <div class="card-header"> + <span>棰嗙敤璁板綍瓒嬪娍</span> + <el-button link @click="refreshUsageChart">鍒锋柊</el-button> + </div> + </template> + <div ref="usageChartRef" class="chart-container"></div> + </el-card> + </el-col> + </el-row> + </div> + + <!-- 璇︾粏鏁版嵁琛ㄦ牸 --> + <el-card class="table-card" shadow="hover"> + <template #header> + <div class="card-header"> + <span>璇︾粏鏁版嵁</span> + <div> + <el-button type="primary" size="small" @click="refreshTable">鍒锋柊</el-button> + <el-button type="success" size="small" @click="exportTable">瀵煎嚭</el-button> + </div> + </div> + </template> + + <el-table + :data="tableData" + style="width: 100%" + v-loading="tableLoading" + stripe + border + > + <el-table-column prop="id" label="缂栧彿" width="80" /> + <el-table-column prop="name" label="鍚嶇О" /> + <el-table-column prop="type" label="绫诲瀷" width="120" /> + <el-table-column prop="status" label="鐘舵��" width="100"> + <template #default="scope"> + <el-tag :type="getStatusType(scope.row.status)"> + {{ scope.row.status }} + </el-tag> + </template> + </el-table-column> + <el-table-column prop="progress" label="杩涘害" width="120"> + <template #default="scope"> + <el-progress :percentage="scope.row.progress" :status="getProgressStatus(scope.row.progress)" /> + </template> + </el-table-column> + <el-table-column prop="createTime" label="鍒涘缓鏃堕棿" width="180" /> + <el-table-column prop="updateTime" label="鏇存柊鏃堕棿" width="180" /> + <el-table-column label="鎿嶄綔" width="150" fixed="right"> + <template #default="scope"> + <el-button link size="small" @click="viewDetail(scope.row)">鏌ョ湅</el-button> + <el-button link size="small" @click="editItem(scope.row)">缂栬緫</el-button> + </template> + </el-table-column> + </el-table> + + <div class="pagination-container"> + <el-pagination + v-model:current-page="pagination.currentPage" + v-model:page-size="pagination.pageSize" + :page-sizes="[10, 20, 50, 100]" + :total="pagination.total" + layout="total, sizes, prev, pager, next, jumper" + @size-change="handleSizeChange" + @current-change="handleCurrentChange" + /> + </div> + </el-card> + </div> +</template> + +<script setup> +import { ref, reactive, onMounted, nextTick } from 'vue' +import { ElMessage, ElMessageBox } from 'element-plus' +import * as echarts from 'echarts' +import { Box, Tools, Document, ShoppingCart } from '@element-plus/icons-vue' + +// 鍝嶅簲寮忔暟鎹� +const filterForm = reactive({ + dateRange: [], + reportType: 'sample' +}) + +const statistics = reactive({ + totalSamples: 1250, + activeEquipment: 45, + completedInspections: 890, + totalUsage: 2340 +}) + +const tableData = ref([]) +const tableLoading = ref(false) +const pagination = reactive({ + currentPage: 1, + pageSize: 20, + total: 0 +}) + +// 鍥捐〃寮曠敤 +const sampleChartRef = ref(null) +const equipmentChartRef = ref(null) +const inspectionChartRef = ref(null) +const usageChartRef = ref(null) + +// 鍥捐〃瀹炰緥 +let sampleChart = null +let equipmentChart = null +let inspectionChart = null +let usageChart = null + +// 鍒濆鍖栨暟鎹� +const initData = () => { + // 妯℃嫙琛ㄦ牸鏁版嵁 + tableData.value = [ + { + id: 'SP001', + name: '鏍峰搧A-001', + type: '閲戝睘鏉愭枡', + status: '妫�娴嬩腑', + progress: 75, + createTime: '2024-01-15 09:30:00', + updateTime: '2024-01-20 14:20:00' + }, + { + id: 'SP002', + name: '鏍峰搧B-002', + type: '濉戞枡鍒跺搧', + status: '宸插畬鎴�', + progress: 100, + createTime: '2024-01-10 10:15:00', + updateTime: '2024-01-18 16:45:00' + }, + { + id: 'SP003', + name: '鏍峰搧C-003', + type: '鐢靛瓙鍏冧欢', + status: '寰呮娴�', + progress: 0, + createTime: '2024-01-22 08:45:00', + updateTime: '2024-01-22 08:45:00' + }, + { + id: 'EQ001', + name: '妫�娴嬭澶嘇', + type: '鍏夎氨浠�', + status: '浣跨敤涓�', + progress: 60, + createTime: '2024-01-05 14:20:00', + updateTime: '2024-01-20 11:30:00' + }, + { + id: 'EQ002', + name: '妫�娴嬭澶嘊', + type: '鏄惧井闀�', + status: '绌洪棽', + progress: 0, + createTime: '2024-01-08 16:10:00', + updateTime: '2024-01-19 09:15:00' + } + ] + + pagination.total = tableData.value.length +} + +// 鍒濆鍖栨牱鍝佽繘搴﹀浘琛� +const initSampleChart = () => { + if (sampleChartRef.value) { + sampleChart = echarts.init(sampleChartRef.value) + const option = { + title: { + show: false + }, + tooltip: { + trigger: 'item', + formatter: '{a} <br/>{b}: {c} ({d}%)' + }, + legend: { + orient: 'vertical', + left: 'left' + }, + series: [ + { + name: '鏍峰搧鐘舵��', + type: 'pie', + radius: ['40%', '70%'], + avoidLabelOverlap: false, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true, + fontSize: '18', + fontWeight: 'bold' + } + }, + labelLine: { + show: false + }, + data: [ + { value: 450, name: '宸插畬鎴�' }, + { value: 320, name: '妫�娴嬩腑' }, + { value: 280, name: '寰呮娴�' }, + { value: 200, name: '宸叉殏鍋�' } + ] + } + ] + } + sampleChart.setOption(option) + } +} + +// 鍒濆鍖栬澶囦娇鐢ㄥ浘琛� +const initEquipmentChart = () => { + if (equipmentChartRef.value) { + equipmentChart = echarts.init(equipmentChartRef.value) + const option = { + title: { + show: false + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow' + } + }, + xAxis: { + type: 'category', + data: ['鍏夎氨浠�', '鏄惧井闀�', '纭害璁�', '鎷夊姏鏈�', '鍐插嚮鏈�', '閲戠浉浠�'] + }, + yAxis: { + type: 'value', + name: '浣跨敤鐜�(%)' + }, + series: [ + { + name: '浣跨敤鐜�', + type: 'bar', + data: [85, 60, 75, 90, 45, 70], + itemStyle: { + color: function(params) { + const colors = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#9C27B0'] + return colors[params.dataIndex] + } + } + } + ] + } + equipmentChart.setOption(option) + } +} + +// 鍒濆鍖栨娴嬮」鐩浘琛� +const initInspectionChart = () => { + if (inspectionChartRef.value) { + inspectionChart = echarts.init(inspectionChartRef.value) + const option = { + title: { + show: false + }, + tooltip: { + trigger: 'item' + }, + legend: { + orient: 'vertical', + left: 'left' + }, + series: [ + { + name: '妫�娴嬮」鐩�', + type: 'pie', + radius: '50%', + data: [ + { value: 335, name: '鐗╃悊鎬ц兘' }, + { value: 310, name: '鍖栧鍒嗘瀽' }, + { value: 234, name: '灏哄娴嬮噺' }, + { value: 135, name: '澶栬妫�鏌�' }, + { value: 148, name: '鍏朵粬妫�娴�' } + ], + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + } + } + ] + } + inspectionChart.setOption(option) + } +} + +// 鍒濆鍖栭鐢ㄨ褰曞浘琛� +const initUsageChart = () => { + if (usageChartRef.value) { + usageChart = echarts.init(usageChartRef.value) + const option = { + title: { + show: false + }, + tooltip: { + trigger: 'axis' + }, + legend: { + data: ['棰嗙敤娆℃暟', '褰掕繕娆℃暟'] + }, + xAxis: { + type: 'category', + boundaryGap: false, + data: ['1鏈�', '2鏈�', '3鏈�', '4鏈�', '5鏈�', '6鏈�', '7鏈�', '8鏈�', '9鏈�', '10鏈�', '11鏈�', '12鏈�'] + }, + yAxis: { + type: 'value' + }, + series: [ + { + name: '棰嗙敤娆℃暟', + type: 'line', + stack: 'Total', + data: [120, 132, 101, 134, 90, 230, 210, 182, 191, 234, 290, 330] + }, + { + name: '褰掕繕娆℃暟', + type: 'line', + stack: 'Total', + data: [110, 125, 95, 128, 85, 220, 200, 175, 185, 225, 280, 320] + } + ] + } + usageChart.setOption(option) + } +} + +// 浜嬩欢澶勭悊鍑芥暟 +const handleFilterChange = () => { + ElMessage.success('绛涢�夋潯浠跺凡鏇存柊') + // 杩欓噷鍙互鏍规嵁绛涢�夋潯浠堕噸鏂板姞杞芥暟鎹� +} + +const resetFilter = () => { + filterForm.dateRange = [] + filterForm.reportType = 'sample' + ElMessage.info('绛涢�夋潯浠跺凡閲嶇疆') +} + +const exportReport = () => { + ElMessage.success('鎶ヨ〃瀵煎嚭鍔熻兘寮�鍙戜腑...') +} + +const refreshSampleChart = () => { + initSampleChart() + ElMessage.success('鏍峰搧杩涘害鍥捐〃宸插埛鏂�') +} + +const refreshEquipmentChart = () => { + initEquipmentChart() + ElMessage.success('璁惧浣跨敤鍥捐〃宸插埛鏂�') +} + +const refreshInspectionChart = () => { + initInspectionChart() + ElMessage.success('妫�娴嬮」鐩浘琛ㄥ凡鍒锋柊') +} + +const refreshUsageChart = () => { + initUsageChart() + ElMessage.success('棰嗙敤璁板綍鍥捐〃宸插埛鏂�') +} + +const refreshTable = () => { + tableLoading.value = true + setTimeout(() => { + tableLoading.value = false + ElMessage.success('琛ㄦ牸鏁版嵁宸插埛鏂�') + }, 1000) +} + +const exportTable = () => { + ElMessage.success('琛ㄦ牸瀵煎嚭鍔熻兘寮�鍙戜腑...') +} + +const handleSizeChange = (val) => { + pagination.pageSize = val + // 閲嶆柊鍔犺浇鏁版嵁 +} + +const handleCurrentChange = (val) => { + pagination.currentPage = val + // 閲嶆柊鍔犺浇鏁版嵁 +} + +const getStatusType = (status) => { + const statusMap = { + '宸插畬鎴�': 'success', + '妫�娴嬩腑': 'warning', + '寰呮娴�': 'info', + '宸叉殏鍋�': 'danger', + '浣跨敤涓�': 'primary', + '绌洪棽': 'info' + } + return statusMap[status] || 'info' +} + +const getProgressStatus = (progress) => { + if (progress === 100) return 'success' + if (progress >= 80) return 'warning' + if (progress >= 50) return '' + return 'exception' +} + +const viewDetail = (row) => { + ElMessage.info(`鏌ョ湅璇︽儏: ${row.name}`) +} + +const editItem = (row) => { + ElMessage.info(`缂栬緫椤圭洰: ${row.name}`) +} + +// 鐢熷懡鍛ㄦ湡 +onMounted(() => { + initData() + nextTick(() => { + initSampleChart() + initEquipmentChart() + initInspectionChart() + initUsageChart() + }) + + // 鐩戝惉绐楀彛澶у皬鍙樺寲锛岄噸鏂拌皟鏁村浘琛ㄥぇ灏� + window.addEventListener('resize', () => { + sampleChart?.resize() + equipmentChart?.resize() + inspectionChart?.resize() + usageChart?.resize() + }) +}) +</script> + +<style scoped> +.report-management { + padding: 20px; + background-color: #f5f5f5; + min-height: 100vh; +} + +.page-header { + margin-bottom: 20px; + text-align: center; +} + +.page-header h2 { + color: #303133; + margin-bottom: 8px; + font-size: 24px; + font-weight: 600; +} + +.page-header p { + color: #909399; + font-size: 14px; + margin: 0; +} + +.filter-card { + margin-bottom: 20px; +} + +.statistics-cards { + margin-bottom: 20px; +} + +.stat-card { + height: 120px; +} + +.stat-content { + display: flex; + align-items: center; + height: 100%; +} + +.stat-icon { + width: 60px; + height: 60px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-right: 20px; + font-size: 24px; + color: white; +} + +.stat-card:nth-child(1) .stat-icon { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +} + +.stat-card:nth-child(2) .stat-icon { + background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); +} + +.stat-card:nth-child(3) .stat-icon { + background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); +} + +.stat-card:nth-child(4) .stat-icon { + background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); +} + +.stat-info { + flex: 1; +} + +.stat-number { + font-size: 28px; + font-weight: bold; + color: #303133; + margin-bottom: 8px; +} + +.stat-label { + font-size: 14px; + color: #909399; +} + +.charts-container { + margin-bottom: 20px; +} + +.chart-card { + margin-bottom: 20px; +} + +.card-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.chart-container { + height: 300px; + width: 100%; +} + +.table-card { + margin-bottom: 20px; +} + +.pagination-container { + margin-top: 20px; + text-align: right; +} + +:deep(.el-card__header) { + padding: 15px 20px; + border-bottom: 1px solid #ebeef5; + background-color: #fafafa; +} + +:deep(.el-card__body) { + padding: 20px; +} + +:deep(.el-table) { + margin-bottom: 20px; +} + +:deep(.el-progress) { + margin: 0; +} + +:deep(.el-tag) { + margin: 0; +} +</style> -- Gitblit v1.9.3