From c7b4b9a2f4c0f05aeb60a9e3f5fba5d9a3676f3f Mon Sep 17 00:00:00 2001 From: gaoluyang <2820782392@qq.com> Date: 星期一, 18 八月 2025 16:22:42 +0800 Subject: [PATCH] 中强恒兴设备管理页面添加 --- src/views/qualityManagement/visualization/qualityDashboard.vue | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 307 insertions(+), 0 deletions(-) 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> + + -- Gitblit v1.9.3