From e76403a881cbad2a4788c5a90e1318b1feb1b5ef Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期日, 04 一月 2026 18:01:00 +0800
Subject: [PATCH] Merge branch 'dev_tide_sbjkxt' into dev_tide_sbjkxt_xindao

---
 src/views/diagnosis/Modal/MaintainModal.vue                  |   58 
 src/views/diagnosis/Form/RepairForm.vue                      |  130 ++
 src/views/measurementEquipment/index.vue                     |  270 ++++
 src/views/calibration/index.vue                              |  246 +++
 src/views/diagnosis/Form/MaintainForm.vue                    |   66 +
 src/views/measurementEquipment/components/calibrationDia.vue |  268 ++++
 src/views/device/index.vue                                   |  322 +++++
 src/views/measurementEquipment/filesDia.vue                  |  202 +++
 src/views/diagnosis/index.vue                                |  327 +++++
 src/views/maintenance/Form/MaintenanceForm.vue               |   77 +
 src/utils/auth.js                                            |    6 
 src/views/maintenance/Form/PlanForm.vue                      |  137 ++
 src/views/device/Form.vue                                    |  239 +++
 /dev/null                                                    |  526 --------
 src/views/maintenance/index.vue                              |  315 +++++
 src/views/maintenance/Modal/MaintenanceModal.vue             |   60 
 src/views/diagnosis/Modal/RepairModal.vue                    |   77 +
 src/views/maintenance/Modal/PlanModal.vue                    |   76 +
 src/views/device/Modal.vue                                   |   69 +
 src/api/equipmentManagement/calibration.js                   |    9 
 src/views/measurementEquipment/components/formDia.vue        |  254 ++++
 21 files changed, 3,205 insertions(+), 529 deletions(-)

diff --git a/src/api/equipmentManagement/calibration.js b/src/api/equipmentManagement/calibration.js
index 54b99f9..472a4ef 100644
--- a/src/api/equipmentManagement/calibration.js
+++ b/src/api/equipmentManagement/calibration.js
@@ -24,4 +24,13 @@
     method: "post",
     data: query,
   });
+}
+
+// 鍒犻櫎璁板綍
+export function ledgerRecordDelete(ids) {
+    return request({
+        url: "/measuringInstrumentLedgerRecord/delete",
+        method: "delete",
+        data: ids,
+    });
 }
\ No newline at end of file
diff --git a/src/utils/auth.js b/src/utils/auth.js
index 423545f..9295d59 100644
--- a/src/utils/auth.js
+++ b/src/utils/auth.js
@@ -3,17 +3,17 @@
 const TokenKey = 'Admin-Token'
 
 export function getToken() {
-    // return Cookies.get(TokenKey)
+  // return Cookies.get(TokenKey)
     return sessionStorage.getItem(TokenKey)
 }
 
 export function setToken(token) {
-    // return Cookies.set(TokenKey, token)
+  // return Cookies.set(TokenKey, token)
     return sessionStorage.setItem(TokenKey, token)
 }
 
 export function removeToken() {
-    // return Cookies.remove(TokenKey)
+  // return Cookies.remove(TokenKey)
     return sessionStorage.removeItem(TokenKey)
 
 }
diff --git a/src/views/calibration/index.vue b/src/views/calibration/index.vue
new file mode 100644
index 0000000..12d5556
--- /dev/null
+++ b/src/views/calibration/index.vue
@@ -0,0 +1,246 @@
+<template>
+	<div class="app-container">
+		<div class="search_form">
+			<div>
+				<span class="search_title">妫�瀹氭棩鏈燂細</span>
+				<el-date-picker
+					v-model="searchForm.recordDate"
+					value-format="YYYY-MM-DD"
+					format="YYYY-MM-DD"
+					type="date"
+					placeholder="璇烽�夋嫨"
+					clearable
+					style="width: 160px"
+					@change="handleQuery"
+				/>
+				<span class="search_title ml10">褰曞叆鏃ユ湡锛�</span>
+				<el-date-picker
+					v-model="searchForm.entryDate"
+					value-format="YYYY-MM-DD"
+					format="YYYY-MM-DD"
+					type="date"
+					placeholder="璇烽�夋嫨"
+					clearable
+					style="width: 160px"
+					@change="handleQuery"
+				/>
+				<span class="search_title ml10">璁¢噺鍣ㄥ叿缂栧彿锛�</span>
+				<el-input v-model="searchForm.code" placeholder="璇疯緭鍏ョ紪鍙�" clearable style="width: 240px" @change="handleQuery"/>
+<!--				<span class="search_title ml10">鐘舵�侊細</span>-->
+<!--				<el-select v-model="searchForm.status" placeholder="璇烽�夋嫨鐘舵��" @change="handleQuery" style="width: 160px" clearable>-->
+<!--					<el-option label="鏈夋晥" :value="1"></el-option>-->
+<!--					<el-option label="閫炬湡" :value="2"></el-option>-->
+<!--				</el-select>-->
+				<el-button type="primary" @click="handleQuery" style="margin-left: 10px"
+				>鎼滅储</el-button
+				>
+			</div>
+			<div>
+				<el-button @click="handleOut">瀵煎嚭</el-button>
+			</div>
+		</div>
+		<div class="table_list">
+			<PIMTable
+				rowKey="id"
+				:column="tableColumn"
+				:tableData="tableData"
+				:page="page"
+				:isSelection="true"
+				@selection-change="handleSelectionChange"
+				:tableLoading="tableLoading"
+				@pagination="pagination"
+			></PIMTable>
+		</div>
+		<calibration-dia ref="calibrationDia" @close="handleQuery"></calibration-dia>
+	</div>
+</template>
+
+<script setup>
+import {onMounted, ref} from "vue";
+import {ElMessageBox, ElMessage} from "element-plus";
+import useUserStore from "../../store/modules/user.js";
+import CalibrationDia from "../../views/measurementEquipment/components/calibrationDia.vue";
+import {ledgerRecordListPage, ledgerRecordDelete} from "../../api/equipmentManagement/calibration.js";
+const { proxy } = getCurrentInstance();
+const userStore = useUserStore()
+
+const data = reactive({
+	searchForm: {
+		recordDate: "",
+		code: "",
+		entryDate: "",
+	},
+});
+const { searchForm } = toRefs(data);
+
+const tableColumn = ref([
+	// {
+	// 	label: "鐘舵��",
+	// 	prop: "status",
+	// 	dataType: "tag",
+	// 	formatData: (params) => {
+	// 		if (params == 1) {
+	// 			return "鏈夋晥";
+	// 		} else if (params == 2) {
+	// 			return "閫炬湡";
+	// 		} else {
+	// 			return null;
+	// 		}
+	// 	},
+	// 	formatType: (params) => {
+	// 		if (params == 1) {
+	// 			return "success";
+	// 		} else if (params == 2) {
+	// 			return "danger";
+	// 		} else {
+	// 			return null;
+	// 		}
+	// 	},
+	// },
+	{
+		label: "妫�瀹氭棩鏈�",
+		prop: "recordDate",
+		width: 130,
+	},
+	{
+		label: "璁¢噺鍣ㄥ叿缂栧彿",
+		prop: "code",
+		width: 150,
+	},
+	{
+		label: "璁¢噺鍣ㄥ叿鍚嶇О",
+		prop: "name",
+		width: 200,
+	},
+	{
+		label: "瑙勬牸鍨嬪彿",
+		prop: "model",
+		width:200
+	},
+	{
+		label: "鏈夋晥鏈�",
+		prop: "valid",
+		width: 100,
+	},
+	{
+		label: "褰曞叆浜�",
+		prop: "userName",
+	},
+	{
+		label: "褰曞叆鏃ユ湡",
+		prop: "entryDate",
+		width: 130,
+	},
+	{
+		dataType: "action",
+		label: "鎿嶄綔",
+		width: 100,
+		align: "center",
+		fixed: 'right',
+		operation: [
+			{
+				name: "缂栬緫",
+				type: "text",
+				clickFun: (row) => {
+					openCalibrationDia("edit", row);
+				},
+			},
+			{
+				name: "鍒犻櫎",
+				type: "text",
+				style: {
+					color: "#F56C6C"
+				},
+				clickFun: (row) => {
+					handleDelete(row);
+				},
+			},
+		],
+	},
+]);
+const tableData = ref([]);
+const tableLoading = ref(false);
+const page = reactive({
+	current: 1,
+	size: 100,
+	total: 0,
+});
+const selectedRows = ref([]);
+
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+	selectedRows.value = selection;
+};
+const calibrationDia = ref()
+
+// 鏌ヨ鍒楄〃
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+	page.current = 1;
+	getList();
+};
+const pagination = (obj) => {
+	page.current = obj.page;
+	page.size = obj.limit;
+	getList();
+};
+const getList = () => {
+	tableLoading.value = true;
+	ledgerRecordListPage({ ...searchForm.value, ...page }).then((res) => {
+		tableLoading.value = false;
+		tableData.value = res.data.records;
+		page.total = res.data.total;
+	}).catch((err) => {
+		tableLoading.value = false;
+	})
+};
+
+// 鎵撳紑妫�瀹氭牎鍑嗗脊妗�
+const openCalibrationDia = (type, row) => {
+	nextTick(() => {
+		calibrationDia.value?.openDialog(type, row)
+	})
+}
+
+// 鍒犻櫎璁板綍
+const handleDelete = (row) => {
+	ElMessageBox.confirm(`纭鍒犻櫎璁¢噺鍣ㄥ叿缂栧彿涓�"${row.code}"鐨勬瀹氳褰曞悧锛焋, "鍒犻櫎纭", {
+		confirmButtonText: "纭",
+		cancelButtonText: "鍙栨秷",
+		type: "warning",
+	})
+		.then(() => {
+			ledgerRecordDelete([row.id]).then(() => {
+				ElMessage.success("鍒犻櫎鎴愬姛");
+				getList();
+			}).catch(() => {
+				ElMessage.error("鍒犻櫎澶辫触");
+			});
+		})
+		.catch(() => {
+			proxy.$modal.msg("宸插彇娑堝垹闄�");
+		});
+};
+
+// 瀵煎嚭
+const handleOut = () => {
+	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+		confirmButtonText: "纭",
+		cancelButtonText: "鍙栨秷",
+		type: "warning",
+	})
+		.then(() => {
+			proxy.download("/measuringInstrumentLedgerRecord/export", {}, "妫�瀹氭牎鍑嗚褰�.xlsx");
+		})
+		.catch(() => {
+			proxy.$modal.msg("宸插彇娑�");
+		});
+};
+onMounted(() => {
+	getList();
+});
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file
diff --git a/src/views/device/DeviceManagement.vue b/src/views/device/DeviceManagement.vue
deleted file mode 100644
index 80ccd27..0000000
--- a/src/views/device/DeviceManagement.vue
+++ /dev/null
@@ -1,513 +0,0 @@
-<template>
-  <div class="device-management-container">
-    <el-card shadow="hover">
-      <template #header>
-        <div class="card-header">
-          <span>璁惧绠$悊</span>
-          <div class="header-buttons">
-            <el-button type="primary" @click="showAddDeviceDialog">
-              <el-icon-plus /> 娣诲姞璁惧
-            </el-button>
-            <el-button @click="exportDevices">
-              <el-icon-download /> 瀵煎嚭
-            </el-button>
-            <el-button @click="showImportDialog">
-              <el-icon-upload /> 瀵煎叆
-            </el-button>
-          </div>
-        </div>
-      </template>
-
-      <!-- 绛涢�夋潯浠� -->
-      <el-form :inline="true" :model="filterForm" class="device-filter-form">
-        <el-form-item label="璁惧鍚嶇О">
-          <el-input v-model="filterForm.name" placeholder="璇疯緭鍏ヨ澶囧悕绉�" clearable></el-input>
-        </el-form-item>
-        <el-form-item label="鍨嬪彿">
-          <el-input v-model="filterForm.model" placeholder="璇疯緭鍏ュ瀷鍙�" clearable></el-input>
-        </el-form-item>
-        <el-form-item label="鐘舵��">
-          <el-select v-model="filterForm.status" placeholder="璇烽�夋嫨鐘舵��" clearable>
-            <el-option label="鍦ㄧ嚎" value="online"></el-option>
-            <el-option label="绂荤嚎" value="offline"></el-option>
-            <el-option label="鏁呴殰" value="fault"></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" @click="handleFilter">鏌ヨ</el-button>
-          <el-button @click="resetFilter">閲嶇疆</el-button>
-        </el-form-item>
-      </el-form>
-
-      <!-- 璁惧鍒楄〃 -->
-      <el-table :data="filteredDevices" stripe style="width: 100%" @selection-change="handleSelectionChange">
-        <el-table-column type="selection" width="55"></el-table-column>
-        <el-table-column prop="id" label="璁惧ID" width="100"></el-table-column>
-        <el-table-column prop="name" label="璁惧鍚嶇О" width="180"></el-table-column>
-        <el-table-column prop="model" label="鍨嬪彿" width="120"></el-table-column>
-        <el-table-column prop="ip" label="IP鍦板潃" width="150"></el-table-column>
-        <el-table-column prop="status" label="鐘舵��" width="100">
-          <template #default="scope">
-            <el-tag :type="scope.row.status === 'online' ? 'success' : scope.row.status === 'offline' ? 'info' : 'danger'">
-              {{ scope.row.status === 'online' ? '鍦ㄧ嚎' : scope.row.status === 'offline' ? '绂荤嚎' : '鏁呴殰' }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="location" label="瀹夎浣嶇疆" width="180"></el-table-column>
-        <el-table-column prop="installDate" label="瀹夎鏃ユ湡" width="150"></el-table-column>
-        <el-table-column prop="manufacturer" label="鍒堕�犲晢" width="150"></el-table-column>
-        <el-table-column label="鎿嶄綔" width="220" fixed="right">
-          <template #default="scope">
-            <el-button type="text" size="small" @click="showDeviceDetail(scope.row)">
-              璇︽儏
-            </el-button>
-            <el-button type="text" size="small" @click="showEditDeviceDialog(scope.row)">
-              缂栬緫
-            </el-button>
-            <el-button type="text" size="small" @click="handleDelete(scope.row)">
-              鍒犻櫎
-            </el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <!-- 鍒嗛〉 -->
-      <div class="pagination-container">
-        <el-pagination
-          background
-          layout="total, sizes, prev, pager, next, jumper"
-          :total="filteredDevices.length"
-          :current-page="currentPage"
-          :page-sizes="[10, 20, 50, 100]"
-          :page-size="pageSize"
-          @size-change="handleSizeChange"
-          @current-change="handleCurrentChange"
-        ></el-pagination>
-      </div>
-    </el-card>
-
-    <!-- 娣诲姞璁惧瀵硅瘽妗� -->
-    <el-dialog v-model="addDeviceDialogVisible" title="娣诲姞璁惧" width="600px">
-      <el-form :model="deviceForm" :rules="deviceRules" ref="deviceFormRef" label-width="100px">
-        <el-form-item label="璁惧鍚嶇О" prop="name">
-          <el-input v-model="deviceForm.name" placeholder="璇疯緭鍏ヨ澶囧悕绉�"></el-input>
-        </el-form-item>
-        <el-form-item label="鍨嬪彿" prop="model">
-          <el-input v-model="deviceForm.model" placeholder="璇疯緭鍏ュ瀷鍙�"></el-input>
-        </el-form-item>
-        <el-form-item label="IP鍦板潃" prop="ip">
-          <el-input v-model="deviceForm.ip" placeholder="璇疯緭鍏P鍦板潃"></el-input>
-        </el-form-item>
-        <el-form-item label="瀹夎浣嶇疆" prop="location">
-          <el-input v-model="deviceForm.location" placeholder="璇疯緭鍏ュ畨瑁呬綅缃�"></el-input>
-        </el-form-item>
-        <el-form-item label="鍒堕�犲晢" prop="manufacturer">
-          <el-input v-model="deviceForm.manufacturer" placeholder="璇疯緭鍏ュ埗閫犲晢"></el-input>
-        </el-form-item>
-        <el-form-item label="瀹夎鏃ユ湡" prop="installDate">
-          <el-date-picker v-model="deviceForm.installDate" type="date" placeholder="閫夋嫨瀹夎鏃ユ湡" style="width: 100%"></el-date-picker>
-        </el-form-item>
-        <el-form-item label="鐘舵��" prop="status">
-          <el-select v-model="deviceForm.status" placeholder="璇烽�夋嫨鐘舵��">
-            <el-option label="鍦ㄧ嚎" value="online"></el-option>
-            <el-option label="绂荤嚎" value="offline"></el-option>
-            <el-option label="鏁呴殰" value="fault"></el-option>
-          </el-select>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="addDeviceDialogVisible = false">鍙栨秷</el-button>
-          <el-button type="primary" @click="handleAddDevice">纭畾</el-button>
-        </span>
-      </template>
-    </el-dialog>
-
-    <!-- 缂栬緫璁惧瀵硅瘽妗� -->
-    <el-dialog v-model="editDeviceDialogVisible" title="缂栬緫璁惧" width="600px">
-      <el-form :model="deviceForm" :rules="deviceRules" ref="deviceFormRef" label-width="100px">
-        <el-form-item label="璁惧鍚嶇О" prop="name">
-          <el-input v-model="deviceForm.name" placeholder="璇疯緭鍏ヨ澶囧悕绉�"></el-input>
-        </el-form-item>
-        <el-form-item label="鍨嬪彿" prop="model">
-          <el-input v-model="deviceForm.model" placeholder="璇疯緭鍏ュ瀷鍙�"></el-input>
-        </el-form-item>
-        <el-form-item label="IP鍦板潃" prop="ip">
-          <el-input v-model="deviceForm.ip" placeholder="璇疯緭鍏P鍦板潃"></el-input>
-        </el-form-item>
-        <el-form-item label="瀹夎浣嶇疆" prop="location">
-          <el-input v-model="deviceForm.location" placeholder="璇疯緭鍏ュ畨瑁呬綅缃�"></el-input>
-        </el-form-item>
-        <el-form-item label="鍒堕�犲晢" prop="manufacturer">
-          <el-input v-model="deviceForm.manufacturer" placeholder="璇疯緭鍏ュ埗閫犲晢"></el-input>
-        </el-form-item>
-        <el-form-item label="瀹夎鏃ユ湡" prop="installDate">
-          <el-date-picker v-model="deviceForm.installDate" type="date" placeholder="閫夋嫨瀹夎鏃ユ湡" style="width: 100%"></el-date-picker>
-        </el-form-item>
-        <el-form-item label="鐘舵��" prop="status">
-          <el-select v-model="deviceForm.status" placeholder="璇烽�夋嫨鐘舵��">
-            <el-option label="鍦ㄧ嚎" value="online"></el-option>
-            <el-option label="绂荤嚎" value="offline"></el-option>
-            <el-option label="鏁呴殰" value="fault"></el-option>
-          </el-select>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="editDeviceDialogVisible = false">鍙栨秷</el-button>
-          <el-button type="primary" @click="handleEditDevice">纭畾</el-button>
-        </span>
-      </template>
-    </el-dialog>
-
-    <!-- 璁惧璇︽儏瀵硅瘽妗� -->
-    <el-dialog v-model="deviceDetailDialogVisible" title="璁惧璇︽儏" width="600px">
-      <el-descriptions :column="1" border>
-        <el-descriptions-item label="璁惧鍚嶇О">{{ selectedDevice.name }}</el-descriptions-item>
-        <el-descriptions-item label="璁惧ID">{{ selectedDevice.id }}</el-descriptions-item>
-        <el-descriptions-item label="鍨嬪彿">{{ selectedDevice.model }}</el-descriptions-item>
-        <el-descriptions-item label="IP鍦板潃">{{ selectedDevice.ip }}</el-descriptions-item>
-        <el-descriptions-item label="鐘舵��">
-          <el-tag :type="selectedDevice.status === 'online' ? 'success' : selectedDevice.status === 'offline' ? 'info' : 'danger'">
-            {{ selectedDevice.status === 'online' ? '鍦ㄧ嚎' : selectedDevice.status === 'offline' ? '绂荤嚎' : '鏁呴殰' }}
-          </el-tag>
-        </el-descriptions-item>
-        <el-descriptions-item label="瀹夎浣嶇疆">{{ selectedDevice.location }}</el-descriptions-item>
-        <el-descriptions-item label="鍒堕�犲晢">{{ selectedDevice.manufacturer }}</el-descriptions-item>
-        <el-descriptions-item label="瀹夎鏃ユ湡">{{ selectedDevice.installDate }}</el-descriptions-item>
-        <el-descriptions-item label="鍒涘缓鏃堕棿">{{ selectedDevice.createTime }}</el-descriptions-item>
-      </el-descriptions>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="deviceDetailDialogVisible = false">鍏抽棴</el-button>
-        </span>
-      </template>
-    </el-dialog>
-
-    <!-- 瀵煎叆瀵硅瘽妗� -->
-    <el-dialog v-model="importDialogVisible" title="瀵煎叆璁惧" width="400px">
-      <el-upload
-        class="upload-demo"
-        action="#"
-        :on-change="handleFileChange"
-        :auto-upload="false"
-        accept=".xlsx,.xls"
-      >
-        <el-button type="primary">閫夋嫨鏂囦欢</el-button>
-        <template #tip>
-          <div class="el-upload__tip">
-            鍙兘涓婁紶 xlsx/xls 鏂囦欢锛屼笖涓嶈秴杩� 2MB
-          </div>
-        </template>
-      </el-upload>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="importDialogVisible = false">鍙栨秷</el-button>
-          <el-button type="primary" @click="handleImport">纭畾</el-button>
-        </span>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-import { ref, computed } from 'vue'
-
-// 璁惧鍒楄〃鏁版嵁
-const devices = ref([
-  {
-    id: 'D001',
-    name: '绌哄帇鏈篈-001',
-    model: 'KA-200',
-    ip: '192.168.1.101',
-    status: 'online',
-    location: '杞﹂棿A-1鍖�',
-    manufacturer: '搴锋櫘鏂�',
-    installDate: '2023-05-10',
-    createTime: '2023-05-10 10:30:00'
-  },
-  {
-    id: 'D002',
-    name: '鍐峰嵈濉擝-002',
-    model: 'CT-300',
-    ip: '192.168.1.102',
-    status: 'warning',
-    location: '杞﹂棿B-2鍖�',
-    manufacturer: '鑹満',
-    installDate: '2023-06-15',
-    createTime: '2023-06-15 14:20:00'
-  },
-  {
-    id: 'D003',
-    name: '姘存车C-003',
-    model: 'WP-150',
-    ip: '192.168.1.103',
-    status: 'online',
-    location: '杞﹂棿C-3鍖�',
-    manufacturer: '鏍煎叞瀵�',
-    installDate: '2023-07-20',
-    createTime: '2023-07-20 09:15:00'
-  },
-  {
-    id: 'D004',
-    name: '鍙戠數鏈篋-004',
-    model: 'GE-500',
-    ip: '192.168.1.104',
-    status: 'fault',
-    location: '鏈烘埧',
-    manufacturer: '鍗$壒褰煎嫆',
-    installDate: '2023-08-25',
-    createTime: '2023-08-25 16:45:00'
-  },
-  {
-    id: 'D005',
-    name: '鍙樺帇鍣‥-005',
-    model: 'TR-1000',
-    ip: '192.168.1.105',
-    status: 'online',
-    location: '閰嶇數鎴�',
-    manufacturer: 'ABB',
-    installDate: '2023-09-30',
-    createTime: '2023-09-30 11:20:00'
-  }
-])
-
-// 绛涢�夎〃鍗�
-const filterForm = ref({
-  name: '',
-  model: '',
-  status: ''
-})
-
-// 鍒嗛〉鏁版嵁
-const currentPage = ref(1)
-const pageSize = ref(10)
-
-// 瀵硅瘽妗嗙姸鎬�
-const addDeviceDialogVisible = ref(false)
-const editDeviceDialogVisible = ref(false)
-const deviceDetailDialogVisible = ref(false)
-const importDialogVisible = ref(false)
-
-// 璁惧琛ㄥ崟鏁版嵁
-const deviceForm = ref({
-  id: '',
-  name: '',
-  model: '',
-  ip: '',
-  status: 'online',
-  location: '',
-  manufacturer: '',
-  installDate: ''
-})
-
-// 琛ㄥ崟楠岃瘉瑙勫垯
-const deviceRules = ref({
-  name: [{ required: true, message: '璇疯緭鍏ヨ澶囧悕绉�', trigger: 'blur' }],
-  model: [{ required: true, message: '璇疯緭鍏ュ瀷鍙�', trigger: 'blur' }],
-  ip: [{ required: true, message: '璇疯緭鍏P鍦板潃', trigger: 'blur' }],
-  location: [{ required: true, message: '璇疯緭鍏ュ畨瑁呬綅缃�', trigger: 'blur' }],
-  manufacturer: [{ required: true, message: '璇疯緭鍏ュ埗閫犲晢', trigger: 'blur' }],
-  installDate: [{ required: true, message: '璇烽�夋嫨瀹夎鏃ユ湡', trigger: 'change' }],
-  status: [{ required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change' }]
-})
-
-// 琛ㄥ崟寮曠敤
-const deviceFormRef = ref(null)
-
-// 閫変腑鐨勮澶�
-const selectedDevice = ref({})
-
-// 閫変腑鐨勮澶囧垪琛紙鐢ㄤ簬鎵归噺鎿嶄綔锛�
-const selectedDevices = ref([])
-
-// 瀵煎叆鐨勬枃浠�
-const importFile = ref(null)
-
-// 杩囨护鍚庣殑璁惧鍒楄〃
-const filteredDevices = computed(() => {
-  let result = [...devices.value]
-
-  // 鎸夊悕绉扮瓫閫�
-  if (filterForm.value.name) {
-    result = result.filter(device => device.name.includes(filterForm.value.name))
-  }
-
-  // 鎸夊瀷鍙风瓫閫�
-  if (filterForm.value.model) {
-    result = result.filter(device => device.model.includes(filterForm.value.model))
-  }
-
-  // 鎸夌姸鎬佺瓫閫�
-  if (filterForm.value.status) {
-    result = result.filter(device => device.status === filterForm.value.status)
-  }
-
-  return result
-})
-
-// 鏄剧ず娣诲姞璁惧瀵硅瘽妗�
-const showAddDeviceDialog = () => {
-  // 閲嶇疆琛ㄥ崟
-  deviceForm.value = {
-    id: '',
-    name: '',
-    model: '',
-    ip: '',
-    status: 'online',
-    location: '',
-    manufacturer: '',
-    installDate: ''
-  }
-  addDeviceDialogVisible.value = true
-}
-
-// 鏄剧ず缂栬緫璁惧瀵硅瘽妗�
-const showEditDeviceDialog = (device) => {
-  deviceForm.value = { ...device }
-  editDeviceDialogVisible.value = true
-}
-
-// 鏄剧ず璁惧璇︽儏
-const showDeviceDetail = (device) => {
-  selectedDevice.value = { ...device }
-  deviceDetailDialogVisible.value = true
-}
-
-// 鏄剧ず瀵煎叆瀵硅瘽妗�
-const showImportDialog = () => {
-  importDialogVisible.value = true
-}
-
-// 澶勭悊娣诲姞璁惧
-const handleAddDevice = () => {
-  // 妯℃嫙娣诲姞璁惧
-  const newDevice = {
-    ...deviceForm.value,
-    id: `D${String(devices.value.length + 1).padStart(3, '0')}`,
-    createTime: new Date().toLocaleString()
-  }
-  devices.value.push(newDevice)
-  addDeviceDialogVisible.value = false
-  ElMessage.success('璁惧娣诲姞鎴愬姛')
-}
-
-// 澶勭悊缂栬緫璁惧
-const handleEditDevice = () => {
-  // 妯℃嫙缂栬緫璁惧
-  const index = devices.value.findIndex(device => device.id === deviceForm.value.id)
-  if (index !== -1) {
-    devices.value[index] = { ...deviceForm.value }
-    editDeviceDialogVisible.value = false
-    ElMessage.success('璁惧缂栬緫鎴愬姛')
-  }
-}
-
-// 澶勭悊鍒犻櫎璁惧
-const handleDelete = (device) => {
-  ElMessageBox.confirm('纭畾瑕佸垹闄よ璁惧鍚楋紵', '鎻愮ず', {
-    confirmButtonText: '纭畾',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning'
-  }).then(() => {
-    // 妯℃嫙鍒犻櫎璁惧
-    const index = devices.value.findIndex(item => item.id === device.id)
-    if (index !== -1) {
-      devices.value.splice(index, 1)
-      ElMessage.success('璁惧鍒犻櫎鎴愬姛')
-    }
-  }).catch(() => {
-    // 鍙栨秷鍒犻櫎
-  })
-}
-
-// 澶勭悊绛涢��
-const handleFilter = () => {
-  // 绛涢�夐�昏緫宸茬粡鍦╟omputed涓疄鐜�
-}
-
-// 閲嶇疆绛涢�夋潯浠�
-const resetFilter = () => {
-  filterForm.value = {
-    name: '',
-    model: '',
-    status: ''
-  }
-}
-
-// 澶勭悊鍒嗛〉澶у皬鍙樺寲
-const handleSizeChange = (size) => {
-  pageSize.value = size
-  currentPage.value = 1
-}
-
-// 澶勭悊褰撳墠椤靛彉鍖�
-const handleCurrentChange = (current) => {
-  currentPage.value = current
-}
-
-// 澶勭悊鏂囦欢鍙樺寲锛堝鍏ワ級
-const handleFileChange = (file) => {
-  importFile.value = file
-}
-
-// 澶勭悊瀵煎叆
-const handleImport = () => {
-  // 妯℃嫙瀵煎叆
-  if (importFile.value) {
-    importDialogVisible.value = false
-    ElMessage.success('璁惧瀵煎叆鎴愬姛')
-    importFile.value = null
-  } else {
-    ElMessage.warning('璇烽�夋嫨瑕佸鍏ョ殑鏂囦欢')
-  }
-}
-
-// 瀵煎嚭璁惧
-const exportDevices = () => {
-  // 妯℃嫙瀵煎嚭
-  ElMessage.success('璁惧瀵煎嚭鎴愬姛')
-}
-
-// 澶勭悊閫夋嫨鍙樺寲锛堢敤浜庢壒閲忔搷浣滐級
-const handleSelectionChange = (selection) => {
-  selectedDevices.value = selection
-}
-</script>
-
-<style scoped>
-.device-management-container {
-  padding: 20px;
-  background-color: #f5f7fa;
-  min-height: 100vh;
-}
-
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-.header-buttons {
-  display: flex;
-  gap: 10px;
-}
-
-.device-filter-form {
-  margin-bottom: 20px;
-  padding: 10px 0;
-  border-bottom: 1px solid #ebeef5;
-}
-
-.pagination-container {
-  display: flex;
-  justify-content: flex-end;
-  margin-top: 20px;
-}
-
-:deep(.el-icon-plus),
-:deep(.el-icon-download),
-:deep(.el-icon-upload) {
-  margin-right: 5px;
-}
-</style>
diff --git a/src/views/device/Form.vue b/src/views/device/Form.vue
new file mode 100644
index 0000000..2cc26c5
--- /dev/null
+++ b/src/views/device/Form.vue
@@ -0,0 +1,239 @@
+<template>
+  <el-form :model="form" label-width="100px" :rules="formRules" ref="formRef">
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="璁惧鍚嶇О" prop="deviceName">
+          <el-input v-model="form.deviceName" placeholder="璇疯緭鍏ヨ澶囧悕绉�" />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="瑙勬牸鍨嬪彿" prop="deviceModel">
+          <el-input v-model="form.deviceModel" placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�" />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="璁惧鍝佺墝" prop="deviceBrand">
+          <el-input v-model="form.deviceBrand" placeholder="璇疯緭鍏ヨ澶囧搧鐗�" />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="渚涘簲鍟�" prop="supplierName">
+          <el-input v-model="form.supplierName" placeholder="璇疯緭鍏ヤ緵搴斿晢" />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="瀛樻斁浣嶇疆" prop="storageLocation">
+          <el-input v-model="form.storageLocation" placeholder="璇疯緭鍏ュ瓨鏀句綅缃�" />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="鍗曚綅" prop="unit">
+          <el-input v-model="form.unit" placeholder="璇疯緭鍏ュ崟浣�" />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="鍚敤鎶樻棫" prop="enableDepreciation">
+          <el-switch v-model="form.enableDepreciation" :active-value="true" :inactive-value="false" />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="鏁伴噺" prop="number">
+          <el-input-number :min="1" style="width: 100%"
+            v-model="form.number"
+													 disabled
+            placeholder="璇疯緭鍏ユ暟閲�"
+            @change="mathNum"
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="鍚◣鍗曚环" prop="taxIncludingPriceUnit">
+          <el-input-number :step="0.01" :min="0" style="width: 100%"
+            v-model="form.taxIncludingPriceUnit"
+            placeholder="璇疯緭鍏ュ惈绋庡崟浠�"
+            maxlength="10"
+            @change="mathNum"
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="鍚◣鎬讳环" prop="taxIncludingPriceTotal">
+          <el-input
+            v-model="form.taxIncludingPriceTotal"
+            placeholder="鑷姩鐢熸垚"
+            type="number"
+            disabled
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="绋庣巼(%)" prop="taxRate">
+          <!-- <el-input
+            v-model="form.taxRate"
+            placeholder="璇疯緭鍏ョ◣鐜�"
+            type="number"
+          >
+            <template #append> % </template>
+          </el-input> -->
+          <el-select
+            v-model="form.taxRate"
+            placeholder="璇烽�夋嫨"
+            clearable
+            @change="mathNum"
+          >
+            <el-option label="1" :value="1" />
+            <el-option label="6" :value="6" />
+            <el-option label="13" :value="13" />
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="涓嶅惈绋庢�讳环" prop="unTaxIncludingPriceTotal">
+          <el-input
+            v-model="form.unTaxIncludingPriceTotal"
+            placeholder="鑷姩鐢熸垚"
+            type="number"
+            disabled
+          />
+        </el-form-item>
+      </el-col>
+      <!-- <el-col :span="12">
+        <el-form-item label="褰曞叆浜�" prop="createUser">
+          <el-input v-model="form.createUser" placeholder="璇疯緭鍏ュ綍鍏ヤ汉" />
+        </el-form-item>
+      </el-col> -->
+      <el-col :span="12">
+        <el-form-item label="褰曞叆鏃ユ湡" prop="createTime">
+          <el-date-picker
+            style="width: 100%"
+            v-model="form.createTime"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            type="date"
+            placeholder="璇烽�夋嫨褰曞叆鏃ユ湡"
+            clearable
+          />
+        </el-form-item>
+      </el-col>
+			<el-col :span="12">
+				<el-form-item label="棰勮杩愯鏃堕棿" prop="planRuntimeTime">
+					<el-date-picker
+						style="width: 100%"
+						v-model="form.planRuntimeTime"
+						format="YYYY-MM-DD"
+						value-format="YYYY-MM-DD"
+						type="date"
+						placeholder="璇烽�夋嫨褰曞叆鏃ユ湡"
+						clearable
+					/>
+				</el-form-item>
+			</el-col>
+    </el-row>
+  </el-form>
+</template>
+
+<script setup>
+import useFormData from "../../hooks/useFormData";
+// import useUserStore from "@/store/modules/user";
+import { getLedgerById } from "../../api/equipmentManagement/ledger";
+import dayjs from "dayjs";
+import {
+  calculateTaxIncludeTotalPrice,
+  calculateTaxExclusiveTotalPrice,
+} from "../../utils/summarizeTable";
+import { ElMessage } from "element-plus";
+import {ref} from "vue";
+
+defineOptions({
+  name: "璁惧鍙拌处琛ㄥ崟",
+});
+const formRef = ref(null);
+const operationType = ref('');
+const formRules = {
+	deviceName: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+	deviceModel: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+	supplierName: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+	unit: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+	number: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+	taxIncludingPriceUnit: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
+	taxRate: [{ required: true, trigger: "change", message: "璇疯緭鍏�" }],
+	planRuntimeTime: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }],
+}
+
+const { form, resetForm } = useFormData({
+  deviceName: undefined, // 璁惧鍚嶇О
+  deviceModel: undefined, // 瑙勬牸鍨嬪彿
+  deviceBrand: undefined, // 璁惧鍝佺墝
+  supplierName: undefined, // 渚涘簲鍟�
+  storageLocation: undefined, // 瀛樻斁浣嶇疆
+  enableDepreciation: false, // 鏄惁鍚敤鎶樻棫
+  unit: undefined, // 鍗曚綅
+  number: 1, // 鏁伴噺
+  taxIncludingPriceUnit: undefined, // 鍚◣鍗曚环
+  taxIncludingPriceTotal: undefined, // 鍚◣鎬讳环
+  taxRate: undefined, // 绋庣巼
+  unTaxIncludingPriceTotal: undefined, // 涓嶅惈绋庢�讳环
+  // createUser: useUserStore().nickName, // 褰曞叆浜�
+  createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // 褰曞叆鏃ユ湡
+	planRuntimeTime: dayjs().format("YYYY-MM-DD"), // 褰曞叆鏃ユ湡
+});
+
+const loadForm = async (id) => {
+	if (id) {
+		operationType.value = 'edit'
+	}
+  const { code, data } = await getLedgerById(id);
+  if (code == 200) {
+    form.deviceName = data.deviceName;
+    form.deviceModel = data.deviceModel;
+    form.deviceBrand = data.deviceBrand;
+    form.supplierName = data.supplierName;
+    form.storageLocation = data.storageLocation;
+    form.enableDepreciation = data.enableDepreciation;
+    form.unit = data.unit;
+    form.number = 1;
+    form.taxIncludingPriceUnit = data.taxIncludingPriceUnit;
+    form.taxIncludingPriceTotal = data.taxIncludingPriceTotal;
+    form.taxRate = data.taxRate;
+    form.unTaxIncludingPriceTotal = data.unTaxIncludingPriceTotal;
+    form.createTime = data.createTime;
+  }
+};
+
+const mathNum = () => {
+  if (!form.taxIncludingPriceUnit) {
+    ElMessage.error("璇疯緭鍏ュ崟浠�");
+    return;
+  }
+  form.taxIncludingPriceTotal = calculateTaxIncludeTotalPrice(
+    form.taxIncludingPriceUnit,
+    form.number
+  );
+  if (form.taxRate) {
+    form.unTaxIncludingPriceTotal = calculateTaxExclusiveTotalPrice(
+      form.taxIncludingPriceTotal,
+      form.taxRate
+    );
+  }
+};
+
+// 娓呴櫎琛ㄥ崟鏍¢獙鐘舵��
+const clearValidate = () => {
+  formRef.value?.clearValidate();
+};
+
+// 閲嶇疆琛ㄥ崟鏁版嵁鍜屾牎楠岀姸鎬�
+const resetFormAndValidate = () => {
+  resetForm();
+  clearValidate();
+};
+
+defineExpose({
+  form,
+  loadForm,
+  resetForm,
+  clearValidate,
+  resetFormAndValidate,
+	formRef,
+});
+</script>
diff --git a/src/views/device/Modal.vue b/src/views/device/Modal.vue
new file mode 100644
index 0000000..3824b86
--- /dev/null
+++ b/src/views/device/Modal.vue
@@ -0,0 +1,69 @@
+<template>
+  <el-dialog :title="modalOptions.title" v-model="visible" @close="close">
+    <Form ref="formRef"></Form>
+    <template #footer>
+			<el-button type="primary" @click="sendForm" :loading="loading">
+				{{ modalOptions.confirmText }}
+			</el-button>
+      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useModal } from "../../hooks/useModal";
+import { addLedger, editLedger } from "../../api/equipmentManagement/ledger";
+import Form from "./Form.vue";
+import { ElMessage } from "element-plus";
+const { proxy } = getCurrentInstance()
+
+defineOptions({
+  name: "璁惧鍙拌处鏂板缂栬緫",
+});
+
+const emits = defineEmits(["success"]);
+
+const formRef = ref();
+const {
+  id,
+  visible,
+  loading,
+  openModal,
+  modalOptions,
+  handleConfirm,
+  closeModal,
+} = useModal({ title: "璁惧鍙拌处" });
+
+const sendForm = () => {
+	proxy.$refs.formRef.$refs.formRef.validate(async valid => {
+		if (valid) {
+			const {code} = id.value
+				? await editLedger({id: id.value, ...formRef.value.form})
+				: await addLedger(formRef.value.form);
+			if (code == 200) {
+				emits("success");
+				ElMessage({message: "鎿嶄綔鎴愬姛", type: "success"});
+				close();
+			} else {
+				loading.value = false;
+			}
+		}
+	})
+};
+
+const close = () => {
+	formRef.value.resetFormAndValidate();
+  closeModal();
+};
+
+const loadForm = async (id) => {
+  openModal(id);
+  await nextTick();
+  formRef.value.loadForm(id);
+};
+
+defineExpose({
+  openModal,
+  loadForm,
+});
+</script>
diff --git a/src/views/device/index.vue b/src/views/device/index.vue
new file mode 100644
index 0000000..e9fff64
--- /dev/null
+++ b/src/views/device/index.vue
@@ -0,0 +1,322 @@
+<template>
+  <div class="app-container">
+    <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-input
+            v-model="filters.deviceModel"
+            style="width: 240px"
+            placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+            clearable
+            :prefix-icon="Search"
+            @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="渚涘簲鍟�">
+        <el-input
+            v-model="filters.supplierName"
+            style="width: 240px"
+            placeholder="璇疯緭鍏ヤ緵搴斿晢"
+            clearable
+            :prefix-icon="Search"
+            @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="鍗曚綅">
+        <el-input
+            v-model="filters.unit"
+            style="width: 240px"
+            placeholder="璇疯緭鍏ュ崟浣�"
+            clearable
+            :prefix-icon="Search"
+            @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="褰曞叆鏃ユ湡:">
+        <el-date-picker v-model="filters.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
+                        placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+      </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 class="table_list">
+      <div class="actions">
+        <div></div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus"> 鏂板 </el-button>
+          <el-button @click="handleOut" icon="download">瀵煎嚭</el-button>
+          <el-button
+            type="danger"
+            icon="Delete"
+            :disabled="multipleList.length <= 0"
+            @click="deleteRow(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"
+      >
+      </PIMTable>
+    </div>
+    <Modal ref="modalRef" @success="getTableData"></Modal>
+    <el-dialog v-model="qrDialogVisible" title="浜岀淮鐮�" width="300px">
+      <div style="text-align:center;">
+        <img :src="qrCodeUrl" alt="浜岀淮鐮�" style="width:200px;height:200px;" />
+        <div style="margin:10px 0;">
+          <el-button type="primary" @click="downloadQRCode">涓嬭浇浜岀淮鐮佸浘鐗�</el-button>
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { usePaginationApi } from "../../hooks/usePaginationApi";
+// import { Search } from "@element-plus/icons-vue";
+import { getLedgerPage, delLedger } from "../../api/equipmentManagement/ledger";
+import { onMounted, getCurrentInstance } from "vue";
+import Modal from "./Modal.vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import dayjs from "dayjs";
+import QRCode from "qrcode";
+import { ref } from "vue";
+
+defineOptions({
+  name: "璁惧鍙拌处",
+});
+
+// 琛ㄦ牸澶氶�夋閫変腑椤�
+const multipleList = ref([]);
+const { proxy } = getCurrentInstance();
+const modalRef = ref();
+const qrDialogVisible = ref(false);
+const qrCodeUrl = ref("");
+const qrRowData = ref(null);
+
+const {
+  filters,
+  columns,
+  dataList,
+  pagination,
+  getTableData,
+  resetFilters,
+  onCurrentChange,
+} = usePaginationApi(
+  getLedgerPage,
+  {
+    deviceName: undefined,
+    deviceModel: undefined,
+    supplierName: undefined,
+    unit: undefined,
+    entryDateStart: undefined,
+    entryDateEnd: undefined,
+  },
+  [
+    {
+      label: "璁惧鍚嶇О",
+      align: "center",
+      prop: "deviceName",
+    },
+    {
+      label: "瑙勬牸鍨嬪彿",
+      align: "center",
+      prop: "deviceModel",
+    },
+    {
+      label: "璁惧鍝佺墝",
+      align: "center",
+      prop: "deviceBrand",
+    },
+    {
+      label: "渚涘簲鍟�",
+      align: "center",
+      prop: "supplierName",
+    },
+    {
+      label: "鍗曚綅",
+      align: "center",
+      prop: "unit",
+    },
+    {
+      label: "瀛樻斁浣嶇疆",
+      align: "center",
+      prop: "storageLocation",
+    },
+    {
+      label: "鏁伴噺",
+      align: "center",
+      prop: "number",
+    },
+    {
+      label: "鍚◣鍗曚环",
+      align: "center",
+      prop: "taxIncludingPriceUnit",
+    },
+    {
+      label: "鍚◣鎬讳环",
+      align: "center",
+      prop: "taxIncludingPriceTotal",
+    },
+    {
+      label: "绋庣巼",
+      align: "center",
+      prop: "taxRate",
+    },
+    {
+      label: "涓嶅惈绋庢�讳环",
+      align: "center",
+      prop: "unTaxIncludingPriceTotal",
+    },
+    {
+      label: "鍚敤鎶樻棫",
+      align: "center",
+      prop: "enableDepreciation",
+      formatData: (v) => (v ? "鏄�" : "鍚�"),
+    },
+    {
+      label: "褰曞叆浜�",
+      align: "center",
+      prop: "createUser",
+    },
+    {
+      label: "褰曞叆鏃ユ湡",
+      align: "center",
+      prop: "createTime",
+    },
+		{
+			dataType: "action",
+			label: "鎿嶄綔",
+			align: "center",
+			fixed: 'right',
+			width: 150,
+			operation: [
+				{
+					name: "缂栬緫",
+					type: "text",
+					clickFun: (row) => {
+						edit(row.id)
+					},
+				},
+				{
+					name: "鐢熸垚浜岀淮鐮�",
+					type: "text",
+					clickFun: (row) => {
+						showQRCode(row)
+					},
+				},
+			],
+		},
+  ]
+);
+
+// 澶氶�夊悗鍋氫粈涔�
+const handleSelectionChange = (selectionList) => {
+  multipleList.value = selectionList;
+};
+
+const add = () => {
+  modalRef.value.openModal();
+};
+const edit = (id) => {
+  modalRef.value.loadForm(id);
+};
+const changePage = ({ page, limit }) => {
+  pagination.currentPage = page;
+	pagination.pageSize = limit;
+  onCurrentChange(page);
+};
+const deleteRow = (id) => {
+  ElMessageBox.confirm("姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ユ枃浠�, 鏄惁缁х画?", "鎻愮ず", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(async () => {
+    const { code } = await delLedger(id);
+    if (code == 200) {
+      ElMessage({
+        type: "success",
+        message: "鍒犻櫎鎴愬姛",
+      });
+      getTableData();
+    }
+  });
+};
+
+const changeDaterange = (value) => {
+  if (value) {
+    filters.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
+    filters.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
+  } else {
+    filters.entryDateStart = undefined;
+    filters.entryDateEnd = undefined;
+  }
+  getTableData();
+};
+
+const handleOut = () => {
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+    .then(() => {
+      proxy.download(`/device/ledger/export`, {}, "璁惧鍙拌处妗f.xlsx");
+    })
+    .catch(() => {
+      proxy.$modal.msg("宸插彇娑�");
+    });
+};
+
+const showQRCode = async (row) => {
+  // 鐩存帴浣跨敤URL锛屼笉瑕佺敤JSON.stringify鍖呰
+  const qrContent = proxy.javaApi + '/device-info?deviceId=' + row.id;
+  qrCodeUrl.value = await QRCode.toDataURL(qrContent);
+  qrRowData.value = row;
+  qrDialogVisible.value = true;
+};
+
+const downloadQRCode = () => {
+  const a = document.createElement("a");
+  a.href = qrCodeUrl.value;
+  a.download = `${qrRowData.value.deviceName || "浜岀淮鐮�"}.png`;
+  a.click();
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.table_list {
+  margin-top: unset;
+}
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 10px;
+}
+</style>
diff --git a/src/views/diagnosis/FaultDiagnosis.vue b/src/views/diagnosis/FaultDiagnosis.vue
deleted file mode 100644
index a1cb864..0000000
--- a/src/views/diagnosis/FaultDiagnosis.vue
+++ /dev/null
@@ -1,411 +0,0 @@
-<template>
-  <div class="fault-diagnosis-container">
-    <el-row :gutter="20">
-      <!-- 宸︿晶锛氭晠闅滈璀﹀垪琛� -->
-      <el-col :span="12">
-        <el-card shadow="hover">
-          <template #header>
-            <div class="card-header">
-              <span>鏁呴殰棰勮鍒楄〃</span>
-            </div>
-          </template>
-          <el-table :data="warningList" stripe style="width: 100%" @row-click="handleWarningClick">
-            <el-table-column prop="deviceName" label="璁惧鍚嶇О" width="180"></el-table-column>
-            <el-table-column prop="warningType" label="棰勮绫诲瀷" width="120"></el-table-column>
-            <el-table-column prop="riskLevel" label="椋庨櫓绛夌骇" width="100">
-              <template #default="scope">
-                <el-tag :type="scope.row.riskLevel === 'high' ? 'danger' : scope.row.riskLevel === 'medium' ? 'warning' : 'info'">
-                  {{ scope.row.riskLevel === 'high' ? '楂�' : scope.row.riskLevel === 'medium' ? '涓�' : '浣�' }}
-                </el-tag>
-              </template>
-            </el-table-column>
-            <el-table-column prop="occurTime" label="鍙戠敓鏃堕棿" width="180"></el-table-column>
-            <el-table-column prop="status" label="澶勭悊鐘舵��" width="100">
-              <template #default="scope">
-                <el-tag :type="scope.row.status === 'pending' ? 'warning' : 'success'">
-                  {{ scope.row.status === 'pending' ? '寰呭鐞�' : '宸插鐞�' }}
-                </el-tag>
-              </template>
-            </el-table-column>
-          </el-table>
-        </el-card>
-
-        <!-- 鏁呴殰鍘嗗彶璁板綍鏌ヨ -->
-        <el-card shadow="hover" style="margin-top: 20px;">
-          <template #header>
-            <div class="card-header">
-              <span>鏁呴殰鍘嗗彶璁板綍</span>
-            </div>
-          </template>
-          <el-form :inline="true" :model="historyFilterForm" class="history-filter-form">
-            <el-form-item label="璁惧">
-              <el-select v-model="historyFilterForm.deviceId" placeholder="璇烽�夋嫨璁惧" clearable>
-                <el-option
-                  v-for="device in devices"
-                  :key="device.id"
-                  :label="device.name"
-                  :value="device.id"
-                ></el-option>
-              </el-select>
-            </el-form-item>
-            <el-form-item label="鏃堕棿鑼冨洿">
-              <el-date-picker
-                v-model="historyTimeRange"
-                type="daterange"
-                range-separator="鑷�"
-                start-placeholder="寮�濮嬫棩鏈�"
-                end-placeholder="缁撴潫鏃ユ湡"
-              ></el-date-picker>
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary" @click="handleHistorySearch">鏌ヨ</el-button>
-            </el-form-item>
-          </el-form>
-          <el-table :data="historyList" stripe style="width: 100%" size="small">
-            <el-table-column prop="deviceName" label="璁惧鍚嶇О" width="150"></el-table-column>
-            <el-table-column prop="faultType" label="鏁呴殰绫诲瀷" width="120"></el-table-column>
-            <el-table-column prop="occurTime" label="鍙戠敓鏃堕棿" width="150"></el-table-column>
-            <el-table-column prop="dealTime" label="澶勭悊鏃堕棿" width="150"></el-table-column>
-            <el-table-column prop="status" label="鐘舵��" width="100">
-              <template #default="scope">
-                <el-tag type="success">{{ scope.row.status }}</el-tag>
-              </template>
-            </el-table-column>
-          </el-table>
-          <div class="pagination-container">
-            <el-pagination
-              background
-              layout="total, prev, pager, next"
-              :total="historyList.length"
-              :page-size="5"
-              size="small"
-            ></el-pagination>
-          </div>
-        </el-card>
-      </el-col>
-
-      <!-- 鍙充晶锛氭晠闅滆瘖鏂粨鏋� -->
-      <el-col :span="12">
-        <!-- 鏁呴殰璇婃柇缁撴灉 -->
-        <el-card shadow="hover">
-          <template #header>
-            <div class="card-header">
-              <span>鏁呴殰璇婃柇缁撴灉</span>
-              <el-button type="primary" size="small" @click="handleDiagnosis">閲嶆柊璇婃柇</el-button>
-            </div>
-          </template>
-          <div v-if="currentWarning" class="diagnosis-result">
-            <h3>{{ currentWarning.deviceName }} - {{ currentWarning.warningType }}</h3>
-            <el-descriptions :column="1" border>
-              <el-descriptions-item label="椋庨櫓绛夌骇">
-                <el-tag :type="currentWarning.riskLevel === 'high' ? 'danger' : currentWarning.riskLevel === 'medium' ? 'warning' : 'info'">
-                  {{ currentWarning.riskLevel === 'high' ? '楂�' : currentWarning.riskLevel === 'medium' ? '涓�' : '浣�' }}
-                </el-tag>
-              </el-descriptions-item>
-              <el-descriptions-item label="鍙戠敓鏃堕棿">{{ currentWarning.occurTime }}</el-descriptions-item>
-              <el-descriptions-item label="鍘熷洜鎺ㄦ祴">{{ diagnosisResult.reason }}</el-descriptions-item>
-              <el-descriptions-item label="褰卞搷鑼冨洿">{{ diagnosisResult.impact }}</el-descriptions-item>
-              <el-descriptions-item label="澶勭悊寤鸿">{{ diagnosisResult.suggestion }}</el-descriptions-item>
-            </el-descriptions>
-          </div>
-          <div v-else class="no-selection">
-            <el-empty description="璇烽�夋嫨涓�涓璀﹂」鏌ョ湅璇婃柇缁撴灉"></el-empty>
-          </div>
-        </el-card>
-
-        <!-- 棰勬祴鎬ц瘖鏂粨鏋� -->
-        <el-card shadow="hover" style="margin-top: 20px;">
-          <template #header>
-            <div class="card-header">
-              <span>棰勬祴鎬ц瘖鏂粨鏋滐紙鏈潵7鏃ユ晠闅滈闄╋級</span>
-            </div>
-          </template>
-          <div class="prediction-result">
-            <el-timeline>
-              <el-timeline-item
-                v-for="item in predictionList"
-                :key="item.date"
-                :timestamp="item.date"
-                :type="item.riskLevel === 'high' ? 'danger' : item.riskLevel === 'medium' ? 'warning' : 'success'"
-              >
-                <div class="timeline-content">
-                  <h4>{{ item.deviceName }}</h4>
-                  <p class="risk-level">
-                    椋庨櫓绛夌骇锛�
-                    <el-tag :type="item.riskLevel === 'high' ? 'danger' : item.riskLevel === 'medium' ? 'warning' : 'success'">
-                      {{ item.riskLevel === 'high' ? '楂�' : item.riskLevel === 'medium' ? '涓�' : '浣�' }}
-                    </el-tag>
-                  </p>
-                  <p class="fault-type">鍙兘鏁呴殰绫诲瀷锛歿{ item.possibleFault }}</p>
-                  <p class="probability">鍙戠敓姒傜巼锛歿{ item.probability }}%</p>
-                </div>
-              </el-timeline-item>
-            </el-timeline>
-          </div>
-        </el-card>
-      </el-col>
-    </el-row>
-  </div>
-</template>
-
-<script setup>
-import { ref, reactive } from 'vue'
-
-// 璁惧鍒楄〃
-const devices = ref([
-  { id: 'D001', name: '绌哄帇鏈篈-001' },
-  { id: 'D002', name: '鍐峰嵈濉擝-002' },
-  { id: 'D003', name: '姘存车C-003' },
-  { id: 'D004', name: '鍙戠數鏈篋-004' },
-  { id: 'D005', name: '鍙樺帇鍣‥-005' }
-])
-
-// 鏁呴殰棰勮鍒楄〃
-const warningList = ref([
-  {
-    id: 1,
-    deviceName: '绌哄帇鏈篈-001',
-    warningType: '鍘嬪姏寮傚父',
-    riskLevel: 'high',
-    occurTime: '2024-12-16 14:32:15',
-    status: 'pending'
-  },
-  {
-    id: 2,
-    deviceName: '鍐峰嵈濉擝-002',
-    warningType: '娓╁害杩囬珮',
-    riskLevel: 'medium',
-    occurTime: '2024-12-16 14:30:45',
-    status: 'pending'
-  },
-  {
-    id: 3,
-    deviceName: '姘存车C-003',
-    warningType: '鎸姩杩囧ぇ',
-    riskLevel: 'medium',
-    occurTime: '2024-12-16 14:28:30',
-    status: 'pending'
-  },
-  {
-    id: 4,
-    deviceName: '鍙戠數鏈篋-004',
-    warningType: '鐢垫祦寮傚父',
-    riskLevel: 'high',
-    occurTime: '2024-12-16 14:25:10',
-    status: 'pending'
-  },
-  {
-    id: 5,
-    deviceName: '鍙樺帇鍣‥-005',
-    warningType: '鐢靛帇娉㈠姩',
-    riskLevel: 'low',
-    occurTime: '2024-12-16 14:20:05',
-    status: 'pending'
-  }
-])
-
-// 褰撳墠閫変腑鐨勯璀﹂」
-const currentWarning = ref(warningList.value[0])
-
-// 鏁呴殰璇婃柇缁撴灉
-const diagnosisResult = reactive({
-  reason: '鏍规嵁璁惧杩愯鏁版嵁鎺ㄦ祴锛屾晠闅滃師鍥犲彲鑳芥槸璁惧鍐呴儴閮ㄤ欢纾ㄦ崯瀵艰嚧鐨勫帇鍔涘紓甯革紝闇�瑕佽繘涓�姝ユ鏌ヨ澶囩殑娲诲鐜拰姘旂几濂椼��',
-  impact: '濡傛灉涓嶅強鏃跺鐞嗭紝鍙兘瀵艰嚧璁惧鍋滄満锛屽奖鍝嶇敓浜х嚎鐨勬甯歌繍琛岋紝棰勮鍋滄満鏃堕棿涓�4-6灏忔椂銆�',
-  suggestion: '1. 绔嬪嵆瀹夋帓缁翠慨浜哄憳杩涜璁惧妫�鏌ワ紱2. 妫�鏌ヨ澶囩殑娲诲鐜拰姘旂几濂楋紱3. 鏇存崲纾ㄦ崯涓ラ噸鐨勯儴浠讹紱4. 妫�鏌ヨ澶囩殑娑︽粦绯荤粺锛岀‘淇濇鼎婊戞甯搞��'
-})
-
-// 棰勬祴鎬ц瘖鏂粨鏋�
-const predictionList = ref([
-  {
-    date: '2024-12-17',
-    deviceName: '绌哄帇鏈篈-001',
-    riskLevel: 'medium',
-    possibleFault: '鍘嬪姏寮傚父',
-    probability: 65
-  },
-  {
-    date: '2024-12-18',
-    deviceName: '鍐峰嵈濉擝-002',
-    riskLevel: 'high',
-    possibleFault: '娓╁害杩囬珮',
-    probability: 85
-  },
-  {
-    date: '2024-12-19',
-    deviceName: '姘存车C-003',
-    riskLevel: 'medium',
-    possibleFault: '鎸姩杩囧ぇ',
-    probability: 70
-  },
-  {
-    date: '2024-12-20',
-    deviceName: '鍙戠數鏈篋-004',
-    riskLevel: 'high',
-    possibleFault: '鐢垫祦寮傚父',
-    probability: 90
-  },
-  {
-    date: '2024-12-21',
-    deviceName: '鍙樺帇鍣‥-005',
-    riskLevel: 'low',
-    possibleFault: '鐢靛帇娉㈠姩',
-    probability: 45
-  },
-  {
-    date: '2024-12-22',
-    deviceName: '绌哄帇鏈篈-001',
-    riskLevel: 'high',
-    possibleFault: '鍘嬪姏寮傚父',
-    probability: 80
-  },
-  {
-    date: '2024-12-23',
-    deviceName: '鍐峰嵈濉擝-002',
-    riskLevel: 'medium',
-    possibleFault: '娓╁害杩囬珮',
-    probability: 60
-  }
-])
-
-// 鏁呴殰鍘嗗彶璁板綍鏌ヨ琛ㄥ崟
-const historyFilterForm = ref({
-  deviceId: ''
-})
-
-// 鍘嗗彶璁板綍鏃堕棿鑼冨洿
-const historyTimeRange = ref([])
-
-// 鏁呴殰鍘嗗彶璁板綍
-const historyList = ref([
-  {
-    id: 1,
-    deviceName: '绌哄帇鏈篈-001',
-    faultType: '鍘嬪姏寮傚父',
-    occurTime: '2024-12-15 08:30:00',
-    dealTime: '2024-12-15 10:45:00',
-    status: '宸插鐞�'
-  },
-  {
-    id: 2,
-    deviceName: '鍐峰嵈濉擝-002',
-    faultType: '娓╁害杩囬珮',
-    occurTime: '2024-12-14 14:20:00',
-    dealTime: '2024-12-14 16:15:00',
-    status: '宸插鐞�'
-  },
-  {
-    id: 3,
-    deviceName: '姘存车C-003',
-    faultType: '鎸姩杩囧ぇ',
-    occurTime: '2024-12-13 09:15:00',
-    dealTime: '2024-12-13 11:30:00',
-    status: '宸插鐞�'
-  },
-  {
-    id: 4,
-    deviceName: '鍙戠數鏈篋-004',
-    faultType: '鐢垫祦寮傚父',
-    occurTime: '2024-12-12 16:45:00',
-    dealTime: '2024-12-12 18:30:00',
-    status: '宸插鐞�'
-  },
-  {
-    id: 5,
-    deviceName: '鍙樺帇鍣‥-005',
-    faultType: '鐢靛帇娉㈠姩',
-    occurTime: '2024-12-11 11:20:00',
-    dealTime: '2024-12-11 13:15:00',
-    status: '宸插鐞�'
-  }
-])
-
-// 澶勭悊棰勮椤圭偣鍑�
-const handleWarningClick = (row) => {
-  currentWarning.value = row
-}
-
-// 閲嶆柊璇婃柇
-const handleDiagnosis = () => {
-  // 妯℃嫙閲嶆柊璇婃柇
-  ElMessage.success('閲嶆柊璇婃柇瀹屾垚')
-}
-
-// 澶勭悊鍘嗗彶璁板綍鏌ヨ
-const handleHistorySearch = () => {
-  // 妯℃嫙鏌ヨ鍘嗗彶璁板綍
-  ElMessage.success('鍘嗗彶璁板綍鏌ヨ鎴愬姛')
-}
-</script>
-
-<style scoped>
-.fault-diagnosis-container {
-  padding: 20px;
-  background-color: #f5f7fa;
-  min-height: 100vh;
-}
-
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-.diagnosis-result h3 {
-  margin-bottom: 20px;
-  color: #303133;
-}
-
-.no-selection {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  height: 200px;
-}
-
-.prediction-result {
-  padding: 10px 0;
-}
-
-.timeline-content {
-  padding: 10px;
-  background-color: #fafafa;
-  border-radius: 4px;
-}
-
-.timeline-content h4 {
-  margin-bottom: 10px;
-  color: #303133;
-}
-
-.timeline-content p {
-  margin: 5px 0;
-  font-size: 14px;
-  color: #606266;
-}
-
-.risk-level {
-  display: flex;
-  align-items: center;
-  gap: 5px;
-}
-
-.fault-type {
-  color: #606266;
-}
-
-.probability {
-  color: #606266;
-}
-
-.history-filter-form {
-  margin-bottom: 20px;
-  padding: 10px 0;
-  border-bottom: 1px solid #ebeef5;
-}
-
-.pagination-container {
-  display: flex;
-  justify-content: flex-end;
-  margin-top: 20px;
-}
-</style>
\ No newline at end of file
diff --git a/src/views/diagnosis/Form/MaintainForm.vue b/src/views/diagnosis/Form/MaintainForm.vue
new file mode 100644
index 0000000..339cfc5
--- /dev/null
+++ b/src/views/diagnosis/Form/MaintainForm.vue
@@ -0,0 +1,66 @@
+<template>
+  <el-form :model="form" label-width="80px">
+    <el-form-item label="缁翠慨浜�">
+      <el-input v-model="form.maintenanceName" placeholder="璇疯緭鍏ョ淮淇汉" />
+    </el-form-item>
+    <el-form-item label="缁翠慨缁撴灉">
+      <el-input v-model="form.maintenanceResult" placeholder="璇疯緭鍏ョ淮淇粨鏋�" />
+    </el-form-item>
+    <el-form-item label="鏁呴殰鐘舵��">
+      <el-select v-model="form.status">
+        <el-option label="寰呮晠闅�" :value="0"></el-option>
+        <el-option label="瀹岀粨" :value="1"></el-option>
+        <el-option label="澶辫触" :value="2"></el-option>
+      </el-select>
+    </el-form-item>
+    <el-form-item label="缁翠慨鏃ユ湡">
+      <el-date-picker
+        v-model="form.maintenanceTime"
+        placeholder="璇烽�夋嫨缁翠慨鏃ユ湡"
+        format="YYYY-MM-DD HH:mm:ss"
+        value-format="YYYY-MM-DD HH:mm:ss"
+        type="datetime"
+        clearable
+        style="width: 100%"
+      />
+    </el-form-item>
+  </el-form>
+</template>
+
+<script setup>
+import useFormData from "../../../hooks/useFormData";
+import useUserStore from "../../../store/modules/user";
+import dayjs from "dayjs";
+
+defineOptions({
+  name: "璁惧缁翠慨琛ㄥ崟",
+});
+
+const userStore = useUserStore();
+const { form, resetForm } = useFormData({
+  maintenanceName: undefined, // 缁翠慨鍚嶇О
+  maintenanceResult: undefined, // 缁翠慨缁撴灉
+  maintenanceTime: undefined, // 缁翠慨鏃ユ湡
+  status: 0,
+});
+
+const setForm = (data) => {
+  form.maintenanceName = data.maintenanceName ?? userStore.nickName;
+  form.maintenanceResult = data.maintenanceResult;
+  form.maintenanceTime =
+    dayjs(data.maintenanceTime).format("YYYY-MM-DD HH:mm:ss") ??
+    dayjs().format("YYYY-MM-DD HH:mm:ss");
+};
+
+const getForm = () => {
+  return form;
+};
+
+defineExpose({
+  getForm,
+  setForm,
+  resetForm,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/diagnosis/Form/RepairForm.vue b/src/views/diagnosis/Form/RepairForm.vue
new file mode 100644
index 0000000..fc3ab81
--- /dev/null
+++ b/src/views/diagnosis/Form/RepairForm.vue
@@ -0,0 +1,130 @@
+<template>
+  <el-form :model="form" label-width="100px">
+    <el-row>
+      <el-col :span="12">
+        <el-form-item label="璁惧鍚嶇О">
+          <el-select v-model="form.deviceLedgerId" @change="setDeviceModel">
+            <el-option
+              v-for="(item, index) in deviceOptions"
+              :key="index"
+              :label="item.deviceName"
+              :value="item.id"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="瑙勬牸鍨嬪彿">
+          <el-input
+            v-model="form.deviceModel"
+            placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+            disabled
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="鏁呴殰鏃ユ湡">
+          <el-date-picker
+            v-model="form.repairTime"
+            placeholder="璇烽�夋嫨鏁呴殰鏃ユ湡"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            type="date"
+            clearable
+            style="width: 100%"
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="涓婃姤浜�">
+          <el-input v-model="form.repairName" placeholder="璇疯緭鍏ヤ笂鎶ヤ汉" />
+        </el-form-item>
+      </el-col>
+    </el-row>
+    <el-row v-if="id">
+      <el-col :span="12">
+        <el-form-item label="鏁呴殰鐘舵��">
+          <el-select v-model="form.status">
+            <el-option label="寰呯淮淇�" :value="0"></el-option>
+            <el-option label="瀹岀粨" :value="1"></el-option>
+            <el-option label="澶辫触" :value="2"></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+    </el-row>
+    <el-row>
+      <el-col :span="24">
+        <el-form-item label="鏁呴殰鐜拌薄">
+          <el-input
+            v-model="form.remark"
+            :rows="2"
+            type="textarea"
+            placeholder="璇疯緭鍏ユ晠闅滅幇璞�"
+          />
+        </el-form-item>
+      </el-col>
+    </el-row>
+  </el-form>
+</template>
+
+<script setup>
+import useFormData from "../../../hooks/useFormData";
+import { getDeviceLedger } from "../../../api/equipmentManagement/ledger";
+import useUserStore from "../../../store/modules/user";
+
+const { id } = defineProps(["id"])
+
+defineOptions({
+  name: "璁惧鏁呴殰琛ㄥ崟",
+});
+
+const userStore = useUserStore();
+const deviceOptions = ref([]);
+
+const loadDeviceName = async () => {
+  const { data } = await getDeviceLedger();
+  deviceOptions.value = data;
+};
+
+const { form, resetForm } = useFormData({
+  deviceLedgerId: undefined, // 璁惧Id
+  deviceName: undefined, // 璁惧鍚嶇О
+  deviceModel: undefined, // 瑙勬牸鍨嬪彿
+  repairTime: undefined, // 鏁呴殰鏃ユ湡
+  repairName: userStore.nickName, // 鏁呴殰浜�
+  remark: undefined, // 鏁呴殰鐜拌薄
+  status: 0, // 鏁呴殰鐘舵��
+});
+
+const setDeviceModel = (id) => {
+  const option = deviceOptions.value.find((item) => item.id === id);
+  form.deviceModel = option.deviceModel;
+};
+
+const getForm = () => {
+  return form;
+};
+
+const setForm = (data) => {
+  form.deviceLedgerId = data.deviceLedgerId;
+  form.deviceName = data.deviceName;
+  form.deviceModel = data.deviceModel;
+  form.repairTime = data.repairTime;
+  form.repairName = data.repairName;
+  form.remark = data.remark;
+  form.status = data.status;
+};
+
+// onMounted(() => {
+//   loadDeviceName();
+// });
+
+defineExpose({
+  loadDeviceName,
+  resetForm,
+  getForm,
+  setForm,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/diagnosis/Modal/MaintainModal.vue b/src/views/diagnosis/Modal/MaintainModal.vue
new file mode 100644
index 0000000..648614b
--- /dev/null
+++ b/src/views/diagnosis/Modal/MaintainModal.vue
@@ -0,0 +1,58 @@
+<template>
+  <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr">
+    <MaintainForm ref="maintainFormRef" />
+    <template #footer>
+			<el-button type="primary" @click="sendForm" :loading="loading">
+				{{ modalOptions.confirmText }}
+			</el-button>
+      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useModal } from "../../../hooks/useModal";
+import MaintainForm from "../Form/MaintainForm.vue";
+import { addMaintain } from "../../../api/equipmentManagement/repair";
+
+defineOptions({
+  name: "缁翠慨妯℃�佹",
+});
+
+const maintainFormRef = ref();
+const emits = defineEmits(["ok"]);
+
+const {
+  id,
+  visible,
+  loading,
+  openModal,
+  modalOptions,
+  handleConfirm,
+  closeModal,
+} = useModal({ title: "璁惧缁翠慨" });
+
+const sendForm = async () => {
+  loading.value = true;
+  const form = await maintainFormRef.value.getForm();
+  const { code } = await addMaintain({ id: id.value, ...form });
+  if (code == 200) {
+    emits("ok");
+    maintainFormRef.value.resetForm();
+    closeModal();
+  }
+  loading.value = false;
+};
+
+const open = async (id, row) => {
+  openModal(id);
+  await nextTick();
+  maintainFormRef.value.setForm(row);
+};
+
+defineExpose({
+  open,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/diagnosis/Modal/RepairModal.vue b/src/views/diagnosis/Modal/RepairModal.vue
new file mode 100644
index 0000000..4e2ef55
--- /dev/null
+++ b/src/views/diagnosis/Modal/RepairModal.vue
@@ -0,0 +1,77 @@
+<template>
+  <el-dialog v-model="visible" :title="modalOptions.title" @close="close">
+    <RepairForm ref="repairFormRef" :id="id" />
+    <template #footer>
+			<el-button type="primary" @click="sendForm" :loading="loading">
+				{{ modalOptions.confirmText }}
+			</el-button>
+      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useModal } from "../../../hooks/useModal";
+import RepairForm from "../Form/RepairForm.vue";
+import {
+  addRepair,
+  editRepair,
+  getRepairById,
+} from "../../../api/equipmentManagement/repair";
+import { ElMessage } from "element-plus";
+
+defineOptions({
+  name: "璁惧鏁呴殰寮圭獥",
+});
+
+const emits = defineEmits(["ok"]);
+
+const repairFormRef = ref();
+const {
+  id,
+  visible,
+  loading,
+  openModal,
+  modalOptions,
+  handleConfirm,
+  closeModal,
+} = useModal({ title: "璁惧鏁呴殰" });
+
+const sendForm = async () => {
+  loading.value = true;
+  const form = await repairFormRef.value.getForm();
+  const { code } = id.value
+    ? await editRepair({ id: unref(id), ...form })
+    : await addRepair(form);
+  if (code == 200) {
+    ElMessage.success(`${id ? "缂栬緫" : "鏂板"}鏁呴殰鎴愬姛`);
+    closeModal();
+    emits("ok");
+  }
+  loading.value = false;
+};
+
+const openAdd = async () => {
+  openModal();
+  await nextTick();
+  await repairFormRef.value.loadDeviceName();
+};
+
+const openEdit = async (id) => {
+  const { data } = await getRepairById(id);
+  openModal(id);
+  await nextTick();
+  await repairFormRef.value.loadDeviceName();
+  await repairFormRef.value.setForm(data);
+};
+
+const close = () => {
+  repairFormRef.value.resetForm();
+  closeModal();
+};
+
+defineExpose({
+  openAdd,
+  openEdit,
+});
+</script>
diff --git a/src/views/diagnosis/index.vue b/src/views/diagnosis/index.vue
new file mode 100644
index 0000000..69674e4
--- /dev/null
+++ b/src/views/diagnosis/index.vue
@@ -0,0 +1,327 @@
+<template>
+  <div class="app-container">
+    <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-input
+            v-model="filters.deviceModel"
+            style="width: 240px"
+            placeholder="璇烽�夋嫨瑙勬牸鍨嬪彿"
+            clearable
+            :prefix-icon="Search"
+            @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="鏁呴殰鐜拌薄">
+        <el-input
+            v-model="filters.remark"
+            style="width: 240px"
+            placeholder="璇疯緭鍏ユ晠闅滅幇璞�"
+            clearable
+            :prefix-icon="Search"
+            @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="缁翠慨浜�">
+        <el-input
+            v-model="filters.maintenanceName"
+            style="width: 240px"
+            placeholder="璇疯緭鍏ョ淮淇汉"
+            clearable
+            :prefix-icon="Search"
+            @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="鏁呴殰鏃ユ湡">
+        <el-date-picker
+            v-model="filters.repairTimeStr"
+            type="date"
+            placeholder="璇烽�夋嫨鏁呴殰鏃ユ湡"
+            size="default"
+            @change="(date) => handleDateChange(date,2)"
+        />
+      </el-form-item>
+      <el-form-item label="缁翠慨鏃ユ湡">
+        <el-date-picker
+            v-model="filters.maintenanceTimeStr"
+            type="date"
+            placeholder="璇烽�夋嫨缁翠慨鏃ユ湡"
+            size="default"
+            @change="(date) => handleDateChange(date,1)"
+        />
+      </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 class="table_list">
+      <div class="actions">
+        <el-text class="mx-1" size="large">璁惧鏁呴殰</el-text>
+        <div>
+          <el-button
+            type="primary"
+            icon="Plus"
+            :disabled="multipleList.length !== 1"
+            @click="addMaintain"
+          >
+            鏂板缁翠慨
+          </el-button>
+          <el-button type="success" icon="Van" @click="addRepair">
+            鏂板鏁呴殰
+          </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>
+      <PIMTable
+        rowKey="id"
+        isSelection
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @selection-change="handleSelectionChange"
+        @pagination="changePage"
+      >
+        <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
+            icon="editPen"
+            @click="editRepair(row.id)"
+          >
+            缂栬緫
+          </el-button>
+          <el-button
+            type="danger"
+            text
+            icon="delete"
+            @click="delRepairByIds(row.id)"
+          >
+            鍒犻櫎
+          </el-button>
+        </template>
+      </PIMTable>
+    </div>
+    <RepairModal ref="repairModalRef" @ok="getTableData" />
+    <MaintainModal ref="maintainModalRef" @ok="getTableData" />
+  </div>
+</template>
+
+<script setup>
+import { usePaginationApi } from "../../hooks/usePaginationApi";
+import { getRepairPage, delRepair } from "../../api/equipmentManagement/repair";
+import { onMounted, getCurrentInstance } from "vue";
+import RepairModal from "./Modal/RepairModal.vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import dayjs from "dayjs";
+import MaintainModal from "./Modal/MaintainModal.vue";
+
+defineOptions({
+  name: "璁惧鏁呴殰",
+});
+
+const { proxy } = getCurrentInstance();
+
+// 妯℃�佹瀹炰緥
+const repairModalRef = ref();
+const maintainModalRef = ref();
+
+// 琛ㄦ牸澶氶�夋閫変腑椤�
+const multipleList = ref([]);
+
+// 琛ㄦ牸閽╁瓙
+const {
+  filters,
+  columns,
+  dataList,
+  pagination,
+  getTableData,
+  resetFilters,
+  onCurrentChange,
+} = usePaginationApi(
+  getRepairPage,
+  {
+    deviceName: undefined,
+    deviceModel: undefined,
+    remark: undefined,
+    maintenanceName: undefined,
+    repairTimeStr: undefined,
+    maintenanceTimeStr: undefined,
+  },
+  [
+    {
+      label: "璁惧鍚嶇О",
+      align: "center",
+      prop: "deviceName",
+    },
+    {
+      label: "瑙勬牸鍨嬪彿",
+      align: "center",
+      prop: "deviceModel",
+    },
+    {
+      label: "鏁呴殰鏃ユ湡",
+      align: "center",
+      prop: "repairTime",
+      formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"),
+    },
+    {
+      label: "涓婃姤浜�",
+      align: "center",
+      prop: "repairName",
+    },
+    {
+      label: "鏁呴殰鐜拌薄",
+      align: "center",
+      prop: "remark",
+    },
+    {
+      label: "缁翠慨浜�",
+      align: "center",
+      prop: "maintenanceName",
+    },
+    {
+      label: "缁翠慨缁撴灉",
+      align: "center",
+      prop: "maintenanceResult",
+    },
+    {
+      label: "缁翠慨鏃ユ湡",
+      align: "center",
+      prop: "maintenanceTime",
+      formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
+    },
+    {
+      label: "鐘舵��",
+      align: "center",
+      prop: "status",
+      dataType: "slot",
+      slot: "statusRef",
+    },
+    {
+      fixed: "right",
+      label: "鎿嶄綔",
+      dataType: "slot",
+      slot: "operation",
+      align: "center",
+      width: "200px",
+    },
+  ]
+);
+
+// type === 1 缁翠慨 2鏁呴殰闂�
+const handleDateChange = (value,type) => {
+  filters.maintenanceTimeStr = null
+  filters.c = null
+  if(type === 1){
+    if (value) {
+      filters.maintenanceTimeStr = dayjs(value).format("YYYY-MM-DD");
+    }
+  }else{
+    if (value) {
+      filters.repairTimeStr = dayjs(value).format("YYYY-MM-DD");
+    }
+  }
+  getTableData();
+};
+
+// 澶氶�夊悗鍋氫粈涔�
+const handleSelectionChange = (selectionList) => {
+  multipleList.value = selectionList;
+};
+
+// 鏂板鏁呴殰
+const addRepair = () => {
+  repairModalRef.value.openAdd();
+};
+
+// 缂栬緫鏁呴殰
+const editRepair = (id) => {
+  repairModalRef.value.openEdit(id);
+};
+
+// 鏂板缁翠慨
+const addMaintain = () => {
+  const row = multipleList.value[0];
+  maintainModalRef.value.open(row.id, row);
+};
+
+const changePage = ({ page, limit }) => {
+	pagination.currentPage = page;
+	pagination.pageSize = limit;
+	onCurrentChange(page);
+};
+
+// 鍗曡鍒犻櫎
+const delRepairByIds = async (ids) => {
+  ElMessageBox.confirm("纭鍒犻櫎鏁呴殰鏁版嵁, 姝ゆ搷浣滀笉鍙��?", "璀﹀憡", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(async () => {
+    const { code } = await delRepair(ids);
+    if (code === 200) {
+      ElMessage.success("鍒犻櫎鎴愬姛");
+      getTableData();
+    }
+  });
+};
+
+// 瀵煎嚭
+const handleOut = () => {
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+    .then(() => {
+      proxy.download("/device/repair/export", {}, "璁惧鏁呴殰.xlsx");
+    })
+    .catch(() => {
+      ElMessage.info("宸插彇娑�");
+    });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.table_list {
+  margin-top: unset;
+}
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 10px;
+}
+</style>
diff --git a/src/views/maintenance/Form/MaintenanceForm.vue b/src/views/maintenance/Form/MaintenanceForm.vue
new file mode 100644
index 0000000..16049e3
--- /dev/null
+++ b/src/views/maintenance/Form/MaintenanceForm.vue
@@ -0,0 +1,77 @@
+<template>
+  <el-form :model="form" label-width="100px">
+    <el-form-item label="瀹為檯淇濆吇浜�">
+      <el-input
+        v-model="form.maintenanceActuallyName"
+        placeholder="璇疯緭鍏ュ疄闄呬繚鍏讳汉"
+      ></el-input>
+    </el-form-item>
+    <el-form-item label="瀹為檯淇濆吇鏃ユ湡">
+      <el-date-picker
+        v-model="form.maintenanceActuallyTime"
+        placeholder="璇烽�夋嫨瀹為檯淇濆吇鏃ユ湡"
+        format="YYYY-MM-DD HH:mm:ss"
+        value-format="YYYY-MM-DD HH:mm:ss"
+        type="datetime"
+        clearable
+        style="width: 100%"
+      />
+    </el-form-item>
+    <el-form-item label="淇濆吇鐘舵��">
+      <el-select v-model="form.status">
+        <el-option label="寰呬繚鍏�" :value="0"></el-option>
+        <el-option label="瀹岀粨" :value="1"></el-option>
+        <el-option label="澶辫触" :value="2"></el-option>
+      </el-select>
+    </el-form-item>
+    <el-form-item label="淇濆吇缁撴灉">
+      <!-- <el-select v-model="form.maintenanceResult" placeholder="璇烽�夋嫨淇濆吇缁撴灉">
+        <el-option label="瀹屽ソ" :value="1"></el-option>
+        <el-option label="缁翠慨" :value="0"></el-option>
+      </el-select> -->
+      <el-input
+        v-model="form.maintenanceResult"
+        placeholder="璇疯緭鍏ヤ繚鍏荤粨鏋�"
+        type="text" />
+    </el-form-item>
+  </el-form>
+</template>
+
+<script setup>
+import useFormData from "../../../hooks/useFormData";
+import dayjs from "dayjs";
+import useUserStore from "../../../store/modules/user";
+
+defineOptions({
+  name: "淇濆吇琛ㄥ崟",
+});
+
+const userStore = useUserStore();
+const { form, resetForm } = useFormData({
+  maintenanceActuallyName: undefined, // 瀹為檯淇濆吇浜�
+  maintenanceActuallyTime: undefined, // 瀹為檯淇濆吇鏃ユ湡
+  maintenanceResult: undefined, // 淇濆吇缁撴灉
+  status: 0, // 淇濆吇鐘舵��
+});
+
+const setForm = (data) => {
+  form.maintenanceActuallyName =
+    data.maintenanceActuallyName ?? userStore.nickName;
+  form.maintenanceActuallyTime =
+    dayjs(data.maintenanceActuallyTime).format("YYYY-MM-DD HH:mm:ss") ??
+    dayjs().format("YYYY-MM-DD HH:mm:ss");
+  form.maintenanceResult = data.maintenanceResult;
+};
+
+const getForm = () => {
+  return form;
+};
+
+defineExpose({
+  getForm,
+  setForm,
+  resetForm,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/maintenance/Form/PlanForm.vue b/src/views/maintenance/Form/PlanForm.vue
new file mode 100644
index 0000000..df7bb7d
--- /dev/null
+++ b/src/views/maintenance/Form/PlanForm.vue
@@ -0,0 +1,137 @@
+<template>
+  <el-form :model="form" label-width="100px">
+    <el-form-item label="璁惧鍚嶇О">
+      <el-select
+        v-model="form.deviceLedgerId"
+        @change="setDeviceModel"
+        placeholder="璇烽�夋嫨璁惧"
+        filterable
+        default-first-option
+        :reserve-keyword="false"
+      >
+        <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="瑙勬牸鍨嬪彿">
+      <el-input
+        v-model="form.deviceModel"
+        placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+        disabled
+      />
+    </el-form-item>
+    <el-form-item label="褰曞叆浜�">
+      <el-select
+        v-model="form.createUser"
+        placeholder="璇烽�夋嫨"
+        filterable
+        default-first-option
+        :reserve-keyword="false"
+        clearable
+      >
+        <el-option
+          v-for="item in userList"
+          :key="item.userId"
+          :label="item.userName"
+          :value="item.userId"
+        />
+      </el-select>
+    </el-form-item>
+    <el-form-item v-if="id" label="淇濅慨鐘舵��">
+      <el-select v-model="form.status">
+        <el-option label="寰呬繚淇�" :value="0"></el-option>
+        <el-option label="瀹岀粨" :value="1"></el-option>
+        <el-option label="澶辫触" :value="2"></el-option>
+      </el-select>
+    </el-form-item>
+    <el-form-item label="璁″垝淇濆吇鏃ユ湡">
+      <el-date-picker
+        style="width: 100%"
+        v-model="form.maintenancePlanTime"
+        format="YYYY-MM-DD"
+        value-format="YYYY-MM-DD HH:mm:ss"
+        type="date"
+        placeholder="璇烽�夋嫨璁″垝淇濆吇鏃ユ湡鏃ユ湡"
+        clearable
+      />
+    </el-form-item>
+  </el-form>
+</template>
+
+<script setup>
+import useFormData from "../../../hooks/useFormData";
+import { getDeviceLedger } from "../../../api/equipmentManagement/ledger";
+import { onMounted } from "vue";
+import dayjs from "dayjs";
+import { userListNoPage } from "../../../api/system/user.js";
+
+defineOptions({
+  name: "璁″垝琛ㄥ崟",
+});
+
+const deviceOptions = ref([]);
+const loadDeviceName = async () => {
+  const { data } = await getDeviceLedger();
+  deviceOptions.value = data;
+};
+
+const { id } = defineProps(['id']);
+
+const { form, resetForm } = useFormData({
+  deviceLedgerId: undefined, // 璁惧Id
+  deviceName: undefined, // 璁惧鍚嶇О
+  deviceModel: undefined, // 瑙勬牸鍨嬪彿
+  maintenancePlanTime: undefined, // 璁″垝淇濆吇鏃ユ湡
+  createUser: undefined, // 褰曞叆浜�
+  status: 0, //淇濅慨鐘舵��
+});
+
+const setDeviceModel = (id) => {
+  const option = deviceOptions.value.find((item) => item.id === id);
+  form.deviceModel = option.deviceModel;
+};
+
+const getForm = () => {
+  return form;
+};
+
+/**
+ * @desc 璁剧疆琛ㄥ崟鍐呭
+ * @param data 璁惧淇℃伅
+ */
+const setForm = (data) => {
+  form.deviceLedgerId = data.deviceLedgerId;
+  form.deviceName = data.deviceName;
+  form.deviceModel = data.deviceModel;
+  form.createUser = Number(data.createUser);
+  form.status = data.status;
+  form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
+    "YYYY-MM-DD HH:mm:ss"
+  );
+};
+
+// 鐢ㄦ埛鍒楄〃
+const userList = ref([]);
+
+const loadForm = () => {};
+
+onMounted(() => {
+  loadDeviceName();
+  userListNoPage().then((res) => {
+    userList.value = res.data;
+  });
+});
+
+defineExpose({
+  loadForm,
+  resetForm,
+  getForm,
+  setForm,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/maintenance/MaintenanceManagement.vue b/src/views/maintenance/MaintenanceManagement.vue
deleted file mode 100644
index 574f333..0000000
--- a/src/views/maintenance/MaintenanceManagement.vue
+++ /dev/null
@@ -1,526 +0,0 @@
-<template>
-  <div class="maintenance-management-container">
-    <!-- 椤堕儴鎿嶄綔鏍� -->
-    <el-card shadow="hover" style="margin-bottom: 20px;">
-      <div class="card-header">
-        <span>缁翠慨绠$悊</span>
-        <div class="header-buttons">
-          <el-button type="primary" @click="showCreateWorkOrderDialog">
-            <el-icon-plus /> 鍒涘缓宸ュ崟
-          </el-button>
-        </div>
-      </div>
-    </el-card>
-
-    <el-row :gutter="20">
-      <!-- 宸︿晶锛氬伐鍗曞垪琛� -->
-      <el-col :span="16">
-        <el-card shadow="hover">
-          <!-- 宸ュ崟鐘舵�佹爣绛鹃〉 -->
-          <el-tabs v-model="activeTab" @tab-change="handleTabChange">
-            <el-tab-pane label="寰呭鐞�" name="pending"></el-tab-pane>
-            <el-tab-pane label="澶勭悊涓�" name="processing"></el-tab-pane>
-            <el-tab-pane label="宸插畬鎴�" name="completed"></el-tab-pane>
-          </el-tabs>
-
-          <!-- 宸ュ崟鍒楄〃 -->
-          <el-table :data="filteredWorkOrders" stripe style="width: 100%" @row-click="handleWorkOrderClick">
-            <el-table-column prop="orderNo" label="宸ュ崟缂栧彿" width="180"></el-table-column>
-            <el-table-column prop="deviceName" label="璁惧鍚嶇О" width="150"></el-table-column>
-            <el-table-column prop="faultType" label="鏁呴殰绫诲瀷" width="120"></el-table-column>
-            <el-table-column prop="createTime" label="鍒涘缓鏃堕棿" width="180"></el-table-column>
-            <el-table-column prop="assignee" label="璐熻矗浜�" width="120"></el-table-column>
-            <el-table-column prop="priority" label="浼樺厛绾�" width="100">
-              <template #default="scope">
-                <el-tag :type="scope.row.priority === 'high' ? 'danger' : scope.row.priority === 'medium' ? 'warning' : 'info'">
-                  {{ scope.row.priority === 'high' ? '楂�' : scope.row.priority === 'medium' ? '涓�' : '浣�' }}
-                </el-tag>
-              </template>
-            </el-table-column>
-            <el-table-column label="鎿嶄綔" width="150">
-              <template #default="scope">
-                <el-button size="small" @click="showEditWorkOrderDialog(scope.row)">缂栬緫</el-button>
-                <el-button type="danger" size="small" @click="handleDeleteWorkOrder(scope.row.id)">鍒犻櫎</el-button>
-              </template>
-            </el-table-column>
-          </el-table>
-
-          <!-- 鍒嗛〉 -->
-          <div class="pagination-container">
-            <el-pagination
-              background
-              layout="total, sizes, prev, pager, next, jumper"
-              :total="filteredWorkOrders.length"
-              :current-page="currentPage"
-              :page-sizes="[10, 20, 50, 100]"
-              :page-size="pageSize"
-              @size-change="handleSizeChange"
-              @current-change="handleCurrentChange"
-            ></el-pagination>
-          </div>
-        </el-card>
-      </el-col>
-
-      <!-- 鍙充晶锛氱淮淇粺璁″拰澶囦欢鎺ㄨ崘 -->
-      <el-col :span="8">
-        <!-- 缁翠慨鍘嗗彶缁熻 -->
-        <el-card shadow="hover" style="margin-bottom: 20px;">
-          <template #header>
-            <div class="card-header">
-              <span>缁翠慨鍘嗗彶缁熻</span>
-            </div>
-          </template>
-          <div class="statistics-content">
-            <div class="stat-item">
-              <div class="stat-label">鏈湀瀹屾垚宸ュ崟</div>
-              <div class="stat-value">{{ monthlyCompleted }}</div>
-            </div>
-            <div class="stat-item">
-              <div class="stat-label">骞冲潎缁翠慨鏃堕暱</div>
-              <div class="stat-value">{{ averageRepairTime }}灏忔椂</div>
-            </div>
-            <div class="stat-item">
-              <div class="stat-label">璁惧鏁呴殰鐜�</div>
-              <div class="stat-value">{{ failureRate }}%</div>
-            </div>
-            <div class="stat-item">
-              <div class="stat-label">甯哥敤缁翠慨璁惧</div>
-              <div class="stat-value">{{ commonDevice }}</div>
-            </div>
-          </div>
-        </el-card>
-
-        <!-- 甯哥敤澶囦欢鍏宠仈鎺ㄨ崘 -->
-        <el-card shadow="hover">
-          <template #header>
-            <div class="card-header">
-              <span>甯哥敤澶囦欢鎺ㄨ崘</span>
-            </div>
-          </template>
-          <div class="spare-parts-content">
-            <el-table :data="spareParts" stripe style="width: 100%" size="small">
-              <el-table-column prop="name" label="澶囦欢鍚嶇О" width="120"></el-table-column>
-              <el-table-column prop="model" label="鍨嬪彿" width="100"></el-table-column>
-              <el-table-column prop="stock" label="搴撳瓨" width="80">
-                <template #default="scope">
-                  <el-tag :type="scope.row.stock < 10 ? 'danger' : 'success'">
-                    {{ scope.row.stock }}
-                  </el-tag>
-                </template>
-              </el-table-column>
-              <el-table-column prop="usageCount" label="浣跨敤娆℃暟" width="80"></el-table-column>
-            </el-table>
-          </div>
-        </el-card>
-      </el-col>
-    </el-row>
-
-    <!-- 鍒涘缓宸ュ崟瀵硅瘽妗� -->
-    <el-dialog v-model="createWorkOrderDialogVisible" title="鍒涘缓缁翠慨宸ュ崟" width="600px">
-      <el-form :model="workOrderForm" :rules="workOrderRules" ref="workOrderFormRef" label-width="100px">
-        <el-form-item label="璁惧" prop="deviceId">
-          <el-select v-model="workOrderForm.deviceId" placeholder="璇烽�夋嫨璁惧">
-            <el-option
-              v-for="device in devices"
-              :key="device.id"
-              :label="device.name"
-              :value="device.id"
-            ></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="鏁呴殰绫诲瀷" prop="faultType">
-          <el-input v-model="workOrderForm.faultType" placeholder="璇疯緭鍏ユ晠闅滅被鍨�"></el-input>
-        </el-form-item>
-        <el-form-item label="鏁呴殰鎻忚堪" prop="faultDescription">
-          <el-input v-model="workOrderForm.faultDescription" type="textarea" placeholder="璇疯缁嗘弿杩版晠闅滄儏鍐�" :rows="3"></el-input>
-        </el-form-item>
-        <el-form-item label="浼樺厛绾�" prop="priority">
-          <el-select v-model="workOrderForm.priority" placeholder="璇烽�夋嫨浼樺厛绾�">
-            <el-option label="楂�" value="high"></el-option>
-            <el-option label="涓�" value="medium"></el-option>
-            <el-option label="浣�" value="low"></el-option>
-          </el-select>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="createWorkOrderDialogVisible = false">鍙栨秷</el-button>
-          <el-button type="primary" @click="handleCreateWorkOrder">纭畾</el-button>
-        </span>
-      </template>
-    </el-dialog>
-
-    <!-- 缂栬緫宸ュ崟瀵硅瘽妗� -->
-    <el-dialog v-model="editWorkOrderDialogVisible" title="缂栬緫缁翠慨宸ュ崟" width="600px">
-      <el-form :model="workOrderForm" :rules="workOrderRules" ref="workOrderFormRef" label-width="100px">
-        <el-form-item label="宸ュ崟缂栧彿" disabled>
-          <el-input v-model="workOrderForm.orderNo"></el-input>
-        </el-form-item>
-        <el-form-item label="璁惧" disabled>
-          <el-input v-model="workOrderForm.deviceName"></el-input>
-        </el-form-item>
-        <el-form-item label="鏁呴殰绫诲瀷" disabled>
-          <el-input v-model="workOrderForm.faultType"></el-input>
-        </el-form-item>
-        <el-form-item label="璐熻矗浜�" prop="assignee">
-          <el-select v-model="workOrderForm.assignee" placeholder="璇烽�夋嫨璐熻矗浜�">
-            <el-option
-              v-for="user in users"
-              :key="user.id"
-              :label="user.name"
-              :value="user.name"
-            ></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="缁翠慨鐘舵��" prop="status">
-          <el-select v-model="workOrderForm.status" placeholder="璇烽�夋嫨鐘舵��">
-            <el-option label="寰呭鐞�" value="pending"></el-option>
-            <el-option label="澶勭悊涓�" value="processing"></el-option>
-            <el-option label="宸插畬鎴�" value="completed"></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="缁翠慨缁撴灉" prop="repairResult">
-          <el-input v-model="workOrderForm.repairResult" type="textarea" placeholder="璇峰~鍐欑淮淇粨鏋�" :rows="3"></el-input>
-        </el-form-item>
-        <el-form-item label="澶囦欢浣跨敤" prop="usedParts">
-          <el-input v-model="workOrderForm.usedParts" type="textarea" placeholder="璇峰~鍐欎娇鐢ㄧ殑澶囦欢" :rows="2"></el-input>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="editWorkOrderDialogVisible = false">鍙栨秷</el-button>
-          <el-button type="primary" @click="handleEditWorkOrder">纭畾</el-button>
-        </span>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-import { ref, computed } from 'vue'
-
-// 璁惧鍒楄〃
-const devices = ref([
-  { id: 'D001', name: '绌哄帇鏈篈-001' },
-  { id: 'D002', name: '鍐峰嵈濉擝-002' },
-  { id: 'D003', name: '姘存车C-003' },
-  { id: 'D004', name: '鍙戠數鏈篋-004' },
-  { id: 'D005', name: '鍙樺帇鍣‥-005' }
-])
-
-// 鐢ㄦ埛鍒楄〃锛堢敤浜庡垎閰嶈礋璐d汉锛�
-const users = ref([
-  { id: 'U001', name: '寮犱笁' },
-  { id: 'U002', name: '鏉庡洓' },
-  { id: 'U003', name: '鐜嬩簲' },
-  { id: 'U004', name: '璧靛叚' }
-])
-
-// 宸ュ崟鍒楄〃鏁版嵁
-const workOrders = ref([
-  {
-    id: 1,
-    orderNo: 'WO20241216001',
-    deviceId: 'D001',
-    deviceName: '绌哄帇鏈篈-001',
-    faultType: '鍘嬪姏寮傚父',
-    faultDescription: '璁惧杩愯鏃跺帇鍔涜秴杩囪瀹氶槇鍊硷紝浼存湁寮傚父鍣煶',
-    priority: 'high',
-    assignee: '',
-    status: 'pending',
-    repairResult: '',
-    usedParts: '',
-    createTime: '2024-12-16 14:32:15',
-    startTime: '',
-    endTime: ''
-  },
-  {
-    id: 2,
-    orderNo: 'WO20241216002',
-    deviceId: 'D002',
-    deviceName: '鍐峰嵈濉擝-002',
-    faultType: '娓╁害杩囬珮',
-    faultDescription: '鍐峰嵈濉斿嚭姘存俯搴﹁秴杩囪瀹氬�硷紝鍐峰嵈鏁堟灉涓嶄匠',
-    priority: 'medium',
-    assignee: '寮犱笁',
-    status: 'processing',
-    repairResult: '',
-    usedParts: '',
-    createTime: '2024-12-16 14:30:45',
-    startTime: '2024-12-16 15:00:00',
-    endTime: ''
-  },
-  {
-    id: 3,
-    orderNo: 'WO20241215001',
-    deviceId: 'D003',
-    deviceName: '姘存车C-003',
-    faultType: '鎸姩杩囧ぇ',
-    faultDescription: '姘存车杩愯鏃舵尟鍔ㄥ�艰秴杩囨爣鍑嗭紝鍙兘褰卞搷璁惧瀵垮懡',
-    priority: 'medium',
-    assignee: '鏉庡洓',
-    status: 'completed',
-    repairResult: '鏇存崲浜嗘按娉佃酱鎵匡紝璋冩暣浜嗚仈杞村櫒锛屾尟鍔ㄥ�兼仮澶嶆甯�',
-    usedParts: '杞存壙脳2锛岃仈杞村櫒脳1',
-    createTime: '2024-12-15 08:30:00',
-    startTime: '2024-12-15 09:00:00',
-    endTime: '2024-12-15 10:45:00'
-  },
-  {
-    id: 4,
-    orderNo: 'WO20241214001',
-    deviceId: 'D004',
-    deviceName: '鍙戠數鏈篋-004',
-    faultType: '鐢垫祦寮傚父',
-    faultDescription: '鍙戠數鏈鸿繍琛屾椂鐢垫祦娉㈠姩杈冨ぇ锛屽彲鑳藉瓨鍦ㄧ煭璺闄�',
-    priority: 'high',
-    assignee: '鐜嬩簲',
-    status: 'completed',
-    repairResult: '妫�鏌ュ苟淇浜嗗彂鐢垫満缁曠粍鐭矾闂锛岀數娴佹仮澶嶆甯�',
-    usedParts: '缁濈紭鏉愭枡脳1锛屽绾棵�5绫�',
-    createTime: '2024-12-14 14:20:00',
-    startTime: '2024-12-14 14:30:00',
-    endTime: '2024-12-14 16:15:00'
-  },
-  {
-    id: 5,
-    orderNo: 'WO20241213001',
-    deviceId: 'D005',
-    deviceName: '鍙樺帇鍣‥-005',
-    faultType: '鐢靛帇娉㈠姩',
-    faultDescription: '鍙樺帇鍣ㄨ緭鍑虹數鍘嬫尝鍔ㄨ緝澶э紝褰卞搷涓嬫父璁惧杩愯',
-    priority: 'low',
-    assignee: '璧靛叚',
-    status: 'completed',
-    repairResult: '璋冩暣浜嗗彉鍘嬪櫒鍒嗘帴寮�鍏筹紝鐢靛帇绋冲畾鍦ㄦ甯歌寖鍥村唴',
-    usedParts: '',
-    createTime: '2024-12-13 09:15:00',
-    startTime: '2024-12-13 10:00:00',
-    endTime: '2024-12-13 11:30:00'
-  }
-])
-
-// 褰撳墠婵�娲荤殑鏍囩椤碉紙宸ュ崟鐘舵�侊級
-const activeTab = ref('pending')
-
-// 鍒嗛〉鏁版嵁
-const currentPage = ref(1)
-const pageSize = ref(10)
-
-// 瀵硅瘽妗嗙姸鎬�
-const createWorkOrderDialogVisible = ref(false)
-const editWorkOrderDialogVisible = ref(false)
-
-// 宸ュ崟琛ㄥ崟鏁版嵁
-const workOrderForm = ref({
-  id: '',
-  orderNo: '',
-  deviceId: '',
-  deviceName: '',
-  faultType: '',
-  faultDescription: '',
-  priority: 'medium',
-  assignee: '',
-  status: 'pending',
-  repairResult: '',
-  usedParts: '',
-  createTime: '',
-  startTime: '',
-  endTime: ''
-})
-
-// 琛ㄥ崟楠岃瘉瑙勫垯
-const workOrderRules = ref({
-  deviceId: [{ required: true, message: '璇烽�夋嫨璁惧', trigger: 'change' }],
-  faultType: [{ required: true, message: '璇疯緭鍏ユ晠闅滅被鍨�', trigger: 'blur' }],
-  faultDescription: [{ required: true, message: '璇疯缁嗘弿杩版晠闅滄儏鍐�', trigger: 'blur' }],
-  priority: [{ required: true, message: '璇烽�夋嫨浼樺厛绾�', trigger: 'change' }],
-  assignee: [{ required: true, message: '璇烽�夋嫨璐熻矗浜�', trigger: 'change' }],
-  status: [{ required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change' }]
-})
-
-// 琛ㄥ崟寮曠敤
-const workOrderFormRef = ref(null)
-
-// 绛涢�夊悗鐨勫伐鍗曞垪琛�
-const filteredWorkOrders = computed(() => {
-  return workOrders.value.filter(order => order.status === activeTab.value)
-})
-
-// 缁翠慨缁熻鏁版嵁
-const monthlyCompleted = ref(28)
-const averageRepairTime = ref(2.5)
-const failureRate = ref(3.2)
-const commonDevice = ref('绌哄帇鏈篈-001')
-
-// 甯哥敤澶囦欢鎺ㄨ崘
-const spareParts = ref([
-  { id: 1, name: '杞存壙', model: '6308', stock: 15, usageCount: 23 },
-  { id: 2, name: '瀵嗗皝浠�', model: 'MS-25', stock: 8, usageCount: 18 },
-  { id: 3, name: '鑱旇酱鍣�', model: 'CL-50', stock: 5, usageCount: 12 },
-  { id: 4, name: '浼犳劅鍣�', model: 'TS-100', stock: 3, usageCount: 15 },
-  { id: 5, name: '娑︽粦娌�', model: 'L-46', stock: 20, usageCount: 30 }
-])
-
-// 鏄剧ず鍒涘缓宸ュ崟瀵硅瘽妗�
-const showCreateWorkOrderDialog = () => {
-  // 閲嶇疆琛ㄥ崟
-  workOrderForm.value = {
-    id: '',
-    orderNo: '',
-    deviceId: '',
-    deviceName: '',
-    faultType: '',
-    faultDescription: '',
-    priority: 'medium',
-    assignee: '',
-    status: 'pending',
-    repairResult: '',
-    usedParts: '',
-    createTime: '',
-    startTime: '',
-    endTime: ''
-  }
-  createWorkOrderDialogVisible.value = true
-}
-
-// 鏄剧ず缂栬緫宸ュ崟瀵硅瘽妗�
-const showEditWorkOrderDialog = (order) => {
-  workOrderForm.value = { ...order }
-  editWorkOrderDialogVisible.value = true
-}
-
-// 澶勭悊宸ュ崟鐐瑰嚮
-const handleWorkOrderClick = (row) => {
-  // 鍙互鍦ㄨ繖閲屾坊鍔犳煡鐪嬪伐鍗曡鎯呯殑閫昏緫
-}
-
-// 澶勭悊鏍囩椤靛垏鎹�
-const handleTabChange = (tab) => {
-  activeTab.value = tab
-  currentPage.value = 1 // 鍒囨崲鏍囩椤垫椂閲嶇疆椤电爜
-}
-
-// 澶勭悊鍒涘缓宸ュ崟
-const handleCreateWorkOrder = () => {
-  // 妯℃嫙鍒涘缓宸ュ崟
-  const newOrder = {
-    ...workOrderForm.value,
-    id: workOrders.value.length + 1,
-    orderNo: `WO${new Date().getFullYear()}${String(new Date().getMonth() + 1).padStart(2, '0')}${String(new Date().getDate()).padStart(2, '0')}${String(workOrders.value.length + 1).padStart(3, '0')}`,
-    deviceName: devices.value.find(d => d.id === workOrderForm.value.deviceId)?.name || '',
-    createTime: new Date().toLocaleString(),
-    status: 'pending'
-  }
-  workOrders.value.unshift(newOrder)
-  createWorkOrderDialogVisible.value = false
-  ElMessage.success('宸ュ崟鍒涘缓鎴愬姛')
-}
-
-// 澶勭悊缂栬緫宸ュ崟
-const handleEditWorkOrder = () => {
-  // 妯℃嫙缂栬緫宸ュ崟
-  const index = workOrders.value.findIndex(order => order.id === workOrderForm.value.id)
-  if (index !== -1) {
-    // 濡傛灉鐘舵�佷粠寰呭鐞嗗彉涓哄鐞嗕腑锛岃缃紑濮嬫椂闂�
-    if (workOrders.value[index].status === 'pending' && workOrderForm.value.status === 'processing') {
-      workOrderForm.value.startTime = new Date().toLocaleString()
-    }
-    // 濡傛灉鐘舵�佷粠澶勭悊涓彉涓哄凡瀹屾垚锛岃缃粨鏉熸椂闂�
-    if (workOrders.value[index].status === 'processing' && workOrderForm.value.status === 'completed') {
-      workOrderForm.value.endTime = new Date().toLocaleString()
-    }
-    workOrders.value[index] = { ...workOrderForm.value }
-    editWorkOrderDialogVisible.value = false
-    ElMessage.success('宸ュ崟缂栬緫鎴愬姛')
-  }
-}
-
-// 澶勭悊鍒犻櫎宸ュ崟
-const handleDeleteWorkOrder = (id) => {
-  ElMessageBox.confirm('纭畾瑕佸垹闄よ宸ュ崟鍚楋紵', '鎻愮ず', {
-    confirmButtonText: '纭畾',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning'
-  }).then(() => {
-    // 妯℃嫙鍒犻櫎宸ュ崟
-    const index = workOrders.value.findIndex(order => order.id === id)
-    if (index !== -1) {
-      workOrders.value.splice(index, 1)
-      ElMessage.success('宸ュ崟鍒犻櫎鎴愬姛')
-    }
-  }).catch(() => {
-    // 鍙栨秷鍒犻櫎
-  })
-}
-
-// 澶勭悊鍒嗛〉澶у皬鍙樺寲
-const handleSizeChange = (size) => {
-  pageSize.value = size
-  currentPage.value = 1
-}
-
-// 澶勭悊褰撳墠椤靛彉鍖�
-const handleCurrentChange = (current) => {
-  currentPage.value = current
-}
-</script>
-
-<style scoped>
-.maintenance-management-container {
-  padding: 20px;
-  background-color: #f5f7fa;
-  min-height: 100vh;
-}
-
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-.header-buttons {
-  display: flex;
-  gap: 10px;
-}
-
-.pagination-container {
-  display: flex;
-  justify-content: flex-end;
-  margin-top: 20px;
-}
-
-.statistics-content {
-  display: grid;
-  grid-template-columns: 1fr 1fr;
-  gap: 20px;
-  padding: 10px 0;
-}
-
-.stat-item {
-  text-align: center;
-  padding: 15px;
-  background-color: #fafafa;
-  border-radius: 4px;
-}
-
-.stat-label {
-  font-size: 14px;
-  color: #606266;
-  margin-bottom: 10px;
-}
-
-.stat-value {
-  font-size: 24px;
-  font-weight: bold;
-  color: #303133;
-}
-
-.spare-parts-content {
-  padding: 10px 0;
-}
-
-:deep(.el-icon-plus) {
-  margin-right: 5px;
-}
-</style>
\ No newline at end of file
diff --git a/src/views/maintenance/Modal/MaintenanceModal.vue b/src/views/maintenance/Modal/MaintenanceModal.vue
new file mode 100644
index 0000000..f41fb54
--- /dev/null
+++ b/src/views/maintenance/Modal/MaintenanceModal.vue
@@ -0,0 +1,60 @@
+<template>
+  <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr">
+    <MaintenanceForm ref="maintenanceFormRef" />
+    <template #footer>
+			<el-button type="primary" @click="sendForm" :loading="loading">
+				{{ modalOptions.confirmText }}
+			</el-button>
+      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import MaintenanceForm from "../Form/MaintenanceForm.vue";
+import { useModal } from "../../../hooks/useModal";
+import { addMaintenance } from "../../../api/equipmentManagement/upkeep";
+
+defineOptions({
+  name: "淇濆吇妯℃�佹",
+});
+
+const maintenanceFormRef = ref();
+const emits = defineEmits(["ok"]);
+
+const {
+  id,
+  visible,
+  loading,
+  openModal,
+  modalOptions,
+  handleConfirm,
+  closeModal,
+} = useModal({ title: "璁惧淇濆吇" });
+
+/**
+ * @desc 淇濆瓨淇濆吇
+ */
+const sendForm = async () => {
+  loading.value = true;
+  const form = await maintenanceFormRef.value.getForm();
+  const { code } = await addMaintenance({ id: id.value, ...form });
+  if (code == 200) {
+    emits("ok");
+    maintenanceFormRef.value.resetForm();
+    closeModal();
+  }
+  loading.value = false;
+};
+
+const open = async (id, row) => {
+  openModal(id);
+  await nextTick();
+  maintenanceFormRef.value.setForm(row);
+};
+defineExpose({
+  open,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/maintenance/Modal/PlanModal.vue b/src/views/maintenance/Modal/PlanModal.vue
new file mode 100644
index 0000000..07d7e8a
--- /dev/null
+++ b/src/views/maintenance/Modal/PlanModal.vue
@@ -0,0 +1,76 @@
+<template>
+  <el-dialog
+    v-model="visible"
+    :title="modalOptions.title"
+    width="30%"
+    @close="close"
+  >
+    <PlanForm ref="planFormRef" :id="id"></PlanForm>
+    <template #footer>
+			<el-button type="primary" @click="sendForm" :loading="loading">
+				{{ modalOptions.confirmText }}
+			</el-button>
+      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useModal } from "../../../hooks/useModal";
+import PlanForm from "../Form/PlanForm";
+import {
+  addUpkeep,
+  editUpkeep,
+  getUpkeepById,
+} from "../../../api/equipmentManagement/upkeep";
+import { ElMessage } from "element-plus";
+
+defineOptions({
+  name: "璁惧淇濆吇鏂板璁″垝",
+});
+
+const emits = defineEmits(["ok"]);
+const planFormRef = ref();
+const {
+  id,
+  visible,
+  loading,
+  openModal,
+  modalOptions,
+  handleConfirm,
+  closeModal,
+} = useModal({ title: "璁惧淇濆吇璁″垝" });
+
+const openEdit = async (id) => {
+  const { data } = await getUpkeepById(id);
+  openModal(id);
+  await nextTick();
+  await planFormRef.value.setForm(data);
+};
+
+const sendForm = async () => {
+  loading.value = true;
+  const form = await planFormRef.value.getForm();
+  const { code } = id.value
+    ? await editUpkeep({ id: unref(id), ...form })
+    : await addUpkeep(form);
+  if (code == 200) {
+    ElMessage.success(`${id ? "缂栬緫" : "鏂板"}璁″垝鎴愬姛`);
+    closeModal();
+    emits("ok");
+  }
+  loading.value = false;
+};
+
+const close = () => {
+  planFormRef.value.resetForm();
+  closeModal();
+};
+
+defineExpose({
+  openModal,
+  openEdit,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/maintenance/index.vue b/src/views/maintenance/index.vue
new file mode 100644
index 0000000..53c7181
--- /dev/null
+++ b/src/views/maintenance/index.vue
@@ -0,0 +1,315 @@
+<template>
+  <div class="app-container">
+    <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 class="table_list">
+      <div class="actions">
+        <el-text class="mx-1" size="large">璁惧淇濆吇</el-text>
+        <div>
+          <el-button
+            type="primary"
+            icon="Plus"
+            :disabled="multipleList.length !== 1"
+            @click="addMaintain"
+          >
+            鏂板淇濆吇
+          </el-button>
+          <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>
+      <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>
+          <!-- <el-tag v-if="row.maintenanceResult === 1" type="success">
+            瀹屽ソ
+          </el-tag>
+          <el-tag v-if="row.maintenanceResult === 0" type="danger">
+            缁翠慨
+          </el-tag> -->
+        </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
+            icon="editPen"
+            @click="editPlan(row.id)"
+          >
+            缂栬緫
+          </el-button>
+          <el-button
+            type="danger"
+            text
+            icon="delete"
+            @click="delRepairByIds(row.id)"
+          >
+            鍒犻櫎
+          </el-button>
+        </template>
+      </PIMTable>
+    </div>
+    <PlanModal ref="planModalRef" @ok="getTableData" />
+    <MaintenanceModal ref="maintainModalRef" @ok="getTableData" />
+  </div>
+</template>
+
+<script setup>
+import { usePaginationApi } from "../../hooks/usePaginationApi";
+import { getUpkeepPage, delUpkeep } from "../../api/equipmentManagement/upkeep";
+import { onMounted, getCurrentInstance } from "vue";
+import PlanModal from "./Modal/PlanModal.vue";
+import MaintenanceModal from "./Modal/MaintenanceModal.vue";
+import dayjs from "dayjs";
+import { ElMessageBox, ElMessage } from "element-plus";
+
+defineOptions({
+  name: "璁惧淇濆吇",
+});
+
+const { proxy } = getCurrentInstance();
+
+// 璁″垝寮圭獥鎺у埗鍣�
+const planModalRef = ref();
+// 淇濆吇寮圭獥鎺у埗鍣�
+const maintainModalRef = ref();
+
+// 琛ㄦ牸澶氶�夋閫変腑椤�
+const multipleList = ref([]);
+
+// 澶氶�夊悗鍋氫粈涔�
+const handleSelectionChange = (selectionList) => {
+  multipleList.value = selectionList;
+};
+
+// 琛ㄦ牸閽╁瓙
+const {
+  filters,
+  columns,
+  dataList,
+  pagination,
+  getTableData,
+  resetFilters,
+  onCurrentChange,
+} = usePaginationApi(getUpkeepPage, {
+  deviceName: undefined,
+  maintenancePlanTime: undefined,
+  maintenanceActuallyTime: undefined,
+  maintenanceActuallyName: undefined,
+}, [
+  {
+    label: "璁惧鍚嶇О",
+    align: "center",
+    prop: "deviceName",
+  },
+  {
+    label: "瑙勬牸鍨嬪彿",
+    align: "center",
+    prop: "deviceModel",
+  },
+  {
+    label: "璁″垝淇濆吇鏃ユ湡",
+    align: "center",
+    prop: "maintenancePlanTime",
+    formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"),
+  },
+  {
+    label: "褰曞叆浜�",
+    align: "center",
+    prop: "createUserName",
+  },
+  // {
+  //   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: "200px",
+  },
+]);
+// type == 1瀹為檯淇濆吇鏃堕棿 2璁″垝淇濆吇鏃堕棿
+const handleDateChange = (value,type) => {
+  filters.maintenanceActuallyTimeReq = null
+  filters.maintenancePlanTimeReq = null
+  if(type === 1){
+    if (value) {
+      filters.maintenanceActuallyTimeReq = dayjs(value).format("YYYY-MM-DD");
+    }
+  }else{
+    if (value) {
+      filters.maintenancePlanTimeReq = dayjs(value).format("YYYY-MM-DD");
+    }
+  }
+  getTableData();
+};
+
+// 鏂板淇濆吇
+const addMaintain = () => {
+  const row = multipleList.value[0];
+  maintainModalRef.value.open(row.id, row);
+};
+
+// 鏂板璁″垝
+const addPlan = () => {
+  planModalRef.value.openModal();
+};
+
+// 缂栬緫璁″垝
+const editPlan = (id) => {
+  planModalRef.value.openEdit(id);
+};
+
+const changePage = ({ page, limit }) => {
+	pagination.currentPage = page;
+	pagination.pageSize = limit;
+	onCurrentChange(page);
+};
+
+// 鍗曡鍒犻櫎
+const delRepairByIds = async (ids) => {
+  ElMessageBox.confirm("纭鍒犻櫎鎶ヤ慨鏁版嵁, 姝ゆ搷浣滀笉鍙��?", "璀﹀憡", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(async () => {
+    const { code } = await delUpkeep(ids);
+    if (code === 200) {
+      ElMessage.success("鍒犻櫎鎴愬姛");
+      getTableData();
+    }
+  });
+};
+
+// 瀵煎嚭
+const handleOut = () => {
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+    .then(() => {
+      proxy.download("/device/maintenance/export", {}, "璁惧淇濆吇.xlsx");
+    })
+    .catch(() => {
+      ElMessage.info("宸插彇娑�");
+    });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.table_list {
+  margin-top: unset;
+}
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 10px;
+}
+</style>
diff --git a/src/views/measurementEquipment/components/calibrationDia.vue b/src/views/measurementEquipment/components/calibrationDia.vue
new file mode 100644
index 0000000..4141f43
--- /dev/null
+++ b/src/views/measurementEquipment/components/calibrationDia.vue
@@ -0,0 +1,268 @@
+<template>
+	<div>
+		<el-dialog
+			v-model="dialogFormVisible"
+			title="璁¢噺鍣ㄥ叿"
+			width="50%"
+			@close="closeDia"
+		>
+			<el-form
+				:model="form"
+				label-width="140px"
+				label-position="top"
+				:rules="rules"
+				ref="formRef"
+			>
+				<el-row :gutter="30">
+					<el-col :span="12">
+						<el-form-item label="璁¢噺鍣ㄥ叿缂栧彿锛�" prop="customerName">
+							<el-input
+								v-model="form.code"
+								placeholder="璇疯緭鍏�"
+								clearable
+								disabled
+							/>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="璁¢噺鍣ㄥ叿鍚嶇О锛�" prop="proDesc">
+							<el-input
+								v-model="form.name"
+								placeholder="璇疯緭鍏�"
+								clearable
+								disabled
+							/>
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<el-row :gutter="30">
+					<el-col :span="12">
+						<el-form-item label="妫�瀹氭棩鏈燂細" prop="recordDate">
+							<el-date-picker
+								style="width: 100%"
+								v-model="form.recordDate"
+								value-format="YYYY-MM-DD"
+								format="YYYY-MM-DD"
+								type="date"
+								placeholder="璇烽�夋嫨"
+								clearable
+							/>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="鏈夋晥鏈燂細" prop="valid">
+							<el-input
+								v-model="form.valid"
+								placeholder="璇疯緭鍏�"
+								clearable
+							>
+								<template #append>鏃�</template>
+							</el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<el-row :gutter="30">
+					<el-col :span="12">
+						<el-form-item label="褰曞叆浜猴細" prop="userId">
+							<el-select
+								v-model="form.userId"
+								placeholder="璇烽�夋嫨"
+                filterable
+                default-first-option
+                :reserve-keyword="false"
+								clearable
+							>
+								<el-option
+									v-for="item in userList"
+									:key="item.userId"
+									:label="item.nickName"
+									:value="item.userId"
+								></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="褰曞叆鏃ユ湡锛�" prop="entryDate">
+							<el-date-picker
+								style="width: 100%"
+								v-model="form.entryDate"
+								value-format="YYYY-MM-DD"
+								format="YYYY-MM-DD"
+								type="date"
+								placeholder="璇烽�夋嫨"
+								clearable
+							/>
+						</el-form-item>
+					</el-col>
+				</el-row>
+<!--				<el-row :gutter="30">-->
+<!--					<el-col :span="24">-->
+<!--						<el-form-item label="闄勪欢鏉愭枡锛�" prop="remark">-->
+<!--							<el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload-->
+<!--												 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"-->
+<!--												 :on-success="handleUploadSuccess" :on-remove="handleRemove">-->
+<!--								<el-button type="primary" v-if="operationType !== 'view'">涓婁紶</el-button>-->
+<!--								<template #tip v-if="operationType !== 'view'">-->
+<!--									<div class="el-upload__tip">-->
+<!--										鏂囦欢鏍煎紡鏀寔-->
+<!--										doc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z-->
+<!--									</div>-->
+<!--								</template>-->
+<!--							</el-upload>-->
+<!--						</el-form-item>-->
+<!--					</el-col>-->
+<!--				</el-row>-->
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitForm">纭</el-button>
+					<el-button @click="closeDia">鍙栨秷</el-button>
+				</div>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script setup>
+import {ref} from "vue";
+import useUserStore from "../../../store/modules/user.js";
+import {userListNoPageByTenantId} from "../../../api/system/user.js";
+import {afterSalesServiceAdd, afterSalesServiceUpdate} from "@/api/customerService/index.js";
+import {getToken} from "../../../utils/auth.js";
+import {ledgerRecordUpdate, ledgerRecordVerifying} from "../../../api/equipmentManagement/calibration.js";
+import {delLedgerFile} from "../../../api/salesManagement/salesLedger.js";
+const { proxy } = getCurrentInstance()
+const emit = defineEmits(['close'])
+const dialogFormVisible = ref(false);
+const operationType = ref('')
+const userStore = useUserStore();
+
+const data = reactive({
+	form: {
+		code: "",
+		name: "",
+		valid: "",
+		recordDate: "",
+		userId: "",
+		entryDate: "",
+    tempFileIds: []
+	},
+	rules: {
+		code: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+		name: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+		valid: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+		recordDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+		userId: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+		entryDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+	}
+})
+const { form, rules } = toRefs(data);
+const userList = ref([])
+const fileList = ref([]);
+const upload = reactive({
+	// 涓婁紶鐨勫湴鍧�
+	url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
+	// 璁剧疆涓婁紶鐨勮姹傚ご閮�
+	headers: { Authorization: "Bearer " + getToken() },
+});
+
+// 鎵撳紑寮规
+const openDialog = (type, row) => {
+  console.log(row)
+	operationType.value = type;
+	dialogFormVisible.value = true;
+	userListNoPageByTenantId().then((res) => {
+		userList.value = res.data;
+	});
+  fileList.value = []
+	if(type !== "add"){
+	  form.value.tempFileIds = [];
+  }
+	if (type === "edit") {
+		form.value.valid = row.valid;
+		form.value.recordDate = row.recordDate;
+    fileList.value = row.commonFiles;
+	}
+	if(type === "add"){
+    fileList.value = row.commonFiles;
+  }
+
+	form.value.id = row.id;
+	form.value.code = row.code;
+	form.value.name = row.name;
+	form.value.userId = userStore.id;
+	form.value.entryDate = getCurrentDate();
+}
+
+// 涓婁紶鍓嶆牎妫�
+function handleBeforeUpload(file) {
+	proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
+	return true;
+}
+// 涓婁紶澶辫触
+function handleUploadError(err) {
+	proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
+	proxy.$modal.closeLoading();
+}
+// 涓婁紶鎴愬姛鍥炶皟
+function handleUploadSuccess(res, file, uploadFiles) {
+	proxy.$modal.closeLoading();
+	if (res.code === 200) {
+		file.tempId = res.data.tempId;
+		form.value.tempFileIds.push(file.tempId);
+		proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+	} else {
+		proxy.$modal.msgError(res.msg);
+		proxy.$refs.fileUpload.handleRemove(file);
+	}
+}
+// 绉婚櫎鏂囦欢
+function handleRemove(file) {
+	if (operationType.value === "edit") {
+		let ids = [];
+		ids.push(file.id);
+		delLedgerFile(ids).then((res) => {
+			proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+		});
+	}
+}
+
+const submitForm = () => {
+	proxy.$refs["formRef"].validate(valid => {
+		if (valid) {
+			if (operationType.value === "verifying") {
+				ledgerRecordVerifying(form.value).then(response => {
+					proxy.$modal.msgSuccess("妫�瀹氭牎鍑嗘垚鍔�")
+					closeDia()
+				})
+			} else {
+				ledgerRecordUpdate(form.value).then(response => {
+					proxy.$modal.msgSuccess("淇敼鎴愬姛")
+					closeDia()
+				})
+			}
+		}
+	})
+}
+// 鍏抽棴寮规
+const closeDia = () => {
+	proxy.resetForm("formRef");
+	dialogFormVisible.value = false;
+	emit('close')
+};
+// 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
+function getCurrentDate() {
+	const today = new Date();
+	const year = today.getFullYear();
+	const month = String(today.getMonth() + 1).padStart(2, "0"); // 鏈堜唤浠�0寮�濮�
+	const day = String(today.getDate()).padStart(2, "0");
+	return `${year}-${month}-${day}`;
+}
+defineExpose({
+	openDialog,
+});
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file
diff --git a/src/views/measurementEquipment/components/formDia.vue b/src/views/measurementEquipment/components/formDia.vue
new file mode 100644
index 0000000..55db4ca
--- /dev/null
+++ b/src/views/measurementEquipment/components/formDia.vue
@@ -0,0 +1,254 @@
+<template>
+  <div>
+    <el-dialog
+        v-model="dialogFormVisible"
+        title="璁¢噺鍣ㄥ叿"
+        width="50%"
+        @close="closeDia"
+    >
+			<el-form
+				:model="form"
+				label-width="140px"
+				label-position="top"
+				:rules="rules"
+				ref="formRef"
+			>
+				<el-row :gutter="30">
+					<el-col :span="12">
+						<el-form-item label="璁¢噺鍣ㄥ叿缂栧彿锛�" prop="code">
+							<el-input
+								v-model="form.code"
+								placeholder="璇疯緭鍏�"
+								clearable
+							/>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="璁¢噺鍣ㄥ叿鍚嶇О锛�" prop="name">
+							<el-input
+								v-model="form.name"
+								placeholder="璇疯緭鍏�"
+								clearable
+							/>
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<el-row :gutter="30">
+					<el-col :span="12">
+						<el-form-item label="瑙勬牸鍨嬪彿锛�" prop="model">
+							<el-input
+								v-model="form.model"
+								placeholder="璇疯緭鍏�"
+								clearable
+							/>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="棰勮涓嬫妫�瀹氭棩鏈燂細" prop="nextDate">
+							<el-date-picker
+								style="width: 100%"
+								v-model="form.nextDate"
+								value-format="YYYY-MM-DD"
+								format="YYYY-MM-DD"
+								type="date"
+								placeholder="璇烽�夋嫨"
+								clearable
+							/>
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<el-row :gutter="30">
+					<el-col :span="12">
+						<el-form-item label="褰曞叆浜猴細" prop="userId">
+							<el-select
+								v-model="form.userId"
+								placeholder="璇烽�夋嫨"
+								clearable
+                filterable
+                default-first-option
+                :reserve-keyword="false"
+							>
+								<el-option
+									v-for="item in userList"
+									:key="item.userId"
+									:label="item.nickName"
+									:value="item.userId"
+								></el-option>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="褰曞叆鏃ユ湡锛�" prop="recordDate">
+							<el-date-picker
+								style="width: 100%"
+								v-model="form.recordDate"
+								value-format="YYYY-MM-DD"
+								format="YYYY-MM-DD"
+								type="date"
+								placeholder="璇烽�夋嫨"
+								clearable
+							/>
+						</el-form-item>
+					</el-col>
+				</el-row>
+<!--				<el-row :gutter="30">-->
+<!--					<el-col :span="24">-->
+<!--						<el-form-item label="闄勪欢鏉愭枡锛�" prop="remark">-->
+<!--							<el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload-->
+<!--												 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"-->
+<!--												 :on-success="handleUploadSuccess" :on-remove="handleRemove">-->
+<!--								<el-button type="primary" v-if="operationType !== 'view'">涓婁紶</el-button>-->
+<!--								<template #tip v-if="operationType !== 'view'">-->
+<!--									<div class="el-upload__tip">-->
+<!--										鏂囦欢鏍煎紡鏀寔-->
+<!--										doc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z-->
+<!--									</div>-->
+<!--								</template>-->
+<!--							</el-upload>-->
+<!--						</el-form-item>-->
+<!--					</el-col>-->
+<!--				</el-row>-->
+			</el-form>
+			<template #footer>
+				<div class="dialog-footer">
+					<el-button type="primary" @click="submitForm">纭</el-button>
+					<el-button @click="closeDia">鍙栨秷</el-button>
+				</div>
+			</template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {ref} from "vue";
+import useUserStore from "../../../store/modules/user.js";
+import {userListNoPageByTenantId} from "../../../api/system/user.js";
+import {afterSalesServiceAdd, afterSalesServiceUpdate} from "@/api/customerService/index.js";
+import {getToken} from "../../../utils/auth.js";
+import {measuringInstrumentAdd, measuringInstrumentUpdate} from "../../../api/equipmentManagement/measurementEquipment.js";
+const { proxy } = getCurrentInstance()
+const emit = defineEmits(['close'])
+const dialogFormVisible = ref(false);
+const operationType = ref('')
+const userStore = useUserStore();
+
+const data = reactive({
+	form: {
+		code: "",
+		name: "",
+		model: "",
+		validDate: "",
+		nextDate: "",
+		userId: "",
+		recordDate: "",
+    tempFileIds: []
+	},
+	rules: {
+		code: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+		name: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+		model: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+		validDate: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+		nextDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+		userId: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+		recordDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+	}
+})
+const { form, rules } = toRefs(data);
+const userList = ref([])
+const fileList = ref([]);
+const upload = reactive({
+	// 涓婁紶鐨勫湴鍧�
+	url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
+	// 璁剧疆涓婁紶鐨勮姹傚ご閮�
+	headers: { Authorization: "Bearer " + getToken() },
+});
+
+// 鎵撳紑寮规
+const openDialog = (type, row) => {
+  operationType.value = type;
+  dialogFormVisible.value = true;
+	fileList.value = []
+	form.value.userId = userStore.id;
+	form.value.recordDate = getCurrentDate();
+	userListNoPageByTenantId().then((res) => {
+		userList.value = res.data;
+	});
+	if (type === "edit") {
+		form.value = {...row}
+	}
+}
+
+// 涓婁紶鍓嶆牎妫�
+function handleBeforeUpload(file) {
+	proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
+	return true;
+}
+// 涓婁紶澶辫触
+function handleUploadError(err) {
+	proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
+	proxy.$modal.closeLoading();
+}
+// 涓婁紶鎴愬姛鍥炶皟
+function handleUploadSuccess(res, file, uploadFiles) {
+	proxy.$modal.closeLoading();
+	if (res.code === 200) {
+		file.tempId = res.data.tempId;
+    form.value.tempFileIds.push(res.data.tempId)
+		proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+	} else {
+		proxy.$modal.msgError(res.msg);
+		proxy.$refs.fileUpload.handleRemove(file);
+	}
+}
+// 绉婚櫎鏂囦欢
+function handleRemove(file) {
+	if (operationType.value === "edit") {
+		let ids = [];
+		ids.push(file.id);
+		delLedgerFile(ids).then((res) => {
+			proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+		});
+	}
+}
+
+const submitForm = () => {
+	proxy.$refs["formRef"].validate(valid => {
+		if (valid) {
+			if (operationType.value === "add") {
+				measuringInstrumentAdd(form.value).then(response => {
+					proxy.$modal.msgSuccess("鏂板鎴愬姛")
+          form.value.tempFileIds = []
+					closeDia()
+				})
+			} else {
+				measuringInstrumentUpdate(form.value).then(response => {
+					proxy.$modal.msgSuccess("淇敼鎴愬姛")
+          form.value.tempFileIds = []
+					closeDia()
+				})
+			}
+		}
+	})
+}
+// 鍏抽棴寮规
+const closeDia = () => {
+	proxy.resetForm("formRef");
+  dialogFormVisible.value = false;
+  emit('close')
+};
+// 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
+function getCurrentDate() {
+	const today = new Date();
+	const year = today.getFullYear();
+	const month = String(today.getMonth() + 1).padStart(2, "0"); // 鏈堜唤浠�0寮�濮�
+	const day = String(today.getDate()).padStart(2, "0");
+	return `${year}-${month}-${day}`;
+}
+defineExpose({
+  openDialog,
+});
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file
diff --git a/src/views/measurementEquipment/filesDia.vue b/src/views/measurementEquipment/filesDia.vue
new file mode 100644
index 0000000..0751eac
--- /dev/null
+++ b/src/views/measurementEquipment/filesDia.vue
@@ -0,0 +1,202 @@
+<template>
+  <div>
+    <el-dialog
+        v-model="dialogFormVisible"
+        title="涓婁紶闄勪欢"
+        width="50%"
+        @close="closeDia"
+    >
+      <div style="margin-bottom: 10px;text-align: right">
+        <el-upload
+            v-model:file-list="fileList"
+            class="upload-demo"
+            :action="uploadUrl"
+            :on-success="handleUploadSuccess"
+            :on-error="handleUploadError"
+            name="file"
+            :show-file-list="false"
+            :headers="headers"
+            style="display: inline;margin-right: 10px"
+        >
+          <el-button type="primary">涓婁紶闄勪欢</el-button>
+        </el-upload>
+        <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+      </div>
+      <PIMTable
+          rowKey="id"
+          :column="tableColumn"
+          :tableData="tableData"
+          :tableLoading="tableLoading"
+          :isSelection="true"
+          @selection-change="handleSelectionChange"
+          height="500"
+      >
+      </PIMTable>
+			<pagination
+				style="margin: 10px 0"
+				v-show="total > 0"
+				@pagination="paginationSearch"
+				:total="total"
+				:page="page.current"
+				:limit="page.size"
+			/>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDia">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <filePreview ref="filePreviewRef" />
+  </div>
+</template>
+
+<script setup>
+import {ref} from "vue";
+import {ElMessageBox} from "element-plus";
+import {getToken} from "../../utils/auth.js";
+import filePreview from '../../components/filePreview/index.vue'
+import {
+  fileAdd,
+  fileDel,
+  fileListPage
+} from "../../api/financialManagement/revenueManagement.js";
+import Pagination from "../../components/PIMTable/Pagination.vue";
+const { proxy } = getCurrentInstance()
+const emit = defineEmits(['close'])
+
+const dialogFormVisible = ref(false);
+const currentId = ref('')
+const selectedRows = ref([]);
+const filePreviewRef = ref()
+const tableColumn = ref([
+  {
+    label: "鏂囦欢鍚嶇О",
+    prop: "name",
+  },
+  {
+    dataType: "action",
+    label: "鎿嶄綔",
+    align: "center",
+    operation: [
+      {
+        name: "涓嬭浇",
+        type: "text",
+        clickFun: (row) => {
+          downLoadFile(row);
+        },
+      },
+      {
+        name: "棰勮",
+        type: "text",
+        clickFun: (row) => {
+          lookFile(row);
+        },
+      }
+    ],
+  },
+]);
+const page = reactive({
+	current: 1,
+	size: 100,
+});
+const total = ref(0);
+const tableData = ref([]);
+const fileList = ref([]);
+const tableLoading = ref(false);
+const accountType = ref('')
+const headers = ref({
+  Authorization: "Bearer " + getToken(),
+});
+const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/file/upload"); // 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃
+
+// 鎵撳紑寮规
+const openDialog = (row,type) => {
+  accountType.value = type;
+  dialogFormVisible.value = true;
+  currentId.value = row.id;
+  getList()
+}
+const paginationSearch = (obj) => {
+	page.current = obj.page;
+	page.size = obj.limit;
+	getList();
+};
+const getList = () => {
+  fileListPage({accountId: currentId.value,accountType:accountType.value, ...page}).then(res => {
+    tableData.value = res.data.records;
+		total.value = res.data.total;
+  })
+}
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection;
+};
+
+// 鍏抽棴寮规
+const closeDia = () => {
+  dialogFormVisible.value = false;
+  emit('close')
+};
+// 涓婁紶鎴愬姛澶勭悊
+function handleUploadSuccess(res, file) {
+  // 濡傛灉涓婁紶鎴愬姛
+  if (res.code == 200) {
+    const fileRow = {}
+    fileRow.name = res.data.originalName
+    fileRow.url = res.data.tempPath
+    uploadFile(fileRow)
+  } else {
+    proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触");
+  }
+}
+function uploadFile(file) {
+  file.accountId = currentId.value;
+  file.accountType = accountType.value;
+  fileAdd(file).then(res => {
+    proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
+    getList()
+  })
+}
+// 涓婁紶澶辫触澶勭悊
+function handleUploadError() {
+  proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触");
+}
+// 涓嬭浇闄勪欢
+const downLoadFile = (row) => {
+  proxy.$download.name(row.url);
+}
+// 鍒犻櫎
+const handleDelete = () => {
+  let ids = [];
+  if (selectedRows.value.length > 0) {
+    ids = selectedRows.value.map((item) => item.id);
+  } else {
+    proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+    return;
+  }
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(() => {
+    fileDel(ids).then((res) => {
+      proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+      getList();
+    });
+  }).catch(() => {
+    proxy.$modal.msg("宸插彇娑�");
+  });
+};
+// 棰勮闄勪欢
+const lookFile = (row) => {
+  filePreviewRef.value.open(row.url)
+}
+
+defineExpose({
+  openDialog,
+});
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file
diff --git a/src/views/measurementEquipment/index.vue b/src/views/measurementEquipment/index.vue
new file mode 100644
index 0000000..58bb003
--- /dev/null
+++ b/src/views/measurementEquipment/index.vue
@@ -0,0 +1,270 @@
+<template>
+	<div class="app-container">
+		<div class="search_form">
+			<div>
+				<span class="search_title">褰曞叆鏃ユ湡锛�</span>
+				<el-date-picker
+					v-model="searchForm.recordDate"
+					value-format="YYYY-MM-DD"
+					format="YYYY-MM-DD"
+					type="date"
+					placeholder="璇烽�夋嫨"
+					clearable
+					style="width: 160px"
+					@change="handleQuery"
+				/>
+				<span class="search_title ml10">璁¢噺鍣ㄥ叿缂栧彿锛�</span>
+				<el-input v-model="searchForm.code" placeholder="璇疯緭鍏ョ紪鍙�" clearable style="width: 240px" @change="handleQuery"/>
+				<span class="search_title ml10">鐘舵�侊細</span>
+				<el-select v-model="searchForm.status" placeholder="璇烽�夋嫨鐘舵��" @change="handleQuery" style="width: 160px" clearable>
+					<el-option label="鏈夋晥" :value="1"></el-option>
+					<el-option label="閫炬湡" :value="2"></el-option>
+				</el-select>
+				<el-button type="primary" @click="handleQuery" style="margin-left: 10px"
+				>鎼滅储</el-button
+				>
+			</div>
+			<div>
+				<el-button type="primary" @click="openForm('add')">鏂板璁¢噺鍣ㄥ叿</el-button>
+				<el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+				<el-button @click="handleOut">瀵煎嚭</el-button>
+			</div>
+		</div>
+		<div class="table_list">
+			<PIMTable
+				rowKey="id"
+				:column="tableColumn"
+				:tableData="tableData"
+				:page="page"
+				:isSelection="true"
+				@selection-change="handleSelectionChange"
+				:tableLoading="tableLoading"
+				@pagination="pagination"
+			></PIMTable>
+		</div>
+		<form-dia ref="formDia" @close="handleQuery"></form-dia>
+		<calibration-dia ref="calibrationDia" @close="handleQuery"></calibration-dia>
+    <files-dia ref="filesDia"></files-dia>
+	</div>
+</template>
+
+<script setup>
+import {onMounted, ref} from "vue";
+import FormDia from "../measurementEquipment/components/formDia.vue";
+import {ElMessageBox} from "element-plus";
+import useUserStore from "../../store/modules/user.js";
+import CalibrationDia from "../measurementEquipment/components/calibrationDia.vue";
+import {
+	measuringInstrumentDelete,
+	measuringInstrumentListPage
+} from "../../api/equipmentManagement/measurementEquipment.js";
+import FilesDia from "./filesDia.vue";
+const { proxy } = getCurrentInstance();
+const userStore = useUserStore()
+
+const data = reactive({
+	searchForm: {
+		recordDate: "",
+		code: "",
+		status: "",
+	},
+});
+const { searchForm } = toRefs(data);
+
+const tableColumn = ref([
+	{
+		label: "鐘舵��",
+		prop: "status",
+		dataType: "tag",
+		formatData: (params) => {
+			if (params == 1) {
+				return "鏈夋晥";
+			} else if (params == 2) {
+				return "閫炬湡";
+			} else {
+				return null;
+			}
+		},
+		formatType: (params) => {
+			if (params == 1) {
+				return "success";
+			} else if (params == 2) {
+				return "danger";
+			} else {
+				return null;
+			}
+		},
+	},
+	{
+		label: "鏈�杩戜竴娆℃瀹氭棩鏈�",
+		prop: "mostDate",
+		width: 130,
+	},
+	{
+		label: "璁¢噺鍣ㄥ叿缂栧彿",
+		prop: "code",
+		width: 150,
+	},
+	{
+		label: "璁¢噺鍣ㄥ叿鍚嶇О",
+		prop: "name",
+		width: 200,
+	},
+	{
+		label: "瑙勬牸鍨嬪彿",
+		prop: "model",
+		width:200
+	},
+	{
+		label: "鏈夋晥鏈�",
+		prop: "valid",
+		width: 130,
+	},
+	{
+		label: "棰勮涓嬫妫�瀹氭棩鏈�",
+		prop: "nextDate",
+		width: 130,
+	},
+	{
+		label: "褰曞叆浜�",
+		prop: "userName",
+	},
+	{
+		label: "褰曞叆鏃ユ湡",
+		prop: "recordDate",
+		width: 130,
+	},
+	{
+		dataType: "action",
+		label: "鎿嶄綔",
+		align: "center",
+		width: '130',
+		fixed: 'right',
+		operation: [
+			{
+				name: "妫�瀹氭牎鍑�",
+				type: "text",
+				clickFun: (row) => {
+					openCalibrationDia("verifying", row);
+				},
+			},
+			// {
+			// 	name: "闄勪欢",
+			// 	type: "text",
+			// 	clickFun: (row) => {
+      //     openFilesFormDia(row);
+			// 	},
+			// },
+		],
+	},
+]);
+const tableData = ref([]);
+const tableLoading = ref(false);
+const filesDia = ref()
+const page = reactive({
+	current: 1,
+	size: 100,
+	total: 0,
+});
+const selectedRows = ref([]);
+
+// 鎵撳紑闄勪欢寮规
+const openFilesFormDia = (row) => {
+  console.log(row)
+  nextTick(() => {
+    filesDia.value?.openDialog( row,'璁¢噺鍣ㄥ叿鍙拌处')
+  })
+};
+
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+	selectedRows.value = selection;
+};
+const formDia = ref()
+const calibrationDia = ref()
+
+// 鏌ヨ鍒楄〃
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+	page.current = 1;
+	getList();
+};
+const pagination = (obj) => {
+	page.current = obj.page;
+	page.size = obj.limit;
+	getList();
+};
+const getList = () => {
+	tableLoading.value = true;
+	measuringInstrumentListPage({ ...searchForm.value, ...page }).then((res) => {
+		tableLoading.value = false;
+		tableData.value = res.data.records;
+		page.total = res.data.total;
+	}).catch((err) => {
+		tableLoading.value = false;
+	})
+};
+
+// 鎵撳紑寮规
+const openForm = (type, row) => {
+	nextTick(() => {
+		formDia.value?.openDialog(type, row)
+	})
+};
+// 鎵撳紑妫�瀹氭牎鍑嗗脊妗�
+const openCalibrationDia = (type, row) => {
+	nextTick(() => {
+		calibrationDia.value?.openDialog(type, row)
+	})
+}
+
+const handleDelete = () => {
+	let ids = [];
+	if (selectedRows.value.length > 0) {
+		ids = selectedRows.value.map((item) => item.id);
+	} else {
+		proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+		return;
+	}
+	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+		confirmButtonText: "纭",
+		cancelButtonText: "鍙栨秷",
+		type: "warning",
+	})
+		.then(() => {
+			tableLoading.value = true;
+			measuringInstrumentDelete(ids)
+				.then((res) => {
+					proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+					getList();
+				})
+				.finally(() => {
+					tableLoading.value = false;
+				});
+		})
+		.catch(() => {
+			proxy.$modal.msg("宸插彇娑�");
+		});
+};
+// 瀵煎嚭
+const handleOut = () => {
+	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+		confirmButtonText: "纭",
+		cancelButtonText: "鍙栨秷",
+		type: "warning",
+	})
+		.then(() => {
+			proxy.download("/measuringInstrumentLedger/export", {}, "璁¢噺鍣ㄥ叿鍙拌处.xlsx");
+		})
+		.catch(() => {
+			proxy.$modal.msg("宸插彇娑�");
+		});
+};
+onMounted(() => {
+	getList();
+});
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file

--
Gitblit v1.9.3