From 1a7678abd99b9bdc5682e3ea27e49d65e91f37d0 Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期四, 04 六月 2026 17:12:33 +0800
Subject: [PATCH] feat(report): 报表图表管理 1.报表管理(样品进度报表,检测项目数据,样品领样记录,设备使用记录) 2.数字化语音看板 3.智能图表

---
 src/api/report/workStatistics.js              |   39 
 src/views/report/workStatistics/index.vue     |  326 ++++++
 src/views/report/sampleRecord/index.vue       |  177 +++
 src/views/report/spcChart/index.vue           |  326 ++++++
 src/api/report/testItemData.js                |   39 
 src/api/report/passRate.js                    |   57 +
 src/api/report/normalDistribution.js          |   39 
 src/api/report/spcChart.js                    |   48 
 src/views/report/normalDistribution/index.vue |  303 +++++
 src/views/report/testItemData/index.vue       |  195 +++
 src/views/report/deviceRecord/index.vue       |  235 ++++
 src/views/report/passRate/index.vue           |  325 ++++++
 src/api/report/deviceRecord.js                |   30 
 src/views/report/dashboard/index.vue          |  496 +++++++++
 src/api/report/sampleRecord.js                |   30 
 src/api/report/dashboard.js                   |   56 +
 src/api/report/sampleProgress.js              |   39 
 src/views/report/sampleProgress/index.vue     |  300 +++++
 18 files changed, 3,060 insertions(+), 0 deletions(-)

diff --git a/src/api/report/dashboard.js b/src/api/report/dashboard.js
new file mode 100644
index 0000000..f45db38
--- /dev/null
+++ b/src/api/report/dashboard.js
@@ -0,0 +1,56 @@
+// 鏁板瓧鍖栬闊崇湅鏉� - 璇曢獙澶у巺鎺ュ彛
+import request from '@/utils/request'
+
+// 鑾峰彇鐪嬫澘姒傝鏁版嵁
+export function getOverview(query) {
+  return request({
+    url: '/report/dashboard/overview',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鍘嗗彶15澶╂娴嬩换鍔℃暟鎹�
+export function getHistory15Days(query) {
+  return request({
+    url: '/report/dashboard/history15Days',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏈潵15澶╀换鍔�
+export function getFuture15Days(query) {
+  return request({
+    url: '/report/dashboard/future15Days',
+    method: 'get',
+    params: query
+  })
+}
+
+// 杩�15澶╂彁浜ゆ帓琛�
+export function getRanking(query) {
+  return request({
+    url: '/report/dashboard/ranking',
+    method: 'get',
+    params: query
+  })
+}
+
+// 杩�30澶╂楠岀粨鏋滅粺璁�
+export function getInsResult(query) {
+  return request({
+    url: '/report/dashboard/insResult',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鑾峰彇璇煶鎾姤闃熷垪
+export function getVoiceQueue(query) {
+  return request({
+    url: '/report/dashboard/voiceQueue',
+    method: 'get',
+    params: query
+  })
+}
\ No newline at end of file
diff --git a/src/api/report/deviceRecord.js b/src/api/report/deviceRecord.js
new file mode 100644
index 0000000..a2b803c
--- /dev/null
+++ b/src/api/report/deviceRecord.js
@@ -0,0 +1,30 @@
+// 璁惧浣跨敤璁板綍鎺ュ彛
+import request from '@/utils/request'
+
+// 鍒嗛〉鏌ヨ璁惧浣跨敤璁板綍
+export function pageDeviceRecord(query) {
+  return request({
+    url: '/report/deviceRecord/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 璁惧浣跨敤缁熻
+export function getDeviceStatistics(query) {
+  return request({
+    url: '/report/deviceRecord/statistics',
+    method: 'get',
+    params: query
+  })
+}
+
+// 瀵煎嚭璁惧浣跨敤璁板綍
+export function exportDeviceRecord(query) {
+  return request({
+    url: '/report/deviceRecord/export',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}
diff --git a/src/api/report/normalDistribution.js b/src/api/report/normalDistribution.js
new file mode 100644
index 0000000..e8d1fd9
--- /dev/null
+++ b/src/api/report/normalDistribution.js
@@ -0,0 +1,39 @@
+// 姝f�佸垎甯冨浘鎺ュ彛
+import request from '@/utils/request'
+
+// 姝f�佸垎甯冨垎鏋�
+export function normalDistributionAnalyze(data) {
+  return request({
+    url: '/chart/normalDistribution/analyze',
+    method: 'post',
+    data: data
+  })
+}
+
+// 瀵煎嚭鍒嗘瀽鏁版嵁
+export function exportNormalDistribution(query) {
+  return request({
+    url: '/chart/normalDistribution/export',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}
+
+// 鑾峰彇妫�娴嬮」鐩垪琛�
+export function getProjectList(query) {
+  return request({
+    url: '/chart/normalDistribution/projectList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鑾峰彇妫�娴嬪弬鏁板垪琛�
+export function getParamList(projectId) {
+  return request({
+    url: '/chart/normalDistribution/paramList',
+    method: 'get',
+    params: { projectId }
+  })
+}
diff --git a/src/api/report/passRate.js b/src/api/report/passRate.js
new file mode 100644
index 0000000..96d58f3
--- /dev/null
+++ b/src/api/report/passRate.js
@@ -0,0 +1,57 @@
+// 鍚堟牸鐜囩粺璁℃帴鍙�
+import request from '@/utils/request'
+
+// 鍘熸潗鏂欏悎鏍肩巼
+export function getRawMaterialPassRate(query) {
+  return request({
+    url: '/chart/passRate/rawMaterial',
+    method: 'get',
+    params: query
+  })
+}
+
+// 渚涘簲鍟嗕笉鍚堟牸缁熻
+export function getSupplierUnqualified(query) {
+  return request({
+    url: '/chart/passRate/supplier',
+    method: 'get',
+    params: query
+  })
+}
+
+// 甯曠疮鎵樺浘鏁版嵁
+export function getParetoData(query) {
+  return request({
+    url: '/chart/passRate/pareto',
+    method: 'get',
+    params: query
+  })
+}
+
+// 宸ュ簭鍚堟牸鐜�
+export function getProcessPassRate(query) {
+  return request({
+    url: '/chart/passRate/process',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏈哄彴涓嶅悎鏍肩粺璁�
+export function getMachineUnqualified(query) {
+  return request({
+    url: '/chart/passRate/machine',
+    method: 'get',
+    params: query
+  })
+}
+
+// 瀵煎嚭鍚堟牸鐜囩粺璁�
+export function exportPassRate(query) {
+  return request({
+    url: '/chart/passRate/export',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}
diff --git a/src/api/report/sampleProgress.js b/src/api/report/sampleProgress.js
new file mode 100644
index 0000000..f9b6119
--- /dev/null
+++ b/src/api/report/sampleProgress.js
@@ -0,0 +1,39 @@
+// 鏍峰搧杩涘害鎶ヨ〃鎺ュ彛
+import request from '@/utils/request'
+
+// 鍒嗛〉鏌ヨ鏍峰搧杩涘害
+export function pageSampleProgress(query) {
+  return request({
+    url: '/report/sampleProgress/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ鏍峰搧杩涘害缁熻
+export function getStatistics(query) {
+  return request({
+    url: '/report/sampleProgress/statistics',
+    method: 'get',
+    params: query
+  })
+}
+
+// 瀵煎嚭鏍峰搧杩涘害鎶ヨ〃
+export function exportSampleProgress(query) {
+  return request({
+    url: '/report/sampleProgress/export',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}
+
+// 鏌ヨ杩涘害鍙鍖栨暟鎹�
+export function getChartData(query) {
+  return request({
+    url: '/report/sampleProgress/chart',
+    method: 'get',
+    params: query
+  })
+}
diff --git a/src/api/report/sampleRecord.js b/src/api/report/sampleRecord.js
new file mode 100644
index 0000000..2db4c74
--- /dev/null
+++ b/src/api/report/sampleRecord.js
@@ -0,0 +1,30 @@
+// 鏍峰搧棰嗘牱璁板綍鎺ュ彛
+import request from '@/utils/request'
+
+// 鍒嗛〉鏌ヨ棰嗘牱璁板綍
+export function pageSampleRecord(query) {
+  return request({
+    url: '/report/sampleRecord/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ鏍峰搧娴佽浆璁板綍
+export function getSampleFlow(id) {
+  return request({
+    url: '/report/sampleRecord/flow',
+    method: 'get',
+    params: { id }
+  })
+}
+
+// 瀵煎嚭棰嗘牱璁板綍
+export function exportSampleRecord(query) {
+  return request({
+    url: '/report/sampleRecord/export',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}
diff --git a/src/api/report/spcChart.js b/src/api/report/spcChart.js
new file mode 100644
index 0000000..170744f
--- /dev/null
+++ b/src/api/report/spcChart.js
@@ -0,0 +1,48 @@
+// SPC鎺у埗鍥炬帴鍙�
+import request from '@/utils/request'
+
+// SPC鍒嗘瀽
+export function spcAnalyze(data) {
+  return request({
+    url: '/chart/spc/analyze',
+    method: 'post',
+    data: data
+  })
+}
+
+// 鍒剁▼鑳藉姏鍒嗘瀽
+export function getCapability(query) {
+  return request({
+    url: '/chart/spc/capability',
+    method: 'get',
+    params: query
+  })
+}
+
+// 瀵煎嚭鍒嗘瀽鏁版嵁
+export function exportSpcData(query) {
+  return request({
+    url: '/chart/spc/export',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}
+
+// 鑾峰彇妫�娴嬮」鐩垪琛�
+export function getProjectList(query) {
+  return request({
+    url: '/chart/spc/projectList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鑾峰彇妫�娴嬪弬鏁板垪琛�
+export function getParamList(projectId) {
+  return request({
+    url: '/chart/spc/paramList',
+    method: 'get',
+    params: { projectId }
+  })
+}
diff --git a/src/api/report/testItemData.js b/src/api/report/testItemData.js
new file mode 100644
index 0000000..353eb2e
--- /dev/null
+++ b/src/api/report/testItemData.js
@@ -0,0 +1,39 @@
+// 妫�娴嬮」鐩暟鎹帴鍙�
+import request from '@/utils/request'
+
+// 鍒嗛〉鏌ヨ妫�娴嬮」鐩暟鎹�
+export function pageTestItemData(query) {
+  return request({
+    url: '/report/testItemData/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ妫�娴嬮」鐩鎯�
+export function getTestItemDetail(id) {
+  return request({
+    url: '/report/testItemData/detail',
+    method: 'get',
+    params: { id }
+  })
+}
+
+// 鏁版嵁妯悜姣旇緝
+export function compareTestItem(query) {
+  return request({
+    url: '/report/testItemData/compare',
+    method: 'get',
+    params: query
+  })
+}
+
+// 瀵煎嚭妫�娴嬮」鐩暟鎹�
+export function exportTestItemData(query) {
+  return request({
+    url: '/report/testItemData/export',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}
diff --git a/src/api/report/workStatistics.js b/src/api/report/workStatistics.js
new file mode 100644
index 0000000..67f377c
--- /dev/null
+++ b/src/api/report/workStatistics.js
@@ -0,0 +1,39 @@
+// 宸ヤ綔缁熻鎺ュ彛
+import request from '@/utils/request'
+
+// 鎸変汉鍛樼粺璁�
+export function getStatisticsByUser(query) {
+  return request({
+    url: '/chart/workStatistics/byUser',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鍙婃椂鐜囩粺璁�
+export function getTimelyRate(query) {
+  return request({
+    url: '/chart/workStatistics/timelyRate',
+    method: 'get',
+    params: query
+  })
+}
+
+// 宸ヤ綔瓒嬪娍鍥�
+export function getWorkTrend(query) {
+  return request({
+    url: '/chart/workStatistics/trend',
+    method: 'get',
+    params: query
+  })
+}
+
+// 瀵煎嚭宸ヤ綔缁熻
+export function exportWorkStatistics(query) {
+  return request({
+    url: '/chart/workStatistics/export',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}
diff --git a/src/views/report/dashboard/index.vue b/src/views/report/dashboard/index.vue
new file mode 100644
index 0000000..a0e3b7c
--- /dev/null
+++ b/src/views/report/dashboard/index.vue
@@ -0,0 +1,496 @@
+<template>
+  <div class="app-container dashboard-container">
+    <!-- 椤堕儴缁熻鍗$墖 -->
+    <el-row :gutter="20" style="margin-bottom: 20px;">
+      <el-col :span="4">
+        <el-card shadow="hover" class="stat-card-wrapper">
+          <div class="stat-card">
+            <div class="stat-icon" style="background: #409EFF;">
+              <i class="el-icon-s-claim" />
+            </div>
+            <div class="stat-content">
+              <div class="stat-title">寰呴鏍峰搧</div>
+              <div class="stat-value">{{ overviewData.waitReceive || 0 }}</div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="4">
+        <el-card shadow="hover" class="stat-card-wrapper">
+          <div class="stat-card">
+            <div class="stat-icon" style="background: #E6A23C;">
+              <i class="el-icon-time" />
+            </div>
+            <div class="stat-content">
+              <div class="stat-title">寰呮鏍峰搧</div>
+              <div class="stat-value">{{ overviewData.waitInspection || 0 }}</div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="4">
+        <el-card shadow="hover" class="stat-card-wrapper">
+          <div class="stat-card">
+            <div class="stat-icon" style="background: #909399;">
+              <i class="el-icon-document-checked" />
+            </div>
+            <div class="stat-content">
+              <div class="stat-title">寰呭鏍�</div>
+              <div class="stat-value">{{ overviewData.waitAudit || 0 }}</div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="4">
+        <el-card shadow="hover" class="stat-card-wrapper">
+          <div class="stat-card">
+            <div class="stat-icon" style="background: #F56C6C;">
+              <i class="el-icon-document" />
+            </div>
+            <div class="stat-content">
+              <div class="stat-title">寰呯紪鍒舵姤鍛�</div>
+              <div class="stat-value">{{ overviewData.waitReport || 0 }}</div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="4">
+        <el-card shadow="hover" class="stat-card-wrapper">
+          <div class="stat-card">
+            <div class="stat-icon" style="background: #67C23A;">
+              <i class="el-icon-circle-check" />
+            </div>
+            <div class="stat-content">
+              <div class="stat-title">浠婃棩瀹屾垚</div>
+              <div class="stat-value">{{ overviewData.todayFinished || 0 }}</div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="4">
+        <el-card shadow="hover" class="voice-control" @click.native="toggleVoice">
+          <div class="voice-content">
+            <i :class="voiceEnabled ? 'el-icon-microphone' : 'el-icon-turn-off-microphone'" />
+            <span>{{ voiceEnabled ? '璇煶鎾姤涓�' : '璇煶宸插叧闂�' }}</span>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 鍥捐〃鍖哄煙 -->
+    <el-row :gutter="20" style="margin-bottom: 20px;">
+      <!-- 鍘嗗彶15澶╂娴嬩换鍔� -->
+      <el-col :span="16">
+        <el-card shadow="hover">
+          <div slot="header">
+            <span>杩�15澶╂娴嬩换鍔�</span>
+            <el-tag type="info" size="mini" style="margin-left: 10px;">瀹炴椂鏇存柊</el-tag>
+          </div>
+          <Echart
+            :xAxis="historyXAxis"
+            :yAxis="historyYAxis"
+            :series="historySeries"
+            :tooltip="{ trigger: 'axis' }"
+            :legend="{ data: ['妫�娴嬩换鍔�', '瀹屾垚浠诲姟'] }"
+            :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+            :chartStyle="{ height: '280px' }"
+          />
+        </el-card>
+      </el-col>
+      <!-- 鏈潵15澶╀换鍔¢瑙� -->
+      <el-col :span="8">
+        <el-card shadow="hover" class="future-task-card">
+          <div slot="header">鏈潵15澶╀换鍔�</div>
+          <div class="future-task-list">
+            <div v-for="(item, index) in futureTasks" :key="index" class="future-task-item">
+              <span class="task-date">{{ item.date }}</span>
+              <el-progress :percentage="item.progress" :stroke-width="10" style="flex: 1; margin: 0 10px;" />
+              <span class="task-count">{{ item.taskCount }}涓换鍔�</span>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20" style="margin-bottom: 20px;">
+      <!-- 鎻愪氦鎺掕姒� -->
+      <el-col :span="12">
+        <el-card shadow="hover">
+          <div slot="header">
+            <span>杩�15澶╂彁浜ゆ帓琛�</span>
+            <el-radio-group v-model="rankingType" size="mini" style="float: right;" @change="getRankingData">
+              <el-radio-button label="record">鍘熷璁板綍</el-radio-button>
+              <el-radio-button label="report">鎶ュ憡</el-radio-button>
+            </el-radio-group>
+          </div>
+          <div class="ranking-list">
+            <div v-for="(item, index) in rankingData" :key="index" class="ranking-item">
+              <span class="ranking-index" :class="getRankingClass(index)">{{ index + 1 }}</span>
+              <span class="ranking-name">{{ item.userName }}</span>
+              <el-progress :percentage="item.percentage" :stroke-width="15" :show-text="false" style="flex: 1; margin: 0 15px;" />
+              <span class="ranking-count">{{ item.count }}浠�</span>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <!-- 妫�楠岀粨鏋滅粺璁� -->
+      <el-col :span="12">
+        <el-card shadow="hover">
+          <div slot="header">杩�30澶╂楠岀粨鏋�</div>
+          <el-row :gutter="20">
+            <el-col :span="8">
+              <div class="result-card">
+                <div class="result-title">鍘熸潗鏂�</div>
+                <Echart
+                  :series="rawMaterialSeries"
+                  :tooltip="{ trigger: 'item', formatter: '{b}: {c} ({d}%)' }"
+                  :chartStyle="{ height: '200px' }"
+                />
+              </div>
+            </el-col>
+            <el-col :span="8">
+              <div class="result-card">
+                <div class="result-title">鍗婃垚鍝�</div>
+                <Echart
+                  :series="semiProductSeries"
+                  :tooltip="{ trigger: 'item', formatter: '{b}: {c} ({d}%)' }"
+                  :chartStyle="{ height: '200px' }"
+                />
+              </div>
+            </el-col>
+            <el-col :span="8">
+              <div class="result-card">
+                <div class="result-title">鎴愬搧</div>
+                <Echart
+                  :series="finishedProductSeries"
+                  :tooltip="{ trigger: 'item', formatter: '{b}: {c} ({d}%)' }"
+                  :chartStyle="{ height: '200px' }"
+                />
+              </div>
+            </el-col>
+          </el-row>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 绱ф�ヤ簨椤规挱鎶� -->
+    <el-card shadow="hover">
+      <div slot="header">
+        <span>绱ф�ヤ簨椤�</span>
+        <el-button type="text" style="float: right;" @click="refreshVoiceQueue">
+          <i class="el-icon-refresh" /> 鍒锋柊
+        </el-button>
+      </div>
+      <el-table :data="urgentItems" border style="width: 100%" max-height="200">
+        <el-table-column prop="type" label="绫诲瀷" width="120" />
+        <el-table-column prop="content" label="鍐呭" />
+        <el-table-column prop="time" label="鏃堕棿" width="180" />
+        <el-table-column prop="level" label="绾у埆" width="100">
+          <template slot-scope="scope">
+            <el-tag :type="getLevelType(scope.row.level)">{{ scope.row.level }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="100">
+          <template slot-scope="scope">
+            <el-button type="text" size="mini" @click="speakContent(scope.row.content)">鎾姤</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import Echart from '@/components/echarts/echarts.vue'
+import {
+  getOverview,
+  getHistory15Days,
+  getFuture15Days,
+  getRanking,
+  getInsResult,
+  getVoiceQueue
+} from '@/api/report/dashboard'
+
+export default {
+  name: 'TestHall',
+  components: { Echart },
+  data() {
+    return {
+      overviewData: {},
+      futureTasks: [],
+      rankingData: [],
+      rankingType: 'record',
+      urgentItems: [],
+      voiceEnabled: true,
+      refreshTimer: null,
+      // 鍘嗗彶15澶╁浘琛ㄩ厤缃�
+      historyXAxis: [{ type: 'category', data: [] }],
+      historyYAxis: [{ type: 'value' }],
+      historySeries: [
+        { name: '妫�娴嬩换鍔�', type: 'bar', data: [] },
+        { name: '瀹屾垚浠诲姟', type: 'line', data: [] }
+      ],
+      // 妫�楠岀粨鏋滈ゼ鍥鹃厤缃�
+      rawMaterialSeries: [{
+        type: 'pie',
+        radius: ['50%', '70%'],
+        data: [
+          { name: '鍚堟牸', value: 0, itemStyle: { color: '#67C23A' } },
+          { name: '涓嶅悎鏍�', value: 0, itemStyle: { color: '#F56C6C' } }
+        ]
+      }],
+      semiProductSeries: [{
+        type: 'pie',
+        radius: ['50%', '70%'],
+        data: [
+          { name: '鍚堟牸', value: 0, itemStyle: { color: '#67C23A' } },
+          { name: '涓嶅悎鏍�', value: 0, itemStyle: { color: '#F56C6C' } }
+        ]
+      }],
+      finishedProductSeries: [{
+        type: 'pie',
+        radius: ['50%', '70%'],
+        data: [
+          { name: '鍚堟牸', value: 0, itemStyle: { color: '#67C23A' } },
+          { name: '涓嶅悎鏍�', value: 0, itemStyle: { color: '#F56C6C' } }
+        ]
+      }]
+    }
+  },
+  mounted() {
+    this.initDashboard()
+    this.startAutoRefresh()
+  },
+  beforeDestroy() {
+    this.stopAutoRefresh()
+  },
+  methods: {
+    // 鍒濆鍖栫湅鏉挎暟鎹�
+    async initDashboard() {
+      await Promise.all([
+        this.getOverviewData(),
+        this.getHistoryData(),
+        this.getFutureData(),
+        this.getRankingData(),
+        this.getInsResultData(),
+        this.refreshVoiceQueue()
+      ])
+    },
+    // 鑾峰彇姒傝鏁版嵁
+    getOverviewData() {
+      return getOverview().then(res => {
+        this.overviewData = res.data || {}
+      })
+    },
+    // 鑾峰彇鍘嗗彶15澶╂暟鎹�
+    getHistoryData() {
+      return getHistory15Days().then(res => {
+        this.historyXAxis[0].data = res.data.dates || []
+        this.historySeries[0].data = res.data.taskCounts || []
+        this.historySeries[1].data = res.data.finishCounts || []
+      })
+    },
+    // 鑾峰彇鏈潵15澶╀换鍔�
+    getFutureData() {
+      return getFuture15Days().then(res => {
+        this.futureTasks = res.data || []
+      })
+    },
+    // 鑾峰彇鎻愪氦鎺掕
+    getRankingData() {
+      return getRanking({ type: this.rankingType }).then(res => {
+        this.rankingData = res.data || []
+      })
+    },
+    // 鑾峰彇妫�楠岀粨鏋滅粺璁�
+    getInsResultData() {
+      return getInsResult().then(res => {
+        if (res.data.rawMaterial) {
+          this.rawMaterialSeries[0].data[0].value = res.data.rawMaterial.passCount || 0
+          this.rawMaterialSeries[0].data[1].value = res.data.rawMaterial.unpassCount || 0
+        }
+        if (res.data.semiProduct) {
+          this.semiProductSeries[0].data[0].value = res.data.semiProduct.passCount || 0
+          this.semiProductSeries[0].data[1].value = res.data.semiProduct.unpassCount || 0
+        }
+        if (res.data.finishedProduct) {
+          this.finishedProductSeries[0].data[0].value = res.data.finishedProduct.passCount || 0
+          this.finishedProductSeries[0].data[1].value = res.data.finishedProduct.unpassCount || 0
+        }
+      })
+    },
+    // 鍒锋柊璇煶鎾姤闃熷垪
+    refreshVoiceQueue() {
+      return getVoiceQueue().then(res => {
+        this.urgentItems = res.data || []
+      })
+    },
+    // 鍒囨崲璇煶鎾姤鐘舵��
+    toggleVoice() {
+      this.voiceEnabled = !this.voiceEnabled
+      if (this.voiceEnabled) {
+        this.$message.success('璇煶鎾姤宸插紑鍚�')
+      } else {
+        this.$message.info('璇煶鎾姤宸插叧闂�')
+      }
+    },
+    // 璇煶鎾姤鍐呭
+    speakContent(content) {
+      if ('speechSynthesis' in window) {
+        const utterance = new SpeechSynthesisUtterance(content)
+        utterance.lang = 'zh-CN'
+        speechSynthesis.speak(utterance)
+      } else {
+        this.$message.warning('褰撳墠娴忚鍣ㄤ笉鏀寔璇煶鎾姤')
+      }
+    },
+    // 寮�鍚嚜鍔ㄥ埛鏂�
+    startAutoRefresh() {
+      this.refreshTimer = setInterval(() => {
+        this.initDashboard()
+      }, 30000)
+    },
+    // 鍋滄鑷姩鍒锋柊
+    stopAutoRefresh() {
+      if (this.refreshTimer) {
+        clearInterval(this.refreshTimer)
+        this.refreshTimer = null
+      }
+    },
+    // 鑾峰彇鎺掕鏍峰紡绫�
+    getRankingClass(index) {
+      if (index === 0) return 'first'
+      if (index === 1) return 'second'
+      if (index === 2) return 'third'
+      return ''
+    },
+    // 鑾峰彇绾у埆鏍囩绫诲瀷
+    getLevelType(level) {
+      const map = { '绱ф��': 'danger', '閲嶈': 'warning', '鏅��': 'info' }
+      return map[level] || 'info'
+    }
+  }
+}
+</script>
+
+<style scoped>
+.dashboard-container {
+  background: #f0f2f5;
+  min-height: calc(100vh - 84px);
+}
+.stat-card-wrapper {
+  cursor: pointer;
+}
+.stat-card {
+  display: flex;
+  align-items: center;
+  padding: 10px;
+}
+.stat-icon {
+  width: 50px;
+  height: 50px;
+  border-radius: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.stat-icon i {
+  font-size: 24px;
+  color: #fff;
+}
+.stat-content {
+  margin-left: 10px;
+}
+.stat-title {
+  font-size: 12px;
+  color: #909399;
+}
+.stat-value {
+  font-size: 22px;
+  font-weight: bold;
+  color: #303133;
+  margin-top: 5px;
+}
+.voice-control {
+  cursor: pointer;
+}
+.voice-content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 15px;
+  color: #409EFF;
+}
+.voice-content i {
+  font-size: 32px;
+}
+.voice-content span {
+  font-size: 12px;
+  margin-top: 8px;
+}
+.future-task-card {
+  height: 380px;
+  overflow: auto;
+}
+.future-task-list {
+  padding: 10px;
+}
+.future-task-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 15px;
+}
+.task-date {
+  width: 80px;
+  font-size: 12px;
+  color: #606266;
+}
+.task-count {
+  width: 60px;
+  text-align: right;
+  font-size: 12px;
+  color: #909399;
+}
+.ranking-list {
+  padding: 10px;
+}
+.ranking-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+}
+.ranking-index {
+  width: 24px;
+  height: 24px;
+  border-radius: 50%;
+  background: #909399;
+  color: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 12px;
+}
+.ranking-index.first { background: #FFD700; }
+.ranking-index.second { background: #C0C0C0; }
+.ranking-index.third { background: #CD7F32; }
+.ranking-name {
+  width: 80px;
+  margin-left: 10px;
+  font-size: 14px;
+}
+.ranking-count {
+  width: 50px;
+  text-align: right;
+  font-size: 14px;
+  color: #606266;
+}
+.result-card {
+  text-align: center;
+}
+.result-title {
+  font-size: 14px;
+  color: #606266;
+  margin-bottom: 10px;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/report/deviceRecord/index.vue b/src/views/report/deviceRecord/index.vue
new file mode 100644
index 0000000..3641878
--- /dev/null
+++ b/src/views/report/deviceRecord/index.vue
@@ -0,0 +1,235 @@
+<template>
+  <div class="app-container">
+    <!-- 鎼滅储琛ㄥ崟 -->
+    <el-form ref="queryForm" :model="queryParams" :inline="true" size="small">
+      <el-form-item label="璁惧缂栧彿" prop="deviceCode">
+        <el-input v-model="queryParams.deviceCode" placeholder="璇疯緭鍏ヨ澶囩紪鍙�" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="璁惧鍚嶇О" prop="deviceName">
+        <el-input v-model="queryParams.deviceName" placeholder="璇疯緭鍏ヨ澶囧悕绉�" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="浣跨敤浜�" prop="useUser">
+        <el-input v-model="queryParams.useUser" placeholder="璇疯緭鍏ヤ娇鐢ㄤ汉" clearable style="width: 150px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="浣跨敤鍛ㄦ湡" prop="timeRange">
+        <el-date-picker
+          v-model="timeRange"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="寮�濮嬫椂闂�"
+          end-placeholder="缁撴潫鏃堕棿"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" @click="handleQuery">鏌ヨ</el-button>
+        <el-button icon="el-icon-refresh" @click="resetQuery">閲嶇疆</el-button>
+        <el-button type="success" icon="el-icon-download" @click="handleExport">瀵煎嚭</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 缁熻鍗$墖 -->
+    <el-row :gutter="20" style="margin-bottom: 20px;">
+      <el-col :span="6">
+        <el-card shadow="hover">
+          <div class="stat-card">
+            <div class="stat-title">璁惧鎬绘暟</div>
+            <div class="stat-value">{{ statistics.totalDevices || 0 }}</div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card shadow="hover">
+          <div class="stat-card">
+            <div class="stat-title">浣跨敤娆℃暟</div>
+            <div class="stat-value">{{ statistics.useCount || 0 }}</div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card shadow="hover">
+          <div class="stat-card">
+            <div class="stat-title">浣跨敤鏃堕暱(h)</div>
+            <div class="stat-value">{{ statistics.useHours || 0 }}</div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card shadow="hover">
+          <div class="stat-card">
+            <div class="stat-title">鍒╃敤鐜�</div>
+            <div class="stat-value">{{ statistics.utilization || 0 }}%</div>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 浣跨敤棰戠巼鍥捐〃 -->
+    <el-row :gutter="20" style="margin-bottom: 20px;">
+      <el-col :span="12">
+        <el-card shadow="hover">
+          <div slot="header">璁惧浣跨敤棰戠巼TOP10</div>
+          <Echart
+            :xAxis="useFrequencyXAxis"
+            :yAxis="useFrequencyYAxis"
+            :series="useFrequencySeries"
+            :tooltip="{ trigger: 'axis' }"
+            :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+            :chartStyle="{ height: '300px' }"
+          />
+        </el-card>
+      </el-col>
+      <el-col :span="12">
+        <el-card shadow="hover">
+          <div slot="header">璁惧浣跨敤瓒嬪娍</div>
+          <Echart
+            :xAxis="useTrendXAxis"
+            :yAxis="useTrendYAxis"
+            :series="useTrendSeries"
+            :tooltip="{ trigger: 'axis' }"
+            :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+            :chartStyle="{ height: '300px' }"
+          />
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 鏁版嵁琛ㄦ牸 -->
+    <lims-table
+      :tableData="tableData"
+      :column="tableColumn"
+      :page="page"
+      :tableLoading="tableLoading"
+      @pagination="handlePagination"
+    />
+  </div>
+</template>
+
+<script>
+import Echart from '@/components/echarts/echarts.vue'
+import limsTable from '@/components/Table/lims-table.vue'
+import { pageDeviceRecord, getDeviceStatistics, exportDeviceRecord } from '@/api/report/deviceRecord'
+
+export default {
+  name: 'DeviceRecord',
+  components: { Echart, limsTable },
+  data() {
+    return {
+      queryParams: {},
+      timeRange: [],
+      tableData: [],
+      tableLoading: false,
+      statistics: {},
+      page: { total: 0, size: 10, current: 1 },
+      tableColumn: [
+        { label: '璁惧缂栧彿', prop: 'deviceCode', minWidth: '120px' },
+        { label: '璁惧鍚嶇О', prop: 'deviceName', minWidth: '150px' },
+        { label: '瑙勬牸鍨嬪彿', prop: 'specModel', minWidth: '120px' },
+        { label: '浣跨敤浜�', prop: 'useUser', minWidth: '80px' },
+        { label: '寮�濮嬫椂闂�', prop: 'startTime', minWidth: '160px' },
+        { label: '缁撴潫鏃堕棿', prop: 'endTime', minWidth: '160px' },
+        { label: '浣跨敤鏃堕暱(h)', prop: 'useHours', minWidth: '100px' },
+        { label: '鍏宠仈鏍峰搧', prop: 'sampleCode', minWidth: '140px' },
+        { label: '妫�娴嬮」鐩�', prop: 'testItem', minWidth: '120px' },
+        { label: '浣跨敤鐘舵��', prop: 'status', minWidth: '100px', dataType: 'tag', formatData: (val) => val === 1 ? '浣跨敤涓�' : '宸茬粨鏉�', formatType: (val) => val === 1 ? 'warning' : 'success' }
+      ],
+      // 浣跨敤棰戠巼鍥捐〃
+      useFrequencyXAxis: [{ type: 'category', data: [], axisLabel: { rotate: 30 } }],
+      useFrequencyYAxis: [{ type: 'value' }],
+      useFrequencySeries: [{ name: '浣跨敤娆℃暟', type: 'bar', data: [] }],
+      // 浣跨敤瓒嬪娍鍥捐〃
+      useTrendXAxis: [{ type: 'category', data: [] }],
+      useTrendYAxis: [{ type: 'value' }],
+      useTrendSeries: [{ name: '浣跨敤鏃堕暱', type: 'line', data: [] }]
+    }
+  },
+  mounted() {
+    this.getList()
+    this.getStatisticsData()
+  },
+  methods: {
+    getList() {
+      this.tableLoading = true
+      const params = { ...this.queryParams }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      pageDeviceRecord({ ...params, ...this.page })
+        .then(res => {
+          this.tableData = res.data.records || []
+          this.page.total = res.data.total || 0
+        })
+        .finally(() => (this.tableLoading = false))
+    },
+    getStatisticsData() {
+      const params = { ...this.queryParams }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      getDeviceStatistics(params).then(res => {
+        this.statistics = res.data.summary || {}
+        // 浣跨敤棰戠巼鍥捐〃鏁版嵁
+        this.useFrequencyXAxis[0].data = (res.data.frequencyData || []).map(item => item.deviceName)
+        this.useFrequencySeries[0].data = (res.data.frequencyData || []).map(item => item.count)
+        // 浣跨敤瓒嬪娍鍥捐〃鏁版嵁
+        this.useTrendXAxis[0].data = (res.data.trendData || []).map(item => item.date)
+        this.useTrendSeries[0].data = (res.data.trendData || []).map(item => item.hours)
+      })
+    },
+    handleQuery() {
+      this.page.current = 1
+      this.getList()
+      this.getStatisticsData()
+    },
+    resetQuery() {
+      this.queryParams = {}
+      this.timeRange = []
+      this.handleQuery()
+    },
+    handleExport() {
+      const params = { ...this.queryParams }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      exportDeviceRecord(params).then(res => {
+        this.downloadFile(res, '璁惧浣跨敤璁板綍.xlsx')
+      })
+    },
+    handlePagination({ page, limit }) {
+      this.page.current = page
+      this.page.size = limit
+      this.getList()
+    },
+    downloadFile(data, fileName) {
+      const blob = new Blob([data])
+      const url = window.URL.createObjectURL(blob)
+      const link = document.createElement('a')
+      link.href = url
+      link.download = fileName
+      link.click()
+      window.URL.revokeObjectURL(url)
+    }
+  }
+}
+</script>
+
+<style scoped>
+.stat-card {
+  text-align: center;
+  padding: 15px 0;
+}
+.stat-title {
+  font-size: 14px;
+  color: #909399;
+}
+.stat-value {
+  font-size: 28px;
+  font-weight: bold;
+  color: #303133;
+  margin-top: 10px;
+}
+</style>
diff --git a/src/views/report/normalDistribution/index.vue b/src/views/report/normalDistribution/index.vue
new file mode 100644
index 0000000..2386063
--- /dev/null
+++ b/src/views/report/normalDistribution/index.vue
@@ -0,0 +1,303 @@
+<template>
+  <div class="app-container">
+    <!-- 鍒嗘瀽閰嶇疆琛ㄥ崟 -->
+    <el-card shadow="hover" style="margin-bottom: 20px;">
+      <div slot="header">姝f�佸垎甯冨垎鏋愰厤缃�</div>
+      <el-form ref="analysisForm" :model="analysisParams" :inline="true" size="small" label-width="100px">
+        <el-form-item label="妫�娴嬮」鐩�" prop="projectId">
+          <el-select v-model="analysisParams.projectId" placeholder="璇烽�夋嫨妫�娴嬮」鐩�" style="width: 200px" @change="handleProjectChange">
+            <el-option v-for="item in projectList" :key="item.id" :label="item.projectName" :value="item.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="妫�娴嬪弬鏁�" prop="paramName">
+          <el-select v-model="analysisParams.paramName" placeholder="璇烽�夋嫨妫�娴嬪弬鏁�" style="width: 200px">
+            <el-option v-for="item in paramList" :key="item.paramName" :label="item.paramName" :value="item.paramName" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鏃堕棿鑼冨洿" prop="timeRange">
+          <el-date-picker
+            v-model="analysisParams.timeRange"
+            type="daterange"
+            range-separator="-"
+            start-placeholder="寮�濮嬫椂闂�"
+            end-placeholder="缁撴潫鏃堕棿"
+            value-format="yyyy-MM-dd"
+            style="width: 240px"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="el-icon-data-analysis" @click="handleAnalysis">寮�濮嬪垎鏋�</el-button>
+          <el-button type="success" icon="el-icon-download" @click="handleExport">瀵煎嚭鏁版嵁</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <el-row :gutter="20" v-if="analysisResult">
+      <!-- 姝f�佸垎甯冨浘 -->
+      <el-col :span="16">
+        <el-card shadow="hover">
+          <div slot="header">姝f�佸垎甯冨浘</div>
+          <Echart
+            :xAxis="distributionXAxis"
+            :yAxis="distributionYAxis"
+            :series="distributionSeries"
+            :tooltip="{ trigger: 'axis' }"
+            :legend="{ data: ['棰戞暟', '姝f�佹洸绾�'] }"
+            :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+            :chartStyle="{ height: '400px' }"
+          />
+        </el-card>
+      </el-col>
+      <!-- 缁熻淇℃伅 -->
+      <el-col :span="8">
+        <el-card shadow="hover">
+          <div slot="header">缁熻淇℃伅</div>
+          <el-descriptions :column="1" border>
+            <el-descriptions-item label="鏍锋湰鏁伴噺">{{ analysisResult.statistics.sampleCount || 0 }}</el-descriptions-item>
+            <el-descriptions-item label="鍧囧��(渭)">{{ (analysisResult.statistics.mean || 0).toFixed(4) }}</el-descriptions-item>
+            <el-descriptions-item label="鏍囧噯宸�(蟽)">{{ (analysisResult.statistics.stdDev || 0).toFixed(4) }}</el-descriptions-item>
+            <el-descriptions-item label="鏈�灏忓��">{{ (analysisResult.statistics.min || 0).toFixed(4) }}</el-descriptions-item>
+            <el-descriptions-item label="鏈�澶у��">{{ (analysisResult.statistics.max || 0).toFixed(4) }}</el-descriptions-item>
+            <el-descriptions-item label="鏋佸樊">{{ (analysisResult.statistics.range || 0).toFixed(4) }}</el-descriptions-item>
+            <el-descriptions-item label="涓綅鏁�">{{ (analysisResult.statistics.median || 0).toFixed(4) }}</el-descriptions-item>
+            <el-descriptions-item label="鍋忓害">{{ (analysisResult.statistics.skewness || 0).toFixed(4) }}</el-descriptions-item>
+            <el-descriptions-item label="宄板害">{{ (analysisResult.statistics.kurtosis || 0).toFixed(4) }}</el-descriptions-item>
+          </el-descriptions>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 鐩存柟鍥炬暟鎹〃鏍� -->
+    <el-row :gutter="20" style="margin-top: 20px;" v-if="analysisResult">
+      <el-col :span="12">
+        <el-card shadow="hover">
+          <div slot="header">棰戞暟鍒嗗竷琛�</div>
+          <el-table :data="analysisResult.histogramData" border style="width: 100%">
+            <el-table-column prop="interval" label="鍖洪棿" />
+            <el-table-column prop="frequency" label="棰戞暟" />
+            <el-table-column prop="relativeFreq" label="鐩稿棰戠巼">
+              <template slot-scope="scope">
+                {{ (scope.row.relativeFreq * 100).toFixed(2) }}%
+              </template>
+            </el-table-column>
+            <el-table-column prop="cumulativeFreq" label="绱棰戠巼">
+              <template slot-scope="scope">
+                {{ (scope.row.cumulativeFreq * 100).toFixed(2) }}%
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-card>
+      </el-col>
+      <el-col :span="12">
+        <el-card shadow="hover">
+          <div slot="header">杩囩▼鑳藉姏</div>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="capability-item">
+                <div class="capability-label">瑙勬牸涓婇檺(USL)</div>
+                <el-input-number v-model="analysisParams.usl" :precision="4" style="width: 100%;" @change="calculateCpk" />
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="capability-item">
+                <div class="capability-label">瑙勬牸涓嬮檺(LSL)</div>
+                <el-input-number v-model="analysisParams.lsl" :precision="4" style="width: 100%;" @change="calculateCpk" />
+              </div>
+            </el-col>
+          </el-row>
+          <el-divider />
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="capability-result">
+                <div class="capability-label">Cp</div>
+                <div class="capability-value" :style="{ color: getCapabilityColor(calculatedCpk.cp) }">
+                  {{ calculatedCpk.cp ? calculatedCpk.cp.toFixed(4) : '--' }}
+                </div>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="capability-result">
+                <div class="capability-label">Cpk</div>
+                <div class="capability-value" :style="{ color: getCapabilityColor(calculatedCpk.cpk) }">
+                  {{ calculatedCpk.cpk ? calculatedCpk.cpk.toFixed(4) : '--' }}
+                </div>
+              </div>
+            </el-col>
+          </el-row>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 鍘熷鏁版嵁 -->
+    <el-card shadow="hover" style="margin-top: 20px;" v-if="analysisResult">
+      <div slot="header">鍘熷鏁版嵁</div>
+      <el-table :data="rawDataTable" border style="width: 100%" max-height="300">
+        <el-table-column type="index" label="搴忓彿" width="60" />
+        <el-table-column prop="value" label="妫�娴嬪��">
+          <template slot-scope="scope">
+            {{ scope.row.value ? scope.row.value.toFixed(4) : '' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="sampleCode" label="鏍峰搧缂栧彿" />
+        <el-table-column prop="testTime" label="妫�娴嬫椂闂�" />
+        <el-table-column prop="tester" label="妫�娴嬩汉" />
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import Echart from '@/components/echarts/echarts.vue'
+import { normalDistributionAnalyze, getProjectList, getParamList, exportNormalDistribution } from '@/api/report/normalDistribution'
+
+export default {
+  name: 'NormalDistribution',
+  components: { Echart },
+  data() {
+    return {
+      projectList: [],
+      paramList: [],
+      analysisParams: {
+        projectId: null,
+        paramName: null,
+        timeRange: [],
+        usl: null,
+        lsl: null
+      },
+      analysisResult: null,
+      rawDataTable: [],
+      calculatedCpk: {
+        cp: null,
+        cpk: null
+      },
+      // 姝f�佸垎甯冨浘
+      distributionXAxis: [{ type: 'category', data: [] }],
+      distributionYAxis: [{ type: 'value' }],
+      distributionSeries: [
+        { name: '棰戞暟', type: 'bar', data: [], barWidth: '60%' },
+        { name: '姝f�佹洸绾�', type: 'line', data: [], smooth: true }
+      ]
+    }
+  },
+  mounted() {
+    this.getProjectList()
+  },
+  methods: {
+    getProjectList() {
+      getProjectList().then(res => {
+        this.projectList = res.data || []
+      })
+    },
+    handleProjectChange(projectId) {
+      this.analysisParams.paramName = null
+      getParamList(projectId).then(res => {
+        this.paramList = res.data || []
+      })
+    },
+    handleAnalysis() {
+      if (!this.analysisParams.projectId) {
+        this.$message.warning('璇烽�夋嫨妫�娴嬮」鐩�')
+        return
+      }
+      if (!this.analysisParams.paramName) {
+        this.$message.warning('璇烽�夋嫨妫�娴嬪弬鏁�')
+        return
+      }
+      const params = {
+        projectId: this.analysisParams.projectId,
+        paramName: this.analysisParams.paramName
+      }
+      if (this.analysisParams.timeRange && this.analysisParams.timeRange.length === 2) {
+        params.startDate = this.analysisParams.timeRange[0]
+        params.endDate = this.analysisParams.timeRange[1]
+      }
+      normalDistributionAnalyze(params).then(res => {
+        this.analysisResult = res.data
+        this.renderCharts(res.data)
+        this.rawDataTable = (res.data.rawData || []).map(item => ({
+          value: item.value,
+          sampleCode: item.sampleCode,
+          testTime: item.testTime,
+          tester: item.tester
+        }))
+        this.$message.success('鍒嗘瀽瀹屾垚')
+      })
+    },
+    renderCharts(data) {
+      const histogramData = data.histogramData || []
+      const normalCurve = data.normalCurve || []
+      // 鐩存柟鍥炬暟鎹�
+      this.distributionXAxis[0].data = histogramData.map(item => item.interval)
+      this.distributionSeries[0].data = histogramData.map(item => item.frequency)
+      // 姝f�佹洸绾挎暟鎹�
+      this.distributionSeries[1].data = normalCurve
+    },
+    calculateCpk() {
+      if (!this.analysisResult || !this.analysisParams.usl || !this.analysisParams.lsl) {
+        return
+      }
+      const stats = this.analysisResult.statistics
+      const mean = stats.mean
+      const stdDev = stats.stdDev
+      const usl = this.analysisParams.usl
+      const lsl = this.analysisParams.lsl
+      // Cp = (USL - LSL) / (6蟽)
+      this.calculatedCpk.cp = (usl - lsl) / (6 * stdDev)
+      // Cpk = min[(USL - 渭) / (3蟽), (渭 - LSL) / (3蟽)]
+      const cpu = (usl - mean) / (3 * stdDev)
+      const cpl = (mean - lsl) / (3 * stdDev)
+      this.calculatedCpk.cpk = Math.min(cpu, cpl)
+    },
+    handleExport() {
+      if (!this.analysisResult) {
+        this.$message.warning('璇峰厛杩涜鍒嗘瀽')
+        return
+      }
+      const params = {
+        projectId: this.analysisParams.projectId,
+        paramName: this.analysisParams.paramName
+      }
+      exportNormalDistribution(params).then(res => {
+        this.downloadFile(res, '姝f�佸垎甯冨垎鏋愭暟鎹�.xlsx')
+      })
+    },
+    getCapabilityColor(val) {
+      if (!val) return '#909399'
+      if (val >= 1.33) return '#67C23A'
+      if (val >= 1) return '#E6A23C'
+      return '#F56C6C'
+    },
+    downloadFile(data, fileName) {
+      const blob = new Blob([data])
+      const url = window.URL.createObjectURL(blob)
+      const link = document.createElement('a')
+      link.href = url
+      link.download = fileName
+      link.click()
+      window.URL.revokeObjectURL(url)
+    }
+  }
+}
+</script>
+
+<style scoped>
+.capability-item {
+  margin-bottom: 15px;
+}
+.capability-label {
+  font-size: 14px;
+  color: #909399;
+  margin-bottom: 8px;
+}
+.capability-result {
+  text-align: center;
+  padding: 15px;
+  background: #f5f7fa;
+  border-radius: 8px;
+}
+.capability-value {
+  font-size: 24px;
+  font-weight: bold;
+  margin-top: 10px;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/report/passRate/index.vue b/src/views/report/passRate/index.vue
new file mode 100644
index 0000000..2b5a6a0
--- /dev/null
+++ b/src/views/report/passRate/index.vue
@@ -0,0 +1,325 @@
+<template>
+  <div class="app-container">
+    <!-- 鎼滅储琛ㄥ崟 -->
+    <el-form ref="queryForm" :model="queryParams" :inline="true" size="small">
+      <el-form-item label="妫�楠岀被鍨�" prop="insType">
+        <el-select v-model="queryParams.insType" placeholder="璇烽�夋嫨妫�楠岀被鍨�" clearable style="width: 150px">
+          <el-option label="鍘熸潗鏂�" value="rawMaterial" />
+          <el-option label="鍗婃垚鍝�" value="semiProduct" />
+          <el-option label="鎴愬搧" value="finishedProduct" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="渚涘簲鍟�" prop="supplier">
+        <el-input v-model="queryParams.supplier" placeholder="璇疯緭鍏ヤ緵搴斿晢" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="宸ュ簭" prop="process">
+        <el-input v-model="queryParams.process" placeholder="璇疯緭鍏ュ伐搴�" clearable style="width: 150px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="鏃堕棿鑼冨洿" prop="timeRange">
+        <el-date-picker
+          v-model="timeRange"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="寮�濮嬫椂闂�"
+          end-placeholder="缁撴潫鏃堕棿"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" @click="handleQuery">鏌ヨ</el-button>
+        <el-button icon="el-icon-refresh" @click="resetQuery">閲嶇疆</el-button>
+        <el-button type="success" icon="el-icon-download" @click="handleExport">瀵煎嚭</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- Tab鍒囨崲 -->
+    <el-tabs v-model="activeTab" @tab-click="handleTabChange">
+      <el-tab-pane label="鍘熸潗鏂欏悎鏍肩巼" name="rawMaterial">
+        <el-card shadow="hover">
+          <div slot="header">鍘熸潗鏂欎笉鍚屾壒娆℃楠屽悎鏍肩巼</div>
+          <Echart
+            :xAxis="rawMaterialXAxis"
+            :yAxis="rawMaterialYAxis"
+            :series="rawMaterialSeries"
+            :tooltip="{ trigger: 'axis' }"
+            :legend="{ data: ['鍚堟牸鐜�', '鎵规鏁�'] }"
+            :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+            :chartStyle="{ height: '350px' }"
+          />
+        </el-card>
+        <lims-table
+          :tableData="rawMaterialTableData"
+          :column="rawMaterialTableColumn"
+          :page="rawMaterialPage"
+          :tableLoading="rawMaterialLoading"
+          @pagination="handleRawMaterialPagination"
+        />
+      </el-tab-pane>
+
+      <el-tab-pane label="渚涘簲鍟嗙粺璁�" name="supplier">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-card shadow="hover">
+              <div slot="header">渚涘簲鍟嗕笉鍚堟牸娆℃暟缁熻</div>
+              <Echart
+                :xAxis="supplierXAxis"
+                :yAxis="supplierYAxis"
+                :series="supplierSeries"
+                :tooltip="{ trigger: 'axis' }"
+                :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+                :chartStyle="{ height: '350px' }"
+              />
+            </el-card>
+          </el-col>
+          <el-col :span="12">
+            <el-card shadow="hover">
+              <div slot="header">涓嶅悎鏍奸」鐩笗绱墭鍥�</div>
+              <Echart
+                :xAxis="paretoXAxis"
+                :yAxis="paretoYAxis"
+                :series="paretoSeries"
+                :tooltip="{ trigger: 'axis' }"
+                :legend="{ data: ['涓嶅悎鏍兼鏁�', '绱鍗犳瘮'] }"
+                :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+                :chartStyle="{ height: '350px' }"
+              />
+            </el-card>
+          </el-col>
+        </el-row>
+      </el-tab-pane>
+
+      <el-tab-pane label="宸ュ簭鍚堟牸鐜�" name="process">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-card shadow="hover">
+              <div slot="header">鍚勫伐搴忓悎鏍肩巼</div>
+              <Echart
+                :xAxis="processXAxis"
+                :yAxis="processYAxis"
+                :series="processSeries"
+                :tooltip="{ trigger: 'axis' }"
+                :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+                :chartStyle="{ height: '350px' }"
+              />
+            </el-card>
+          </el-col>
+          <el-col :span="12">
+            <el-card shadow="hover">
+              <div slot="header">鏈哄彴涓嶅悎鏍兼鏁扮粺璁�</div>
+              <Echart
+                :xAxis="machineXAxis"
+                :yAxis="machineYAxis"
+                :series="machineSeries"
+                :tooltip="{ trigger: 'axis' }"
+                :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+                :chartStyle="{ height: '350px' }"
+              />
+            </el-card>
+          </el-col>
+        </el-row>
+        <lims-table
+          :tableData="processTableData"
+          :column="processTableColumn"
+          :page="processPage"
+          :tableLoading="processLoading"
+          @pagination="handleProcessPagination"
+        />
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import Echart from '@/components/echarts/echarts.vue'
+import limsTable from '@/components/Table/lims-table.vue'
+import {
+  getRawMaterialPassRate,
+  getSupplierUnqualified,
+  getParetoData,
+  getProcessPassRate,
+  getMachineUnqualified,
+  exportPassRate
+} from '@/api/report/passRate'
+
+export default {
+  name: 'PassRate',
+  components: { Echart, limsTable },
+  data() {
+    return {
+      queryParams: {},
+      timeRange: [],
+      activeTab: 'rawMaterial',
+      // 鍘熸潗鏂欏悎鏍肩巼
+      rawMaterialLoading: false,
+      rawMaterialTableData: [],
+      rawMaterialPage: { total: 0, size: 10, current: 1 },
+      rawMaterialTableColumn: [
+        { label: '鎵规鍙�', prop: 'batchCode', minWidth: '140px' },
+        { label: '鐗╂枡鍚嶇О', prop: 'materialName', minWidth: '150px' },
+        { label: '渚涘簲鍟�', prop: 'supplier', minWidth: '150px' },
+        { label: '妫�楠屾暟閲�', prop: 'totalCount', minWidth: '100px' },
+        { label: '鍚堟牸鏁伴噺', prop: 'passCount', minWidth: '100px' },
+        { label: '涓嶅悎鏍兼暟閲�', prop: 'unpassCount', minWidth: '100px' },
+        { label: '鍚堟牸鐜�', prop: 'passRate', minWidth: '100px', dataType: 'tag', formatData: (val) => `${val}%`, formatType: (val) => val >= 90 ? 'success' : val >= 70 ? 'warning' : 'danger' }
+      ],
+      // 鍘熸潗鏂欏浘琛�
+      rawMaterialXAxis: [{ type: 'category', data: [], axisLabel: { rotate: 30 } }],
+      rawMaterialYAxis: [{ type: 'value', max: 100 }, { type: 'value', position: 'right' }],
+      rawMaterialSeries: [
+        { name: '鍚堟牸鐜�', type: 'bar', data: [] },
+        { name: '鎵规鏁�', type: 'line', yAxisIndex: 1, data: [] }
+      ],
+      // 渚涘簲鍟嗗浘琛�
+      supplierXAxis: [{ type: 'category', data: [], axisLabel: { rotate: 30 } }],
+      supplierYAxis: [{ type: 'value' }],
+      supplierSeries: [{ name: '涓嶅悎鏍兼鏁�', type: 'bar', data: [] }],
+      // 甯曠疮鎵樺浘
+      paretoXAxis: [{ type: 'category', data: [] }],
+      paretoYAxis: [{ type: 'value' }, { type: 'value', max: 100, position: 'right' }],
+      paretoSeries: [
+        { name: '涓嶅悎鏍兼鏁�', type: 'bar', data: [] },
+        { name: '绱鍗犳瘮', type: 'line', yAxisIndex: 1, data: [] }
+      ],
+      // 宸ュ簭鍥捐〃
+      processLoading: false,
+      processTableData: [],
+      processPage: { total: 0, size: 10, current: 1 },
+      processTableColumn: [
+        { label: '宸ュ簭鍚嶇О', prop: 'processName', minWidth: '120px' },
+        { label: '妫�楠屾暟閲�', prop: 'totalCount', minWidth: '100px' },
+        { label: '鍚堟牸鏁伴噺', prop: 'passCount', minWidth: '100px' },
+        { label: '涓嶅悎鏍兼暟閲�', prop: 'unpassCount', minWidth: '100px' },
+        { label: '鍚堟牸鐜�', prop: 'passRate', minWidth: '100px', dataType: 'tag', formatData: (val) => `${val}%`, formatType: (val) => val >= 90 ? 'success' : val >= 70 ? 'warning' : 'danger' }
+      ],
+      processXAxis: [{ type: 'category', data: [] }],
+      processYAxis: [{ type: 'value', max: 100 }],
+      processSeries: [{ name: '鍚堟牸鐜�', type: 'bar', data: [] }],
+      // 鏈哄彴鍥捐〃
+      machineXAxis: [{ type: 'category', data: [] }],
+      machineYAxis: [{ type: 'value' }],
+      machineSeries: [{ name: '涓嶅悎鏍兼鏁�', type: 'bar', data: [] }]
+    }
+  },
+  mounted() {
+    this.getRawMaterialData()
+  },
+  methods: {
+    handleTabChange(tab) {
+      if (tab.name === 'rawMaterial') {
+        this.getRawMaterialData()
+      } else if (tab.name === 'supplier') {
+        this.getSupplierData()
+        this.getParetoData()
+      } else if (tab.name === 'process') {
+        this.getProcessData()
+        this.getMachineData()
+      }
+    },
+    getRawMaterialData() {
+      this.rawMaterialLoading = true
+      const params = this.buildParams()
+      getRawMaterialPassRate({ ...params, ...this.rawMaterialPage })
+        .then(res => {
+          this.rawMaterialTableData = res.data.records || []
+          this.rawMaterialPage.total = res.data.total || 0
+          // 鍥捐〃鏁版嵁
+          const chartData = (res.data.chartData || []).slice(0, 15)
+          this.rawMaterialXAxis[0].data = chartData.map(item => item.batchCode)
+          this.rawMaterialSeries[0].data = chartData.map(item => item.passRate)
+          this.rawMaterialSeries[1].data = chartData.map(item => item.batchCount)
+        })
+        .finally(() => (this.rawMaterialLoading = false))
+    },
+    getSupplierData() {
+      const params = this.buildParams()
+      getSupplierUnqualified(params).then(res => {
+        const data = res.data || []
+        this.supplierXAxis[0].data = data.map(item => item.supplier)
+        this.supplierSeries[0].data = data.map(item => item.unpassCount)
+      })
+    },
+    getParetoData() {
+      const params = this.buildParams()
+      getParetoData(params).then(res => {
+        this.paretoXAxis[0].data = res.data.categories || []
+        this.paretoSeries[0].data = res.data.values || []
+        this.paretoSeries[1].data = res.data.cumulativePercent || []
+      })
+    },
+    getProcessData() {
+      this.processLoading = true
+      const params = this.buildParams()
+      getProcessPassRate({ ...params, ...this.processPage })
+        .then(res => {
+          this.processTableData = res.data.records || []
+          this.processPage.total = res.data.total || 0
+          // 鍥捐〃鏁版嵁
+          const chartData = res.data.chartData || []
+          this.processXAxis[0].data = chartData.map(item => item.processName)
+          this.processSeries[0].data = chartData.map(item => item.passRate)
+        })
+        .finally(() => (this.processLoading = false))
+    },
+    getMachineData() {
+      const params = this.buildParams()
+      getMachineUnqualified(params).then(res => {
+        const data = res.data || []
+        this.machineXAxis[0].data = data.map(item => item.machineCode)
+        this.machineSeries[0].data = data.map(item => item.unpassCount)
+      })
+    },
+    buildParams() {
+      const params = { ...this.queryParams }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      return params
+    },
+    handleQuery() {
+      if (this.activeTab === 'rawMaterial') {
+        this.rawMaterialPage.current = 1
+        this.getRawMaterialData()
+      } else if (this.activeTab === 'supplier') {
+        this.getSupplierData()
+        this.getParetoData()
+      } else {
+        this.processPage.current = 1
+        this.getProcessData()
+        this.getMachineData()
+      }
+    },
+    resetQuery() {
+      this.queryParams = {}
+      this.timeRange = []
+      this.handleQuery()
+    },
+    handleExport() {
+      const params = { ...this.buildParams(), type: this.activeTab }
+      exportPassRate(params).then(res => {
+        this.downloadFile(res, '鍚堟牸鐜囩粺璁�.xlsx')
+      })
+    },
+    handleRawMaterialPagination({ page, limit }) {
+      this.rawMaterialPage.current = page
+      this.rawMaterialPage.size = limit
+      this.getRawMaterialData()
+    },
+    handleProcessPagination({ page, limit }) {
+      this.processPage.current = page
+      this.processPage.size = limit
+      this.getProcessData()
+    },
+    downloadFile(data, fileName) {
+      const blob = new Blob([data])
+      const url = window.URL.createObjectURL(blob)
+      const link = document.createElement('a')
+      link.href = url
+      link.download = fileName
+      link.click()
+      window.URL.revokeObjectURL(url)
+    }
+  }
+}
+</script>
diff --git a/src/views/report/sampleProgress/index.vue b/src/views/report/sampleProgress/index.vue
new file mode 100644
index 0000000..3506409
--- /dev/null
+++ b/src/views/report/sampleProgress/index.vue
@@ -0,0 +1,300 @@
+<template>
+  <div class="app-container">
+    <!-- 鎼滅储琛ㄥ崟 -->
+    <el-form ref="queryForm" :model="queryParams" :inline="true" size="small">
+      <el-form-item label="濮旀墭缂栧彿" prop="entrustCode">
+        <el-input v-model="queryParams.entrustCode" placeholder="璇疯緭鍏ュ鎵樼紪鍙�" clearable style="width: 200px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="鏍峰搧缂栧彿" prop="sampleCode">
+        <el-input v-model="queryParams.sampleCode" placeholder="璇疯緭鍏ユ牱鍝佺紪鍙�" clearable style="width: 200px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="鏍峰搧鍚嶇О" prop="sampleName">
+        <el-input v-model="queryParams.sampleName" placeholder="璇疯緭鍏ユ牱鍝佸悕绉�" clearable style="width: 200px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="妫�娴嬬姸鎬�" prop="insState">
+        <el-select v-model="queryParams.insState" placeholder="璇烽�夋嫨妫�娴嬬姸鎬�" clearable style="width: 150px">
+          <el-option label="寰呮" :value="0" />
+          <el-option label="妫�楠屼腑" :value="1" />
+          <el-option label="宸叉楠�" :value="2" />
+          <el-option label="寰呭鏍�" :value="3" />
+          <el-option label="瀹℃牳鏈�氳繃" :value="4" />
+          <el-option label="瀹℃牳閫氳繃" :value="5" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鏃堕棿鑼冨洿" prop="timeRange">
+        <el-date-picker
+          v-model="timeRange"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="寮�濮嬫椂闂�"
+          end-placeholder="缁撴潫鏃堕棿"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" @click="handleQuery">鏌ヨ</el-button>
+        <el-button icon="el-icon-refresh" @click="resetQuery">閲嶇疆</el-button>
+        <el-button type="success" icon="el-icon-download" @click="handleExport">瀵煎嚭</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 缁熻鍗$墖 -->
+    <el-row :gutter="20" style="margin-bottom: 20px;">
+      <el-col :span="6">
+        <el-card shadow="hover" class="stat-card-wrapper">
+          <div class="stat-card">
+            <div class="stat-icon" style="background: #409EFF;">
+              <i class="el-icon-time" />
+            </div>
+            <div class="stat-content">
+              <div class="stat-title">寰呮鏍峰搧</div>
+              <div class="stat-value">{{ statistics.waitInspection || 0 }}</div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card shadow="hover" class="stat-card-wrapper">
+          <div class="stat-card">
+            <div class="stat-icon" style="background: #E6A23C;">
+              <i class="el-icon-loading" />
+            </div>
+            <div class="stat-content">
+              <div class="stat-title">妫�楠屼腑</div>
+              <div class="stat-value">{{ statistics.inspecting || 0 }}</div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card shadow="hover" class="stat-card-wrapper">
+          <div class="stat-card">
+            <div class="stat-icon" style="background: #909399;">
+              <i class="el-icon-document-checked" />
+            </div>
+            <div class="stat-content">
+              <div class="stat-title">寰呭鏍�</div>
+              <div class="stat-value">{{ statistics.waitAudit || 0 }}</div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card shadow="hover" class="stat-card-wrapper">
+          <div class="stat-card">
+            <div class="stat-icon" style="background: #67C23A;">
+              <i class="el-icon-circle-check" />
+            </div>
+            <div class="stat-content">
+              <div class="stat-title">宸插畬鎴�</div>
+              <div class="stat-value">{{ statistics.finished || 0 }}</div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 杩涘害鍥捐〃 -->
+    <el-card shadow="hover" style="margin-bottom: 20px;">
+      <div slot="header">
+        <span>妫�娴嬭繘搴﹁秼鍔�</span>
+        <el-radio-group v-model="chartTimeType" size="mini" style="float: right;" @change="getChart">
+          <el-radio-button label="week">杩戜竴鍛�</el-radio-button>
+          <el-radio-button label="month">杩戜竴鏈�</el-radio-button>
+        </el-radio-group>
+      </div>
+      <Echart
+        ref="progressChart"
+        :xAxis="chartXAxis"
+        :yAxis="chartYAxis"
+        :series="chartSeries"
+        :tooltip="chartTooltip"
+        :grid="chartGrid"
+        :chartStyle="{ height: '300px' }"
+      />
+    </el-card>
+
+    <!-- 鏁版嵁琛ㄦ牸 -->
+    <lims-table
+      :tableData="tableData"
+      :column="tableColumn"
+      :page="page"
+      :tableLoading="tableLoading"
+      @pagination="handlePagination"
+    />
+  </div>
+</template>
+
+<script>
+import Echart from '@/components/echarts/echarts.vue'
+import limsTable from '@/components/Table/lims-table.vue'
+import { pageSampleProgress, getStatistics, getChartData, exportSampleProgress } from '@/api/report/sampleProgress'
+
+export default {
+  name: 'SampleProgress',
+  components: { Echart, limsTable },
+  data() {
+    return {
+      queryParams: {},
+      timeRange: [],
+      tableData: [],
+      tableLoading: false,
+      statistics: {},
+      page: { total: 0, size: 10, current: 1 },
+      chartTimeType: 'week',
+      tableColumn: [
+        { label: '濮旀墭缂栧彿', prop: 'entrustCode', minWidth: '140px' },
+        { label: '鏍峰搧缂栧彿', prop: 'sampleCode', minWidth: '140px' },
+        { label: '鏍峰搧鍚嶇О', prop: 'sampleName', minWidth: '150px' },
+        { label: '鎶ュ憡缂栧彿', prop: 'reportCode', minWidth: '140px' },
+        {
+          label: '妫�娴嬬姸鎬�',
+          prop: 'insState',
+          minWidth: '100px',
+          dataType: 'tag',
+          formatData: (val) => this.formatInsState(val),
+          formatType: (val) => this.formatInsStateType(val)
+        },
+        { label: '杩涘害', prop: 'progressPercent', minWidth: '120px', dataType: 'progress' },
+        {
+          label: '宸插畬鎴�/鎬绘暟',
+          prop: 'itemCount',
+          minWidth: '100px',
+          formatData: (val, row) => `${row.finishedItems || 0}/${row.totalItems || 0}`
+        },
+        { label: '璐熻矗浜�', prop: 'chargeUser', minWidth: '80px' },
+        { label: '瀹㈡埛鍚嶇О', prop: 'custom', minWidth: '150px' },
+        { label: '鍒涘缓鏃堕棿', prop: 'createTime', minWidth: '160px' }
+      ],
+      // 鍥捐〃閰嶇疆
+      chartXAxis: [{ type: 'category', data: [] }],
+      chartYAxis: [{ type: 'value' }],
+      chartSeries: [
+        { name: '鏍峰搧鏁伴噺', type: 'bar', data: [] },
+        { name: '瀹屾垚鏁伴噺', type: 'line', data: [] }
+      ],
+      chartTooltip: { trigger: 'axis' },
+      chartGrid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }
+    }
+  },
+  mounted() {
+    this.getList()
+    this.getStatisticsData()
+    this.getChart()
+  },
+  methods: {
+    getList() {
+      this.tableLoading = true
+      const params = { ...this.queryParams }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      pageSampleProgress({ ...params, ...this.page })
+        .then(res => {
+          this.tableData = res.data.records || []
+          this.page.total = res.data.total || 0
+        })
+        .finally(() => (this.tableLoading = false))
+    },
+    getStatisticsData() {
+      const params = { ...this.queryParams }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      getStatistics(params).then(res => {
+        this.statistics = res.data || {}
+      })
+    },
+    getChart() {
+      const params = { timeType: this.chartTimeType }
+      getChartData(params).then(res => {
+        this.chartXAxis[0].data = res.data.dates || []
+        this.chartSeries[0].data = res.data.totalCounts || []
+        this.chartSeries[1].data = res.data.finishedCounts || []
+      })
+    },
+    handleQuery() {
+      this.page.current = 1
+      this.getList()
+      this.getStatisticsData()
+      this.getChart()
+    },
+    resetQuery() {
+      this.queryParams = {}
+      this.timeRange = []
+      this.handleQuery()
+    },
+    handleExport() {
+      const params = { ...this.queryParams }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      exportSampleProgress(params).then(res => {
+        this.downloadFile(res, '鏍峰搧杩涘害鎶ヨ〃.xlsx')
+      })
+    },
+    handlePagination({ page, limit }) {
+      this.page.current = page
+      this.page.size = limit
+      this.getList()
+    },
+    formatInsState(val) {
+      const map = { 0: '寰呮', 1: '妫�楠屼腑', 2: '宸叉楠�', 3: '寰呭鏍�', 4: '瀹℃牳鏈�氳繃', 5: '瀹℃牳閫氳繃' }
+      return map[val] || ''
+    },
+    formatInsStateType(val) {
+      const map = { 0: 'warning', 1: 'primary', 2: 'info', 3: 'warning', 4: 'danger', 5: 'success' }
+      return map[val] || ''
+    },
+    downloadFile(data, fileName) {
+      const blob = new Blob([data])
+      const url = window.URL.createObjectURL(blob)
+      const link = document.createElement('a')
+      link.href = url
+      link.download = fileName
+      link.click()
+      window.URL.revokeObjectURL(url)
+    }
+  }
+}
+</script>
+
+<style scoped>
+.stat-card-wrapper {
+  cursor: pointer;
+}
+.stat-card {
+  display: flex;
+  align-items: center;
+  padding: 10px;
+}
+.stat-icon {
+  width: 60px;
+  height: 60px;
+  border-radius: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.stat-icon i {
+  font-size: 28px;
+  color: #fff;
+}
+.stat-content {
+  margin-left: 15px;
+}
+.stat-title {
+  font-size: 14px;
+  color: #909399;
+}
+.stat-value {
+  font-size: 28px;
+  font-weight: bold;
+  color: #303133;
+  margin-top: 5px;
+}
+</style>
diff --git a/src/views/report/sampleRecord/index.vue b/src/views/report/sampleRecord/index.vue
new file mode 100644
index 0000000..4cbafd4
--- /dev/null
+++ b/src/views/report/sampleRecord/index.vue
@@ -0,0 +1,177 @@
+<template>
+  <div class="app-container">
+    <!-- 鎼滅储琛ㄥ崟 -->
+    <el-form ref="queryForm" :model="queryParams" :inline="true" size="small">
+      <el-form-item label="鏍峰搧缂栧彿" prop="sampleCode">
+        <el-input v-model="queryParams.sampleCode" placeholder="璇疯緭鍏ユ牱鍝佺紪鍙�" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="鏍峰搧鍚嶇О" prop="sampleName">
+        <el-input v-model="queryParams.sampleName" placeholder="璇疯緭鍏ユ牱鍝佸悕绉�" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="瀹㈡埛鍚嶇О" prop="custom">
+        <el-input v-model="queryParams.custom" placeholder="璇疯緭鍏ュ鎴峰悕绉�" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="棰嗙敤浜�" prop="receiveUser">
+        <el-input v-model="queryParams.receiveUser" placeholder="璇疯緭鍏ラ鐢ㄤ汉" clearable style="width: 150px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="鏃堕棿鑼冨洿" prop="timeRange">
+        <el-date-picker
+          v-model="timeRange"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="寮�濮嬫椂闂�"
+          end-placeholder="缁撴潫鏃堕棿"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" @click="handleQuery">鏌ヨ</el-button>
+        <el-button icon="el-icon-refresh" @click="resetQuery">閲嶇疆</el-button>
+        <el-button type="success" icon="el-icon-download" @click="handleExport">瀵煎嚭</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 鏁版嵁琛ㄦ牸 -->
+    <lims-table
+      :tableData="tableData"
+      :column="tableColumn"
+      :page="page"
+      :tableLoading="tableLoading"
+      @pagination="handlePagination"
+    >
+      <template #operation="{ row }">
+        <el-button type="text" size="mini" @click="handleFlow(row)">娴佽浆璁板綍</el-button>
+      </template>
+    </lims-table>
+
+    <!-- 娴佽浆璁板綍寮圭獥 -->
+    <el-dialog title="鏍峰搧娴佽浆璁板綍" :visible.sync="flowVisible" width="70%">
+      <el-timeline>
+        <el-timeline-item
+          v-for="(item, index) in flowData"
+          :key="index"
+          :timestamp="item.operateTime"
+          placement="top"
+          :color="getTimelineColor(item.status)"
+        >
+          <el-card>
+            <h4>{{ item.operateType }}</h4>
+            <p>鎿嶄綔浜猴細{{ item.operator }}</p>
+            <p>鐘舵�侊細{{ item.statusName }}</p>
+            <p v-if="item.remark">澶囨敞锛歿{ item.remark }}</p>
+          </el-card>
+        </el-timeline-item>
+      </el-timeline>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import limsTable from '@/components/Table/lims-table.vue'
+import { pageSampleRecord, getSampleFlow, exportSampleRecord } from '@/api/report/sampleRecord'
+
+export default {
+  name: 'SampleRecord',
+  components: { limsTable },
+  data() {
+    return {
+      queryParams: {},
+      timeRange: [],
+      tableData: [],
+      tableLoading: false,
+      page: { total: 0, size: 10, current: 1 },
+      flowVisible: false,
+      flowData: [],
+      tableColumn: [
+        { label: '鏍峰搧缂栧彿', prop: 'sampleCode', minWidth: '140px' },
+        { label: '鏍峰搧鍚嶇О', prop: 'sampleName', minWidth: '150px' },
+        { label: '濮旀墭缂栧彿', prop: 'entrustCode', minWidth: '140px' },
+        { label: '棰嗙敤浜�', prop: 'receiveUser', minWidth: '80px' },
+        { label: '棰嗙敤鏃堕棿', prop: 'receiveTime', minWidth: '160px' },
+        { label: '棰嗙敤鏁伴噺', prop: 'receiveNum', minWidth: '100px' },
+        { label: '棰嗙敤鐢ㄩ��', prop: 'purpose', minWidth: '120px' },
+        { label: '瀹㈡埛鍚嶇О', prop: 'custom', minWidth: '150px' },
+        { label: '瀛樻斁浣嶇疆', prop: 'location', minWidth: '120px' },
+        { label: '褰撳墠鐘舵��', prop: 'status', minWidth: '100px', dataType: 'tag', formatData: (val) => this.formatStatus(val), formatType: (val) => this.formatStatusType(val) },
+        {
+          label: '鎿嶄綔',
+          dataType: 'slot',
+          slot: 'operation',
+          minWidth: '100px'
+        }
+      ]
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    getList() {
+      this.tableLoading = true
+      const params = { ...this.queryParams }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      pageSampleRecord({ ...params, ...this.page })
+        .then(res => {
+          this.tableData = res.data.records || []
+          this.page.total = res.data.total || 0
+        })
+        .finally(() => (this.tableLoading = false))
+    },
+    handleQuery() {
+      this.page.current = 1
+      this.getList()
+    },
+    resetQuery() {
+      this.queryParams = {}
+      this.timeRange = []
+      this.handleQuery()
+    },
+    handleExport() {
+      const params = { ...this.queryParams }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      exportSampleRecord(params).then(res => {
+        this.downloadFile(res, '鏍峰搧棰嗘牱璁板綍.xlsx')
+      })
+    },
+    handleFlow(row) {
+      getSampleFlow(row.id).then(res => {
+        this.flowData = res.data || []
+        this.flowVisible = true
+      })
+    },
+    handlePagination({ page, limit }) {
+      this.page.current = page
+      this.page.size = limit
+      this.getList()
+    },
+    formatStatus(val) {
+      const map = { 0: '鍦ㄥ簱', 1: '宸查鐢�', 2: '宸插綊杩�', 3: '宸插鐞�' }
+      return map[val] || ''
+    },
+    formatStatusType(val) {
+      const map = { 0: 'success', 1: 'warning', 2: 'info', 3: 'danger' }
+      return map[val] || ''
+    },
+    getTimelineColor(status) {
+      const map = { 0: '#67C23A', 1: '#E6A23C', 2: '#909399', 3: '#F56C6C' }
+      return map[status] || '#409EFF'
+    },
+    downloadFile(data, fileName) {
+      const blob = new Blob([data])
+      const url = window.URL.createObjectURL(blob)
+      const link = document.createElement('a')
+      link.href = url
+      link.download = fileName
+      link.click()
+      window.URL.revokeObjectURL(url)
+    }
+  }
+}
+</script>
diff --git a/src/views/report/spcChart/index.vue b/src/views/report/spcChart/index.vue
new file mode 100644
index 0000000..66c7015
--- /dev/null
+++ b/src/views/report/spcChart/index.vue
@@ -0,0 +1,326 @@
+<template>
+  <div class="app-container">
+    <!-- 鍒嗘瀽閰嶇疆琛ㄥ崟 -->
+    <el-card shadow="hover" style="margin-bottom: 20px;">
+      <div slot="header">SPC鍒嗘瀽閰嶇疆</div>
+      <el-form ref="analysisForm" :model="analysisParams" :inline="true" size="small" label-width="100px">
+        <el-form-item label="妫�娴嬮」鐩�" prop="projectId">
+          <el-select v-model="analysisParams.projectId" placeholder="璇烽�夋嫨妫�娴嬮」鐩�" style="width: 200px" @change="handleProjectChange">
+            <el-option v-for="item in projectList" :key="item.id" :label="item.projectName" :value="item.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="妫�娴嬪弬鏁�" prop="paramName">
+          <el-select v-model="analysisParams.paramName" placeholder="璇烽�夋嫨妫�娴嬪弬鏁�" style="width: 200px">
+            <el-option v-for="item in paramList" :key="item.paramName" :label="item.paramName" :value="item.paramName" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鏃堕棿鑼冨洿" prop="timeRange">
+          <el-date-picker
+            v-model="analysisParams.timeRange"
+            type="daterange"
+            range-separator="-"
+            start-placeholder="寮�濮嬫椂闂�"
+            end-placeholder="缁撴潫鏃堕棿"
+            value-format="yyyy-MM-dd"
+            style="width: 240px"
+          />
+        </el-form-item>
+        <el-form-item label="瀛愮粍澶у皬" prop="subgroupSize">
+          <el-input-number v-model="analysisParams.subgroupSize" :min="2" :max="25" style="width: 150px" />
+        </el-form-item>
+        <el-form-item label="鎺у埗涓婇檺UCL" prop="ucl">
+          <el-input-number v-model="analysisParams.ucl" :precision="4" style="width: 150px" />
+        </el-form-item>
+        <el-form-item label="鎺у埗涓嬮檺LCL" prop="lcl">
+          <el-input-number v-model="analysisParams.lcl" :precision="4" style="width: 150px" />
+        </el-form-item>
+        <el-form-item label="鐩爣鍊糃L" prop="targetValue">
+          <el-input-number v-model="analysisParams.targetValue" :precision="4" style="width: 150px" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="el-icon-data-analysis" @click="handleAnalysis">寮�濮嬪垎鏋�</el-button>
+          <el-button type="success" icon="el-icon-download" @click="handleExport">瀵煎嚭鏁版嵁</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <!-- SPC鍥捐〃鍖哄煙 -->
+    <el-row :gutter="20" v-if="analysisResult">
+      <el-col :span="12">
+        <el-card shadow="hover">
+          <div slot="header">X-Bar鎺у埗鍥�</div>
+          <Echart
+            :xAxis="xBarXAxis"
+            :yAxis="xBarYAxis"
+            :series="xBarSeries"
+            :tooltip="{ trigger: 'axis' }"
+            :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+            :chartStyle="{ height: '350px' }"
+          />
+        </el-card>
+      </el-col>
+      <el-col :span="12">
+        <el-card shadow="hover">
+          <div slot="header">R鎺у埗鍥�</div>
+          <Echart
+            :xAxis="rChartXAxis"
+            :yAxis="rChartYAxis"
+            :series="rChartSeries"
+            :tooltip="{ trigger: 'axis' }"
+            :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+            :chartStyle="{ height: '350px' }"
+          />
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 鍒剁▼鑳藉姏鍒嗘瀽 -->
+    <el-card shadow="hover" style="margin-top: 20px;" v-if="analysisResult">
+      <div slot="header">鍒剁▼鑳藉姏鍒嗘瀽</div>
+      <el-row :gutter="20">
+        <el-col :span="6">
+          <div class="capability-card">
+            <div class="capability-label">Cp (鍒剁▼绮惧瘑搴�)</div>
+            <div class="capability-value" :style="{ color: getCapabilityColor(analysisResult.capability.cp) }">
+              {{ analysisResult.capability.cp || '--' }}
+            </div>
+            <div class="capability-status">{{ getCapabilityStatus(analysisResult.capability.cp) }}</div>
+          </div>
+        </el-col>
+        <el-col :span="6">
+          <div class="capability-card">
+            <div class="capability-label">Cpk (鍒剁▼绮剧‘搴�)</div>
+            <div class="capability-value" :style="{ color: getCapabilityColor(analysisResult.capability.cpk) }">
+              {{ analysisResult.capability.cpk || '--' }}
+            </div>
+            <div class="capability-status">{{ getCapabilityStatus(analysisResult.capability.cpk) }}</div>
+          </div>
+        </el-col>
+        <el-col :span="6">
+          <div class="capability-card">
+            <div class="capability-label">Pp (杩囩▼绮惧瘑搴�)</div>
+            <div class="capability-value" :style="{ color: getCapabilityColor(analysisResult.capability.pp) }">
+              {{ analysisResult.capability.pp || '--' }}
+            </div>
+            <div class="capability-status">{{ getCapabilityStatus(analysisResult.capability.pp) }}</div>
+          </div>
+        </el-col>
+        <el-col :span="6">
+          <div class="capability-card">
+            <div class="capability-label">Ppk (杩囩▼绮剧‘搴�)</div>
+            <div class="capability-value" :style="{ color: getCapabilityColor(analysisResult.capability.ppk) }">
+              {{ analysisResult.capability.ppk || '--' }}
+            </div>
+            <div class="capability-status">{{ getCapabilityStatus(analysisResult.capability.ppk) }}</div>
+          </div>
+        </el-col>
+      </el-row>
+    </el-card>
+
+    <!-- 鍒嗘瀽鏁版嵁琛ㄦ牸 -->
+    <el-card shadow="hover" style="margin-top: 20px;" v-if="analysisResult">
+      <div slot="header">鍒嗘瀽鏁版嵁鏄庣粏</div>
+      <el-table :data="analysisDataTable" border style="width: 100%" max-height="400">
+        <el-table-column prop="subgroupNo" label="瀛愮粍鍙�" width="80" />
+        <el-table-column prop="xBar" label="X鍧囧��" width="100" />
+        <el-table-column prop="range" label="鏋佸樊R" width="100" />
+        <el-table-column v-for="(item, index) in subgroupColumns" :key="index" :prop="`value${index + 1}`" :label="`鏁版嵁${index + 1}`" width="100" />
+        <el-table-column prop="status" label="鐘舵��" width="100">
+          <template slot-scope="scope">
+            <el-tag :type="scope.row.status === '姝e父' ? 'success' : 'danger'">{{ scope.row.status }}</el-tag>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import Echart from '@/components/echarts/echarts.vue'
+import { spcAnalyze, getProjectList, getParamList, exportSpcData } from '@/api/report/spcChart'
+
+export default {
+  name: 'SpcChart',
+  components: { Echart },
+  data() {
+    return {
+      projectList: [],
+      paramList: [],
+      analysisParams: {
+        projectId: null,
+        paramName: null,
+        timeRange: [],
+        subgroupSize: 5,
+        ucl: null,
+        lcl: null,
+        targetValue: null
+      },
+      analysisResult: null,
+      analysisDataTable: [],
+      subgroupColumns: [],
+      // X-Bar鍥捐〃
+      xBarXAxis: [{ type: 'category', data: [] }],
+      xBarYAxis: [{ type: 'value' }],
+      xBarSeries: [
+        { name: 'X鍧囧��', type: 'line', data: [], markLine: { data: [] } }
+      ],
+      // R鍥捐〃
+      rChartXAxis: [{ type: 'category', data: [] }],
+      rChartYAxis: [{ type: 'value' }],
+      rChartSeries: [
+        { name: '鏋佸樊R', type: 'line', data: [], markLine: { data: [] } }
+      ]
+    }
+  },
+  mounted() {
+    this.getProjectList()
+  },
+  methods: {
+    getProjectList() {
+      getProjectList().then(res => {
+        this.projectList = res.data || []
+      })
+    },
+    handleProjectChange(projectId) {
+      this.analysisParams.paramName = null
+      getParamList(projectId).then(res => {
+        this.paramList = res.data || []
+      })
+    },
+    handleAnalysis() {
+      if (!this.analysisParams.projectId) {
+        this.$message.warning('璇烽�夋嫨妫�娴嬮」鐩�')
+        return
+      }
+      if (!this.analysisParams.paramName) {
+        this.$message.warning('璇烽�夋嫨妫�娴嬪弬鏁�')
+        return
+      }
+      const params = {
+        projectId: this.analysisParams.projectId,
+        paramName: this.analysisParams.paramName,
+        subgroupSize: this.analysisParams.subgroupSize,
+        ucl: this.analysisParams.ucl,
+        lcl: this.analysisParams.lcl,
+        targetValue: this.analysisParams.targetValue
+      }
+      if (this.analysisParams.timeRange && this.analysisParams.timeRange.length === 2) {
+        params.startDate = this.analysisParams.timeRange[0]
+        params.endDate = this.analysisParams.timeRange[1]
+      }
+      spcAnalyze(params).then(res => {
+        this.analysisResult = res.data
+        this.renderCharts(res.data)
+        this.renderDataTable(res.data)
+        this.$message.success('鍒嗘瀽瀹屾垚')
+      })
+    },
+    renderCharts(data) {
+      const xBarData = data.xBar || {}
+      const rChartData = data.rChart || {}
+      const subgroupLabels = (xBarData.data || []).map((_, i) => `缁�${i + 1}`)
+      // X-Bar鍥�
+      this.xBarXAxis[0].data = subgroupLabels
+      this.xBarSeries[0].data = xBarData.data || []
+      this.xBarSeries[0].markLine = {
+        data: [
+          { yAxis: xBarData.ucl, name: 'UCL', lineStyle: { color: '#F56C6C' } },
+          { yAxis: xBarData.lcl, name: 'LCL', lineStyle: { color: '#F56C6C' } },
+          { yAxis: xBarData.cl, name: 'CL', lineStyle: { color: '#409EFF' } }
+        ]
+      }
+      // R鍥�
+      this.rChartXAxis[0].data = subgroupLabels
+      this.rChartSeries[0].data = rChartData.data || []
+      this.rChartSeries[0].markLine = {
+        data: [
+          { yAxis: rChartData.ucl, name: 'UCL', lineStyle: { color: '#F56C6C' } },
+          { yAxis: rChartData.lcl, name: 'LCL', lineStyle: { color: '#F56C6C' } },
+          { yAxis: rChartData.cl, name: 'CL', lineStyle: { color: '#409EFF' } }
+        ]
+      }
+    },
+    renderDataTable(data) {
+      const subgroupSize = this.analysisParams.subgroupSize
+      this.subgroupColumns = []
+      for (let i = 1; i <= subgroupSize; i++) {
+        this.subgroupColumns.push({ prop: `value${i}` })
+      }
+      const rawData = data.rawData || []
+      const xBarData = data.xBar?.data || []
+      const rChartData = data.rChart?.data || []
+      const ucl = data.xBar?.ucl
+      const lcl = data.xBar?.lcl
+      this.analysisDataTable = rawData.map((group, index) => {
+        const xBar = xBarData[index]
+        const status = (ucl && lcl) ? (xBar >= lcl && xBar <= ucl ? '姝e父' : '寮傚父') : '姝e父'
+        const row = {
+          subgroupNo: index + 1,
+          xBar: xBar?.toFixed(4),
+          range: rChartData[index]?.toFixed(4),
+          status
+        }
+        group.forEach((val, i) => {
+          row[`value${i + 1}`] = val?.toFixed(4)
+        })
+        return row
+      })
+    },
+    handleExport() {
+      if (!this.analysisResult) {
+        this.$message.warning('璇峰厛杩涜SPC鍒嗘瀽')
+        return
+      }
+      const params = {
+        projectId: this.analysisParams.projectId,
+        paramName: this.analysisParams.paramName
+      }
+      exportSpcData(params).then(res => {
+        this.downloadFile(res, 'SPC鍒嗘瀽鏁版嵁.xlsx')
+      })
+    },
+    getCapabilityColor(val) {
+      if (val >= 1.33) return '#67C23A'
+      if (val >= 1) return '#E6A23C'
+      return '#F56C6C'
+    },
+    getCapabilityStatus(val) {
+      if (val >= 1.33) return '鍒剁▼鑳藉姏浼樼'
+      if (val >= 1) return '鍒剁▼鑳藉姏鍚堟牸'
+      return '鍒剁▼鑳藉姏涓嶈冻'
+    },
+    downloadFile(data, fileName) {
+      const blob = new Blob([data])
+      const url = window.URL.createObjectURL(blob)
+      const link = document.createElement('a')
+      link.href = url
+      link.download = fileName
+      link.click()
+      window.URL.revokeObjectURL(url)
+    }
+  }
+}
+</script>
+
+<style scoped>
+.capability-card {
+  text-align: center;
+  padding: 20px;
+  background: #f5f7fa;
+  border-radius: 8px;
+}
+.capability-label {
+  font-size: 14px;
+  color: #909399;
+}
+.capability-value {
+  font-size: 32px;
+  font-weight: bold;
+  margin-top: 10px;
+}
+.capability-status {
+  font-size: 12px;
+  color: #909399;
+  margin-top: 5px;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/report/testItemData/index.vue b/src/views/report/testItemData/index.vue
new file mode 100644
index 0000000..3040919
--- /dev/null
+++ b/src/views/report/testItemData/index.vue
@@ -0,0 +1,195 @@
+<template>
+  <div class="app-container">
+    <!-- 鎼滅储琛ㄥ崟 -->
+    <el-form ref="queryForm" :model="queryParams" :inline="true" size="small">
+      <el-form-item label="鐢熶骇璁㈠崟" prop="orderCode">
+        <el-input v-model="queryParams.orderCode" placeholder="璇疯緭鍏ョ敓浜ц鍗�" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="鎵规鍙�" prop="batchCode">
+        <el-input v-model="queryParams.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="鏍峰搧缂栧彿" prop="sampleCode">
+        <el-input v-model="queryParams.sampleCode" placeholder="璇疯緭鍏ユ牱鍝佺紪鍙�" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="鏍峰搧鍚嶇О" prop="sampleName">
+        <el-input v-model="queryParams.sampleName" placeholder="璇疯緭鍏ユ牱鍝佸悕绉�" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="妫�娴嬮」鐩�" prop="testItem">
+        <el-input v-model="queryParams.testItem" placeholder="璇疯緭鍏ユ娴嬮」鐩�" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" @click="handleQuery">鏌ヨ</el-button>
+        <el-button icon="el-icon-refresh" @click="resetQuery">閲嶇疆</el-button>
+        <el-button type="success" icon="el-icon-download" @click="handleExport">瀵煎嚭</el-button>
+        <el-button type="warning" icon="el-icon-sort" @click="handleCompare">妯悜姣旇緝</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 鏁版嵁琛ㄦ牸 -->
+    <lims-table
+      :tableData="tableData"
+      :column="tableColumn"
+      :page="page"
+      :tableLoading="tableLoading"
+      @pagination="handlePagination"
+    >
+      <template #operation="{ row }">
+        <el-button type="text" size="mini" @click="handleDetail(row)">鏌ョ湅璇︽儏</el-button>
+      </template>
+    </lims-table>
+
+    <!-- 璇︽儏寮圭獥 -->
+    <el-dialog title="妫�娴嬮」鐩鎯�" :visible.sync="detailVisible" width="80%" top="5vh">
+      <el-descriptions :column="3" border>
+        <el-descriptions-item label="鏍峰搧缂栧彿">{{ detailData.sampleCode }}</el-descriptions-item>
+        <el-descriptions-item label="鏍峰搧鍚嶇О">{{ detailData.sampleName }}</el-descriptions-item>
+        <el-descriptions-item label="濮旀墭缂栧彿">{{ detailData.entrustCode }}</el-descriptions-item>
+        <el-descriptions-item label="妫�娴嬮」鐩�">{{ detailData.testItem }}</el-descriptions-item>
+        <el-descriptions-item label="妫�娴嬬粨鏋�">{{ detailData.testResult }}</el-descriptions-item>
+        <el-descriptions-item label="鏍囧噯鍊�">{{ detailData.standardValue }}</el-descriptions-item>
+        <el-descriptions-item label="妫�娴嬩汉">{{ detailData.tester }}</el-descriptions-item>
+        <el-descriptions-item label="妫�娴嬫椂闂�">{{ detailData.testTime }}</el-descriptions-item>
+        <el-descriptions-item label="妫�娴嬭澶�">{{ detailData.deviceName }}</el-descriptions-item>
+      </el-descriptions>
+      <div style="margin-top: 20px;">
+        <h4>妫�娴嬫暟鎹槑缁�</h4>
+        <el-table :data="detailData.dataList" border style="width: 100%">
+          <el-table-column prop="paramName" label="鍙傛暟鍚嶇О" />
+          <el-table-column prop="standardValue" label="鏍囧噯鍊�" />
+          <el-table-column prop="actualValue" label="瀹炴祴鍊�" />
+          <el-table-column prop="unit" label="鍗曚綅" />
+          <el-table-column prop="result" label="鍒ゅ畾缁撴灉">
+            <template slot-scope="scope">
+              <el-tag :type="scope.row.result === '鍚堟牸' ? 'success' : 'danger'">{{ scope.row.result }}</el-tag>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </el-dialog>
+
+    <!-- 妯悜姣旇緝寮圭獥 -->
+    <el-dialog title="妫�娴嬫暟鎹í鍚戞瘮杈�" :visible.sync="compareVisible" width="90%" top="5vh">
+      <el-table :data="compareData" border style="width: 100%" max-height="500">
+        <el-table-column prop="sampleCode" label="鏍峰搧缂栧彿" fixed width="140" />
+        <el-table-column prop="sampleName" label="鏍峰搧鍚嶇О" fixed width="150" />
+        <el-table-column v-for="item in compareColumns" :key="item.prop" :prop="item.prop" :label="item.label" min-width="100">
+          <template slot-scope="scope">
+            <span :style="{ color: getCompareColor(scope.row[item.prop], item.standardValue) }">
+              {{ scope.row[item.prop] }}
+            </span>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import limsTable from '@/components/Table/lims-table.vue'
+import { pageTestItemData, getTestItemDetail, compareTestItem, exportTestItemData } from '@/api/report/testItemData'
+
+export default {
+  name: 'TestItemData',
+  components: { limsTable },
+  data() {
+    return {
+      queryParams: {},
+      tableData: [],
+      tableLoading: false,
+      page: { total: 0, size: 10, current: 1 },
+      detailVisible: false,
+      detailData: {},
+      compareVisible: false,
+      compareData: [],
+      compareColumns: [],
+      tableColumn: [
+        { label: '濮旀墭缂栧彿', prop: 'entrustCode', minWidth: '140px' },
+        { label: '鐢熶骇璁㈠崟', prop: 'orderCode', minWidth: '140px' },
+        { label: '鎵规鍙�', prop: 'batchCode', minWidth: '120px' },
+        { label: '鏍峰搧缂栧彿', prop: 'sampleCode', minWidth: '140px' },
+        { label: '鏍峰搧鍚嶇О', prop: 'sampleName', minWidth: '150px' },
+        { label: '妫�娴嬮」鐩�', prop: 'testItem', minWidth: '120px' },
+        {
+          label: '妫�娴嬬粨鏋�',
+          prop: 'testResult',
+          minWidth: '100px',
+          dataType: 'tag',
+          formatData: (val) => val === 1 ? '鍚堟牸' : '涓嶅悎鏍�',
+          formatType: (val) => val === 1 ? 'success' : 'danger'
+        },
+        { label: '妫�娴嬩汉', prop: 'tester', minWidth: '80px' },
+        { label: '妫�娴嬫椂闂�', prop: 'testTime', minWidth: '160px' },
+        { label: '鎶ュ憡缂栧彿', prop: 'reportCode', minWidth: '140px' },
+        {
+          label: '鎿嶄綔',
+          dataType: 'slot',
+          slot: 'operation',
+          minWidth: '100px'
+        }
+      ]
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    getList() {
+      this.tableLoading = true
+      pageTestItemData({ ...this.queryParams, ...this.page })
+        .then(res => {
+          this.tableData = res.data.records || []
+          this.page.total = res.data.total || 0
+        })
+        .finally(() => (this.tableLoading = false))
+    },
+    handleQuery() {
+      this.page.current = 1
+      this.getList()
+    },
+    resetQuery() {
+      this.queryParams = {}
+      this.handleQuery()
+    },
+    handleExport() {
+      exportTestItemData(this.queryParams).then(res => {
+        this.downloadFile(res, '妫�娴嬮」鐩暟鎹�.xlsx')
+      })
+    },
+    handleDetail(row) {
+      getTestItemDetail(row.id).then(res => {
+        this.detailData = res.data || {}
+        this.detailVisible = true
+      })
+    },
+    handleCompare() {
+      compareTestItem(this.queryParams).then(res => {
+        this.compareData = res.data.dataList || []
+        this.compareColumns = (res.data.columns || []).map(col => ({
+          prop: col.field,
+          label: col.name,
+          standardValue: col.standardValue
+        }))
+        this.compareVisible = true
+      })
+    },
+    handlePagination({ page, limit }) {
+      this.page.current = page
+      this.page.size = limit
+      this.getList()
+    },
+    getCompareColor(value, standard) {
+      if (!standard) return ''
+      return value >= standard.min && value <= standard.max ? '' : '#F56C6C'
+    },
+    downloadFile(data, fileName) {
+      const blob = new Blob([data])
+      const url = window.URL.createObjectURL(blob)
+      const link = document.createElement('a')
+      link.href = url
+      link.download = fileName
+      link.click()
+      window.URL.revokeObjectURL(url)
+    }
+  }
+}
+</script>
diff --git a/src/views/report/workStatistics/index.vue b/src/views/report/workStatistics/index.vue
new file mode 100644
index 0000000..0209a5b
--- /dev/null
+++ b/src/views/report/workStatistics/index.vue
@@ -0,0 +1,326 @@
+<template>
+  <div class="app-container">
+    <!-- 鎼滅储琛ㄥ崟 -->
+    <el-form ref="queryForm" :model="queryParams" :inline="true" size="small">
+      <el-form-item label="浜哄憳濮撳悕" prop="userName">
+        <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ヤ汉鍛樺鍚�" clearable style="width: 150px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="閮ㄩ棬" prop="dept">
+        <el-input v-model="queryParams.dept" placeholder="璇疯緭鍏ラ儴闂�" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
+      </el-form-item>
+      <el-form-item label="鏃堕棿鑼冨洿" prop="timeRange">
+        <el-date-picker
+          v-model="timeRange"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="寮�濮嬫椂闂�"
+          end-placeholder="缁撴潫鏃堕棿"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" @click="handleQuery">鏌ヨ</el-button>
+        <el-button icon="el-icon-refresh" @click="resetQuery">閲嶇疆</el-button>
+        <el-button type="success" icon="el-icon-download" @click="handleExport">瀵煎嚭</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- Tab鍒囨崲 -->
+    <el-tabs v-model="activeTab" @tab-click="handleTabChange">
+      <el-tab-pane label="浜哄憳宸ヤ綔缁熻" name="user">
+        <!-- 缁熻鍗$墖 -->
+        <el-row :gutter="20" style="margin-bottom: 20px;">
+          <el-col :span="6">
+            <el-card shadow="hover">
+              <div class="stat-card">
+                <div class="stat-title">妫�娴嬫牱鍝佹�绘暟</div>
+                <div class="stat-value">{{ userStatistics.totalSamples || 0 }}</div>
+              </div>
+            </el-card>
+          </el-col>
+          <el-col :span="6">
+            <el-card shadow="hover">
+              <div class="stat-card">
+                <div class="stat-title">妫�娴嬮」鐩�绘暟</div>
+                <div class="stat-value">{{ userStatistics.totalItems || 0 }}</div>
+              </div>
+            </el-card>
+          </el-col>
+          <el-col :span="6">
+            <el-card shadow="hover">
+              <div class="stat-card">
+                <div class="stat-title">骞冲潎妫�娴嬪強鏃剁巼</div>
+                <div class="stat-value">{{ userStatistics.avgTimelyRate || 0 }}%</div>
+              </div>
+            </el-card>
+          </el-col>
+          <el-col :span="6">
+            <el-card shadow="hover">
+              <div class="stat-card">
+                <div class="stat-title">鍙備笌浜哄憳鏁�</div>
+                <div class="stat-value">{{ userStatistics.userCount || 0 }}</div>
+              </div>
+            </el-card>
+          </el-col>
+        </el-row>
+
+        <!-- 鏁版嵁琛ㄦ牸 -->
+        <lims-table
+          :tableData="userTableData"
+          :column="userTableColumn"
+          :page="page"
+          :tableLoading="tableLoading"
+          @pagination="handlePagination"
+        />
+      </el-tab-pane>
+
+      <el-tab-pane label="鍙婃椂鐜囩粺璁�" name="timely">
+        <!-- 鍙婃椂鐜囧浘琛� -->
+        <el-row :gutter="20" style="margin-bottom: 20px;">
+          <el-col :span="12">
+            <el-card shadow="hover">
+              <div slot="header">鏍峰搧璐熻矗浜哄強鏃剁巼</div>
+              <Echart
+                :xAxis="chargeTimelyXAxis"
+                :yAxis="chargeTimelyYAxis"
+                :series="chargeTimelySeries"
+                :tooltip="{ trigger: 'axis' }"
+                :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+                :chartStyle="{ height: '350px' }"
+              />
+            </el-card>
+          </el-col>
+          <el-col :span="12">
+            <el-card shadow="hover">
+              <div slot="header">璇曢獙鍛樺強鏃剁巼</div>
+              <Echart
+                :xAxis="testerTimelyXAxis"
+                :yAxis="testerTimelyYAxis"
+                :series="testerTimelySeries"
+                :tooltip="{ trigger: 'axis' }"
+                :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+                :chartStyle="{ height: '350px' }"
+              />
+            </el-card>
+          </el-col>
+        </el-row>
+
+        <!-- 鍙婃椂鐜囨暟鎹〃鏍� -->
+        <lims-table
+          :tableData="timelyTableData"
+          :column="timelyTableColumn"
+          :page="timelyPage"
+          :tableLoading="timelyTableLoading"
+          @pagination="handleTimelyPagination"
+        />
+      </el-tab-pane>
+
+      <el-tab-pane label="宸ヤ綔瓒嬪娍" name="trend">
+        <el-card shadow="hover">
+          <div slot="header">
+            <span>宸ヤ綔瓒嬪娍鍥�</span>
+            <el-radio-group v-model="trendType" size="mini" style="float: right;" @change="getTrendData">
+              <el-radio-button label="week">杩戜竴鍛�</el-radio-button>
+              <el-radio-button label="month">杩戜竴鏈�</el-radio-button>
+              <el-radio-button label="year">杩戜竴骞�</el-radio-button>
+            </el-radio-group>
+          </div>
+          <Echart
+            :xAxis="trendXAxis"
+            :yAxis="trendYAxis"
+            :series="trendSeries"
+            :tooltip="{ trigger: 'axis' }"
+            :legend="{ data: ['鏍峰搧鏁�', '椤圭洰鏁�', '鍙婃椂鐜�'] }"
+            :grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
+            :chartStyle="{ height: '400px' }"
+          />
+        </el-card>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import Echart from '@/components/echarts/echarts.vue'
+import limsTable from '@/components/Table/lims-table.vue'
+import { getStatisticsByUser, getTimelyRate, getWorkTrend, exportWorkStatistics } from '@/api/report/workStatistics'
+
+export default {
+  name: 'WorkStatistics',
+  components: { Echart, limsTable },
+  data() {
+    return {
+      queryParams: {},
+      timeRange: [],
+      activeTab: 'user',
+      tableLoading: false,
+      userTableData: [],
+      userStatistics: {},
+      page: { total: 0, size: 10, current: 1 },
+      userTableColumn: [
+        { label: '浜哄憳濮撳悕', prop: 'userName', minWidth: '100px' },
+        { label: '閮ㄩ棬', prop: 'dept', minWidth: '120px' },
+        { label: '妫�娴嬫牱鍝佹暟', prop: 'sampleCount', minWidth: '100px' },
+        { label: '妫�娴嬮」鐩暟', prop: 'itemCount', minWidth: '100px' },
+        { label: '鍙婃椂瀹屾垚鏁�', prop: 'timelyCount', minWidth: '100px' },
+        { label: '瓒呮湡瀹屾垚鏁�', prop: 'overdueCount', minWidth: '100px' },
+        { label: '鍙婃椂鐜�', prop: 'timelyRate', minWidth: '100px', formatData: (val) => `${val}%` },
+        { label: '骞冲潎鐢ㄦ椂(h)', prop: 'avgTime', minWidth: '100px' }
+      ],
+      // 鍙婃椂鐜囨暟鎹�
+      timelyTableLoading: false,
+      timelyTableData: [],
+      timelyPage: { total: 0, size: 10, current: 1 },
+      timelyTableColumn: [
+        { label: '浜哄憳濮撳悕', prop: 'userName', minWidth: '100px' },
+        { label: '瑙掕壊', prop: 'roleType', minWidth: '100px' },
+        { label: '璐熻矗鏍峰搧鏁�', prop: 'sampleCount', minWidth: '100px' },
+        { label: '鎸夋椂瀹屾垚鏁�', prop: 'timelyCount', minWidth: '100px' },
+        { label: '瓒呮湡鏁�', prop: 'overdueCount', minWidth: '100px' },
+        { label: '鍙婃椂鐜�', prop: 'timelyRate', minWidth: '100px', dataType: 'tag', formatData: (val) => `${val}%`, formatType: (val) => val >= 90 ? 'success' : val >= 70 ? 'warning' : 'danger' }
+      ],
+      trendType: 'week',
+      // 鍥捐〃閰嶇疆
+      chargeTimelyXAxis: [{ type: 'category', data: [] }],
+      chargeTimelyYAxis: [{ type: 'value', max: 100 }],
+      chargeTimelySeries: [{ name: '鍙婃椂鐜�', type: 'bar', data: [] }],
+      testerTimelyXAxis: [{ type: 'category', data: [] }],
+      testerTimelyYAxis: [{ type: 'value', max: 100 }],
+      testerTimelySeries: [{ name: '鍙婃椂鐜�', type: 'bar', data: [] }],
+      trendXAxis: [{ type: 'category', data: [] }],
+      trendYAxis: [{ type: 'value' }, { type: 'value', max: 100, position: 'right' }],
+      trendSeries: [
+        { name: '鏍峰搧鏁�', type: 'bar', data: [] },
+        { name: '椤圭洰鏁�', type: 'bar', data: [] },
+        { name: '鍙婃椂鐜�', type: 'line', yAxisIndex: 1, data: [] }
+      ]
+    }
+  },
+  mounted() {
+    this.getUserData()
+  },
+  methods: {
+    handleTabChange(tab) {
+      if (tab.name === 'user') {
+        this.getUserData()
+      } else if (tab.name === 'timely') {
+        this.getTimelyData()
+      } else if (tab.name === 'trend') {
+        this.getTrendData()
+      }
+    },
+    getUserData() {
+      this.tableLoading = true
+      const params = { ...this.queryParams }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      getStatisticsByUser({ ...params, ...this.page })
+        .then(res => {
+          this.userTableData = res.data.records || []
+          this.page.total = res.data.total || 0
+          this.userStatistics = res.data.statistics || {}
+        })
+        .finally(() => (this.tableLoading = false))
+    },
+    getTimelyData() {
+      this.timelyTableLoading = true
+      const params = { ...this.queryParams }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      getTimelyRate({ ...params, ...this.timelyPage })
+        .then(res => {
+          this.timelyTableData = res.data.records || []
+          this.timelyPage.total = res.data.total || 0
+          // 鍥捐〃鏁版嵁
+          const chargeData = (res.data.chargeList || []).slice(0, 10)
+          this.chargeTimelyXAxis[0].data = chargeData.map(item => item.userName)
+          this.chargeTimelySeries[0].data = chargeData.map(item => item.timelyRate)
+          const testerData = (res.data.testerList || []).slice(0, 10)
+          this.testerTimelyXAxis[0].data = testerData.map(item => item.userName)
+          this.testerTimelySeries[0].data = testerData.map(item => item.timelyRate)
+        })
+        .finally(() => (this.timelyTableLoading = false))
+    },
+    getTrendData() {
+      const params = { timeType: this.trendType }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      getWorkTrend(params).then(res => {
+        this.trendXAxis[0].data = res.data.dates || []
+        this.trendSeries[0].data = res.data.sampleCounts || []
+        this.trendSeries[1].data = res.data.itemCounts || []
+        this.trendSeries[2].data = res.data.timelyRates || []
+      })
+    },
+    handleQuery() {
+      if (this.activeTab === 'user') {
+        this.page.current = 1
+        this.getUserData()
+      } else if (this.activeTab === 'timely') {
+        this.timelyPage.current = 1
+        this.getTimelyData()
+      } else {
+        this.getTrendData()
+      }
+    },
+    resetQuery() {
+      this.queryParams = {}
+      this.timeRange = []
+      this.handleQuery()
+    },
+    handleExport() {
+      const params = { ...this.queryParams, type: this.activeTab }
+      if (this.timeRange && this.timeRange.length === 2) {
+        params.startTime = this.timeRange[0]
+        params.endTime = this.timeRange[1]
+      }
+      exportWorkStatistics(params).then(res => {
+        this.downloadFile(res, '宸ヤ綔缁熻.xlsx')
+      })
+    },
+    handlePagination({ page, limit }) {
+      this.page.current = page
+      this.page.size = limit
+      this.getUserData()
+    },
+    handleTimelyPagination({ page, limit }) {
+      this.timelyPage.current = page
+      this.timelyPage.size = limit
+      this.getTimelyData()
+    },
+    downloadFile(data, fileName) {
+      const blob = new Blob([data])
+      const url = window.URL.createObjectURL(blob)
+      const link = document.createElement('a')
+      link.href = url
+      link.download = fileName
+      link.click()
+      window.URL.revokeObjectURL(url)
+    }
+  }
+}
+</script>
+
+<style scoped>
+.stat-card {
+  text-align: center;
+  padding: 15px 0;
+}
+.stat-title {
+  font-size: 14px;
+  color: #909399;
+}
+.stat-value {
+  font-size: 28px;
+  font-weight: bold;
+  color: #303133;
+  margin-top: 10px;
+}
+</style>

--
Gitblit v1.9.3