From 3df460667d74868d0a54fce12d5e881bbd6c0c1a Mon Sep 17 00:00:00 2001
From: WXL <wl_5969728@163.com>
Date: 星期四, 17 九月 2026 17:11:18 +0800
Subject: [PATCH] 青岛维护统计

---
 src/assets/styles/index.scss                                 |    6 
 src/utils/statisticsMode.js                                  |   21 
 src/views/business/transfer/TransportEdit.vue                |    2 
 src/views/business/course/ccss.vue                           |    2 
 src/views/business/ethicalReview/ethicalReviewInfo.vue       |   29 
 src/api/businessApi/index.js                                 |    1 
 src/views/business/GetWitness/GetWitnessInfo.vue             |    2 
 src/views/statistics/rate.vue                                |  406 +++--
 src/views/business/OrganUtilization/OrganUtilizationInfo.vue |    2 
 src/utils/totals.js                                          |   42 
 src/utils/chartStyle.js                                      |  139 ++
 src/assets/logo.zip                                          |    0 
 src/views/business/affirm/affirmInfo.vue                     |   49 
 src/views/business/maintain/maintainInfo.vue                 |    2 
 src/views/statistics/organ.vue                               |  239 +--
 src/views/statistics/case.vue                                |  369 ++---
 src/views/business/followupVisit/info.vue                    |    2 
 src/views/business/assess/assessInfo.vue                     |    2 
 src/views/statistics/willingness.vue                         |  464 +++---
 src/api/businessApi/ethicalReview.js                         |    8 
 src/views/business/decide/DecideInfo.vue                     |    2 
 src/views/statistics/index.vue                               |   50 
 src/store/modules/permission.js                              |    5 
 src/views/statistics/donor.vue                               |  580 +++-----
 src/views/business/course/index.vue                          |   17 
 /dev/null                                                    |    0 
 src/api/businessApi/statistics.js                            |   60 
 src/views/business/course/components/EthicalReviewStage.vue  |   18 
 src/views/business/allocation/allocationInfo.vue             |    2 
 src/views/business/ethicalReview/index.vue                   |  131 +-
 src/layout/components/AppMain.vue                            |   11 
 src/views/statistics/components/CaseDetail.vue               |   92 +
 src/api/businessApi/statisticsMock.js                        |  157 ++
 src/components/FilePreviewDialog/index.vue                   |   63 
 src/views/statistics/utilization.vue                         |  657 ++--------
 35 files changed, 1,885 insertions(+), 1,747 deletions(-)

diff --git "a/dist \0502\051.zip" "b/dist \0502\051.zip"
deleted file mode 100644
index c97fd34..0000000
--- "a/dist \0502\051.zip"
+++ /dev/null
Binary files differ
diff --git a/src/api/businessApi/ethicalReview.js b/src/api/businessApi/ethicalReview.js
index 7a28f1b..0ec8c8e 100644
--- a/src/api/businessApi/ethicalReview.js
+++ b/src/api/businessApi/ethicalReview.js
@@ -32,6 +32,14 @@
     params: query
   });
 }
+// 浼︾悊瀹℃煡妗f涓嬭浇
+export function ethicalReviewDownload(query) {
+  return request({
+    url: "/project/ethicalreviewopinions/download",
+    method: "get",
+    params: query
+  });
+}
 // 浼︾悊瀹℃煡淇℃伅璇︽儏
 export function ethicalreviewInfo(id) {
   return request({
diff --git a/src/api/businessApi/index.js b/src/api/businessApi/index.js
index 07a8fbe..e20ee31 100644
--- a/src/api/businessApi/index.js
+++ b/src/api/businessApi/index.js
@@ -9,3 +9,4 @@
 export * from "./maintain";
 export * from "./OrganUtilization";
 export * from "./transfer";
+export * from "./statistics";
diff --git a/src/api/businessApi/statistics.js b/src/api/businessApi/statistics.js
new file mode 100644
index 0000000..bc19ff4
--- /dev/null
+++ b/src/api/businessApi/statistics.js
@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+import { isStatisticsMock } from '@/utils/statisticsMode'
+import { mockTotals } from './statisticsMock'
+
+// mock 妯″紡涓嬭繑鍥炴ā鎷熸暟鎹紝鍚﹀垯璇锋眰鐪熷疄鎺ュ彛
+function post(url, data) {
+  if (isStatisticsMock()) {
+    return Promise.resolve({ code: 200, msg: '鎿嶄綔鎴愬姛', data: mockTotals(url, data) })
+  }
+  return request({
+    url,
+    method: 'post',
+    data
+  })
+}
+
+// 妗堜緥瓒嬪娍鍖哄煙
+export function caseTrendArea(data) {
+  return post('/opo/totals/caseTrendArea', data)
+}
+// 妗堜緥瓒嬪娍
+export function caseTrend(data) {
+  return post('/opo/totals/caseTrend', data)
+}
+// 妗堜緥瓒嬪娍鏈堜唤
+export function caseTrendMoon(data) {
+  return post('/opo/totals/caseTrendMoon', data)
+}
+// 骞冲潎骞撮緞
+export function caseAvgAge(data) {
+  return post('/opo/totals/caseAvgAge', data)
+}
+// 鎬у埆涓庤鍨嬪垎甯�
+export function caseBloodType(data) {
+  return post('/opo/totals/caseBloodType', data)
+}
+// 鎹愮尞鑰呭勾榫勬�у埆鍒嗗竷
+export function caseSex(data) {
+  return post('/opo/totals/caseSex', data)
+}
+// 鎹愮尞鍣ㄥ畼缁熻鏄庣粏
+export function organ(data) {
+  return post('/opo/totals/organ', data)
+}
+// 鍣ㄥ畼鑾峰彇/绉绘瓒嬪娍
+export function organMoon(data) {
+  return post('/opo/totals/organMoon', data)
+}
+// 鍣ㄥ畼绫诲瀷鍒嗗竷
+export function organType(data) {
+  return post('/opo/totals/organType', data)
+}
+// 鎬荤粺璁★紙鎬绘綔鍦ㄦ崘鐚渚� / 鎬诲畬鎴愭崘鐚暟锛�
+export function totals(data) {
+  return post('/opo/totals/totals', data)
+}
+// 妗堜緥鏄庣粏鍒楄〃
+export function trendList(data) {
+  return post('/opo/totals/trendList', data)
+}
diff --git a/src/api/businessApi/statisticsMock.js b/src/api/businessApi/statisticsMock.js
new file mode 100644
index 0000000..34a52d4
--- /dev/null
+++ b/src/api/businessApi/statisticsMock.js
@@ -0,0 +1,157 @@
+// 缁熻妯″潡妯℃嫙鏁版嵁
+// 鍝嶅簲瀛楁涓� /opo/totals/* 杩斿洖鐨� Totals(opo缁熻) 瀵硅薄淇濇寔涓�鑷�
+// mock 妯″紡涓嬬敱 statistics.js 杩斿洖锛岄〉闈㈤�昏緫鏃犻渶鍖哄垎鏁版嵁鏉ユ簮
+
+const REGIONS = ['闈掑矝鍦板尯', '鏃ョ収鍦板尯', '娴庡崡鍦板尯', '鐑熷彴鍦板尯', '濞佹捣鍦板尯', '娼嶅潑鍦板尯', '涓存矀鍦板尯'];
+const MONTHS = ['2024-01', '2024-02', '2024-03', '2024-04', '2024-05', '2024-06'];
+const YEARS = ['2022', '2023', '2024'];
+const ORGANS = ['鍏ㄨ倽', '鑲捐剰', '蹇冭剰', '鑲�', '鑳拌吅', '鐪艰鑶�'];
+const BLOOD_TYPES = ['A', 'B', 'O', 'AB'];
+const AGE_GROUPS = ['1', '2', '3', '4'];
+
+// 纭畾鎬т吉闅忔満锛屼繚璇佹瘡娆℃覆鏌撴暟鎹竴鑷�
+function createRand(seed) {
+  let s = seed % 2147483647;
+  if (s <= 0) s += 2147483646;
+  return function () {
+    s = (s * 16807) % 2147483647;
+    return (s - 1) / 2147483646;
+  };
+}
+
+// 妗堜緥瓒嬪娍锛堟湀浠界淮搴︼級
+function mockCaseTrend() {
+  const r = createRand(11);
+  return MONTHS.map((m) => {
+    const potential = 40 + Math.round(r() * 40);
+    return { donateTime: m, potentialCount: potential, completeCount: Math.round(potential * (0.6 + r() * 0.2)), totalCount: potential };
+  });
+}
+
+// 妗堜緥瓒嬪娍锛堝湴鍖虹淮搴︼級
+function mockCaseTrendArea() {
+  const r = createRand(22);
+  return REGIONS.map((area) => {
+    const potential = 30 + Math.round(r() * 70);
+    return { area, potentialCount: potential, completeCount: Math.round(potential * (0.6 + r() * 0.2)) };
+  });
+}
+
+// 妗堜緥鏄庣粏锛堝湴鍖� x 鏈堜唤锛�
+function mockCaseTrendMoon() {
+  const r = createRand(33);
+  const rows = [];
+  REGIONS.forEach((area) => {
+    MONTHS.forEach((m) => {
+      const potential = 5 + Math.round(r() * 15);
+      rows.push({ area, donateTime: m, potentialCount: potential, completeCount: Math.round(potential * (0.5 + r() * 0.4)), totalCount: potential });
+    });
+  });
+  return rows;
+}
+
+// 骞冲潎骞撮緞
+function mockCaseAvgAge() {
+  return [{ avgAge: 45.6 }];
+}
+
+// 鎬у埆涓庤鍨嬪垎甯�
+function mockCaseBloodType() {
+  const r = createRand(44);
+  return BLOOD_TYPES.map((boodType) => {
+    const man = 20 + Math.round(r() * 30);
+    const woman = 20 + Math.round(r() * 30);
+    return { boodType, manCount: man, womanCount: woman, boodTypeCount: man + woman };
+  });
+}
+
+// 鎹愮尞鑰呭勾榫勬�у埆鍒嗗竷
+function mockCaseSex() {
+  const r = createRand(55);
+  return AGE_GROUPS.map((ageGroup) => ({
+    ageGroup,
+    manCount: 15 + Math.round(r() * 40),
+    womanCount: 15 + Math.round(r() * 40)
+  }));
+}
+
+// 鎹愮尞鍣ㄥ畼缁熻鏄庣粏锛堝勾浠� x 鍣ㄥ畼 x 鍦板尯锛�
+function mockOrgan() {
+  const r = createRand(66);
+  const rows = [];
+  YEARS.forEach((year) => {
+    ORGANS.forEach((organName) => {
+      REGIONS.forEach((area) => {
+        const get = 10 + Math.round(r() * 40);
+        rows.push({ donateTime: year, organName, area, getCount: get, useCount: Math.round(get * (0.7 + r() * 0.25)) });
+      });
+    });
+  });
+  return rows;
+}
+
+// 鍣ㄥ畼鑾峰彇/绉绘瓒嬪娍锛堟湀浠界淮搴︼級
+function mockOrganMoon() {
+  const r = createRand(77);
+  return MONTHS.map((m) => {
+    const get = 50 + Math.round(r() * 60);
+    return { donateTime: m, getCount: get, useCount: Math.round(get * (0.75 + r() * 0.2)) };
+  });
+}
+
+// 鍣ㄥ畼绫诲瀷鍒嗗竷
+function mockOrganType() {
+  const r = createRand(88);
+  return ORGANS.map((organName) => {
+    const get = 40 + Math.round(r() * 80);
+    return { organName, getCount: get, useCount: Math.round(get * (0.75 + r() * 0.2)) };
+  });
+}
+
+// 鎬荤粺璁★紙鎬绘綔鍦ㄦ崘鐚渚� / 鎬诲畬鎴愭崘鐚暟锛�
+function mockTotalsAll() {
+  return [{ totalPotential: 486, totalCompletion: 327 }];
+}
+
+// 妗堜緥鏄庣粏鍒楄〃锛坱rendList锛�
+function mockTrendList() {
+  const r = createRand(99);
+  const NAMES = ['寮犱笁', '鏉庡洓', '鐜嬩簲', '璧靛叚', '闄堜竷', '鍒樺叓', '鍛ㄤ節'];
+  const HOSPITALS = ['闈掑矝澶у闄勫睘鍖婚櫌', '闈掑矝甯傜珛鍖婚櫌', '闈掑矝甯備腑蹇冨尰闄�', '灞变笢澶у榻愰瞾鍖婚櫌(闈掑矝)'];
+  const DEATHS = ['鑴戝浼�', '蹇冩悘楠ゅ仠', '鑴戣绠℃剰澶�', '棰呰剳鑲跨槫', '缂烘哀鎬ц剳鐥�'];
+  const WORKFLOWS = [1, 2, 3, 4, 5, 6, 7, 99];
+  return NAMES.map((name, i) => {
+    const month = MONTHS[i % MONTHS.length];
+    const day = String(1 + Math.floor(r() * 27)).padStart(2, '0');
+    return {
+      id: 1000 + i,
+      caseNo: `C${month.replace('-', '')}00${i + 1}`,
+      name,
+      inpatientNo: `ZY${month.replace('-', '')}00${i + 1}`,
+      workflow: WORKFLOWS[i % WORKFLOWS.length],
+      organName: ORGANS[i % ORGANS.length],
+      donatetime: `${month}-${day} 10:30`,
+      deathReason: DEATHS[i % DEATHS.length],
+      treatmentHospitalName: HOSPITALS[i % HOSPITALS.length]
+    };
+  });
+}
+
+const MOCK_MAP = {
+  '/opo/totals/caseTrend': mockCaseTrend,
+  '/opo/totals/caseTrendArea': mockCaseTrendArea,
+  '/opo/totals/caseTrendMoon': mockCaseTrendMoon,
+  '/opo/totals/caseAvgAge': mockCaseAvgAge,
+  '/opo/totals/caseBloodType': mockCaseBloodType,
+  '/opo/totals/caseSex': mockCaseSex,
+  '/opo/totals/organ': mockOrgan,
+  '/opo/totals/organMoon': mockOrganMoon,
+  '/opo/totals/organType': mockOrganType,
+  '/opo/totals/totals': mockTotalsAll,
+  '/opo/totals/trendList': mockTrendList
+};
+
+export function mockTotals(url) {
+  const fn = MOCK_MAP[url];
+  return fn ? fn() : [];
+}
diff --git a/src/assets/logo.zip b/src/assets/logo.zip
new file mode 100644
index 0000000..323c979
--- /dev/null
+++ b/src/assets/logo.zip
Binary files differ
diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss
index 49eb8c9..877f0d2 100644
--- a/src/assets/styles/index.scss
+++ b/src/assets/styles/index.scss
@@ -132,6 +132,12 @@
   // background-color: rgba(94, 255, 0, 0.198);
 }
 
+// business 妯″潡锛氬ぇ瀹藉睆灞呬腑闄愬鐣欑櫧锛屽皬灞忚嚜鍔ㄩ摵婊�
+.app-main.business-mode > * {
+  max-width: 1200px;
+  margin: 0 auto;
+}
+
 .components-container {
   margin: 30px 50px;
   position: relative;
diff --git a/src/components/FilePreviewDialog/index.vue b/src/components/FilePreviewDialog/index.vue
index 4bfff0a..357c237 100644
--- a/src/components/FilePreviewDialog/index.vue
+++ b/src/components/FilePreviewDialog/index.vue
@@ -61,7 +61,7 @@
     <!-- PDF棰勮 -->
     <div v-else-if="fileType === 'pdf'" class="preview-container pdf-preview">
       <div class="pdf-toolbar">
-        <el-button-group>
+        <el-button-group v-if="!useNativePdf">
           <el-button
             size="mini"
             @click="changePage(currentPage - 1)"
@@ -81,7 +81,7 @@
           </el-button>
         </el-button-group>
 
-        <el-button-group class="zoom-controls">
+        <el-button-group v-if="!useNativePdf" class="zoom-controls">
           <el-button size="mini" @click="zoomOut" :disabled="scale <= 50">
             <i class="el-icon-zoom-out"></i> 缂╁皬
           </el-button>
@@ -99,7 +99,15 @@
         </el-button>
       </div>
 
-      <div class="pdf-content" v-loading="pdfLoading">
+      <!-- pdf.js 鍥犺法鍩�(CORS)鍔犺浇澶辫触鏃讹紝闄嶇骇涓烘祻瑙堝櫒鍘熺敓 PDF 棰勮锛坕frame 瀵艰埅涓嶅彈鍚屾簮绛栫暐闄愬埗锛� -->
+      <iframe
+        v-if="useNativePdf"
+        :src="fileUrl"
+        class="pdf-native-iframe"
+        frameborder="0"
+      ></iframe>
+
+      <div class="pdf-content" v-else v-loading="pdfLoading">
         <pdf
           ref="pdf"
           :src="pdfUrl"
@@ -214,6 +222,8 @@
       pageCount: 0,
       scale: 100,
       pageRotate: 0,
+      // pdf.js 鍔犺浇澶辫触锛堝 CORS锛夋椂闄嶇骇涓烘祻瑙堝櫒鍘熺敓棰勮
+      useNativePdf: false,
       // 鍥剧墖鐩稿叧
       imageScale: 100
     };
@@ -267,30 +277,11 @@
     },
     /** 璁$畻 baseUrlHt */
     calculateBaseUrl() {
-      // 鑾峰彇褰撳墠娴忚鍣ㄥ湴鍧�
-      const currentUrl = window.location.href;
-      console.log("褰撳墠娴忚鍣ㄥ湴鍧�:", currentUrl);
-
-      try {
-        // 浣跨敤 URL 瀵硅薄瑙f瀽
-        const urlObj = new URL(currentUrl);
-        // 鑾峰彇涓绘満鍚嶏紙IP 鎴栧煙鍚嶏級
-        const hostname = urlObj.hostname;
-        // 鎷兼帴绔彛 :9095
-        this.baseUrlHt = `http://${hostname}:9095`;
-        console.log("璁$畻寰楀埌鐨� baseUrlHt:", this.baseUrlHt);
-      } catch (error) {
-        console.error("瑙f瀽URL澶辫触:", error);
-        // 澶囩敤鏂规锛氫娇鐢ㄦ鍒欐彁鍙�
-        const match = currentUrl.match(/https?:\/\/([^:\/]+)/);
-        if (match) {
-          this.baseUrlHt = `http://${match[1]}:9095`;
-        } else {
-          // 鏈�鍚庣殑澶囩敤鏂规
-          this.baseUrlHt =
-            process.env.VUE_APP_BASE_API || "http://localhost:9095";
-        }
-      }
+      // 璧板綋鍓嶅煙鍚嶏紙鍚屽煙鐩稿璺緞锛夛紝鐢� nginx 鐨� /profile/ 浠g悊杞彂鍒板悗绔� 9095銆�
+      // 鐩磋繛 9095 绔彛浼氬洜绔彛涓嶅悓浜х敓璺ㄥ煙(CORS)锛岃�屽悗绔瓨鍦ㄥ弻閲� CORS 鍝嶅簲澶撮棶棰橈紝
+      // 鍚屽煙璇锋眰涓嶈Е鍙� CORS 妫�鏌ワ紝鍙粠鏍规簮涓婄粫寮�璇ラ棶棰橈紙鍥剧墖/PDF/涓嬭浇鍏ㄩ儴鐢熸晥锛夈��
+      this.baseUrlHt = "";
+      console.log("鏂囦欢璁块棶璧板悓鍩熶唬鐞�: /profile/");
     },
 
     /** 鍒濆鍖栭瑙� */
@@ -302,6 +293,7 @@
 
       // 澶勭悊URL锛屾浛鎹㈠煙鍚嶉儴鍒�
       this.fileUrl = this.processFileUrl(originalUrl);
+      console.log(this.fileUrl,'this.fileUrl');
 
       this.fileType = this.getFileType(this.fileName);
 
@@ -312,6 +304,7 @@
       this.scale = 100;
       this.imageScale = 100;
       this.pageCount = 0;
+      this.useNativePdf = false;
 
       // 妯℃嫙鍔犺浇寤惰繜
       setTimeout(() => {
@@ -360,8 +353,13 @@
     pdfErrorHandler(error) {
       console.error("PDF鍔犺浇澶辫触:", error);
       this.pdfLoading = false;
-      this.$message.error("PDF鏂囦欢鍔犺浇澶辫触锛岃灏濊瘯涓嬭浇鍚庢煡鐪�");
-      this.previewVisible = false;
+      // pdf.js 閫氳繃 fetch 鍔犺浇璺ㄥ煙 PDF 鍙� CORS 闄愬埗锛屽け璐ユ椂闄嶇骇涓烘祻瑙堝櫒鍘熺敓棰勮
+      // 锛坕frame 灞炰簬瀵艰埅璇锋眰锛屼笉鍙楀悓婧愮瓥鐣ラ檺鍒讹紝鍙互姝e父鍔犺浇璺ㄥ煙 PDF锛�
+      this.useNativePdf = true;
+      this.$message({
+        type: "info",
+        message: "宸插垏鎹负娴忚鍣ㄥ師鐢熼瑙堟ā寮�"
+      });
     },
 
     /** 鍥剧墖鍔犺浇瀹屾垚 */
@@ -513,6 +511,13 @@
     align-items: flex-start;
   }
 
+  /* 鍘熺敓 PDF 棰勮锛圕ORS 闄嶇骇鏂规锛� */
+  .pdf-native-iframe {
+    flex: 1;
+    border: none;
+    background: #f8f9fa;
+  }
+
   /* Office棰勮鏍峰紡 */
   .office-content {
     flex: 1;
diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue
index 0c6f4b7..72d9569 100644
--- a/src/layout/components/AppMain.vue
+++ b/src/layout/components/AppMain.vue
@@ -1,5 +1,5 @@
 <template>
-  <section class="app-main">
+  <section class="app-main" :class="{ 'business-mode': isBusiness }">
     <transition name="fade-transform" mode="out-in">
       <keep-alive :include="cachedViews">
         <router-view :key="key" />
@@ -17,6 +17,15 @@
     },
     key() {
       return this.$route.path
+    },
+    isBusiness() {
+      return (this.$route.matched || []).some((r) => {
+        const cp = r.meta && r.meta.componentPath;
+        if (typeof cp !== 'string' || cp.indexOf('business/') !== 0) return false;
+        // 涓氬姟妯″潡鐨� index 鍒楄〃椤典笉闇�瑕侀檺瀹斤紱course/index 鏄鎯呴〉锛屼繚鐣欓檺瀹�
+        if (/^business\/[^/]+\/index$/.test(cp) && cp !== 'business/course/index') return false;
+        return true;
+      });
     }
   }
 }
diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js
index dba9252..ce1c002 100644
--- a/src/store/modules/permission.js
+++ b/src/store/modules/permission.js
@@ -61,6 +61,11 @@
       route.children = filterChildren(route.children)
     }
     if (route.component) {
+      // 璁板綍鍘熷缁勪欢璺緞锛屼緵甯冨眬灞傚垽鏂墍灞炴ā鍧楋紙business/project 绛夛級
+      if (typeof route.component === 'string') {
+        route.meta = route.meta || {};
+        route.meta.componentPath = route.component; // 渚嬪 "business/assess/index"
+      }
       // Layout ParentView 缁勪欢鐗规畩澶勭悊
       if (route.component === 'Layout') {
         route.component = Layout
diff --git a/src/utils/chartStyle.js b/src/utils/chartStyle.js
new file mode 100644
index 0000000..9ba5bbe
--- /dev/null
+++ b/src/utils/chartStyle.js
@@ -0,0 +1,139 @@
+// 缁熻鍥捐〃缁熶竴娓呯埥绠�娲侀鏍硷紙echarts 4 鍏煎锛�
+// 渚� statistics/*.vue 澶嶇敤锛屼繚璇佽瑙変竴鑷�
+
+const toRgba = (hex, alpha) => {
+  const n = parseInt(hex.slice(1), 16);
+  const r = (n >> 16) & 255;
+  const g = (n >> 8) & 255;
+  const b = n & 255;
+  return `rgba(${r}, ${g}, ${b}, ${alpha})`;
+};
+
+export const COLORS = {
+  blue: '#5B8FF9',
+  green: '#5AD8A6',
+  amber: '#F6BD16',
+  coral: '#E8684A',
+  sky: '#6DC8EC',
+  purple: '#9270CA',
+  orange: '#FF9D4D',
+  teal: '#269A99'
+};
+
+export const PALETTE = Object.values(COLORS);
+
+const tipBase = {
+  backgroundColor: '#ffffff',
+  borderColor: '#e8eaf0',
+  borderWidth: 1,
+  padding: [10, 14],
+  textStyle: { color: '#333', fontSize: 13 },
+  extraCssText: 'box-shadow:0 6px 18px rgba(31,45,61,.1);border-radius:10px;'
+};
+
+// 鍧愭爣杞寸被 tooltip锛堟煴鐘�/鎶樼嚎锛�
+export function axisTooltip() {
+  return { trigger: 'axis', ...tipBase, axisPointer: { lineStyle: { color: '#c9d2e0' } } };
+}
+
+// 鍥惧舰绫� tooltip锛堥ゼ鍥撅級
+export function itemTooltip() {
+  return { trigger: 'item', ...tipBase, formatter: '{b}锛歿c} ({d}%)' };
+}
+
+export function grid() {
+  return { left: 8, right: 16, top: 44, bottom: 8, containLabel: true };
+}
+
+// 绫荤洰杞�
+export function xAxis(data) {
+  return {
+    type: 'category',
+    data,
+    axisLine: { show: false },
+    axisTick: { show: false },
+    axisLabel: { color: '#8a919f', fontSize: 12 }
+  };
+}
+
+// 鏁板�艰酱
+export function yAxis(name) {
+  return {
+    type: 'value',
+    name,
+    nameTextStyle: { color: '#b0b6c3', fontSize: 12 },
+    axisLabel: { color: '#8a919f', fontSize: 12 },
+    splitLine: { lineStyle: { type: 'dashed', color: '#f0f1f5' } }
+  };
+}
+
+export function legend(data) {
+  return {
+    data,
+    icon: 'roundRect',
+    itemWidth: 10,
+    itemHeight: 10,
+    itemGap: 18,
+    textStyle: { color: '#8a919f', fontSize: 12 }
+  };
+}
+
+// 鏌辩姸绯诲垪
+export function barSeries(name, data, color, opts = {}) {
+  return {
+    name,
+    type: 'bar',
+    data,
+    barMaxWidth: opts.maxWidth || 20,
+    itemStyle: { color, borderRadius: opts.radius || [4, 4, 0, 0] },
+    stack: opts.stack,
+    yAxisIndex: opts.yAxisIndex
+  };
+}
+
+// 鎶樼嚎绯诲垪
+export function lineSeries(name, data, color, opts = {}) {
+  const s = {
+    name,
+    type: 'line',
+    data,
+    smooth: true,
+    symbol: 'circle',
+    symbolSize: opts.symbolSize || 6,
+    lineStyle: { width: 3, color },
+    itemStyle: { color },
+    yAxisIndex: opts.yAxisIndex
+  };
+  if (opts.area) {
+    s.areaStyle = {
+      color: {
+        type: 'linear',
+        x: 0,
+        y: 0,
+        x2: 0,
+        y2: 1,
+        colorStops: [
+          { offset: 0, color: toRgba(color, 0.24) },
+          { offset: 1, color: toRgba(color, 0) }
+        ]
+      }
+    };
+  }
+  return s;
+}
+
+// 鐜舰鍥剧郴鍒�
+export function donutSeries(name, data) {
+  return {
+    name,
+    type: 'pie',
+    radius: ['52%', '72%'],
+    center: ['50%', '50%'],
+    avoidLabelOverlap: false,
+    itemStyle: { borderColor: '#fff', borderWidth: 2 },
+    label: { show: false },
+    labelLine: { show: false },
+    data,
+    color: PALETTE
+  };
+}
diff --git a/src/utils/statisticsMode.js b/src/utils/statisticsMode.js
new file mode 100644
index 0000000..8e7713b
--- /dev/null
+++ b/src/utils/statisticsMode.js
@@ -0,0 +1,21 @@
+// 缁熻妯″潡鏁版嵁婧愬紑鍏筹細mock(妯℃嫙鏁版嵁) / real(鐪熷疄鎺ュ彛)
+// 鍏ㄥ眬鍏变韩锛屽垏鎹㈠悗缁熻鍚勯〉闈㈣鍙栧悓涓�鐘舵��
+import Vue from 'vue';
+
+const KEY = 'opo-statistics-mock';
+const initial = localStorage.getItem(KEY) === '1';
+
+const state = Vue.observable({
+  useMock: initial
+});
+
+export function setStatisticsMock(useMock) {
+  state.useMock = !!useMock;
+  localStorage.setItem(KEY, state.useMock ? '1' : '0');
+}
+
+export function isStatisticsMock() {
+  return state.useMock;
+}
+
+export default state;
diff --git a/src/utils/totals.js b/src/utils/totals.js
new file mode 100644
index 0000000..069239a
--- /dev/null
+++ b/src/utils/totals.js
@@ -0,0 +1,42 @@
+// /opo/totals/* 缁熻鎺ュ彛鍏叡宸ュ叿
+// 鎺ュ彛璇锋眰浣撲负 Totals(opo缁熻) 瀵硅薄锛屽搷搴斾负 RuoYi Result { code, msg, data, total }
+// data 鍙兘鏄� Totals 鏁扮粍銆佸崟涓� Totals 瀵硅薄锛屾垨鍖呬簡涓�灞� {list/rows} 鐨勫璞�
+
+// 褰掍竴鍖栬繑鍥炵粨鏋滀负 Totals 鏁扮粍
+export function toTotalsList(result) {
+  const data = result && result.data;
+  if (!data) return [];
+  if (Array.isArray(data)) return data;
+  if (Array.isArray(data.list)) return data.list;
+  if (Array.isArray(data.rows)) return data.rows;
+  // 鍗曚釜缁熻瀵硅薄
+  return [data];
+}
+
+// 缁勮鏌ヨ鏉′欢锛屽彧浼犳湁鍊肩殑瀛楁
+// area: 鍦板尯鍚嶇О(涓枃)锛泂tartTime/endTime: yyyy-MM锛沘geGroup/sex/boodType/organName 涓虹瓫閫夌淮搴�
+export function buildTotalsQuery({ area, startTime, endTime, ageGroup, sex, boodType, organName } = {}) {
+  const q = {};
+  if (area) q.area = area;
+  if (startTime) q.startTime = startTime;
+  if (endTime) q.endTime = endTime;
+  if (ageGroup) q.ageGroup = ageGroup;
+  if (sex) q.sex = sex;
+  if (boodType) q.boodType = boodType;
+  if (organName) q.organName = organName;
+  return q;
+}
+
+// 鏈堜唤/鏃堕棿瀛楃涓茬粺涓�鎴� yyyy-MM
+export function toMonth(value) {
+  if (!value) return '';
+  const s = String(value);
+  const m = s.match(/(\d{4})-(\d{2})/);
+  return m ? `${m[1]}-${m[2]}` : s;
+}
+
+// 鏁板瓧瀹夊叏鍙栨暣
+export function toNum(v) {
+  const n = Number(v);
+  return Number.isFinite(n) ? n : 0;
+}
diff --git a/src/views/business/GetWitness/GetWitnessInfo.vue b/src/views/business/GetWitness/GetWitnessInfo.vue
index ea5578b..ccf0995 100644
--- a/src/views/business/GetWitness/GetWitnessInfo.vue
+++ b/src/views/business/GetWitness/GetWitnessInfo.vue
@@ -1759,8 +1759,6 @@
 .paper-form {
   border-left: 1px solid #d0d3db;
   background: #fff;
-  /* 瓒呭灞忛檺鍒惰〃鍗曟渶澶у搴︼紝鍙充晶鐣欑櫧锛岄伩鍏嶅瓧娈靛垎甯冭繃寮� */
-  max-width: 1400px;
 }
 
 .paper-form >>> .form-row {
diff --git a/src/views/business/OrganUtilization/OrganUtilizationInfo.vue b/src/views/business/OrganUtilization/OrganUtilizationInfo.vue
index 181ffa6..cc5957b 100644
--- a/src/views/business/OrganUtilization/OrganUtilizationInfo.vue
+++ b/src/views/business/OrganUtilization/OrganUtilizationInfo.vue
@@ -2073,8 +2073,6 @@
 .paper-form {
   border-left: 1px solid #d0d3db;
   background: #fff;
-  /* 瓒呭灞忛檺鍒惰〃鍗曟渶澶у搴︼紝鍙充晶鐣欑櫧锛岄伩鍏嶅瓧娈靛垎甯冭繃寮� */
-  max-width: 1400px;
 }
 
 .paper-form >>> .form-row {
diff --git a/src/views/business/affirm/affirmInfo.vue b/src/views/business/affirm/affirmInfo.vue
index 1bc030e..09db00e 100644
--- a/src/views/business/affirm/affirmInfo.vue
+++ b/src/views/business/affirm/affirmInfo.vue
@@ -1,20 +1,27 @@
 <template>
   <div class="confirmation-detail">
-    <case-basic-info :case-id="caseId" :show-attachment="true":column="3" />
+    <case-basic-info :case-id="caseId" :show-attachment="true" :column="3" />
 
     <el-card class="detail-card paper-card">
       <!-- 鍩虹淇℃伅 -->
       <div slot="header" class="clearfix">
         <span class="detail-title">鎹愮尞纭淇℃伅</span>
-
       </div>
 
       <el-form :model="form" ref="form" label-width="120px" class="paper-form">
         <div class="form-row">
-          <el-form-item label="鍗忚皟鍛�1" prop="coordinatedusernameo" class="col-1-3">
+          <el-form-item
+            label="鍗忚皟鍛�1"
+            prop="coordinatedusernameo"
+            class="col-1-3"
+          >
             <el-input v-model="form.coordinatedusernameo" />
           </el-form-item>
-          <el-form-item label="鍗忚皟鍛�2" prop="coordinatedusernamet" class="col-1-3">
+          <el-form-item
+            label="鍗忚皟鍛�2"
+            prop="coordinatedusernamet"
+            class="col-1-3"
+          >
             <el-input v-model="form.coordinatedusernamet" />
           </el-form-item>
           <!-- <el-col :span="8">
@@ -49,7 +56,7 @@
         </el-alert>
         <div style="margin-top: 15px;">
           <el-button
-          style="float: right; margin-bottom: 5px;"
+            style="float: right; margin-bottom: 5px;"
             type="primary"
             size="mini"
             icon="el-icon-plus"
@@ -144,7 +151,11 @@
         </div>
 
         <div class="form-row">
-          <el-form-item label="瀹跺睘鎰忚澶囨敞" prop="relativeRemark" class="col-full">
+          <el-form-item
+            label="瀹跺睘鎰忚澶囨敞"
+            prop="relativeRemark"
+            class="col-full"
+          >
             <el-input
               type="textarea"
               :rows="3"
@@ -618,9 +629,6 @@
     },
 
     getAttachmentsByType(type) {
-      console.log(type);
-      console.log(this.attachmentData[type]);
-
       return this.attachmentData[type] || [];
     },
 
@@ -689,7 +697,6 @@
         uploader: "褰撳墠鐢ㄦ埛"
       };
       this.attachmentData[this.currentUploadType].push(newAttachment);
-      console.log(this.attachmentData,'this.attachmentData');
 
       this.updateAssessannexField();
       this.$message.success("涓婁紶鎴愬姛");
@@ -1079,8 +1086,6 @@
 .paper-form {
   border-left: 1px solid #d0d3db;
   background: #fff;
-  /* 瓒呭灞忛檺鍒惰〃鍗曟渶澶у搴︼紝鍙充晶鐣欑櫧锛岄伩鍏嶅瓧娈靛垎甯冭繃寮� */
-  max-width: 1400px;
 }
 
 .paper-form >>> .form-row {
@@ -1117,10 +1122,18 @@
   display: none;
 }
 
-.paper-form >>> .col-1-3 { flex: 0 0 33.3333%; }
-.paper-form >>> .col-half { flex: 0 0 50%; }
-.paper-form >>> .col-2-3 { flex: 0 0 66.6666%; }
-.paper-form >>> .col-full { flex: 0 0 100%; }
+.paper-form >>> .col-1-3 {
+  flex: 0 0 33.3333%;
+}
+.paper-form >>> .col-half {
+  flex: 0 0 50%;
+}
+.paper-form >>> .col-2-3 {
+  flex: 0 0 66.6666%;
+}
+.paper-form >>> .col-full {
+  flex: 0 0 100%;
+}
 
 .paper-form >>> .el-form-item__label {
   flex: 0 0 150px;
@@ -1139,7 +1152,9 @@
   line-height: 1.3;
   white-space: nowrap;
 }
-.paper-form >>> .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label:before {
+.paper-form
+  >>> .el-form-item.is-required:not(.is-no-asterisk)
+  > .el-form-item__label:before {
   color: #f56c6c;
   margin-right: 2px;
 }
diff --git a/src/views/business/allocation/allocationInfo.vue b/src/views/business/allocation/allocationInfo.vue
index d714426..24fd22c 100644
--- a/src/views/business/allocation/allocationInfo.vue
+++ b/src/views/business/allocation/allocationInfo.vue
@@ -1885,8 +1885,6 @@
 .paper-form {
   border-left: 1px solid #d0d3db;
   background: #fff;
-  /* 瓒呭灞忛檺鍒惰〃鍗曟渶澶у搴︼紝鍙充晶鐣欑櫧锛岄伩鍏嶅瓧娈靛垎甯冭繃寮� */
-  max-width: 1400px;
 }
 
 .paper-form >>> .form-row {
diff --git a/src/views/business/assess/assessInfo.vue b/src/views/business/assess/assessInfo.vue
index bb6f49c..2a5d5a8 100644
--- a/src/views/business/assess/assessInfo.vue
+++ b/src/views/business/assess/assessInfo.vue
@@ -1558,8 +1558,6 @@
 .paper-form {
   border-left: 1px solid #d0d3db;
   background: #fff;
-  /* 瓒呭灞忛檺鍒惰〃鍗曟渶澶у搴︼紝鍙充晶鐣欑櫧锛岄伩鍏嶅瓧娈靛垎甯冭繃寮� */
-  max-width: 1400px;
 }
 
 .paper-form >>> .form-row {
diff --git a/src/views/business/course/ccss.vue b/src/views/business/course/ccss.vue
index d3442f8..05e160a 100644
--- a/src/views/business/course/ccss.vue
+++ b/src/views/business/course/ccss.vue
@@ -486,8 +486,6 @@
 .register-form {
   border-left: 1px solid #d0d3db;
   background: #fff;
-  /* 瓒呭灞忛檺鍒惰〃鍗曟渶澶у搴︼紝鍙充晶鐣欑櫧锛岄伩鍏嶅瓧娈靛垎甯冭繃寮� */
-  max-width: 1400px;
 }
 
 /* 姣忎竴琛屼负 flex 琛岋紝鑷甫涓婅竟妗嗕繚璇佺綉鏍艰繛缁�� */
diff --git a/src/views/business/course/components/EthicalReviewStage.vue b/src/views/business/course/components/EthicalReviewStage.vue
index 768af02..7d340ee 100644
--- a/src/views/business/course/components/EthicalReviewStage.vue
+++ b/src/views/business/course/components/EthicalReviewStage.vue
@@ -580,7 +580,7 @@
 
     // 浼︾悊瀹℃煡缁撹鏂囨湰锛堝彧璇诲睍绀猴級
     expertConclusionLabel() {
-      const map = { 0: "涓嶅悓鎰�", 1: "鍚屾剰", 2: "瀹℃煡涓�" };
+      const map = { 0: "涓嶅悓鎰�", 1: "鍚屾剰", 2: "瀹℃煡涓�", 3: "寮冩潈", 4: "淇敼鍚庡悓鎰�" };
       return map[this.form.expertConclusion] || "-";
     },
 
@@ -947,7 +947,9 @@
       console.log(file, "file");
 
       this.currentPreviewFile = {
-        fileName: file,
+        // 绛惧悕闄勪欢 URL 閫氬父鏃犳墿灞曞悕锛堝 .../file-1787645145583锛夛紝
+        // 棰勮缁勪欢渚濊禆鏂囦欢鍚嶆墿灞曞悕璇嗗埆鏂囦欢绫诲瀷锛岃繖閲岃ˉ .png 璁╁叾鎸夊浘鐗囬瑙�
+        fileName: "鎵嬪啓绛惧悕.png",
         fileUrl: file,
         fileType: "png"
       };
@@ -1038,7 +1040,7 @@
     getReviewStatusFilter(status) {
       const statusMap = {
         "0": "info", // 寰呮帴鏀�
-        "1": "warning", // 鏈帴鏀�
+        "1": "warning", // 宸插彂閫�
         "2": "success", // 宸叉帴鏀�
         "3": "danger", // 瓒呮椂
         "4": "danger", // 涓
@@ -1050,7 +1052,7 @@
     getReviewStatusText(status) {
       const statusMap = {
         "0": "寰呮帴鏀�",
-        "1": "鏈帴鏀�",
+        "1": "宸插彂閫�",
         "2": "宸叉帴鏀�",
         "3": "瓒呮椂",
         "4": "涓",
@@ -1062,18 +1064,22 @@
     // 缁撹杩囨护鍣�
     getConclusionFilter(conclusion) {
       const conclusionMap = {
+        "0": "danger", // 涓嶅悓鎰�
         "1": "success", // 鍚屾剰
         "2": "warning", // 瀹℃煡涓�
-        "0": "danger" // 涓嶅悓鎰�
+        "3": "info", // 寮冩潈
+        "4": "success" // 淇敼鍚庡悓鎰�
       };
       return conclusionMap[conclusion] || "info";
     },
 
     getConclusionText(conclusion) {
       const conclusionMap = {
+        "0": "涓嶅悓鎰�",
         "1": "鍚屾剰",
         "2": "瀹℃煡涓�",
-        "0": "涓嶅悓鎰�"
+        "3": "寮冩潈",
+        "4": "淇敼鍚庡悓鎰�"
       };
       return conclusionMap[conclusion] || "鏈煡";
     },
diff --git a/src/views/business/course/index.vue b/src/views/business/course/index.vue
index b0aa20f..6064df5 100644
--- a/src/views/business/course/index.vue
+++ b/src/views/business/course/index.vue
@@ -374,9 +374,8 @@
   flex-direction: column;
   gap: 20px;
   min-height: 0;
+  min-width: 0;
   /* 閲嶈锛氬厑璁竑lex瀛愰」鍘嬬缉 */
-  max-width: 1600px;
-  /* 瓒呭灞忎笂闄愶細閬垮厤鎻忚堪鍒楄〃姣忓垪杩囧銆佸唴瀹圭█鐤忥紝鍙充晶鐣欏嚭閫傞噺绌虹櫧 */
 }
 
 .basic-info-section {
@@ -433,6 +432,8 @@
   flex-direction: column;
   min-height: 400px;
   /* 鏈�灏忛珮搴� */
+  min-width: 0;
+  /* 閲嶈锛氬厑璁� flex 瀛愰」鏀剁缉 */
   /* max-height: 1000px; */
   /* 鏈�澶ч珮搴︼紝鍙牴鎹渶瑕佽皟鏁� */
   overflow: hidden;
@@ -442,11 +443,21 @@
 .stage-content-wrapper {
   flex: 1;
   overflow-y: auto;
+  overflow-x: auto;
   /* 闃舵璇︽儏鍐呴儴鍙粴鍔� */
   margin-top: 20px;
   padding-right: 8px;
   min-height: 0;
-  /* 閲嶈 */
+  min-width: 0;
+  /* 閲嶈锛氬厑璁� flex 瀛愰」鏀剁缉锛屽惁鍒欏浐瀹氬垪瀹界殑琛ㄦ牸浼氭妸鍐呭鎾戝骞舵孩鍑� */
+}
+
+/* 绾︽潫鍚� Stage 瀛愮粍浠舵牴鑺傜偣瀹藉害锛岄厤鍚堜笂鏂� min-width:0锛�
+   璁� el-table 鍦ㄨ嚜韬唴閮ㄥ嚭鐜版í鍚戞粴鍔ㄦ潯锛岃�屼笉鏄澶栧眰 overflow:hidden 瑁佹帀 */
+.stage-content-wrapper :deep(> div) {
+  width: 100%;
+  min-width: 0;
+  box-sizing: border-box;
 }
 
 .stage-content-wrapper::-webkit-scrollbar {
diff --git a/src/views/business/decide/DecideInfo.vue b/src/views/business/decide/DecideInfo.vue
index 15d8996..0590feb 100644
--- a/src/views/business/decide/DecideInfo.vue
+++ b/src/views/business/decide/DecideInfo.vue
@@ -1387,8 +1387,6 @@
 .paper-form {
   border-left: 1px solid #d0d3db;
   background: #fff;
-  /* 瓒呭灞忛檺鍒惰〃鍗曟渶澶у搴︼紝鍙充晶鐣欑櫧锛岄伩鍏嶅瓧娈靛垎甯冭繃寮� */
-  max-width: 1400px;
 }
 
 .paper-form >>> .form-row {
diff --git a/src/views/business/ethicalReview/ethicalReviewInfo.vue b/src/views/business/ethicalReview/ethicalReviewInfo.vue
index 2e175aa..6321ef7 100644
--- a/src/views/business/ethicalReview/ethicalReviewInfo.vue
+++ b/src/views/business/ethicalReview/ethicalReviewInfo.vue
@@ -41,6 +41,8 @@
               <el-option label="鍚屾剰" value="1" />
               <el-option label="瀹℃煡涓�" value="2" />
               <el-option label="涓嶅悓鎰�" value="0" />
+              <el-option label="寮冩潈" value="3" />
+              <el-option label="淇敼鍚庡悓鎰�" value="4" />
             </el-select>
           </el-form-item>
           <el-form-item label="浼︾悊瀹℃煡缁撹鏃堕棿" prop="expertTime" class="col-1-3">
@@ -725,7 +727,7 @@
           <el-row :gutter="20" style="margin-top: 20px;">
             <el-col :span="12">
               <div class="history-stat-item">
-                <div class="history-stat-label">鏈帴鏀舵暟閲�</div>
+                <div class="history-stat-label">宸插彂閫佹湭鎺ユ敹鏁伴噺</div>
                 <div class="history-stat-value">
                   {{ expertHistoryData.notAcceptCount || 0 }}
                 </div>
@@ -1333,7 +1335,9 @@
       console.log(file, "file");
 
       this.currentPreviewFile = {
-        fileName: file,
+        // 绛惧悕闄勪欢 URL 閫氬父鏃犳墿灞曞悕锛堝 .../file-1787645145583锛夛紝
+        // 棰勮缁勪欢渚濊禆鏂囦欢鍚嶆墿灞曞悕璇嗗埆鏂囦欢绫诲瀷锛岃繖閲岃ˉ .png 璁╁叾鎸夊浘鐗囬瑙�
+        fileName: "鎵嬪啓绛惧悕.png",
         fileUrl: file,
         fileType: "png"
       };
@@ -1423,8 +1427,8 @@
     // 瀹℃煡鐘舵�佽繃婊ゅ櫒
     getReviewStatusFilter(status) {
       const statusMap = {
-        "0": "info", // 寰呮帴鏀�
-        "1": "warning", // 鏈帴鏀�
+        "0": "info", // 寰呭彂閫�
+        "1": "warning", // 宸插彂閫�
         "2": "success", // 宸叉帴鏀�
         "3": "danger", // 瓒呮椂
         "4": "danger", // 涓
@@ -1435,8 +1439,8 @@
 
     getReviewStatusText(status) {
       const statusMap = {
-        "0": "寰呮帴鏀�",
-        "1": "鏈帴鏀�",
+        "0": "寰呭彂閫�",
+        "1": "宸插彂閫�",
         "2": "宸叉帴鏀�",
         "3": "瓒呮椂",
         "4": "涓",
@@ -1448,18 +1452,22 @@
     // 缁撹杩囨护鍣�
     getConclusionFilter(conclusion) {
       const conclusionMap = {
+        "0": "danger", // 涓嶅悓鎰�
         "1": "success", // 鍚屾剰
         "2": "warning", // 瀹℃煡涓�
-        "0": "danger" // 涓嶅悓鎰�
+        "3": "info", // 寮冩潈
+        "4": "success" // 淇敼鍚庡悓鎰�
       };
       return conclusionMap[conclusion] || "info";
     },
 
     getConclusionText(conclusion) {
       const conclusionMap = {
+        "0": "涓嶅悓鎰�",
         "1": "鍚屾剰",
         "2": "瀹℃煡涓�",
-        "0": "涓嶅悓鎰�"
+        "3": "寮冩潈",
+        "4": "淇敼鍚庡悓鎰�"
       };
       return conclusionMap[conclusion] || "鏈煡";
     },
@@ -2005,7 +2013,7 @@
           deptName: expert.unitname || "",
           title: expert.title || "",
           donorno: expert.telephone || "",
-          receiveStatus: "0", // 寰呮帴鏀�
+          receiveStatus: "0", // 寰呭彂閫�
           expertconclusion: "",
           expertopinion: "",
           conclusionannex: "",
@@ -2204,6 +2212,7 @@
 
               if (index != -1) {
                 this.form.ethicalreviewopinionsList[index].receiveStatus = "1"; // 宸叉帴鏀�
+                this.form.ethicalreviewopinionsList[index].expertconclusion = "2"; // 瀹℃煡涓�
                 this.form.ethicalreviewopinionsList[
                   index
                 ].startTime = this.sendForm.startTime;
@@ -2750,8 +2759,6 @@
 .paper-form {
   border-left: 1px solid #d0d3db;
   background: #fff;
-  /* 瓒呭灞忛檺鍒惰〃鍗曟渶澶у搴︼紝鍙充晶鐣欑櫧锛岄伩鍏嶅瓧娈靛垎甯冭繃寮� */
-  max-width: 1400px;
 }
 
 .paper-form >>> .form-row {
diff --git a/src/views/business/ethicalReview/index.vue b/src/views/business/ethicalReview/index.vue
index 3033248..0428f61 100644
--- a/src/views/business/ethicalReview/index.vue
+++ b/src/views/business/ethicalReview/index.vue
@@ -86,12 +86,6 @@
             >淇敼</el-button
           > -->
           <el-button
-            type="warning"
-            icon="el-icon-download"
-            @click="handleExport"
-            >瀵煎嚭</el-button
-          >
-          <el-button
             type="info"
             icon="el-icon-check"
             :disabled="single"
@@ -121,12 +115,7 @@
           prop="inpatientno"
           width="120"
         />
-        <el-table-column
-          label="濮撳悕"
-          align="center"
-          prop="name"
-          width="100"
-        />
+        <el-table-column label="濮撳悕" align="center" prop="name" width="100" />
 
         <el-table-column label="鎬у埆" align="center" prop="sex" width="80">
           <template slot-scope="scope">
@@ -195,7 +184,7 @@
         <el-table-column
           label="鎿嶄綔"
           align="center"
-          width="180"
+          width="240"
           fixed="right"
           class-name="small-padding fixed-width"
         >
@@ -222,6 +211,13 @@
               :disabled="scope.row.status === '2'"
               >缁撴潫</el-button
             >
+            <el-button
+              size="mini"
+              type="text"
+              icon="el-icon-download"
+              @click="handleDownload(scope.row)"
+              >涓嬭浇</el-button
+            >
           </template>
         </el-table-column>
       </el-table>
@@ -239,13 +235,23 @@
 </template>
 
 <script>
-import { reviewinitiateBaseInfoList, ethicalreviewedit,  } from "@/api/businessApi";
+import {
+  reviewinitiateBaseInfoList,
+  ethicalreviewedit,
+  ethicalReviewDownload
+} from "@/api/businessApi";
 import Pagination from "@/components/Pagination";
+import { getToken } from "@/utils/auth";
 
 export default {
   name: "EthicsReviewList",
   components: { Pagination },
-  dicts: ["sys_user_sex", "ReviewForm_status",'sys_ethical','expert_Conclusion'],
+  dicts: [
+    "sys_user_sex",
+    "ReviewForm_status",
+    "sys_ethical",
+    "expert_Conclusion"
+  ],
   data() {
     return {
       // 閬僵灞�
@@ -291,9 +297,14 @@
       delete params.reviewTimeRange;
 
       // 濡傛灉鏈夋椂闂磋寖鍥村弬鏁�
-      if (this.queryParams.reviewTimeRange && this.queryParams.reviewTimeRange.length === 2) {
-        params.expertTimeStart = this.queryParams.reviewTimeRange[0] + ' 00:00:00';
-        params.expertTimeEnd = this.queryParams.reviewTimeRange[1] + ' 23:59:59';
+      if (
+        this.queryParams.reviewTimeRange &&
+        this.queryParams.reviewTimeRange.length === 2
+      ) {
+        params.expertTimeStart =
+          this.queryParams.reviewTimeRange[0] + " 00:00:00";
+        params.expertTimeEnd =
+          this.queryParams.reviewTimeRange[1] + " 23:59:59";
       }
 
       reviewinitiateBaseInfoList(params)
@@ -333,7 +344,7 @@
     handleView(row) {
       this.$router.push({
         path: "/case/ethicalReviewInfo",
-        query: { infoid: row.infoid,id:row.id }
+        query: { infoid: row.infoid, id: row.id }
       });
     },
     // 鏂板鎸夐挳鎿嶄綔
@@ -345,12 +356,13 @@
       const infoid = row.infoid;
       this.$router.push({
         path: "/case/ethicalReviewInfo",
-        query: { infoid: infoid,id:row.id }
+        query: { infoid: infoid, id: row.id }
       });
     },
     // 缁撴潫瀹℃煡鎿嶄綔
     handleEndReview(row) {
-      const selectedRow = row || (this.selectedRows.length === 1 ? this.selectedRows[0] : null);
+      const selectedRow =
+        row || (this.selectedRows.length === 1 ? this.selectedRows[0] : null);
       if (!selectedRow) {
         this.$message.warning("璇烽�夋嫨涓�鏉¤褰�");
         return;
@@ -365,7 +377,7 @@
           // 璋冪敤淇敼鎺ュ彛锛屽皢鐘舵�佹敼涓虹粨鏉燂紙status: '2'锛�
           const updateData = {
             infoid: selectedRow.infoid,
-            status: '2',
+            status: "2",
             // 鍙互淇濈暀鍘熸湁鏁版嵁
             expertName: selectedRow.expertName,
             expertConclusion: selectedRow.expertConclusion,
@@ -385,50 +397,41 @@
         })
         .catch(() => {});
     },
-    // 瀵煎嚭鎸夐挳鎿嶄綔
-    handleExport() {
-      const queryParams = { ...this.queryParams };
-      delete queryParams.pageNum;
-      delete queryParams.pageSize;
-
-      // 澶勭悊鏃堕棿鑼冨洿鍙傛暟
-      if (queryParams.reviewTimeRange && queryParams.reviewTimeRange.length === 2) {
-        queryParams.expertTimeStart = queryParams.reviewTimeRange[0] + ' 00:00:00';
-        queryParams.expertTimeEnd = queryParams.reviewTimeRange[1] + ' 23:59:59';
-      }
-      delete queryParams.reviewTimeRange;
-
-      this.$confirm("鏄惁纭瀵煎嚭鎵�鏈変鸡鐞嗗鏌ユ暟鎹紵", "璀﹀憡", {
-        confirmButtonText: "纭畾",
-        cancelButtonText: "鍙栨秷",
-        type: "warning"
-      })
-        .then(() => {
-          this.loading = true;
-          // return ethicalreviewexport(queryParams);
-        })
-        .then(response => {
-          if (response.code === 200) {
-            this.$message.success("瀵煎嚭鎴愬姛");
-            // 澶勭悊瀵煎嚭鏂囦欢涓嬭浇
-            if (response.data) {
-              const blob = new Blob([response.data], { type: 'application/vnd.ms-excel' });
-              const downloadElement = document.createElement('a');
-              const href = window.URL.createObjectURL(blob);
-              downloadElement.href = href;
-              downloadElement.download = '浼︾悊瀹℃煡鍒楄〃.xlsx';
-              document.body.appendChild(downloadElement);
-              downloadElement.click();
-              document.body.removeChild(downloadElement);
-              window.URL.revokeObjectURL(href);
-            }
-          } else {
-            this.$message.error(response.msg || "瀵煎嚭澶辫触");
+    // 涓嬭浇浼︾悊瀹℃煡妗f
+    handleDownload(row) {
+      ethicalReviewDownload({ nitiateId: row.id })
+        .then(res => {
+          if (res.code !== 200 || !res.data || !res.data.downloadUrl) {
+            this.$message.error(res.msg || "涓嬭浇澶辫触");
+            return;
           }
-          this.loading = false;
+          const fileUrl = res.data;
+          // 鎷兼帴涓嬭浇鍦板潃锛歰rigin + /dev-api(寮�鍙戜唬鐞嗗墠缂�) + /profile + downloadUrl锛�
+          // 鍚庣闈欐�佹枃浠跺疄闄呮寕鍦� /profile 涓嬶紝downloadUrl 灏戜簡杩欏眰鍓嶇紑
+          const urlBase = process.env.VUE_APP_BASE_API;
+          const curWWWPath = window.document.location.href;
+          const pos = curWWWPath.indexOf(window.document.location.pathname);
+          const fullUrl =
+            curWWWPath.substring(0, pos) + urlBase + "/profile" + fileUrl.downloadUrl;
+
+          fetch(fullUrl, {
+            headers: { Authorization: "Bearer " + getToken() }
+          })
+            .then(response => response.blob())
+            .then(blob => {
+              const url = window.URL.createObjectURL(new Blob([blob]));
+              const link = document.createElement("a");
+              link.href = url;
+              link.setAttribute("download", fileUrl.downloadName);
+              document.body.appendChild(link);
+              link.click();
+              link.parentNode.removeChild(link);
+              window.URL.revokeObjectURL(url);
+            });
         })
-        .catch(() => {
-          this.loading = false;
+        .catch(error => {
+          console.error("涓嬭浇妗f澶辫触:", error);
+          this.$message.error("涓嬭浇澶辫触");
         });
     },
     // 鏃堕棿鏍煎紡鍖�
diff --git a/src/views/business/followupVisit/info.vue b/src/views/business/followupVisit/info.vue
index 62142e3..5303dd5 100644
--- a/src/views/business/followupVisit/info.vue
+++ b/src/views/business/followupVisit/info.vue
@@ -740,8 +740,6 @@
 .paper-form {
   border-left: 1px solid #d0d3db;
   background: #fff;
-  /* 瓒呭灞忛檺鍒惰〃鍗曟渶澶у搴︼紝鍙充晶鐣欑櫧锛岄伩鍏嶅瓧娈靛垎甯冭繃寮� */
-  max-width: 1400px;
 }
 
 .paper-form >>> .form-row {
diff --git a/src/views/business/maintain/maintainInfo.vue b/src/views/business/maintain/maintainInfo.vue
index 8dbb0ef..155204f 100644
--- a/src/views/business/maintain/maintainInfo.vue
+++ b/src/views/business/maintain/maintainInfo.vue
@@ -1375,8 +1375,6 @@
 .paper-form {
   border-left: 1px solid #d0d3db;
   background: #fff;
-  /* 瓒呭灞忛檺鍒惰〃鍗曟渶澶у搴︼紝鍙充晶鐣欑櫧锛岄伩鍏嶅瓧娈靛垎甯冭繃寮� */
-  max-width: 1400px;
 }
 
 .paper-form >>> .form-row {
diff --git a/src/views/business/transfer/TransportEdit.vue b/src/views/business/transfer/TransportEdit.vue
index 3dc9bac..3d5c020 100644
--- a/src/views/business/transfer/TransportEdit.vue
+++ b/src/views/business/transfer/TransportEdit.vue
@@ -839,8 +839,6 @@
 .paper-form {
   border-left: 1px solid #d0d3db;
   background: #fff;
-  /* 瓒呭灞忛檺鍒惰〃鍗曟渶澶у搴︼紝鍙充晶鐣欑櫧锛岄伩鍏嶅瓧娈靛垎甯冭繃寮� */
-  max-width: 1400px;
 }
 
 .paper-form >>> .form-row {
diff --git a/src/views/statistics/case.vue b/src/views/statistics/case.vue
index 0b7f00b..8bc4386 100644
--- a/src/views/statistics/case.vue
+++ b/src/views/statistics/case.vue
@@ -35,16 +35,25 @@
         </el-form-item>
 
         <el-form-item>
-          <el-button type="primary" icon="el-icon-search" @click="handleSearch">鏌ヨ</el-button>
-          <el-button icon="el-icon-refresh" @click="handleReset">閲嶇疆</el-button>
-          <el-button type="success" icon="el-icon-download" @click="handleExport">瀵煎嚭</el-button>
+          <el-button type="primary" icon="el-icon-search" @click="handleSearch"
+            >鏌ヨ</el-button
+          >
+          <el-button icon="el-icon-refresh" @click="handleReset"
+            >閲嶇疆</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" class="overview-row">
-      <el-col :span="6" v-for="item in overviewData" :key="item.title">
+      <el-col :span="8" v-for="item in overviewData" :key="item.title">
         <el-card class="overview-card">
           <div class="overview-content">
             <div class="overview-icon" :style="{ backgroundColor: item.color }">
@@ -71,7 +80,7 @@
               type="text"
               @click="toggleChartType('trend')"
             >
-              {{ chartTypes.trend === 'line' ? '鏌辩姸鍥�' : '鎶樼嚎鍥�' }}
+              {{ chartTypes.trend === "line" ? "鏌辩姸鍥�" : "鎶樼嚎鍥�" }}
             </el-button>
           </div>
           <EChartsWrapper
@@ -117,15 +126,23 @@
         border
         stripe
       >
-        <el-table-column label="鍦板尯" prop="region" align="center"  />
-        <el-table-column label="鏈堜唤" prop="month" align="center" >
+        <el-table-column label="鍦板尯" prop="region" align="center" />
+        <el-table-column label="鏈堜唤" prop="month" align="center">
           <template slot-scope="{ row }">
             {{ formatMonth(row.month) }}
           </template>
         </el-table-column>
-        <el-table-column label="娼滃湪鎹愮尞妗堜緥鏁�" prop="potentialCount" align="center"  />
-        <el-table-column label="瀹屾垚鎹愮尞鏁伴噺" prop="completedCount" align="center"  />
-        <el-table-column label="瀹屾垚鐜�" prop="completionRate" align="center" >
+        <el-table-column
+          label="娼滃湪鎹愮尞妗堜緥鏁�"
+          prop="potentialCount"
+          align="center"
+        />
+        <el-table-column
+          label="瀹屾垚鎹愮尞鏁伴噺"
+          prop="completedCount"
+          align="center"
+        />
+        <el-table-column label="瀹屾垚鐜�" prop="completionRate" align="center">
           <template slot-scope="{ row }">
             <el-progress
               :percentage="row.completionRate"
@@ -135,13 +152,9 @@
             <span>{{ row.completionRate }}%</span>
           </template>
         </el-table-column>
-        <el-table-column label="妗堜緥鏄庣粏" align="center" >
+        <el-table-column label="妗堜緥鏄庣粏" align="center">
           <template slot-scope="{ row }">
-            <el-button
-              type="text"
-              size="mini"
-              @click="viewCaseDetail(row)"
-            >
+            <el-button type="text" size="mini" @click="viewCaseDetail(row)">
               鏌ョ湅({{ row.caseCount || 0 }})
             </el-button>
           </template>
@@ -161,26 +174,21 @@
     </el-card>
 
     <!-- 妗堜緥鏄庣粏寮圭獥 -->
-    <el-dialog
-      title="妗堜緥鏄庣粏"
-      :visible.sync="showDetail"
-
-      append-to-body
-    >
-      <case-detail
-        :query="detailQuery"
-        @close="showDetail = false"
-      />
+    <el-dialog title="妗堜緥鏄庣粏" :visible.sync="showDetail" append-to-body>
+      <case-detail :query="detailQuery" @close="showDetail = false" />
     </el-dialog>
   </div>
 </template>
 
 <script>
-import EChartsWrapper from '@/components/charts/EChartsWrapper.vue';
-import CaseDetail from './components/CaseDetail.vue';
-
+import EChartsWrapper from "@/components/charts/EChartsWrapper.vue";
+import CaseDetail from "./components/CaseDetail.vue";
+import { caseTrendArea, caseTrend, caseTrendMoon, totals,trendList } from "@/api/businessApi";
+import { toTotalsList, toMonth, toNum } from "@/utils/totals";
+import statisticsMode from "@/utils/statisticsMode";
+import { COLORS, axisTooltip, grid, xAxis, yAxis, legend, barSeries, lineSeries } from "@/utils/chartStyle";
 export default {
-  name: 'CaseStatistics',
+  name: "CaseStatistics",
   components: {
     EChartsWrapper,
     CaseDetail
@@ -195,15 +203,15 @@
         limit: 10
       },
 
-      // 鍦板尯閫夐」
+      // 鍦板尯閫夐」锛坴alue 鐩存帴浼犲湴鍖哄悕绉扮粰鎺ュ彛 area 瀛楁锛�
       regionOptions: [
-        { label: '闈掑矝鍦板尯', value: 'qingdao' },
-        { label: '鏃ョ収鍦板尯', value: 'rizhao' },
-        { label: '娴庡崡鍦板尯', value: 'jinan' },
-        { label: '鐑熷彴鍦板尯', value: 'yantai' },
-        { label: '濞佹捣鍦板尯', value: 'weihai' },
-        { label: '娼嶅潑鍦板尯', value: 'weifang' },
-        { label: '涓存矀鍦板尯', value: 'linyi' }
+        { label: "闈掑矝鍦板尯", value: "闈掑矝鍦板尯" },
+        { label: "鏃ョ収鍦板尯", value: "鏃ョ収鍦板尯" },
+        { label: "娴庡崡鍦板尯", value: "娴庡崡鍦板尯" },
+        { label: "鐑熷彴鍦板尯", value: "鐑熷彴鍦板尯" },
+        { label: "濞佹捣鍦板尯", value: "濞佹捣鍦板尯" },
+        { label: "娼嶅潑鍦板尯", value: "娼嶅潑鍦板尯" },
+        { label: "涓存矀鍦板尯", value: "涓存矀鍦板尯" }
       ],
 
       // 鍔犺浇鐘舵��
@@ -215,15 +223,32 @@
 
       // 姒傝鏁版嵁
       overviewData: [
-        { title: '鎬绘綔鍦ㄦ崘鐚渚�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-flag', color: '#409EFF' },
-        { title: '鎬诲畬鎴愭崘鐚暟閲�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-circle-check', color: '#67C23A' },
-        { title: '骞冲潎瀹屾垚鐜�', value: '0%', desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-data', color: '#E6A23C' },
-        { title: '娑夊強妗堜緥鏁�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-document', color: '#F56C6C' }
+        {
+          title: "鎬绘綔鍦ㄦ崘鐚渚�",
+          value: 0,
+          desc: "缁熻鍛ㄦ湡鍐�",
+          icon: "el-icon-s-flag",
+          color: "#5B8FF9"
+        },
+        {
+          title: "鎬诲畬鎴愭崘鐚暟閲�",
+          value: 0,
+          desc: "缁熻鍛ㄦ湡鍐�",
+          icon: "el-icon-circle-check",
+          color: "#5AD8A6"
+        },
+        {
+          title: "骞冲潎瀹屾垚鐜�",
+          value: "0%",
+          desc: "缁熻鍛ㄦ湡鍐�",
+          icon: "el-icon-s-data",
+          color: "#F6BD16"
+        }
       ],
 
       // 鍥捐〃绫诲瀷
       chartTypes: {
-        trend: 'line'
+        trend: "line"
       },
 
       // 鍥捐〃鏁版嵁
@@ -234,200 +259,157 @@
 
       // 寮圭獥鎺у埗
       showDetail: false,
-      detailQuery: {},
-
-      // 妯℃嫙鏁版嵁
-      mockData: {
-        table: [
-          { id: 1, region: '闈掑矝鍦板尯', month: '2024-01', potentialCount: 15, completedCount: 8, completionRate: 53.3, caseCount: 5 },
-          { id: 2, region: '闈掑矝鍦板尯', month: '2024-02', potentialCount: 18, completedCount: 10, completionRate: 55.6, caseCount: 6 },
-          { id: 3, region: '鏃ョ収鍦板尯', month: '2024-01', potentialCount: 8, completedCount: 4, completionRate: 50.0, caseCount: 3 },
-          { id: 4, region: '鏃ョ収鍦板尯', month: '2024-02', potentialCount: 10, completedCount: 6, completionRate: 60.0, caseCount: 4 },
-          { id: 5, region: '娴庡崡鍦板尯', month: '2024-01', potentialCount: 20, completedCount: 12, completionRate: 60.0, caseCount: 7 },
-          { id: 6, region: '娴庡崡鍦板尯', month: '2024-02', potentialCount: 22, completedCount: 14, completionRate: 63.6, caseCount: 8 }
-        ],
-        trend: [
-          { month: '2024-01', potential: 55, completed: 31 },
-          { month: '2024-02', potential: 64, completed: 39 },
-          { month: '2024-03', potential: 58, completed: 35 },
-          { month: '2024-04', potential: 62, completed: 40 },
-          { month: '2024-05', potential: 68, completed: 45 },
-          { month: '2024-06', potential: 72, completed: 50 }
-        ],
-        region: [
-          { region: '闈掑矝鍦板尯', potential: 150, completed: 85 },
-          { region: '鏃ョ収鍦板尯', potential: 80, completed: 45 },
-          { region: '娴庡崡鍦板尯', potential: 200, completed: 120 },
-          { region: '鐑熷彴鍦板尯', potential: 100, completed: 60 }
-        ]
-      }
+      detailQuery: {}
     };
   },
   created() {
     this.loadData();
+    // 鏁版嵁婧�(mock/鐪熷疄鎺ュ彛)鍒囨崲鏃堕噸鏂板姞杞�
+    this.$watch(() => statisticsMode.useMock, () => this.loadData());
   },
   methods: {
+    // 缁勮鎺ュ彛鏌ヨ鏉′欢
+    buildQuery() {
+      const q = {};
+      if (this.query.region && this.query.region.length) {
+        q.area = this.query.region.join(",");
+      }
+      if (this.query.monthRange && this.query.monthRange.length === 2) {
+        q.startTime = this.query.monthRange[0];
+        q.endTime = this.query.monthRange[1];
+      }
+      return q;
+    },
+
     // 鍔犺浇鏁版嵁
     async loadData() {
       this.loading = true;
       try {
-        // 妯℃嫙鎺ュ彛璋冪敤
-        await new Promise(resolve => setTimeout(resolve, 500));
+        const q = this.buildQuery();
+        const [areaRes, trendRes, moonRes, totalsRes] = await Promise.all([
+          caseTrendArea(q),
+          caseTrend(q),
+          caseTrendMoon(q),
+          totals(q)
+        ]);
 
-        // 澶勭悊琛ㄦ牸鏁版嵁
-        let filteredData = [...this.mockData.table];
+        // 鍦板尯鍒嗗竷瀵规瘮
+        this.chartData.region = toTotalsList(areaRes).map(item => ({
+          region: item.area || item.region || "",
+          potential: toNum(item.potentialCount),
+          completed: toNum(item.completeCount)
+        }));
 
-        if (this.query.region.length > 0) {
-          filteredData = filteredData.filter(item =>
-            this.query.region.includes(item.region.replace('鍦板尯', '').toLowerCase())
-          );
-        }
+        // 妗堜緥瓒嬪娍
+        this.chartData.trend = toTotalsList(trendRes).map(item => ({
+          month: toMonth(item.donateTime) || toMonth(item.month),
+          potential: toNum(item.potentialCount),
+          completed: toNum(item.completeCount)
+        }));
 
-        if (this.query.monthRange && this.query.monthRange.length === 2) {
-          const [start, end] = this.query.monthRange;
-          filteredData = filteredData.filter(item =>
-            item.month >= start && item.month <= end
-          );
-        }
+        // 鏄庣粏琛紙鍦板尯 x 鏈堜唤锛�
+        const all = toTotalsList(moonRes).map(item => {
+          const potential = toNum(item.potentialCount);
+          const completed = toNum(item.completeCount);
+          const rate =
+            potential > 0
+              ? Number(((completed / potential) * 100).toFixed(1))
+              : 0;
+          return {
+            region: item.area || item.region || "",
+            month: toMonth(item.donateTime) || toMonth(item.month),
+            potentialCount: potential,
+            completedCount: completed,
+            completionRate: rate,
+            caseCount: toNum(item.totalCount) || potential
+          };
+        });
+        this.total = all.length;
+        const start = (this.query.page - 1) * this.query.limit;
+        this.tableData = all.slice(start, start + this.query.limit);
 
-        this.tableData = filteredData;
-        this.total = filteredData.length;
-
-        // 鏇存柊姒傝鏁版嵁
-        this.updateOverviewData(filteredData);
-
-        // 鏇存柊鍥捐〃鏁版嵁
-        this.chartData.trend = [...this.mockData.trend];
-        this.chartData.region = [...this.mockData.region];
-
+        this.updateOverviewData(toTotalsList(totalsRes));
       } catch (error) {
-        console.error('鍔犺浇鏁版嵁澶辫触:', error);
-        this.$message.error('鏁版嵁鍔犺浇澶辫触');
+        console.error("鍔犺浇鏁版嵁澶辫触:", error);
+        this.$message.error("鏁版嵁鍔犺浇澶辫触");
       } finally {
         this.loading = false;
       }
     },
 
-    // 鏇存柊姒傝鏁版嵁
-    updateOverviewData(data) {
-      if (data.length === 0) return;
-
-      const totalPotential = data.reduce((sum, item) => sum + item.potentialCount, 0);
-      const totalCompleted = data.reduce((sum, item) => sum + item.completedCount, 0);
-      const avgRate = data.length > 0
-        ? (data.reduce((sum, item) => sum + item.completionRate, 0) / data.length).toFixed(1)
-        : 0;
-      const totalCases = data.reduce((sum, item) => sum + (item.caseCount || 0), 0);
+    // 鏇存柊姒傝鏁版嵁锛堟�绘綔鍦�/鎬诲畬鎴愭崘鐚暟鏉ヨ嚜 /opo/totals/totals 鎺ュ彛锛�
+    updateOverviewData(totalsList) {
+      const totalPotential = totalsList.reduce(
+        (sum, item) => sum + toNum(item.totalPotential),
+        0
+      );
+      const totalCompleted = totalsList.reduce(
+        (sum, item) => sum + toNum(item.totalCompletion),
+        0
+      );
+      const avgRate =
+        totalPotential > 0
+          ? ((totalCompleted / totalPotential) * 100).toFixed(1)
+          : 0;
 
       this.overviewData[0].value = totalPotential;
       this.overviewData[1].value = totalCompleted;
       this.overviewData[2].value = `${avgRate}%`;
-      this.overviewData[3].value = totalCases;
     },
 
     // 鑾峰彇瓒嬪娍鍥捐〃閰嶇疆
     getTrendChartOptions() {
-      const isLine = this.chartTypes.trend === 'line';
+      const isLine = this.chartTypes.trend === "line";
+      const xData = this.chartData.trend.map(item => item.month);
+      const potential = this.chartData.trend.map(item => item.potential);
+      const completed = this.chartData.trend.map(item => item.completed);
+
+      const series = isLine
+        ? [
+            lineSeries("娼滃湪鎹愮尞鏁�", potential, COLORS.blue, { area: true }),
+            lineSeries("瀹屾垚鎹愮尞鏁�", completed, COLORS.green, { area: true })
+          ]
+        : [
+            barSeries("娼滃湪鎹愮尞鏁�", potential, COLORS.blue),
+            barSeries("瀹屾垚鎹愮尞鏁�", completed, COLORS.green)
+          ];
 
       return {
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: {
-            type: 'shadow'
-          }
-        },
-        legend: {
-          data: ['娼滃湪鎹愮尞鏁�', '瀹屾垚鎹愮尞鏁�']
-        },
-        grid: {
-          left: '3%',
-          right: '4%',
-          bottom: '3%',
-          containLabel: true
-        },
-        xAxis: {
-          type: 'category',
-          data: this.chartData.trend.map(item => this.formatMonth(item.month))
-        },
-        yAxis: {
-          type: 'value',
-          name: '鏁伴噺(涓�)'
-        },
-        series: [
-          {
-            name: '娼滃湪鎹愮尞鏁�',
-            type: isLine ? 'line' : 'bar',
-            data: this.chartData.trend.map(item => item.potential),
-            itemStyle: { color: '#409EFF' },
-            smooth: isLine,
-            lineStyle: isLine ? { width: 3 } : {}
-          },
-          {
-            name: '瀹屾垚鎹愮尞鏁�',
-            type: isLine ? 'line' : 'bar',
-            data: this.chartData.trend.map(item => item.completed),
-            itemStyle: { color: '#67C23A' },
-            smooth: isLine,
-            lineStyle: isLine ? { width: 3 } : {}
-          }
-        ]
+        tooltip: axisTooltip(),
+        legend: legend(["娼滃湪鎹愮尞鏁�", "瀹屾垚鎹愮尞鏁�"]),
+        grid: grid(),
+        xAxis: xAxis(xData),
+        yAxis: yAxis(),
+        series
       };
     },
 
     // 鑾峰彇鍦板尯鍥捐〃閰嶇疆
     getRegionChartOptions() {
       return {
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: {
-            type: 'shadow'
-          }
-        },
-        legend: {
-          data: ['娼滃湪鎹愮尞鏁�', '瀹屾垚鎹愮尞鏁�']
-        },
-        grid: {
-          left: '3%',
-          right: '4%',
-          bottom: '3%',
-          containLabel: true
-        },
-        xAxis: {
-          type: 'category',
-          data: this.chartData.region.map(item => item.region)
-        },
-        yAxis: {
-          type: 'value',
-          name: '鏁伴噺(涓�)'
-        },
+        tooltip: axisTooltip(),
+        legend: legend(["娼滃湪鎹愮尞鏁�", "瀹屾垚鎹愮尞鏁�"]),
+        grid: grid(),
+        xAxis: xAxis(this.chartData.region.map(item => item.region)),
+        yAxis: yAxis(),
         series: [
-          {
-            name: '娼滃湪鎹愮尞鏁�',
-            type: 'bar',
-            data: this.chartData.region.map(item => item.potential),
-            itemStyle: { color: '#409EFF' }
-          },
-          {
-            name: '瀹屾垚鎹愮尞鏁�',
-            type: 'bar',
-            data: this.chartData.region.map(item => item.completed),
-            itemStyle: { color: '#67C23A' }
-          }
+          barSeries("娼滃湪鎹愮尞鏁�", this.chartData.region.map(item => item.potential), COLORS.blue),
+          barSeries("瀹屾垚鎹愮尞鏁�", this.chartData.region.map(item => item.completed), COLORS.green)
         ]
       };
     },
 
     // 鑾峰彇杩涘害鏉¢鑹�
     getProgressColor(percentage) {
-      if (percentage >= 60) return '#67C23A';
-      if (percentage >= 50) return '#E6A23C';
-      return '#F56C6C';
+      if (percentage >= 60) return COLORS.green;
+      if (percentage >= 50) return COLORS.amber;
+      return COLORS.coral;
     },
 
     // 鏍煎紡鍖栨湀浠芥樉绀�
     formatMonth(month) {
-      if (!month) return '';
-      return month.replace('-', '骞�') + '鏈�';
+      if (!month) return "";
+      return month.replace("-", "骞�") + "鏈�";
     },
 
     // 澶勭悊鏌ヨ
@@ -456,13 +438,12 @@
 
     // 澶勭悊瀵煎嚭
     handleExport() {
-      this.$message.info('瀵煎嚭鍔熻兘寮�鍙戜腑...');
-      // 杩欓噷鍙互璋冪敤瀵煎嚭鎺ュ彛
+      this.$message.info("瀵煎嚭鍔熻兘寮�鍙戜腑...");
     },
 
     // 鍒囨崲鍥捐〃绫诲瀷
     toggleChartType(type) {
-      this.chartTypes[type] = this.chartTypes[type] === 'line' ? 'bar' : 'line';
+      this.chartTypes[type] = this.chartTypes[type] === "line" ? "bar" : "line";
     },
 
     // 鏌ョ湅妗堜緥璇︽儏
@@ -537,7 +518,7 @@
 
 .overview-desc {
   font-size: 12px;
-  color: #C0C4CC;
+  color: #c0c4cc;
 }
 
 .chart-row {
diff --git a/src/views/statistics/components/CaseDetail.vue b/src/views/statistics/components/CaseDetail.vue
index 1d2d5d2..0d173bf 100644
--- a/src/views/statistics/components/CaseDetail.vue
+++ b/src/views/statistics/components/CaseDetail.vue
@@ -8,27 +8,25 @@
       border
       stripe
     >
-      <el-table-column label="妗堜緥缂栧彿" prop="caseNo" align="center"  />
-      <el-table-column label="鎹愮尞鑰呭鍚�" prop="donorName" align="center"  />
-      <el-table-column label="浣忛櫌鍙�" prop="inpatientNo" align="center"  />
-      <el-table-column label="妗堜緥鐘舵��" prop="status" align="center" >
+      <el-table-column label="妗堜緥缂栧彿" prop="caseNo" align="center" />
+      <el-table-column label="鎹愮尞鑰呭鍚�" prop="name" align="center" />
+      <el-table-column label="浣忛櫌鍙�" prop="inpatientNo" align="center" />
+      <el-table-column label="妗堜緥鐘舵��" prop="workflow" align="center">
         <template slot-scope="{ row }">
-          <el-tag :type="getStatusTag(row.status)" size="small">
-            {{ getStatusText(row.status) }}
+          <el-tag :type="getStatusTag(row.workflow)" size="small">
+            {{ getStatusText(row.workflow) }}
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="鎹愮尞鍣ㄥ畼" prop="organs" align="center" >
+      <el-table-column label="鎹愮尞鍣ㄥ畼" prop="organName" align="center">
         <template slot-scope="{ row }">
-          <span v-if="row.organs && row.organs.length > 0">
-            {{ row.organs.join('銆�') }}
-          </span>
+          <span v-if="row.organName">{{ row.organName }}</span>
           <span v-else>-</span>
         </template>
       </el-table-column>
-      <el-table-column label="鎹愮尞鏃堕棿" prop="donationTime" align="center"  />
-      <el-table-column label="姝讳骸鍘熷洜" prop="deathCause" align="center"  />
-      <el-table-column label="鍖婚櫌" prop="hospital" align="center" width="200" />
+      <el-table-column label="妗堜緥鏃堕棿" prop="donatetime" align="center" />
+      <el-table-column label="姝讳骸鍘熷洜" prop="deathReason" align="center" />
+      <el-table-column label="鍖婚櫌" prop="treatmentHospitalName" align="center" width="200" />
     </el-table>
 
     <div style="text-align: center; margin-top: 20px;">
@@ -38,6 +36,9 @@
 </template>
 
 <script>
+import { trendList } from '@/api/businessApi';
+import { toTotalsList } from '@/utils/totals';
+
 export default {
   name: 'CaseDetail',
   props: {
@@ -49,21 +50,64 @@
   data() {
     return {
       loading: false,
-      caseList: [
-        { caseNo: 'C202401001', donorName: '寮犱笁', inpatientNo: 'ZY202401001', status: 'completed', organs: ['鑲濊剰', '鑲捐剰'], donationTime: '2024-01-15 10:30', deathCause: '鑴戝浼�', hospital: '闈掑矝澶у闄勫睘鍖婚櫌' },
-        { caseNo: 'C202401002', donorName: '鏉庡洓', inpatientNo: 'ZY202401002', status: 'completed', organs: ['蹇冭剰', '鐪艰鑶�'], donationTime: '2024-01-20 14:20', deathCause: '蹇冩悘楠ゅ仠', hospital: '闈掑矝甯傜珛鍖婚櫌' },
-        { caseNo: 'C202401003', donorName: '鐜嬩簲', inpatientNo: 'ZY202401003', status: 'potential', organs: [], donationTime: null, deathCause: '鑴戣绠℃剰澶�', hospital: '闈掑矝甯備腑蹇冨尰闄�' }
-      ]
+      caseList: []
     };
   },
+  created() {
+    this.loadList();
+  },
+  watch: {
+    query: {
+      handler() {
+        this.loadList();
+      },
+      deep: true
+    }
+  },
   methods: {
-    getStatusTag(status) {
-      const map = { completed: 'success', potential: 'warning', abandoned: 'danger' };
-      return map[status] || 'info';
+    // 鍔犺浇妗堜緥鏄庣粏锛�/opo/totals/trendList
+    async loadList() {
+      this.loading = true;
+      try {
+        const params = {};
+        if (this.query.region) params.area = this.query.region;
+        if (this.query.month) {
+          params.startTime = `${this.query.month}-01`;
+          params.endTime = this.endOfMonth(this.query.month);
+        }
+        const res = await trendList(params);
+        this.caseList = toTotalsList(res);
+      } catch (error) {
+        console.error('鍔犺浇妗堜緥鏄庣粏澶辫触:', error);
+        this.$message.error('妗堜緥鏄庣粏鍔犺浇澶辫触');
+      } finally {
+        this.loading = false;
+      }
     },
-    getStatusText(status) {
-      const map = { completed: '宸插畬鎴�', potential: '娼滃湪鎹愮尞', abandoned: '鏀惧純鎹愮尞' };
-      return map[status] || '鏈煡';
+    // 鏈堜唤(yyyy-MM) -> 褰撴湀鏈�鍚庝竴澶�(yyyy-MM-dd)
+    endOfMonth(month) {
+      const [y, m] = String(month).split('-').map(Number);
+      const lastDay = new Date(y, m, 0).getDate();
+      return `${month}-${String(lastDay).padStart(2, '0')}`;
+    },
+    // 宸ヤ綔娴佺姸鎬佹枃妗�
+    getStatusText(workflow) {
+      const map = {
+        1: '渚涜�呯淮鎶�',
+        2: '鎹愮尞鎰忔効',
+        3: '鍖诲璇勪及',
+        4: '姝讳骸鍒ゅ畾',
+        5: '浼︾悊瀹℃煡',
+        6: '鍣ㄥ畼鍒嗛厤',
+        7: '鍣ㄥ畼鍒╃敤',
+        99: '鎹愮尞瀹屾垚'
+      };
+      return map[workflow] || '鏈煡';
+    },
+    getStatusTag(workflow) {
+      if (workflow === 99) return 'success';
+      if (workflow) return 'warning';
+      return 'info';
     }
   }
 };
diff --git a/src/views/statistics/donor.vue b/src/views/statistics/donor.vue
index 192e13c..a2e8bb8 100644
--- a/src/views/statistics/donor.vue
+++ b/src/views/statistics/donor.vue
@@ -67,23 +67,27 @@
             <span>涓浗鍒嗙被鍒嗗竷</span>
           </div>
           <EChartsWrapper
-            :id="'category-chart'"
-            :options="categoryChartOptions"
+            v-if="useMock"
+            :id="'china-category-chart'"
+            :options="chinaCategoryOptions"
             height="400px"
           />
+          <el-empty v-else description="鍚庣鏆傛湭鎻愪緵涓浗鍒嗙被缁熻鎺ュ彛"></el-empty>
         </el-card>
       </el-col>
 
       <el-col :span="12">
         <el-card>
           <div slot="header" class="clearfix">
-            <span>姝讳骸鍘熷洜鍒嗘瀽</span>
+            <span>姝讳骸鍘熷洜鍒嗗竷</span>
           </div>
           <EChartsWrapper
-            :id="'death-chart'"
-            :options="deathChartOptions"
+            v-if="useMock"
+            :id="'death-cause-chart'"
+            :options="deathCauseOptions"
             height="400px"
           />
+          <el-empty v-else description="鍚庣鏆傛湭鎻愪緵姝讳骸鍘熷洜缁熻鎺ュ彛"></el-empty>
         </el-card>
       </el-col>
     </el-row>
@@ -94,71 +98,54 @@
         <span>鎹愮尞鑰呰缁嗕俊鎭�</span>
       </div>
 
-      <el-table
-        v-loading="loading"
-        :data="tableData"
-        style="width: 100%"
-        border
-        stripe
-      >
-        <el-table-column label="鎹愮尞鑰呯紪鍙�" prop="donorNo" align="center"  />
-        <el-table-column label="濮撳悕" prop="name" align="center"  />
-        <el-table-column label="鎬у埆" prop="gender" align="center" >
-          <template slot-scope="{ row }">
-            <el-tag :type="row.gender === '鐢�' ? 'primary' : 'danger'" size="small">
-              {{ row.gender }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="骞撮緞" prop="age" align="center"  />
-        <el-table-column label="琛�鍨�" prop="bloodType" align="center" >
-          <template slot-scope="{ row }">
-            <el-tag :type="getBloodTypeTag(row.bloodType)" size="small">
-              {{ row.bloodType }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="鍦板尯" prop="region" align="center"  />
-        <el-table-column label="鎹愮尞鏃堕棿" prop="donationTime" align="center"  />
-        <el-table-column label="涓浗鍒嗙被" prop="chinaCategory" align="center"  />
-        <el-table-column label="姝讳骸鍘熷洜" prop="deathCause" align="center"  />
-        <el-table-column label="鎹愮尞鍣ㄥ畼" prop="organs" align="center" >
-          <template slot-scope="{ row }">
-            <span>{{ row.organs.join('銆�') }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="鎿嶄綔" align="center" " fixed="right">
-          <template slot-scope="{ row }">
-            <el-button
-              type="text"
-              size="mini"
-              @click="viewDonorDetail(row)"
-            >
-              璇︽儏
-            </el-button>
-          </template>
-        </el-table-column>
-      </el-table>
+      <template v-if="useMock">
+        <el-table
+          v-loading="loading"
+          :data="tableData"
+          style="width: 100%"
+          border
+          stripe
+        >
+          <el-table-column label="鎹愮尞鑰呯紪鍙�" prop="donorNo" align="center" />
+          <el-table-column label="濮撳悕" prop="name" align="center" />
+          <el-table-column label="鎬у埆" prop="gender" align="center" />
+          <el-table-column label="骞撮緞" prop="age" align="center" />
+          <el-table-column label="琛�鍨�" prop="bloodType" align="center" />
+          <el-table-column label="鍦板尯" prop="region" align="center" />
+          <el-table-column label="鎹愮尞鏃堕棿" prop="donationTime" align="center" />
+        </el-table>
 
-      <el-pagination
-        v-show="total > 0"
-        :total="total"
-        :page.sync="query.page"
-        :limit.sync="query.limit"
-        @current-change="handlePagination"
-        @size-change="handlePageSizeChange"
-        style="margin-top: 20px;"
-        layout="total, sizes, prev, pager, next, jumper"
-        :page-sizes="[10, 20, 50, 100]"
-      />
+        <el-pagination
+          v-show="total > 0"
+          :total="total"
+          :page.sync="query.page"
+          :limit.sync="query.limit"
+          @current-change="handlePagination"
+          @size-change="handlePageSizeChange"
+          style="margin-top: 20px;"
+          layout="total, sizes, prev, pager, next, jumper"
+          :page-sizes="[10, 20, 50, 100]"
+        />
+      </template>
+      <el-empty v-else description="鍚庣鏆傛湭鎻愪緵鎹愮尞鑰呰缁嗕俊鎭帴鍙�"></el-empty>
     </el-card>
   </div>
 </template>
 
 <script>
-// 淇瀵煎叆璺緞
 import FilterPanel from '@/components/charts/FilterPanel.vue';
 import EChartsWrapper from '@/components/charts/EChartsWrapper.vue';
+import { caseSex, caseBloodType, caseAvgAge } from '@/api/businessApi';
+import { toTotalsList, toNum } from '@/utils/totals';
+import statisticsMode, { isStatisticsMock } from '@/utils/statisticsMode';
+import { COLORS, PALETTE, axisTooltip, grid, legend } from '@/utils/chartStyle';
+
+const AGE_GROUP_MAP = {
+  '1': '<17宀�',
+  '2': '18-49宀�',
+  '3': '50-69宀�',
+  '4': '>69宀�'
+};
 
 export default {
   name: 'DonorStatistics',
@@ -187,13 +174,13 @@
           type: 'select',
           multiple: true,
           options: [
-            { label: '闈掑矝鍦板尯', value: 'qingdao' },
-            { label: '鏃ョ収鍦板尯', value: 'rizhao' },
-            { label: '娴庡崡鍦板尯', value: 'jinan' },
-            { label: '鐑熷彴鍦板尯', value: 'yantai' },
-            { label: '濞佹捣鍦板尯', value: 'weihai' },
-            { label: '娼嶅潑鍦板尯', value: 'weifang' },
-            { label: '涓存矀鍦板尯', value: 'linyi' }
+            { label: '闈掑矝鍦板尯', value: '闈掑矝鍦板尯' },
+            { label: '鏃ョ収鍦板尯', value: '鏃ョ収鍦板尯' },
+            { label: '娴庡崡鍦板尯', value: '娴庡崡鍦板尯' },
+            { label: '鐑熷彴鍦板尯', value: '鐑熷彴鍦板尯' },
+            { label: '濞佹捣鍦板尯', value: '濞佹捣鍦板尯' },
+            { label: '娼嶅潑鍦板尯', value: '娼嶅潑鍦板尯' },
+            { label: '涓存矀鍦板尯', value: '涓存矀鍦板尯' }
           ],
           width: '300px'
         },
@@ -206,19 +193,6 @@
           endPlaceholder: '缁撴潫鏈堜唤',
           valueFormat: 'yyyy-MM',
           width: '300px'
-        },
-        {
-          label: '骞撮緞娈�',
-          prop: 'ageRange',
-          type: 'select',
-          multiple: true,
-          options: [
-            { label: '<17宀�', value: '0-17' },
-            { label: '18-49宀�', value: '18-49' },
-            { label: '50-69宀�', value: '50-69' },
-            { label: '>69宀�', value: '70-100' }
-          ],
-          width: '200px'
         },
         {
           label: '鎬у埆',
@@ -258,115 +232,109 @@
       // 鍥捐〃閰嶇疆
       ageChartOptions: {},
       genderBloodChartOptions: {},
-      categoryChartOptions: {},
-      deathChartOptions: {},
+
+      // 鎺ュ彛杩斿洖鏁版嵁
+      sexData: [],
+      bloodData: [],
+
+      // 鏃犳帴鍙e浘琛ㄧ殑妯℃嫙鏁版嵁锛堜粎 mock 妯″紡灞曠ず锛�
+      chinaCategoryData: [
+        { name: '涓浗涓�绫�(DBD)', value: 45 },
+        { name: '涓浗浜岀被(DCD)', value: 35 },
+        { name: '涓浗涓夌被(DBCD)', value: 20 }
+      ],
+      deathCauseData: [
+        { name: '鑴戣绠$柧鐥�', value: 40 },
+        { name: '棰呰剳鎹熶激', value: 30 },
+        { name: '蹇冭剰鐤剧梾', value: 15 },
+        { name: '鍏朵粬鍘熷洜', value: 15 }
+      ],
+
+      // 鎹愮尞鑰呰缁嗕俊鎭ā鎷熸暟鎹紙鏃犲悗绔帴鍙o紝浠� mock 妯″紡灞曠ず锛�
+      donorList: [
+        { donorNo: 'D2024001', name: '寮犱紵', gender: '鐢�', age: 45, bloodType: 'A', region: '闈掑矝鍦板尯', donationTime: '2024-03-15' },
+        { donorNo: 'D2024002', name: '鐜嬭姵', gender: '濂�', age: 38, bloodType: 'B', region: '闈掑矝鍦板尯', donationTime: '2024-03-22' },
+        { donorNo: 'D2024003', name: '鏉庡', gender: '濂�', age: 52, bloodType: 'O', region: '鏃ョ収鍦板尯', donationTime: '2024-04-02' },
+        { donorNo: 'D2024004', name: '鍒樻磱', gender: '鐢�', age: 29, bloodType: 'AB', region: '闈掑矝鍦板尯', donationTime: '2024-04-18' },
+        { donorNo: 'D2024005', name: '闄堥潤', gender: '濂�', age: 47, bloodType: 'A', region: '娴庡崡鍦板尯', donationTime: '2024-05-06' },
+        { donorNo: 'D2024006', name: '鏉ㄥ己', gender: '鐢�', age: 33, bloodType: 'B', region: '鐑熷彴鍦板尯', donationTime: '2024-05-21' },
+        { donorNo: 'D2024007', name: '璧垫晱', gender: '濂�', age: 41, bloodType: 'O', region: '闈掑矝鍦板尯', donationTime: '2024-06-03' },
+        { donorNo: 'D2024008', name: '榛勭', gender: '鐢�', age: 55, bloodType: 'A', region: '娼嶅潑鍦板尯', donationTime: '2024-06-17' },
+        { donorNo: 'D2024009', name: '鍛ㄦ澃', gender: '鐢�', age: 62, bloodType: 'O', region: '涓存矀鍦板尯', donationTime: '2024-07-01' },
+        { donorNo: 'D2024010', name: '鍚撮湠', gender: '濂�', age: 26, bloodType: 'AB', region: '鏃ョ収鍦板尯', donationTime: '2024-07-15' },
+        { donorNo: 'D2024011', name: '寰愮', gender: '鐢�', age: 48, bloodType: 'B', region: '闈掑矝鍦板尯', donationTime: '2024-08-09' },
+        { donorNo: 'D2024012', name: '瀛欎附', gender: '濂�', age: 35, bloodType: 'O', region: '濞佹捣鍦板尯', donationTime: '2024-08-24' },
+        { donorNo: 'D2024013', name: '椹秴', gender: '鐢�', age: 44, bloodType: 'A', region: '娴庡崡鍦板尯', donationTime: '2024-09-05' },
+        { donorNo: 'D2024014', name: '鏈卞┓', gender: '濂�', age: 58, bloodType: 'B', region: '闈掑矝鍦板尯', donationTime: '2024-09-19' },
+        { donorNo: 'D2024015', name: '鑳″啗', gender: '鐢�', age: 31, bloodType: 'O', region: '鐑熷彴鍦板尯', donationTime: '2024-10-08' },
+        { donorNo: 'D2024016', name: '閮稕', gender: '鐢�', age: 68, bloodType: 'A', region: '闈掑矝鍦板尯', donationTime: '2024-10-22' },
+        { donorNo: 'D2024017', name: '浣曞钩', gender: '濂�', age: 39, bloodType: 'AB', region: '娼嶅潑鍦板尯', donationTime: '2024-11-06' },
+        { donorNo: 'D2024018', name: '楂樼繑', gender: '鐢�', age: 50, bloodType: 'B', region: '涓存矀鍦板尯', donationTime: '2024-11-20' },
+        { donorNo: 'D2024019', name: '鏋楄姵', gender: '濂�', age: 27, bloodType: 'O', region: '闈掑矝鍦板尯', donationTime: '2024-12-04' },
+        { donorNo: 'D2024020', name: '缃楀垰', gender: '鐢�', age: 42, bloodType: 'A', region: '鏃ョ収鍦板尯', donationTime: '2024-12-18' },
+        { donorNo: 'D2023021', name: '閮戠埥', gender: '濂�', age: 34, bloodType: 'B', region: '闈掑矝鍦板尯', donationTime: '2023-06-10' },
+        { donorNo: 'D2023022', name: '姊佸畤', gender: '鐢�', age: 57, bloodType: 'O', region: '娴庡崡鍦板尯', donationTime: '2023-07-25' },
+        { donorNo: 'D2023023', name: '璋㈠', gender: '濂�', age: 49, bloodType: 'A', region: '濞佹捣鍦板尯', donationTime: '2023-08-14' },
+        { donorNo: 'D2023024', name: '闊╁啺', gender: '鐢�', age: 23, bloodType: 'AB', region: '闈掑矝鍦板尯', donationTime: '2023-09-30' }
+      ],
 
       // 琛ㄦ牸鏁版嵁
       tableData: [],
-      total: 0,
-
-      // 妯℃嫙鏁版嵁
-      mockData: {
-        table: [
-          { id: 1, donorNo: 'D2024001', name: '寮犱笁', gender: '鐢�', age: 45, bloodType: 'A', region: '闈掑矝鍦板尯', donationTime: '2024-01-15', chinaCategory: '涓浗涓�绫�(DBD)', deathCause: '鑴戝浼�', organs: ['鍏ㄨ倽', '宸﹁偩', '鍙宠偩'] },
-          { id: 2, donorNo: 'D2024002', name: '鏉庡洓', gender: '濂�', age: 32, bloodType: 'B', region: '闈掑矝鍦板尯', donationTime: '2024-01-20', chinaCategory: '涓浗浜岀被(DCD)', deathCause: '蹇冩悘楠ゅ仠', organs: ['蹇冭剰', '鐪艰鑶�'] },
-          { id: 3, donorNo: 'D2024003', name: '鐜嬩簲', gender: '鐢�', age: 58, bloodType: 'O', region: '鏃ョ収鍦板尯', donationTime: '2024-02-10', chinaCategory: '涓浗涓夌被(DBCD)', deathCause: '鑴戣绠℃剰澶�', organs: ['鍏ㄨ倽', '鑳拌吅'] },
-          { id: 4, donorNo: 'D2024004', name: '璧靛叚', gender: '鐢�', age: 28, bloodType: 'AB', region: '娴庡崡鍦板尯', donationTime: '2024-02-15', chinaCategory: '涓浗涓�绫�(DBD)', deathCause: '浜ら�氫簨鏁�', organs: ['鍏ㄨ偤', '宸﹁偩', '鍙宠偩'] },
-          { id: 5, donorNo: 'D2024005', name: '閽变竷', gender: '濂�', age: 65, bloodType: 'A', region: '鐑熷彴鍦板尯', donationTime: '2024-02-20', chinaCategory: '涓浗浜岀被(DCD)', deathCause: '蹇冭倢姊楁', organs: ['蹇冭剰'] },
-          { id: 6, donorNo: 'D2024006', name: '瀛欏叓', gender: '鐢�', age: 42, bloodType: 'B', region: '濞佹捣鍦板尯', donationTime: '2024-03-05', chinaCategory: '涓浗涓�绫�(DBD)', deathCause: '鑴戣偪鐦�', organs: ['宸﹁偩', '鍙宠偩', '鐪艰鑶�'] },
-          { id: 7, donorNo: 'D2024007', name: '鍛ㄤ節', gender: '鐢�', age: 37, bloodType: 'O', region: '娼嶅潑鍦板尯', donationTime: '2024-03-10', chinaCategory: '涓浗涓夌被(DBCD)', deathCause: '鑴戝浼�', organs: ['鑲濊剰', '鑳拌吅', '鐪艰鑶�'] },
-          { id: 8, donorNo: 'D2024008', name: '鍚村崄', gender: '濂�', age: 25, bloodType: 'AB', region: '涓存矀鍦板尯', donationTime: '2024-03-15', chinaCategory: '涓浗浜岀被(DCD)', deathCause: '浜ら�氫簨鏁�', organs: ['蹇冭剰', '鑲�'] }
-        ],
-        age: [
-          { ageRange: '<17宀�', male: 5, female: 3 },
-          { ageRange: '18-49宀�', male: 45, female: 30 },
-          { ageRange: '50-69宀�', male: 35, female: 25 },
-          { ageRange: '>69宀�', male: 10, female: 8 }
-        ],
-        gender: [
-          { name: '鐢锋��', value: 95 },
-          { name: '濂虫��', value: 66 }
-        ],
-        blood: [
-          { name: 'A鍨�', value: 45 },
-          { name: 'B鍨�', value: 38 },
-          { name: 'O鍨�', value: 52 },
-          { name: 'AB鍨�', value: 26 }
-        ],
-        category: [
-          { name: '涓浗涓�绫�(DBD)', value: 85 },
-          { name: '涓浗浜岀被(DCD)', value: 55 },
-          { name: '涓浗涓夌被(DBCD)', value: 21 }
-        ],
-        death: [
-          { name: '鑴戝浼�', value: 45 },
-          { name: '蹇冩悘楠ゅ仠', value: 35 },
-          { name: '鑴戣绠℃剰澶�', value: 35 },
-          { name: '浜ら�氫簨鏁�', value: 20 },
-          { name: '蹇冭倢姊楁', value: 25 },
-          { name: '鍏朵粬', value: 15 }
-        ]
-      }
+      total: 0
     };
+  },
+  computed: {
+    useMock() {
+      return isStatisticsMock();
+    },
+    chinaCategoryOptions() {
+      return this.buildPieOptions('涓浗鍒嗙被', this.chinaCategoryData);
+    },
+    deathCauseOptions() {
+      return this.buildPieOptions('姝讳骸鍘熷洜', this.deathCauseData);
+    }
   },
   created() {
     this.loadData();
+    // 鏁版嵁婧�(mock/鐪熷疄鎺ュ彛)鍒囨崲鏃堕噸鏂板姞杞�
+    this.$watch(() => statisticsMode.useMock, () => this.loadData());
   },
   methods: {
+    // 缁勮鏌ヨ鏉′欢
+    buildQuery() {
+      const q = {};
+      if (this.query.region && this.query.region.length) {
+        q.area = this.query.region.join(',');
+      }
+      if (this.query.monthRange && this.query.monthRange.length === 2) {
+        q.startTime = this.query.monthRange[0];
+        q.endTime = this.query.monthRange[1];
+      }
+      if (this.query.sex) q.sex = this.query.sex;
+      if (this.query.bloodType) q.boodType = this.query.bloodType;
+      return q;
+    },
+
     // 鍔犺浇鏁版嵁
     async loadData() {
       this.loading = true;
       try {
-        await new Promise(resolve => setTimeout(resolve, 500));
+        const q = this.buildQuery();
+        const [sexRes, bloodRes, avgRes] = await Promise.all([
+          caseSex(q),
+          caseBloodType(q),
+          caseAvgAge(q)
+        ]);
 
-        // 澶勭悊琛ㄦ牸鏁版嵁
-        let filteredData = [...this.mockData.table];
+        this.sexData = toTotalsList(sexRes);
+        this.bloodData = toTotalsList(bloodRes);
+        const avgList = toTotalsList(avgRes);
 
-        if (this.query.region.length > 0) {
-          filteredData = filteredData.filter(item =>
-            this.query.region.includes(item.region.replace('鍦板尯', '').toLowerCase())
-          );
-        }
-
-        if (this.query.monthRange && this.query.monthRange.length === 2) {
-          const [start, end] = this.query.monthRange;
-          filteredData = filteredData.filter(item =>
-            item.donationTime >= start && item.donationTime <= end
-          );
-        }
-
-        if (this.query.ageRange.length > 0) {
-          filteredData = filteredData.filter(item => {
-            const age = item.age;
-            return this.query.ageRange.some(range => {
-              if (range === '0-17') return age < 18;
-              if (range === '18-49') return age >= 18 && age <= 49;
-              if (range === '50-69') return age >= 50 && age <= 69;
-              if (range === '70-100') return age >= 70;
-              return false;
-            });
-          });
-        }
-
-        if (this.query.gender) {
-          filteredData = filteredData.filter(item => item.gender === this.query.gender);
-        }
-
-        if (this.query.bloodType) {
-          filteredData = filteredData.filter(item => item.bloodType === this.query.bloodType);
-        }
-
-        // 鍒嗛〉
-        const start = (this.query.page - 1) * this.query.limit;
-        this.tableData = filteredData.slice(start, start + this.query.limit);
-        this.total = filteredData.length;
-
-        // 鏇存柊姒傝鏁版嵁
-        this.updateOverviewData(filteredData);
-
-        // 鏇存柊鍥捐〃
+        this.updateOverviewData(avgList);
         this.updateCharts();
 
+        // 鎹愮尞鑰呰缁嗕俊鎭棤 /opo/totals 鑱氬悎鎺ュ彛锛屼娇鐢ㄦā鎷熸暟鎹ˉ鍏�
+        this.updateDonorTable();
       } catch (error) {
         console.error('鍔犺浇鏁版嵁澶辫触:', error);
         this.$message.error('鏁版嵁鍔犺浇澶辫触');
@@ -376,220 +344,164 @@
     },
 
     // 鏇存柊姒傝鏁版嵁
-    updateOverviewData(data) {
-      if (data.length === 0) return;
-
-      const total = data.length;
-      const males = data.filter(item => item.gender === '鐢�').length;
-      const females = data.filter(item => item.gender === '濂�').length;
-      const avgAge = data.reduce((sum, item) => sum + item.age, 0) / total;
+    updateOverviewData(avgList) {
+      const males = this.sexData.reduce((sum, item) => sum + toNum(item.manCount), 0);
+      const females = this.sexData.reduce((sum, item) => sum + toNum(item.womanCount), 0);
+      const total = males + females;
+      const avgAge = avgList.length
+        ? toNum(avgList.reduce((sum, item) => sum + toNum(item.avgAge), 0) / avgList.length)
+        : 0;
 
       this.overviewData[0].value = total;
-      this.overviewData[1].value = `${((males / total) * 100).toFixed(1)}%`;
-      this.overviewData[2].value = `${((females / total) * 100).toFixed(1)}%`;
+      this.overviewData[1].value = total > 0 ? `${((males / total) * 100).toFixed(1)}%` : '0%';
+      this.overviewData[2].value = total > 0 ? `${((females / total) * 100).toFixed(1)}%` : '0%';
       this.overviewData[3].value = avgAge.toFixed(1);
     },
 
     // 鏇存柊鍥捐〃
     updateCharts() {
-      // 骞撮緞鍒嗗竷鍥�
+      // 骞撮緞鍒嗗竷鍥撅紙宸︾敺鍙冲コ锛屾竻鐖藉彂鏁i噾瀛楀锛�
       this.ageChartOptions = {
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: {
-            type: 'shadow'
-          }
+        tooltip: axisTooltip(),
+        legend: legend(['鐢锋��', '濂虫��']),
+        grid: grid(),
+        xAxis: {
+          type: 'value',
+          axisLine: { show: false },
+          axisTick: { show: false },
+          axisLabel: { color: '#8a919f', fontSize: 12, formatter: value => Math.abs(value) },
+          splitLine: { lineStyle: { type: 'dashed', color: '#f0f1f5' } }
         },
-        legend: {
-          data: ['鐢锋��', '濂虫��']
+        yAxis: {
+          type: 'category',
+          data: this.sexData.map(item => AGE_GROUP_MAP[item.ageGroup] || item.ageGroup || '鏈煡'),
+          axisLine: { show: false },
+          axisTick: { show: false },
+          axisLabel: { color: '#606266', fontSize: 13 }
         },
-        grid: {
-          left: '3%',
-          right: '4%',
-          bottom: '3%',
-          containLabel: true
-        },
-        xAxis: [
-          {
-            type: 'value',
-            position: 'bottom',
-            axisLabel: {
-              formatter: function(value) {
-                return Math.abs(value);
-              }
-            }
-          }
-        ],
-        yAxis: [
-          {
-            type: 'category',
-            axisTick: {
-              show: false
-            },
-            data: this.mockData.age.map(item => item.ageRange)
-          }
-        ],
         series: [
           {
             name: '鐢锋��',
             type: 'bar',
             stack: '鎬у埆',
-            data: this.mockData.age.map(item => item.male),
-            itemStyle: { color: '#409EFF' }
+            barMaxWidth: 18,
+            data: this.sexData.map(item => toNum(item.manCount)),
+            itemStyle: { color: COLORS.blue, borderRadius: [4, 0, 0, 4] }
           },
           {
             name: '濂虫��',
             type: 'bar',
             stack: '鎬у埆',
-            data: this.mockData.age.map(item => -item.female),
-            itemStyle: { color: '#F56C6C' }
+            barMaxWidth: 18,
+            data: this.sexData.map(item => -toNum(item.womanCount)),
+            itemStyle: { color: COLORS.coral, borderRadius: [0, 4, 4, 0] }
           }
         ]
       };
 
-      // 鎬у埆涓庤鍨嬬粍鍚堝浘
+      // 鎬у埆涓庤鍨嬪垎甯冨浘锛堝悓蹇冪幆锛岄厤鑹叉竻鐖斤級
+      const males = this.bloodData.reduce((sum, item) => sum + toNum(item.manCount), 0);
+      const females = this.bloodData.reduce((sum, item) => sum + toNum(item.womanCount), 0);
+      const bloodPie = this.bloodData.map(item => ({
+        name: item.boodType || '鏈煡',
+        value: toNum(item.boodTypeCount) || (toNum(item.manCount) + toNum(item.womanCount))
+      }));
+
       this.genderBloodChartOptions = {
         tooltip: {
           trigger: 'item',
           formatter: '{a} <br/>{b} : {c} ({d}%)'
         },
-        legend: {
-          data: [
-            '鐢锋��', '濂虫��',
-            'A鍨�', 'B鍨�', 'O鍨�', 'AB鍨�'
-          ],
-          top: 20
-        },
-        grid: {
-          top: '20%',
-          bottom: '20%',
-          left: '10%',
-          right: '10%'
-        },
+        legend: { data: ['鐢锋��', '濂虫��', ...this.bloodData.map(item => item.boodType || '鏈煡')], top: 20 },
         series: [
           {
             name: '鎬у埆鍒嗗竷',
             type: 'pie',
-            radius: [0, '30%'],
+            radius: [0, '40%'],
             center: ['25%', '50%'],
-            label: {
-              position: 'inner',
-              fontSize: 12
-            },
-            data: this.mockData.gender
+            label: { position: 'inner', fontSize: 12 },
+            data: [
+              { name: '鐢锋��', value: males },
+              { name: '濂虫��', value: females }
+            ],
+            color: [COLORS.blue, COLORS.coral]
           },
           {
             name: '琛�鍨嬪垎甯�',
             type: 'pie',
-            radius: ['40%', '55%'],
+            radius: ['55%', '75%'],
             center: ['25%', '50%'],
-            data: this.mockData.blood
-          },
-          {
-            name: '鎬у埆琛�鍨嬪姣�',
-            type: 'pie',
-            radius: [0, '30%'],
-            center: ['75%', '50%'],
-            label: {
-              position: 'inner',
-              fontSize: 12
-            },
-            data: [
-              { name: '鐢�-A', value: 20 },
-              { name: '鐢�-B', value: 15 },
-              { name: '鐢�-O', value: 25 },
-              { name: '鐢�-AB', value: 10 },
-              { name: '濂�-A', value: 12 },
-              { name: '濂�-B', value: 8 },
-              { name: '濂�-O', value: 10 },
-              { name: '濂�-AB', value: 5 }
-            ]
+            label: { fontSize: 14, color: '#606266' },
+            data: bloodPie,
+            color: [COLORS.amber, COLORS.teal, COLORS.purple, COLORS.orange]
           }
         ]
       };
+    },
 
-      // 涓浗鍒嗙被鍒嗗竷鍥�
-      this.categoryChartOptions = {
+    // 鎹愮尞鑰呰缁嗕俊鎭紙鏃犳帴鍙o紝妯℃嫙鏁版嵁琛ュ叏 + 杩囨护鍒嗛〉锛�
+    updateDonorTable() {
+      let list = this.donorList;
+      if (this.query.region && this.query.region.length) {
+        list = list.filter(item => this.query.region.includes(item.region));
+      }
+      if (this.query.monthRange && this.query.monthRange.length === 2) {
+        const start = this.query.monthRange[0];
+        const end = this.query.monthRange[1];
+        list = list.filter(item => {
+          const m = item.donationTime.slice(0, 7);
+          return m >= start && m <= end;
+        });
+      }
+      if (this.query.gender) {
+        list = list.filter(item => item.gender === this.query.gender);
+      }
+      if (this.query.bloodType) {
+        list = list.filter(item => item.bloodType === this.query.bloodType);
+      }
+      this.total = list.length;
+      const start = (this.query.page - 1) * this.query.limit;
+      this.tableData = list.slice(start, start + this.query.limit);
+    },
+
+    // 鐢熸垚楗煎浘閰嶇疆锛堟棤鎺ュ彛鍥捐〃鐨� mock 灞曠ず锛�
+    buildPieOptions(name, data) {
+      return {
+        color: PALETTE,
         tooltip: {
           trigger: 'item',
           formatter: '{a} <br/>{b} : {c} ({d}%)'
         },
         legend: {
           orient: 'vertical',
-          right: 10,
-          top: 'center',
-          data: this.mockData.category.map(item => item.name)
+          left: 'left',
+          top: 'middle',
+          itemWidth: 10,
+          itemHeight: 10,
+          itemGap: 16,
+          textStyle: { fontSize: 14, color: '#606266' },
+          data: data.map(item => item.name)
         },
         series: [
           {
-            name: '涓浗鍒嗙被',
+            name,
             type: 'pie',
-            radius: ['30%', '70%'],
-            center: ['40%', '50%'],
-            roseType: 'radius',
-            label: {
-              show: true
-            },
+            radius: '55%',
+            center: ['55%', '50%'],
+            itemStyle: { borderColor: '#fff', borderWidth: 2 },
+            label: { fontSize: 15, color: '#606266', formatter: '{b} {d}%' },
+            labelLine: { lineStyle: { color: '#c0c4cc' } },
+            data,
             emphasis: {
-              label: {
-                show: true
+              itemStyle: {
+                shadowBlur: 10,
+                shadowOffsetX: 0,
+                shadowColor: 'rgba(0, 0, 0, 0.5)'
               }
-            },
-            data: this.mockData.category
+            }
           }
         ]
       };
-
-      // 姝讳骸鍘熷洜鍒嗘瀽鍥�
-      this.deathChartOptions = {
-        tooltip: {
-          trigger: 'item',
-          formatter: '{b}: {c} ({d}%)'
-        },
-        legend: {
-          orient: 'vertical',
-          left: 10,
-          top: 'center',
-          data: this.mockData.death.map(item => item.name)
-        },
-        series: [
-          {
-            name: '姝讳骸鍘熷洜',
-            type: 'pie',
-            radius: ['30%', '60%'],
-            center: ['60%', '50%'],
-            avoidLabelOverlap: false,
-            label: {
-              show: true,
-              formatter: '{b|{b}}\n{c|{c} ({d}%)}',
-              rich: {
-                b: {
-                  fontSize: 12,
-                  lineHeight: 20
-                },
-                c: {
-                  fontSize: 10,
-                  color: '#999'
-                }
-              }
-            },
-            emphasis: {
-              label: {
-                show: true,
-                fontSize: '16',
-                fontWeight: 'bold'
-              }
-            },
-            data: this.mockData.death
-          }
-        ]
-      };
-    },
-
-    // 鑾峰彇琛�鍨嬫爣绛剧被鍨�
-    getBloodTypeTag(type) {
-      const map = { 'A': 'danger', 'B': 'primary', 'O': 'success', 'AB': 'warning' };
-      return map[type] || 'info';
     },
 
     // 澶勭悊鏌ヨ
@@ -628,14 +540,6 @@
     // 澶勭悊瀵煎嚭
     handleExport() {
       this.$message.info('瀵煎嚭鍔熻兘寮�鍙戜腑...');
-    },
-
-    // 鏌ョ湅鎹愮尞鑰呰鎯�
-    viewDonorDetail(row) {
-      this.$router.push({
-        path: '/donor/detail',
-        query: { id: row.id }
-      });
     }
   }
 };
diff --git a/src/views/statistics/index.vue b/src/views/statistics/index.vue
index 65545dd..8dfc7fe 100644
--- a/src/views/statistics/index.vue
+++ b/src/views/statistics/index.vue
@@ -1,6 +1,21 @@
 <!-- src/views/statistics/index.vue -->
 <template>
   <div class="statistics-home">
+    <el-card class="mode-card" shadow="never">
+      <div class="mode-bar">
+        <span class="mode-label">缁熻鏁版嵁婧愶細</span>
+        <el-switch
+          v-model="useMock"
+          active-text="妯℃嫙鏁版嵁"
+          inactive-text="鐪熷疄鎺ュ彛"
+          active-color="#E6A23C"
+          inactive-color="#409EFF"
+          @change="handleModeChange"
+        />
+        <span class="mode-tip">鍒囨崲鍚庤繘鍏ュ悇缁熻椤甸潰鐢熸晥锛堟ā鎷熸暟鎹敤浜庢紨绀猴紝鐪熷疄鎺ュ彛璇诲彇鍚庣锛�</span>
+      </div>
+    </el-card>
+
     <el-row :gutter="20">
       <el-col :span="6" v-for="item in menuList" :key="item.path">
         <el-card
@@ -24,38 +39,41 @@
 </template>
 
 <script>
+import { isStatisticsMock, setStatisticsMock } from '@/utils/statisticsMode';
+
 export default {
   name: 'StatisticsHome',
   data() {
     return {
+      useMock: isStatisticsMock(),
       menuList: [
         {
           title: '妗堜緥缁熻',
           desc: '鍦板尯銆佹湀浠界淮搴︾殑妗堜緥缁熻鍒嗘瀽',
           icon: 'el-icon-s-data',
           color: '#409EFF',
-          path: '/statistics/case'
+          path: '/statics/caseStatistics'
         },
         {
           title: '鎹愮尞鍣ㄥ畼缁熻',
           desc: '鍣ㄥ畼鑾峰彇銆佺Щ妞嶆暟閲忕粺璁�',
           icon: 'el-icon-s-claim',
           color: '#67C23A',
-          path: '/statistics/organ'
+          path: '/statics/organstatistics'
         },
         {
           title: '鑾峰彇鐜囩粺璁�',
           desc: '鎹愮尞鑾峰彇鐜囥�佸櫒瀹樺純鐢ㄧ巼鍒嗘瀽',
           icon: 'el-icon-s-flag',
           color: '#E6A23C',
-          path: '/statistics/rate'
+          path: '/statics/statisticsStatistics'
         },
         {
           title: '鎹愮尞鎰忔効缁熻',
           desc: '鎹愮尞鎰忔効瓒嬪娍涓庡垎甯冨垎鏋�',
           icon: 'el-icon-star-on',
           color: '#F56C6C',
-          path: '/statistics/willingness'
+          path: '/statics/donorstatistics'
         },
         {
           title: '鎹愮尞鑰呭垎鏋�',
@@ -75,6 +93,9 @@
     };
   },
   methods: {
+    handleModeChange(val) {
+      setStatisticsMock(val);
+    },
     goToPage(path) {
       this.$router.push(path);
     }
@@ -87,6 +108,27 @@
   padding: 20px;
 }
 
+.mode-card {
+  margin-bottom: 20px;
+}
+
+.mode-bar {
+  display: flex;
+  align-items: center;
+}
+
+.mode-label {
+  font-size: 14px;
+  color: #303133;
+  margin-right: 10px;
+}
+
+.mode-tip {
+  margin-left: 16px;
+  font-size: 12px;
+  color: #909399;
+}
+
 .stat-card {
   cursor: pointer;
   margin-bottom: 20px;
diff --git a/src/views/statistics/organ.vue b/src/views/statistics/organ.vue
index bff9883..9c420ae 100644
--- a/src/views/statistics/organ.vue
+++ b/src/views/statistics/organ.vue
@@ -81,20 +81,20 @@
         border
         stripe
       >
-        <el-table-column label="骞翠唤" prop="year" align="center"  />
-        <el-table-column label="鍣ㄥ畼鍚嶇О" prop="organName" align="center"  />
-        <el-table-column label="鑾峰彇鏁伴噺" prop="acquisitionCount" align="center"  />
-        <el-table-column label="绉绘鏁伴噺" prop="transplantCount" align="center"  />
-        <el-table-column label="绉绘鐜�" prop="transplantRate" align="center" >
+        <el-table-column label="骞翠唤" prop="year" align="center" />
+        <el-table-column label="鍣ㄥ畼鍚嶇О" prop="organName" align="center" />
+        <el-table-column label="鑾峰彇鏁伴噺" prop="acquisitionCount" align="center" />
+        <el-table-column label="绉绘鏁伴噺" prop="transplantCount" align="center" />
+        <el-table-column label="绉绘鐜�" prop="transplantRate" align="center">
           <template slot-scope="{ row }">
             <el-tag :type="getRateTag(row.transplantRate)" size="small">
               {{ row.transplantRate }}%
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="寮冪敤鏁伴噺" prop="abandonCount" align="center"  />
-        <el-table-column label="鍦板尯" prop="region" align="center"  />
-        <el-table-column label="鎿嶄綔" align="center"  fixed="right">
+        <el-table-column label="寮冪敤鏁伴噺" prop="abandonCount" align="center" />
+        <el-table-column label="鍦板尯" prop="region" align="center" />
+        <el-table-column label="鎿嶄綔" align="center" fixed="right">
           <template slot-scope="{ row }">
             <el-button
               type="text"
@@ -139,6 +139,10 @@
 import FilterPanel from '@/components/charts/FilterPanel.vue';
 import EChartsWrapper from '@/components/charts/EChartsWrapper.vue';
 import OrganDetail from './components/OrganDetail.vue';
+import { organ, organMoon, organType } from '@/api/businessApi';
+import { toTotalsList, toMonth, toNum } from '@/utils/totals';
+import statisticsMode from '@/utils/statisticsMode';
+import { COLORS, PALETTE, axisTooltip, grid, xAxis, yAxis, legend, barSeries } from '@/utils/chartStyle';
 
 export default {
   name: 'OrganStatistics',
@@ -166,13 +170,13 @@
           type: 'select',
           multiple: true,
           options: [
-            { label: '闈掑矝鍦板尯', value: 'qingdao' },
-            { label: '鏃ョ収鍦板尯', value: 'rizhao' },
-            { label: '娴庡崡鍦板尯', value: 'jinan' },
-            { label: '鐑熷彴鍦板尯', value: 'yantai' },
-            { label: '濞佹捣鍦板尯', value: 'weihai' },
-            { label: '娼嶅潑鍦板尯', value: 'weifang' },
-            { label: '涓存矀鍦板尯', value: 'linyi' }
+            { label: '闈掑矝鍦板尯', value: '闈掑矝鍦板尯' },
+            { label: '鏃ョ収鍦板尯', value: '鏃ョ収鍦板尯' },
+            { label: '娴庡崡鍦板尯', value: '娴庡崡鍦板尯' },
+            { label: '鐑熷彴鍦板尯', value: '鐑熷彴鍦板尯' },
+            { label: '濞佹捣鍦板尯', value: '濞佹捣鍦板尯' },
+            { label: '娼嶅潑鍦板尯', value: '娼嶅潑鍦板尯' },
+            { label: '涓存矀鍦板尯', value: '涓存矀鍦板尯' }
           ],
           width: '300px'
         },
@@ -191,12 +195,12 @@
           prop: 'organType',
           type: 'select',
           options: [
-            { label: '鍏ㄨ倽', value: 'liver' },
-            { label: '鑲捐剰', value: 'kidney' },
-            { label: '蹇冭剰', value: 'heart' },
-            { label: '鑲�', value: 'lung' },
-            { label: '鑳拌吅', value: 'pancreas' },
-            { label: '鐪艰鑶�', value: 'cornea' }
+            { label: '鍏ㄨ倽', value: '鍏ㄨ倽' },
+            { label: '鑲捐剰', value: '鑲捐剰' },
+            { label: '蹇冭剰', value: '蹇冭剰' },
+            { label: '鑲�', value: '鑲�' },
+            { label: '鑳拌吅', value: '鑳拌吅' },
+            { label: '鐪艰鑶�', value: '鐪艰鑶�' }
           ],
           width: '200px'
         }
@@ -211,93 +215,97 @@
 
       // 姒傝鏁版嵁
       overviewData: [
-        { title: '鎬昏幏鍙栨暟閲�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-claim', color: '#409EFF' },
-        { title: '鎬荤Щ妞嶆暟閲�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-success', color: '#67C23A' },
-        { title: '鎬荤Щ妞嶇巼', value: '0%', desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-data', color: '#E6A23C' },
-        { title: '鎬诲純鐢ㄦ暟閲�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-warning', color: '#F56C6C' }
+        { title: '鎬昏幏鍙栨暟閲�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-claim', color: '#5B8FF9' },
+        { title: '鎬荤Щ妞嶆暟閲�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-success', color: '#5AD8A6' },
+        { title: '鎬荤Щ妞嶇巼', value: '0%', desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-data', color: '#F6BD16' },
+        { title: '鎬诲純鐢ㄦ暟閲�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-warning', color: '#E8684A' }
       ],
 
       // 鍥捐〃閰嶇疆
       trendChartOptions: {},
       distributionChartOptions: {},
 
+      // 鍥捐〃鏁版嵁
+      chartData: {
+        trend: [],
+        distribution: []
+      },
+
       // 寮圭獥鎺у埗
       showOrganDetail: false,
-      detailQuery: {},
-
-      // 妯℃嫙鏁版嵁
-      mockData: {
-        table: [
-          { id: 1, year: '2024', organName: '鍏ㄨ倽', acquisitionCount: 15, transplantCount: 12, transplantRate: 80.0, abandonCount: 3, region: '闈掑矝鍦板尯' },
-          { id: 2, year: '2024', organName: '鑲捐剰', acquisitionCount: 20, transplantCount: 18, transplantRate: 90.0, abandonCount: 2, region: '闈掑矝鍦板尯' },
-          { id: 3, year: '2024', organName: '鍏ㄨ倽', acquisitionCount: 8, transplantCount: 6, transplantRate: 75.0, abandonCount: 2, region: '鏃ョ収鍦板尯' },
-          { id: 4, year: '2024', organName: '蹇冭剰', acquisitionCount: 5, transplantCount: 4, transplantRate: 80.0, abandonCount: 1, region: '闈掑矝鍦板尯' },
-          { id: 5, year: '2023', organName: '鍏ㄨ倽', acquisitionCount: 12, transplantCount: 10, transplantRate: 83.3, abandonCount: 2, region: '闈掑矝鍦板尯' },
-          { id: 6, year: '2023', organName: '鐪艰鑶�', acquisitionCount: 25, transplantCount: 22, transplantRate: 88.0, abandonCount: 3, region: '娴庡崡鍦板尯' }
-        ],
-        trend: [
-          { year: '2020', acquisition: 120, transplant: 100 },
-          { year: '2021', acquisition: 150, transplant: 130 },
-          { year: '2022', acquisition: 180, transplant: 160 },
-          { year: '2023', acquisition: 220, transplant: 200 },
-          { year: '2024', acquisition: 250, transplant: 230 }
-        ],
-        distribution: [
-          { name: '鍏ㄨ倽', value: 85 },
-          { name: '鑲捐剰', value: 120 },
-          { name: '蹇冭剰', value: 45 },
-          { name: '鑲�', value: 60 },
-          { name: '鑳拌吅', value: 30 },
-          { name: '鐪艰鑶�', value: 200 }
-        ]
-      }
+      detailQuery: {}
     };
   },
   created() {
     this.loadData();
+    // 鏁版嵁婧�(mock/鐪熷疄鎺ュ彛)鍒囨崲鏃堕噸鏂板姞杞�
+    this.$watch(() => statisticsMode.useMock, () => this.loadData());
   },
   methods: {
+    // 缁勮鏌ヨ鏉′欢
+    buildQuery() {
+      const q = {};
+      if (this.query.region && this.query.region.length) {
+        q.area = this.query.region.join(',');
+      }
+      if (this.query.yearRange && this.query.yearRange.length === 2) {
+        q.startTime = this.query.yearRange[0];
+        q.endTime = this.query.yearRange[1];
+      }
+      if (this.query.organType) {
+        q.organName = this.query.organType;
+      }
+      return q;
+    },
+
     // 鍔犺浇鏁版嵁
     async loadData() {
       this.loading = true;
       try {
-        await new Promise(resolve => setTimeout(resolve, 500));
+        const q = this.buildQuery();
+        const [moonRes, typeRes, organRes] = await Promise.all([
+          organMoon(q),
+          organType(q),
+          organ(q)
+        ]);
 
-        // 澶勭悊琛ㄦ牸鏁版嵁
-        let filteredData = [...this.mockData.table];
+        // 鑾峰彇/绉绘瓒嬪娍
+        this.chartData.trend = toTotalsList(moonRes).map(item => ({
+          time: toMonth(item.donateTime) || toMonth(item.month),
+          acquisition: toNum(item.getCount),
+          transplant: toNum(item.useCount)
+        }));
 
-        if (this.query.region.length > 0) {
-          filteredData = filteredData.filter(item =>
-            this.query.region.includes(item.region.replace('鍦板尯', '').toLowerCase())
-          );
-        }
+        // 鍣ㄥ畼绫诲瀷鍒嗗竷
+        this.chartData.distribution = toTotalsList(typeRes).map(item => ({
+          name: item.organName || '',
+          value: toNum(item.getCount)
+        }));
 
-        if (this.query.yearRange && this.query.yearRange.length === 2) {
-          const [start, end] = this.query.yearRange;
-          filteredData = filteredData.filter(item =>
-            parseInt(item.year) >= parseInt(start) && parseInt(item.year) <= parseInt(end)
-          );
-        }
-
-        if (this.query.organType) {
-          const organMap = { liver: '鑲�', kidney: '鑲�', heart: '蹇冭剰', lung: '鑲�', pancreas: '鑳拌吅', cornea: '鐪艰鑶�' };
-          const organName = organMap[this.query.organType];
-          if (organName) {
-            filteredData = filteredData.filter(item => item.organName.includes(organName));
-          }
-        }
-
-        // 鍒嗛〉
+        // 鏄庣粏琛�
+        const all = toTotalsList(organRes).map(item => {
+          const get = toNum(item.getCount);
+          const use = toNum(item.useCount);
+          const rate = get > 0 ? Number(((use / get) * 100).toFixed(1)) : 0;
+          return {
+            year: String(item.donateTime || item.year || '').slice(0, 4),
+            organName: item.organName || '',
+            acquisitionCount: get,
+            transplantCount: use,
+            transplantRate: rate,
+            abandonCount: get - use,
+            region: item.area || item.region || ''
+          };
+        });
+        this.total = all.length;
         const start = (this.query.page - 1) * this.query.limit;
-        this.tableData = filteredData.slice(start, start + this.query.limit);
-        this.total = filteredData.length;
+        this.tableData = all.slice(start, start + this.query.limit);
 
-        // 鏇存柊姒傝鏁版嵁
-        this.updateOverviewData(filteredData);
+        // 鏇存柊姒傝
+        this.updateOverviewData(all);
 
         // 鏇存柊鍥捐〃
         this.updateCharts();
-
       } catch (error) {
         console.error('鍔犺浇鏁版嵁澶辫触:', error);
         this.$message.error('鏁版嵁鍔犺浇澶辫触');
@@ -308,12 +316,10 @@
 
     // 鏇存柊姒傝鏁版嵁
     updateOverviewData(data) {
-      if (data.length === 0) return;
-
-      const totalAcquisition = data.reduce((sum, item) => sum + item.acquisitionCount, 0);
-      const totalTransplant = data.reduce((sum, item) => sum + item.transplantCount, 0);
-      const totalAbandon = data.reduce((sum, item) => sum + item.abandonCount, 0);
-      const transplantRate = totalAcquisition > 0 ? (totalTransplant / totalAcquisition * 100).toFixed(1) : 0;
+      const totalAcquisition = data.reduce((sum, item) => sum + toNum(item.acquisitionCount), 0);
+      const totalTransplant = data.reduce((sum, item) => sum + toNum(item.transplantCount), 0);
+      const totalAbandon = data.reduce((sum, item) => sum + toNum(item.abandonCount), 0);
+      const transplantRate = totalAcquisition > 0 ? ((totalTransplant / totalAcquisition) * 100).toFixed(1) : 0;
 
       this.overviewData[0].value = totalAcquisition;
       this.overviewData[1].value = totalTransplant;
@@ -325,47 +331,20 @@
     updateCharts() {
       // 瓒嬪娍鍥�
       this.trendChartOptions = {
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: {
-            type: 'shadow'
-          }
-        },
-        legend: {
-          data: ['鑾峰彇鏁伴噺', '绉绘鏁伴噺']
-        },
-        grid: {
-          left: '3%',
-          right: '4%',
-          bottom: '3%',
-          containLabel: true
-        },
-        xAxis: {
-          type: 'category',
-          data: this.mockData.trend.map(item => item.year + '骞�')
-        },
-        yAxis: {
-          type: 'value',
-          name: '鏁伴噺(涓�)'
-        },
+        tooltip: axisTooltip(),
+        legend: legend(['鑾峰彇鏁伴噺', '绉绘鏁伴噺']),
+        grid: grid(),
+        xAxis: xAxis(this.chartData.trend.map(item => item.time)),
+        yAxis: yAxis(),
         series: [
-          {
-            name: '鑾峰彇鏁伴噺',
-            type: 'bar',
-            data: this.mockData.trend.map(item => item.acquisition),
-            itemStyle: { color: '#409EFF' }
-          },
-          {
-            name: '绉绘鏁伴噺',
-            type: 'bar',
-            data: this.mockData.trend.map(item => item.transplant),
-            itemStyle: { color: '#67C23A' }
-          }
+          barSeries('鑾峰彇鏁伴噺', this.chartData.trend.map(item => item.acquisition), COLORS.blue),
+          barSeries('绉绘鏁伴噺', this.chartData.trend.map(item => item.transplant), COLORS.green)
         ]
       };
 
       // 鍒嗗竷鍥�
       this.distributionChartOptions = {
+        color: PALETTE,
         tooltip: {
           trigger: 'item',
           formatter: '{a} <br/>{b} : {c} ({d}%)'
@@ -373,15 +352,23 @@
         legend: {
           orient: 'vertical',
           left: 'left',
-          data: this.mockData.distribution.map(item => item.name)
+          top: 'middle',
+          itemWidth: 10,
+          itemHeight: 10,
+          itemGap: 16,
+          textStyle: { fontSize: 14, color: '#606266' },
+          data: this.chartData.distribution.map(item => item.name)
         },
         series: [
           {
             name: '鍣ㄥ畼鍒嗗竷',
             type: 'pie',
             radius: '55%',
-            center: ['50%', '60%'],
-            data: this.mockData.distribution,
+            center: ['55%', '50%'],
+            itemStyle: { borderColor: '#fff', borderWidth: 2 },
+            label: { fontSize: 14, color: '#606266', formatter: '{b} {d}%' },
+            labelLine: { lineStyle: { color: '#c0c4cc' } },
+            data: this.chartData.distribution,
             emphasis: {
               itemStyle: {
                 shadowBlur: 10,
diff --git a/src/views/statistics/rate.vue b/src/views/statistics/rate.vue
index 58c3edc..9eeb6d3 100644
--- a/src/views/statistics/rate.vue
+++ b/src/views/statistics/rate.vue
@@ -53,7 +53,7 @@
       <el-col :span="12">
         <el-card>
           <div slot="header" class="clearfix">
-            <span>寮冪敤鐜囨瀯鎴愬垎鏋�</span>
+            <span>鍦板尯鑾峰彇/寮冪敤鐜囧姣�</span>
           </div>
           <EChartsWrapper
             :id="'abandon-chart'"
@@ -104,6 +104,18 @@
         <el-table-column label="鍣ㄥ畼鎬绘暟" prop="totalOrgans" align="center"  />
         <el-table-column label="绉绘鎬绘暟" prop="totalTransplants" align="center"  />
       </el-table>
+
+      <el-pagination
+        v-show="total > 0"
+        :total="total"
+        :page.sync="query.page"
+        :limit.sync="query.limit"
+        @current-change="handlePagination"
+        @size-change="handlePageSizeChange"
+        style="margin-top: 20px;"
+        layout="total, sizes, prev, pager, next, jumper"
+        :page-sizes="[10, 20, 50, 100]"
+      />
     </el-card>
   </div>
 </template>
@@ -111,6 +123,10 @@
 <script>
 import FilterPanel from '@/components/charts/FilterPanel.vue';
 import EChartsWrapper from '@/components/charts/EChartsWrapper.vue';
+import { organ, organMoon, caseTrendMoon } from '@/api/businessApi';
+import { toTotalsList, toMonth, toNum } from '@/utils/totals';
+import statisticsMode from '@/utils/statisticsMode';
+import { COLORS, axisTooltip, grid, xAxis, legend, barSeries, lineSeries } from '@/utils/chartStyle';
 
 export default {
   name: 'RateStatistics',
@@ -136,13 +152,13 @@
           type: 'select',
           multiple: true,
           options: [
-            { label: '闈掑矝鍦板尯', value: 'qingdao' },
-            { label: '鏃ョ収鍦板尯', value: 'rizhao' },
-            { label: '娴庡崡鍦板尯', value: 'jinan' },
-            { label: '鐑熷彴鍦板尯', value: 'yantai' },
-            { label: '濞佹捣鍦板尯', value: 'weihai' },
-            { label: '娼嶅潑鍦板尯', value: 'weifang' },
-            { label: '涓存矀鍦板尯', value: 'linyi' }
+            { label: '闈掑矝鍦板尯', value: '闈掑矝鍦板尯' },
+            { label: '鏃ョ収鍦板尯', value: '鏃ョ収鍦板尯' },
+            { label: '娴庡崡鍦板尯', value: '娴庡崡鍦板尯' },
+            { label: '鐑熷彴鍦板尯', value: '鐑熷彴鍦板尯' },
+            { label: '濞佹捣鍦板尯', value: '濞佹捣鍦板尯' },
+            { label: '娼嶅潑鍦板尯', value: '娼嶅潑鍦板尯' },
+            { label: '涓存矀鍦板尯', value: '涓存矀鍦板尯' }
           ],
           width: '300px'
         },
@@ -166,25 +182,25 @@
         {
           key: 'acquisition',
           title: '鎹愮尞鑾峰彇鐜�',
-          value: '85.5%',
+          value: '0%',
           trendIcon: 'el-icon-top',
-          trendText: '鈫�2.7%',
+          trendText: '鈥�',
           trendClass: 'up'
         },
         {
           key: 'avg',
           title: '骞冲潎鍣ㄥ畼鏁�',
-          value: '3.2',
+          value: '0',
           trendIcon: 'el-icon-top',
-          trendText: '鈫�0.2',
+          trendText: '鈥�',
           trendClass: 'up'
         },
         {
           key: 'abandon',
           title: '鍣ㄥ畼寮冪敤鐜�',
-          value: '12.3%',
+          value: '0%',
           trendIcon: 'el-icon-bottom',
-          trendText: '鈫�2.7%',
+          trendText: '鈥�',
           trendClass: 'down'
         }
       ],
@@ -193,69 +209,150 @@
       trendChartOptions: {},
       abandonChartOptions: {},
 
+      // 鍥捐〃鏁版嵁
+      trendData: [],
+      regionCompareData: [],
+
       // 琛ㄦ牸鏁版嵁
       tableData: [],
-      total: 0,
-
-      // 妯℃嫙鏁版嵁
-      mockData: {
-        table: [
-          { id: 1, year: '2024', region: '闈掑矝鍦板尯', acquisitionRate: 85.5, avgOrgans: 3.2, abandonRate: 12.3, totalDonation: 280, totalOrgans: 896, totalTransplants: 765 },
-          { id: 2, year: '2024', region: '鏃ョ収鍦板尯', acquisitionRate: 80.3, avgOrgans: 2.9, abandonRate: 15.8, totalDonation: 140, totalOrgans: 406, totalTransplants: 325 },
-          { id: 3, year: '2024', region: '娴庡崡鍦板尯', acquisitionRate: 89.1, avgOrgans: 3.4, abandonRate: 9.5, totalDonation: 320, totalOrgans: 1088, totalTransplants: 970 },
-          { id: 4, year: '2023', region: '闈掑矝鍦板尯', acquisitionRate: 82.8, avgOrgans: 3.0, abandonRate: 15.0, totalDonation: 250, totalOrgans: 750, totalTransplants: 620 },
-          { id: 5, year: '2023', region: '鏃ョ収鍦板尯', acquisitionRate: 78.5, avgOrgans: 2.7, abandonRate: 18.2, totalDonation: 120, totalOrgans: 324, totalTransplants: 252 },
-          { id: 6, year: '2023', region: '娴庡崡鍦板尯', acquisitionRate: 85.2, avgOrgans: 3.1, abandonRate: 12.5, totalDonation: 280, totalOrgans: 868, totalTransplants: 740 }
-        ],
-        trend: [
-          { year: '2020', acquisition: 78.5, avgOrgans: 2.8, abandon: 18.5 },
-          { year: '2021', acquisition: 81.2, avgOrgans: 3.0, abandon: 16.3 },
-          { year: '2022', acquisition: 84.6, avgOrgans: 3.2, abandon: 13.8 },
-          { year: '2023', acquisition: 87.3, avgOrgans: 3.4, abandon: 11.2 },
-          { year: '2024', acquisition: 89.1, avgOrgans: 3.5, abandon: 9.5 }
-        ],
-        abandon: [
-          { name: '鍣ㄥ畼璐ㄩ噺涓嶅悎鏍�', value: 45 },
-          { name: '渚涗綋鐤剧梾', value: 25 },
-          { name: '杩愯緭闂', value: 15 },
-          { name: '鍙椾綋鍖归厤闂', value: 10 },
-          { name: '鍏朵粬鍘熷洜', value: 5 }
-        ]
-      }
+      total: 0
     };
   },
   created() {
     this.loadData();
+    // 鏁版嵁婧�(mock/鐪熷疄鎺ュ彛)鍒囨崲鏃堕噸鏂板姞杞�
+    this.$watch(() => statisticsMode.useMock, () => this.loadData());
   },
   methods: {
+    // 缁勮鏌ヨ鏉′欢
+    buildQuery() {
+      const q = {};
+      if (this.query.region && this.query.region.length) {
+        q.area = this.query.region.join(',');
+      }
+      if (this.query.yearRange && this.query.yearRange.length === 2) {
+        q.startTime = this.query.yearRange[0];
+        q.endTime = this.query.yearRange[1];
+      }
+      return q;
+    },
+
     // 鍔犺浇鏁版嵁
     async loadData() {
       this.loading = true;
       try {
-        await new Promise(resolve => setTimeout(resolve, 500));
+        const q = this.buildQuery();
+        const [organRes, moonRes, caseMoonRes] = await Promise.all([
+          organ(q),
+          organMoon(q),
+          caseTrendMoon(q)
+        ]);
 
-        // 澶勭悊琛ㄦ牸鏁版嵁
-        let filteredData = [...this.mockData.table];
+        // 鍣ㄥ畼鎸夋湀瓒嬪娍锛氳幏鍙栫巼(绉绘/鑾峰彇)銆佸純鐢ㄧ巼
+        this.trendData = toTotalsList(moonRes)
+          .map(item => {
+            const get = toNum(item.getCount);
+            const use = toNum(item.useCount);
+            return {
+              month: toMonth(item.donateTime) || toMonth(item.month),
+              acquisition: get > 0 ? Number(((use / get) * 100).toFixed(1)) : 0,
+              abandon: get > 0 ? Number((((get - use) / get) * 100).toFixed(1)) : 0
+            };
+          })
+          .sort((a, b) => String(a.month).localeCompare(String(b.month)));
 
-        if (this.query.region.length > 0) {
-          filteredData = filteredData.filter(item =>
-            this.query.region.includes(item.region.replace('鍦板尯', '').toLowerCase())
-          );
-        }
+        // 鎸� 骞翠唤|鍦板尯 姹囨�诲櫒瀹樻暟鎹�
+        const organByKey = {};
+        toTotalsList(organRes).forEach(item => {
+          const year = String(item.donateTime || item.year || '').slice(0, 4);
+          const region = item.area || item.region || '';
+          const key = `${year}|${region}`;
+          if (!organByKey[key]) organByKey[key] = { year, region, organs: 0, transplants: 0 };
+          organByKey[key].organs += toNum(item.getCount);
+          organByKey[key].transplants += toNum(item.useCount);
+        });
 
-        if (this.query.yearRange && this.query.yearRange.length === 2) {
-          const [start, end] = this.query.yearRange;
-          filteredData = filteredData.filter(item =>
-            parseInt(item.year) >= parseInt(start) && parseInt(item.year) <= parseInt(end)
-          );
-        }
+        // 鎸� 骞翠唤|鍦板尯 姹囨�绘渚嬫暟鎹紙鎹愮尞鎬绘暟銆佹綔鍦ㄦ渚嬶級
+        const caseByKey = {};
+        toTotalsList(caseMoonRes).forEach(item => {
+          const month = toMonth(item.donateTime) || toMonth(item.month);
+          const year = String(month).slice(0, 4);
+          const region = item.area || item.region || '';
+          const key = `${year}|${region}`;
+          if (!caseByKey[key]) caseByKey[key] = { year, region, potential: 0, complete: 0 };
+          caseByKey[key].potential += toNum(item.potentialCount);
+          caseByKey[key].complete += toNum(item.completeCount);
+        });
 
-        this.tableData = filteredData;
-        this.total = filteredData.length;
+        // 鍚堝苟 骞翠唤|鍦板尯 鏄庣粏
+        const keys = new Set([...Object.keys(organByKey), ...Object.keys(caseByKey)]);
+        const rows = [];
+        keys.forEach(key => {
+          const o = organByKey[key] || {};
+          const c = caseByKey[key] || {};
+          const year = o.year || c.year || '';
+          const region = o.region || c.region || '';
+          const organs = o.organs || 0;
+          const transplants = o.transplants || 0;
+          const potential = c.potential || 0;
+          const complete = c.complete || 0;
 
-        // 鏇存柊鍥捐〃
+          rows.push({
+            year,
+            region,
+            acquisitionRate: potential > 0 ? Number(((complete / potential) * 100).toFixed(1)) : 0,
+            avgOrgans: complete > 0 ? Number((organs / complete).toFixed(1)) : 0,
+            abandonRate: organs > 0 ? Number((((organs - transplants) / organs) * 100).toFixed(1)) : 0,
+            totalDonation: complete,
+            totalOrgans: organs,
+            totalTransplants: transplants
+          });
+        });
+        rows.sort((a, b) => String(a.year + a.region).localeCompare(String(b.year + b.region)));
+
+        this.total = rows.length;
+        const start = (this.query.page - 1) * this.query.limit;
+        this.tableData = rows.slice(start, start + this.query.limit);
+
+        // 鍦板尯缁村害鐨勮幏鍙栫巼/寮冪敤鐜囷紝鐢ㄦ綔鍦�/瀹屾垚銆佸櫒瀹�/绉绘鍘熷鏁版嵁姹囨��
+        const regionCompare = {};
+        toTotalsList(caseMoonRes).forEach(item => {
+          const region = item.area || item.region || '';
+          if (!regionCompare[region]) regionCompare[region] = { region, potential: 0, complete: 0 };
+          regionCompare[region].potential += toNum(item.potentialCount);
+          regionCompare[region].complete += toNum(item.completeCount);
+        });
+        const organRegion = {};
+        toTotalsList(organRes).forEach(item => {
+          const region = item.area || item.region || '';
+          if (!organRegion[region]) organRegion[region] = { organs: 0, transplants: 0 };
+          organRegion[region].organs += toNum(item.getCount);
+          organRegion[region].transplants += toNum(item.useCount);
+        });
+        const regionNames = new Set([...Object.keys(regionCompare), ...Object.keys(organRegion)]);
+        this.regionCompareData = [...regionNames].map(region => {
+          const c = regionCompare[region] || {};
+          const o = organRegion[region] || {};
+          const potential = c.potential || 0;
+          const complete = c.complete || 0;
+          const organs = o.organs || 0;
+          const transplants = o.transplants || 0;
+          return {
+            region,
+            acquisition: potential > 0 ? Number(((complete / potential) * 100).toFixed(1)) : 0,
+            abandon: organs > 0 ? Number((((organs - transplants) / organs) * 100).toFixed(1)) : 0
+          };
+        });
+
+        const totals = {
+          totalPotential: Object.values(caseByKey).reduce((s, c) => s + c.potential, 0),
+          totalDonation: Object.values(caseByKey).reduce((s, c) => s + c.complete, 0),
+          totalOrgans: Object.values(organByKey).reduce((s, o) => s + o.organs, 0),
+          totalTransplants: Object.values(organByKey).reduce((s, o) => s + o.transplants, 0)
+        };
+
+        this.updateOverviewData(totals);
         this.updateCharts();
-
       } catch (error) {
         console.error('鍔犺浇鏁版嵁澶辫触:', error);
         this.$message.error('鏁版嵁鍔犺浇澶辫触');
@@ -264,157 +361,107 @@
       }
     },
 
+    // 鏇存柊浠〃鐩�
+    updateOverviewData(totals) {
+      const { totalPotential, totalDonation, totalOrgans, totalTransplants } = totals;
+
+      const acquisitionRate = totalPotential > 0 ? Number(((totalDonation / totalPotential) * 100).toFixed(1)) : 0;
+      const avgOrgans = totalDonation > 0 ? Number((totalOrgans / totalDonation).toFixed(1)) : 0;
+      const abandonRate = totalOrgans > 0 ? Number((((totalOrgans - totalTransplants) / totalOrgans) * 100).toFixed(1)) : 0;
+
+      this.dashboardData[0].value = `${acquisitionRate}%`;
+      this.dashboardData[1].value = `${avgOrgans}`;
+      this.dashboardData[2].value = `${abandonRate}%`;
+      this.dashboardData[0].trendText = acquisitionRate >= 80 ? '鈫戣揪鏍�' : '鍋忎綆';
+      this.dashboardData[2].trendText = abandonRate <= 15 ? '鈫撹揪鏍�' : '鍋忛珮';
+    },
+
     // 鏇存柊鍥捐〃
     updateCharts() {
-      // 瓒嬪娍鍥�
+      // 鑾峰彇鐜囪秼鍔垮浘
       this.trendChartOptions = {
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: {
-            type: 'cross',
-            label: {
-              backgroundColor: '#6a7985'
-            }
-          }
-        },
-        legend: {
-          data: ['鎹愮尞鑾峰彇鐜�', '骞冲潎鍣ㄥ畼鏁�', '鍣ㄥ畼寮冪敤鐜�']
-        },
-        grid: {
-          left: '3%',
-          right: '4%',
-          bottom: '3%',
-          containLabel: true
-        },
+        tooltip: axisTooltip(),
+        legend: legend(['鍣ㄥ畼鍒╃敤鐜�', '鍣ㄥ畼寮冪敤鐜�']),
+        grid: grid(),
         xAxis: {
           type: 'category',
           boundaryGap: false,
-          data: this.mockData.trend.map(item => item.year + '骞�')
+          data: this.trendData.map(item => item.month),
+          axisLine: { show: false },
+          axisTick: { show: false },
+          axisLabel: { color: '#8a919f', fontSize: 12 }
         },
         yAxis: {
           type: 'value',
-          axisLabel: {
-            formatter: '{value}'
-          }
+          axisLabel: { color: '#8a919f', fontSize: 12, formatter: '{value}%' },
+          splitLine: { lineStyle: { type: 'dashed', color: '#f0f1f5' } }
         },
         series: [
-          {
-            name: '鎹愮尞鑾峰彇鐜�',
-            type: 'line',
-            data: this.mockData.trend.map(item => item.acquisition),
-            itemStyle: { color: '#409EFF' },
-            smooth: true
-          },
-          {
-            name: '骞冲潎鍣ㄥ畼鏁�',
-            type: 'line',
-            data: this.mockData.trend.map(item => item.avgOrgans * 20), // 缂╂斁鏄剧ず
-            itemStyle: { color: '#67C23A' },
-            smooth: true
-          },
-          {
-            name: '鍣ㄥ畼寮冪敤鐜�',
-            type: 'line',
-            data: this.mockData.trend.map(item => item.abandon),
-            itemStyle: { color: '#F56C6C' },
-            smooth: true
-          }
+          lineSeries('鍣ㄥ畼鍒╃敤鐜�', this.trendData.map(item => item.acquisition), COLORS.blue, { area: true }),
+          lineSeries('鍣ㄥ畼寮冪敤鐜�', this.trendData.map(item => item.abandon), COLORS.coral)
         ]
       };
 
-      // 寮冪敤鐜囨瀯鎴愬浘
+      // 鍦板尯鑾峰彇/寮冪敤鐜囧姣斿浘
       this.abandonChartOptions = {
-        tooltip: {
-          trigger: 'item',
-          formatter: '{a} <br/>{b} : {c} ({d}%)'
-        },
-        legend: {
-          orient: 'vertical',
-          left: 'left',
-          data: this.mockData.abandon.map(item => item.name)
+        tooltip: axisTooltip(),
+        legend: legend(['鎹愮尞鑾峰彇鐜�', '鍣ㄥ畼寮冪敤鐜�']),
+        grid: grid(),
+        xAxis: xAxis(this.regionCompareData.map(item => item.region)),
+        yAxis: {
+          type: 'value',
+          axisLabel: { color: '#8a919f', fontSize: 12, formatter: '{value}%' },
+          splitLine: { lineStyle: { type: 'dashed', color: '#f0f1f5' } }
         },
         series: [
-          {
-            name: '寮冪敤鍘熷洜',
-            type: 'pie',
-            radius: ['40%', '70%'],
-            avoidLabelOverlap: false,
-            label: {
-              show: false,
-              position: 'center'
-            },
-            emphasis: {
-              label: {
-                show: true,
-                fontSize: '20',
-                fontWeight: 'bold'
-              }
-            },
-            labelLine: {
-              show: false
-            },
-            data: this.mockData.abandon
-          }
+          barSeries('鎹愮尞鑾峰彇鐜�', this.regionCompareData.map(item => item.acquisition), COLORS.blue),
+          barSeries('鍣ㄥ畼寮冪敤鐜�', this.regionCompareData.map(item => item.abandon), COLORS.coral)
         ]
       };
     },
 
-    // 鑾峰彇浠〃鐩樺浘琛ㄩ厤缃�
+    // 鑾峰彇浠〃鐩樺浘琛ㄩ厤缃紙琛ㄧ洏 + 鎸囬拡锛屾暟鍊肩敱涓婃柟澶ф暟瀛楀睍绀猴紝鍒诲害澶栫疆锛�
     getDashboardOptions(item) {
       const value = parseFloat(item.value);
       const max = item.key === 'avg' ? 5 : 100;
-      const data = [{ value, name: item.title }];
+      const color = item.key === 'abandon' ? COLORS.coral : item.key === 'acquisition' ? COLORS.blue : COLORS.green;
+      // 骞冲潎鍣ㄥ畼鏁拌寖鍥村皬鐢ㄦ暣鏁板埢搴︼紝鐧惧垎姣旂敤 0/25/50/75/100 浜斾釜鍒诲害锛岄伩鍏嶆暟瀛楁嫢鎸�
+      const splitNumber = item.key === 'avg' ? 5 : 4;
 
       return {
-        tooltip: {
-          formatter: '{b}: {c}' + (item.key === 'abandon' ? '%' : item.key === 'acquisition' ? '%' : '')
-        },
         series: [
           {
-            name: item.title,
             type: 'gauge',
-            progress: {
-              show: true,
-              width: 18
-            },
+            startAngle: 210,
+            endAngle: -30,
+            min: 0,
+            max,
+            splitNumber,
+            radius: '60%',
+            center: ['50%', '52%'],
             axisLine: {
               lineStyle: {
-                width: 18
+                width: 10,
+                color: [[value / max, color], [1, '#eef0f5']]
               }
             },
-            axisTick: {
-              show: false
+            pointer: {
+              length: '60%',
+              width: 4,
+              itemStyle: { color }
             },
-            splitLine: {
-              length: 15,
-              lineStyle: {
-                width: 2,
-                color: '#999'
-              }
-            },
+            anchor: { show: false },
+            axisTick: { show: false },
+            // length 褰掗浂 + 璐熻窛绂伙紝浣垮埢搴︽暟瀛楄惤鍦ㄨ〃鐩樺渾寮у渚э紙澶栧洿锛�
+            splitLine: { show: false, length: 0 },
             axisLabel: {
-              distance: 25,
-              color: '#999',
+              distance: -18,
+              color: '#8a919f',
               fontSize: 12
             },
-            anchor: {
-              show: true,
-              showAbove: true,
-              size: 25,
-              itemStyle: {
-                borderWidth: 10
-              }
-            },
-            title: {
-              show: false
-            },
-            detail: {
-              valueAnimation: true,
-              fontSize: 20,
-              offsetCenter: [0, '70%']
-            },
-            data: data,
-            max: max
+            title: { show: false },
+            detail: { show: false },
+            data: [{ value, name: item.title }]
           }
         ]
       };
@@ -455,6 +502,19 @@
       this.loadData();
     },
 
+    // 澶勭悊鍒嗛〉
+    handlePagination(page) {
+      this.query.page = page;
+      this.loadData();
+    },
+
+    // 澶勭悊鍒嗛〉澶у皬鍙樺寲
+    handlePageSizeChange(size) {
+      this.query.limit = size;
+      this.query.page = 1;
+      this.loadData();
+    },
+
     // 澶勭悊瀵煎嚭
     handleExport() {
       this.$message.info('瀵煎嚭鍔熻兘寮�鍙戜腑...');
diff --git a/src/views/statistics/utilization.vue b/src/views/statistics/utilization.vue
index 51ea1c9..76c03b1 100644
--- a/src/views/statistics/utilization.vue
+++ b/src/views/statistics/utilization.vue
@@ -29,124 +29,56 @@
     <el-card style="margin-bottom: 20px;">
       <div slot="header" class="clearfix">
         <h3>2. 鍣ㄥ畼寮冪敤鍘熷洜鍒嗘瀽</h3>
-        <el-form :inline="true" style="float: right;">
-          <el-form-item label="鍣ㄥ畼绫诲瀷" style="margin-bottom: 0;">
-            <el-select
-              v-model="abandonQuery.organType"
-              placeholder="璇烽�夋嫨鍣ㄥ畼绫诲瀷"
-              clearable
-              size="mini"
-              style="width: 150px"
-              @change="handleAbandonQuery"
-            >
-              <el-option
-                v-for="item in organOptions"
-                :key="item.value"
-                :label="item.label"
-                :value="item.value"
-              />
-            </el-select>
-          </el-form-item>
-        </el-form>
       </div>
       <EChartsWrapper
+        v-if="useMock"
         :id="'abandon-analysis-chart'"
         :options="abandonChartOptions"
         height="400px"
       />
+      <el-empty v-else description="鍚庣鏆傛湭鎻愪緵鍣ㄥ畼寮冪敤鍘熷洜缁熻鎺ュ彛"></el-empty>
     </el-card>
 
     <!-- 绗笁閮ㄥ垎锛氭瘡渚涗綋鍣ㄥ畼鑾峰彇鏁伴噺 -->
     <el-card>
       <div slot="header" class="clearfix">
         <h3>3. 姣忎緵浣撳櫒瀹樿幏鍙栨暟閲�</h3>
-        <el-form :inline="true" style="float: right;">
-          <el-form-item label="骞翠唤" style="margin-bottom: 0;">
-            <el-date-picker
-              v-model="perDonorQuery.year"
-              type="year"
-              placeholder="閫夋嫨骞翠唤"
-              value-format="yyyy"
-              size="mini"
-              style="width: 120px"
-              @change="handlePerDonorQuery"
-            />
-          </el-form-item>
-
-          <el-form-item label="鍣ㄥ畼绫诲瀷" style="margin-bottom: 0;">
-            <el-select
-              v-model="perDonorQuery.organType"
-              placeholder="璇烽�夋嫨鍣ㄥ畼绫诲瀷"
-              clearable
-              size="mini"
-              style="width: 150px"
-              @change="handlePerDonorQuery"
-            >
-              <el-option
-                v-for="item in organOptions"
-                :key="item.value"
-                :label="item.label"
-                :value="item.value"
-              />
-            </el-select>
-          </el-form-item>
-        </el-form>
       </div>
 
-      <el-table
-        v-loading="loading"
-        :data="tableData"
-        style="width: 100%"
-        border
-        stripe
-      >
-        <el-table-column label="骞翠唤" prop="year" align="center"  />
-        <el-table-column label="渚涗綋濮撳悕" prop="donorName" align="center"  />
-        <el-table-column label="鑾峰彇鏁伴噺" prop="acquisitionCount" align="center" >
-          <template slot-scope="{ row }">
-            <el-tag :type="getCountTag(row.acquisitionCount)" size="small">
-              {{ row.acquisitionCount }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="鍏ㄨ倽" prop="liver" align="center"  />
-        <el-table-column label="宸﹀崐鑲�" prop="leftLiver" align="center"  />
-        <el-table-column label="鍙冲崐鑲�" prop="rightLiver" align="center"  />
-        <el-table-column label="蹇冭剰" prop="heart" align="center"  />
-        <el-table-column label="灏忚偁" prop="intestine" align="center"  />
-        <el-table-column label="鑳拌吅" prop="pancreas" align="center"  />
-        <el-table-column label="宸﹁偩" prop="leftKidney" align="center"  />
-        <el-table-column label="鍙宠偩" prop="rightKidney" align="center"  />
-        <el-table-column label="鍏ㄨ偤" prop="fullLung" align="center"  />
-        <el-table-column label="宸﹁偤" prop="leftLung" align="center"  />
-        <el-table-column label="鍙宠偤" prop="rightLung" align="center"  />
-        <el-table-column label="宸︾溂瑙掕啘" prop="leftCornea" align="center"  />
-        <el-table-column label="鍙崇溂瑙掕啘" prop="rightCornea" align="center"  />
-        <el-table-column label="鍦板尯" prop="region" align="center"  />
-        <el-table-column label="鎿嶄綔" align="center"  fixed="right">
-          <template slot-scope="{ row }">
-            <el-button
-              type="text"
-              size="mini"
-              @click="viewDonorDetail(row)"
-            >
-              璇︽儏
-            </el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <el-pagination
-        v-show="total > 0"
-        :total="total"
-        :page.sync="query.page"
-        :limit.sync="query.limit"
-        @current-change="handlePagination"
-        @size-change="handlePageSizeChange"
-        style="margin-top: 20px;"
-        layout="total, sizes, prev, pager, next, jumper"
-        :page-sizes="[10, 20, 50, 100]"
-      />
+      <template v-if="useMock">
+        <el-table
+          v-loading="loading"
+          :data="perDonorTable"
+          style="width: 100%"
+          border
+          stripe
+        >
+          <el-table-column label="骞翠唤" prop="year" align="center" />
+          <el-table-column label="渚涗綋濮撳悕" prop="donorName" align="center" />
+          <el-table-column label="鑾峰彇鏁伴噺" prop="acquisitionCount" align="center">
+            <template slot-scope="{ row }">
+              <el-tag :type="getCountTag(row.acquisitionCount)" size="small">
+                {{ row.acquisitionCount }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="鍏ㄨ倽" prop="liver" align="center" />
+          <el-table-column label="宸﹀崐鑲�" prop="leftLiver" align="center" />
+          <el-table-column label="鍙冲崐鑲�" prop="rightLiver" align="center" />
+          <el-table-column label="蹇冭剰" prop="heart" align="center" />
+          <el-table-column label="灏忚偁" prop="intestine" align="center" />
+          <el-table-column label="鑳拌吅" prop="pancreas" align="center" />
+          <el-table-column label="宸﹁偩" prop="leftKidney" align="center" />
+          <el-table-column label="鍙宠偩" prop="rightKidney" align="center" />
+          <el-table-column label="鍏ㄨ偤" prop="fullLung" align="center" />
+          <el-table-column label="宸﹁偤" prop="leftLung" align="center" />
+          <el-table-column label="鍙宠偤" prop="rightLung" align="center" />
+          <el-table-column label="宸︾溂瑙掕啘" prop="leftCornea" align="center" />
+          <el-table-column label="鍙崇溂瑙掕啘" prop="rightCornea" align="center" />
+          <el-table-column label="鍦板尯" prop="region" align="center" />
+        </el-table>
+      </template>
+      <el-empty v-else description="鍚庣鏆傛湭鎻愪緵姣忎緵浣撳櫒瀹樿幏鍙栨槑缁嗘帴鍙�"></el-empty>
     </el-card>
   </div>
 </template>
@@ -154,6 +86,10 @@
 <script>
 import FilterPanel from '@/components/charts/FilterPanel.vue';
 import EChartsWrapper from '@/components/charts/EChartsWrapper.vue';
+import { organType } from '@/api/businessApi';
+import { toTotalsList, toNum } from '@/utils/totals';
+import statisticsMode, { isStatisticsMock } from '@/utils/statisticsMode';
+import { COLORS, axisTooltip, grid, xAxis, yAxis, legend, barSeries } from '@/utils/chartStyle';
 
 export default {
   name: 'OrganUtilization',
@@ -167,8 +103,6 @@
       query: {
         region: [],
         monthRange: [],
-        ageRange: [],
-        chinaCategory: [],
         organType: null,
         page: 1,
         limit: 10
@@ -182,13 +116,13 @@
           type: 'select',
           multiple: true,
           options: [
-            { label: '闈掑矝鍦板尯', value: 'qingdao' },
-            { label: '鏃ョ収鍦板尯', value: 'rizhao' },
-            { label: '娴庡崡鍦板尯', value: 'jinan' },
-            { label: '鐑熷彴鍦板尯', value: 'yantai' },
-            { label: '濞佹捣鍦板尯', value: 'weihai' },
-            { label: '娼嶅潑鍦板尯', value: 'weifang' },
-            { label: '涓存矀鍦板尯', value: 'linyi' }
+            { label: '闈掑矝鍦板尯', value: '闈掑矝鍦板尯' },
+            { label: '鏃ョ収鍦板尯', value: '鏃ョ収鍦板尯' },
+            { label: '娴庡崡鍦板尯', value: '娴庡崡鍦板尯' },
+            { label: '鐑熷彴鍦板尯', value: '鐑熷彴鍦板尯' },
+            { label: '濞佹捣鍦板尯', value: '濞佹捣鍦板尯' },
+            { label: '娼嶅潑鍦板尯', value: '娼嶅潑鍦板尯' },
+            { label: '涓存矀鍦板尯', value: '涓存矀鍦板尯' }
           ],
           width: '300px'
         },
@@ -203,60 +137,19 @@
           width: '300px'
         },
         {
-          label: '骞撮緞娈�',
-          prop: 'ageRange',
-          type: 'select',
-          multiple: true,
-          options: [
-            { label: '<17宀�', value: '0-17' },
-            { label: '18-49宀�', value: '18-49' },
-            { label: '50-69宀�', value: '50-69' },
-            { label: '>69宀�', value: '70-100' }
-          ],
-          width: '200px'
-        },
-        {
-          label: '涓浗鍒嗙被',
-          prop: 'chinaCategory',
-          type: 'select',
-          multiple: true,
-          options: [
-            { label: '涓浗涓�绫�(DBD)', value: '1' },
-            { label: '涓浗浜岀被(DCD)', value: '2' },
-            { label: '涓浗涓夌被(DBCD)', value: '3' }
-          ],
-          width: '200px'
-        },
-        {
           label: '鍣ㄥ畼绫诲瀷',
           prop: 'organType',
           type: 'select',
           options: [
-            { label: '鍏ㄨ倽', value: 'liver' },
-            { label: '鑲捐剰', value: 'kidney' },
-            { label: '蹇冭剰', value: 'heart' },
-            { label: '鑲�', value: 'lung' },
-            { label: '鐪艰鑶�', value: 'cornea' }
+            { label: '鍏ㄨ倽', value: '鍏ㄨ倽' },
+            { label: '鑲捐剰', value: '鑲捐剰' },
+            { label: '蹇冭剰', value: '蹇冭剰' },
+            { label: '鑲�', value: '鑲�' },
+            { label: '鑳拌吅', value: '鑳拌吅' },
+            { label: '鐪艰鑶�', value: '鐪艰鑶�' }
           ],
           width: '200px'
         }
-      ],
-
-      // 鍣ㄥ畼閫夐」
-      organOptions: [
-        { label: '鍏ㄨ倽', value: 'liver' },
-        { label: '宸﹀崐鑲�', value: 'leftLiver' },
-        { label: '鍙冲崐鑲�', value: 'rightLiver' },
-        { label: '蹇冭剰', value: 'heart' },
-        { label: '灏忚偁', value: 'intestine' },
-        { label: '鑳拌吅', value: 'pancreas' },
-        { label: '宸﹁偩', value: 'leftKidney' },
-        { label: '鍙宠偩', value: 'rightKidney' },
-        { label: '鍏ㄨ偤', value: 'fullLung' },
-        { label: '宸﹁偤', value: 'leftLung' },
-        { label: '鍙宠偤', value: 'rightLung' },
-        { label: '宸︾溂瑙掕啘', value: 'leftCornea' },
-        { label: '鍙崇溂瑙掕啘', value: 'rightCornea' }
       ],
 
       // 鍔犺浇鐘舵��
@@ -264,322 +157,33 @@
 
       // 鍥捐〃閰嶇疆
       acquisitionChartOptions: {},
-      abandonChartOptions: {},
 
-      // 琛ㄦ牸鏁版嵁
-      tableData: [],
-      total: 0,
+      // 鍣ㄥ畼绫诲瀷鍒嗗竷鏁版嵁
+      organData: [],
 
-      // 寮冪敤鍒嗘瀽鏌ヨ鍙傛暟
-      abandonQuery: {
-        organType: null
-      },
-
-      // 姣忎緵浣撴煡璇㈠弬鏁�
-      perDonorQuery: {
-        year: '2024',
-        organType: null
-      },
-
-      // 妯℃嫙鏁版嵁
-      mockData: {
-        acquisition: [
-          {
-            month: '2024-01',
-            region: '闈掑矝鍦板尯',
-            liver: { acquisition: 8, transplant: 7 },
-            kidney: { acquisition: 12, transplant: 10 },
-            heart: { acquisition: 3, transplant: 3 },
-            lung: { acquisition: 5, transplant: 4 },
-            cornea: { acquisition: 20, transplant: 18 }
-          },
-          {
-            month: '2024-02',
-            region: '闈掑矝鍦板尯',
-            liver: { acquisition: 10, transplant: 9 },
-            kidney: { acquisition: 15, transplant: 13 },
-            heart: { acquisition: 4, transplant: 3 },
-            lung: { acquisition: 6, transplant: 5 },
-            cornea: { acquisition: 25, transplant: 22 }
-          },
-          {
-            month: '2024-01',
-            region: '鏃ョ収鍦板尯',
-            liver: { acquisition: 5, transplant: 4 },
-            kidney: { acquisition: 8, transplant: 7 },
-            heart: { acquisition: 2, transplant: 2 },
-            lung: { acquisition: 3, transplant: 2 },
-            cornea: { acquisition: 15, transplant: 13 }
-          }
-        ],
-        abandon: [
-          { name: '鍣ㄥ畼璐ㄩ噺涓嶅悎鏍�', value: 45 },
-          { name: '渚涗綋鐤剧梾', value: 25 },
-          { name: '杩愯緭闂', value: 15 },
-          { name: '鍙椾綋鍖归厤闂', value: 10 },
-          { name: '鍏朵粬鍘熷洜', value: 5 }
-        ],
-        table: [
-          {
-            id: 1,
-            year: '2024',
-            donorName: '寮犱笁',
-            acquisitionCount: 5,
-            liver: 1,
-            leftLiver: 0,
-            rightLiver: 0,
-            heart: 0,
-            intestine: 0,
-            pancreas: 0,
-            leftKidney: 1,
-            rightKidney: 1,
-            fullLung: 0,
-            leftLung: 0,
-            rightLung: 0,
-            leftCornea: 1,
-            rightCornea: 1,
-            region: '闈掑矝鍦板尯'
-          },
-          {
-            id: 2,
-            year: '2024',
-            donorName: '鏉庡洓',
-            acquisitionCount: 3,
-            liver: 0,
-            leftLiver: 0,
-            rightLiver: 0,
-            heart: 1,
-            intestine: 0,
-            pancreas: 0,
-            leftKidney: 0,
-            rightKidney: 0,
-            fullLung: 0,
-            leftLung: 1,
-            rightLung: 1,
-            leftCornea: 0,
-            rightCornea: 0,
-            region: '闈掑矝鍦板尯'
-          },
-          {
-            id: 3,
-            year: '2024',
-            donorName: '鐜嬩簲',
-            acquisitionCount: 6,
-            liver: 1,
-            leftLiver: 0,
-            rightLiver: 0,
-            heart: 0,
-            intestine: 0,
-            pancreas: 1,
-            leftKidney: 1,
-            rightKidney: 1,
-            fullLung: 0,
-            leftLung: 0,
-            rightLung: 0,
-            leftCornea: 1,
-            rightCornea: 1,
-            region: '鏃ョ収鍦板尯'
-          },
-          {
-            id: 4,
-            year: '2024',
-            donorName: '璧靛叚',
-            acquisitionCount: 4,
-            liver: 0,
-            leftLiver: 0,
-            rightLiver: 0,
-            heart: 0,
-            intestine: 0,
-            pancreas: 0,
-            leftKidney: 1,
-            rightKidney: 1,
-            fullLung: 0,
-            leftLung: 0,
-            rightLung: 0,
-            leftCornea: 1,
-            rightCornea: 1,
-            region: '娴庡崡鍦板尯'
-          },
-          {
-            id: 5,
-            year: '2023',
-            donorName: '閽变竷',
-            acquisitionCount: 3,
-            liver: 0,
-            leftLiver: 0,
-            rightLiver: 0,
-            heart: 1,
-            intestine: 0,
-            pancreas: 0,
-            leftKidney: 0,
-            rightKidney: 0,
-            fullLung: 1,
-            leftLung: 0,
-            rightLung: 1,
-            leftCornea: 0,
-            rightCornea: 0,
-            region: '鐑熷彴鍦板尯'
-          }
-        ]
-      }
+      // 鏃犳帴鍙i儴鍒嗙殑妯℃嫙鏁版嵁锛堜粎 mock 妯″紡灞曠ず锛�
+      abandonReasons: [
+        { name: '鍣ㄥ畼璐ㄩ噺涓嶅悎鏍�', value: 45 },
+        { name: '渚涗綋鐤剧梾', value: 25 },
+        { name: '杩愯緭闂', value: 15 },
+        { name: '鍙椾綋鍖归厤闂', value: 10 },
+        { name: '鍏朵粬鍘熷洜', value: 5 }
+      ],
+      perDonorTable: [
+        { id: 1, year: '2024', donorName: '寮犱笁', acquisitionCount: 5, liver: 1, leftLiver: 0, rightLiver: 0, heart: 0, intestine: 0, pancreas: 0, leftKidney: 1, rightKidney: 1, fullLung: 0, leftLung: 0, rightLung: 0, leftCornea: 1, rightCornea: 1, region: '闈掑矝鍦板尯' },
+        { id: 2, year: '2024', donorName: '鏉庡洓', acquisitionCount: 3, liver: 0, leftLiver: 0, rightLiver: 0, heart: 1, intestine: 0, pancreas: 0, leftKidney: 0, rightKidney: 0, fullLung: 0, leftLung: 1, rightLung: 1, leftCornea: 0, rightCornea: 0, region: '闈掑矝鍦板尯' },
+        { id: 3, year: '2024', donorName: '鐜嬩簲', acquisitionCount: 6, liver: 1, leftLiver: 0, rightLiver: 0, heart: 0, intestine: 0, pancreas: 1, leftKidney: 1, rightKidney: 1, fullLung: 0, leftLung: 0, rightLung: 0, leftCornea: 1, rightCornea: 1, region: '鏃ョ収鍦板尯' },
+        { id: 4, year: '2024', donorName: '璧靛叚', acquisitionCount: 4, liver: 0, leftLiver: 0, rightLiver: 0, heart: 0, intestine: 0, pancreas: 0, leftKidney: 1, rightKidney: 1, fullLung: 0, leftLung: 0, rightLung: 0, leftCornea: 1, rightCornea: 1, region: '娴庡崡鍦板尯' },
+        { id: 5, year: '2023', donorName: '閽变竷', acquisitionCount: 3, liver: 0, leftLiver: 0, rightLiver: 0, heart: 1, intestine: 0, pancreas: 0, leftKidney: 0, rightKidney: 0, fullLung: 1, leftLung: 0, rightLung: 1, leftCornea: 0, rightCornea: 0, region: '鐑熷彴鍦板尯' }
+      ]
     };
   },
-  created() {
-    this.loadData();
-  },
-  methods: {
-    // 鍔犺浇鏁版嵁
-    async loadData() {
-      this.loading = true;
-      try {
-        await new Promise(resolve => setTimeout(resolve, 500));
-
-        // 澶勭悊琛ㄦ牸鏁版嵁
-        let filteredData = [...this.mockData.table];
-
-        if (this.query.region.length > 0) {
-          filteredData = filteredData.filter(item =>
-            this.query.region.includes(item.region.replace('鍦板尯', '').toLowerCase())
-          );
-        }
-
-        if (this.query.monthRange && this.query.monthRange.length === 2) {
-          // 杩欓噷鍙互娣诲姞鏈堜唤绛涢�夐�昏緫
-        }
-
-        if (this.query.ageRange.length > 0) {
-          // 杩欓噷鍙互娣诲姞骞撮緞绛涢�夐�昏緫
-        }
-
-        if (this.query.chinaCategory.length > 0) {
-          // 杩欓噷鍙互娣诲姞涓浗鍒嗙被绛涢�夐�昏緫
-        }
-
-        if (this.query.organType) {
-          const organMap = { liver: 'liver', kidney: ['leftKidney', 'rightKidney'], heart: 'heart', lung: ['fullLung', 'leftLung', 'rightLung'], cornea: ['leftCornea', 'rightCornea'] };
-          const organProp = organMap[this.query.organType];
-          if (organProp) {
-            if (Array.isArray(organProp)) {
-              filteredData = filteredData.filter(item =>
-                organProp.some(prop => item[prop] > 0)
-              );
-            } else {
-              filteredData = filteredData.filter(item => item[organProp] > 0);
-            }
-          }
-        }
-
-        // 鍒嗛〉
-        const start = (this.query.page - 1) * this.query.limit;
-        this.tableData = filteredData.slice(start, start + this.query.limit);
-        this.total = filteredData.length;
-
-        // 鏇存柊鍥捐〃
-        this.updateCharts();
-
-      } catch (error) {
-        console.error('鍔犺浇鏁版嵁澶辫触:', error);
-        this.$message.error('鏁版嵁鍔犺浇澶辫触');
-      } finally {
-        this.loading = false;
-      }
+  computed: {
+    useMock() {
+      return isStatisticsMock();
     },
-
-    // 鏇存柊鍥捐〃
-    updateCharts() {
-      // 鑾峰彇绉绘缁熻鍥�
-      const regions = [...new Set(this.mockData.acquisition.map(item => item.region))];
-      const months = [...new Set(this.mockData.acquisition.map(item => item.month))];
-      const organTypes = ['liver', 'kidney', 'heart', 'lung', 'cornea'];
-      const organNames = ['鑲濊剰', '鑲捐剰', '蹇冭剰', '鑲�', '鐪艰鑶�'];
-
-      const seriesData = [];
-      const xAxisData = [];
-
-      // 鐢熸垚x杞存暟鎹�
-      months.forEach(month => {
-        regions.forEach(region => {
-          xAxisData.push(`${month}\n${region}`);
-        });
-      });
-
-      // 鐢熸垚绯诲垪鏁版嵁
-      organTypes.forEach((organType, index) => {
-        const data = [];
-        months.forEach(month => {
-          regions.forEach(region => {
-            const item = this.mockData.acquisition.find(d => d.month === month && d.region === region);
-            if (item) {
-              data.push(item[organType]?.acquisition || 0);
-            } else {
-              data.push(0);
-            }
-          });
-        });
-
-        seriesData.push({
-          name: `${organNames[index]}-鑾峰彇`,
-          type: 'bar',
-          stack: '鎬婚噺',
-          data: data,
-          itemStyle: this.getSeriesColor(index * 2)
-        });
-
-        const transplantData = [];
-        months.forEach(month => {
-          regions.forEach(region => {
-            const item = this.mockData.acquisition.find(d => d.month === month && d.region === region);
-            if (item) {
-              transplantData.push(item[organType]?.transplant || 0);
-            } else {
-              transplantData.push(0);
-            }
-          });
-        });
-
-        seriesData.push({
-          name: `${organNames[index]}-绉绘`,
-          type: 'bar',
-          stack: '鎬婚噺',
-          data: transplantData,
-          itemStyle: this.getSeriesColor(index * 2 + 1)
-        });
-      });
-
-      this.acquisitionChartOptions = {
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: {
-            type: 'shadow'
-          }
-        },
-        legend: {
-          data: seriesData.map(item => item.name),
-          top: 20
-        },
-        grid: {
-          left: '3%',
-          right: '4%',
-          bottom: '3%',
-          containLabel: true
-        },
-        xAxis: {
-          type: 'category',
-          data: xAxisData,
-          axisLabel: {
-            interval: 0,
-            rotate: 45
-          }
-        },
-        yAxis: {
-          type: 'value',
-          name: '鏁伴噺(涓�)'
-        },
-        series: seriesData
-      };
-
-      // 寮冪敤鍒嗘瀽鍥�
-      this.abandonChartOptions = {
+    abandonChartOptions() {
+      return {
         tooltip: {
           trigger: 'item',
           formatter: '{a} <br/>{b} : {c} ({d}%)'
@@ -587,7 +191,7 @@
         legend: {
           orient: 'vertical',
           left: 'left',
-          data: this.mockData.abandon.map(item => item.name)
+          data: this.abandonReasons.map(item => item.name)
         },
         series: [
           {
@@ -595,7 +199,7 @@
             type: 'pie',
             radius: '55%',
             center: ['50%', '60%'],
-            data: this.mockData.abandon,
+            data: this.abandonReasons,
             emphasis: {
               itemStyle: {
                 shadowBlur: 10,
@@ -606,18 +210,65 @@
           }
         ]
       };
+    }
+  },
+  created() {
+    this.loadData();
+    // 鏁版嵁婧�(mock/鐪熷疄鎺ュ彛)鍒囨崲鏃堕噸鏂板姞杞�
+    this.$watch(() => statisticsMode.useMock, () => this.loadData());
+  },
+  methods: {
+    // 缁勮鏌ヨ鏉′欢
+    buildQuery() {
+      const q = {};
+      if (this.query.region && this.query.region.length) {
+        q.area = this.query.region.join(',');
+      }
+      if (this.query.monthRange && this.query.monthRange.length === 2) {
+        q.startTime = this.query.monthRange[0];
+        q.endTime = this.query.monthRange[1];
+      }
+      if (this.query.organType) {
+        q.organName = this.query.organType;
+      }
+      return q;
     },
 
-    // 鑾峰彇绯诲垪棰滆壊
-    getSeriesColor(index) {
-      const colors = [
-        '#409EFF', '#8cc5ff',  // 鑲濊剰
-        '#67C23A', '#95d475',  // 鑲捐剰
-        '#E6A23C', '#f3c96b',  // 蹇冭剰
-        '#F56C6C', '#f89898',  // 鑲�
-        '#909399', '#b1b3b8'   // 鐪艰鑶�
-      ];
-      return { color: colors[index % colors.length] };
+    // 鍔犺浇鏁版嵁
+    async loadData() {
+      this.loading = true;
+      try {
+        const q = this.buildQuery();
+        const typeRes = await organType(q);
+
+        this.organData = toTotalsList(typeRes).map(item => ({
+          name: item.organName || '鏈煡',
+          acquisition: toNum(item.getCount),
+          transplant: toNum(item.useCount)
+        }));
+
+        this.updateCharts();
+      } catch (error) {
+        console.error('鍔犺浇鏁版嵁澶辫触:', error);
+        this.$message.error('鏁版嵁鍔犺浇澶辫触');
+      } finally {
+        this.loading = false;
+      }
+    },
+
+    // 鏇存柊鍥捐〃
+    updateCharts() {
+      this.acquisitionChartOptions = {
+        tooltip: axisTooltip(),
+        legend: legend(['鑾峰彇鏁伴噺', '绉绘鏁伴噺']),
+        grid: grid(),
+        xAxis: xAxis(this.organData.map(item => item.name)),
+        yAxis: yAxis('鏁伴噺(涓�)'),
+        series: [
+          barSeries('鑾峰彇鏁伴噺', this.organData.map(item => item.acquisition), COLORS.blue),
+          barSeries('绉绘鏁伴噺', this.organData.map(item => item.transplant), COLORS.green)
+        ]
+      };
     },
 
     // 鑾峰彇鏁伴噺鏍囩
@@ -638,8 +289,6 @@
       this.query = {
         region: [],
         monthRange: [],
-        ageRange: [],
-        chinaCategory: [],
         organType: null,
         page: 1,
         limit: 10
@@ -647,42 +296,16 @@
       this.loadData();
     },
 
-    // 澶勭悊鍒嗛〉
-    handlePagination(page) {
-      this.query.page = page;
-      this.loadData();
-    },
-
-    // 澶勭悊鍒嗛〉澶у皬鍙樺寲
-    handlePageSizeChange(size) {
-      this.query.limit = size;
-      this.query.page = 1;
-      this.loadData();
-    },
-
-    // 澶勭悊寮冪敤鏌ヨ
-    handleAbandonQuery() {
-      // 杩欓噷鍙互鏍规嵁abandonQuery绛涢�夋暟鎹�
-      console.log('寮冪敤鏌ヨ:', this.abandonQuery);
-    },
-
-    // 澶勭悊姣忎緵浣撴煡璇�
-    handlePerDonorQuery() {
-      this.loadData();
-    },
-
     // 澶勭悊瀵煎嚭
     handleExport() {
       this.$message.info('瀵煎嚭鍔熻兘寮�鍙戜腑...');
-    },
-
-    // 鏌ョ湅鎹愮尞鑰呰鎯�
-    viewDonorDetail(row) {
-      this.$router.push({
-        path: '/donor/detail',
-        query: { id: row.id }
-      });
     }
   }
 };
 </script>
+
+<style scoped>
+.statistics-page {
+  padding: 20px;
+}
+</style>
diff --git a/src/views/statistics/willingness.vue b/src/views/statistics/willingness.vue
index 20e469b..f300ad7 100644
--- a/src/views/statistics/willingness.vue
+++ b/src/views/statistics/willingness.vue
@@ -79,7 +79,6 @@
             {{ formatMonth(row.month) }}
           </template>
         </el-table-column>
-        <el-table-column label="鍣ㄥ畼鍚嶇О" prop="organName" align="center"  />
         <el-table-column label="妗堜緥鏁伴噺" prop="caseCount" align="center"  />
         <el-table-column label="鎹愮尞鏁伴噺" prop="donationCount" align="center"  />
         <el-table-column label="鎹愮尞鎰忔効" prop="willingnessRate" align="center" >
@@ -98,6 +97,18 @@
           </template>
         </el-table-column>
       </el-table>
+
+      <el-pagination
+        v-show="total > 0"
+        :total="total"
+        :page.sync="query.page"
+        :limit.sync="query.limit"
+        @current-change="handlePagination"
+        @size-change="handlePageSizeChange"
+        style="margin-top: 20px;"
+        layout="total, sizes, prev, pager, next, jumper"
+        :page-sizes="[10, 20, 50, 100]"
+      />
     </el-card>
   </div>
 </template>
@@ -105,7 +116,12 @@
 <script>
 import FilterPanel from '@/components/charts/FilterPanel.vue';
 import EChartsWrapper from '@/components/charts/EChartsWrapper.vue';
+import { caseTrend, caseTrendArea, caseTrendMoon } from '@/api/businessApi';
+import { toTotalsList, toMonth, toNum } from '@/utils/totals';
+import statisticsMode from '@/utils/statisticsMode';
+import { COLORS, axisTooltip, grid, xAxis, legend, barSeries, lineSeries } from '@/utils/chartStyle';
 
+// 鎹愮尞鎰忔効 = 瀹屾垚鎹愮尞 / 娼滃湪鎹愮尞锛岀粺涓�澶嶇敤 case 鐩稿叧鎺ュ彛鎺ㄥ
 export default {
   name: 'WillingnessStatistics',
   components: {
@@ -118,7 +134,6 @@
       query: {
         region: [],
         monthRange: [],
-        organType: null,
         page: 1,
         limit: 10
       },
@@ -131,13 +146,13 @@
           type: 'select',
           multiple: true,
           options: [
-            { label: '闈掑矝鍦板尯', value: 'qingdao' },
-            { label: '鏃ョ収鍦板尯', value: 'rizhao' },
-            { label: '娴庡崡鍦板尯', value: 'jinan' },
-            { label: '鐑熷彴鍦板尯', value: 'yantai' },
-            { label: '濞佹捣鍦板尯', value: 'weihai' },
-            { label: '娼嶅潑鍦板尯', value: 'weifang' },
-            { label: '涓存矀鍦板尯', value: 'linyi' }
+            { label: '闈掑矝鍦板尯', value: '闈掑矝鍦板尯' },
+            { label: '鏃ョ収鍦板尯', value: '鏃ョ収鍦板尯' },
+            { label: '娴庡崡鍦板尯', value: '娴庡崡鍦板尯' },
+            { label: '鐑熷彴鍦板尯', value: '鐑熷彴鍦板尯' },
+            { label: '濞佹捣鍦板尯', value: '濞佹捣鍦板尯' },
+            { label: '娼嶅潑鍦板尯', value: '娼嶅潑鍦板尯' },
+            { label: '涓存矀鍦板尯', value: '涓存矀鍦板尯' }
           ],
           width: '300px'
         },
@@ -150,20 +165,6 @@
           endPlaceholder: '缁撴潫鏈堜唤',
           valueFormat: 'yyyy-MM',
           width: '300px'
-        },
-        {
-          label: '鍣ㄥ畼绫诲瀷',
-          prop: 'organType',
-          type: 'select',
-          options: [
-            { label: '鍏ㄨ倽', value: 'liver' },
-            { label: '鑲捐剰', value: 'kidney' },
-            { label: '蹇冭剰', value: 'heart' },
-            { label: '鑲�', value: 'lung' },
-            { label: '鐪艰鑶�', value: 'cornea' },
-            { label: '澶氬櫒瀹�', value: 'multi' }
-          ],
-          width: '200px'
         }
       ],
 
@@ -172,92 +173,125 @@
 
       // 姒傝鏁版嵁
       overviewData: [
-        { title: '鎬绘渚嬫暟', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-flag', color: '#409EFF' },
-        { title: '鎬绘崘鐚暟閲�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-check', color: '#67C23A' },
-        { title: '骞冲潎鎹愮尞鎰忔効', value: '0%', desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-data', color: '#E6A23C' },
-        { title: '瓒嬪娍鍙樺寲', value: '0%', desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-trend', color: '#F56C6C' }
+        { title: '鎬绘渚嬫暟', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-flag', color: '#5B8FF9' },
+        { title: '鎬绘崘鐚暟閲�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-check', color: '#5AD8A6' },
+        { title: '骞冲潎鎹愮尞鎰忔効', value: '0%', desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-data', color: '#F6BD16' },
+        { title: '瓒嬪娍鍙樺寲', value: '0%', desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-trend', color: '#E8684A' }
       ],
 
       // 鍥捐〃閰嶇疆
       trendChartOptions: {},
       regionChartOptions: {},
 
+      // 鍥捐〃鏁版嵁
+      trendData: [],
+      regionData: [],
+
       // 琛ㄦ牸鏁版嵁
       tableData: [],
-      total: 0,
-
-      // 妯℃嫙鏁版嵁
-      mockData: {
-        table: [
-          { id: 1, region: '闈掑矝鍦板尯', month: '2024-01', organName: '鍏ㄨ倽', caseCount: 15, donationCount: 10, willingnessRate: 66.7, monthCompare: 2.5 },
-          { id: 2, region: '闈掑矝鍦板尯', month: '2024-01', organName: '鑲捐剰', caseCount: 20, donationCount: 16, willingnessRate: 80.0, monthCompare: 1.2 },
-          { id: 3, region: '鏃ョ収鍦板尯', month: '2024-01', organName: '鍏ㄨ倽', caseCount: 8, donationCount: 5, willingnessRate: 62.5, monthCompare: 0.8 },
-          { id: 4, region: '鏃ョ収鍦板尯', month: '2024-01', organName: '鐪艰鑶�', caseCount: 12, donationCount: 9, willingnessRate: 75.0, monthCompare: 3.1 },
-          { id: 5, region: '闈掑矝鍦板尯', month: '2024-02', organName: '鍏ㄨ倽', caseCount: 18, donationCount: 12, willingnessRate: 66.7, monthCompare: 0.0 },
-          { id: 6, region: '闈掑矝鍦板尯', month: '2024-02', organName: '蹇冭剰', caseCount: 5, donationCount: 4, willingnessRate: 80.0, monthCompare: 5.2 },
-          { id: 7, region: '娴庡崡鍦板尯', month: '2024-02', organName: '鍏ㄨ倽', caseCount: 25, donationCount: 20, willingnessRate: 80.0, monthCompare: 1.8 },
-          { id: 8, region: '娴庡崡鍦板尯', month: '2024-02', organName: '澶氬櫒瀹�', caseCount: 8, donationCount: 6, willingnessRate: 75.0, monthCompare: 2.3 }
-        ],
-        trend: [
-          { month: '2023-10', willingness: 72.5, cases: 80, donations: 58 },
-          { month: '2023-11', willingness: 73.8, cases: 85, donations: 63 },
-          { month: '2023-12', willingness: 75.2, cases: 90, donations: 68 },
-          { month: '2024-01', willingness: 76.8, cases: 95, donations: 73 },
-          { month: '2024-02', willingness: 78.3, cases: 100, donations: 78 },
-          { month: '2024-03', willingness: 79.1, cases: 105, donations: 83 }
-        ],
-        region: [
-          { region: '闈掑矝鍦板尯', willingness: 78.3, cases: 150, donations: 118 },
-          { region: '鏃ョ収鍦板尯', willingness: 72.5, cases: 80, donations: 58 },
-          { region: '娴庡崡鍦板尯', willingness: 82.6, cases: 200, donations: 165 },
-          { region: '鐑熷彴鍦板尯', willingness: 75.8, cases: 100, donations: 76 }
-        ]
-      }
+      total: 0
     };
   },
   created() {
     this.loadData();
+    // 鏁版嵁婧�(mock/鐪熷疄鎺ュ彛)鍒囨崲鏃堕噸鏂板姞杞�
+    this.$watch(() => statisticsMode.useMock, () => this.loadData());
   },
   methods: {
+    // 缁勮鏌ヨ鏉′欢
+    buildQuery() {
+      const q = {};
+      if (this.query.region && this.query.region.length) {
+        q.area = this.query.region.join(',');
+      }
+      if (this.query.monthRange && this.query.monthRange.length === 2) {
+        q.startTime = this.query.monthRange[0];
+        q.endTime = this.query.monthRange[1];
+      }
+      return q;
+    },
+
+    // 璁$畻鎰忔効鐜�
+    calcWillingness(potential, complete) {
+      const p = toNum(potential);
+      return p > 0 ? Number(((toNum(complete) / p) * 100).toFixed(1)) : 0;
+    },
+
     // 鍔犺浇鏁版嵁
     async loadData() {
       this.loading = true;
       try {
-        await new Promise(resolve => setTimeout(resolve, 500));
+        const q = this.buildQuery();
+        const [trendRes, areaRes, moonRes] = await Promise.all([
+          caseTrend(q),
+          caseTrendArea(q),
+          caseTrendMoon(q)
+        ]);
 
-        // 澶勭悊琛ㄦ牸鏁版嵁
-        let filteredData = [...this.mockData.table];
+        // 瓒嬪娍锛堟湀浠界淮搴︼級
+        this.trendData = toTotalsList(trendRes)
+          .map(item => {
+            const potential = toNum(item.potentialCount);
+            const complete = toNum(item.completeCount);
+            return {
+              month: toMonth(item.donateTime) || toMonth(item.month),
+              potential,
+              complete,
+              willingness: this.calcWillingness(potential, complete)
+            };
+          })
+          .sort((a, b) => String(a.month).localeCompare(String(b.month)));
 
-        if (this.query.region.length > 0) {
-          filteredData = filteredData.filter(item =>
-            this.query.region.includes(item.region.replace('鍦板尯', '').toLowerCase())
-          );
-        }
+        // 鍦板尯瀵规瘮
+        this.regionData = toTotalsList(areaRes).map(item => {
+          const potential = toNum(item.potentialCount);
+          const complete = toNum(item.completeCount);
+          return {
+            region: item.area || item.region || '',
+            potential,
+            complete,
+            willingness: this.calcWillingness(potential, complete)
+          };
+        });
 
-        if (this.query.monthRange && this.query.monthRange.length === 2) {
-          const [start, end] = this.query.monthRange;
-          filteredData = filteredData.filter(item =>
-            item.month >= start && item.month <= end
-          );
-        }
+        // 鏄庣粏琛紙鍦板尯 x 鏈堜唤锛�
+        const rows = toTotalsList(moonRes)
+          .map(item => {
+            const potential = toNum(item.potentialCount);
+            const complete = toNum(item.completeCount);
+            return {
+              region: item.area || item.region || '',
+              month: toMonth(item.donateTime) || toMonth(item.month),
+              potential,
+              complete
+            };
+          })
+          .sort((a, b) => String(a.month).localeCompare(String(b.month)));
 
-        if (this.query.organType) {
-          const organMap = { liver: '鑲�', kidney: '鑲�', heart: '蹇冭剰', lung: '鑲�', cornea: '鐪艰鑶�', multi: '澶氬櫒瀹�' };
-          const organName = organMap[this.query.organType];
-          if (organName) {
-            filteredData = filteredData.filter(item => item.organName.includes(organName));
+        // 璁$畻鐜瘮鍙樺寲锛堝悓涓�鍦板尯锛岀浉瀵逛笂涓�鏈堜唤鎰忔効鐜囧彉鍖栵紝鐧惧垎鐐癸級
+        rows.forEach(row => {
+          const willingness = this.calcWillingness(row.potential, row.complete);
+          // 璇ュ湴鍖烘寜鏈堜唤鎺掑簭鍚庣殑涓婁竴涓湀
+          const regionRows = rows.filter(r => r.region === row.region);
+          const idx = regionRows.indexOf(row);
+          let monthCompare = 0;
+          if (idx > 0) {
+            const prev = regionRows[idx - 1];
+            const prevWillingness = this.calcWillingness(prev.potential, prev.complete);
+            monthCompare = Number((willingness - prevWillingness).toFixed(1));
           }
-        }
+          row.willingnessRate = willingness;
+          row.caseCount = row.potential;
+          row.donationCount = row.complete;
+          row.monthCompare = monthCompare;
+        });
 
-        this.tableData = filteredData;
-        this.total = filteredData.length;
+        this.total = rows.length;
+        const start = (this.query.page - 1) * this.query.limit;
+        this.tableData = rows.slice(start, start + this.query.limit);
 
-        // 鏇存柊姒傝鏁版嵁
-        this.updateOverviewData(filteredData);
-
-        // 鏇存柊鍥捐〃
+        this.updateOverviewData(rows);
         this.updateCharts();
-
       } catch (error) {
         console.error('鍔犺浇鏁版嵁澶辫触:', error);
         this.$message.error('鏁版嵁鍔犺浇澶辫触');
@@ -267,20 +301,18 @@
     },
 
     // 鏇存柊姒傝鏁版嵁
-    updateOverviewData(data) {
-      if (data.length === 0) return;
+    updateOverviewData(rows) {
+      const totalCases = rows.reduce((sum, r) => sum + toNum(r.potential), 0);
+      const totalDonations = rows.reduce((sum, r) => sum + toNum(r.complete), 0);
+      const avgWillingness = this.calcWillingness(totalCases, totalDonations);
 
-      const totalCases = data.reduce((sum, item) => sum + item.caseCount, 0);
-      const totalDonations = data.reduce((sum, item) => sum + item.donationCount, 0);
-      const avgWillingness = data.length > 0
-        ? (data.reduce((sum, item) => sum + parseFloat(item.willingnessRate), 0) / data.length).toFixed(1)
-        : 0;
-
-      // 璁$畻瓒嬪娍鍙樺寲锛堝彇鏈�杩戜袱涓湀鐨勫钩鍧囧彉鍖栵級
-      const recentData = data.slice(-2);
-      const trend = recentData.length >= 2
-        ? ((recentData[1].willingnessRate - recentData[0].willingnessRate) / recentData[0].willingnessRate * 100).toFixed(1)
-        : 0;
+      // 瓒嬪娍鍙樺寲锛氭渶杩戜袱涓湀鎰忔効鐜囧樊鍊�
+      let trend = 0;
+      if (this.trendData.length >= 2) {
+        const last = this.trendData[this.trendData.length - 1].willingness;
+        const prev = this.trendData[this.trendData.length - 2].willingness;
+        trend = Number((last - prev).toFixed(1));
+      }
 
       this.overviewData[0].value = totalCases;
       this.overviewData[1].value = totalDonations;
@@ -290,139 +322,55 @@
 
     // 鏇存柊鍥捐〃
     updateCharts() {
-      // 瓒嬪娍鍥�
+      const dualAxis = [
+        {
+          type: 'value',
+          name: '鎰忔効鐜�(%)',
+          nameTextStyle: { color: '#b0b6c3', fontSize: 12 },
+          axisLabel: { color: '#8a919f', fontSize: 12, formatter: '{value}%' },
+          splitLine: { lineStyle: { type: 'dashed', color: '#f0f1f5' } }
+        },
+        {
+          type: 'value',
+          name: '鏁伴噺(涓�)',
+          nameTextStyle: { color: '#b0b6c3', fontSize: 12 },
+          axisLabel: { color: '#8a919f', fontSize: 12 },
+          splitLine: { show: false }
+        }
+      ];
+
+      // 瓒嬪娍鍥撅紙鎰忔効鐜囨姌绾� + 鏁伴噺鏌辩姸锛屽弻杞达級
       this.trendChartOptions = {
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: {
-            type: 'cross',
-            label: {
-              backgroundColor: '#6a7985'
-            }
-          }
-        },
-        legend: {
-          data: ['鎹愮尞鎰忔効', '妗堜緥鏁伴噺', '鎹愮尞鏁伴噺']
-        },
-        grid: {
-          left: '3%',
-          right: '4%',
-          bottom: '3%',
-          containLabel: true
-        },
+        tooltip: axisTooltip(),
+        legend: legend(['鎹愮尞鎰忔効', '妗堜緥鏁伴噺', '鎹愮尞鏁伴噺']),
+        grid: grid(),
         xAxis: {
           type: 'category',
-          boundaryGap: false,
-          data: this.mockData.trend.map(item => item.month.replace('-', '骞�') + '鏈�')
+          boundaryGap: true,
+          data: this.trendData.map(item => item.month),
+          axisLine: { show: false },
+          axisTick: { show: false },
+          axisLabel: { color: '#8a919f', fontSize: 12 }
         },
-        yAxis: [
-          {
-            type: 'value',
-            name: '鎰忔効鐜�(%)',
-            min: 60,
-            max: 90,
-            axisLabel: {
-              formatter: '{value}%'
-            }
-          },
-          {
-            type: 'value',
-            name: '鏁伴噺(涓�)',
-            position: 'right'
-          }
-        ],
+        yAxis: dualAxis,
         series: [
-          {
-            name: '鎹愮尞鎰忔効',
-            type: 'line',
-            smooth: true,
-            data: this.mockData.trend.map(item => item.willingness),
-            itemStyle: { color: '#409EFF' },
-            markLine: {
-              data: [
-                { yAxis: 75, name: '鐩爣绾�' }
-              ],
-              lineStyle: {
-                type: 'dashed',
-                color: '#F56C6C'
-              }
-            }
-          },
-          {
-            name: '妗堜緥鏁伴噺',
-            type: 'bar',
-            yAxisIndex: 1,
-            data: this.mockData.trend.map(item => item.cases),
-            itemStyle: { color: '#67C23A' }
-          },
-          {
-            name: '鎹愮尞鏁伴噺',
-            type: 'bar',
-            yAxisIndex: 1,
-            data: this.mockData.trend.map(item => item.donations),
-            itemStyle: { color: '#E6A23C' }
-          }
+          lineSeries('鎹愮尞鎰忔効', this.trendData.map(item => item.willingness), COLORS.blue, { area: true }),
+          barSeries('妗堜緥鏁伴噺', this.trendData.map(item => item.potential), COLORS.green, { yAxisIndex: 1 }),
+          barSeries('鎹愮尞鏁伴噺', this.trendData.map(item => item.complete), COLORS.amber, { yAxisIndex: 1 })
         ]
       };
 
       // 鍦板尯瀵规瘮鍥�
       this.regionChartOptions = {
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: {
-            type: 'shadow'
-          }
-        },
-        legend: {
-          data: ['鎹愮尞鎰忔効', '妗堜緥鏁伴噺', '鎹愮尞鏁伴噺']
-        },
-        grid: {
-          left: '3%',
-          right: '4%',
-          bottom: '3%',
-          containLabel: true
-        },
-        xAxis: {
-          type: 'category',
-          data: this.mockData.region.map(item => item.region)
-        },
-        yAxis: [
-          {
-            type: 'value',
-            name: '鎰忔効鐜�(%)',
-            min: 60,
-            max: 90,
-            axisLabel: {
-              formatter: '{value}%'
-            }
-          },
-          {
-            type: 'value',
-            name: '鏁伴噺(涓�)',
-            position: 'right'
-          }
-        ],
+        tooltip: axisTooltip(),
+        legend: legend(['鎹愮尞鎰忔効', '妗堜緥鏁伴噺', '鎹愮尞鏁伴噺']),
+        grid: grid(),
+        xAxis: xAxis(this.regionData.map(item => item.region)),
+        yAxis: dualAxis,
         series: [
-          {
-            name: '鎹愮尞鎰忔効',
-            type: 'bar',
-            data: this.mockData.region.map(item => item.willingness),
-            itemStyle: { color: '#409EFF' }
-          },
-          {
-            name: '妗堜緥鏁伴噺',
-            type: 'bar',
-            yAxisIndex: 1,
-            data: this.mockData.region.map(item => item.cases),
-            itemStyle: { color: '#67C23A' }
-          },
-          {
-            name: '鎹愮尞鏁伴噺',
-            type: 'bar',
-            yAxisIndex: 1,
-            data: this.mockData.region.map(item => item.donations),
-            itemStyle: { color: '#E6A23C' }
-          }
+          barSeries('鎹愮尞鎰忔効', this.regionData.map(item => item.willingness), COLORS.blue),
+          barSeries('妗堜緥鏁伴噺', this.regionData.map(item => item.potential), COLORS.green, { yAxisIndex: 1 }),
+          barSeries('鎹愮尞鏁伴噺', this.regionData.map(item => item.complete), COLORS.amber, { yAxisIndex: 1 })
         ]
       };
     },
@@ -435,9 +383,9 @@
 
     // 鑾峰彇杩涘害鏉¢鑹�
     getProgressColor(percentage) {
-      if (percentage >= 80) return '#67C23A';
-      if (percentage >= 70) return '#E6A23C';
-      return '#F56C6C';
+      if (percentage >= 80) return COLORS.green;
+      if (percentage >= 70) return COLORS.amber;
+      return COLORS.coral;
     },
 
     // 鑾峰彇姣旇緝鏍囩绫诲瀷
@@ -458,10 +406,22 @@
       this.query = {
         region: [],
         monthRange: [],
-        organType: null,
         page: 1,
         limit: 10
       };
+      this.loadData();
+    },
+
+    // 澶勭悊鍒嗛〉
+    handlePagination(page) {
+      this.query.page = page;
+      this.loadData();
+    },
+
+    // 澶勭悊鍒嗛〉澶у皬鍙樺寲
+    handlePageSizeChange(size) {
+      this.query.limit = size;
+      this.query.page = 1;
       this.loadData();
     },
 
@@ -472,3 +432,63 @@
   }
 };
 </script>
+
+<style scoped>
+.statistics-page {
+  padding: 20px;
+}
+
+.overview-row {
+  margin-bottom: 20px;
+}
+
+.overview-card {
+  height: 100%;
+}
+
+.overview-content {
+  display: flex;
+  align-items: center;
+}
+
+.overview-icon {
+  width: 50px;
+  height: 50px;
+  border-radius: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 15px;
+}
+
+.overview-icon i {
+  font-size: 24px;
+  color: white;
+}
+
+.overview-info {
+  flex: 1;
+}
+
+.overview-title {
+  font-size: 14px;
+  color: #909399;
+  margin-bottom: 4px;
+}
+
+.overview-value {
+  font-size: 24px;
+  font-weight: bold;
+  color: #303133;
+  margin-bottom: 4px;
+}
+
+.overview-desc {
+  font-size: 12px;
+  color: #C0C4CC;
+}
+
+.chart-row {
+  margin-bottom: 20px;
+}
+</style>
diff --git "a/\347\256\241\347\220\206\347\253\257.zip" "b/\347\256\241\347\220\206\347\253\257.zip"
deleted file mode 100644
index fbf1609..0000000
--- "a/\347\256\241\347\220\206\347\253\257.zip"
+++ /dev/null
Binary files differ
diff --git "a/\347\256\241\347\220\206\347\253\2577.20.zip" "b/\347\256\241\347\220\206\347\253\2577.20.zip"
deleted file mode 100644
index d3cc747..0000000
--- "a/\347\256\241\347\220\206\347\253\2577.20.zip"
+++ /dev/null
Binary files differ
diff --git "a/\347\256\241\347\220\206\347\253\2578.20.zip" "b/\347\256\241\347\220\206\347\253\2578.20.zip"
deleted file mode 100644
index a17503e..0000000
--- "a/\347\256\241\347\220\206\347\253\2578.20.zip"
+++ /dev/null
Binary files differ
diff --git "a/\347\256\241\347\220\206\347\253\2578.3.zip" "b/\347\256\241\347\220\206\347\253\2578.3.zip"
deleted file mode 100644
index 5865108..0000000
--- "a/\347\256\241\347\220\206\347\253\2578.3.zip"
+++ /dev/null
Binary files differ

--
Gitblit v1.9.3