From 73d112ddc9bdfeb7eaaf3b7308cfb4dbeb470c89 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期五, 07 八月 2026 17:28:10 +0800
Subject: [PATCH] 天津_意博凯信 1.备件管理-新增独立备件出库功能入口,支持直接完成备件领用出库操作。 2.设备保养-支持按日期分组折叠展示,分组条目可展开查看明细;单条明细行末尾增加【保养完成】操作按钮,可单独办结单条保养任务;折叠汇总状态下,新增【一键保养完成】批量操作按钮,快速办结当前分组全部保养项。 3.设备巡检-按日期分组折叠展示,分组支持展开查看明细;每条巡检明细后增设【巡检完成】按钮,支持单独办结单条巡检记录;分组折叠未展开状态下,提供【一键巡检完成】批量操作,统一办结该分组下所有巡检任务。 4.增值税对比-优化数据展示逻辑,列表展示各订单增值税明细;页面右侧新增柱状可视化图表,直观对比进销项增值税数据。 5.管理驾驶舱-指标修改:将顶部「销售产品数」调整为「销售订单数」;时间筛选:页面左上角新增日期范围下拉筛选控件,所有统计指标标注当前筛选时段;

---
 src/views/equipmentManagement/upkeep/index.vue                           | 1282 ++++++-----
 src/views/equipmentManagement/spareParts/index.vue                       |  931 +++++---
 src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue         |  306 +-
 src/api/procurementManagement/taxComparison.js                           |   20 
 src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue    |  550 ++--
 src/api/equipmentManagement/upkeep.js                                    |   17 
 src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue |  510 ++--
 src/views/equipmentManagement/inspectionManagement/index.vue             |  741 +++---
 src/views/reportAnalysis/PSIDataAnalysis/index.vue                       |  458 ++-
 src/api/equipmentManagement/sparePartsUsage.js                           |    9 
 src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue      |  356 +-
 src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue    |  263 +-
 src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue       |  161 
 src/api/inspectionManagement/index.js                                    |   17 
 src/views/reportAnalysis/taxComparison/index.vue                         |  491 +++
 src/api/viewIndex.js                                                     |   22 
 src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue    |  190 
 17 files changed, 3,443 insertions(+), 2,881 deletions(-)

diff --git a/src/api/equipmentManagement/sparePartsUsage.js b/src/api/equipmentManagement/sparePartsUsage.js
index e9384aa..9befc6c 100644
--- a/src/api/equipmentManagement/sparePartsUsage.js
+++ b/src/api/equipmentManagement/sparePartsUsage.js
@@ -34,3 +34,12 @@
   });
 };
 
+// 澶囦欢鐩存帴鍑哄簱
+export function outboundSpareParts(data) {
+  return request({
+    url: '/sparePartsRequisitionRecord/outbound',
+    method: 'post',
+    data
+  })
+}
+
diff --git a/src/api/equipmentManagement/upkeep.js b/src/api/equipmentManagement/upkeep.js
index 234d2a5..5fa070a 100644
--- a/src/api/equipmentManagement/upkeep.js
+++ b/src/api/equipmentManagement/upkeep.js
@@ -94,6 +94,23 @@
     params: params,
   });
 };
+// 鍗曟潯淇濆吇瀹屾垚
+export function completeMaintenance(id) {
+  return request({
+    url: '/device/maintenance/complete/' + id,
+    method: 'put'
+  })
+}
+
+// 涓�閿繚鍏诲畬鎴�
+export function batchCompleteMaintenance(dateStr) {
+  return request({
+    url: '/device/maintenance/batchComplete',
+    method: 'put',
+    params: { dateStr }
+  })
+}
+
 // 璁惧淇濆吇瀹氭椂浠诲姟鍒楄〃
 export const deviceMaintenanceTaskDel = (params) => {
   return request({
diff --git a/src/api/inspectionManagement/index.js b/src/api/inspectionManagement/index.js
index d0c444a..7fc1256 100644
--- a/src/api/inspectionManagement/index.js
+++ b/src/api/inspectionManagement/index.js
@@ -58,4 +58,21 @@
         method: 'post',
         data: query
     })
+}
+
+// 鍗曟潯宸℃瀹屾垚
+export function completeInspection(id) {
+    return request({
+        url: '/inspectionTask/complete/' + id,
+        method: 'put'
+    })
+}
+
+// 涓�閿贰妫�瀹屾垚
+export function batchCompleteInspection(dateStr) {
+    return request({
+        url: '/inspectionTask/batchComplete',
+        method: 'put',
+        params: { dateStr }
+    })
 }
\ No newline at end of file
diff --git a/src/api/procurementManagement/taxComparison.js b/src/api/procurementManagement/taxComparison.js
index 726a27f..750bccb 100644
--- a/src/api/procurementManagement/taxComparison.js
+++ b/src/api/procurementManagement/taxComparison.js
@@ -1,6 +1,6 @@
 import request from "@/utils/request";
 
-// 鍒嗛〉鏌ヨ
+// 鍒嗛〉鏌ヨ锛堟柊澧� type 鍙傛暟锛�
 export function getTaxList(query) {
   return request({
     url: "/purchase/report/listVat",
@@ -8,3 +8,21 @@
     params: query,
   });
 }
+
+// 澧炲�肩◣杩涢攢椤瑰浘琛ㄦ暟鎹�
+export function getVatChart(year) {
+  return request({
+    url: '/purchase/report/vatChart',
+    method: 'get',
+    params: { year }
+  })
+}
+
+// 澧炲�肩◣鏄庣粏
+export function getVatDetail(query) {
+  return request({
+    url: '/purchase/report/listVatDetail',
+    method: 'get',
+    params: query
+  })
+}
diff --git a/src/api/viewIndex.js b/src/api/viewIndex.js
index 0087d66..2bba3ad 100644
--- a/src/api/viewIndex.js
+++ b/src/api/viewIndex.js
@@ -202,20 +202,20 @@
 };
 
 // 渚涘簲鍟嗛噰璐帓鍚�
-export const supplierPurchaseRanking = (query) => {
+export const supplierPurchaseRanking = (params) => {
   return request({
     url: "/home/supplierPurchaseRanking",
     method: "get",
-    params: query,
+    params,
   });
 };
 
 // 瀹㈡埛閲戦璐$尞鎺掑悕
-export const customerContributionRanking = (query) => {
+export const customerContributionRanking = (params) => {
   return request({
     url: "/home/customerContributionRanking",
     method: "get",
-    params: query,
+    params,
   });
 };
 
@@ -228,10 +228,11 @@
 };
 
 // 浜у搧閿�鍞噾棰濆垎鏋�
-export const productSalesAnalysis = () => {
+export const productSalesAnalysis = (params) => {
   return request({
     url: "/home/productSalesAnalysis",
     method: "get",
+    params,
   });
 };
 
@@ -245,10 +246,11 @@
 };
 
 // 鍘熸潗鏂欓噰璐噾棰濆崰姣�
-export const rawMaterialPurchaseAmountRatio = () => {
+export const rawMaterialPurchaseAmountRatio = (params) => {
   return request({
     url: "/home/rawMaterialPurchaseAmountRatio",
     method: "get",
+    params,
   });
 };
 
@@ -262,10 +264,11 @@
 };
 
 // 閿�鍞�/閲囪喘/鍌ㄥ瓨浜у搧鏁�
-export const salesPurchaseStorageProductCount = () => {
+export const salesPurchaseStorageProductCount = (startDate, endDate) => {
   return request({
     url: "/home/salesPurchaseStorageProductCount",
     method: "get",
+    params: { startDate, endDate },
   });
 };
 
@@ -288,10 +291,11 @@
 };
 
 // 浜у搧鍛ㄨ浆澶╂暟
-export const productTurnoverDays = () => {
+export const productTurnoverDays = (params) => {
   return request({
     url: "/home/productTurnoverDays",
     method: "get",
+    params,
   });
 };
 
@@ -349,7 +353,7 @@
 
 export const productionOrderProgress = (params = {}) => {
   const safePageNum = Math.max(1, Number(params.pageNum || 1));
-  const safeTab = ["all", "inProgress", "completed", "end"].includes(params.tab)
+  const safeTab = ["all", "inProgress", "completed", "paused"].includes(params.tab)
     ? params.tab
     : "all";
   return request({
diff --git a/src/views/equipmentManagement/inspectionManagement/index.vue b/src/views/equipmentManagement/inspectionManagement/index.vue
index 8c0d52c..e67f497 100644
--- a/src/views/equipmentManagement/inspectionManagement/index.vue
+++ b/src/views/equipmentManagement/inspectionManagement/index.vue
@@ -1,449 +1,366 @@
 <template>
-  <div class="app-container">
-    <el-form :inline="true"
-             :model="queryParams"
-             class="search-form">
-      <el-form-item label="宸℃浠诲姟鍚嶇О">
-        <el-input v-model="queryParams.taskName"
-                  placeholder="璇疯緭鍏ュ贰妫�浠诲姟鍚嶇О"
-                  clearable
-                  style="width: 200px " />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary"
-                   @click="handleQuery">鏌ヨ</el-button>
-        <el-button @click="resetQuery">閲嶇疆</el-button>
-      </el-form-item>
-    </el-form>
-    <el-card>
-      <div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom: 10px;">
-        <el-radio-group v-model="activeRadio"
-                        @change="radioChange">
-          <el-radio-button v-for="tab in radios"
-                           :key="tab.name"
-                           :label="tab.label"
-                           :value="tab.name" />
-        </el-radio-group>
-        <!-- 鎿嶄綔鎸夐挳鍖� -->
-        <el-space v-if="activeRadio !== 'task'">
-          <el-button type="primary"
-                     :icon="Plus"
-                     @click="handleAdd(undefined)">鏂板缓</el-button>
-          <el-button type="danger"
-                     :icon="Delete"
-                     @click="handleDelete">鍒犻櫎</el-button>
-          <el-button @click="handleOut">瀵煎嚭</el-button>
-        </el-space>
-        <el-space v-else>
-          <el-button @click="handleOut">瀵煎嚭</el-button>
-        </el-space>
-      </div>
-      <div>
-        <PIMTable :table-loading="tableLoading"
-                  :table-data="tableData"
-                  :column="tableColumns"
-                  @selection-change="handleSelectionChange"
-                  @pagination="handlePagination"
-                  :is-selection="true"
-                  :border="true"
-                  :page="{
+	<div class="app-container">
+		<el-form :inline="true"
+		         :model="queryParams"
+		         class="search-form">
+			<el-form-item label="宸℃浠诲姟鍚嶇О">
+				<el-input v-model="queryParams.taskName"
+				          placeholder="璇疯緭鍏ュ贰妫�浠诲姟鍚嶇О"
+				          clearable
+				          style="width: 200px " />
+			</el-form-item>
+			<el-form-item>
+				<el-button type="primary"
+				           @click="handleQuery">鏌ヨ</el-button>
+				<el-button @click="resetQuery">閲嶇疆</el-button>
+			</el-form-item>
+		</el-form>
+		<el-card>
+			<div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom: 10px;">
+				<el-radio-group v-model="activeRadio"
+				                @change="radioChange">
+					<el-radio-button v-for="tab in radios"
+					                 :key="tab.name"
+					                 :label="tab.label"
+					                 :value="tab.name" />
+				</el-radio-group>
+				<el-space v-if="activeRadio !== 'task'">
+					<el-button type="primary"
+					           :icon="Plus"
+					           @click="handleAdd(undefined)">鏂板缓</el-button>
+					<el-button type="danger"
+					           :icon="Delete"
+					           @click="handleDelete">鍒犻櫎</el-button>
+					<el-button @click="handleOut">瀵煎嚭</el-button>
+				</el-space>
+				<el-space v-else>
+					<el-button @click="handleOut">瀵煎嚭</el-button>
+				</el-space>
+			</div>
+			
+			<!-- 宸℃浠诲姟 -->
+			<div v-show="activeRadio === 'taskManage'">
+				<PIMTable :table-loading="tableLoading"
+				          :table-data="tableData"
+				          :column="tableColumns"
+				          @selection-change="handleSelectionChange"
+				          @pagination="handlePagination"
+				          :is-selection="true"
+				          :border="true"
+				          :page="{
                   current: pageNum,
                   size: pageSize,
                   total: total,
                   layout: 'total, sizes, prev, pager, next, jumper'
                 }"
-                  height="calc(100vh - 23em)"
-                  :table-style="{ width: '100%' }">
-          <template #inspector="{ row }">
-            <div class="person-tags">
-              <!-- 璋冭瘯淇℃伅锛屼笂绾挎椂鍒犻櫎 -->
-              <!-- {{ console.log('inspector data:', row.inspector) }} -->
-              <template v-if="row.inspector && row.inspector.length > 0">
-                <el-tag v-for="(person, index) in row.inspector"
-                        :key="index"
-                        size="small"
-                        type="primary"
-                        class="person-tag">
-                  {{ person }}
-                </el-tag>
-              </template>
-              <span v-else
-                    class="no-data">--</span>
-            </div>
-          </template>
-          <template #isEnabled="{ row }">
-            <el-tag :type="row.isEnabled === 1 ? 'success' : 'danger'"
-                    size="small">
-              {{ row.isEnabled == 1 ? '鏄�' : '鍚�' }}
-            </el-tag>
-          </template>
-        </PIMTable>
-      </div>
-    </el-card>
-    <form-dia ref="formDia"
-              @closeDia="handleQuery"></form-dia>
-    <view-files ref="viewFiles"></view-files>
-    <upload-files ref="uploadFiles"
-                  @success="handleQuery"
-                  @closeDia="handleQuery"></upload-files>
-  </div>
+				          height="calc(100vh - 23em)"
+				          :table-style="{ width: '100%' }">
+					<template #inspector="{ row }">
+						<div class="person-tags">
+							<template v-if="row.inspector && row.inspector.length > 0">
+								<el-tag v-for="(person, index) in row.inspector"
+								        :key="index"
+								        size="small"
+								        type="primary"
+								        class="person-tag">
+									{{ person }}
+								</el-tag>
+							</template>
+							<span v-else class="no-data">--</span>
+						</div>
+					</template>
+					<template #isEnabled="{ row }">
+						<el-tag :type="row.isEnabled === 1 ? 'success' : 'danger'"
+						        size="small">
+							{{ row.isEnabled == 1 ? '鏄�' : '鍚�' }}
+						</el-tag>
+					</template>
+				</PIMTable>
+			</div>
+			
+			<!-- 宸℃璁板綍 - 鏃ユ湡鍒嗙粍鎶樺彔 -->
+			<div v-show="activeRadio === 'task'">
+				<el-table
+					:data="groupedInspectionList"
+					border
+					v-loading="tableLoading"
+					:expand-row-keys="inspectionExpandedRowKeys"
+					:row-key="(row) => row.date"
+					@expand-change="onInspectionExpandChange"
+					height="calc(100vh - 23em)"
+				>
+					<el-table-column type="expand">
+						<template #default="props">
+							<el-table :data="props.row.children" border row-key="id">
+								<el-table-column label="宸℃浠诲姟鍚嶇О" prop="taskName" align="center" min-width="180" />
+								<el-table-column label="宸℃椤圭洰" prop="inspectionProject" align="center" min-width="160" />
+								<el-table-column label="宸℃浜�" align="center" min-width="150">
+									<template #default="{ row }">
+										<template v-if="row.inspector && row.inspector.length > 0">
+											<el-tag v-for="(p, i) in row.inspector" :key="i" size="small" type="primary" class="person-tag">{{ p }}</el-tag>
+										</template>
+										<span v-else>--</span>
+									</template>
+								</el-table-column>
+								<el-table-column label="宸℃鐘舵��" align="center" width="100">
+									<template #default="{ row }">
+										<el-tag :type="row.status === 1 ? 'success' : 'warning'" size="small">
+											{{ row.status === 1 ? '宸插畬鎴�' : '寰呭贰妫�' }}
+										</el-tag>
+									</template>
+								</el-table-column>
+								<el-table-column label="宸℃缁撴灉" prop="inspectionResult" align="center" min-width="120" />
+								<el-table-column label="澶囨敞" prop="remarks" align="center" min-width="150" />
+								<el-table-column label="寮傚父鎻忚堪" prop="abnormalDescription" align="center" min-width="150" />
+								<el-table-column label="鎿嶄綔" align="center" width="200">
+									<template #default="{ row }">
+										<el-button type="primary" link @click="openUploadDialog(row)">涓婁紶</el-button>
+										<el-button type="success" link @click="viewFile(row)">鏌ョ湅闄勪欢</el-button>
+										<el-button type="warning" link v-if="row.status === 0" @click="handleSingleComplete(row)">宸℃瀹屾垚</el-button>
+									</template>
+								</el-table-column>
+							</el-table>
+						</template>
+					</el-table-column>
+					<el-table-column label="宸℃鏃ユ湡" prop="date" align="center" />
+					<el-table-column label="宸℃璁板綍鏁�" prop="count" align="center" />
+					<el-table-column label="寰呭贰妫�鏁�" align="center" width="120">
+						<template #default="{ row }">
+							{{ row.children.filter(c => c.status === 0).length }}
+						</template>
+					</el-table-column>
+					<el-table-column label="鎿嶄綔" align="center" width="200">
+						<template #default="{ row }">
+							<el-button
+								type="primary"
+								size="small"
+								:disabled="!row.children.some(c => c.status === 0)"
+								@click="handleBatchInspection(row.date)"
+							>
+								涓�閿贰妫�瀹屾垚
+							</el-button>
+						</template>
+					</el-table-column>
+				</el-table>
+				<Pagination
+					v-show="total > 0"
+					:total="total"
+					layout="total, sizes, prev, pager, next, jumper"
+					:page="pageNum"
+					:limit="pageSize"
+					@pagination="handlePagination"
+				/>
+			</div>
+		</el-card>
+		<form-dia ref="formDia"
+		          @closeDia="handleQuery"></form-dia>
+		<view-files ref="viewFiles"></view-files>
+		<upload-files ref="uploadFiles"
+		              @success="handleQuery"
+		              @closeDia="handleQuery"></upload-files>
+	
+	</div>
 </template>
 
 <script setup>
-  import { Delete, Plus } from "@element-plus/icons-vue";
-  import { onMounted, ref, reactive, getCurrentInstance, nextTick } from "vue";
-  import { ElMessageBox } from "element-plus";
-  import dayjs from "dayjs";
+import { Delete, Plus } from "@element-plus/icons-vue";
+import { onMounted, ref, reactive, getCurrentInstance, nextTick, computed } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import dayjs from "dayjs";
 
-  // 缁勪欢寮曞叆
-  import PIMTable from "@/components/PIMTable/PIMTable.vue";
-  import FormDia from "@/views/equipmentManagement/inspectionManagement/components/formDia.vue";
-  import UploadFiles from "@/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue";
-  import ViewFiles from "@/views/equipmentManagement/inspectionManagement/components/viewFiles.vue";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import FormDia from "@/views/equipmentManagement/inspectionManagement/components/formDia.vue";
+import UploadFiles from "@/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue";
+import ViewFiles from "@/views/equipmentManagement/inspectionManagement/components/viewFiles.vue";
 
-  // 鎺ュ彛寮曞叆
-  import {
-    delTimingTask,
-    inspectionTaskList,
-    timingTaskList,
-  } from "@/api/inspectionManagement/index.js";
+import {
+	delTimingTask,
+	inspectionTaskList,
+	timingTaskList,
+	completeInspection,
+	batchCompleteInspection,
+} from "@/api/inspectionManagement/index.js";
+import Pagination from "@/components/PIMTable/Pagination.vue";
 
-  // 鍏ㄥ眬鍙橀噺
-  const { proxy } = getCurrentInstance();
-  const formDia = ref();
-  const viewFiles = ref();
-  const uploadFiles = ref();
+const { proxy } = getCurrentInstance();
+const formDia = ref();
+const viewFiles = ref();
+const uploadFiles = ref();
 
-  // 鏌ヨ鍙傛暟
-  const queryParams = reactive({
-    taskName: "",
-  });
+const queryParams = reactive({ taskName: "" });
 
-  // 鍗曢�夋閰嶇疆
-  const activeRadio = ref("taskManage");
-  const radios = reactive([
-    { name: "taskManage", label: "宸℃浠诲姟" },
-    { name: "task", label: "宸℃璁板綍" },
-  ]);
+const activeRadio = ref("taskManage");
+const radios = reactive([
+	{ name: "taskManage", label: "宸℃浠诲姟" },
+	{ name: "task", label: "宸℃璁板綍" },
+]);
 
-  // 琛ㄦ牸鏁版嵁
-  const selectedRows = ref([]);
-  const tableData = ref([]);
-  const operationsArr = ref([]);
-  const tableColumns = ref([]);
-  const tableLoading = ref(false);
-  const total = ref(0);
-  const pageNum = ref(1);
-  const pageSize = ref(10);
+const selectedRows = ref([]);
+const tableData = ref([]);
+const operationsArr = ref([]);
+const tableColumns = ref([]);
+const tableLoading = ref(false);
+const total = ref(0);
+const pageNum = ref(1);
+const pageSize = ref(100);
 
-  // 鍒楅厤缃�
-  const columns = ref([
-    { prop: "taskName", label: "宸℃浠诲姟鍚嶇О", minWidth: 200 },
-    { prop: "inspectionProject", label: "宸℃椤圭洰", minWidth: 180 },
-    { prop: "remarks", label: "澶囨敞", minWidth: 180 },
-    { prop: "inspector", label: "鎵ц宸℃浜�", minWidth: 180, slot: "inspector" },
-    {
-      prop: "isEnabled",
-      label: "鏄惁鍚敤",
-      minWidth: 100,
-      dataType: "slot",
-      slot: "isEnabled",
-    },
-    {
-      prop: "frequencyType",
-      label: "棰戞",
-      minWidth: 120,
-      formatData: params => {
-        return params === "DAILY"
-          ? "姣忔棩"
-          : params === "WEEKLY"
-          ? "姣忓懆"
-          : params === "MONTHLY"
-          ? "姣忔湀"
-          : params === "QUARTERLY"
-          ? "瀛e害"
-          : "";
-      },
-    },
-    {
-      prop: "frequencyDetail",
-      label: "寮�濮嬫棩鏈熶笌鏃堕棿",
-      minWidth: 200,
-      formatter: (row, column, cellValue) => {
-        // 鍏堝垽鏂槸鍚︽槸瀛楃涓�
-        if (typeof cellValue !== "string") return "";
-        let val = cellValue;
-        const replacements = {
-          MON: "鍛ㄤ竴",
-          TUE: "鍛ㄤ簩",
-          WED: "鍛ㄤ笁",
-          THU: "鍛ㄥ洓",
-          FRI: "鍛ㄤ簲",
-          SAT: "鍛ㄥ叚",
-          SUN: "鍛ㄦ棩",
-        };
-        // 浣跨敤姝e垯涓�娆℃�ф浛鎹㈡墍鏈夊尮閰嶉」
-        return val.replace(
-          /MON|TUE|WED|THU|FRI|SAT|SUN/g,
-          match => replacements[match]
-        );
-      },
-    },
-    { prop: "registrant", label: "鐧昏浜�", minWidth: 120 },
-    {
-      prop: "createTime",
-      label: "鐧昏鏃ユ湡",
-      minWidth: 180,
-      formatData: cell => {
-        if (!cell) return "-";
-        try {
-          return dayjs(cell).format("YYYY-MM-DD HH:mm:ss");
-        } catch {
-          return cell;
-        }
-      },
-    },
-    // {
-    //   prop: "inspectionResult",
-    //   label: "宸℃缁撴灉",
-    //   minWidth: 100,
-    //   dataType: "tag",
-    //   formatData: val => {
-    //     return val == 1 ? "姝e父" : "寮傚父";
-    //   },
-    //   formatType: val => {
-    //     return val == 1 ? "success" : "danger";
-    //   },
-    // },
-    { prop: "abnormalDescription", label: "寮傚父鎻忚堪", minWidth: 150 },
-  ]);
+const columns = ref([
+	{ prop: "taskName", label: "宸℃浠诲姟鍚嶇О", minWidth: 200 },
+	{ prop: "inspectionProject", label: "宸℃椤圭洰", minWidth: 180 },
+	{ prop: "remarks", label: "澶囨敞", minWidth: 180 },
+	{ prop: "inspector", label: "鎵ц宸℃浜�", minWidth: 180, slot: "inspector" },
+	{ prop: "isEnabled", label: "鏄惁鍚敤", minWidth: 100, dataType: "slot", slot: "isEnabled" },
+	{
+		prop: "frequencyType", label: "棰戞", minWidth: 120,
+		formatData: params => ({ DAILY: "姣忔棩", WEEKLY: "姣忓懆", MONTHLY: "姣忔湀", QUARTERLY: "瀛e害" }[params] || ""),
+	},
+	{
+		prop: "frequencyDetail", label: "寮�濮嬫棩鏈熶笌鏃堕棿", minWidth: 200,
+		formatter: (row, column, cellValue) => {
+			if (typeof cellValue !== "string") return "";
+			return cellValue.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => ({ MON: "鍛ㄤ竴", TUE: "鍛ㄤ簩", WED: "鍛ㄤ笁", THU: "鍛ㄥ洓", FRI: "鍛ㄤ簲", SAT: "鍛ㄥ叚", SUN: "鍛ㄦ棩" }[match]));
+		},
+	},
+	{ prop: "registrant", label: "鐧昏浜�", minWidth: 120 },
+	{
+		prop: "createTime", label: "鐧昏鏃ユ湡", minWidth: 180,
+		formatData: cell => { if (!cell) return "-"; try { return dayjs(cell).format("YYYY-MM-DD HH:mm:ss"); } catch { return cell; } },
+	},
+	{ prop: "abnormalDescription", label: "寮傚父鎻忚堪", minWidth: 150 },
+]);
 
-  // 鎿嶄綔鍒楅厤缃�
-  const getOperationColumn = operations => {
-    if (!operations || operations.length === 0) return null;
+const getOperationColumn = operations => {
+	if (!operations || operations.length === 0) return null;
+	return {
+		label: "鎿嶄綔", width: operations.length > 1 ? 180 : 130, fixed: "right", align: "center", dataType: "action",
+		operation: operations.map(op => {
+			switch (op) {
+				case "edit": return { name: "缂栬緫", clickFun: handleAdd, color: "#409EFF" };
+				case "upload": return { name: "涓婁紶", clickFun: openUploadDialog, color: "#409EFF" };
+				case "viewFile": return { name: "鏌ョ湅闄勪欢", clickFun: viewFile, color: "#67C23A" };
+				default: return null;
+			}
+		}).filter(Boolean),
+	};
+};
 
-    const operationConfig = {
-      label: "鎿嶄綔",
-      width: operations.length > 1 ? 180 : 130,
-      fixed: "right",
-      align: "center",
-      dataType: "action",
-      operation: operations
-        .map(op => {
-          switch (op) {
-            case "edit":
-              return {
-                name: "缂栬緫",
-                clickFun: handleAdd,
-                color: "#409EFF",
-              };
-            case "upload":
-              return {
-                name: "涓婁紶",
-                clickFun: openUploadDialog,
-                color: "#409EFF",
-              };
-            case "viewFile":
-              return {
-                name: "鏌ョ湅闄勪欢",
-                clickFun: viewFile,
-                color: "#67C23A",
-              };
-            default:
-              return null;
-          }
-        })
-        .filter(Boolean),
-    };
+// 宸℃璁板綍 - 鏃ユ湡鍒嗙粍
+const inspectionExpandedRowKeys = ref([]);
 
-    return operationConfig;
-  };
+const groupInspectionByDate = (list) => {
+	const map = {};
+	list.forEach(item => {
+		const date = item.dateStr || (item.createTime ? dayjs(item.createTime).format('YYYY-MM-DD') : '鏈煡鏃ユ湡');
+		if (!map[date]) map[date] = [];
+		map[date].push(item);
+	});
+	return Object.entries(map).map(([date, children]) => ({ date, children, count: children.length }));
+};
 
-  onMounted(() => {
-    radioChange("taskManage");
-  });
+const groupedInspectionList = computed(() => groupInspectionByDate(tableData.value));
 
-  // 鍗曢�夊彉鍖�
-  const radioChange = value => {
-    if (value === "taskManage") {
-      const operationColumn = getOperationColumn(["edit"]);
-      tableColumns.value = [
-        ...columns.value,
-        ...(operationColumn ? [operationColumn] : []),
-      ];
-      operationsArr.value = ["edit"];
-    } else if (value === "task") {
-      const operationColumn = getOperationColumn(["upload", "viewFile"]);
-      // 宸℃璁板綍涓嶅睍绀�"鏄惁鍚敤"鍒�
-      const taskColumns = columns.value.filter(col => col.prop !== "isEnabled");
-      tableColumns.value = [
-        ...taskColumns,
-        ...(operationColumn ? [operationColumn] : []),
-      ];
-      operationsArr.value = ["upload", "viewFile"];
-    }
-    pageNum.value = 1;
-    pageSize.value = 10;
-    getList();
-  };
+const onInspectionExpandChange = (row, expandedRows) => {
+	inspectionExpandedRowKeys.value = expandedRows.map(r => r.date);
+};
 
-  // 鏌ヨ鎿嶄綔
-  const handleQuery = () => {
-    pageNum.value = 1;
-    pageSize.value = 10;
-    getList();
-  };
-  // 鍒嗛〉澶勭悊
-  const handlePagination = val => {
-    pageNum.value = val.page;
-    pageSize.value = val.limit;
-    getList();
-  };
-  // 鑾峰彇鍒楄〃鏁版嵁
-  const getList = () => {
-    tableLoading.value = true;
+// 鍗曟潯宸℃瀹屾垚
+const handleSingleComplete = async (row) => {
+	try {
+		await ElMessageBox.confirm('纭灏嗚宸℃璁板綍鏍囪涓哄畬鎴愶紵', '鎻愮ず', { type: 'warning' });
+		const res = await completeInspection(row.id);
+		if (res.code === 200) {
+			ElMessage.success('宸℃瀹屾垚');
+			getList();
+		} else {
+			ElMessage.error(res.msg || '鎿嶄綔澶辫触');
+		}
+	} catch { /* 鍙栨秷 */ }
+};
 
-    const params = {
-      ...queryParams,
-      size: pageSize.value,
-      current: pageNum.value,
-    };
+// 涓�閿贰妫�瀹屾垚
+const handleBatchInspection = async (dateStr) => {
+	try {
+		await ElMessageBox.confirm(`纭灏� ${dateStr} 鐨勬墍鏈夊緟宸℃璁板綍鏍囪涓哄畬鎴愶紵`, '鎻愮ず', { type: 'warning' });
+		const res = await batchCompleteInspection(dateStr);
+		if (res.code === 200) {
+			ElMessage.success(res.msg || '鎵归噺瀹屾垚鎴愬姛');
+			getList();
+		} else {
+			ElMessage.error(res.msg || '鎿嶄綔澶辫触');
+		}
+	} catch { /* 鍙栨秷 */ }
+};
 
-    let apiCall;
-    if (activeRadio.value === "task") {
-      apiCall = inspectionTaskList(params);
-    } else {
-      apiCall = timingTaskList(params);
-    }
+onMounted(() => { radioChange("taskManage"); });
 
-    apiCall
-      .then(res => {
-        const rawData = res.data.records || [];
-        // 澶勭悊 inspector 瀛楁锛屽皢瀛楃涓茶浆鎹负鏁扮粍锛堥�傜敤浜庢墍鏈夋儏鍐碉級
-        tableData.value = rawData.map(item => {
-          const processedItem = { ...item };
-          processedItem.__raw = { ...item };
+const radioChange = value => {
+	inspectionExpandedRowKeys.value = [];
+	if (value === "taskManage") {
+		const operationColumn = getOperationColumn(["edit"]);
+		tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
+		operationsArr.value = ["edit"];
+	} else if (value === "task") {
+		const operationColumn = getOperationColumn(["upload", "viewFile"]);
+		const taskColumns = columns.value.filter(col => col.prop !== "isEnabled");
+		tableColumns.value = [...taskColumns, ...(operationColumn ? [operationColumn] : [])];
+		operationsArr.value = ["upload", "viewFile"];
+	}
+	pageNum.value = 1;
+	getList();
+};
 
-          // 澶勭悊 inspector 瀛楁
-          if (processedItem.inspector) {
-            if (typeof processedItem.inspector === "string") {
-              // 瀛楃涓叉寜閫楀彿鍒嗗壊
-              processedItem.inspector = processedItem.inspector
-                .split(",")
-                .map(s => s.trim())
-                .filter(s => s);
-            } else if (!Array.isArray(processedItem.inspector)) {
-              // 闈炴暟缁勮浆涓烘暟缁�
-              processedItem.inspector = [processedItem.inspector];
-            }
-          } else {
-            // 绌哄�艰涓虹┖鏁扮粍
-            processedItem.inspector = [];
-          }
+const handleQuery = () => { pageNum.value = 1; getList(); };
 
-          return processedItem;
-        });
-        total.value = res.data.total || 0;
-      })
-      .finally(() => {
-        tableLoading.value = false;
-      });
-  };
+const handlePagination = val => { pageNum.value = val.page; pageSize.value = val.limit; getList(); };
 
-  // 閲嶇疆鏌ヨ
-  const resetQuery = () => {
-    for (const key in queryParams) {
-      if (!["pageNum", "pageSize"].includes(key)) {
-        queryParams[key] = "";
-      }
-    }
-    handleQuery();
-  };
+const getList = () => {
+	tableLoading.value = true;
+	const params = { ...queryParams, size: pageSize.value, current: pageNum.value };
+	const apiCall = activeRadio.value === "task" ? inspectionTaskList(params) : timingTaskList(params);
+	apiCall.then(res => {
+		const rawData = res.data.records || [];
+		tableData.value = rawData.map(item => {
+			const processedItem = { ...item };
+			if (processedItem.inspector) {
+				if (typeof processedItem.inspector === "string") {
+					processedItem.inspector = processedItem.inspector.split(",").map(s => s.trim()).filter(s => s);
+				} else if (!Array.isArray(processedItem.inspector)) {
+					processedItem.inspector = [processedItem.inspector];
+				}
+			} else {
+				processedItem.inspector = [];
+			}
+			return processedItem;
+		});
+		total.value = res.data.total || 0;
+	}).finally(() => { tableLoading.value = false; });
+};
 
-  // 鏂板 / 缂栬緫
-  const handleAdd = row => {
-    const type = row ? "edit" : "add";
-    nextTick(() => {
-      formDia.value?.openDialog(type, row);
-    });
-  };
+const resetQuery = () => { queryParams.taskName = ""; handleQuery(); };
 
-  // 鏌ョ湅闄勪欢
-  const viewFile = row => {
-    nextTick(() => {
-      viewFiles.value?.openDialog(row);
-    });
-  };
+const handleAdd = row => { nextTick(() => { formDia.value?.openDialog(row ? "edit" : "add", row); }); };
+const viewFile = row => { nextTick(() => { viewFiles.value?.openDialog(row); }); };
+const openUploadDialog = row => { nextTick(() => { uploadFiles.value?.openDialog(row); }); };
 
-  const openUploadDialog = row => {
-    nextTick(() => {
-      uploadFiles.value?.openDialog(row);
-    });
-  };
+const handleDelete = () => {
+	if (!selectedRows.value.length) { proxy.$modal.msgWarning("璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁"); return; }
+	proxy.$modal.confirm("鏄惁纭鍒犻櫎鎵�閫夋暟鎹」锛�").then(() => delTimingTask(selectedRows.value.map(item => item.id)))
+		.then(() => { proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛"); handleQuery(); }).catch(() => {});
+};
 
-  // 鍒犻櫎鎿嶄綔
-  const handleDelete = () => {
-    if (!selectedRows.value.length) {
-      proxy.$modal.msgWarning("璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁");
-      return;
-    }
+const handleSelectionChange = selection => { selectedRows.value = selection; };
 
-    const deleteIds = selectedRows.value.map(item => item.id);
-
-    proxy.$modal
-      .confirm("鏄惁纭鍒犻櫎鎵�閫夋暟鎹」锛�")
-      .then(() => {
-        return delTimingTask(deleteIds);
-      })
-      .then(() => {
-        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-        handleQuery();
-      })
-      .catch(() => {});
-  };
-
-  // 澶氶�夊彉鏇�
-  const handleSelectionChange = selection => {
-    selectedRows.value = selection;
-  };
-
-  // 瀵煎嚭
-  const handleOut = () => {
-    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
-      confirmButtonText: "纭",
-      cancelButtonText: "鍙栨秷",
-      type: "warning",
-    })
-      .then(() => {
-        // 鏍规嵁褰撳墠閫変腑鐨勬爣绛鹃〉璋冪敤涓嶅悓鐨勫鍑烘帴鍙�
-        if (activeRadio.value === "taskManage") {
-          // 宸℃浠诲姟
-          proxy.download("/timingTask/export", {}, "宸℃浠诲姟.xlsx");
-        } else if (activeRadio.value === "task") {
-          // 宸℃璁板綍
-          proxy.download("/inspectionTask/export", {}, "宸℃璁板綍.xlsx");
-        }
-      })
-      .catch(() => {
-        proxy.$modal.msg("宸插彇娑�");
-      });
-  };
+const handleOut = () => {
+	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", { confirmButtonText: "纭", cancelButtonText: "鍙栨秷", type: "warning" })
+		.then(() => {
+			if (activeRadio.value === "taskManage") proxy.download("/timingTask/export", {}, "宸℃浠诲姟.xlsx");
+			else proxy.download("/inspectionTask/export", {}, "宸℃璁板綍.xlsx");
+		}).catch(() => { proxy.$modal.msg("宸插彇娑�"); });
+};
 </script>
 
 <style scoped>
-  .person-tags {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 4px;
-  }
-
-  .person-tag {
-    margin-right: 4px;
-    margin-bottom: 2px;
-  }
-
-  .no-data {
-    color: #909399;
-    font-size: 14px;
-  }
+.person-tags { display: flex; flex-wrap: wrap; gap: 4px; }
+.person-tag { margin-right: 4px; margin-bottom: 2px; }
+.no-data { color: #909399; font-size: 14px; }
 </style>
diff --git a/src/views/equipmentManagement/spareParts/index.vue b/src/views/equipmentManagement/spareParts/index.vue
index 8abe35d..c25736f 100644
--- a/src/views/equipmentManagement/spareParts/index.vue
+++ b/src/views/equipmentManagement/spareParts/index.vue
@@ -1,26 +1,26 @@
 <template>
-  <div class="spare-part-category">
-    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
-      <el-tab-pane label="澶囦欢鍒楄〃" name="list">
-        <div class="search_form">
-          <el-form :inline="true" :model="queryParams" class="search-form">
-            <el-form-item label="澶囦欢鍚嶇О">
-              <el-input
-                v-model="queryParams.name"
-                placeholder="璇疯緭鍏ュ浠跺悕绉�"
-                clearable
-                style="width: 240px"
-              />
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
-              <el-button @click="resetQuery">閲嶇疆</el-button>
-            </el-form-item>
-          </el-form>
-          <div>
-            <el-button type="primary" @click="addCategory">鏂板</el-button>
-          </div>
-        </div>
+	<div class="spare-part-category">
+		<el-tabs v-model="activeTab" @tab-change="handleTabChange">
+			<el-tab-pane label="澶囦欢鍒楄〃" name="list">
+				<div class="search_form">
+					<el-form :inline="true" :model="queryParams" class="search-form">
+						<el-form-item label="澶囦欢鍚嶇О">
+							<el-input
+								v-model="queryParams.name"
+								placeholder="璇疯緭鍏ュ浠跺悕绉�"
+								clearable
+								style="width: 240px"
+							/>
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
+							<el-button @click="resetQuery">閲嶇疆</el-button>
+						</el-form-item>
+					</el-form>
+					<div>
+						<el-button type="primary" @click="addCategory">鏂板</el-button>
+					</div>
+				</div>
 				<div class="table_list">
 					<PIMTable
 						rowKey="id"
@@ -36,83 +36,87 @@
 						</template>
 					</PIMTable>
 				</div>
-
-        <el-dialog title="鍒嗙被绠$悊" v-model="dialogVisible" width="60%">
-          <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
-            <el-form-item label="璁惧" prop="deviceLedgerIds">
-              <el-select
-                v-model="form.deviceLedgerIds"
-                placeholder="璇烽�夋嫨璁惧"
-                filterable
-                default-first-option
-                :reserve-keyword="false"
-                multiple
-                style="width: 100%"
-              >
-                <el-option
-                  v-for="(item, index) in deviceOptions"
-                  :key="index"
-                  :label="item.deviceName"
-                  :value="item.id"
-                ></el-option>
-              </el-select>
-            </el-form-item>
-            <el-form-item label="澶囦欢鍚嶇О" prop="name">
-              <el-input v-model="form.name"></el-input>
-            </el-form-item>
-            <el-form-item label="澶囦欢缂栧彿" prop="sparePartsNo">
-              <el-input v-model="form.sparePartsNo"></el-input>
-            </el-form-item>
-            <el-form-item label="鏁伴噺" prop="quantity">
-              <el-input type="number" v-model="form.quantity"></el-input>
-            </el-form-item>
-            <el-form-item label="鐘舵��" prop="status">
-              <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��">
-                <el-option label="姝e父" value="姝e父"></el-option>
-                <el-option label="绂佺敤" value="绂佺敤"></el-option>
-              </el-select>
-            </el-form-item>
-            <el-form-item label="鎻忚堪" prop="description">
-              <el-input v-model="form.description"></el-input>
-            </el-form-item>
-            <el-form-item label="浠锋牸" prop="price">
-              <el-input-number
-                v-model="form.price"
-                placeholder="璇疯緭鍏ヤ环鏍�"
-                :min="0"
-                :step="0.01"
-                :precision="2"
-                style="width: 100%"
-              ></el-input-number>
-            </el-form-item>
-          </el-form>
-          <template #footer>
+				
+				<el-dialog title="鍒嗙被绠$悊" v-model="dialogVisible" width="60%">
+					<el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
+						<el-form-item label="璁惧" prop="deviceLedgerIds">
+							<el-select
+								v-model="form.deviceLedgerIds"
+								placeholder="璇烽�夋嫨璁惧"
+								filterable
+								default-first-option
+								:reserve-keyword="false"
+								multiple
+								style="width: 100%"
+							>
+								<el-option
+									v-for="(item, index) in deviceOptions"
+									:key="index"
+									:label="item.deviceName"
+									:value="item.id"
+								></el-option>
+							</el-select>
+						</el-form-item>
+						<el-form-item label="澶囦欢鍚嶇О" prop="name">
+							<el-input v-model="form.name"></el-input>
+						</el-form-item>
+						<el-form-item label="澶囦欢缂栧彿" prop="sparePartsNo">
+							<el-input v-model="form.sparePartsNo"></el-input>
+						</el-form-item>
+						<el-form-item label="鏁伴噺" prop="quantity">
+							<el-input type="number" v-model="form.quantity"></el-input>
+						</el-form-item>
+						<el-form-item label="鐘舵��" prop="status">
+							<el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��">
+								<el-option label="姝e父" value="姝e父"></el-option>
+								<el-option label="绂佺敤" value="绂佺敤"></el-option>
+							</el-select>
+						</el-form-item>
+						<el-form-item label="鎻忚堪" prop="description">
+							<el-input v-model="form.description"></el-input>
+						</el-form-item>
+						<el-form-item label="浠锋牸" prop="price">
+							<el-input-number
+								v-model="form.price"
+								placeholder="璇疯緭鍏ヤ环鏍�"
+								:min="0"
+								:step="0.01"
+								:precision="2"
+								style="width: 100%"
+							></el-input-number>
+						</el-form-item>
+					</el-form>
+					<template #footer>
             <span class="dialog-footer">
               <el-button type="primary" @click="submitForm" :loading="formLoading">纭畾</el-button>
               <el-button @click="dialogVisible = false" :disabled="formLoading">鍙栨秷</el-button>
             </span>
-          </template>
-        </el-dialog>
-      </el-tab-pane>
-
-      <el-tab-pane label="澶囦欢棰嗙敤璁板綍" name="usage">
-        <div class="search_form">
-          <el-form :inline="true" :model="usageQuery" class="search-form">
-            <el-form-item label="澶囦欢鍚嶇О">
-              <el-input v-model="usageQuery.sparePartsName" placeholder="璇疯緭鍏ュ浠跺悕绉�" clearable style="width: 240px" />
-            </el-form-item>
-            <el-form-item label="鏉ユ簮">
-              <el-select v-model="usageQuery.sourceType" placeholder="璇烽�夋嫨" clearable style="width: 200px">
-                <el-option label="缁翠慨" :value="0" />
-                <el-option label="淇濆吇" :value="1" />
-              </el-select>
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary" @click="handleUsageQuery">鏌ヨ</el-button>
-              <el-button @click="resetUsageQuery">閲嶇疆</el-button>
-            </el-form-item>
-          </el-form>
-        </div>
+					</template>
+				</el-dialog>
+			</el-tab-pane>
+			
+			<el-tab-pane label="澶囦欢棰嗙敤璁板綍" name="usage">
+				<div class="search_form">
+					<el-form :inline="true" :model="usageQuery" class="search-form">
+						<el-form-item label="澶囦欢鍚嶇О">
+							<el-input v-model="usageQuery.sparePartsName" placeholder="璇疯緭鍏ュ浠跺悕绉�" clearable style="width: 240px" />
+						</el-form-item>
+						<el-form-item label="鏉ユ簮">
+							<el-select v-model="usageQuery.sourceType" placeholder="璇烽�夋嫨" clearable style="width: 200px">
+								<el-option label="缁翠慨" :value="0" />
+								<el-option label="淇濆吇" :value="1" />
+								<el-option label="鐩存帴鍑哄簱" :value="2" />
+							</el-select>
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary" @click="handleUsageQuery">鏌ヨ</el-button>
+							<el-button @click="resetUsageQuery">閲嶇疆</el-button>
+						</el-form-item>
+					</el-form>
+					<div>
+						<el-button type="primary" @click="openRequisitionDialog">鐩存帴鍑哄簱</el-button>
+					</div>
+				</div>
 				<div class="table_list">
 					<PIMTable
 						rowKey="rowKey"
@@ -124,9 +128,63 @@
 						@pagination="handleUsagePageChange"
 					/>
 				</div>
-      </el-tab-pane>
-    </el-tabs>
-  </div>
+			</el-tab-pane>
+		</el-tabs>
+		
+		<!-- 鐩存帴鍑哄簱寮圭獥 -->
+		<el-dialog title="鐩存帴鍑哄簱" v-model="requisitionDialogVisible" width="500px">
+			<el-form :model="requisitionForm" :rules="requisitionRules" ref="requisitionFormRef" label-width="100px">
+				<el-form-item label="璁惧閫夋嫨" prop="deviceLedgerId">
+					<el-select
+						v-model="requisitionForm.deviceLedgerId"
+						placeholder="璇烽�夋嫨璁惧"
+						filterable
+						clearable
+						style="width: 100%"
+					>
+						<el-option
+							v-for="item in requisitionDeviceOptions"
+							:key="item.id"
+							:label="item.deviceName"
+							:value="item.id"
+						/>
+					</el-select>
+				</el-form-item>
+				<el-form-item label="澶囦欢閫夋嫨" prop="sparePartsId">
+					<el-select
+						v-model="requisitionForm.sparePartsId"
+						placeholder="璇烽�夋嫨澶囦欢"
+						filterable
+						clearable
+						style="width: 100%"
+						@change="onRequisitionSparePartChange"
+					>
+						<el-option
+							v-for="item in requisitionSparePartsOptions"
+							:key="item.id"
+							:label="`${item.name}锛堝簱瀛�: ${item.quantity}锛塦"
+							:value="item.id"
+						/>
+					</el-select>
+				</el-form-item>
+				<el-form-item label="棰嗙敤鏁伴噺" prop="quantity">
+					<el-input-number
+						v-model="requisitionForm.quantity"
+						:min="1"
+						:max="selectedSparePartMaxQty"
+						style="width: 100%"
+					/>
+				</el-form-item>
+				<el-form-item label="澶囨敞" prop="remark">
+					<el-input v-model="requisitionForm.remark" placeholder="璇疯緭鍏ュ娉�" />
+				</el-form-item>
+			</el-form>
+			<template #footer>
+				<el-button type="primary" @click="submitRequisition" :loading="requisitionLoading">纭畾</el-button>
+				<el-button @click="requisitionDialogVisible = false">鍙栨秷</el-button>
+			</template>
+		</el-dialog>
+	</div>
 </template>
 
 <script setup>
@@ -135,7 +193,7 @@
 import { getSparePartsList, addSparePart, editSparePart, delSparePart } from "@/api/equipmentManagement/spareParts";
 import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
 import PIMTable from "@/components/PIMTable/PIMTable.vue";
-import { getSparePartsUsagePage } from "@/api/equipmentManagement/sparePartsUsage";
+import { getSparePartsUsagePage, outboundSpareParts } from "@/api/equipmentManagement/sparePartsUsage";
 
 // 鍔犺浇鐘舵��
 const loading = ref(false);
@@ -157,375 +215,456 @@
 const formRef = ref(null);
 // 鏌ヨ鍙傛暟
 const queryParams = reactive({
-  name: ''
+	name: ''
 });
 // 鍒嗛〉鍙傛暟
 const pagination = reactive({
-  current: 1,
-  size: 10,
-  total: 0
+	current: 1,
+	size: 10,
+	total: 0
 });
 
 // 澶囦欢棰嗙敤璁板綍
 const usageLoading = ref(false);
 const usageQuery = reactive({
-  sparePartsName: "",
-  sourceType: "",
+	sparePartsName: "",
+	sourceType: "",
 });
 const usagePagination = reactive({
-  current: 1,
-  size: 10,
-  total: 0,
+	current: 1,
+	size: 10,
+	total: 0,
 });
 const usageTableData = ref([]);
 const usageColumns = ref([
-  { label: "鏉ユ簮", prop: "sourceText" },
-  { label: "鍗曟嵁/璁板綍ID", prop: "sourceId" },
-  { label: "璁惧鍚嶇О", prop: "deviceName" },
-  { label: "澶囦欢鍚嶇О", prop: "sparePartsName" },
-  { label: "棰嗙敤鏁伴噺", prop: "quantity" },
-  { label: "鎿嶄綔浜�", prop: "operator" },
-  { label: "鏃堕棿", prop: "createTime" },
+	{ label: "鏉ユ簮", prop: "sourceText" },
+	{ label: "鍗曟嵁/璁板綍ID", prop: "sourceId" },
+	{ label: "璁惧鍚嶇О", prop: "deviceName" },
+	{ label: "澶囦欢鍚嶇О", prop: "sparePartsName" },
+	{ label: "棰嗙敤鏁伴噺", prop: "quantity" },
+	{ label: "鎿嶄綔浜�", prop: "operator" },
+	{ label: "鏃堕棿", prop: "createTime" },
 ]);
 
 const handleTabChange = async (name) => {
-  if (name === "usage") {
-    usagePagination.current = 1;
-    await fetchUsageData();
-  }
+	if (name === "usage") {
+		usagePagination.current = 1;
+		await fetchUsageData();
+	}
 };
 const columns = ref([
-  {
-    label: "璁惧鍚嶇О",
-    prop: "deviceNameStr",
-  },
-  {
-    label: "澶囦欢鍚嶇О",
-    prop: "name",
-  },
-  {
-    label: "澶囦欢缂栧彿",
-    prop: "sparePartsNo",
-  },
-  {
-    label: "鐘舵��",
-    prop: "status",
-    slot: "status",
-    dataType: "slot",
-  },
-  {
-    label: "浠锋牸",
-    prop: "price",
-  },
-  {
-    label: "鏁伴噺",
-    prop: "quantity",
-  },
-  {
-    label: "鎻忚堪",
-    prop: "description",
-  },
-  {
-    label: "鎿嶄綔",
-    prop: "operation",
-    width: 150,
-    fixed: 'right',
-    align: "center",
-    dataType: "action",
-    operation: [
-      {
-        name: "缂栬緫",
-        clickFun: (row) => {
-          editCategory(row)
-        },
-      },
-      {
-        name: "鍒犻櫎",
-        clickFun: (row) => {
-          deleteCategory(row.id)
-        },
-      },
-    ],
-  },
+	{
+		label: "璁惧鍚嶇О",
+		prop: "deviceNameStr",
+	},
+	{
+		label: "澶囦欢鍚嶇О",
+		prop: "name",
+	},
+	{
+		label: "澶囦欢缂栧彿",
+		prop: "sparePartsNo",
+	},
+	{
+		label: "鐘舵��",
+		prop: "status",
+		slot: "status",
+		dataType: "slot",
+	},
+	{
+		label: "浠锋牸",
+		prop: "price",
+	},
+	{
+		label: "鏁伴噺",
+		prop: "quantity",
+	},
+	{
+		label: "鎻忚堪",
+		prop: "description",
+	},
+	{
+		label: "鎿嶄綔",
+		prop: "operation",
+		width: 150,
+		fixed: 'right',
+		align: "center",
+		dataType: "action",
+		operation: [
+			{
+				name: "缂栬緫",
+				clickFun: (row) => {
+					editCategory(row)
+				},
+			},
+			{
+				name: "鍒犻櫎",
+				clickFun: (row) => {
+					deleteCategory(row.id)
+				},
+			},
+		],
+	},
 ]);
 // 琛ㄥ崟鏁版嵁
 const form = reactive({
-  id:'',
-  name: '',
-  sparePartsNo: '',
-  status: '',
-  description: '',
-  deviceLedgerIds: [],
-  price: null
+	id:'',
+	name: '',
+	sparePartsNo: '',
+	status: '',
+	description: '',
+	deviceLedgerIds: [],
+	price: null
 });
 
 // 琛ㄥ崟楠岃瘉瑙勫垯
 const rules = reactive({
-  name: [
-    { required: true, message: '璇疯緭鍏ュ浠跺悕绉�', trigger: 'blur' }
-  ],
-  sparePartsNo: [
-    { required: true, message: '璇疯緭鍏ュ浠剁紪鍙�', trigger: 'blur' }
-  ],
-  quantity:[
-    { required: true, message: '璇疯緭鍏ユ暟閲�', trigger: 'blur' }
-  ],
-  status: [
-    { required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change' }
-  ],
-  deviceLedgerIds: [
-    { 
-      required: true, 
-      message: '璇烽�夋嫨璁惧', 
-      trigger: 'change',
-      validator: (rule, value, callback) => {
-        if (operationType.value === 'add' && (!value || value.length === 0)) {
-          callback(new Error('璇烽�夋嫨璁惧'));
-        } else {
-          callback();
-        }
-      }
-    }
-  ]
+	name: [
+		{ required: true, message: '璇疯緭鍏ュ浠跺悕绉�', trigger: 'blur' }
+	],
+	sparePartsNo: [
+		{ required: true, message: '璇疯緭鍏ュ浠剁紪鍙�', trigger: 'blur' }
+	],
+	quantity:[
+		{ required: true, message: '璇疯緭鍏ユ暟閲�', trigger: 'blur' }
+	],
+	status: [
+		{ required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change' }
+	],
+	deviceLedgerIds: [
+		{
+			required: true,
+			message: '璇烽�夋嫨璁惧',
+			trigger: 'change',
+			validator: (rule, value, callback) => {
+				if (operationType.value === 'add' && (!value || value.length === 0)) {
+					callback(new Error('璇烽�夋嫨璁惧'));
+				} else {
+					callback();
+				}
+			}
+		}
+	]
 });
 // 鑾峰彇缂╄繘閲�
 const getIndentation = (row) => {
-  // 杩欓噷绠�鍗曡繑鍥� 20锛屽彲鏍规嵁瀹為檯闇�姹傚疄鐜板眰绾х缉杩涢�昏緫
-  return 20; 
+	// 杩欓噷绠�鍗曡繑鍥� 20锛屽彲鏍规嵁瀹為檯闇�姹傚疄鐜板眰绾х缉杩涢�昏緫
+	return 20;
 };
 // 瀹氫箟 buildTree 鍑芥暟
 const buildTree = (flatData) => {
-  const map = {};
-  const result = [];
-  if(flatData){
-    return result;
-  }
-  flatData.forEach(item => {
-    map[item.id] = { ...item, children: [] };
-  });
-  flatData.forEach(item => {
-    if (item.parentId === null || !map[item.parentId]) {
-      result.push(map[item.id]);
-    } else {
-      map[item.parentId].children.push(map[item.id]);
-    }
-  });
-  return result;
+	const map = {};
+	const result = [];
+	if(flatData){
+		return result;
+	}
+	flatData.forEach(item => {
+		map[item.id] = { ...item, children: [] };
+	});
+	flatData.forEach(item => {
+		if (item.parentId === null || !map[item.parentId]) {
+			result.push(map[item.id]);
+		} else {
+			map[item.parentId].children.push(map[item.id]);
+		}
+	});
+	return result;
 };
 // 鑾峰彇鍒楄〃鏁版嵁
 const fetchListData = async () => {
-  loading.value = true;
-  try {
-    const params = {
-      current: pagination.current,
-      size: pagination.size
-    };
-    if (queryParams.name) {
-      params.name = queryParams.name;
-    }
-    const res = await getSparePartsList(params);
-    if (res.code === 200) {
-      renderTableData.value = res.data.records || [];
-      categories.value = res.data.records || [];
-      pagination.total = res.data.total || 0;
-    }
-  } catch (error) {
+	loading.value = true;
+	try {
+		const params = {
+			current: pagination.current,
+			size: pagination.size
+		};
+		if (queryParams.name) {
+			params.name = queryParams.name;
+		}
+		const res = await getSparePartsList(params);
+		if (res.code === 200) {
+			renderTableData.value = res.data.records || [];
+			categories.value = res.data.records || [];
+			pagination.total = res.data.total || 0;
+		}
+	} catch (error) {
 		loading.value = false;
-  } finally {
-    loading.value = false;
-  }
+	} finally {
+		loading.value = false;
+	}
 }
 
 const fetchUsageData = async () => {
-  usageLoading.value = true;
-  try {
-    const res = await getSparePartsUsagePage({
-      current: usagePagination.current,
-      size: usagePagination.size,
-      sparePartsName: usageQuery.sparePartsName || undefined,
-      sourceType: usageQuery.sourceType || undefined,
-    });
-    if (res?.code === 200) {
-      const records = res?.data?.records || [];
-      usagePagination.total = res?.data?.total || 0;
-      usageTableData.value = records.map((r, idx) => ({
-        rowKey: r.id ?? `${usagePagination.current}-${idx}`,
-        ...r,
-        sourceText: r.sourceText === "" ? "-" : r.sourceText,
-      }));
-    } else {
-      usagePagination.total = 0;
-      usageTableData.value = [];
-    }
-  } finally {
-    usageLoading.value = false;
-  }
+	usageLoading.value = true;
+	try {
+		const res = await getSparePartsUsagePage({
+			current: usagePagination.current,
+			size: usagePagination.size,
+			sparePartsName: usageQuery.sparePartsName || undefined,
+			sourceType: usageQuery.sourceType || undefined,
+		});
+		if (res?.code === 200) {
+			const records = res?.data?.records || [];
+			usagePagination.total = res?.data?.total || 0;
+			usageTableData.value = records.map((r, idx) => ({
+				rowKey: r.id ?? `${usagePagination.current}-${idx}`,
+				...r,
+				sourceText: r.sourceText === "" ? "-" : r.sourceText,
+			}));
+		} else {
+			usagePagination.total = 0;
+			usageTableData.value = [];
+		}
+	} finally {
+		usageLoading.value = false;
+	}
 };
 
 const handleUsageQuery = () => {
-  usagePagination.current = 1;
-  fetchUsageData();
+	usagePagination.current = 1;
+	fetchUsageData();
 };
 const resetUsageQuery = () => {
-  usageQuery.sparePartsName = "";
-  usageQuery.sourceType = "";
-  usagePagination.current = 1;
-  fetchUsageData();
+	usageQuery.sparePartsName = "";
+	usageQuery.sourceType = "";
+	usagePagination.current = 1;
+	fetchUsageData();
 };
 const handleUsagePageChange = (obj) => {
-  usagePagination.current = obj.page;
-  usagePagination.size = obj.limit;
-  fetchUsageData();
+	usagePagination.current = obj.page;
+	usagePagination.size = obj.limit;
+	fetchUsageData();
 };
 
 // 鏌ヨ
 const handleQuery = () => {
-  pagination.current = 1;
-  fetchListData();
+	pagination.current = 1;
+	fetchListData();
 }
 
 // 閲嶇疆鏌ヨ
 const resetQuery = () => {
-  queryParams.name = '';
-  pagination.current = 1;
-  fetchListData();
+	queryParams.name = '';
+	pagination.current = 1;
+	fetchListData();
 }
 
-// 鍒嗛〉澶у皬鏀瑰彉
-const handleSizeChange = (size) => {
-  pagination.size = size;
-  pagination.current = 1;
-  fetchListData();
-}
-
-// 褰撳墠椤垫敼鍙�
-const handleCurrentChange = (current) => {
-  pagination.current = current;
-  fetchListData();
+// 鍒嗛〉鍙樺寲
+const handleSizeChange = ({ page, limit }) => {
+	pagination.current = page;
+	pagination.size = limit;
+	fetchListData();
 }
 
 // 鍔犺浇璁惧鍒楄〃锛堝湪鎵撳紑寮规鏃惰皟鐢級
 const loadDeviceName = async () => {
-  try {
-    const { data } = await getDeviceLedger();
-    deviceOptions.value = data || [];
-  } catch (error) {
-    ElMessage.error('鑾峰彇璁惧鍒楄〃澶辫触');
-  }
+	try {
+		const { data } = await getDeviceLedger();
+		deviceOptions.value = data || [];
+	} catch (error) {
+		ElMessage.error('鑾峰彇璁惧鍒楄〃澶辫触');
+	}
 };
 
 // 鏂板鍒嗙被
 const addCategory = async () => {
-  await loadDeviceName();
-  form.id = '';
-  form.name = '';
-  form.sparePartsNo = '';
-  form.status = '';
-  form.description = '';
-  form.deviceLedgerIds = [];
-  form.quantity = undefined;
-  form.price = null;
-  operationType.value = 'add'
-  dialogVisible.value = true;
+	await loadDeviceName();
+	form.id = '';
+	form.name = '';
+	form.sparePartsNo = '';
+	form.status = '';
+	form.description = '';
+	form.deviceLedgerIds = [];
+	form.quantity = undefined;
+	form.price = null;
+	operationType.value = 'add'
+	dialogVisible.value = true;
 };
 
 // 缂栬緫鍒嗙被
 const editCategory = async (row) => {
-  await loadDeviceName();
-  Object.assign(form, row);
-  // 濡傛灉鍚庣杩斿洖鐨勬槸 deviceIds 瀛楃涓诧紝闇�瑕佽浆鎹负鏁扮粍
-  if (row.deviceIds && typeof row.deviceIds === 'string') {
-    // 纭繚ID绫诲瀷涓庤澶囬�夐」涓殑ID绫诲瀷涓�鑷�
-    const deviceIdsArray = row.deviceIds.split(',').map(id => id.trim()).filter(id => id);
-    // 濡傛灉璁惧閫夐」涓殑ID鏄暟瀛楃被鍨嬶紝鍒欒浆鎹负鏁板瓧
-    if (deviceOptions.value.length > 0 && typeof deviceOptions.value[0].id === 'number') {
-      form.deviceLedgerIds = deviceIdsArray.map(id => Number(id)).filter(id => !isNaN(id));
-    } else {
-      form.deviceLedgerIds = deviceIdsArray;
-    }
-  } else if (row.deviceIds && Array.isArray(row.deviceIds)) {
-    form.deviceLedgerIds = row.deviceIds;
-  } else {
-    form.deviceLedgerIds = [];
-  }
-  operationType.value = 'edit'
-  dialogVisible.value = true;
+	await loadDeviceName();
+	Object.assign(form, row);
+	// 濡傛灉鍚庣杩斿洖鐨勬槸 deviceIds 瀛楃涓诧紝闇�瑕佽浆鎹负鏁扮粍
+	if (row.deviceIds && typeof row.deviceIds === 'string') {
+		// 纭繚ID绫诲瀷涓庤澶囬�夐」涓殑ID绫诲瀷涓�鑷�
+		const deviceIdsArray = row.deviceIds.split(',').map(id => id.trim()).filter(id => id);
+		// 濡傛灉璁惧閫夐」涓殑ID鏄暟瀛楃被鍨嬶紝鍒欒浆鎹负鏁板瓧
+		if (deviceOptions.value.length > 0 && typeof deviceOptions.value[0].id === 'number') {
+			form.deviceLedgerIds = deviceIdsArray.map(id => Number(id)).filter(id => !isNaN(id));
+		} else {
+			form.deviceLedgerIds = deviceIdsArray;
+		}
+	} else if (row.deviceIds && Array.isArray(row.deviceIds)) {
+		form.deviceLedgerIds = row.deviceIds;
+	} else {
+		form.deviceLedgerIds = [];
+	}
+	operationType.value = 'edit'
+	dialogVisible.value = true;
 };
 
 // 鍒犻櫎鍒嗙被
 const deleteCategory = async (id) => {
-  try {
-    await ElMessageBox.confirm('姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ュ垎绫伙紝鏄惁缁х画?', '鎻愮ず', {
-      confirmButtonText: '纭畾',
-      cancelButtonText: '鍙栨秷',
-      type: 'warning'
-    });
-    loading.value = true;
-    const res = await delSparePart(id);
-    if (res.code === 200) {
-      ElMessage.success('鍒犻櫎鎴愬姛');
-      fetchListData();
-    } else {
-      ElMessage.error(res.message || '鍒犻櫎澶辫触');
-    }
-  } catch (error) {
-    if (error !== 'cancel') {
-      ElMessage.error('鍒犻櫎澶辫触');
-    }
-  } finally {
-    loading.value = false;
-  }
+	try {
+		await ElMessageBox.confirm('姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ュ垎绫伙紝鏄惁缁х画?', '鎻愮ず', {
+			confirmButtonText: '纭畾',
+			cancelButtonText: '鍙栨秷',
+			type: 'warning'
+		});
+		loading.value = true;
+		const res = await delSparePart(id);
+		if (res.code === 200) {
+			ElMessage.success('鍒犻櫎鎴愬姛');
+			fetchListData();
+		} else {
+			ElMessage.error(res.message || '鍒犻櫎澶辫触');
+		}
+	} catch (error) {
+		if (error !== 'cancel') {
+			ElMessage.error('鍒犻櫎澶辫触');
+		}
+	} finally {
+		loading.value = false;
+	}
 };
 
 // 鎻愪氦琛ㄥ崟
 const submitForm = async () => {
-  if (!formRef.value) return;
-  try {
-    await formRef.value.validate();
-    formLoading.value = true;
-    
-    // 鏋勫缓鎻愪氦鏁版嵁
-    const submitData = {
-      ...form,
-      deviceIds: form.deviceLedgerIds && form.deviceLedgerIds.length > 0 
-        ? form.deviceLedgerIds.join(',') 
-        : ''
-    };
-    
-    // 鍒犻櫎涓嶉渶瑕佺殑瀛楁
-    delete submitData.deviceLedgerIds;
-    
-    if (operationType.value === 'edit') {
-      let res = await editSparePart(submitData);
-      if (res.code === 200) {
-        ElMessage.success('缂栬緫鎴愬姛');
-        dialogVisible.value = false;
-        fetchListData();
-      }
-    } else {
-      let res = await addSparePart(submitData);
-      if (res.code === 200) {
-        ElMessage.success('鏂板鎴愬姛');
-        dialogVisible.value = false;
-        fetchListData();
-      }
-    }
-  } catch (error) {
-    ElMessage.error('璇峰~鍐欏畬鏁磋〃鍗曚俊鎭�');
-  } finally {
-    formLoading.value = false;
-  }
+	if (!formRef.value) return;
+	try {
+		await formRef.value.validate();
+		formLoading.value = true;
+		
+		// 鏋勫缓鎻愪氦鏁版嵁
+		const submitData = {
+			...form,
+			deviceIds: form.deviceLedgerIds && form.deviceLedgerIds.length > 0
+				? form.deviceLedgerIds.join(',')
+				: ''
+		};
+		
+		// 鍒犻櫎涓嶉渶瑕佺殑瀛楁
+		delete submitData.deviceLedgerIds;
+		
+		if (operationType.value === 'edit') {
+			let res = await editSparePart(submitData);
+			if (res.code === 200) {
+				ElMessage.success('缂栬緫鎴愬姛');
+				dialogVisible.value = false;
+				fetchListData();
+			}
+		} else {
+			let res = await addSparePart(submitData);
+			if (res.code === 200) {
+				ElMessage.success('鏂板鎴愬姛');
+				dialogVisible.value = false;
+				fetchListData();
+			}
+		}
+	} catch (error) {
+		ElMessage.error('璇峰~鍐欏畬鏁磋〃鍗曚俊鎭�');
+	} finally {
+		formLoading.value = false;
+	}
+};
+
+// 鐩存帴鍑哄簱
+const requisitionDialogVisible = ref(false);
+const requisitionLoading = ref(false);
+const requisitionFormRef = ref(null);
+const requisitionDeviceOptions = ref([]);
+const requisitionSparePartsOptions = ref([]);
+const selectedSparePartMaxQty = ref(1);
+const requisitionForm = reactive({
+	deviceLedgerId: null,
+	sparePartsId: null,
+	quantity: 1,
+	remark: '',
+});
+const requisitionRules = {
+	sparePartsId: [{ required: true, message: '璇烽�夋嫨澶囦欢', trigger: 'change' }],
+	quantity: [{ required: true, message: '璇疯緭鍏ラ鐢ㄦ暟閲�', trigger: 'blur' }],
+};
+
+const loadRequisitionOptions = async () => {
+	try {
+		const [deviceRes, spareRes] = await Promise.all([
+			getDeviceLedger(),
+			getSparePartsList({ current: 1, size: 9999 }),
+		]);
+		requisitionDeviceOptions.value = deviceRes.data || [];
+		requisitionSparePartsOptions.value = spareRes.data?.records || [];
+	} catch (e) {
+		ElMessage.error('鍔犺浇閫夐」澶辫触');
+	}
+};
+
+const openRequisitionDialog = async () => {
+	requisitionForm.deviceLedgerId = null;
+	requisitionForm.sparePartsId = null;
+	requisitionForm.quantity = 1;
+	requisitionForm.remark = '';
+	selectedSparePartMaxQty.value = 1;
+	await loadRequisitionOptions();
+	requisitionDialogVisible.value = true;
+};
+
+const onRequisitionSparePartChange = (val) => {
+	const selected = requisitionSparePartsOptions.value.find(item => item.id === val);
+	selectedSparePartMaxQty.value = selected ? selected.quantity : 1;
+	if (requisitionForm.quantity > selectedSparePartMaxQty.value) {
+		requisitionForm.quantity = selectedSparePartMaxQty.value;
+	}
+};
+
+const submitRequisition = async () => {
+	if (!requisitionFormRef.value) return;
+	try {
+		await requisitionFormRef.value.validate();
+	} catch {
+		return;
+	}
+	if (requisitionForm.quantity <= 0) {
+		ElMessage.warning('棰嗙敤鏁伴噺蹇呴』澶т簬0');
+		return;
+	}
+	const selected = requisitionSparePartsOptions.value.find(item => item.id === requisitionForm.sparePartsId);
+	if (selected && requisitionForm.quantity > selected.quantity) {
+		ElMessage.warning(`搴撳瓨涓嶈冻锛屽綋鍓嶅簱瀛樹负 ${selected.quantity}`);
+		return;
+	}
+	requisitionLoading.value = true;
+	try {
+		const res = await outboundSpareParts({
+			deviceLedgerId: requisitionForm.deviceLedgerId || undefined,
+			sparePartsId: requisitionForm.sparePartsId,
+			quantity: requisitionForm.quantity,
+			remark: requisitionForm.remark || undefined,
+		});
+		if (res.code === 200) {
+			ElMessage.success('棰嗙敤鎴愬姛');
+			requisitionDialogVisible.value = false;
+			fetchUsageData();
+		} else {
+			ElMessage.error(res.msg || '棰嗙敤澶辫触');
+		}
+	} catch {
+		ElMessage.error('棰嗙敤澶辫触');
+	} finally {
+		requisitionLoading.value = false;
+	}
 };
 
 // 缁勪欢鎸傝浇鏃惰幏鍙栧垪琛ㄦ暟鎹�
 onMounted(() => {
-  fetchListData();
+	fetchListData();
 });
 </script>
 
 <style scoped>
 .spare-part-category {
-  padding: 20px;
+	padding: 20px;
 }
 .search_form {
 	display: flex;
@@ -533,38 +672,38 @@
 	justify-content: space-between;
 }
 .table_list {
-  margin-top: unset;
+	margin-top: unset;
 }
 
 .pagination-container {
-  margin-top: 20px;
-  display: flex;
-  justify-content: flex-end;
+	margin-top: 20px;
+	display: flex;
+	justify-content: flex-end;
 }
 
 .el-table__header-wrapper th {
-  background-color: #f5f7fa;
-  font-weight: 600;
+	background-color: #f5f7fa;
+	font-weight: 600;
 }
 
 .el-table__row:hover > td {
-  background-color: #fafafa;
+	background-color: #fafafa;
 }
 
 /* 鎸夐挳缁勬牱寮� */
 .actions > div {
-  display: flex;
-  gap: 10px;
+	display: flex;
+	gap: 10px;
 }
 
 /* 纭繚琛ㄦ牸涓殑鎿嶄綔鎸夐挳涓嶄細琚埅鏂� */
 .el-table-column--fixed-right .el-button {
-  margin: 0 2px;
+	margin: 0 2px;
 }
 
 /* 鏍戝舰鑺傜偣鍐呭鏍峰紡 */
 .nested-tree .el-tree-node__expand-icon {
-  font-size: 12px;
-  margin-right: 4px;
+	font-size: 12px;
+	margin-right: 4px;
 }
 </style>
\ No newline at end of file
diff --git a/src/views/equipmentManagement/upkeep/index.vue b/src/views/equipmentManagement/upkeep/index.vue
index 245a162..9b92281 100644
--- a/src/views/equipmentManagement/upkeep/index.vue
+++ b/src/views/equipmentManagement/upkeep/index.vue
@@ -1,652 +1,726 @@
 <template>
-  <div class="app-container">
-    <el-tabs v-model="activeTab"
-             @tab-change="handleTabChange">
-      <!-- 淇濆吇浠诲姟tab -->
-      <el-tab-pane label="淇濆吇浠诲姟"
-                   name="scheduled">
-        <div class="search_form">
-          <el-form :model="scheduledFilters"
-                   :inline="true">
-            <el-form-item label="浠诲姟鍚嶇О">
-              <el-input v-model="scheduledFilters.taskName"
-                        style="width: 240px"
-                        placeholder="璇疯緭鍏ヤ换鍔″悕绉�"
-                        clearable
-                        :prefix-icon="Search"
-                        @change="getScheduledTableData" />
-            </el-form-item>
-            <el-form-item label="浠诲姟鐘舵��">
-              <el-select v-model="scheduledFilters.status"
-                         placeholder="璇烽�夋嫨浠诲姟鐘舵��"
-                         clearable
-                         style="width: 200px">
-                <el-option label="鍚敤"
-                           value="1" />
-                <el-option label="鍋滅敤"
-                           value="0" />
-              </el-select>
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary"
-                         @click="getScheduledTableData">鎼滅储</el-button>
-              <el-button @click="resetScheduledFilters">閲嶇疆</el-button>
-            </el-form-item>
-          </el-form>
-        </div>
-        <div class="table_list">
-          <div class="actions">
-            <el-text class="mx-1"
-                     size="large">淇濆吇浠诲姟</el-text>
-            <div>
-              <el-button type="primary"
-                         icon="Plus"
-                         @click="addScheduledTask">
-                鏂板浠诲姟
-              </el-button>
-              <el-button type="danger"
-                         icon="Delete"
-                         :disabled="scheduledMultipleList.length <= 0"
-                         @click="delScheduledTaskByIds(scheduledMultipleList.map((item) => item.id))">
-                鎵归噺鍒犻櫎
-              </el-button>
-            </div>
-          </div>
-          <PIMTable rowKey="id"
-                    isSelection
-                    :column="scheduledColumns"
-                    :tableData="scheduledDataList"
-                    :page="{
+	<div class="app-container">
+		<el-tabs v-model="activeTab"
+		         @tab-change="handleTabChange">
+			<!-- 淇濆吇浠诲姟tab -->
+			<el-tab-pane label="淇濆吇浠诲姟"
+			             name="scheduled">
+				<div class="search_form">
+					<el-form :model="scheduledFilters"
+					         :inline="true">
+						<el-form-item label="浠诲姟鍚嶇О">
+							<el-input v-model="scheduledFilters.taskName"
+							          style="width: 240px"
+							          placeholder="璇疯緭鍏ヤ换鍔″悕绉�"
+							          clearable
+							          :prefix-icon="Search"
+							          @change="getScheduledTableData" />
+						</el-form-item>
+						<el-form-item label="浠诲姟鐘舵��">
+							<el-select v-model="scheduledFilters.status"
+							           placeholder="璇烽�夋嫨浠诲姟鐘舵��"
+							           clearable
+							           style="width: 200px">
+								<el-option label="鍚敤"
+								           value="1" />
+								<el-option label="鍋滅敤"
+								           value="0" />
+							</el-select>
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary"
+							           @click="getScheduledTableData">鎼滅储</el-button>
+							<el-button @click="resetScheduledFilters">閲嶇疆</el-button>
+						</el-form-item>
+					</el-form>
+				</div>
+				<div class="table_list">
+					<div class="actions">
+						<el-text class="mx-1"
+						         size="large">淇濆吇浠诲姟</el-text>
+						<div>
+							<el-button type="primary"
+							           icon="Plus"
+							           @click="addScheduledTask">
+								鏂板浠诲姟
+							</el-button>
+							<el-button type="danger"
+							           icon="Delete"
+							           :disabled="scheduledMultipleList.length <= 0"
+							           @click="delScheduledTaskByIds(scheduledMultipleList.map((item) => item.id))">
+								鎵归噺鍒犻櫎
+							</el-button>
+						</div>
+					</div>
+					<PIMTable rowKey="id"
+					          isSelection
+					          :column="scheduledColumns"
+					          :tableData="scheduledDataList"
+					          :page="{
               current: scheduledPagination.currentPage,
               size: scheduledPagination.pageSize,
               total: scheduledPagination.total,
             }"
-                    @selection-change="handleScheduledSelectionChange"
-                    @pagination="changeScheduledPage">
-            <template #statusRef="{ row }">
-              <el-tag v-if="row.status === 1"
-                      type="success">鍚敤</el-tag>
-              <el-tag v-if="row.status === 0"
-                      type="danger">鍋滅敤</el-tag>
-            </template>
-            <template #operation="{ row }">
-              <el-button type="primary"
-                         link
-                         @click="editScheduledTask(row)">
-                缂栬緫
-              </el-button>
-              <el-button type="danger"
-                         link
-                         @click="delScheduledTaskByIds(row.id)">
-                鍒犻櫎
-              </el-button>
-            </template>
-          </PIMTable>
-        </div>
-      </el-tab-pane>
-      <!-- 淇濆吇璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛� -->
-      <el-tab-pane label="淇濆吇璁板綍"
-                   name="record">
-        <div class="search_form">
-          <el-form :model="filters"
-                   :inline="true">
-            <el-form-item label="璁惧鍚嶇О">
-              <el-input v-model="filters.deviceName"
-                        style="width: 240px"
-                        placeholder="璇疯緭鍏ヨ澶囧悕绉�"
-                        clearable
-                        :prefix-icon="Search"
-                        @change="getTableData" />
-            </el-form-item>
-            <el-form-item label="璁″垝淇濆吇鏃ユ湡">
-              <el-date-picker v-model="filters.maintenancePlanTime"
-                              type="date"
-                              placeholder="璇烽�夋嫨璁″垝淇濆吇鏃ユ湡"
-                              size="default"
-                              @change="(date) => handleDateChange(date,2)" />
-            </el-form-item>
-            <el-form-item label="瀹為檯淇濆吇鏃ユ湡">
-              <el-date-picker v-model="filters.maintenanceActuallyTime"
-                              type="date"
-                              placeholder="璇烽�夋嫨瀹為檯淇濆吇鏃ユ湡"
-                              size="default"
-                              @change="(date) => handleDateChange(date,1)" />
-            </el-form-item>
-            <el-form-item label="瀹為檯淇濆吇浜�">
-              <el-input v-model="filters.maintenanceActuallyName"
-                        style="width: 240px"
-                        placeholder="璇疯緭鍏ュ疄闄呬繚鍏讳汉"
-                        clearable
-                        :prefix-icon="Search"
-                        @change="getTableData" />
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary"
-                         @click="getTableData">鎼滅储</el-button>
-              <el-button @click="resetFilters">閲嶇疆</el-button>
-            </el-form-item>
-          </el-form>
-        </div>
-        <div class="table_list">
-          <div class="actions">
-            <el-text class="mx-1"
-                     size="large">淇濆吇璁板綍</el-text>
-            <div>
-              <el-button type="success"
-                         icon="Van"
-                         @click="addPlan">
-                鏂板璁″垝
-              </el-button>
-              <el-button @click="handleOut">
-                瀵煎嚭
-              </el-button>
-              <el-button type="danger"
-                         icon="Delete"
-                         :disabled="multipleList.length <= 0 || hasFinishedStatus"
-                         @click="delRepairByIds(multipleList.map((item) => item.id))">
-                鎵归噺鍒犻櫎
-              </el-button>
-            </div>
-          </div>
-          <PIMTable rowKey="id"
-                    isSelection
-                    :column="columns"
-                    :tableData="dataList"
-                    :page="{
-          current: pagination.currentPage,
-          size: pagination.pageSize,
-          total: pagination.total,
-        }"
-                    @selection-change="handleSelectionChange"
-                    @pagination="changePage">
-            <template #maintenanceResultRef="{ row }">
-              <div>{{ row.maintenanceResult || '-' }}</div>
-            </template>
-            <template #statusRef="{ row }">
-              <el-tag v-if="row.status === 2"
-                      type="danger">澶辫触</el-tag>
-              <el-tag v-if="row.status === 1"
-                      type="success">瀹岀粨</el-tag>
-              <el-tag v-if="row.status === 0"
-                      type="warning">寰呬繚鍏�</el-tag>
-            </template>
-            <template #operation="{ row }">
-              <!-- 杩欎釜鍔熻兘璺熸柊澧炰繚鍏诲姛鑳戒竴妯′竴鏍凤紝鏈夊暐鎰忎箟锛� -->
-              <!-- <el-button
-              type="primary"
-              text
-              @click="addMaintain(row)"
-          >
-            鏂板淇濆吇
-          </el-button> -->
-              <el-button type="primary"
-                         link
-                         :disabled="row.status === 1"
-                         @click="editPlan(row.id)">
-                缂栬緫
-              </el-button>
-              <el-button type="success"
-                         link
-                         :disabled="row.status === 1"
-                         @click="addMaintain(row)">
-                淇濆吇
-              </el-button>
-              <el-button type="danger"
-                         link
-                         :disabled="row.status === 1"
-                         @click="delRepairByIds(row.id)">
-                鍒犻櫎
-              </el-button>
-              <el-button type="primary"
-                         link
-                         @click="openFileDialog(row)">
-                闄勪欢
-              </el-button>
-            </template>
-          </PIMTable>
-        </div>
-      </el-tab-pane>
-    </el-tabs>
-    <PlanModal ref="planModalRef"
-               @ok="getTableData" />
-    <MaintenanceModal ref="maintainModalRef"
-                      @ok="getTableData" />
-    <FormDia ref="formDiaRef"
-             @closeDia="getScheduledTableData" />
-    <FileList v-if="fileDialogVisible"
-              v-model:visible="fileDialogVisible"
-              :record-type="'device_maintenance'"
-              :record-id="currentMaintenanceTaskId" />
-  </div>
+					          @selection-change="handleScheduledSelectionChange"
+					          @pagination="changeScheduledPage">
+						<template #statusRef="{ row }">
+							<el-tag v-if="row.status === 1"
+							        type="success">鍚敤</el-tag>
+							<el-tag v-if="row.status === 0"
+							        type="danger">鍋滅敤</el-tag>
+						</template>
+						<template #operation="{ row }">
+							<el-button type="primary"
+							           link
+							           @click="editScheduledTask(row)">
+								缂栬緫
+							</el-button>
+							<el-button type="danger"
+							           link
+							           @click="delScheduledTaskByIds(row.id)">
+								鍒犻櫎
+							</el-button>
+						</template>
+					</PIMTable>
+				</div>
+			</el-tab-pane>
+			<!-- 淇濆吇璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛� -->
+			<el-tab-pane label="淇濆吇璁板綍"
+			             name="record">
+				<div class="search_form">
+					<el-form :model="filters"
+					         :inline="true">
+						<el-form-item label="璁惧鍚嶇О">
+							<el-input v-model="filters.deviceName"
+							          style="width: 240px"
+							          placeholder="璇疯緭鍏ヨ澶囧悕绉�"
+							          clearable
+							          :prefix-icon="Search"
+							          @change="getTableData" />
+						</el-form-item>
+						<el-form-item label="璁″垝淇濆吇鏃ユ湡">
+							<el-date-picker v-model="filters.maintenancePlanTime"
+							                type="date"
+							                placeholder="璇烽�夋嫨璁″垝淇濆吇鏃ユ湡"
+							                size="default"
+							                @change="(date) => handleDateChange(date,2)" />
+						</el-form-item>
+						<el-form-item label="瀹為檯淇濆吇鏃ユ湡">
+							<el-date-picker v-model="filters.maintenanceActuallyTime"
+							                type="date"
+							                placeholder="璇烽�夋嫨瀹為檯淇濆吇鏃ユ湡"
+							                size="default"
+							                @change="(date) => handleDateChange(date,1)" />
+						</el-form-item>
+						<el-form-item label="瀹為檯淇濆吇浜�">
+							<el-input v-model="filters.maintenanceActuallyName"
+							          style="width: 240px"
+							          placeholder="璇疯緭鍏ュ疄闄呬繚鍏讳汉"
+							          clearable
+							          :prefix-icon="Search"
+							          @change="getTableData" />
+						</el-form-item>
+						<el-form-item>
+							<el-button type="primary"
+							           @click="getTableData">鎼滅储</el-button>
+							<el-button @click="resetFilters">閲嶇疆</el-button>
+						</el-form-item>
+					</el-form>
+				</div>
+				<div class="table_list">
+					<div class="actions">
+						<el-text class="mx-1"
+						         size="large">淇濆吇璁板綍</el-text>
+						<div>
+							<el-button type="success"
+							           icon="Van"
+							           @click="addPlan">
+								鏂板璁″垝
+							</el-button>
+							<el-button @click="handleOut">
+								瀵煎嚭
+							</el-button>
+							<el-button type="danger"
+							           icon="Delete"
+							           :disabled="multipleList.length <= 0"
+							           @click="delRepairByIds(multipleList.map((item) => item.id))">
+								鎵归噺鍒犻櫎
+							</el-button>
+						</div>
+					</div>
+					<el-table
+						:data="groupedDataList"
+						border
+						v-loading="tableLoading"
+						@selection-change="handleSelectionChange"
+						:expand-row-keys="expandedRowKeys"
+						:row-key="(row) => row.date"
+						@expand-change="onExpandChange"
+					>
+						<el-table-column type="selection" width="55" />
+						<el-table-column type="expand">
+							<template #default="props">
+								<el-table :data="props.row.children" border row-key="id">
+									<el-table-column label="璁惧鍚嶇О" prop="deviceName" align="center" />
+									<el-table-column label="瑙勬牸鍨嬪彿" prop="deviceModel" align="center" />
+									<el-table-column label="褰曞叆浜�" prop="createUserName" align="center" />
+									<el-table-column label="淇濆吇椤圭洰" prop="machineryCategory" align="center">
+										<template #default="{ row }">
+											{{ row.machineryCategory || '--' }}
+										</template>
+									</el-table-column>
+									<el-table-column label="瀹為檯淇濆吇浜�" prop="maintenanceActuallyName" align="center" />
+									<el-table-column label="瀹為檯淇濆吇鏃ユ湡" align="center">
+										<template #default="{ row }">
+											{{ row.maintenanceActuallyTime ? dayjs(row.maintenanceActuallyTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
+										</template>
+									</el-table-column>
+									<el-table-column label="淇濆吇缁撴灉" align="center">
+										<template #default="{ row }">
+											{{ row.maintenanceResult || '-' }}
+										</template>
+									</el-table-column>
+									<el-table-column label="鐘舵��" align="center">
+										<template #default="{ row }">
+											<el-tag v-if="row.status === 2" type="danger">澶辫触</el-tag>
+											<el-tag v-if="row.status === 1" type="success">瀹岀粨</el-tag>
+											<el-tag v-if="row.status === 0" type="warning">寰呬繚鍏�</el-tag>
+										</template>
+									</el-table-column>
+									<el-table-column label="鎿嶄綔" align="center" width="300">
+										<template #default="{ row }">
+											<el-button type="primary" link :disabled="row.status === 1" @click="editPlan(row.id)">缂栬緫</el-button>
+											<el-button type="success" link :disabled="row.status !== 0" @click="handleSingleComplete(row.id)">淇濆吇瀹屾垚</el-button>
+											<el-button type="success" link :disabled="row.status === 1" @click="addMaintain(row)">淇濆吇</el-button>
+											<el-button type="danger" link :disabled="row.status === 1" @click="delRepairByIds(row.id)">鍒犻櫎</el-button>
+											<el-button type="primary" link @click="openFileDialog(row)">闄勪欢</el-button>
+										</template>
+									</el-table-column>
+								</el-table>
+							</template>
+						</el-table-column>
+						<el-table-column label="璁″垝淇濆吇鏃ユ湡" prop="date" align="center" />
+						<el-table-column label="淇濆吇璁板綍鏁�" prop="count" align="center" />
+						<el-table-column label="寰呬繚鍏绘暟" align="center" width="120">
+							<template #default="{ row }">
+								{{ row.children.filter(c => c.status === 0).length }}
+							</template>
+						</el-table-column>
+						<el-table-column label="鎿嶄綔" align="center" width="200">
+							<template #default="{ row }">
+								<el-button
+									type="primary"
+									size="small"
+									:disabled="!row.children.some(c => c.status === 0)"
+									@click="handleBatchComplete(row.date)"
+								>
+									涓�閿繚鍏诲畬鎴�
+								</el-button>
+							</template>
+						</el-table-column>
+					</el-table>
+					<Pagination
+						v-show="pagination.total > 0"
+						:total="pagination.total"
+						layout="total, sizes, prev, pager, next, jumper"
+						:page="pagination.currentPage"
+						:limit="pagination.pageSize"
+						@pagination="changePage"
+					/>
+				</div>
+			</el-tab-pane>
+		</el-tabs>
+		<PlanModal ref="planModalRef"
+		           @ok="getTableData" />
+		<MaintenanceModal ref="maintainModalRef"
+		                  @ok="getTableData" />
+		<FormDia ref="formDiaRef"
+		         @closeDia="getScheduledTableData" />
+		<FileList v-if="fileDialogVisible"
+		          v-model:visible="fileDialogVisible"
+		          :record-type="'device_maintenance'"
+		          :record-id="currentMaintenanceTaskId" />
+	
+	</div>
 </template>
 
 <script setup>
-  import {
-    ref,
-    onMounted,
-    reactive,
-    getCurrentInstance,
-    nextTick,
-    computed,
-    defineAsyncComponent,
-  } from "vue";
-  import { Search } from "@element-plus/icons-vue";
-  import { ElMessage, ElMessageBox } from "element-plus";
-  import PlanModal from "./Form/PlanModal.vue";
-  import MaintenanceModal from "./Form/MaintenanceModal.vue";
-  import FormDia from "./Form/formDia.vue";
-  import {
-    getUpkeepPage,
-    delUpkeep,
-    deviceMaintenanceTaskList,
-    deviceMaintenanceTaskDel,
-  } from "@/api/equipmentManagement/upkeep";
-  import dayjs from "dayjs";
+import {
+	ref,
+	onMounted,
+	reactive,
+	getCurrentInstance,
+	nextTick,
+	computed,
+	defineAsyncComponent,
+} from "vue";
+import { Search } from "@element-plus/icons-vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import PlanModal from "./Form/PlanModal.vue";
+import MaintenanceModal from "./Form/MaintenanceModal.vue";
+import FormDia from "./Form/formDia.vue";
+import {
+	getUpkeepPage,
+	delUpkeep,
+	deviceMaintenanceTaskList,
+	deviceMaintenanceTaskDel,
+	batchCompleteMaintenance,
+	completeMaintenance,
+} from "@/api/equipmentManagement/upkeep";
+import dayjs from "dayjs";
+import Pagination from "@/components/PIMTable/Pagination.vue";
 
-  const { proxy } = getCurrentInstance();
-  const FileList = defineAsyncComponent(() =>
-    import("@/components/Dialog/FileList.vue")
-  );
+const { proxy } = getCurrentInstance();
+const FileList = defineAsyncComponent(() =>
+	import("@/components/Dialog/FileList.vue")
+);
 
-  // Tab鐩稿叧
-  const activeTab = ref("scheduled");
+// Tab鐩稿叧
+const activeTab = ref("scheduled");
 
-  // 璁″垝寮圭獥鎺у埗鍣�
-  const planModalRef = ref();
-  // 淇濆吇寮圭獥鎺у埗鍣�
-  const maintainModalRef = ref();
-  // 瀹氭椂浠诲姟寮圭獥鎺у埗鍣�
-  const formDiaRef = ref();
-  // 闄勪欢寮圭獥
-  const fileListDialogRef = ref(null);
-  const fileDialogVisible = ref(false);
-  const currentMaintenanceTaskId = ref(null);
+// 璁″垝寮圭獥鎺у埗鍣�
+const planModalRef = ref();
+// 淇濆吇寮圭獥鎺у埗鍣�
+const maintainModalRef = ref();
+// 瀹氭椂浠诲姟寮圭獥鎺у埗鍣�
+const formDiaRef = ref();
+// 闄勪欢寮圭獥
+const fileListDialogRef = ref(null);
+const fileDialogVisible = ref(false);
+const currentMaintenanceTaskId = ref(null);
 
-  // 淇濆吇璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛夌浉鍏冲彉閲�
-  const filters = reactive({
-    deviceName: "",
-    maintenancePlanTime: "",
-    maintenanceActuallyTime: "",
-    maintenanceActuallyName: "",
-  });
+// 淇濆吇璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛夌浉鍏冲彉閲�
+const filters = reactive({
+	deviceName: "",
+	maintenancePlanTime: "",
+	maintenanceActuallyTime: "",
+	maintenanceActuallyName: "",
+});
 
-  const dataList = ref([]);
-  const pagination = ref({
-    currentPage: 1,
-    pageSize: 10,
-    total: 0,
-  });
-  const multipleList = ref([]);
+const dataList = ref([]);
+const pagination = ref({
+	currentPage: 1,
+	pageSize: 100,
+	total: 0,
+});
+const multipleList = ref([]);
+const tableLoading = ref(false);
+const expandedRowKeys = ref([]);
 
-  // 淇濆吇浠诲姟tab鐩稿叧鍙橀噺
-  const scheduledFilters = reactive({
-    taskName: "",
-    status: "",
-  });
+const groupByDate = (list) => {
+	const map = {};
+	list.forEach(item => {
+		const date = item.dateStr || (item.maintenancePlanTime ? dayjs(item.maintenancePlanTime).format('YYYY-MM-DD') : (item.createTime ? dayjs(item.createTime).format('YYYY-MM-DD') : '鏈煡鏃ユ湡'));
+		if (!map[date]) map[date] = [];
+		map[date].push(item);
+	});
+	return Object.entries(map).map(([date, children]) => ({ date, children, count: children.length }));
+};
 
-  const scheduledDataList = ref([]);
-  const scheduledPagination = reactive({
-    currentPage: 1,
-    pageSize: 10,
-    total: 0,
-  });
-  const scheduledMultipleList = ref([]);
+const groupedDataList = computed(() => groupByDate(dataList.value));
 
-  // 淇濆吇浠诲姟琛ㄦ牸鍒楅厤缃�
-  const scheduledColumns = ref([
-    { prop: "taskName", label: "璁惧鍚嶇О" },
-    {
-      label: "瑙勬牸鍨嬪彿",
-      prop: "deviceModel",
-    },
-    {
-      label: "淇濆吇椤圭洰",
-      prop: "machineryCategory",
-      minWidth: 120,
-      formatData: cell => cell || "--",
-    },
-    {
-      prop: "frequencyType",
-      label: "棰戞",
-      minWidth: 150,
-      // PIMTable 浣跨敤鐨勬槸 formatData锛岃�屼笉鏄� Element-Plus 鐨� formatter
-      formatData: cell =>
-        ({
-          DAILY: "姣忔棩",
-          WEEKLY: "姣忓懆",
-          MONTHLY: "姣忔湀",
-          QUARTERLY: "瀛e害",
-        }[cell] || ""),
-    },
-    {
-      prop: "frequencyDetail",
-      label: "寮�濮嬫棩鏈熶笌鏃堕棿",
-      minWidth: 150,
-      // 鍚屾牱鏀圭敤 formatData锛孭IMTable 鍐呴儴浼氭妸鍗曞厓鏍煎�间紶杩涙潵
-      formatData: cell => {
-        if (typeof cell !== "string") return "";
-        let val = cell;
-        const replacements = {
-          MON: "鍛ㄤ竴",
-          TUE: "鍛ㄤ簩",
-          WED: "鍛ㄤ笁",
-          THU: "鍛ㄥ洓",
-          FRI: "鍛ㄤ簲",
-          SAT: "鍛ㄥ叚",
-          SUN: "鍛ㄦ棩",
-        };
-        // 浣跨敤姝e垯涓�娆℃�ф浛鎹㈡墍鏈夊尮閰嶉」
-        return val.replace(
-          /MON|TUE|WED|THU|FRI|SAT|SUN/g,
-          match => replacements[match]
-        );
-      },
-    },
-    { prop: "maintenancePerson", label: "淇濆吇浜�", minWidth: 100 },
-    { prop: "registrant", label: "鐧昏浜�", minWidth: 100 },
-    {
-      prop: "registrationDate",
-      label: "鐧昏鏃ユ湡",
-      minWidth: 100,
-      formatData: cell =>
-        cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-",
-    },
-    {
-      fixed: "right",
-      label: "鎿嶄綔",
-      dataType: "slot",
-      slot: "operation",
-      align: "center",
-      width: "200px",
-    },
-  ]);
+// 鍗曟潯淇濆吇瀹屾垚
+const handleSingleComplete = async (id) => {
+	try {
+		await ElMessageBox.confirm('纭灏嗚淇濆吇璁板綍鏍囪涓哄畬鎴愶紵', '鎻愮ず', { type: 'warning' });
+		const res = await completeMaintenance(id);
+		if (res.code === 200) {
+			ElMessage.success('淇濆吇瀹屾垚');
+			getTableData();
+		} else {
+			ElMessage.error(res.msg || '鎿嶄綔澶辫触');
+		}
+	} catch { /* 鍙栨秷 */ }
+};
 
-  // 淇濆吇璁板綍琛ㄦ牸鍒楅厤缃紙鍘熻澶囦繚鍏昏〃鏍煎垪锛�
-  const columns = ref([
-    {
-      label: "璁惧鍚嶇О",
-      align: "center",
-      prop: "deviceName",
-    },
-    {
-      label: "瑙勬牸鍨嬪彿",
-      align: "center",
-      prop: "deviceModel",
-    },
-    {
-      label: "璁″垝淇濆吇鏃ユ湡",
-      align: "center",
-      prop: "maintenancePlanTime",
-      formatData: cell => {
-        return cell == null ? "-" : dayjs(cell).format("YYYY-MM-DD");
-      },
-    },
-    {
-      label: "褰曞叆浜�",
-      align: "center",
-      prop: "createUserName",
-    },
-    {
-      label: "淇濆吇椤圭洰",
-      align: "center",
-      prop: "machineryCategory",
-      formatData: cell => cell || "--",
-    },
-    // {
-    //   label: "褰曞叆鏃ユ湡",
-    //   align: "center",
-    //   prop: "createTime",
-    //   formatData: (cell) => dayjs(cell).format("YYYY-MM-DD HH:mm:ss"),
-    //   width: 200,
-    // },
-    {
-      label: "瀹為檯淇濆吇浜�",
-      align: "center",
-      prop: "maintenanceActuallyName",
-    },
-    {
-      label: "瀹為檯淇濆吇鏃ユ湡",
-      align: "center",
-      prop: "maintenanceActuallyTime",
-      formatData: cell =>
-        cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-",
-    },
-    {
-      label: "淇濆吇缁撴灉",
-      align: "center",
-      prop: "maintenanceResult",
-      dataType: "slot",
-      slot: "maintenanceResultRef",
-    },
-    {
-      label: "鐘舵��",
-      align: "center",
-      prop: "status",
-      dataType: "slot",
-      slot: "statusRef",
-    },
-    {
-      fixed: "right",
-      label: "鎿嶄綔",
-      dataType: "slot",
-      slot: "operation",
-      align: "center",
-      width: "350px",
-    },
-  ]);
+// 涓�閿繚鍏诲畬鎴�
+const handleBatchComplete = async (dateStr) => {
+	try {
+		await ElMessageBox.confirm(`纭灏� ${dateStr} 鐨勬墍鏈夊緟淇濆吇璁板綍鏍囪涓哄畬鎴愶紵`, '鎻愮ず', { type: 'warning' });
+		const res = await batchCompleteMaintenance(dateStr);
+		if (res.code === 200) {
+			ElMessage.success(res.msg || '鎵归噺淇濆吇瀹屾垚鎴愬姛');
+			getTableData();
+		} else {
+			ElMessage.error(res.msg || '鎵归噺淇濆吇瀹屾垚澶辫触');
+		}
+	} catch { /* 鍙栨秷 */ }
+};
 
-  // Tab鍒囨崲澶勭悊
-  const handleTabChange = tabName => {
-    if (tabName === "record") {
-      getTableData();
-    } else if (tabName === "scheduled") {
-      getScheduledTableData();
-    }
-  };
+const onExpandChange = (row, expandedRows) => {
+	expandedRowKeys.value = expandedRows.map(r => r.date);
+};
 
-  // 淇濆吇浠诲姟鐩稿叧鏂规硶
-  const getScheduledTableData = async () => {
-    try {
-      const params = {
-        current: scheduledPagination.currentPage,
-        size: scheduledPagination.pageSize,
-        taskName: scheduledFilters.taskName || undefined,
-        status: scheduledFilters.status || undefined,
-      };
-      const { code, data } = await deviceMaintenanceTaskList(params);
-      if (code === 200) {
-        scheduledDataList.value = data?.records || [];
-        scheduledPagination.total = data?.total || 0;
-      }
-    } catch (error) {
-      ElMessage.error("鑾峰彇瀹氭椂浠诲姟鍒楄〃澶辫触");
-    }
-  };
+// 淇濆吇浠诲姟tab鐩稿叧鍙橀噺
+const scheduledFilters = reactive({
+	taskName: "",
+	status: "",
+});
 
-  const resetScheduledFilters = () => {
-    scheduledFilters.taskName = "";
-    scheduledFilters.status = "";
-    getScheduledTableData();
-  };
+const scheduledDataList = ref([]);
+const scheduledPagination = reactive({
+	currentPage: 1,
+	pageSize: 10,
+	total: 0,
+});
+const scheduledMultipleList = ref([]);
 
-  const handleScheduledSelectionChange = selection => {
-    scheduledMultipleList.value = selection;
-  };
+// 淇濆吇浠诲姟琛ㄦ牸鍒楅厤缃�
+const scheduledColumns = ref([
+	{ prop: "taskName", label: "璁惧鍚嶇О" },
+	{
+		label: "瑙勬牸鍨嬪彿",
+		prop: "deviceModel",
+	},
+	{
+		label: "淇濆吇椤圭洰",
+		prop: "machineryCategory",
+		minWidth: 120,
+		formatData: cell => cell || "--",
+	},
+	{
+		prop: "frequencyType",
+		label: "棰戞",
+		minWidth: 150,
+		// PIMTable 浣跨敤鐨勬槸 formatData锛岃�屼笉鏄� Element-Plus 鐨� formatter
+		formatData: cell =>
+			({
+				DAILY: "姣忔棩",
+				WEEKLY: "姣忓懆",
+				MONTHLY: "姣忔湀",
+				QUARTERLY: "瀛e害",
+			}[cell] || ""),
+	},
+	{
+		prop: "frequencyDetail",
+		label: "寮�濮嬫棩鏈熶笌鏃堕棿",
+		minWidth: 150,
+		// 鍚屾牱鏀圭敤 formatData锛孭IMTable 鍐呴儴浼氭妸鍗曞厓鏍煎�间紶杩涙潵
+		formatData: cell => {
+			if (typeof cell !== "string") return "";
+			let val = cell;
+			const replacements = {
+				MON: "鍛ㄤ竴",
+				TUE: "鍛ㄤ簩",
+				WED: "鍛ㄤ笁",
+				THU: "鍛ㄥ洓",
+				FRI: "鍛ㄤ簲",
+				SAT: "鍛ㄥ叚",
+				SUN: "鍛ㄦ棩",
+			};
+			// 浣跨敤姝e垯涓�娆℃�ф浛鎹㈡墍鏈夊尮閰嶉」
+			return val.replace(
+				/MON|TUE|WED|THU|FRI|SAT|SUN/g,
+				match => replacements[match]
+			);
+		},
+	},
+	{ prop: "maintenancePerson", label: "淇濆吇浜�", minWidth: 100 },
+	{ prop: "registrant", label: "鐧昏浜�", minWidth: 100 },
+	{
+		prop: "registrationDate",
+		label: "鐧昏鏃ユ湡",
+		minWidth: 100,
+		formatData: cell =>
+			cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-",
+	},
+	{
+		fixed: "right",
+		label: "鎿嶄綔",
+		dataType: "slot",
+		slot: "operation",
+		align: "center",
+		width: "200px",
+	},
+]);
 
-  const changeScheduledPage = page => {
-    scheduledPagination.currentPage = page.page;
-    scheduledPagination.pageSize = page.limit;
-    getScheduledTableData();
-  };
+// 淇濆吇璁板綍琛ㄦ牸鍒楅厤缃紙鍘熻澶囦繚鍏昏〃鏍煎垪锛�
+const columns = ref([
+	{
+		label: "璁惧鍚嶇О",
+		align: "center",
+		prop: "deviceName",
+	},
+	{
+		label: "瑙勬牸鍨嬪彿",
+		align: "center",
+		prop: "deviceModel",
+	},
+	{
+		label: "璁″垝淇濆吇鏃ユ湡",
+		align: "center",
+		prop: "maintenancePlanTime",
+		formatData: cell => {
+			return cell == null ? "-" : dayjs(cell).format("YYYY-MM-DD");
+		},
+	},
+	{
+		label: "褰曞叆浜�",
+		align: "center",
+		prop: "createUserName",
+	},
+	{
+		label: "淇濆吇椤圭洰",
+		align: "center",
+		prop: "machineryCategory",
+		formatData: cell => cell || "--",
+	},
+	// {
+	//   label: "褰曞叆鏃ユ湡",
+	//   align: "center",
+	//   prop: "createTime",
+	//   formatData: (cell) => dayjs(cell).format("YYYY-MM-DD HH:mm:ss"),
+	//   width: 200,
+	// },
+	{
+		label: "瀹為檯淇濆吇浜�",
+		align: "center",
+		prop: "maintenanceActuallyName",
+	},
+	{
+		label: "瀹為檯淇濆吇鏃ユ湡",
+		align: "center",
+		prop: "maintenanceActuallyTime",
+		formatData: cell =>
+			cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-",
+	},
+	{
+		label: "淇濆吇缁撴灉",
+		align: "center",
+		prop: "maintenanceResult",
+		dataType: "slot",
+		slot: "maintenanceResultRef",
+	},
+	{
+		label: "鐘舵��",
+		align: "center",
+		prop: "status",
+		dataType: "slot",
+		slot: "statusRef",
+	},
+	{
+		fixed: "right",
+		label: "鎿嶄綔",
+		dataType: "slot",
+		slot: "operation",
+		align: "center",
+		width: "350px",
+	},
+]);
 
-  const addScheduledTask = () => {
-    nextTick(() => {
-      formDiaRef.value?.openDialog("add");
-    });
-  };
+// Tab鍒囨崲澶勭悊
+const handleTabChange = tabName => {
+	if (tabName === "record") {
+		getTableData();
+	} else if (tabName === "scheduled") {
+		getScheduledTableData();
+	}
+};
 
-  const editScheduledTask = row => {
-    if (row) {
-      nextTick(() => {
-        formDiaRef.value?.openDialog("edit", row);
-      });
-    }
-  };
+// 淇濆吇浠诲姟鐩稿叧鏂规硶
+const getScheduledTableData = async () => {
+	try {
+		const params = {
+			current: scheduledPagination.currentPage,
+			size: scheduledPagination.pageSize,
+			taskName: scheduledFilters.taskName || undefined,
+			status: scheduledFilters.status || undefined,
+		};
+		const { code, data } = await deviceMaintenanceTaskList(params);
+		if (code === 200) {
+			scheduledDataList.value = data?.records || [];
+			scheduledPagination.total = data?.total || 0;
+		}
+	} catch (error) {
+		ElMessage.error("鑾峰彇瀹氭椂浠诲姟鍒楄〃澶辫触");
+	}
+};
 
-  const delScheduledTaskByIds = async ids => {
-    try {
-      await ElMessageBox.confirm("纭畾鍒犻櫎閫変腑鐨勫畾鏃朵换鍔″悧锛�", "鎻愮ず", {
-        type: "warning",
-      });
-      const payload = Array.isArray(ids) ? ids : [ids];
-      await deviceMaintenanceTaskDel(payload);
-      ElMessage.success("鍒犻櫎瀹氭椂浠诲姟鎴愬姛");
-      getScheduledTableData();
-    } catch (error) {
-      // 鐢ㄦ埛鍙栨秷鍒犻櫎
-    }
-  };
+const resetScheduledFilters = () => {
+	scheduledFilters.taskName = "";
+	scheduledFilters.status = "";
+	getScheduledTableData();
+};
 
-  const handleScheduledOut = () => {
-    ElMessage.info("瀵煎嚭瀹氭椂浠诲姟鍔熻兘寰呭疄鐜�");
-  };
+const handleScheduledSelectionChange = selection => {
+	scheduledMultipleList.value = selection;
+};
 
-  // 淇濆吇璁板綍鐩稿叧鏂规硶锛堝師璁惧淇濆吇椤甸潰鏂规硶锛�
-  const getTableData = async () => {
-    try {
-      const params = {
-        current: pagination.value.currentPage,
-        size: pagination.value.pageSize,
-        deviceName: filters.deviceName || undefined,
-        maintenancePlanTime: filters.maintenancePlanTime
-          ? dayjs(filters.maintenancePlanTime).format("YYYY-MM-DD")
-          : undefined,
-        maintenanceActuallyTime: filters.maintenanceActuallyTime
-          ? dayjs(filters.maintenanceActuallyTime).format("YYYY-MM-DD")
-          : undefined,
-        maintenanceActuallyName: filters.maintenanceActuallyName || undefined,
-      };
+const changeScheduledPage = page => {
+	scheduledPagination.currentPage = page.page;
+	scheduledPagination.pageSize = page.limit;
+	getScheduledTableData();
+};
 
-      const { code, data } = await getUpkeepPage(params);
-      if (code === 200) {
-        dataList.value = data.records;
-        pagination.value.total = data.total;
-      }
-    } catch (error) {
-      console.log(error);
-    }
-  };
+const addScheduledTask = () => {
+	nextTick(() => {
+		formDiaRef.value?.openDialog("add");
+	});
+};
 
-  const resetFilters = () => {
-    filters.deviceName = "";
-    filters.maintenancePlanTime = "";
-    filters.maintenanceActuallyTime = "";
-    filters.maintenanceActuallyName = "";
-    getTableData();
-  };
+const editScheduledTask = row => {
+	if (row) {
+		nextTick(() => {
+			formDiaRef.value?.openDialog("edit", row);
+		});
+	}
+};
 
-  const handleSelectionChange = selection => {
-    multipleList.value = selection;
-  };
+const delScheduledTaskByIds = async ids => {
+	try {
+		await ElMessageBox.confirm("纭畾鍒犻櫎閫変腑鐨勫畾鏃朵换鍔″悧锛�", "鎻愮ず", {
+			type: "warning",
+		});
+		const payload = Array.isArray(ids) ? ids : [ids];
+		await deviceMaintenanceTaskDel(payload);
+		ElMessage.success("鍒犻櫎瀹氭椂浠诲姟鎴愬姛");
+		getScheduledTableData();
+	} catch (error) {
+		// 鐢ㄦ埛鍙栨秷鍒犻櫎
+	}
+};
 
-  // 妫�鏌ラ�変腑鐨勮褰曚腑鏄惁鏈夊畬缁撶姸鎬佺殑
-  const hasFinishedStatus = computed(() => {
-    return multipleList.value.some(item => item.status === 1);
-  });
+const handleScheduledOut = () => {
+	ElMessage.info("瀵煎嚭瀹氭椂浠诲姟鍔熻兘寰呭疄鐜�");
+};
 
-  const changePage = page => {
-    pagination.value.currentPage = page.page;
-    pagination.value.pageSize = page.limit;
-    getTableData();
-  };
+// 淇濆吇璁板綍鐩稿叧鏂规硶锛堝師璁惧淇濆吇椤甸潰鏂规硶锛�
+const getTableData = async () => {
+	tableLoading.value = true;
+	try {
+		const params = {
+			current: pagination.value.currentPage,
+			size: pagination.value.pageSize,
+			deviceName: filters.deviceName || undefined,
+			maintenancePlanTime: filters.maintenancePlanTime
+				? dayjs(filters.maintenancePlanTime).format("YYYY-MM-DD")
+				: undefined,
+			maintenanceActuallyTime: filters.maintenanceActuallyTime
+				? dayjs(filters.maintenanceActuallyTime).format("YYYY-MM-DD")
+				: undefined,
+			maintenanceActuallyName: filters.maintenanceActuallyName || undefined,
+		};
+		
+		const { code, data } = await getUpkeepPage(params);
+		if (code === 200) {
+			dataList.value = data.records;
+			pagination.value.total = data.total;
+		}
+	} catch (error) {
+		console.log(error);
+	} finally {
+		tableLoading.value = false;
+	}
+};
 
-  const addMaintain = row => {
-    maintainModalRef.value.open(row.id, row);
-  };
+const resetFilters = () => {
+	filters.deviceName = "";
+	filters.maintenancePlanTime = "";
+	filters.maintenanceActuallyTime = "";
+	filters.maintenanceActuallyName = "";
+	getTableData();
+};
 
-  const addPlan = () => {
-    planModalRef.value.openModal();
-  };
+const handleSelectionChange = selection => {
+	// 浠庨�変腑鐨勫垎缁勮涓敹闆嗘墍鏈夊瓙璁板綍ID
+	const ids = [];
+	selection.forEach(group => {
+		if (group.children) {
+			group.children.forEach(child => ids.push(child.id));
+		}
+	});
+	multipleList.value = ids;
+};
 
-  const editPlan = id => {
-    planModalRef.value.openEdit(id);
-  };
+const changePage = page => {
+	pagination.value.currentPage = page.page;
+	pagination.value.pageSize = page.limit;
+	getTableData();
+};
 
-  const delRepairByIds = async ids => {
-    // 妫�鏌ユ槸鍚︽湁瀹岀粨鐘舵�佺殑璁板綍
-    const hasFinished = multipleList.value.some(item => item.status === 1);
-    if (hasFinished) {
-      ElMessage.warning("涓嶈兘鍒犻櫎鐘舵�佷负瀹岀粨鐨勮褰�");
-      return;
-    }
+const addMaintain = row => {
+	maintainModalRef.value.open(row.id, row);
+};
 
-    try {
-      await ElMessageBox.confirm("纭鍒犻櫎淇濆吇鏁版嵁, 姝ゆ搷浣滀笉鍙��?", "璀﹀憡", {
-        confirmButtonText: "纭畾",
-        cancelButtonText: "鍙栨秷",
-        type: "warning",
-      });
+const addPlan = () => {
+	planModalRef.value.openModal();
+};
 
-      const { code } = await delUpkeep(ids);
-      if (code === 200) {
-        ElMessage.success("鍒犻櫎鎴愬姛");
-        getTableData();
-      }
-    } catch (error) {
-      // 鐢ㄦ埛鍙栨秷鍒犻櫎
-    }
-  };
+const editPlan = id => {
+	planModalRef.value.openEdit(id);
+};
 
-  const handleOut = () => {
-    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
-      confirmButtonText: "纭",
-      cancelButtonText: "鍙栨秷",
-      type: "warning",
-    })
-      .then(() => {
-        proxy.download("/device/maintenance/export", {}, "璁惧淇濆吇.xlsx");
-      })
-      .catch(() => {
-        ElMessage.info("宸插彇娑�");
-      });
-  };
+const delRepairByIds = async ids => {
+	const idList = Array.isArray(ids) ? ids : [ids];
+	if (idList.length === 0) return;
+	
+	try {
+		await ElMessageBox.confirm("纭鍒犻櫎淇濆吇鏁版嵁, 姝ゆ搷浣滀笉鍙��?", "璀﹀憡", {
+			confirmButtonText: "纭畾",
+			cancelButtonText: "鍙栨秷",
+			type: "warning",
+		});
+		
+		const { code } = await delUpkeep(idList);
+		if (code === 200) {
+			ElMessage.success("鍒犻櫎鎴愬姛");
+			getTableData();
+		}
+	} catch (error) {
+		// 鐢ㄦ埛鍙栨秷鍒犻櫎
+	}
+};
 
-  const handleDateChange = (date, type) => {
-    if (type === 1) {
-      filters.maintenanceActuallyTime = date
-        ? dayjs(date).format("YYYY-MM-DD")
-        : "";
-    } else {
-      filters.maintenancePlanTime = date ? dayjs(date).format("YYYY-MM-DD") : "";
-    }
-    getTableData();
-  };
+const handleOut = () => {
+	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+		confirmButtonText: "纭",
+		cancelButtonText: "鍙栨秷",
+		type: "warning",
+	})
+		.then(() => {
+			proxy.download("/device/maintenance/export", {}, "璁惧淇濆吇.xlsx");
+		})
+		.catch(() => {
+			ElMessage.info("宸插彇娑�");
+		});
+};
 
-  // 鎵撳紑闄勪欢寮圭獥
-  const openFileDialog = async row => {
-    currentMaintenanceTaskId.value = row.id;
-    fileDialogVisible.value = true;
-  };
+const handleDateChange = (date, type) => {
+	if (type === 1) {
+		filters.maintenanceActuallyTime = date
+			? dayjs(date).format("YYYY-MM-DD")
+			: "";
+	} else {
+		filters.maintenancePlanTime = date ? dayjs(date).format("YYYY-MM-DD") : "";
+	}
+	getTableData();
+};
 
-  onMounted(() => {
-    // 鏍规嵁榛樿婵�娲荤殑 Tab 璋冪敤瀵瑰簲鐨勬煡璇㈡帴鍙�
-    if (activeTab.value === "scheduled") {
-      getScheduledTableData();
-    } else {
-      getTableData();
-    }
-  });
+// 鎵撳紑闄勪欢寮圭獥
+const openFileDialog = async row => {
+	currentMaintenanceTaskId.value = row.id;
+	fileDialogVisible.value = true;
+};
+
+onMounted(() => {
+	// 鏍规嵁榛樿婵�娲荤殑 Tab 璋冪敤瀵瑰簲鐨勬煡璇㈡帴鍙�
+	if (activeTab.value === "scheduled") {
+		getScheduledTableData();
+	} else {
+		getTableData();
+	}
+});
 </script>
 
 <style lang="scss" scoped>
-  .table_list {
-    margin-top: unset;
-  }
-  .actions {
-    display: flex;
-    justify-content: space-between;
-    margin-bottom: 10px;
-  }
+.table_list {
+	margin-top: unset;
+}
+.actions {
+	display: flex;
+	justify-content: space-between;
+	margin-bottom: 10px;
+}
 </style>
 
 
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue
index f4e49b6..43a4bbd 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue
@@ -1,25 +1,25 @@
 <template>
-  <div>
-    <PanelHeader title="鍑哄叆搴撹秼鍔�" />
-    <div class="main-panel panel-item-customers">
-      <div class="filters-row">
-
-        <ProductTypeSwitch v-model="productType" @change="handleFilterChange" />
-      </div>
-      <Echarts
-        ref="chart"
-        :chartStyle="chartStyle"
-        :grid="grid"
-        :legend="lineLegend"
-        :series="lineSeries"
-        :tooltip="tooltip"
-        :xAxis="xAxis1"
-        :yAxis="yAxis1"
-        :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
-        style="height: 260px"
-      />
-    </div>
-  </div>
+	<div>
+		<PanelHeader title="鍑哄叆搴撹秼鍔�" />
+		<div class="main-panel panel-item-customers">
+			<div class="filters-row">
+				
+				<ProductTypeSwitch v-model="productType" @change="handleFilterChange" />
+			</div>
+			<Echarts
+				ref="chart"
+				:chartStyle="chartStyle"
+				:grid="grid"
+				:legend="lineLegend"
+				:series="lineSeries"
+				:tooltip="tooltip"
+				:xAxis="xAxis1"
+				:yAxis="yAxis1"
+				:options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+				style="height: 260px"
+			/>
+		</div>
+	</div>
 </template>
 
 <script setup>
@@ -35,154 +35,157 @@
 const chartStyle = { width: '100%', height: '130%' }
 
 const grid = {
-  left: '3%',
-  right: '4%',
-  bottom: '3%',
-  top: '16%',
-  containLabel: true,
+	left: '3%',
+	right: '4%',
+	bottom: '3%',
+	top: '16%',
+	containLabel: true,
 }
 
 const lineLegend = {
-  show: true,
-  top: '2%',
-  left: 'center',
-  itemGap: 24,
-  itemWidth: 12,
-  itemHeight: 12,
-  textStyle: { color: '#B8C8E0', fontSize: 14 },
-  data: [
-    { name: '鍑哄簱', itemStyle: { color: 'rgba(11, 137, 254, 1)' } },
-    { name: '鍏ュ簱', itemStyle: { color: 'rgba(11, 249, 254, 1)' } },
-  ],
+	show: true,
+	top: '2%',
+	left: 'center',
+	itemGap: 24,
+	itemWidth: 12,
+	itemHeight: 12,
+	textStyle: { color: '#B8C8E0', fontSize: 14 },
+	data: [
+		{ name: '鍑哄簱', itemStyle: { color: 'rgba(11, 137, 254, 1)' } },
+		{ name: '鍏ュ簱', itemStyle: { color: 'rgba(11, 249, 254, 1)' } },
+	],
 }
 
 const xAxis1 = ref([
-  {
-    type: 'category',
-    data: [],
-    axisTick: { show: false },
-    axisLine: { show: false, lineStyle: { color: 'rgba(184, 200, 224, 0.3)' } },
-    axisLabel: { color: '#B8C8E0', fontSize: 12 },
-    splitLine: { show: false, lineStyle: { type: 'dashed', color: 'rgba(184, 200, 224, 0.2)' } },
-  },
+	{
+		type: 'category',
+		data: [],
+		axisTick: { show: false },
+		axisLine: { show: false, lineStyle: { color: 'rgba(184, 200, 224, 0.3)' } },
+		axisLabel: { color: '#B8C8E0', fontSize: 12 },
+		splitLine: { show: false, lineStyle: { type: 'dashed', color: 'rgba(184, 200, 224, 0.2)' } },
+	},
 ])
 
 const yAxis1 = [
-  {
-    type: 'value',
-    name: '鍗曚綅: 浠�',
-    nameTextStyle: { color: '#B8C8E0', fontSize: 12, padding: [0, 0, 0, 0] },
-    axisLine: { show: false },
-    axisTick: { show: false },
-    axisLabel: { color: '#B8C8E0', fontSize: 12 },
-    splitLine: { lineStyle: { color: '#B8C8E0' } },
-  },
+	{
+		type: 'value',
+		name: '鍗曚綅: 浠�',
+		nameTextStyle: { color: '#B8C8E0', fontSize: 12, padding: [0, 0, 0, 0] },
+		axisLine: { show: false },
+		axisTick: { show: false },
+		axisLabel: { color: '#B8C8E0', fontSize: 12 },
+		splitLine: { lineStyle: { color: '#B8C8E0' } },
+	},
 ]
 
 const lineSeries = ref([
-  {
-    name: '鍑哄簱',
-    type: 'line',
-    smooth: false,
-    showSymbol: true,
-    symbol: 'circle',
-    symbolSize: 8,
-    lineStyle: { color: 'rgba(11, 137, 254, 1)', width: 2 },
-    itemStyle: { color: 'rgba(11, 137, 254, 1)', borderWidth: 0 },
-    areaStyle: {
-      color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-        { offset: 0, color: 'rgba(11, 137, 254, 0.40)' },
-        { offset: 1, color: 'rgba(11, 137, 254, 0.05)' },
-      ]),
-    },
-    data: [],
-    emphasis: { focus: 'series' },
-  },
-  {
-    name: '鍏ュ簱',
-    type: 'line',
-    smooth: false,
-    showSymbol: true,
-    symbol: 'circle',
-    symbolSize: 8,
-    lineStyle: { color: 'rgba(11, 249, 254, 1)', width: 2 },
-    itemStyle: { color: 'rgba(11, 249, 254, 1)', borderWidth: 0 },
-    areaStyle: {
-      color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-        { offset: 0, color: 'rgba(11, 249, 254, 0.5)' },
-        { offset: 1, color: 'rgba(11, 249, 254, 0.05)' },
-      ]),
-    },
-    data: [],
-    emphasis: { focus: 'series' },
-  },
+	{
+		name: '鍑哄簱',
+		type: 'line',
+		smooth: false,
+		showSymbol: true,
+		symbol: 'circle',
+		symbolSize: 8,
+		lineStyle: { color: 'rgba(11, 137, 254, 1)', width: 2 },
+		itemStyle: { color: 'rgba(11, 137, 254, 1)', borderWidth: 0 },
+		areaStyle: {
+			color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+				{ offset: 0, color: 'rgba(11, 137, 254, 0.40)' },
+				{ offset: 1, color: 'rgba(11, 137, 254, 0.05)' },
+			]),
+		},
+		data: [],
+		emphasis: { focus: 'series' },
+	},
+	{
+		name: '鍏ュ簱',
+		type: 'line',
+		smooth: false,
+		showSymbol: true,
+		symbol: 'circle',
+		symbolSize: 8,
+		lineStyle: { color: 'rgba(11, 249, 254, 1)', width: 2 },
+		itemStyle: { color: 'rgba(11, 249, 254, 1)', borderWidth: 0 },
+		areaStyle: {
+			color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+				{ offset: 0, color: 'rgba(11, 249, 254, 0.5)' },
+				{ offset: 1, color: 'rgba(11, 249, 254, 0.05)' },
+			]),
+		},
+		data: [],
+		emphasis: { focus: 'series' },
+	},
 ])
 
 const tooltip = {
-  trigger: 'axis',
-  axisPointer: { type: 'line' },
-  borderWidth: 1,
-  textStyle: { fontSize: 12 },
-  formatter(params) {
-    let result = params[0].axisValue + '<br/>'
-    params.forEach((item) => {
-      result += `${item.marker} ${item.seriesName}: ${item.value} 浠�<br/>`
-    })
-    return result
-  },
+	trigger: 'axis',
+	axisPointer: { type: 'line' },
+	borderWidth: 1,
+	textStyle: { fontSize: 12 },
+	formatter(params) {
+		let result = params[0].axisValue + '<br/>'
+		params.forEach((item) => {
+			result += `${item.marker} ${item.seriesName}: ${item.value} 浠�<br/>`
+		})
+		return result
+	},
 }
 
+const dateRange = inject('psiDateRange', null)
+
 const fetchData = () => {
-  productInOutAnalysis({ type: productType.value })
-    .then((res) => {
-      if (res.code === 200 && Array.isArray(res.data)) {
-        const list = res.data
-        xAxis1.value[0].data = list.map((d) => d.date)
-        lineSeries.value[0].data = list.map((d) => Number(d.outCount) || 0)
-        lineSeries.value[1].data = list.map((d) => Number(d.inCount) || 0)
-      }
-    })
-    .catch((err) => {
-      console.error('鑾峰彇浜у搧鍑哄叆搴撳垎鏋愬け璐�:', err)
-    })
+	const params = { type: productType.value, ...(dateRange?.value || {}) }
+	productInOutAnalysis(params)
+		.then((res) => {
+			if (res.code === 200 && Array.isArray(res.data)) {
+				const list = res.data
+				xAxis1.value[0].data = list.map((d) => d.date)
+				lineSeries.value[0].data = list.map((d) => Number(d.outCount) || 0)
+				lineSeries.value[1].data = list.map((d) => Number(d.inCount) || 0)
+			}
+		})
+		.catch((err) => {
+			console.error('鑾峰彇浜у搧鍑哄叆搴撳垎鏋愬け璐�:', err)
+		})
 }
 
 const handleFilterChange = () => {
-  fetchData()
+	fetchData()
 }
 
 const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
 if (dataDashboardRefreshTick) {
-  watch(dataDashboardRefreshTick, () => {
-    fetchData()
-  })
+	watch(dataDashboardRefreshTick, () => {
+		fetchData()
+	})
 }
 
 onMounted(() => {
-  fetchData()
+	fetchData()
 })
 </script>
 
 <style scoped>
 .main-panel {
-  display: flex;
-  flex-direction: column;
-  gap: 20px;
+	display: flex;
+	flex-direction: column;
+	gap: 20px;
 }
 
 .filters-row {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  gap: 12px;
-  margin-bottom: 10px;
+	display: flex;
+	justify-content: flex-end;
+	align-items: center;
+	gap: 12px;
+	margin-bottom: 10px;
 }
 
 .panel-item-customers {
-  border: 1px solid #1a58b0;
-  padding: 18px;
-  width: 100%;
-  height: 428px;
+	border: 1px solid #1a58b0;
+	padding: 18px;
+	width: 100%;
+	height: 428px;
 }
 </style>
 
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue
index 6e39eb1..6b113b9 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue
@@ -1,29 +1,29 @@
 <template>
-  <div>
-    <!-- 璁惧缁熻 -->
-    <div class="equipment-stats">
-      <div class="equipment-header">
-        <img
-          src="@/assets/BI/shujutongjiicon@2x.png"
-          alt="鍥炬爣"
-          class="equipment-icon"
-        />
-        <span class="equipment-title">浜у搧鍛ㄨ浆澶╂暟</span>
-      </div>
-      <Echarts
-        ref="chart"
-        :chartStyle="chartStyle"
-        :grid="grid"
-        :legend="barLegend"
-        :series="barSeries1"
-        :tooltip="tooltip"
-        :xAxis="xAxis1"
-        :yAxis="yAxis1"
-        :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
-        style="height: 260px"
-      />
-    </div>
-  </div>
+	<div>
+		<!-- 璁惧缁熻 -->
+		<div class="equipment-stats">
+			<div class="equipment-header">
+				<img
+					src="@/assets/BI/shujutongjiicon@2x.png"
+					alt="鍥炬爣"
+					class="equipment-icon"
+				/>
+				<span class="equipment-title">浜у搧鍛ㄨ浆澶╂暟</span>
+			</div>
+			<Echarts
+				ref="chart"
+				:chartStyle="chartStyle"
+				:grid="grid"
+				:legend="barLegend"
+				:series="barSeries1"
+				:tooltip="tooltip"
+				:xAxis="xAxis1"
+				:yAxis="yAxis1"
+				:options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+				style="height: 260px"
+			/>
+		</div>
+	</div>
 </template>
 
 <script setup>
@@ -31,106 +31,108 @@
 import Echarts from '@/components/Echarts/echarts.vue'
 import { productTurnoverDays } from '@/api/viewIndex.js'
 
+const dateRange = inject('psiDateRange', null)
+
 const chartStyle = { width: '100%', height: '100%' }
 const grid = { left: '3%', right: '4%', bottom: '3%', top: '4%', containLabel: true }
 const barLegend = { show: false, textStyle: { color: '#B8C8E0' }, data: ['鍛ㄨ浆澶╂暟'] }
 const barSeries1 = ref([
-  {
-    name: '鍛ㄨ浆澶╂暟',
-    type: 'bar',
-    barGap: 0,
-    barWidth: 30,
-    emphasis: { focus: 'series' },
-    itemStyle: {
-      color: {
-        type: 'linear',
-        x: 0, y: 1, x2: 0, y2: 0,
-        colorStops: [
-          { offset: 0, color: 'rgba(0,164,237,0)' },
-          { offset: 1, color: '#4EE4FF' },
-        ],
-      },
-    },
-    data: [],
-  },
+	{
+		name: '鍛ㄨ浆澶╂暟',
+		type: 'bar',
+		barGap: 0,
+		barWidth: 30,
+		emphasis: { focus: 'series' },
+		itemStyle: {
+			color: {
+				type: 'linear',
+				x: 0, y: 1, x2: 0, y2: 0,
+				colorStops: [
+					{ offset: 0, color: 'rgba(0,164,237,0)' },
+					{ offset: 1, color: '#4EE4FF' },
+				],
+			},
+		},
+		data: [],
+	},
 ])
 const tooltip = {
-  trigger: 'axis',
-  axisPointer: { type: 'shadow' },
-  formatter(params) {
-    let result = params[0].axisValueLabel + '<br/>'
-    params.forEach((item) => {
-      result += `<div>${item.marker} ${item.seriesName}: ${item.value} 澶�</div>`
-    })
-    return result
-  },
+	trigger: 'axis',
+	axisPointer: { type: 'shadow' },
+	formatter(params) {
+		let result = params[0].axisValueLabel + '<br/>'
+		params.forEach((item) => {
+			result += `<div>${item.marker} ${item.seriesName}: ${item.value} 澶�</div>`
+		})
+		return result
+	},
 }
 const xAxis1 = ref([{ type: 'category', axisTick: { show: false }, axisLabel: { color: '#B8C8E0' }, data: [] }])
 const yAxis1 = [{ type: 'value', axisLabel: { color: '#B8C8E0' } }]
 
 const fetchData = () => {
-  productTurnoverDays()
-    .then((res) => {
-      if (res.code === 200 && Array.isArray(res.data)) {
-        const list = res.data
-        xAxis1.value[0].data = list.map((d) => d.name)
-        barSeries1.value[0].data = list.map((d) => Number(d.value) || 0)
-      }
-    })
-    .catch((err) => {
-      console.error('鑾峰彇浜у搧鍛ㄨ浆澶╂暟澶辫触:', err)
-    })
+	productTurnoverDays(dateRange?.value || undefined)
+		.then((res) => {
+			if (res.code === 200 && Array.isArray(res.data)) {
+				const list = res.data
+				xAxis1.value[0].data = list.map((d) => d.name)
+				barSeries1.value[0].data = list.map((d) => Number(d.value) || 0)
+			}
+		})
+		.catch((err) => {
+			console.error('鑾峰彇浜у搧鍛ㄨ浆澶╂暟澶辫触:', err)
+		})
 }
 
 const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
 if (dataDashboardRefreshTick) {
-  watch(dataDashboardRefreshTick, () => {
-    fetchData()
-  })
+	watch(dataDashboardRefreshTick, () => {
+		fetchData()
+	})
 }
 
 onMounted(() => {
-  fetchData()
+	fetchData()
 })
 </script>
 
 <style scoped>
 .equipment-stats {
-  border: 1px solid #1a58b0;
-  padding: 0 18px 18px;
-  display: flex;
-  flex-direction: column;
-  gap: 16px;
+	border: 1px solid #1a58b0;
+	padding: 0 18px 18px;
+	display: flex;
+	flex-direction: column;
+	gap: 16px;
 }
 
 .equipment-header {
-  font-weight: 500;
-  font-size: 21px;
-  display: flex;
-  border-bottom: 1px solid;
-  border-image: linear-gradient(
-      270deg,
-      rgba(0, 126, 255, 0) 0%,
-      rgba(0, 126, 255, 0.4549) 35%,
-      #007eff 78%,
-      #007eff 100%
-    )
-    1;
-  padding-bottom: 2px;
+	font-weight: 500;
+	font-size: 21px;
+	display: flex;
+	border-bottom: 1px solid;
+	border-image: linear-gradient(
+		270deg,
+		rgba(0, 126, 255, 0) 0%,
+		rgba(0, 126, 255, 0.4549) 35%,
+		#007eff 78%,
+		#007eff 100%
+	)
+	1;
+	padding-bottom: 2px;
 }
 
 .equipment-title {
-  font-weight: 500;
-  font-size: 18px;
-  background: linear-gradient(360deg, #056dff 0%, #43e8fc 100%);
-  -webkit-background-clip: text;
-  -webkit-text-fill-color: transparent;
-  background-clip: text;
-  line-height: 50px;
+	font-weight: 500;
+	font-size: 18px;
+	background: linear-gradient(360deg, #056dff 0%, #43e8fc 100%);
+	-webkit-background-clip: text;
+	-webkit-text-fill-color: transparent;
+	background-clip: text;
+	line-height: 50px;
 }
 
 .equipment-icon {
-  width: 50px;
-  height: 50px;
+	width: 50px;
+	height: 50px;
 }
 </style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue
index 055fb66..f211418 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue
@@ -1,26 +1,26 @@
 <template>
-  <div>
-    <!-- 椤堕儴缁熻鍗$墖 -->
-    <div class="stats-cards">
-      <div
-        v-for="item in statItems"
-        :key="item.name"
-        class="stat-card"
-      >
-        <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
-        <div class="card-content">
-          <span class="card-label">{{ item.name }}</span>
-          <span class="card-value">{{ item.value }}</span>
-          <div class="card-compare" :class="compareClass(Number(item.rate))">
-            <span>鍚屾瘮</span>
-            <span class="compare-value">{{ formatPercent(item.rate) }}</span>
-            <span class="compare-icon">{{ Number(item.rate) >= 0 ? '鈫�' : '鈫�' }}</span>
-          </div>
-        </div>
-      </div>
-    </div>
-
-  </div>
+	<div>
+		<!-- 椤堕儴缁熻鍗$墖 -->
+		<div class="stats-cards">
+			<div
+				v-for="item in statItems"
+				:key="item.name"
+				class="stat-card"
+			>
+				<img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+				<div class="card-content">
+					<span class="card-label">{{ item.name }}</span>
+					<span class="card-value">{{ item.value }}</span>
+					<div class="card-compare" :class="compareClass(Number(item.rate))">
+						<span>鍚屾瘮</span>
+						<span class="compare-value">{{ formatPercent(item.rate) }}</span>
+						<span class="compare-icon">{{ Number(item.rate) >= 0 ? '鈫�' : '鈫�' }}</span>
+					</div>
+				</div>
+			</div>
+		</div>
+	
+	</div>
 </template>
 
 <script setup>
@@ -30,115 +30,118 @@
 const statItems = ref([])
 
 const formatPercent = (val) => {
-  const num = Number(val) || 0
-  return `${num.toFixed(2)}%`
+	const num = Number(val) || 0
+	return `${num.toFixed(2)}%`
 }
 
 const compareClass = (val) => (val >= 0 ? 'compare-up' : 'compare-down')
 
+const dateRange = inject('psiDateRange', null)
+
 const fetchData = () => {
-  salesPurchaseStorageProductCount()
-    .then((res) => {
-      if (res.code === 200 && Array.isArray(res.data)) {
-        statItems.value = res.data.map((item) => ({
-          name: item.name,
-          value: item.value,
-          rate: item.rate,
-        }))
-      }
-    })
-    .catch((err) => {
-      console.error('鑾峰彇閿�鍞�/閲囪喘/鍌ㄥ瓨浜у搧鏁板け璐�:', err)
-    })
+	const params = dateRange?.value || {}
+	salesPurchaseStorageProductCount(params.startDate, params.endDate)
+		.then((res) => {
+			if (res.code === 200 && Array.isArray(res.data)) {
+				statItems.value = res.data.map((item) => ({
+					name: item.name === '閿�鍞骇鍝佹暟' ? '閿�鍞鍗曟暟' : item.name,
+					value: item.value,
+					rate: item.rate,
+				}))
+			}
+		})
+		.catch((err) => {
+			console.error('鑾峰彇閿�鍞�/閲囪喘/鍌ㄥ瓨浜у搧鏁板け璐�:', err)
+		})
 }
 
 const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
 if (dataDashboardRefreshTick) {
-  watch(dataDashboardRefreshTick, () => {
-    fetchData()
-  })
+	watch(dataDashboardRefreshTick, () => {
+		fetchData()
+	})
 }
 
 onMounted(() => {
-  fetchData()
+	fetchData()
 })
 </script>
 
 <style scoped>
 .stats-cards {
-  display: flex;
-  gap: 30px;
+	display: flex;
+	gap: 30px;
 }
 
 .stat-card {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  background-image: url('@/assets/BI/border@2x.png');
-  background-size: 100% 100%;
-  background-position: center;
-  background-repeat: no-repeat;
-  height: 142px;
+	flex: 1;
+	display: flex;
+	align-items: center;
+	background-image: url('@/assets/BI/border@2x.png');
+	background-size: 100% 100%;
+	background-position: center;
+	background-repeat: no-repeat;
+	height: 142px;
 }
 
 .card-icon {
-  width: 100px;
-  height: 100px;
-  margin: 20px 20px 0 10px;
+	width: 100px;
+	height: 100px;
+	margin: 20px 20px 0 10px;
 }
 
 .card-content {
-  display: flex;
-  flex-direction: column;
-  gap: 10px;
+	display: flex;
+	flex-direction: column;
+	gap: 10px;
 }
 
 .card-value {
-  font-weight: 500;
-  font-size: 40px;
-  background: linear-gradient(360deg, #008bfd 0%, #ffffff 100%);
-  -webkit-background-clip: text;
-  -webkit-text-fill-color: transparent;
-  background-clip: text;
+	font-weight: 500;
+	font-size: 40px;
+	background: linear-gradient(360deg, #008bfd 0%, #ffffff 100%);
+	-webkit-background-clip: text;
+	-webkit-text-fill-color: transparent;
+	background-clip: text;
 }
 
 .card-label {
-  font-weight: 400;
-  font-size: 19px;
-  color: rgba(208, 231, 255, 0.7);
+	font-weight: 400;
+	font-size: 19px;
+	color: rgba(208, 231, 255, 0.7);
 }
 
 .card-compare {
-  display: flex;
-  align-items: center;
-  gap: 6px;
-  font-size: 15px;
-  color: #d0e7ff;
+	display: flex;
+	align-items: center;
+	gap: 6px;
+	font-size: 15px;
+	color: #d0e7ff;
 }
 
 .card-compare > span:first-child {
-  font-size: 13px;
-  opacity: 0.8;
+	font-size: 13px;
+	opacity: 0.8;
 }
 
 .compare-value {
-  font-weight: 600;
+	font-weight: 600;
 }
 
 .compare-icon {
-  font-size: 14px;
-  position: relative;
-  top: -1px; /* 杞诲井涓婄Щ锛岃绠ご涓庢枃瀛楀瀭鐩村眳涓榻� */
+	font-size: 14px;
+	position: relative;
+	top: -1px; /* 杞诲井涓婄Щ锛岃绠ご涓庢枃瀛楀瀭鐩村眳涓榻� */
 }
 
 .compare-up .compare-value,
 .compare-up .compare-icon {
-  color: #00c853;
+	color: #00c853;
 }
 
 .compare-down .compare-value,
 .compare-down .compare-icon {
-  color: #ff5252;
+	color: #ff5252;
 }
 
 </style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
index 65a72fe..6a4ffa6 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
@@ -1,24 +1,24 @@
 <template>
-  <div>
-    <PanelHeader title="閲囪喘鍝佸垎甯�" />
-    <div class="main-panel panel-item-customers">
-      <CarouselCards :items="cardItems" :visible-count="3" />
-      <div class="pie-chart-wrapper" ref="pieWrapperRef">
-        <div class="pie-background" ref="pieBackgroundRef"></div>
-        <Echarts
-          ref="chart"
-          :chartStyle="chartStyle"
-          :legend="landLegend"
-          :series="landSeries"
-          :tooltip="landTooltip"
-          :color="landColors"
-          :options="pieOptions"
-          style="height: 320px"
-          class="land-chart"
-        />
-      </div>
-    </div>
-  </div>
+	<div>
+		<PanelHeader title="閲囪喘鍝佸垎甯�" />
+		<div class="main-panel panel-item-customers">
+			<CarouselCards :items="cardItems" :visible-count="3" />
+			<div class="pie-chart-wrapper" ref="pieWrapperRef">
+				<div class="pie-background" ref="pieBackgroundRef"></div>
+				<Echarts
+					ref="chart"
+					:chartStyle="chartStyle"
+					:legend="landLegend"
+					:series="landSeries"
+					:tooltip="landTooltip"
+					:color="landColors"
+					:options="pieOptions"
+					style="height: 320px"
+					class="land-chart"
+				/>
+			</div>
+		</div>
+	</div>
 </template>
 
 <script setup>
@@ -28,6 +28,8 @@
 import CarouselCards from './CarouselCards.vue'
 import { rawMaterialPurchaseAmountRatio } from '@/api/viewIndex.js'
 import { useChartBackground } from '@/hooks/useChartBackground.js'
+
+const dateRange = inject('psiDateRange', null)
 
 const pieWrapperRef = ref(null)
 const pieBackgroundRef = ref(null)
@@ -39,11 +41,11 @@
  * @return {杩斿洖绫诲瀷璇存槑}
  */
 function array2obj(array, key) {
-  const resObj = {}
-  for (let i = 0; i < array.length; i++) {
-    resObj[array[i][key]] = array[i]
-  }
-  return resObj
+	const resObj = {}
+	for (let i = 0; i < array.length; i++) {
+		resObj[array[i][key]] = array[i]
+	}
+	return resObj
 }
 
 // 鏁版嵁鍒楄〃锛堟潵鑷帴鍙o級
@@ -59,204 +61,204 @@
 
 // 鍥句緥閰嶇疆锛堝彸渚х珫鎺掞級
 const landLegend = computed(() => {
-  const data = dataList.value.map((d, idx) => ({
-    name: d.name,
-    icon: 'circle',
-    textStyle: {
-      fontSize: 18,
-      color: landColors[idx % landColors.length],
-    },
-  }))
-
-  return {
-    orient: 'vertical',
-    top: 'center',
-    left: '52%',
-    itemGap: 30,
-    data: data,
-    formatter: function (name) {
-      const item = landObjData.value[name]
-      if (!item) return name
-      return `{title|${name}}{value|${item.value}}{unit|鍏儅{percent|${item.rate}}{unit|%}`
-    },
-    textStyle: {
-      rich: {
-        value: {
-          color: '#43e8fc',
-          fontSize: 14,
-          fontWeight: 600,
-          padding: [0, 0, 0, 10],
-        },
-        unit: {
-          color: '#82baff',
-          fontSize: 12,
-          fontWeight: 600,
-          padding: [0, 10, 0, 0],
-        },
-        percent: {
-          color: '#43e8fc',
-          fontSize: 14,
-          fontWeight: 600,
-          padding: [0, 0, 0, 0],
-        },
-        title: {
-          fontSize: 12,
-          padding: [0, 0, 0, 0],
-        },
-      },
-    },
-  }
+	const data = dataList.value.map((d, idx) => ({
+		name: d.name,
+		icon: 'circle',
+		textStyle: {
+			fontSize: 18,
+			color: landColors[idx % landColors.length],
+		},
+	}))
+	
+	return {
+		orient: 'vertical',
+		top: 'center',
+		left: '52%',
+		itemGap: 30,
+		data: data,
+		formatter: function (name) {
+			const item = landObjData.value[name]
+			if (!item) return name
+			return `{title|${name}}{value|${item.value}}{unit|鍏儅{percent|${item.rate}}{unit|%}`
+		},
+		textStyle: {
+			rich: {
+				value: {
+					color: '#43e8fc',
+					fontSize: 14,
+					fontWeight: 600,
+					padding: [0, 0, 0, 10],
+				},
+				unit: {
+					color: '#82baff',
+					fontSize: 12,
+					fontWeight: 600,
+					padding: [0, 10, 0, 0],
+				},
+				percent: {
+					color: '#43e8fc',
+					fontSize: 14,
+					fontWeight: 600,
+					padding: [0, 0, 0, 0],
+				},
+				title: {
+					fontSize: 12,
+					padding: [0, 0, 0, 0],
+				},
+			},
+		},
+	}
 })
 
 // 鎻愮ず妗�
 const landTooltip = {
-  trigger: 'item',
-  formatter: '{a} <br/>{b} : {c}鍏� ({d}%)',
+	trigger: 'item',
+	formatter: '{a} <br/>{b} : {c}鍏� ({d}%)',
 }
 
 // 鍙屽眰鐜舰楗煎浘
 const landSeries = ref([
-  {
-    name: '浜у搧閲囪喘閲戦鍒嗘瀽',
-    type: 'pie',
-    radius: ['40%', '60%'],
-    center: ['25%', '50%'],
-    itemStyle: {
-      borderColor: '#0a1c3a',
-      borderWidth: 2,
-      color: function (params) {
-        return landColors[params.dataIndex % landColors.length]
-      },
-    },
-    label: {
-      show: false
-    },
-    minAngle: 15,
-    data: dataList.value,
-    animationType: 'scale',
-    animationEasing: 'elasticOut',
-    animationDelay: function () {
-      return Math.random() * 200
-    },
-  },
-  {
-    // 鍐呭湀
-    type: 'pie',
-    radius: ['40%', '45%'],
-    center: ['25%', '50%'],
-    silent: true,
-    label: {
-      show: false,
-    },
-    labelLine: {
-      show: false,
-    },
-    itemStyle: {
-      color: 'rgba(0, 127, 255, 0.25)',
-    },
-    data: [1],
-  },
+	{
+		name: '浜у搧閲囪喘閲戦鍒嗘瀽',
+		type: 'pie',
+		radius: ['40%', '60%'],
+		center: ['25%', '50%'],
+		itemStyle: {
+			borderColor: '#0a1c3a',
+			borderWidth: 2,
+			color: function (params) {
+				return landColors[params.dataIndex % landColors.length]
+			},
+		},
+		label: {
+			show: false
+		},
+		minAngle: 15,
+		data: dataList.value,
+		animationType: 'scale',
+		animationEasing: 'elasticOut',
+		animationDelay: function () {
+			return Math.random() * 200
+		},
+	},
+	{
+		// 鍐呭湀
+		type: 'pie',
+		radius: ['40%', '45%'],
+		center: ['25%', '50%'],
+		silent: true,
+		label: {
+			show: false,
+		},
+		labelLine: {
+			show: false,
+		},
+		itemStyle: {
+			color: 'rgba(0, 127, 255, 0.25)',
+		},
+		data: [1],
+	},
 ])
 
 const chartStyle = {
-  width: '100%',
-  height: '100%',
+	width: '100%',
+	height: '100%',
 }
 
 const pieOptions = {
-  backgroundColor: 'transparent',
-  textStyle: { color: '#B8C8E0' },
+	backgroundColor: 'transparent',
+	textStyle: { color: '#B8C8E0' },
 }
 
 // 浣跨敤灏佽鐨勮儗鏅綅缃皟鏁存柟娉�
 // 鍥捐〃涓績鏄� ['25%', '50%']锛岃儗鏅渶瑕佸榻愬埌杩欎釜浣嶇疆
 const { init: initBackground, cleanup: cleanupBackground } = useChartBackground({
-  wrapperRef: pieWrapperRef,
-  backgroundRef: pieBackgroundRef,
-  left: '25%',       // 鍥捐〃涓績 X 鏄� 25%
-  top: '50%',        // 鍥捐〃涓績 Y 鏄� 50%
-  offsetX: '-51.5%', // X 杞村亸绉�
-  offsetY: '-50%',   // Y 杞村亸绉�
-  watchData: dataList // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
+	wrapperRef: pieWrapperRef,
+	backgroundRef: pieBackgroundRef,
+	left: '25%',       // 鍥捐〃涓績 X 鏄� 25%
+	top: '50%',        // 鍥捐〃涓績 Y 鏄� 50%
+	offsetX: '-51.5%', // X 杞村亸绉�
+	offsetY: '-50%',   // Y 杞村亸绉�
+	watchData: dataList // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
 })
 
 const fetchData = () => {
-  rawMaterialPurchaseAmountRatio()
-    .then((res) => {
-      if (res.code === 200 && Array.isArray(res.data)) {
-        const items = res.data
-        cardItems.value = items.map((item) => ({
-          label: item.name,
-          value: item.value,
-          unit: '鍏�',
-          rate: item.rate,
-        }))
-        dataList.value = items.map((it) => ({
-          name: it.name,
-          value: parseFloat(it.value) || 0,
-          rate: it.rate,
-          children: [],
-        }))
-        landSeries.value[0].data = dataList.value
-      }
-    })
-    .catch((err) => {
-      console.error('鑾峰彇鍘熸潗鏂欓噰璐噾棰濆崰姣斿け璐�:', err)
-    })
+	rawMaterialPurchaseAmountRatio(dateRange?.value || undefined)
+		.then((res) => {
+			if (res.code === 200 && Array.isArray(res.data)) {
+				const items = res.data
+				cardItems.value = items.map((item) => ({
+					label: item.name,
+					value: item.value,
+					unit: '鍏�',
+					rate: item.rate,
+				}))
+				dataList.value = items.map((it) => ({
+					name: it.name,
+					value: parseFloat(it.value) || 0,
+					rate: it.rate,
+					children: [],
+				}))
+				landSeries.value[0].data = dataList.value
+			}
+		})
+		.catch((err) => {
+			console.error('鑾峰彇鍘熸潗鏂欓噰璐噾棰濆崰姣斿け璐�:', err)
+		})
 }
 
 const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
 if (dataDashboardRefreshTick) {
-  watch(dataDashboardRefreshTick, () => {
-    fetchData()
-  })
+	watch(dataDashboardRefreshTick, () => {
+		fetchData()
+	})
 }
 
 onMounted(() => {
-  fetchData()
-  initBackground()
+	fetchData()
+	initBackground()
 })
 
 onBeforeUnmount(() => {
-  cleanupBackground()
+	cleanupBackground()
 })
 </script>
 
 <style scoped>
 .main-panel {
-  display: flex;
-  flex-direction: column;
-  gap: 20px;
+	display: flex;
+	flex-direction: column;
+	gap: 20px;
 }
 
 .panel-item-customers {
-  border: 1px solid #1a58b0;
-  padding: 18px;
-  width: 100%;
-  height: 449px;
+	border: 1px solid #1a58b0;
+	padding: 18px;
+	width: 100%;
+	height: 449px;
 }
 
 .pie-chart-wrapper {
-  position: relative;
-  width: 100%;
-  height: 320px;
-  background: transparent;
+	position: relative;
+	width: 100%;
+	height: 320px;
+	background: transparent;
 }
 
 
 .pie-background {
-  position: absolute;
-  width: 310px;
-  height: 310px;
-  background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
-  background-size: contain;
-  background-position: center;
-  background-repeat: no-repeat;
-  z-index: 1;
-  pointer-events: none;
-  /* 浣嶇疆鐢� JS 鍔ㄦ�佽缃紝榛樿灞呬腑 */
-  left: 25%;
-  top: 50%;
-  transform: translate(-51.5%, -50%);
+	position: absolute;
+	width: 310px;
+	height: 310px;
+	background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
+	background-size: contain;
+	background-position: center;
+	background-repeat: no-repeat;
+	z-index: 1;
+	pointer-events: none;
+	/* 浣嶇疆鐢� JS 鍔ㄦ�佽缃紝榛樿灞呬腑 */
+	left: 25%;
+	top: 50%;
+	transform: translate(-51.5%, -50%);
 }
 </style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
index f5dac31..afe771a 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
@@ -1,29 +1,31 @@
 <template>
-  <div>
-    <PanelHeader title="閿�鍞搧鍒嗗竷" />
-    <div class="main-panel panel-item-customers">
-      <CarouselCards :items="cardItems" :visible-count="3" />
-      <div class="pie-chart-wrapper" ref="pieWrapperRef">
-        <div class="pie-background" ref="pieBackgroundRef"></div>
-        <Echarts
-          ref="echartsRef"
-          :chartStyle="chartStyle"
-          :legend="pieLegend"
-          :series="pieSeries"
-          :tooltip="pieTooltip"
-          :color="pieColors"
-          :options="pieOptions"
-          style="height: 320px"
-        />
-      </div>
-    </div>
-  </div>
+	<div>
+		<PanelHeader title="閿�鍞搧鍒嗗竷" />
+		<div class="main-panel panel-item-customers">
+			<CarouselCards :items="cardItems" :visible-count="3" />
+			<div class="pie-chart-wrapper" ref="pieWrapperRef">
+				<div class="pie-background" ref="pieBackgroundRef"></div>
+				<Echarts
+					ref="echartsRef"
+					:chartStyle="chartStyle"
+					:legend="pieLegend"
+					:series="pieSeries"
+					:tooltip="pieTooltip"
+					:color="pieColors"
+					:options="pieOptions"
+					style="height: 320px"
+				/>
+			</div>
+		</div>
+	</div>
 </template>
 
 <script setup>
 import { ref, onMounted, onBeforeUnmount, computed, inject, watch } from 'vue'
 import { productSalesAnalysis } from '@/api/viewIndex.js'
 import PanelHeader from './PanelHeader.vue'
+
+const dateRange = inject('psiDateRange', null)
 import CarouselCards from './CarouselCards.vue'
 import Echarts from '@/components/Echarts/echarts.vue'
 import { useChartBackground } from '@/hooks/useChartBackground.js'
@@ -38,16 +40,16 @@
  * @return {杩斿洖绫诲瀷璇存槑}
  */
 function array2obj(array, key) {
-  const resObj = {}
-  for (let i = 0; i < array.length; i++) {
-    resObj[array[i][key]] = array[i]
-  }
-  return resObj
+	const resObj = {}
+	for (let i = 0; i < array.length; i++) {
+		resObj[array[i][key]] = array[i]
+	}
+	return resObj
 }
 
 const chartStyle = {
-  width: '100%',
-  height: '100%',
+	width: '100%',
+	height: '100%',
 }
 
 const echartsRef = ref(null)
@@ -57,171 +59,171 @@
 const pieObjData = computed(() => array2obj(pieDatas.value, 'name'))
 
 const pieLegend = computed(() => {
-  const data = pieDatas.value.map((d, idx) => ({
-    name: d.name,
-    icon: 'circle',
-    textStyle: {
-      fontSize: 18,
-      color: pieColors[idx % pieColors.length],
-    },
-  }))
-
-  return {
-    orient: 'vertical',
-    top: 'center',
-    left: '52%',
-    itemGap: 30,
-    data: data,
-    formatter: function (name) {
-      const item = pieObjData.value[name]
-      if (!item) return name
-      return `{title|${name}}{value|${item.value}}{unit|鍏儅{percent|${item.rate}}{unit|%}`
-    },
-    textStyle: {
-      rich: {
-        value: {
-          color: '#43e8fc',
-          fontSize: 14,
-          fontWeight: 600,
-          padding: [0, 0, 0, 10],
-        },
-        unit: {
-          color: '#82baff',
-          fontSize: 12,
-          fontWeight: 600,
-          padding: [0, 10, 0, 0],
-        },
-        percent: {
-          color: '#43e8fc',
-          fontSize: 14,
-          fontWeight: 600,
-          padding: [0, 0, 0, 0],
-        },
-        title: {
-          fontSize: 12,
-          padding: [0, 0, 0, 0],
-        },
-      },
-    },
-  }
+	const data = pieDatas.value.map((d, idx) => ({
+		name: d.name,
+		icon: 'circle',
+		textStyle: {
+			fontSize: 18,
+			color: pieColors[idx % pieColors.length],
+		},
+	}))
+	
+	return {
+		orient: 'vertical',
+		top: 'center',
+		left: '52%',
+		itemGap: 30,
+		data: data,
+		formatter: function (name) {
+			const item = pieObjData.value[name]
+			if (!item) return name
+			return `{title|${name}}{value|${item.value}}{unit|鍏儅{percent|${item.rate}}{unit|%}`
+		},
+		textStyle: {
+			rich: {
+				value: {
+					color: '#43e8fc',
+					fontSize: 14,
+					fontWeight: 600,
+					padding: [0, 0, 0, 10],
+				},
+				unit: {
+					color: '#82baff',
+					fontSize: 12,
+					fontWeight: 600,
+					padding: [0, 10, 0, 0],
+				},
+				percent: {
+					color: '#43e8fc',
+					fontSize: 14,
+					fontWeight: 600,
+					padding: [0, 0, 0, 0],
+				},
+				title: {
+					fontSize: 12,
+					padding: [0, 0, 0, 0],
+				},
+			},
+		},
+	}
 })
 
 const pieTooltip = {
-  trigger: 'item',
-  formatter: '{a} <br/>{b} : {c}鍏� ({d}%)',
+	trigger: 'item',
+	formatter: '{a} <br/>{b} : {c}鍏� ({d}%)',
 }
 
 const pieSeries = computed(() => [
-  {
-    name: '浜у搧閿�鍞噾棰濆垎鏋�',
-    type: 'pie',
-    radius: '60%',
-    center: ['25%', '50%'],
-    itemStyle: {
-      borderColor: '#0a1c3a',
-      borderWidth: 2,
-    },
-    label: {
-      show: false
-    },
-    minAngle: 15,
-    data: pieDatas.value,
-    animationType: 'scale',
-    animationEasing: 'elasticOut',
-    animationDelay: function () {
-      return Math.random() * 200
-    },
-  },
+	{
+		name: '浜у搧閿�鍞噾棰濆垎鏋�',
+		type: 'pie',
+		radius: '60%',
+		center: ['25%', '50%'],
+		itemStyle: {
+			borderColor: '#0a1c3a',
+			borderWidth: 2,
+		},
+		label: {
+			show: false
+		},
+		minAngle: 15,
+		data: pieDatas.value,
+		animationType: 'scale',
+		animationEasing: 'elasticOut',
+		animationDelay: function () {
+			return Math.random() * 200
+		},
+	},
 ])
 
 const pieOptions = {
-  backgroundColor: 'transparent',
-  textStyle: { color: '#B8C8E0' },
+	backgroundColor: 'transparent',
+	textStyle: { color: '#B8C8E0' },
 }
 
 const cardItems = ref([])
 
 // 浣跨敤灏佽鐨勮儗鏅綅缃皟鏁存柟娉曪紙涓庡叾浠栨枃浠朵繚鎸佷竴鑷达級
 const { init: initBackground, cleanup: cleanupBackground } = useChartBackground({
-  wrapperRef: pieWrapperRef,
-  backgroundRef: pieBackgroundRef,
-  left: '25%',       // 鍥捐〃涓績 X 鏄� 25%
-  top: '50%',        // 鍥捐〃涓績 Y 鏄� 50%
-  offsetX: '-51.5%', // X 杞村亸绉�
-  offsetY: '-50%',   // Y 杞村亸绉�
-  watchData: pieDatas // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
+	wrapperRef: pieWrapperRef,
+	backgroundRef: pieBackgroundRef,
+	left: '25%',       // 鍥捐〃涓績 X 鏄� 25%
+	top: '50%',        // 鍥捐〃涓績 Y 鏄� 50%
+	offsetX: '-51.5%', // X 杞村亸绉�
+	offsetY: '-50%',   // Y 杞村亸绉�
+	watchData: pieDatas // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
 })
 
 const fetchData = () => {
-  productSalesAnalysis()
-    .then((res) => {
-      if (res.code === 200 && Array.isArray(res.data)) {
-        const items = res.data
-        cardItems.value = items.map((item) => ({
-          label: item.name,
-          value: item.value,
-          unit: '鍏�',
-          rate: item.rate,
-        }))
-        pieDatas.value = items.map((item) => ({
-          name: item.name,
-          value: parseFloat(item.value) || 0,
-          rate: item.rate,
-        }))
-      }
-    })
-    .catch((err) => {
-      console.error('鑾峰彇浜у搧閿�鍞噾棰濆垎鏋愬け璐�:', err)
-    })
+	productSalesAnalysis(dateRange?.value || undefined)
+		.then((res) => {
+			if (res.code === 200 && Array.isArray(res.data)) {
+				const items = res.data
+				cardItems.value = items.map((item) => ({
+					label: item.name,
+					value: item.value,
+					unit: '鍏�',
+					rate: item.rate,
+				}))
+				pieDatas.value = items.map((item) => ({
+					name: item.name,
+					value: parseFloat(item.value) || 0,
+					rate: item.rate,
+				}))
+			}
+		})
+		.catch((err) => {
+			console.error('鑾峰彇浜у搧閿�鍞噾棰濆垎鏋愬け璐�:', err)
+		})
 }
 
 const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
 if (dataDashboardRefreshTick) {
-  watch(dataDashboardRefreshTick, () => {
-    fetchData()
-  })
+	watch(dataDashboardRefreshTick, () => {
+		fetchData()
+	})
 }
 
 onMounted(() => {
-  fetchData()
-  initBackground()
+	fetchData()
+	initBackground()
 })
 
 onBeforeUnmount(() => {
-  cleanupBackground()
+	cleanupBackground()
 })
 </script>
 
 <style scoped>
 .main-panel {
-  display: flex;
-  flex-direction: column;
-  gap: 20px;
+	display: flex;
+	flex-direction: column;
+	gap: 20px;
 }
 
 .panel-item-customers {
-  border: 1px solid #1a58b0;
-  padding: 18px;
-  width: 100%;
-  height: 449px;
+	border: 1px solid #1a58b0;
+	padding: 18px;
+	width: 100%;
+	height: 449px;
 }
 .pie-chart-wrapper {
-  position: relative;
-  width: 100%;
-  height: 320px;
+	position: relative;
+	width: 100%;
+	height: 320px;
 }
 .pie-background {
-  position: absolute;
-  left: 25%;
-  top: 50%;
-  transform: translate(-51.5%, -50%);
-  width: 310px;
-  height: 310px;
-  background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
-  background-size: contain;
-  background-position: center;
-  background-repeat: no-repeat;
-  z-index: 1;
-  pointer-events: none;
+	position: absolute;
+	left: 25%;
+	top: 50%;
+	transform: translate(-51.5%, -50%);
+	width: 310px;
+	height: 310px;
+	background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
+	background-size: contain;
+	background-position: center;
+	background-repeat: no-repeat;
+	z-index: 1;
+	pointer-events: none;
 }
 </style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/index.vue b/src/views/reportAnalysis/PSIDataAnalysis/index.vue
index f81e482..ab6840c 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/index.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/index.vue
@@ -1,50 +1,66 @@
 <template>
-  <div class="scale-container">
-    <div class="data-dashboard" :style="{ transform: `scale(${scaleRatio})` }">
-    <!-- 鍏ㄥ睆鎸夐挳 - 绉诲姩鍒板乏涓婅 -->
-    <button class="fullscreen-btn" @click="toggleFullscreen" :title="isFullscreen ? '閫�鍑哄叏灞�' : '鍏ㄥ睆鏄剧ず'">
-      <svg v-if="!isFullscreen" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-        <path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>
-      </svg>
-      <svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-        <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
-      </svg>
-    </button>
-
-    <!-- 椤堕儴鏍囬鏍� -->
-    <div class="dashboard-header">
-      <div class="factory-name">PSI 鏁版嵁鍒嗘瀽</div>
-    </div>
-
-    <!-- 涓昏鍐呭鍖哄煙 -->
-    <div class="dashboard-content">
-      <!-- 宸︿晶鍖哄煙 -->
-      <div class="left-panel">
-        <LeftTop />
-
-        <LeftBottom />
-      </div>
-
-      <!-- 涓棿鍖哄煙 -->
-      <div class="center-panel">
-        <CenterTop />
-        <CenterCenter/>
-        <CenterBottom />
-      </div>
-
-      <!-- 鍙充晶鍖哄煙 -->
-      <div class="right-panel">
-        <RightBottom />
-        <RightTop />
-      </div>
-    </div>
-    </div>
-  </div>
+	<div class="scale-container">
+		<div class="data-dashboard" :style="{ transform: `scale(${scaleRatio})` }">
+			<!-- 鍏ㄥ睆鎸夐挳 - 绉诲姩鍒板乏涓婅 -->
+			<button class="fullscreen-btn" @click="toggleFullscreen" :title="isFullscreen ? '閫�鍑哄叏灞�' : '鍏ㄥ睆鏄剧ず'">
+				<svg v-if="!isFullscreen" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+					<path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>
+				</svg>
+				<svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+					<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
+				</svg>
+			</button>
+			
+			<!-- 椤堕儴鏍囬鏍� -->
+			<div class="dashboard-header">
+				<div class="header-left"></div>
+				<div class="factory-name">PSI 鏁版嵁鍒嗘瀽</div>
+				<div class="header-right">
+					<div class="date-picker-wrapper">
+						<el-date-picker
+							v-model="psiDateRange"
+							type="daterange"
+							range-separator="鑷�"
+							start-placeholder="寮�濮嬫棩鏈�"
+							end-placeholder="缁撴潫鏃ユ湡"
+							value-format="YYYY-MM-DD"
+							:shortcuts="dateShortcuts"
+							@change="onDateRangeChange"
+						/>
+					</div>
+				</div>
+			</div>
+			
+			<!-- 涓昏鍐呭鍖哄煙 -->
+			<div class="dashboard-content">
+				<!-- 宸︿晶鍖哄煙 -->
+				<div class="left-panel">
+					<LeftTop />
+					
+					<LeftBottom />
+				</div>
+				
+				<!-- 涓棿鍖哄煙 -->
+				<div class="center-panel">
+					<CenterTop />
+					<CenterCenter/>
+					<CenterBottom />
+				</div>
+				
+				<!-- 鍙充晶鍖哄煙 -->
+				<div class="right-panel">
+					<RightBottom />
+					<RightTop />
+				</div>
+			</div>
+		</div>
+	</div>
 </template>
 
 <script setup>
-import { ref, onMounted, onBeforeUnmount, nextTick, provide } from 'vue'
+import { ref, onMounted, onBeforeUnmount, nextTick, provide, reactive } from 'vue'
 import autofit from 'autofit.js'
+import dayjs from 'dayjs'
 import LeftBottom from './components/left-bottom.vue'
 import CenterCenter from './components/center-center.vue'
 import RightTop from '../dataDashboard/components/basic/right-top.vue'
@@ -66,6 +82,27 @@
 // 鐢ㄦ埛store
 const userStore = useUserStore()
 
+// 鏃ユ湡鑼冨洿绛涢��
+const psiDateRange = ref([])
+const psiDateRangeParams = ref({})
+const dateShortcuts = [
+	{ text: '鏈湀', value: () => { const start = dayjs().startOf('month').format('YYYY-MM-DD'); const end = dayjs().format('YYYY-MM-DD'); return [start, end] } },
+	{ text: '杩�7澶�', value: () => { const start = dayjs().subtract(6, 'day').format('YYYY-MM-DD'); const end = dayjs().format('YYYY-MM-DD'); return [start, end] } },
+	{ text: '杩�30澶�', value: () => { const start = dayjs().subtract(29, 'day').format('YYYY-MM-DD'); const end = dayjs().format('YYYY-MM-DD'); return [start, end] } },
+	{ text: '杩�90澶�', value: () => { const start = dayjs().subtract(89, 'day').format('YYYY-MM-DD'); const end = dayjs().format('YYYY-MM-DD'); return [start, end] } },
+]
+
+const onDateRangeChange = (val) => {
+	if (val && val.length === 2) {
+		psiDateRangeParams.value = { startDate: val[0], endDate: val[1] }
+	} else {
+		psiDateRangeParams.value = {}
+	}
+	dataDashboardRefreshTick.value++
+}
+
+provide('psiDateRange', psiDateRangeParams)
+
 /** 涓� dataDashboard 鍏辩敤娉ㄥ叆鍚嶏紝瀛愮粍浠讹紙鍚鐢ㄧ殑 right-top/right-bottom锛夋瘡鍒嗛挓鍒锋柊 */
 const DASHBOARD_REFRESH_MS = 60 * 1000
 const dataDashboardRefreshTick = ref(0)
@@ -74,232 +111,253 @@
 
 // 璁$畻缂╂斁姣斾緥
 const calculateScale = () => {
-  const container = document.querySelector('.scale-container')
-  if (!container) return
-
-  // 鑾峰彇瀹瑰櫒鐨勫疄闄呭昂瀵�
-  const rect = container.getBoundingClientRect?.()
-  const containerWidth = container.clientWidth || rect?.width || window.innerWidth
-  const containerHeight = container.clientHeight || rect?.height || window.innerHeight
-
-  // 璁$畻瀹介珮缂╂斁姣斾緥锛屽彇杈冨皬鍊间互淇濊瘉鍐呭瀹屾暣鏄剧ず锛堢瓑姣旂缉鏀撅級
-  const scaleX = containerWidth / designWidth
-  const scaleY = containerHeight / designHeight
-  scaleRatio.value = Math.min(scaleX, scaleY)
+	const container = document.querySelector('.scale-container')
+	if (!container) return
+	
+	// 鑾峰彇瀹瑰櫒鐨勫疄闄呭昂瀵�
+	const rect = container.getBoundingClientRect?.()
+	const containerWidth = container.clientWidth || rect?.width || window.innerWidth
+	const containerHeight = container.clientHeight || rect?.height || window.innerHeight
+	
+	// 璁$畻瀹介珮缂╂斁姣斾緥锛屽彇杈冨皬鍊间互淇濊瘉鍐呭瀹屾暣鏄剧ず锛堢瓑姣旂缉鏀撅級
+	const scaleX = containerWidth / designWidth
+	const scaleY = containerHeight / designHeight
+	scaleRatio.value = Math.min(scaleX, scaleY)
 }
 
 // 绐楀彛澶у皬鍙樺寲澶勭悊
 const handleResize = () => {
-  // 寤惰繜鎵ц锛岀‘淇滵OM鏇存柊瀹屾垚
-  setTimeout(() => {
-    calculateScale()
-  }, 100)
+	// 寤惰繜鎵ц锛岀‘淇滵OM鏇存柊瀹屾垚
+	setTimeout(() => {
+		calculateScale()
+	}, 100)
 }
 
 // 鍏ㄥ睆鍔熻兘瀹炵幇 - 閽堝scale-container鍏冪礌
 const toggleFullscreen = () => {
-  const element = document.querySelector('.scale-container')
-
-  if (!element) return
-
-  if (!isFullscreen.value) {
-    if (element.requestFullscreen) {
-      element.requestFullscreen()
-    } else if (element.webkitRequestFullscreen) {
-      element.webkitRequestFullscreen()
-    } else if (element.msRequestFullscreen) {
-      element.msRequestFullscreen()
-    }
-  } else {
-    if (document.exitFullscreen) {
-      document.exitFullscreen()
-    } else if (document.webkitExitFullscreen) {
-      document.webkitExitFullscreen()
-    } else if (document.msExitFullscreen) {
-      document.msExitFullscreen()
-    }
-  }
+	const element = document.querySelector('.scale-container')
+	
+	if (!element) return
+	
+	if (!isFullscreen.value) {
+		if (element.requestFullscreen) {
+			element.requestFullscreen()
+		} else if (element.webkitRequestFullscreen) {
+			element.webkitRequestFullscreen()
+		} else if (element.msRequestFullscreen) {
+			element.msRequestFullscreen()
+		}
+	} else {
+		if (document.exitFullscreen) {
+			document.exitFullscreen()
+		} else if (document.webkitExitFullscreen) {
+			document.webkitExitFullscreen()
+		} else if (document.msExitFullscreen) {
+			document.msExitFullscreen()
+		}
+	}
 }
 
 // 鐩戝惉鍏ㄥ睆鍙樺寲浜嬩欢
 const handleFullscreenChange = () => {
-  const fullscreenElement = document.fullscreenElement || 
-                           document.webkitFullscreenElement || 
-                           document.msFullscreenElement
-  isFullscreen.value = fullscreenElement && fullscreenElement.classList.contains('scale-container')
-
-  // 鍏ㄥ睆鐘舵�佸彉鍖栨椂锛屽欢杩熼噸鏂拌绠楃缉鏀炬瘮渚嬶紙纭繚DOM鏇存柊瀹屾垚锛�
-  setTimeout(() => {
-    calculateScale()
-  }, 200)
+	const fullscreenElement = document.fullscreenElement ||
+		document.webkitFullscreenElement ||
+		document.msFullscreenElement
+	isFullscreen.value = fullscreenElement && fullscreenElement.classList.contains('scale-container')
+	
+	// 鍏ㄥ睆鐘舵�佸彉鍖栨椂锛屽欢杩熼噸鏂拌绠楃缉鏀炬瘮渚嬶紙纭繚DOM鏇存柊瀹屾垚锛�
+	setTimeout(() => {
+		calculateScale()
+	}, 200)
 }
 
 // 鐢熷懡鍛ㄦ湡閽╁瓙
 onMounted(() => {
-  // 浣跨敤nextTick纭繚DOM瀹屽叏娓叉煋鍚庡啀鍒濆鍖�
-  nextTick(() => {
-    // 璁$畻鍒濆缂╂斁姣斾緥
-    calculateScale()
-  })
-
-  window.addEventListener('resize', handleResize)
-  window.addEventListener('fullscreenchange', handleFullscreenChange)
-  window.addEventListener('webkitfullscreenchange', handleFullscreenChange)
-  window.addEventListener('MSFullscreenChange', handleFullscreenChange)
-
-  dashboardPollTimer = setInterval(() => {
-    dataDashboardRefreshTick.value++
-  }, DASHBOARD_REFRESH_MS)
+	// 浣跨敤nextTick纭繚DOM瀹屽叏娓叉煋鍚庡啀鍒濆鍖�
+	nextTick(() => {
+		// 璁$畻鍒濆缂╂斁姣斾緥
+		calculateScale()
+	})
+	
+	window.addEventListener('resize', handleResize)
+	window.addEventListener('fullscreenchange', handleFullscreenChange)
+	window.addEventListener('webkitfullscreenchange', handleFullscreenChange)
+	window.addEventListener('MSFullscreenChange', handleFullscreenChange)
+	
+	dashboardPollTimer = setInterval(() => {
+		dataDashboardRefreshTick.value++
+	}, DASHBOARD_REFRESH_MS)
 })
 
 onBeforeUnmount(() => {
-  if (dashboardPollTimer) {
-    clearInterval(dashboardPollTimer)
-    dashboardPollTimer = null
-  }
-  window.removeEventListener('resize', handleResize)
-  window.removeEventListener('fullscreenchange', handleFullscreenChange)
-  window.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
-  window.removeEventListener('MSFullscreenChange', handleFullscreenChange)
-  // 绉婚櫎鎴戜滑娣诲姞鐨刟utofit鍔ㄦ�佽皟鏁寸洃鍚櫒
-  if (window._autofitUpdateHandler) {
-    window.removeEventListener('resize', window._autofitUpdateHandler)
-    delete window._autofitUpdateHandler
-  }
-  // 鍏抽棴autofit
-  autofit.off()
+	if (dashboardPollTimer) {
+		clearInterval(dashboardPollTimer)
+		dashboardPollTimer = null
+	}
+	window.removeEventListener('resize', handleResize)
+	window.removeEventListener('fullscreenchange', handleFullscreenChange)
+	window.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
+	window.removeEventListener('MSFullscreenChange', handleFullscreenChange)
+	// 绉婚櫎鎴戜滑娣诲姞鐨刟utofit鍔ㄦ�佽皟鏁寸洃鍚櫒
+	if (window._autofitUpdateHandler) {
+		window.removeEventListener('resize', window._autofitUpdateHandler)
+		delete window._autofitUpdateHandler
+	}
+	// 鍏抽棴autofit
+	autofit.off()
 })
 </script>
 
 <style scoped>
 /* 澶栭儴缂╂斁瀹瑰櫒 - 鍗犳嵁鏁翠釜瑙嗗彛 */
 .scale-container {
-position: relative;
-width: 100%;
-/* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
-height: calc(100vh - 84px);
-display: flex;
-align-items: center;
-justify-content: center;
-background-color: #000;
-overflow: hidden;
+	position: relative;
+	width: 100%;
+	/* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
+	height: calc(100vh - 84px);
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	background-color: #000;
+	overflow: hidden;
 }
 
 /* 鍐呴儴鍐呭鍖哄煙 - 鍥哄畾璁捐灏哄 */
 .data-dashboard {
-position: relative;
-width: 1920px;
-height: 1080px;
-background-image: url("@/assets/BI/backImage@2x.png");
-background-size: cover;
-background-position: center;
-background-repeat: no-repeat;
-transform-origin: center center;
+	position: relative;
+	width: 1920px;
+	height: 1080px;
+	background-image: url("@/assets/BI/backImage@2x.png");
+	background-size: cover;
+	background-position: center;
+	background-repeat: no-repeat;
+	transform-origin: center center;
 }
 
 /* 鍏ㄥ睆鐘舵�佺殑鏍峰紡 - 浣滅敤浜巗cale-container */
 .scale-container:fullscreen {
-width: 100vw;
-height: 100vh;
-margin: 0;
-padding: 0;
-background-color: #000;
-z-index: 9999;
+	width: 100vw;
+	height: 100vh;
+	margin: 0;
+	padding: 0;
+	background-color: #000;
+	z-index: 9999;
 }
 
 /* Webkit娴忚鍣ㄥ墠缂� */
 .scale-container:-webkit-full-screen {
-width: 100vw;
-height: 100vh;
-margin: 0;
-padding: 0;
-background-color: #000;
-z-index: 9999;
+	width: 100vw;
+	height: 100vh;
+	margin: 0;
+	padding: 0;
+	background-color: #000;
+	z-index: 9999;
 }
 
 /* MS娴忚鍣ㄥ墠缂� */
 .scale-container:-ms-fullscreen {
-width: 100vw;
-height: 100vh;
-margin: 0;
-padding: 0;
-background-color: #000;
-z-index: 9999;
+	width: 100vw;
+	height: 100vh;
+	margin: 0;
+	padding: 0;
+	background-color: #000;
+	z-index: 9999;
 }
 
 
 .dashboard-header {
-position: relative;
-z-index: 1;
-height: 86px;
-background-image: url("@/assets/BI/biaoti.png");
-background-size: cover;
-background-repeat: no-repeat;
-display: flex;
-align-items: center;
-justify-content: center;
+	position: relative;
+	z-index: 1;
+	height: 86px;
+	background-image: url("@/assets/BI/biaoti.png");
+	background-size: cover;
+	background-repeat: no-repeat;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+}
+
+.header-left {
+	width: 300px;
+	padding-left: 20px;
+	display: flex;
+	align-items: center;
+}
+
+.header-right {
+	width: 300px;
+	display: flex;
+	align-items: center;
+	justify-content: flex-end;
+	padding-right: 20px;
+}
+
+.date-picker-wrapper {
+	/* 鏃ユ湡閫夋嫨鍣ㄥ湪澶у睆涓婄殑鏍峰紡 */
+	--el-fill-color-blank: rgba(0, 20, 60, 0.8);
+	--el-border-color: rgba(0, 212, 255, 0.3);
+	--el-text-color-regular: #00d4ff;
+	--el-color-primary: #00d4ff;
 }
 
 .factory-name {
-font-weight: 600;
-font-size: 52px;
-color: #FFFFFF;
-top: 16px;
-position: absolute;
+	font-weight: 600;
+	font-size: 52px;
+	color: #FFFFFF;
 }
 
 .fullscreen-btn {
-position: absolute;
-top: 10px;
-left: 20px;
-width: 40px;
-height: 40px;
-background: rgba(0, 20, 60, 0.8);
-border: 1px solid rgba(0, 212, 255, 0.3);
-border-radius: 6px;
-color: #00d4ff;
-cursor: pointer;
-display: flex;
-align-items: center;
-justify-content: center;
-transition: all 0.3s;
-z-index: 10000;
+	position: absolute;
+	top: 10px;
+	left: 20px;
+	width: 40px;
+	height: 40px;
+	background: rgba(0, 20, 60, 0.8);
+	border: 1px solid rgba(0, 212, 255, 0.3);
+	border-radius: 6px;
+	color: #00d4ff;
+	cursor: pointer;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	transition: all 0.3s;
+	z-index: 10000;
 }
 
 .fullscreen-btn:hover {
-background: rgba(0, 30, 90, 0.9);
-border-color: rgba(0, 212, 255, 0.5);
+	background: rgba(0, 30, 90, 0.9);
+	border-color: rgba(0, 212, 255, 0.5);
 }
 
 .dashboard-content {
-position: relative;
-z-index: 1;
-display: flex;
-gap: 30px;
-padding: 0 30px;
-height: calc(100% - 86px);
-overflow: hidden;
+	position: relative;
+	z-index: 1;
+	display: flex;
+	gap: 30px;
+	padding: 0 30px;
+	height: calc(100% - 86px);
+	overflow: hidden;
 }
 
 /* 纭繚鍚勯潰鏉胯兘澶熸纭樉绀� */
 .left-panel, .center-panel, .right-panel {
-overflow: hidden;
+	overflow: hidden;
 }
 
 .left-panel,
 .right-panel {
-flex: 1;
-display: flex;
-flex-direction: column;
-gap: 24px;
-width: 520px;
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	gap: 24px;
+	width: 520px;
 }
 
 .center-panel {
-flex: 1.5;
-display: flex;
-flex-direction: column;
-gap: 20px;
+	flex: 1.5;
+	display: flex;
+	flex-direction: column;
+	gap: 20px;
 }
 
 </style>
\ No newline at end of file
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue b/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue
index 6fcd80f..493b441 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue
@@ -1,24 +1,24 @@
 <template>
-  <div>
-    <PanelHeader title="瀹㈡埛璐$尞鎺掑悕" />
-    <div class="panel-item-customers">
-      <div class="switch-container">
-        <DateTypeSwitch v-model="dateType" @change="handleDateTypeChange" />
-      </div>
-      <Echarts
-        ref="chart"
-        :chartStyle="chartStyle"
-        :grid="grid"
-        :legend="{ show: false }"
-        :series="series"
-        :tooltip="tooltip"
-        :xAxis="xAxis"
-        :yAxis="yAxis"
-        :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
-        style="height: 360px"
-      />
-    </div>
-  </div>
+	<div>
+		<PanelHeader title="瀹㈡埛璐$尞鎺掑悕" />
+		<div class="panel-item-customers">
+			<div class="switch-container">
+				<DateTypeSwitch v-model="dateType" @change="handleDateTypeChange" />
+			</div>
+			<Echarts
+				ref="chart"
+				:chartStyle="chartStyle"
+				:grid="grid"
+				:legend="{ show: false }"
+				:series="series"
+				:tooltip="tooltip"
+				:xAxis="xAxis"
+				:yAxis="yAxis"
+				:options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+				style="height: 360px"
+			/>
+		</div>
+	</div>
 </template>
 
 <script setup>
@@ -28,31 +28,33 @@
 import DateTypeSwitch from '../DateTypeSwitch.vue'
 import { customerContributionRanking } from '@/api/viewIndex.js'
 
+const dateRange = inject('psiDateRange', null)
+
 const chartStyle = {
-  width: '100%',
-  height: '100%',
+	width: '100%',
+	height: '100%',
 }
 
 const dateType = ref(1) // 1=鍛� 2=鏈� 3=瀛e害
 
 // 椋炴満鍥炬爣 SVG path锛堜笌 right-top 涓�鑷达級
 const aircraft =
-  'path://M107.000,71.000 C104.936,71.000 102.665,70.806 100.273,70.467 C94.592,76.922 86.275,81.000 77.000,81.000 C70.794,81.000 65.020,79.170 60.172,76.029 C66.952,74.165 72.647,69.714 76.173,63.817 C69.821,61.362 64.063,58.593 60.000,56.039 L60.000,52.813 C70.456,53.950 80.723,55.000 83.000,55.000 C88.972,55.000 93.000,53.723 93.000,50.000 C93.000,47.071 89.222,45.000 83.000,45.000 C80.723,45.000 70.456,46.050 60.000,47.187 L60.000,43.989 C64.057,41.431 69.807,38.644 76.168,36.173 C72.641,30.281 66.948,25.834 60.172,23.971 C65.020,20.830 70.794,19.000 77.000,19.000 C86.270,19.000 94.584,23.074 100.265,29.524 C102.647,29.191 104.918,29.000 107.000,29.000 C129.644,29.000 148.000,50.000 148.000,50.000 C148.000,50.000 129.644,71.000 107.000,71.000 ZM113.000,38.000 C106.373,38.000 101.000,43.373 101.000,50.000 C101.000,56.627 106.373,62.000 113.000,62.000 C119.627,62.000 125.000,56.627 125.000,50.000 C125.000,43.373 119.627,38.000 113.000,38.000 ZM113.000,56.000 C109.686,56.000 107.000,53.314 107.000,50.000 C107.000,46.686 109.686,44.000 113.000,44.000 C116.314,44.000 119.000,46.686 119.000,50.000 C119.000,53.314 116.314,56.000 113.000,56.000 ZM110.500,19.000 C109.567,19.000 108.763,18.483 108.334,17.726 C100.231,9.857 89.187,5.000 77.000,5.000 C64.813,5.000 53.769,9.857 45.666,17.726 C45.237,18.483 44.433,19.000 43.500,19.000 C42.119,19.000 41.000,17.881 41.000,16.500 C41.000,15.847 41.256,15.259 41.665,14.813 L41.575,14.718 C50.629,5.628 63.156,-0.000 77.000,-0.000 C90.844,-0.000 103.371,5.628 112.425,14.718 L112.335,14.813 C112.744,15.259 113.000,15.847 113.000,16.500 C113.000,17.881 111.881,19.000 110.500,19.000 ZM53.000,49.484 C61.406,48.626 77.810,47.000 81.345,47.000 C87.353,47.000 91.000,48.243 91.000,50.000 C91.000,52.234 87.111,53.000 81.345,53.000 C77.810,53.000 61.406,51.374 53.000,50.516 L53.000,49.484 ZM53.000,47.000 L9.000,50.000 L53.000,53.000 L53.000,56.000 L-0.000,50.000 L53.000,44.000 L53.000,47.000 ZM43.500,81.000 C44.433,81.000 45.237,81.517 45.666,82.274 C53.769,90.143 64.813,95.000 77.000,95.000 C89.187,95.000 100.231,90.143 108.334,82.274 C108.763,81.517 109.567,81.000 110.500,81.000 C111.881,81.000 113.000,82.119 113.000,83.500 C113.000,84.153 112.744,84.741 112.335,85.187 L112.425,85.282 C103.371,94.372 90.844,100.000 77.000,100.000 C63.156,100.000 50.629,94.372 41.575,85.282 L41.665,85.187 C41.256,84.741 41.000,84.153 41.000,83.500 C41.000,82.119 42.119,81.000 43.500,81.000 Z'
+	'path://M107.000,71.000 C104.936,71.000 102.665,70.806 100.273,70.467 C94.592,76.922 86.275,81.000 77.000,81.000 C70.794,81.000 65.020,79.170 60.172,76.029 C66.952,74.165 72.647,69.714 76.173,63.817 C69.821,61.362 64.063,58.593 60.000,56.039 L60.000,52.813 C70.456,53.950 80.723,55.000 83.000,55.000 C88.972,55.000 93.000,53.723 93.000,50.000 C93.000,47.071 89.222,45.000 83.000,45.000 C80.723,45.000 70.456,46.050 60.000,47.187 L60.000,43.989 C64.057,41.431 69.807,38.644 76.168,36.173 C72.641,30.281 66.948,25.834 60.172,23.971 C65.020,20.830 70.794,19.000 77.000,19.000 C86.270,19.000 94.584,23.074 100.265,29.524 C102.647,29.191 104.918,29.000 107.000,29.000 C129.644,29.000 148.000,50.000 148.000,50.000 C148.000,50.000 129.644,71.000 107.000,71.000 ZM113.000,38.000 C106.373,38.000 101.000,43.373 101.000,50.000 C101.000,56.627 106.373,62.000 113.000,62.000 C119.627,62.000 125.000,56.627 125.000,50.000 C125.000,43.373 119.627,38.000 113.000,38.000 ZM113.000,56.000 C109.686,56.000 107.000,53.314 107.000,50.000 C107.000,46.686 109.686,44.000 113.000,44.000 C116.314,44.000 119.000,46.686 119.000,50.000 C119.000,53.314 116.314,56.000 113.000,56.000 ZM110.500,19.000 C109.567,19.000 108.763,18.483 108.334,17.726 C100.231,9.857 89.187,5.000 77.000,5.000 C64.813,5.000 53.769,9.857 45.666,17.726 C45.237,18.483 44.433,19.000 43.500,19.000 C42.119,19.000 41.000,17.881 41.000,16.500 C41.000,15.847 41.256,15.259 41.665,14.813 L41.575,14.718 C50.629,5.628 63.156,-0.000 77.000,-0.000 C90.844,-0.000 103.371,5.628 112.425,14.718 L112.335,14.813 C112.744,15.259 113.000,15.847 113.000,16.500 C113.000,17.881 111.881,19.000 110.500,19.000 ZM53.000,49.484 C61.406,48.626 77.810,47.000 81.345,47.000 C87.353,47.000 91.000,48.243 91.000,50.000 C91.000,52.234 87.111,53.000 81.345,53.000 C77.810,53.000 61.406,51.374 53.000,50.516 L53.000,49.484 ZM53.000,47.000 L9.000,50.000 L53.000,53.000 L53.000,56.000 L-0.000,50.000 L53.000,44.000 L53.000,47.000 ZM43.500,81.000 C44.433,81.000 45.237,81.517 45.666,82.274 C53.769,90.143 64.813,95.000 77.000,95.000 C89.187,95.000 100.231,90.143 108.334,82.274 C108.763,81.517 109.567,81.000 110.500,81.000 C111.881,81.000 113.000,82.119 113.000,83.500 C113.000,84.153 112.744,84.741 112.335,85.187 L112.425,85.282 C103.371,94.372 90.844,100.000 77.000,100.000 C63.156,100.000 50.629,94.372 41.575,85.282 L41.665,85.187 C41.256,84.741 41.000,84.153 41.000,83.500 C41.000,82.119 42.119,81.000 43.500,81.000 Z'
 
 // 棰滆壊閰嶇疆锛堜笌 right-top 涓�鑷达級
 const color = {
-  0: '#ff5676',
-  1: '#ffd83e',
-  2: '#fbff94',
-  3: '#7daeff',
+	0: '#ff5676',
+	1: '#ffd83e',
+	2: '#fbff94',
+	3: '#7daeff',
 }
 
 // 鍘熷鏁版嵁锛堢粺涓�鎴� { NAME, NUM }锛�
 const dataArr = ref([])
 
 const dataArray = computed(() => {
-  const sortedAsc = [...dataArr.value].sort((a, b) => a.NUM - b.NUM)
-  return sortedAsc.length > 5 ? sortedAsc.slice(-5) : sortedAsc
+	const sortedAsc = [...dataArr.value].sort((a, b) => a.NUM - b.NUM)
+	return sortedAsc.length > 5 ? sortedAsc.slice(-5) : sortedAsc
 })
 
 const total = computed(() => dataArray.value.reduce((sum, v) => sum + Number(v.NUM || 0), 0))
@@ -60,277 +62,277 @@
 const xdataName = computed(() => dataArray.value.map((v) => v.NAME))
 
 const dataNum = computed(() => {
-  return dataArray.value.map((v, i) => {
-    const index = dataArray.value.length - i - 1
-    const isTop3 = index < 3
-
-    return {
-      value: Number(v.NUM),
-      itemStyle: {
-        color: {
-          type: 'linear',
-          x: 1,
-          y: 0,
-          x2: 0,
-          y2: 0,
-          colorStops: [
-            { offset: 0, color: isTop3 ? '#ffdae1' : '#ecf3ff' },
-            { offset: 0.07, color: isTop3 ? color[index] : color[3] },
-            {
-              offset: 1,
-              color: isTop3 ? 'rgba(255, 86, 118, .1)' : 'rgba(125,174,255, .1)',
-            },
-          ],
-          global: false,
-        },
-        barBorderRadius: [0, 20, 20, 0],
-      },
-      symbol: isTop3 ? aircraft : 'none',
-      symbolPosition: 'end',
-      symbolSize: [30, 25],
-      symbolOffset: [35, 0],
-    }
-  })
+	return dataArray.value.map((v, i) => {
+		const index = dataArray.value.length - i - 1
+		const isTop3 = index < 3
+		
+		return {
+			value: Number(v.NUM),
+			itemStyle: {
+				color: {
+					type: 'linear',
+					x: 1,
+					y: 0,
+					x2: 0,
+					y2: 0,
+					colorStops: [
+						{ offset: 0, color: isTop3 ? '#ffdae1' : '#ecf3ff' },
+						{ offset: 0.07, color: isTop3 ? color[index] : color[3] },
+						{
+							offset: 1,
+							color: isTop3 ? 'rgba(255, 86, 118, .1)' : 'rgba(125,174,255, .1)',
+						},
+					],
+					global: false,
+				},
+				barBorderRadius: [0, 20, 20, 0],
+			},
+			symbol: isTop3 ? aircraft : 'none',
+			symbolPosition: 'end',
+			symbolSize: [30, 25],
+			symbolOffset: [35, 0],
+		}
+	})
 })
 
 const bgData = computed(() => {
-  const maxValue = Math.max(0, ...dataNum.value.map((v) => v.value))
-  return dataNum.value.map(() => maxValue + 200)
+	const maxValue = Math.max(0, ...dataNum.value.map((v) => v.value))
+	return dataNum.value.map(() => maxValue + 200)
 })
 
 const tooltip = computed(() => ({
-  trigger: 'axis',
-  textStyle: { fontSize: '100%' },
-  formatter: function (params) {
-    let result = params[0].axisValueLabel + '<br/>'
-    params.forEach((item) => {
-      result += `<div>${item.marker} ${item.seriesName}: ${item.value}鍏�</div>`
-    })
-    return result
-  },
+	trigger: 'axis',
+	textStyle: { fontSize: '100%' },
+	formatter: function (params) {
+		let result = params[0].axisValueLabel + '<br/>'
+		params.forEach((item) => {
+			result += `<div>${item.marker} ${item.seriesName}: ${item.value}鍏�</div>`
+		})
+		return result
+	},
 }))
 
 const grid = computed(() => ({ top: 0, left: '20%', right: '10%', bottom: 0 }))
 
 const xAxis = computed(() => [
-  {
-    splitLine: { show: false },
-    axisLine: { show: false },
-    axisLabel: { show: false },
-    axisTick: { show: false },
-  },
+	{
+		splitLine: { show: false },
+		axisLine: { show: false },
+		axisLabel: { show: false },
+		axisTick: { show: false },
+	},
 ])
 
 const yAxis = computed(() => [
-  {
-    type: 'category',
-    inverse: false,
-    data: xdataName.value,
-    axisLabel: {
-      formatter: (value) => {
-        if (!value) return ''
-        const maxLen = 6 // 姣忚鏈�澶氬瓧绗︽暟锛屽彲鎸夐渶璋冩暣
-        if (value.length <= maxLen) return `{a|${value}}`
-
-        const lines = []
-        for (let i = 0; i < value.length; i += maxLen) {
-          lines.push(value.slice(i, i + maxLen))
-        }
-        return lines.map((line) => `{a|${line}}`).join('\n')
-      },
-      rich: {
-        a: {
-          width: 120,
-          fontSize: 14,
-          color: '#fff',
-          padding: [5, 4, 5, 0],
-          align: 'right',
-        },
-      },
-    },
-    axisLine: { show: false },
-    axisTick: { show: false },
-    splitLine: { show: false },
-  },
-  {
-    type: 'category',
-    data: dataNum.value.map((item) => item.value),
-    axisLabel: {
-      formatter: (params, index) => {
-        const value = typeof params === 'object' ? params.value : params
-        const percent = total.value ? ((value / total.value) * 100).toFixed(0) : 0
-        const rank = dataArray.value.length - index
-        const isTop3 = rank < 4
-
-        return `{a${isTop3 ? rank : ''}|${percent} }{b${isTop3 ? rank : ''}|%}`
-      },
-      rich: {
-        a: { fontSize: 18, color: '#98bfff', verticalAlign: 'bottom' },
-        a1: { fontSize: 18, color: '#ff7f97', verticalAlign: 'bottom' },
-        a2: { fontSize: 18, color: '#ffce64', verticalAlign: 'bottom' },
-        a3: { fontSize: 18, color: '#e8ed66', verticalAlign: 'bottom' },
-        b: { fontSize: 12, color: '#98bfff', verticalAlign: 'bottom' },
-        b1: { fontSize: 12, color: '#ff7f97', verticalAlign: 'bottom' },
-        b2: { fontSize: 12, color: '#ffce64', verticalAlign: 'bottom' },
-        b3: { fontSize: 12, color: '#e8ed66', verticalAlign: 'bottom' },
-      },
-    },
-    axisLine: { show: false },
-    axisTick: { show: false },
-    splitLine: { show: false },
-  },
+	{
+		type: 'category',
+		inverse: false,
+		data: xdataName.value,
+		axisLabel: {
+			formatter: (value) => {
+				if (!value) return ''
+				const maxLen = 6 // 姣忚鏈�澶氬瓧绗︽暟锛屽彲鎸夐渶璋冩暣
+				if (value.length <= maxLen) return `{a|${value}}`
+				
+				const lines = []
+				for (let i = 0; i < value.length; i += maxLen) {
+					lines.push(value.slice(i, i + maxLen))
+				}
+				return lines.map((line) => `{a|${line}}`).join('\n')
+			},
+			rich: {
+				a: {
+					width: 120,
+					fontSize: 14,
+					color: '#fff',
+					padding: [5, 4, 5, 0],
+					align: 'right',
+				},
+			},
+		},
+		axisLine: { show: false },
+		axisTick: { show: false },
+		splitLine: { show: false },
+	},
+	{
+		type: 'category',
+		data: dataNum.value.map((item) => item.value),
+		axisLabel: {
+			formatter: (params, index) => {
+				const value = typeof params === 'object' ? params.value : params
+				const percent = total.value ? ((value / total.value) * 100).toFixed(0) : 0
+				const rank = dataArray.value.length - index
+				const isTop3 = rank < 4
+				
+				return `{a${isTop3 ? rank : ''}|${percent} }{b${isTop3 ? rank : ''}|%}`
+			},
+			rich: {
+				a: { fontSize: 18, color: '#98bfff', verticalAlign: 'bottom' },
+				a1: { fontSize: 18, color: '#ff7f97', verticalAlign: 'bottom' },
+				a2: { fontSize: 18, color: '#ffce64', verticalAlign: 'bottom' },
+				a3: { fontSize: 18, color: '#e8ed66', verticalAlign: 'bottom' },
+				b: { fontSize: 12, color: '#98bfff', verticalAlign: 'bottom' },
+				b1: { fontSize: 12, color: '#ff7f97', verticalAlign: 'bottom' },
+				b2: { fontSize: 12, color: '#ffce64', verticalAlign: 'bottom' },
+				b3: { fontSize: 12, color: '#e8ed66', verticalAlign: 'bottom' },
+			},
+		},
+		axisLine: { show: false },
+		axisTick: { show: false },
+		splitLine: { show: false },
+	},
 ])
 
 const series = computed(() => [
-  {
-    name: '閲戦',
-    z: 6,
-    type: 'pictorialBar',
-    data: dataNum.value,
-  },
-  {
-    name: '鑳屾櫙',
-    z: 6,
-    type: 'bar',
-    barWidth: 25,
-    tooltip: { show: false },
-    itemStyle: {
-      color: 'rgba(255,255,255,.1)',
-      barBorderRadius: [0, 20, 20, 0],
-    },
-    data: bgData.value,
-  },
-  {
-    name: '閲戦娓愬彉',
-    type: 'bar',
-    barWidth: 25,
-    barGap: '-100%',
-    tooltip: { show: false },
-    itemStyle: {
-      color: {
-        type: 'linear',
-        x: 1,
-        y: 0,
-        x2: 0,
-        y2: 0,
-        colorStops: [
-          { offset: 0, color: 'rgba(255, 218, 220)' },
-          { offset: 0.07, color: 'rgba(255, 86, 118)' },
-          { offset: 1, color: 'rgba(255, 86, 118, 0)' },
-        ],
-        global: false,
-      },
-      barBorderRadius: [0, 20, 20, 0],
-    },
-    data: dataNum.value,
-  },
+	{
+		name: '閲戦',
+		z: 6,
+		type: 'pictorialBar',
+		data: dataNum.value,
+	},
+	{
+		name: '鑳屾櫙',
+		z: 6,
+		type: 'bar',
+		barWidth: 25,
+		tooltip: { show: false },
+		itemStyle: {
+			color: 'rgba(255,255,255,.1)',
+			barBorderRadius: [0, 20, 20, 0],
+		},
+		data: bgData.value,
+	},
+	{
+		name: '閲戦娓愬彉',
+		type: 'bar',
+		barWidth: 25,
+		barGap: '-100%',
+		tooltip: { show: false },
+		itemStyle: {
+			color: {
+				type: 'linear',
+				x: 1,
+				y: 0,
+				x2: 0,
+				y2: 0,
+				colorStops: [
+					{ offset: 0, color: 'rgba(255, 218, 220)' },
+					{ offset: 0.07, color: 'rgba(255, 86, 118)' },
+					{ offset: 1, color: 'rgba(255, 86, 118, 0)' },
+				],
+				global: false,
+			},
+			barBorderRadius: [0, 20, 20, 0],
+		},
+		data: dataNum.value,
+	},
 ])
 
 const normalizeItem = (item) => {
-  const name =
-    item?.NAME ??
-    item?.name ??
-    item?.customerName ??
-    item?.customer ??
-    item?.label ??
-    '-'
-
-  const num =
-    item?.NUM ??
-    item?.num ??
-    item?.value ??
-    item?.amount ??
-    item?.money ??
-    0
-
-  return { NAME: String(name), NUM: Number(num) || 0 }
+	const name =
+		item?.NAME ??
+		item?.name ??
+		item?.customerName ??
+		item?.customer ??
+		item?.label ??
+		'-'
+	
+	const num =
+		item?.NUM ??
+		item?.num ??
+		item?.value ??
+		item?.amount ??
+		item?.money ??
+		0
+	
+	return { NAME: String(name), NUM: Number(num) || 0 }
 }
 
 const getMockListByType = (type) => {
-  // 妯℃嫙鍋囨暟鎹紙閲戦璐$尞鎺掑悕锛�
-  // type: 1=鍛� 2=鏈� 3=瀛e害
-  if (type === 2) {
-    return [
-      { NAME: '鍗庝笢绮惧瘑', NUM: 5120000 },
-      { NAME: '鏄熻景鐢靛瓙', NUM: 3860000 },
-      { NAME: '鍚埅绉戞妧', NUM: 2720000 },
-      { NAME: '閾瘹鍒堕��', NUM: 2160000 },
-      { NAME: '杩滄櫙鏉愭枡', NUM: 1430000 },
-      { NAME: '寰锋鼎璐告槗', NUM: 910000 },
-      { NAME: '瀹忚揪閰嶅', NUM: 680000 },
-    ]
-  }
-  if (type === 3) {
-    return [
-      { NAME: '鍗庝笢绮惧瘑', NUM: 16800000 },
-      { NAME: '鏄熻景鐢靛瓙', NUM: 12960000 },
-      { NAME: '鍚埅绉戞妧', NUM: 9720000 },
-      { NAME: '閾瘹鍒堕��', NUM: 7560000 },
-      { NAME: '杩滄櫙鏉愭枡', NUM: 5430000 },
-      { NAME: '寰锋鼎璐告槗', NUM: 3910000 },
-      { NAME: '瀹忚揪閰嶅', NUM: 2680000 },
-    ]
-  }
-  return [
-    { NAME: '鍗庝笢绮惧瘑', NUM: 1280000 },
-    { NAME: '鏄熻景鐢靛瓙', NUM: 860000 },
-    { NAME: '鍚埅绉戞妧', NUM: 720000 },
-    { NAME: '閾瘹鍒堕��', NUM: 560000 },
-    { NAME: '杩滄櫙鏉愭枡', NUM: 430000 },
-    { NAME: '寰锋鼎璐告槗', NUM: 310000 },
-    { NAME: '瀹忚揪閰嶅', NUM: 180000 },
-  ]
+	// 妯℃嫙鍋囨暟鎹紙閲戦璐$尞鎺掑悕锛�
+	// type: 1=鍛� 2=鏈� 3=瀛e害
+	if (type === 2) {
+		return [
+			{ NAME: '鍗庝笢绮惧瘑', NUM: 5120000 },
+			{ NAME: '鏄熻景鐢靛瓙', NUM: 3860000 },
+			{ NAME: '鍚埅绉戞妧', NUM: 2720000 },
+			{ NAME: '閾瘹鍒堕��', NUM: 2160000 },
+			{ NAME: '杩滄櫙鏉愭枡', NUM: 1430000 },
+			{ NAME: '寰锋鼎璐告槗', NUM: 910000 },
+			{ NAME: '瀹忚揪閰嶅', NUM: 680000 },
+		]
+	}
+	if (type === 3) {
+		return [
+			{ NAME: '鍗庝笢绮惧瘑', NUM: 16800000 },
+			{ NAME: '鏄熻景鐢靛瓙', NUM: 12960000 },
+			{ NAME: '鍚埅绉戞妧', NUM: 9720000 },
+			{ NAME: '閾瘹鍒堕��', NUM: 7560000 },
+			{ NAME: '杩滄櫙鏉愭枡', NUM: 5430000 },
+			{ NAME: '寰锋鼎璐告槗', NUM: 3910000 },
+			{ NAME: '瀹忚揪閰嶅', NUM: 2680000 },
+		]
+	}
+	return [
+		{ NAME: '鍗庝笢绮惧瘑', NUM: 1280000 },
+		{ NAME: '鏄熻景鐢靛瓙', NUM: 860000 },
+		{ NAME: '鍚埅绉戞妧', NUM: 720000 },
+		{ NAME: '閾瘹鍒堕��', NUM: 560000 },
+		{ NAME: '杩滄櫙鏉愭枡', NUM: 430000 },
+		{ NAME: '寰锋鼎璐告槗', NUM: 310000 },
+		{ NAME: '瀹忚揪閰嶅', NUM: 180000 },
+	]
 }
 
 const setMockData = (type) => {
-  dataArr.value = getMockListByType(type).map(normalizeItem)
+	dataArr.value = getMockListByType(type).map(normalizeItem)
 }
 
 const fetchCustomerRanking = () => {
-  customerContributionRanking({ type: dateType.value })
-    .then((res) => {
-      if (res.code === 200 && Array.isArray(res.data)) {
-        dataArr.value = res.data.map(item => ({
-          NAME: item.customerName,
-          NUM: item.totalAmount
-        }))
-      } else {
-        setMockData(dateType.value)
-      }
-    })
-    .catch((error) => {
-      console.error('鑾峰彇瀹㈡埛閲戦璐$尞鎺掑悕澶辫触:', error)
-      setMockData(dateType.value)
-    })
+	customerContributionRanking({ type: dateType.value, ...(dateRange?.value || {}) })
+		.then((res) => {
+			if (res.code === 200 && Array.isArray(res.data)) {
+				dataArr.value = res.data.map(item => ({
+					NAME: item.customerName,
+					NUM: item.totalAmount
+				}))
+			} else {
+				setMockData(dateType.value)
+			}
+		})
+		.catch((error) => {
+			console.error('鑾峰彇瀹㈡埛閲戦璐$尞鎺掑悕澶辫触:', error)
+			setMockData(dateType.value)
+		})
 }
 
 const handleDateTypeChange = () => {
-  fetchCustomerRanking()
+	fetchCustomerRanking()
 }
 
 const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
 if (dataDashboardRefreshTick) {
-  watch(dataDashboardRefreshTick, () => {
-    fetchCustomerRanking()
-  })
+	watch(dataDashboardRefreshTick, () => {
+		fetchCustomerRanking()
+	})
 }
 
 onMounted(() => {
-  fetchCustomerRanking()
+	fetchCustomerRanking()
 })
 </script>
 
 <style scoped>
 .panel-item-customers {
-  border: 1px solid #1a58b0;
-  padding: 18px;
-  width: 100%;
-  height: 449px;
+	border: 1px solid #1a58b0;
+	padding: 18px;
+	width: 100%;
+	height: 449px;
 }
 
 .switch-container {
-  display: flex;
-  justify-content: flex-end;
-  margin-bottom: 16px;
+	display: flex;
+	justify-content: flex-end;
+	margin-bottom: 16px;
 }
 </style>
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue b/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue
index 96e4548..73b7b62 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue
@@ -1,23 +1,23 @@
 <template>
-  <div>
-    <PanelHeader title="渚涘簲鍟嗛噰璐帓鍚�" />
-    <div class="panel-item-customers">
-      <div class="switch-container">
-        <DateTypeSwitch v-model="radio1" @change="handleDateTypeChange" />
-      </div>
-      <Echarts
-        ref="chart"
-        :chartStyle="chartStyle"
-        :grid="grid"
-        :series="series"
-        :tooltip="tooltip"
-        :xAxis="xAxis"
-        :yAxis="yAxis"
-        :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
-        style="height: 360px"
-      />
-    </div>
-  </div>
+	<div>
+		<PanelHeader title="渚涘簲鍟嗛噰璐帓鍚�" />
+		<div class="panel-item-customers">
+			<div class="switch-container">
+				<DateTypeSwitch v-model="radio1" @change="handleDateTypeChange" />
+			</div>
+			<Echarts
+				ref="chart"
+				:chartStyle="chartStyle"
+				:grid="grid"
+				:series="series"
+				:tooltip="tooltip"
+				:xAxis="xAxis"
+				:yAxis="yAxis"
+				:options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+				style="height: 360px"
+			/>
+		</div>
+	</div>
 </template>
 
 <script setup>
@@ -27,23 +27,25 @@
 import DateTypeSwitch from '../DateTypeSwitch.vue'
 import { supplierPurchaseRanking } from '@/api/viewIndex.js'
 
+const dateRange = inject('psiDateRange', null)
+
 const chartStyle = {
-  width: '100%',
-  height: '100%',
+	width: '100%',
+	height: '100%',
 }
 
 const radio1 = ref(1)
 
 // 椋炴満鍥炬爣 SVG path
 const aircraft =
-  'path://M107.000,71.000 C104.936,71.000 102.665,70.806 100.273,70.467 C94.592,76.922 86.275,81.000 77.000,81.000 C70.794,81.000 65.020,79.170 60.172,76.029 C66.952,74.165 72.647,69.714 76.173,63.817 C69.821,61.362 64.063,58.593 60.000,56.039 L60.000,52.813 C70.456,53.950 80.723,55.000 83.000,55.000 C88.972,55.000 93.000,53.723 93.000,50.000 C93.000,47.071 89.222,45.000 83.000,45.000 C80.723,45.000 70.456,46.050 60.000,47.187 L60.000,43.989 C64.057,41.431 69.807,38.644 76.168,36.173 C72.641,30.281 66.948,25.834 60.172,23.971 C65.020,20.830 70.794,19.000 77.000,19.000 C86.270,19.000 94.584,23.074 100.265,29.524 C102.647,29.191 104.918,29.000 107.000,29.000 C129.644,29.000 148.000,50.000 148.000,50.000 C148.000,50.000 129.644,71.000 107.000,71.000 ZM113.000,38.000 C106.373,38.000 101.000,43.373 101.000,50.000 C101.000,56.627 106.373,62.000 113.000,62.000 C119.627,62.000 125.000,56.627 125.000,50.000 C125.000,43.373 119.627,38.000 113.000,38.000 ZM113.000,56.000 C109.686,56.000 107.000,53.314 107.000,50.000 C107.000,46.686 109.686,44.000 113.000,44.000 C116.314,44.000 119.000,46.686 119.000,50.000 C119.000,53.314 116.314,56.000 113.000,56.000 ZM110.500,19.000 C109.567,19.000 108.763,18.483 108.334,17.726 C100.231,9.857 89.187,5.000 77.000,5.000 C64.813,5.000 53.769,9.857 45.666,17.726 C45.237,18.483 44.433,19.000 43.500,19.000 C42.119,19.000 41.000,17.881 41.000,16.500 C41.000,15.847 41.256,15.259 41.665,14.813 L41.575,14.718 C50.629,5.628 63.156,-0.000 77.000,-0.000 C90.844,-0.000 103.371,5.628 112.425,14.718 L112.335,14.813 C112.744,15.259 113.000,15.847 113.000,16.500 C113.000,17.881 111.881,19.000 110.500,19.000 ZM53.000,49.484 C61.406,48.626 77.810,47.000 81.345,47.000 C87.353,47.000 91.000,48.243 91.000,50.000 C91.000,52.234 87.111,53.000 81.345,53.000 C77.810,53.000 61.406,51.374 53.000,50.516 L53.000,49.484 ZM53.000,47.000 L9.000,50.000 L53.000,53.000 L53.000,56.000 L-0.000,50.000 L53.000,44.000 L53.000,47.000 ZM43.500,81.000 C44.433,81.000 45.237,81.517 45.666,82.274 C53.769,90.143 64.813,95.000 77.000,95.000 C89.187,95.000 100.231,90.143 108.334,82.274 C108.763,81.517 109.567,81.000 110.500,81.000 C111.881,81.000 113.000,82.119 113.000,83.500 C113.000,84.153 112.744,84.741 112.335,85.187 L112.425,85.282 C103.371,94.372 90.844,100.000 77.000,100.000 C63.156,100.000 50.629,94.372 41.575,85.282 L41.665,85.187 C41.256,84.741 41.000,84.153 41.000,83.500 C41.000,82.119 42.119,81.000 43.500,81.000 Z'
+	'path://M107.000,71.000 C104.936,71.000 102.665,70.806 100.273,70.467 C94.592,76.922 86.275,81.000 77.000,81.000 C70.794,81.000 65.020,79.170 60.172,76.029 C66.952,74.165 72.647,69.714 76.173,63.817 C69.821,61.362 64.063,58.593 60.000,56.039 L60.000,52.813 C70.456,53.950 80.723,55.000 83.000,55.000 C88.972,55.000 93.000,53.723 93.000,50.000 C93.000,47.071 89.222,45.000 83.000,45.000 C80.723,45.000 70.456,46.050 60.000,47.187 L60.000,43.989 C64.057,41.431 69.807,38.644 76.168,36.173 C72.641,30.281 66.948,25.834 60.172,23.971 C65.020,20.830 70.794,19.000 77.000,19.000 C86.270,19.000 94.584,23.074 100.265,29.524 C102.647,29.191 104.918,29.000 107.000,29.000 C129.644,29.000 148.000,50.000 148.000,50.000 C148.000,50.000 129.644,71.000 107.000,71.000 ZM113.000,38.000 C106.373,38.000 101.000,43.373 101.000,50.000 C101.000,56.627 106.373,62.000 113.000,62.000 C119.627,62.000 125.000,56.627 125.000,50.000 C125.000,43.373 119.627,38.000 113.000,38.000 ZM113.000,56.000 C109.686,56.000 107.000,53.314 107.000,50.000 C107.000,46.686 109.686,44.000 113.000,44.000 C116.314,44.000 119.000,46.686 119.000,50.000 C119.000,53.314 116.314,56.000 113.000,56.000 ZM110.500,19.000 C109.567,19.000 108.763,18.483 108.334,17.726 C100.231,9.857 89.187,5.000 77.000,5.000 C64.813,5.000 53.769,9.857 45.666,17.726 C45.237,18.483 44.433,19.000 43.500,19.000 C42.119,19.000 41.000,17.881 41.000,16.500 C41.000,15.847 41.256,15.259 41.665,14.813 L41.575,14.718 C50.629,5.628 63.156,-0.000 77.000,-0.000 C90.844,-0.000 103.371,5.628 112.425,14.718 L112.335,14.813 C112.744,15.259 113.000,15.847 113.000,16.500 C113.000,17.881 111.881,19.000 110.500,19.000 ZM53.000,49.484 C61.406,48.626 77.810,47.000 81.345,47.000 C87.353,47.000 91.000,48.243 91.000,50.000 C91.000,52.234 87.111,53.000 81.345,53.000 C77.810,53.000 61.406,51.374 53.000,50.516 L53.000,49.484 ZM53.000,47.000 L9.000,50.000 L53.000,53.000 L53.000,56.000 L-0.000,50.000 L53.000,44.000 L53.000,47.000 ZM43.500,81.000 C44.433,81.000 45.237,81.517 45.666,82.274 C53.769,90.143 64.813,95.000 77.000,95.000 C89.187,95.000 100.231,90.143 108.334,82.274 C108.763,81.517 109.567,81.000 110.500,81.000 C111.881,81.000 113.000,82.119 113.000,83.500 C113.000,84.153 112.744,84.741 112.335,85.187 L112.425,85.282 C103.371,94.372 90.844,100.000 77.000,100.000 C63.156,100.000 50.629,94.372 41.575,85.282 L41.665,85.187 C41.256,84.741 41.000,84.153 41.000,83.500 C41.000,82.119 42.119,81.000 43.500,81.000 Z'
 
 // 棰滆壊閰嶇疆
 const color = {
-  0: '#ff5676',
-  1: '#ffd83e',
-  2: '#fbff94',
-  3: '#7daeff',
+	0: '#ff5676',
+	1: '#ffd83e',
+	2: '#fbff94',
+	3: '#7daeff',
 }
 
 // 鍘熷鏁版嵁
@@ -51,315 +53,315 @@
 
 // 鎺掑簭鍚庣殑鏁版嵁
 const dataArray = computed(() => {
-  return [...dataArr.value].sort((a, b) => a.NUM - b.NUM)
+	return [...dataArr.value].sort((a, b) => a.NUM - b.NUM)
 })
 
 // 璁$畻鎬绘暟
 const total = computed(() => {
-  return dataArray.value.reduce((sum, v) => sum + Number(v.NUM), 0)
+	return dataArray.value.reduce((sum, v) => sum + Number(v.NUM), 0)
 })
 
 // x杞存暟鎹紙鍚嶇О锛�
 const xdataName = computed(() => {
-  return dataArray.value.map((v) => v.NAME)
+	return dataArray.value.map((v) => v.NAME)
 })
 
 // y杞存暟鎹紙鏁板�硷紝甯︽牱寮忥級
 const dataNum = computed(() => {
-  return dataArray.value.map((v, i) => {
-    const index = dataArray.value.length - i - 1
-    const isTop3 = index < 3
-
-    return {
-      value: Number(v.NUM),
-      itemStyle: {
-        color: {
-          type: 'linear',
-          x: 1,
-          y: 0,
-          x2: 0,
-          y2: 0,
-          colorStops: [
-            {
-              offset: 0,
-              color: isTop3 ? '#ffdae1' : '#ecf3ff',
-            },
-            {
-              offset: 0.07,
-              color: isTop3 ? color[index] : color[3],
-            },
-            {
-              offset: 1,
-              color: isTop3
-                ? 'rgba(255, 86, 118, .1)'
-                : 'rgba(125,174,255, .1)',
-            },
-          ],
-          global: false,
-        },
-        barBorderRadius: [0, 20, 20, 0],
-      },
-      symbol: isTop3 ? aircraft : 'none',
-      symbolPosition: 'end',
-      symbolSize: [30, 25],
-      symbolOffset: [35, 0],
-    }
-  })
+	return dataArray.value.map((v, i) => {
+		const index = dataArray.value.length - i - 1
+		const isTop3 = index < 3
+		
+		return {
+			value: Number(v.NUM),
+			itemStyle: {
+				color: {
+					type: 'linear',
+					x: 1,
+					y: 0,
+					x2: 0,
+					y2: 0,
+					colorStops: [
+						{
+							offset: 0,
+							color: isTop3 ? '#ffdae1' : '#ecf3ff',
+						},
+						{
+							offset: 0.07,
+							color: isTop3 ? color[index] : color[3],
+						},
+						{
+							offset: 1,
+							color: isTop3
+								? 'rgba(255, 86, 118, .1)'
+								: 'rgba(125,174,255, .1)',
+						},
+					],
+					global: false,
+				},
+				barBorderRadius: [0, 20, 20, 0],
+			},
+			symbol: isTop3 ? aircraft : 'none',
+			symbolPosition: 'end',
+			symbolSize: [30, 25],
+			symbolOffset: [35, 0],
+		}
+	})
 })
 
 // 鑳屾櫙鏁版嵁
 const bgData = computed(() => {
-  const maxValue = Math.max(...dataNum.value.map((v) => v.value))
-  return dataNum.value.map(() => maxValue + 200)
+	const maxValue = Math.max(...dataNum.value.map((v) => v.value))
+	return dataNum.value.map(() => maxValue + 200)
 })
 
 // tooltip
 const tooltip = computed(() => {
-  return {
-    trigger: 'axis',
-    textStyle: { fontSize: '100%' },
-    formatter: function (params) {
-      let result = params[0].axisValueLabel + '<br/>'
-      result += `<div>${params[0].marker}${params[0].value}鍏�</div>`
-      return result
-    },
-  }
+	return {
+		trigger: 'axis',
+		textStyle: { fontSize: '100%' },
+		formatter: function (params) {
+			let result = params[0].axisValueLabel + '<br/>'
+			result += `<div>${params[0].marker}${params[0].value}鍏�</div>`
+			return result
+		},
+	}
 })
 
 // grid
 const grid = computed(() => {
-  return { top: 0, left: '20%', right: '10%', bottom: 0 }
+	return { top: 0, left: '20%', right: '10%', bottom: 0 }
 })
 
 // xAxis
 const xAxis = computed(() => {
-  return [
-    {
-      splitLine: { show: false },
-      axisLine: { show: false },
-      axisLabel: { show: false },
-      axisTick: { show: false },
-    },
-  ]
+	return [
+		{
+			splitLine: { show: false },
+			axisLine: { show: false },
+			axisLabel: { show: false },
+			axisTick: { show: false },
+		},
+	]
 })
 
 // yAxis
 const yAxis = computed(() => {
-  return [
-    {
-      type: 'category',
-      inverse: false,
-      data: xdataName.value,
-      axisLabel: {
-        formatter: (value) => {
-          if (!value) return ''
-          const maxLen = 6 // 姣忚鏈�澶氬瓧绗︽暟锛屽彲鎸夐渶璋冩暣
-          if (value.length <= maxLen) return `{a|${value}}`
-
-          const lines = []
-          for (let i = 0; i < value.length; i += maxLen) {
-            lines.push(value.slice(i, i + maxLen))
-          }
-          // 澶氳鏂囨湰锛屾瘡琛岄兘濂楀悓涓�涓� rich 鏍峰紡
-          return lines.map((line) => `{a|${line}}`).join('\n')
-        },
-        rich: {
-          a: {
-            width: 120,
-            fontSize: 14,
-            color: '#fff',
-            padding: [5, 4, 5, 0],
-            align: 'right',
-          },
-        },
-      },
-      axisLine: { show: false },
-      axisTick: { show: false },
-      splitLine: { show: false },
-    },
-    {
-      type: 'category',
-      data: dataNum.value.map((item) => item.value),
-      axisLabel: {
-        formatter: (params, index) => {
-          const value = typeof params === 'object' ? params.value : params
-          const percent = ((value / total.value) * 100).toFixed(0)
-          const rank = dataArray.value.length - index
-          const isTop3 = rank < 4
-
-          return `{a${isTop3 ? rank : ''}|${percent} }{b${isTop3 ? rank : ''}|%}`
-        },
-        rich: {
-          a: {
-            fontSize: 18,
-            color: '#98bfff',
-            verticalAlign: 'bottom',
-          },
-          a1: {
-            fontSize: 18,
-            color: '#ff7f97',
-            verticalAlign: 'bottom',
-          },
-          a2: {
-            fontSize: 18,
-            color: '#ffce64',
-            verticalAlign: 'bottom',
-          },
-          a3: {
-            fontSize: 18,
-            color: '#e8ed66',
-            verticalAlign: 'bottom',
-          },
-          b: {
-            fontSize: 12,
-            color: '#98bfff',
-            verticalAlign: 'bottom',
-          },
-          b1: {
-            fontSize: 12,
-            color: '#ff7f97',
-            verticalAlign: 'bottom',
-          },
-          b2: {
-            fontSize: 12,
-            color: '#ffce64',
-            verticalAlign: 'bottom',
-          },
-          b3: {
-            fontSize: 12,
-            color: '#e8ed66',
-            verticalAlign: 'bottom',
-          },
-        },
-      },
-      axisLine: { show: false },
-      axisTick: { show: false },
-      splitLine: { show: false },
-    },
-  ]
+	return [
+		{
+			type: 'category',
+			inverse: false,
+			data: xdataName.value,
+			axisLabel: {
+				formatter: (value) => {
+					if (!value) return ''
+					const maxLen = 6 // 姣忚鏈�澶氬瓧绗︽暟锛屽彲鎸夐渶璋冩暣
+					if (value.length <= maxLen) return `{a|${value}}`
+					
+					const lines = []
+					for (let i = 0; i < value.length; i += maxLen) {
+						lines.push(value.slice(i, i + maxLen))
+					}
+					// 澶氳鏂囨湰锛屾瘡琛岄兘濂楀悓涓�涓� rich 鏍峰紡
+					return lines.map((line) => `{a|${line}}`).join('\n')
+				},
+				rich: {
+					a: {
+						width: 120,
+						fontSize: 14,
+						color: '#fff',
+						padding: [5, 4, 5, 0],
+						align: 'right',
+					},
+				},
+			},
+			axisLine: { show: false },
+			axisTick: { show: false },
+			splitLine: { show: false },
+		},
+		{
+			type: 'category',
+			data: dataNum.value.map((item) => item.value),
+			axisLabel: {
+				formatter: (params, index) => {
+					const value = typeof params === 'object' ? params.value : params
+					const percent = ((value / total.value) * 100).toFixed(0)
+					const rank = dataArray.value.length - index
+					const isTop3 = rank < 4
+					
+					return `{a${isTop3 ? rank : ''}|${percent} }{b${isTop3 ? rank : ''}|%}`
+				},
+				rich: {
+					a: {
+						fontSize: 18,
+						color: '#98bfff',
+						verticalAlign: 'bottom',
+					},
+					a1: {
+						fontSize: 18,
+						color: '#ff7f97',
+						verticalAlign: 'bottom',
+					},
+					a2: {
+						fontSize: 18,
+						color: '#ffce64',
+						verticalAlign: 'bottom',
+					},
+					a3: {
+						fontSize: 18,
+						color: '#e8ed66',
+						verticalAlign: 'bottom',
+					},
+					b: {
+						fontSize: 12,
+						color: '#98bfff',
+						verticalAlign: 'bottom',
+					},
+					b1: {
+						fontSize: 12,
+						color: '#ff7f97',
+						verticalAlign: 'bottom',
+					},
+					b2: {
+						fontSize: 12,
+						color: '#ffce64',
+						verticalAlign: 'bottom',
+					},
+					b3: {
+						fontSize: 12,
+						color: '#e8ed66',
+						verticalAlign: 'bottom',
+					},
+				},
+			},
+			axisLine: { show: false },
+			axisTick: { show: false },
+			splitLine: { show: false },
+		},
+	]
 })
 
 // series
 const series = computed(() => {
-  return [
-    {
-      z: 6,
-      type: 'pictorialBar',
-      data: dataNum.value,
-    },
-    {
-      z: 6,
-      type: 'bar',
-      barWidth: 25,
-      tooltip: { show: false },
-      itemStyle: {
-        color: 'rgba(255,255,255,.1)',
-        barBorderRadius: [0, 20, 20, 0],
-      },
-      data: bgData.value,
-    },
-    {
-      type: 'bar',
-      barWidth: 25,
-      barGap: '-100%',
-      tooltip: { show: false },
-      itemStyle: {
-        color: {
-          type: 'linear',
-          x: 1,
-          y: 0,
-          x2: 0,
-          y2: 0,
-          colorStops: [
-            {
-              offset: 0,
-              color: 'rgba(255, 218, 220)',
-            },
-            {
-              offset: 0.07,
-              color: 'rgba(255, 86, 118)',
-            },
-            {
-              offset: 1,
-              color: 'rgba(255, 86, 118, 0)',
-            },
-          ],
-          global: false,
-        },
-        barBorderRadius: [0, 20, 20, 0],
-      },
-      data: dataNum.value,
-    },
-  ]
+	return [
+		{
+			z: 6,
+			type: 'pictorialBar',
+			data: dataNum.value,
+		},
+		{
+			z: 6,
+			type: 'bar',
+			barWidth: 25,
+			tooltip: { show: false },
+			itemStyle: {
+				color: 'rgba(255,255,255,.1)',
+				barBorderRadius: [0, 20, 20, 0],
+			},
+			data: bgData.value,
+		},
+		{
+			type: 'bar',
+			barWidth: 25,
+			barGap: '-100%',
+			tooltip: { show: false },
+			itemStyle: {
+				color: {
+					type: 'linear',
+					x: 1,
+					y: 0,
+					x2: 0,
+					y2: 0,
+					colorStops: [
+						{
+							offset: 0,
+							color: 'rgba(255, 218, 220)',
+						},
+						{
+							offset: 0.07,
+							color: 'rgba(255, 86, 118)',
+						},
+						{
+							offset: 1,
+							color: 'rgba(255, 86, 118, 0)',
+						},
+					],
+					global: false,
+				},
+				barBorderRadius: [0, 20, 20, 0],
+			},
+			data: dataNum.value,
+		},
+	]
 })
 
 // 渚涘簲鍟嗛噰璐帓鍚�
 const fetchSupplierRanking = () => {
-  supplierPurchaseRanking({ type: radio1.value })
-    .then((res) => {
-      if (res.code === 200 && Array.isArray(res.data)) {
-        dataArr.value = res.data.map(item => ({
-          NAME: item.supplierName,
-          NUM: item.totalAmount
-        }))
-      } else {
-        // 濡傛灉娌℃湁鏁版嵁锛屼娇鐢ㄦā鎷熸暟鎹�
-        dataArr.value = [
-          { NAME: '渚涘簲鍟咥', NUM: 102 },
-          { NAME: '渚涘簲鍟咮', NUM: 122 },
-          { NAME: '渚涘簲鍟咰', NUM: 282 },
-          { NAME: '渚涘簲鍟咲', NUM: 453 },
-          { NAME: '渚涘簲鍟咵', NUM: 753 },
-        ]
-      }
-    })
-    .catch((error) => {
-      console.error('鑾峰彇渚涘簲鍟嗛噰璐帓鍚嶅け璐�:', error)
-      // 浣跨敤妯℃嫙鏁版嵁
-      dataArr.value = [
-        { NAME: '渚涘簲鍟咥', NUM: 102 },
-        { NAME: '渚涘簲鍟咮', NUM: 122 },
-        { NAME: '渚涘簲鍟咰', NUM: 282 },
-        { NAME: '渚涘簲鍟咲', NUM: 453 },
-        { NAME: '渚涘簲鍟咵', NUM: 753 },
-      ]
-    })
+	supplierPurchaseRanking({ type: radio1.value, ...(dateRange?.value || {}) })
+		.then((res) => {
+			if (res.code === 200 && Array.isArray(res.data)) {
+				dataArr.value = res.data.map(item => ({
+					NAME: item.supplierName,
+					NUM: item.totalAmount
+				}))
+			} else {
+				// 濡傛灉娌℃湁鏁版嵁锛屼娇鐢ㄦā鎷熸暟鎹�
+				dataArr.value = [
+					{ NAME: '渚涘簲鍟咥', NUM: 102 },
+					{ NAME: '渚涘簲鍟咮', NUM: 122 },
+					{ NAME: '渚涘簲鍟咰', NUM: 282 },
+					{ NAME: '渚涘簲鍟咲', NUM: 453 },
+					{ NAME: '渚涘簲鍟咵', NUM: 753 },
+				]
+			}
+		})
+		.catch((error) => {
+			console.error('鑾峰彇渚涘簲鍟嗛噰璐帓鍚嶅け璐�:', error)
+			// 浣跨敤妯℃嫙鏁版嵁
+			dataArr.value = [
+				{ NAME: '渚涘簲鍟咥', NUM: 102 },
+				{ NAME: '渚涘簲鍟咮', NUM: 122 },
+				{ NAME: '渚涘簲鍟咰', NUM: 282 },
+				{ NAME: '渚涘簲鍟咲', NUM: 453 },
+				{ NAME: '渚涘簲鍟咵', NUM: 753 },
+			]
+		})
 }
 
 // 澶勭悊鏃ユ湡绫诲瀷鍒囨崲
 const handleDateTypeChange = (value) => {
-  fetchSupplierRanking()
+	fetchSupplierRanking()
 }
 
 const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null)
 if (dataDashboardRefreshTick) {
-  watch(dataDashboardRefreshTick, () => {
-    fetchSupplierRanking()
-  })
+	watch(dataDashboardRefreshTick, () => {
+		fetchSupplierRanking()
+	})
 }
 
 onMounted(() => {
-  fetchSupplierRanking()
+	fetchSupplierRanking()
 })
 </script>
 
 <style scoped>
 .panel-item-customers {
-  border: 1px solid #1a58b0;
-  padding: 18px;
-  width: 100%;
-  height: 449px;
+	border: 1px solid #1a58b0;
+	padding: 18px;
+	width: 100%;
+	height: 449px;
 }
 
 .switch-container {
-  display: flex;
-  justify-content: flex-end;
-  margin-bottom: 16px;
+	display: flex;
+	justify-content: flex-end;
+	margin-bottom: 16px;
 }
 
 .section-title {
-  font-weight: 500;
-  font-size: 16px;
-  color: #d9ecff;
+	font-weight: 500;
+	font-size: 16px;
+	color: #d9ecff;
 }
 </style>
diff --git a/src/views/reportAnalysis/taxComparison/index.vue b/src/views/reportAnalysis/taxComparison/index.vue
index d27ab07..25f3827 100644
--- a/src/views/reportAnalysis/taxComparison/index.vue
+++ b/src/views/reportAnalysis/taxComparison/index.vue
@@ -1,118 +1,411 @@
 <template>
-  <div class="app-container">
-    <el-form :model="filters" :inline="true">
-      <el-form-item label="鏃ユ湡">
-        <el-date-picker
-          style="width: 240px"
-          v-model="filters.month"
-          value-format="YYYY-MM"
-          format="YYYY-MM"
-          type="month"
-          placeholder="閫夋嫨鏈堜唤"
-          clearable
-          @change="getTableData"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" @click="getTableData"> 鎼滅储 </el-button>
-        <el-button @click="resetFilters"> 閲嶇疆 </el-button>
-        <el-button @click="handleOut"> 瀵煎嚭 </el-button>
-      </el-form-item>
-    </el-form>
-    <div class="table_list">
-      <PIMTable
-        rowKey="id"
-        :column="columns"
-        :tableData="dataList"
-        :page="{
-          current: pagination.currentPage,
-          size: pagination.pageSize,
-          total: pagination.total,
-        }"
-        @pagination="changePage"
-      />
-    </div>
-  </div>
+	<div class="app-container vat-page">
+		<el-row :gutter="20" class="vat-row">
+			<!-- 宸︿晶锛氬鍊肩◣鏄庣粏鍒楄〃 -->
+			<el-col :span="15" class="vat-col">
+				<el-card shadow="never" class="vat-card">
+					<template #header>
+						<div class="card-header">
+							<span class="card-title">澧炲�肩◣鏄庣粏</span>
+							<el-select v-model="queryMonth" placeholder="閫夋嫨鏈堜唤" clearable style="width: 150px;" @change="handleMonthChange">
+								<el-option v-for="m in monthOptions" :key="m" :label="m" :value="m" />
+							</el-select>
+						</div>
+					</template>
+					<template v-if="!loading && vatDetailList.length === 0">
+						<div class="empty-full">
+							<el-empty description="鏆傛棤澧炲�肩◣鏄庣粏鏁版嵁" :image-size="160" />
+						</div>
+					</template>
+					<template v-else>
+						<div class="table-wrapper">
+							<el-table :data="vatDetailList" border v-loading="loading" stripe height="100%">
+								<el-table-column prop="orderType" label="绫诲瀷" width="65" align="center">
+									<template #default="scope">
+										<el-tag :type="scope.row.orderType === '杩涢」' ? '' : 'warning'" size="small" effect="plain">
+											{{ scope.row.orderType }}
+										</el-tag>
+									</template>
+								</el-table-column>
+								<el-table-column prop="invoiceNo" label="鍙戠エ鍙�" min-width="140" show-overflow-tooltip />
+								<el-table-column prop="salesContractNo" label="鍚堝悓鍙�" min-width="140" show-overflow-tooltip />
+								<el-table-column prop="supplierName" label="渚涘簲鍟�" min-width="100" show-overflow-tooltip />
+								<el-table-column prop="customerName" label="瀹㈡埛" min-width="100" show-overflow-tooltip />
+								<el-table-column prop="invoiceDate" label="寮�绁ㄦ棩鏈�" width="110" align="center" />
+								<el-table-column prop="taxRate" label="绋庣巼" width="75" align="center">
+									<template #default="scope">
+										{{ scope.row.taxRate }}%
+									</template>
+								</el-table-column>
+								<el-table-column prop="taxAmount" label="绋庨" width="110" align="right">
+									<template #default="scope">
+                    <span class="tax-amount" :class="scope.row.orderType === '杩涢」' ? 'input-tax' : 'output-tax'">
+                      楼{{ formatNumber(scope.row.taxAmount) }}
+                    </span>
+									</template>
+								</el-table-column>
+							</el-table>
+						</div>
+						<el-pagination
+							v-if="page.total > 0"
+							class="vat-pagination"
+							background
+							layout="total, sizes, prev, pager, next, jumper"
+							:current-page="page.current"
+							:page-sizes="[10, 20, 50]"
+							:page-size="page.size"
+							:total="page.total"
+							@size-change="handleSizeChange"
+							@current-change="handleCurrentChange" />
+					</template>
+				</el-card>
+			</el-col>
+			
+			<!-- 鍙充晶锛氭煴鐘跺姣斿浘 -->
+			<el-col :span="9" class="vat-col">
+				<el-card shadow="never" class="vat-card">
+					<template #header>
+						<span class="card-title">杩涢攢椤瑰鍊肩◣瀵规瘮</span>
+					</template>
+					<div class="summary-row">
+						<div class="summary-item input-bg">
+							<div class="summary-label">杩涢」绋庨鍚堣</div>
+							<div class="summary-value">楼{{ formatNumber(inputTotal) }}</div>
+						</div>
+						<div class="summary-item output-bg">
+							<div class="summary-label">閿�椤圭◣棰濆悎璁�</div>
+							<div class="summary-value">楼{{ formatNumber(outputTotal) }}</div>
+						</div>
+						<div class="summary-item diff-bg">
+							<div class="summary-label">宸</div>
+							<div class="summary-value" :class="diffValue >= 0 ? 'positive' : 'negative'">
+								{{ diffValue >= 0 ? '+' : '' }}楼{{ formatNumber(diffValue) }}
+							</div>
+						</div>
+					</div>
+					<div v-if="vatDetailList.length > 0" ref="vatChart" class="chart-container"></div>
+					<div v-else class="chart-empty">鏆傛棤鏁版嵁</div>
+				</el-card>
+			</el-col>
+		</el-row>
+	</div>
 </template>
 
 <script setup>
-import { usePaginationApi } from "@/hooks/usePaginationApi";
-import { onMounted, getCurrentInstance } from "vue";
-import { getTaxList } from "@/api/procurementManagement/taxComparison";
-import { ElMessageBox } from "element-plus";
-
-const { proxy } = getCurrentInstance();
+import { ref, reactive, computed, onMounted, nextTick, onBeforeUnmount } from "vue";
+import * as echarts from 'echarts';
+import { getVatDetail } from "@/api/procurementManagement/taxComparison";
 
 defineOptions({
-  name: "澧炲�肩◣姣斿",
+	name: "澧炲�肩◣姣斿",
 });
 
-const {
-  loading,
-  filters,
-  columns,
-  dataList,
-  pagination,
-  getTableData,
-  resetFilters,
-  onCurrentChange,
-} = usePaginationApi(
-  getTaxList,
-  {
-    month: [], // 鏉ョエ鏃ユ湡
-  },
-  [
-    {
-      label: "鏈堜唤",
-      prop: "month",
-      align: "center",
-    },
-    {
-      label: "閿�椤圭◣棰�",
-      prop: "jtaxAmount",
-      align: "center",
-    },
-    {
-      label: "杩涢」绋庨",
-      prop: "xtaxAmount",
-      align: "center",
-    },
-    {
-      label: "閿�-杩�",
-      prop: "taxAmount",
-      align: "center",
-    },
-  ],
-  {}
-);
+const vatChart = ref(null);
+let chartInstance = null;
 
-const changePage = ({ page }) => {
-  pagination.currentPage = page;
-  onCurrentChange(page);
+const queryMonth = ref(new Date().toISOString().slice(0, 7));
+const monthOptions = ref([]);
+const vatDetailList = ref([]);
+const loading = ref(false);
+const page = reactive({ current: 1, size: 20, total: 0 });
+
+// 姹囨�婚
+const inputTotal = computed(() => {
+	return vatDetailList.value
+		.filter(i => i.orderType === '杩涢」')
+		.reduce((sum, i) => sum + (Number(i.taxAmount) || 0), 0);
+});
+const outputTotal = computed(() => {
+	return vatDetailList.value
+		.filter(i => i.orderType === '閿�椤�')
+		.reduce((sum, i) => sum + (Number(i.taxAmount) || 0), 0);
+});
+const diffValue = computed(() => outputTotal.value - inputTotal.value);
+
+const formatNumber = (val) => {
+	const num = Number(val);
+	if (isNaN(num)) return '0.00';
+	return num.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
 };
 
-// 瀵煎嚭
-const handleOut = () => {
-  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
-    confirmButtonText: "纭",
-    cancelButtonText: "鍙栨秷",
-    type: "warning",
-  })
-    .then(() => {
-      proxy.download("/purchase/report/exportTwo", {}, "澧炲�肩◣姣斿.xlsx");
-    })
-    .catch(() => {
-      proxy.$modal.msg("宸插彇娑�");
-    });
+const loadVatDetail = () => {
+	loading.value = true;
+	const params = {
+		current: page.current,
+		size: page.size,
+		month: queryMonth.value || undefined,
+	};
+	getVatDetail(params)
+		.then((res) => {
+			if (res.code === 200) {
+				vatDetailList.value = res.data.records || [];
+				page.total = res.data.total || 0;
+				nextTick(() => renderChart());
+			}
+		})
+		.finally(() => {
+			loading.value = false;
+		});
+};
+
+const renderChart = () => {
+	if (!vatChart.value) return;
+	if (chartInstance) {
+		chartInstance.dispose();
+	}
+	chartInstance = echarts.init(vatChart.value);
+	
+	const data = vatDetailList.value;
+	const invoices = data.map(i => i.invoiceNo);
+	
+	chartInstance.setOption({
+		tooltip: {
+			trigger: 'axis',
+			backgroundColor: 'rgba(255,255,255,0.95)',
+			borderColor: '#e0e0e0',
+			borderWidth: 1,
+			textStyle: { color: '#333', fontSize: 13 },
+			formatter: function (params) {
+				let html = `<b>${params[0].axisValue}</b><br/>`;
+				params.forEach(p => {
+					if (p.value > 0) {
+						html += `${p.marker} ${p.seriesName}: 楼${formatNumber(p.value)}<br/>`;
+					}
+				});
+				return html;
+			},
+		},
+		legend: {
+			data: ['杩涢」绋庨', '閿�椤圭◣棰�'],
+			bottom: 0,
+			textStyle: { fontSize: 12 },
+		},
+		grid: {
+			left: '10%',
+			right: '8%',
+			top: '8%',
+			bottom: '12%',
+		},
+		xAxis: {
+			type: 'category',
+			data: invoices,
+			axisLabel: {
+				rotate: 45,
+				fontSize: 10,
+				interval: 0,
+			},
+			axisTick: { alignWithLabel: true },
+		},
+		yAxis: {
+			type: 'value',
+			name: '绋庨(鍏�)',
+			nameTextStyle: { fontSize: 11 },
+			axisLabel: {
+				formatter: (val) => val >= 10000 ? `${(val / 10000).toFixed(1)}涓嘸 : val,
+			},
+		},
+		series: [
+			{
+				name: '杩涢」绋庨',
+				type: 'bar',
+				barWidth: '35%',
+				data: data.map(i => i.orderType === '杩涢」' ? Number(i.taxAmount) || 0 : null),
+				itemStyle: {
+					color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+						{ offset: 0, color: '#667eea' },
+						{ offset: 1, color: '#764ba2' },
+					]),
+					borderRadius: [4, 4, 0, 0],
+				},
+				emphasis: {
+					itemStyle: { color: '#667eea' },
+				},
+			},
+			{
+				name: '閿�椤圭◣棰�',
+				type: 'bar',
+				barWidth: '35%',
+				data: data.map(i => i.orderType === '閿�椤�' ? Number(i.taxAmount) || 0 : null),
+				itemStyle: {
+					color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+						{ offset: 0, color: '#f093fb' },
+						{ offset: 1, color: '#f5576c' },
+					]),
+					borderRadius: [4, 4, 0, 0],
+				},
+				emphasis: {
+					itemStyle: { color: '#f093fb' },
+				},
+			},
+		],
+	});
+};
+
+const handleMonthChange = () => {
+	page.current = 1;
+	loadVatDetail();
+};
+
+const handleSizeChange = (val) => {
+	page.size = val;
+	loadVatDetail();
+};
+
+const handleCurrentChange = (val) => {
+	page.current = val;
+	loadVatDetail();
+};
+
+const generateMonthOptions = () => {
+	const now = new Date();
+	const options = [];
+	for (let i = 11; i >= 0; i--) {
+		const d = new Date(now.getFullYear(), now.getMonth() - i, 1);
+		const y = d.getFullYear();
+		const m = String(d.getMonth() + 1).padStart(2, '0');
+		options.push(`${y}-${m}`);
+	}
+	monthOptions.value = options;
+};
+
+const handleResize = () => {
+	if (chartInstance) {
+		chartInstance.resize();
+	}
 };
 
 onMounted(() => {
-  getTableData();
+	generateMonthOptions();
+	loadVatDetail();
+	window.addEventListener('resize', handleResize);
+});
+
+onBeforeUnmount(() => {
+	window.removeEventListener('resize', handleResize);
+	if (chartInstance) {
+		chartInstance.dispose();
+	}
 });
 </script>
 
 <style lang="scss" scoped>
-.table_list {
-  margin-top: unset;
+.vat-page {
+	height: calc(100vh - 84px);
 }
-</style>
+
+.vat-row {
+	height: 100%;
+}
+
+.vat-col {
+	height: 100%;
+}
+
+.vat-card {
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	
+	:deep(.el-card__header) {
+		flex-shrink: 0;
+	}
+	:deep(.el-card__body) {
+		flex: 1;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+	}
+}
+
+.card-header {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+}
+
+.card-title {
+	font-size: 15px;
+	font-weight: 600;
+	color: #303133;
+}
+
+.table-wrapper {
+	flex: 1;
+	overflow: hidden;
+}
+
+.vat-pagination {
+	flex-shrink: 0;
+	margin-top: 12px;
+	justify-content: flex-end;
+}
+
+.tax-amount {
+	font-weight: 600;
+	font-family: 'Monaco', 'Menlo', monospace;
+	&.input-tax { color: #667eea; }
+	&.output-tax { color: #f5576c; }
+}
+
+.summary-row {
+	display: flex;
+	gap: 12px;
+	margin-bottom: 16px;
+	flex-shrink: 0;
+	
+	.summary-item {
+		flex: 1;
+		padding: 12px 14px;
+		border-radius: 8px;
+		text-align: center;
+		
+		&.input-bg {
+			background: linear-gradient(135deg, #f3f0ff 0%, #e8e5ff 100%);
+			.summary-value { color: #667eea; }
+		}
+		&.output-bg {
+			background: linear-gradient(135deg, #fff0f3 0%, #ffe0e6 100%);
+			.summary-value { color: #f5576c; }
+		}
+		&.diff-bg {
+			background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
+			.summary-value {
+				&.positive { color: #f5576c; }
+				&.negative { color: #22c55e; }
+			}
+		}
+		
+		.summary-label {
+			font-size: 12px;
+			color: #909399;
+			margin-bottom: 4px;
+		}
+		.summary-value {
+			font-size: 18px;
+			font-weight: 700;
+			font-family: 'Monaco', 'Menlo', monospace;
+		}
+	}
+}
+
+.chart-container {
+	flex: 1;
+	min-height: 0;
+}
+
+.empty-full {
+	flex: 1;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+}
+
+.chart-empty {
+	flex: 1;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	color: #c0c4cc;
+	font-size: 13px;
+}
+</style>
\ No newline at end of file

--
Gitblit v1.9.3