<template>
|
<view class="quality-dashboard-page">
|
<PageHeader title="质量看板" @back="goBack" />
|
|
<scroll-view scroll-y class="dashboard-scroll">
|
<!-- 样品状态列表 -->
|
<view class="dashboard-card">
|
<view class="card-header">
|
<text class="card-title">检测样品动态状态</text>
|
<up-switch v-model="voiceEnabled" size="18" activeText="语音" inactiveText="静音"></up-switch>
|
</view>
|
<view class="status-list">
|
<view v-for="item in sampleStatus" :key="item.id" class="status-item">
|
<view class="status-left">
|
<view class="status-dot" :class="item.status"></view>
|
<text class="sample-name">{{ item.name }}</text>
|
</view>
|
<view class="status-right">
|
<up-tag :text="statusLabel(item.status)" :type="statusTagType(item.status)" size="mini"></up-tag>
|
<text class="sample-time">{{ item.time }}</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<!-- 合格率分析 (仪表盘) -->
|
<view class="dashboard-card">
|
<view class="card-header">
|
<text class="card-title">合格率分析</text>
|
</view>
|
<view class="chart-box">
|
<qiun-data-charts
|
type="gauge"
|
:opts="gaugeOpts"
|
:chartData="gaugeData"
|
/>
|
</view>
|
<view class="passrate-summary">
|
<text>当前合格率:</text>
|
<text class="highlight">{{ (passRate * 100).toFixed(1) }}%</text>
|
</view>
|
</view>
|
|
<!-- 任务排行 (柱状图) -->
|
<view class="dashboard-card">
|
<view class="card-header">
|
<text class="card-title">任务排行 (Top 10)</text>
|
</view>
|
<view class="chart-box">
|
<qiun-data-charts
|
type="column"
|
:opts="columnOpts"
|
:chartData="columnData"
|
/>
|
</view>
|
</view>
|
|
<!-- 历史趋势 (折线图) -->
|
<view class="dashboard-card">
|
<view class="card-header">
|
<text class="card-title">历史趋势</text>
|
</view>
|
<view class="chart-box">
|
<qiun-data-charts
|
type="line"
|
:opts="lineOpts"
|
:chartData="lineData"
|
/>
|
</view>
|
</view>
|
</scroll-view>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue';
|
|
const voiceEnabled = ref(false);
|
let dataTimer = null;
|
|
// 1) 样品动态状态
|
const sampleStatus = ref([]);
|
const statusPool = ['processing', 'warning', 'error', 'success'];
|
|
const statusLabel = (s) => {
|
const labels = { 'processing': '检测中', 'warning': '预警', 'error': '不合格', 'success': '合格' };
|
return labels[s] || '未知';
|
};
|
|
const statusTagType = (s) => {
|
const types = { 'processing': 'primary', 'warning': 'warning', 'error': 'error', 'success': 'success' };
|
return types[s] || 'info';
|
};
|
|
const 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 passRate = ref(0.92);
|
const gaugeData = ref({
|
categories: [{ value: 0.92, color: "#2fc25b" }],
|
series: [{ name: "合格率", data: 0.92 }]
|
});
|
const gaugeOpts = {
|
title: { name: "合格率", color: "#2fc25b", fontSize: 16 },
|
subtitle: { name: "92%", color: "#666666", fontSize: 12 },
|
extra: { gauge: { type: "default", width: 15, labelColor: "#666666", splitLine: { fixRadius: -10 } } }
|
};
|
|
// 3) 任务排行 (柱状图)
|
const columnData = ref({
|
categories: ["任务1", "任务2", "任务3", "任务4", "任务5"],
|
series: [{ name: "完成数", data: [35, 36, 31, 33, 13] }]
|
});
|
const columnOpts = {
|
color: ["#1890FF"],
|
padding: [15, 15, 0, 5],
|
enableScroll: false,
|
xAxis: { disableGrid: true },
|
yAxis: { data: [{ min: 0 }] },
|
extra: { column: { type: "group", width: 30, activeBgColor: "#000000", activeBgOpacity: 0.08 } }
|
};
|
|
// 4) 历史趋势 (折线图)
|
const lineData = ref({
|
categories: ["10:00", "10:05", "10:10", "10:15", "10:20"],
|
series: [
|
{ name: "来样数", data: [35, 8, 25, 37, 4, 20] },
|
{ name: "完成数", data: [70, 40, 65, 100, 44, 68] }
|
]
|
});
|
const lineOpts = {
|
color: ["#1890FF", "#91CB74"],
|
padding: [15, 10, 0, 15],
|
enableScroll: false,
|
xAxis: { disableGrid: true },
|
yAxis: { gridType: "dash", dashLength: 2 },
|
legend: { position: "top" },
|
extra: { line: { type: "straight", width: 2 } }
|
};
|
|
const refreshData = () => {
|
// 模拟数据更新
|
const next = randomSample();
|
sampleStatus.value = [next, ...sampleStatus.value].slice(0, 5);
|
|
const delta = (Math.random() - 0.5) * 0.02;
|
passRate.value = Math.min(0.99, Math.max(0.6, passRate.value + delta));
|
gaugeData.value = {
|
series: [{ name: "合格率", data: passRate.value }]
|
};
|
gaugeOpts.subtitle.name = (passRate.value * 100).toFixed(1) + '%';
|
};
|
|
const goBack = () => {
|
uni.navigateBack();
|
};
|
|
onMounted(() => {
|
for (let i = 0; i < 5; i++) {
|
sampleStatus.value.push(randomSample());
|
}
|
dataTimer = setInterval(refreshData, 3000);
|
});
|
|
onBeforeUnmount(() => {
|
if (dataTimer) clearInterval(dataTimer);
|
});
|
</script>
|
|
<style lang="scss" scoped>
|
.quality-dashboard-page {
|
background-color: #f5f7fa;
|
height: 100vh;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.dashboard-scroll {
|
flex: 1;
|
padding: 20rpx;
|
}
|
|
.dashboard-card {
|
background-color: #ffffff;
|
border-radius: 16rpx;
|
padding: 30rpx;
|
margin-bottom: 30rpx;
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
}
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 30rpx;
|
border-left: 8rpx solid #3c9cff;
|
padding-left: 20rpx;
|
}
|
|
.card-title {
|
font-size: 30rpx;
|
font-weight: bold;
|
color: #303133;
|
}
|
|
.status-list {
|
display: flex;
|
flex-direction: column;
|
gap: 20rpx;
|
}
|
|
.status-item {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 10rpx 0;
|
}
|
|
.status-left {
|
display: flex;
|
align-items: center;
|
gap: 15rpx;
|
}
|
|
.status-dot {
|
width: 12rpx;
|
height: 12rpx;
|
border-radius: 50%;
|
|
&.processing { background-color: #3c9cff; }
|
&.warning { background-color: #f9ae3d; }
|
&.error { background-color: #f56c6c; }
|
&.success { background-color: #5ac725; }
|
}
|
|
.sample-name {
|
font-size: 28rpx;
|
color: #303133;
|
}
|
|
.status-right {
|
display: flex;
|
align-items: center;
|
gap: 20rpx;
|
}
|
|
.sample-time {
|
font-size: 24rpx;
|
color: #909399;
|
}
|
|
.chart-box {
|
width: 100%;
|
height: 400rpx;
|
}
|
|
.passrate-summary {
|
text-align: center;
|
margin-top: 20rpx;
|
font-size: 28rpx;
|
color: #606266;
|
|
.highlight {
|
font-size: 36rpx;
|
font-weight: bold;
|
color: #3c9cff;
|
margin-left: 10rpx;
|
}
|
}
|
</style>
|