From 2d7864ea93229deef2793cbc4e650070bdc6f191 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期一, 29 六月 2026 18:01:06 +0800
Subject: [PATCH] 特种设备管理

---
 src/api/safeProduction/specialEquipmentManagement.js          |  105 +++++++++
 src/views/safeProduction/specialEquipmentManagement/index.vue |  497 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 602 insertions(+), 0 deletions(-)

diff --git a/src/api/safeProduction/specialEquipmentManagement.js b/src/api/safeProduction/specialEquipmentManagement.js
new file mode 100644
index 0000000..778e07c
--- /dev/null
+++ b/src/api/safeProduction/specialEquipmentManagement.js
@@ -0,0 +1,105 @@
+import request from "@/utils/request";
+
+export function specialEquipmentLedgerListPage(query) {
+  return request({
+    url: "/safe/specialEquipment/ledger/listPage",
+    method: "get",
+    params: query,
+  });
+}
+
+export function specialEquipmentLedgerList(query) {
+  return request({
+    url: "/safe/specialEquipment/ledger/list",
+    method: "get",
+    params: query,
+  });
+}
+
+export function specialEquipmentLedgerAdd(data) {
+  return request({
+    url: "/safe/specialEquipment/ledger/add",
+    method: "post",
+    data,
+  });
+}
+
+export function specialEquipmentLedgerUpdate(data) {
+  return request({
+    url: "/safe/specialEquipment/ledger/update",
+    method: "post",
+    data,
+  });
+}
+
+export function specialEquipmentLedgerDel(ids) {
+  return request({
+    url: "/safe/specialEquipment/ledger/delete",
+    method: "delete",
+    data: ids,
+  });
+}
+
+export function specialEquipmentRecordListPage(query) {
+  return request({
+    url: "/safe/specialEquipment/record/listPage",
+    method: "get",
+    params: query,
+  });
+}
+
+export function specialEquipmentRecordAdd(data) {
+  return request({
+    url: "/safe/specialEquipment/record/add",
+    method: "post",
+    data,
+  });
+}
+
+export function specialEquipmentRecordUpdate(data) {
+  return request({
+    url: "/safe/specialEquipment/record/update",
+    method: "post",
+    data,
+  });
+}
+
+export function specialEquipmentRecordDel(ids) {
+  return request({
+    url: "/safe/specialEquipment/record/delete",
+    method: "delete",
+    data: ids,
+  });
+}
+
+export function specialEquipmentRectificationListPage(query) {
+  return request({
+    url: "/safe/specialEquipment/rectification/listPage",
+    method: "get",
+    params: query,
+  });
+}
+
+export function specialEquipmentRectificationAdd(data) {
+  return request({
+    url: "/safe/specialEquipment/rectification/add",
+    method: "post",
+    data,
+  });
+}
+
+export function specialEquipmentRectificationClose(data) {
+  return request({
+    url: "/safe/specialEquipment/rectification/close",
+    method: "post",
+    data,
+  });
+}
+
+export function specialEquipmentRectificationDel(ids) {
+  return request({
+    url: "/safe/specialEquipment/rectification/delete",
+    method: "delete",
+    data: ids,
+  });
+}
diff --git a/src/views/safeProduction/specialEquipmentManagement/index.vue b/src/views/safeProduction/specialEquipmentManagement/index.vue
new file mode 100644
index 0000000..1b8da5d
--- /dev/null
+++ b/src/views/safeProduction/specialEquipmentManagement/index.vue
@@ -0,0 +1,497 @@
+<template>
+  <div class="app-container">
+    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+      <el-tab-pane label="鐢靛瓙鍙拌处" name="ledger">
+        <div class="search_form mb20">
+          <div>
+            <span class="search_title">璁惧鍚嶇О锛�</span>
+            <el-input v-model="ledgerSearch.equipmentName" style="width: 240px" clearable placeholder="璇疯緭鍏ヨ澶囧悕绉�" @change="ledgerQuery" :prefix-icon="Search" />
+            <el-button type="primary" style="margin-left: 10px" @click="ledgerQuery">鎼滅储</el-button>
+          </div>
+          <div style="margin-left: auto">
+            <el-button type="primary" @click="openLedgerForm()">鏂板鍙拌处</el-button>
+            <el-button type="danger" plain @click="deleteLedger">鍒犻櫎</el-button>
+          </div>
+        </div>
+        <PIMTable rowKey="id" :column="ledgerColumns" :tableData="ledgerTableData" :page="ledgerPage" :isSelection="true" @selection-change="handleLedgerSelectionChange" :tableLoading="ledgerLoading" @pagination="ledgerPagination" :total="ledgerPage.total" />
+      </el-tab-pane>
+      <el-tab-pane label="妫�楠岀淮淇濊褰�" name="record">
+        <div class="search_form mb20">
+          <div>
+            <span class="search_title">璁板綍绫诲瀷锛�</span>
+            <el-select v-model="recordSearch.recordType" style="width: 180px" clearable @change="recordQuery">
+              <el-option label="妫�楠�" :value="1" />
+              <el-option label="缁翠繚" :value="2" />
+            </el-select>
+            <el-button type="primary" style="margin-left: 10px" @click="recordQuery">鎼滅储</el-button>
+          </div>
+          <div>
+            <el-button type="primary" @click="openRecordForm()">鏂板璁板綍</el-button>
+            <el-button type="danger" plain @click="deleteRecord">鍒犻櫎</el-button>
+          </div>
+        </div>
+        <PIMTable rowKey="id" :column="recordColumns" :tableData="recordTableData" :page="recordPage" :isSelection="true" @selection-change="handleRecordSelectionChange" :tableLoading="recordLoading" @pagination="recordPagination" :total="recordPage.total" />
+      </el-tab-pane>
+      <el-tab-pane label="闅愭偅鏁存敼" name="rectification">
+        <div class="search_form mb20">
+          <div>
+            <span class="search_title">鏁存敼鐘舵�侊細</span>
+            <el-select v-model="rectSearch.rectificationStatus" style="width: 180px" clearable @change="rectQuery">
+              <el-option label="寰呮暣鏀�" :value="1" />
+              <el-option label="鏁存敼涓�" :value="2" />
+              <el-option label="寰呭鏍�" :value="3" />
+              <el-option label="宸查棴鐜�" :value="4" />
+            </el-select>
+            <el-button type="primary" style="margin-left: 10px" @click="rectQuery">鎼滅储</el-button>
+          </div>
+          <div>
+            <el-button type="primary" @click="openRectForm()">鏂板鏁存敼</el-button>
+            <el-button type="danger" plain @click="deleteRect">鍒犻櫎</el-button>
+          </div>
+        </div>
+        <PIMTable rowKey="id" :column="rectColumns" :tableData="rectTableData" :page="rectPage" :isSelection="true" @selection-change="handleRectSelectionChange" :tableLoading="rectLoading" @pagination="rectPagination" :total="rectPage.total" />
+      </el-tab-pane>
+    </el-tabs>
+
+    <el-dialog v-model="ledgerDialogVisible" :title="ledgerDialogTitle" width="900px" :close-on-click-modal="false">
+      <el-form ref="ledgerFormRef" :model="ledgerForm" label-position="top" :rules="ledgerRules">
+        <el-row :gutter="20">
+          <el-col :span="12"><el-form-item label="璁惧缂栧彿" prop="equipmentNo"><el-input v-model="ledgerForm.equipmentNo" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="璁惧鍚嶇О" prop="equipmentName"><el-input v-model="ledgerForm.equipmentName" /></el-form-item></el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12"><el-form-item label="璁惧绫诲埆"><el-input v-model="ledgerForm.equipmentType" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="瑙勬牸鍨嬪彿"><el-input v-model="ledgerForm.modelSpec" /></el-form-item></el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12"><el-form-item label="瀹夎浣嶇疆"><el-input v-model="ledgerForm.installLocation" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="浣跨敤鍗曚綅"><el-input v-model="ledgerForm.useUnit" /></el-form-item></el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璐熻矗浜�">
+              <el-select v-model="ledgerForm.managerId" filterable clearable style="width: 100%" placeholder="璇烽�夋嫨璐熻矗浜�" @change="handleLedgerManagerChange">
+                <el-option v-for="item in activeUserOptions" :key="item.userId" :label="item.nickName || item.userName" :value="item.userId" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitLedgerForm">纭畾</el-button>
+        <el-button @click="ledgerDialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog v-model="recordDialogVisible" :title="recordDialogTitle" width="900px" :close-on-click-modal="false">
+      <el-form ref="recordFormRef" :model="recordForm" label-position="top" :rules="recordRules">
+        <el-row :gutter="20">
+          <el-col :span="12"><el-form-item label="鍏宠仈璁惧" prop="equipmentId"><el-select v-model="recordForm.equipmentId" filterable clearable style="width: 100%" placeholder="璇烽�夋嫨璁惧"><el-option v-for="item in activeLedgerOptions" :key="item.id" :label="item.equipmentName" :value="item.id" /></el-select></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="璁板綍绫诲瀷" prop="recordType"><el-select v-model="recordForm.recordType" clearable style="width: 100%" placeholder="璇烽�夋嫨璁板綍绫诲瀷"><el-option label="妫�楠�" :value="1" /><el-option label="缁翠繚" :value="2" /></el-select></el-form-item></el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12"><el-form-item label="璁板綍缂栧彿" prop="recordNo"><el-input v-model="recordForm.recordNo" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="璁板綍鏃ユ湡" prop="recordDate"><el-date-picker v-model="recordForm.recordDate" value-format="YYYY-MM-DD" type="date" style="width: 100%" /></el-form-item></el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鎵ц浜�" prop="executorId">
+              <el-select v-model="recordForm.executorId" filterable clearable style="width: 100%" placeholder="璇烽�夋嫨鎵ц浜�" @change="handleExecutorChange">
+                <el-option v-for="item in activeUserOptions" :key="item.userId" :label="item.nickName || item.userName" :value="item.userId" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12"><el-form-item label="涓嬫鍒版湡鏃ユ湡" prop="nextDueDate"><el-date-picker v-model="recordForm.nextDueDate" value-format="YYYY-MM-DD" type="date" style="width: 100%" /></el-form-item></el-col>
+        </el-row>
+        <el-form-item label="鎵ц缁撴灉" prop="executeResult"><el-input v-model="recordForm.executeResult" type="textarea" :rows="3" /></el-form-item>
+        <el-form-item label="闂鎻忚堪" prop="problemDescription"><el-input v-model="recordForm.problemDescription" type="textarea" :rows="3" /></el-form-item>
+        <el-form-item label="澶勭悊鎺柦" prop="disposeMeasure"><el-input v-model="recordForm.disposeMeasure" type="textarea" :rows="3" /></el-form-item>
+        <el-form-item label="闄勪欢璇存槑" prop="attachmentRemark"><el-input v-model="recordForm.attachmentRemark" type="textarea" :rows="2" /></el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitRecordForm">纭畾</el-button>
+        <el-button @click="recordDialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog v-model="rectDialogVisible" :title="rectDialogTitle" width="900px" :close-on-click-modal="false">
+      <el-form ref="rectFormRef" :model="rectForm" label-position="top" :rules="rectRules">
+        <el-row :gutter="20">
+          <el-col :span="12"><el-form-item label="鍏宠仈璁惧" prop="equipmentId"><el-select v-model="rectForm.equipmentId" filterable clearable style="width: 100%" placeholder="璇烽�夋嫨璁惧"><el-option v-for="item in activeLedgerOptions" :key="item.id" :label="item.equipmentName" :value="item.id" /></el-select></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="椋庨櫓绛夌骇" prop="riskLevel"><el-select v-model="rectForm.riskLevel" clearable style="width: 100%" placeholder="璇烽�夋嫨椋庨櫓绛夌骇"><el-option label="浣�" value="浣�" /><el-option label="涓�" value="涓�" /><el-option label="楂�" value="楂�" /></el-select></el-form-item></el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12"><el-form-item label="鍙戠幇鏃ユ湡" prop="discoveredDate"><el-date-picker v-model="rectForm.discoveredDate" value-format="YYYY-MM-DD" type="date" style="width: 100%" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="鏁存敼鏈熼檺" prop="rectificationDeadline"><el-date-picker v-model="rectForm.rectificationDeadline" value-format="YYYY-MM-DD" type="date" style="width: 100%" /></el-form-item></el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鏁存敼璐d换浜�" prop="responsibleUserId">
+              <el-select v-model="rectForm.responsibleUserId" filterable clearable style="width: 100%" placeholder="璇烽�夋嫨鏁存敼璐d换浜�" @change="handleResponsibleUserChange">
+                <el-option v-for="item in activeUserOptions" :key="item.userId" :label="item.nickName || item.userName" :value="item.userId" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12"><el-form-item label="鏁存敼鐘舵��" prop="rectificationStatus"><el-select v-model="rectForm.rectificationStatus" clearable style="width: 100%" placeholder="璇烽�夋嫨鏁存敼鐘舵��"><el-option label="寰呮暣鏀�" :value="1" /><el-option label="鏁存敼涓�" :value="2" /><el-option label="寰呭鏍�" :value="3" /><el-option label="宸查棴鐜�" :value="4" /></el-select></el-form-item></el-col>
+        </el-row>
+        <el-form-item label="闅愭偅鎻忚堪" prop="hazardDescription"><el-input v-model="rectForm.hazardDescription" type="textarea" :rows="3" /></el-form-item>
+        <el-form-item label="鏁存敼鎺柦" prop="rectificationMeasures"><el-input v-model="rectForm.rectificationMeasures" type="textarea" :rows="3" /></el-form-item>
+        <el-form-item label="鏁存敼缁撴灉" prop="rectificationResult"><el-input v-model="rectForm.rectificationResult" type="textarea" :rows="3" /></el-form-item>
+        <el-form-item label="澶囨敞" prop="remarks"><el-input v-model="rectForm.remarks" type="textarea" :rows="2" /></el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitRectForm">纭畾</el-button>
+        <el-button @click="rectDialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { Search } from "@element-plus/icons-vue";
+import { computed, reactive, ref, onMounted, getCurrentInstance } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import {
+  specialEquipmentLedgerList,
+  specialEquipmentLedgerListPage,
+  specialEquipmentLedgerAdd,
+  specialEquipmentLedgerUpdate,
+  specialEquipmentLedgerDel,
+  specialEquipmentRecordListPage,
+  specialEquipmentRecordAdd,
+  specialEquipmentRecordUpdate,
+  specialEquipmentRecordDel,
+  specialEquipmentRectificationListPage,
+  specialEquipmentRectificationAdd,
+  specialEquipmentRectificationClose,
+  specialEquipmentRectificationDel,
+} from "@/api/safeProduction/specialEquipmentManagement.js";
+import { userListNoPageByTenantId } from "@/api/system/user.js";
+
+const { proxy } = getCurrentInstance();
+const activeTab = ref("ledger");
+const userOptions = ref([]);
+
+const ledgerSearch = reactive({ equipmentName: "" });
+const recordSearch = reactive({ recordType: "" });
+const rectSearch = reactive({ rectificationStatus: "" });
+
+const ledgerPage = reactive({ current: 1, size: 10, total: 0 });
+const recordPage = reactive({ current: 1, size: 10, total: 0 });
+const rectPage = reactive({ current: 1, size: 10, total: 0 });
+const ledgerTableData = ref([]);
+const recordTableData = ref([]);
+const rectTableData = ref([]);
+const ledgerSelection = ref([]);
+const recordSelection = ref([]);
+const rectSelection = ref([]);
+const ledgerLoading = ref(false);
+const recordLoading = ref(false);
+const rectLoading = ref(false);
+
+const ledgerDialogVisible = ref(false);
+const ledgerDialogTitle = ref("");
+const ledgerFormRef = ref();
+const ledgerForm = reactive({
+  equipmentNo: "",
+  equipmentName: "",
+  equipmentType: "",
+  modelSpec: "",
+  installLocation: "",
+  useUnit: "",
+  managerId: "",
+  managerName: "",
+});
+const ledgerRules = {
+  equipmentNo: [{ required: true, message: "璇疯緭鍏ヨ澶囩紪鍙�", trigger: "blur" }],
+  equipmentName: [{ required: true, message: "璇疯緭鍏ヨ澶囧悕绉�", trigger: "blur" }],
+};
+const recordDialogVisible = ref(false);
+const recordDialogTitle = ref("");
+const recordFormRef = ref();
+const recordForm = reactive({
+  equipmentId: "",
+  recordType: "",
+  recordNo: "",
+  recordDate: "",
+  executeResult: "",
+  problemDescription: "",
+  disposeMeasure: "",
+  executorId: "",
+  executorName: "",
+  nextDueDate: "",
+  attachmentRemark: "",
+});
+const recordRules = {
+  equipmentId: [{ required: true, message: "璇烽�夋嫨鍏宠仈璁惧", trigger: "change" }],
+  recordType: [{ required: true, message: "璇烽�夋嫨璁板綍绫诲瀷", trigger: "change" }],
+  recordNo: [{ required: true, message: "璇疯緭鍏ヨ褰曠紪鍙�", trigger: "blur" }],
+  recordDate: [{ required: true, message: "璇烽�夋嫨璁板綍鏃ユ湡", trigger: "change" }],
+};
+const rectDialogVisible = ref(false);
+const rectDialogTitle = ref("");
+const rectFormRef = ref();
+const rectForm = reactive({
+  equipmentId: "",
+  hazardDescription: "",
+  riskLevel: "",
+  discoveredDate: "",
+  responsibleUserId: "",
+  responsibleUserName: "",
+  rectificationDeadline: "",
+  rectificationStatus: 1,
+  rectificationMeasures: "",
+  rectificationResult: "",
+  remarks: "",
+});
+const rectRules = {
+  equipmentId: [{ required: true, message: "璇烽�夋嫨鍏宠仈璁惧", trigger: "change" }],
+  hazardDescription: [{ required: true, message: "璇疯緭鍏ラ殣鎮f弿杩�", trigger: "blur" }],
+  discoveredDate: [{ required: true, message: "璇烽�夋嫨鍙戠幇鏃ユ湡", trigger: "change" }],
+  rectificationDeadline: [{ required: true, message: "璇烽�夋嫨鏁存敼鏈熼檺", trigger: "change" }],
+};
+const ledgerOptions = ref([]);
+const activeLedgerOptions = computed(() => ledgerOptions.value);
+const activeUserOptions = computed(() => userOptions.value || []);
+
+const ledgerColumns = [
+  { label: "璁惧缂栧彿", prop: "equipmentNo" },
+  { label: "璁惧鍚嶇О", prop: "equipmentName" },
+  { label: "璁惧绫诲埆", prop: "equipmentType" },
+  { label: "瀹夎浣嶇疆", prop: "installLocation" },
+  { label: "璐熻矗浜�", prop: "managerName" },
+  {
+    label: "鎿嶄綔",
+    dataType: "action",
+    width: "180",
+    operation: [
+      { name: "缂栬緫", clickFun: (row) => openLedgerForm(row) },
+      { name: "鍒犻櫎", clickFun: (row) => deleteLedger(row.id) },
+    ],
+  },
+];
+
+const recordColumns = [
+  { label: "璁板綍缂栧彿", prop: "recordNo" },
+  { label: "璁板綍绫诲瀷", prop: "recordType", formatData: { 1: "妫�楠�", 2: "缁翠繚" } },
+  { label: "璁板綍鏃ユ湡", prop: "recordDate" },
+  { label: "鎵ц浜�", prop: "executorName" },
+  { label: "鎵ц缁撴灉", prop: "executeResult" },
+  { label: "涓嬫鍒版湡鏃ユ湡", prop: "nextDueDate" },
+  {
+    label: "鎿嶄綔",
+    dataType: "action",
+    width: "180",
+    operation: [
+      { name: "缂栬緫", clickFun: (row) => openRecordForm(row) },
+      { name: "鍒犻櫎", clickFun: (row) => deleteRecord(row.id) },
+    ],
+  },
+];
+
+const rectColumns = [
+  { label: "闅愭偅鎻忚堪", prop: "hazardDescription" },
+  { label: "椋庨櫓绛夌骇", prop: "riskLevel" },
+  { label: "鍙戠幇鏃ユ湡", prop: "discoveredDate" },
+  { label: "璐d换浜�", prop: "responsibleUserName" },
+  { label: "鏁存敼鏈熼檺", prop: "rectificationDeadline" },
+  { label: "鏁存敼鐘舵��", prop: "rectificationStatus", formatData: { 1: "寰呮暣鏀�", 2: "鏁存敼涓�", 3: "寰呭鏍�", 4: "宸查棴鐜�" } },
+  {
+    label: "鎿嶄綔",
+    dataType: "action",
+    width: "220",
+    operation: [
+      { name: "鍒犻櫎", clickFun: (row) => deleteRect(row.id) },
+    ],
+  },
+];
+
+const ledgerQuery = async () => {
+  ledgerLoading.value = true;
+  const res = await specialEquipmentLedgerListPage({ ...ledgerSearch, ...ledgerPage });
+  ledgerTableData.value = res.data?.records || res.data?.rows || [];
+  ledgerPage.total = res.data?.total || 0;
+  ledgerLoading.value = false;
+};
+
+const loadLedgerOptions = async () => {
+  const res = await specialEquipmentLedgerList({});
+  ledgerOptions.value = res.data || [];
+};
+
+const recordQuery = async () => {
+  recordLoading.value = true;
+  const res = await specialEquipmentRecordListPage({ ...recordSearch, ...recordPage });
+  recordTableData.value = res.data?.records || res.data?.rows || [];
+  recordPage.total = res.data?.total || 0;
+  recordLoading.value = false;
+};
+
+const rectQuery = async () => {
+  rectLoading.value = true;
+  const res = await specialEquipmentRectificationListPage({ ...rectSearch, ...rectPage });
+  rectTableData.value = res.data?.records || res.data?.rows || [];
+  rectPage.total = res.data?.total || 0;
+  rectLoading.value = false;
+};
+
+const handleTabChange = () => {
+  if (activeTab.value === "ledger") ledgerQuery();
+  if (activeTab.value === "record") recordQuery();
+  if (activeTab.value === "rectification") rectQuery();
+};
+
+const ledgerPagination = ({ page, limit }) => { ledgerPage.current = page; ledgerPage.size = limit; ledgerQuery(); };
+const recordPagination = ({ page, limit }) => { recordPage.current = page; recordPage.size = limit; recordQuery(); };
+const rectPagination = ({ page, limit }) => { rectPage.current = page; rectPage.size = limit; rectQuery(); };
+const handleLedgerSelectionChange = (rows) => (ledgerSelection.value = rows);
+const handleRecordSelectionChange = (rows) => (recordSelection.value = rows);
+const handleRectSelectionChange = (rows) => (rectSelection.value = rows);
+
+const getUserDisplayName = (userId) => {
+  const user = userOptions.value.find((item) => item.userId === userId);
+  return user ? (user.nickName || user.userName || "") : "";
+};
+
+const handleLedgerManagerChange = (userId) => {
+  ledgerForm.managerName = getUserDisplayName(userId);
+};
+
+const openLedgerForm = (row = {}) => {
+  Object.assign(ledgerForm, {
+    id: "",
+    equipmentNo: "",
+    equipmentName: "",
+    equipmentType: "",
+    modelSpec: "",
+    installLocation: "",
+    useUnit: "",
+    managerName: "",
+  });
+  Object.assign(ledgerForm, row);
+  ledgerDialogTitle.value = row.id ? "缂栬緫鍙拌处" : "鏂板鍙拌处";
+  ledgerDialogVisible.value = true;
+};
+
+const submitLedgerForm = async () => {
+  await ledgerFormRef.value.validate();
+  const api = ledgerForm.id ? specialEquipmentLedgerUpdate : specialEquipmentLedgerAdd;
+  await api(ledgerForm);
+  ElMessage.success("淇濆瓨鎴愬姛");
+  ledgerDialogVisible.value = false;
+  ledgerQuery();
+};
+
+const deleteLedger = async (id) => {
+  const ids = id ? [id] : ledgerSelection.value.map((i) => i.id);
+  if (!ids.length) return ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁");
+  await ElMessageBox.confirm("纭鍒犻櫎鎵�閫夊彴璐﹀悧锛�", "鎻愮ず", { type: "warning" });
+  await specialEquipmentLedgerDel(ids);
+  ElMessage.success("鍒犻櫎鎴愬姛");
+  ledgerQuery();
+};
+
+const openRecordForm = async (row = {}) => {
+  Object.assign(recordForm, {
+    id: "",
+    equipmentId: "",
+    recordType: "",
+    recordNo: "",
+    recordDate: "",
+    executeResult: "",
+    problemDescription: "",
+    disposeMeasure: "",
+    executorId: "",
+    executorName: "",
+    nextDueDate: "",
+    attachmentRemark: "",
+  });
+  Object.assign(recordForm, row);
+  if (!recordForm.executorId && recordForm.executorName) {
+    const matched = userOptions.value.find((item) => (item.nickName || item.userName) === recordForm.executorName);
+    recordForm.executorId = matched?.userId || "";
+  }
+  recordDialogTitle.value = row.id ? "缂栬緫璁板綍" : "鏂板璁板綍";
+  recordDialogVisible.value = true;
+};
+
+const submitRecordForm = async () => {
+  await recordFormRef.value.validate();
+  const api = recordForm.id ? specialEquipmentRecordUpdate : specialEquipmentRecordAdd;
+  await api(recordForm);
+  ElMessage.success("淇濆瓨鎴愬姛");
+  recordDialogVisible.value = false;
+  recordQuery();
+};
+
+const deleteRecord = async (id) => {
+  const ids = id ? [id] : recordSelection.value.map((i) => i.id);
+  if (!ids.length) return ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁");
+  await ElMessageBox.confirm("纭鍒犻櫎鎵�閫夎褰曞悧锛�", "鎻愮ず", { type: "warning" });
+  await specialEquipmentRecordDel(ids);
+  ElMessage.success("鍒犻櫎鎴愬姛");
+  recordQuery();
+};
+
+const openRectForm = (row = {}) => {
+  Object.assign(rectForm, {
+    id: "",
+    equipmentId: "",
+    hazardDescription: "",
+    riskLevel: "",
+    discoveredDate: "",
+    responsibleUserId: "",
+    responsibleUserName: "",
+    rectificationDeadline: "",
+    rectificationStatus: 1,
+    rectificationMeasures: "",
+    rectificationResult: "",
+    remarks: "",
+  });
+  Object.assign(rectForm, row);
+  if (!rectForm.responsibleUserId && rectForm.responsibleUserName) {
+    const matched = userOptions.value.find((item) => (item.nickName || item.userName) === rectForm.responsibleUserName);
+    rectForm.responsibleUserId = matched?.userId || "";
+  }
+  rectDialogTitle.value = "鏂板鏁存敼";
+  rectDialogVisible.value = true;
+};
+
+const submitRectForm = async () => {
+  await rectFormRef.value.validate();
+  await specialEquipmentRectificationAdd(rectForm);
+  ElMessage.success("淇濆瓨鎴愬姛");
+  rectDialogVisible.value = false;
+  rectQuery();
+};
+
+const deleteRect = async (id) => {
+  const ids = id ? [id] : rectSelection.value.map((i) => i.id);
+  if (!ids.length) return ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁");
+  await ElMessageBox.confirm("纭鍒犻櫎鎵�閫夋暣鏀规暟鎹悧锛�", "鎻愮ず", { type: "warning" });
+  await specialEquipmentRectificationDel(ids);
+  ElMessage.success("鍒犻櫎鎴愬姛");
+  rectQuery();
+};
+
+onMounted(() => {
+  ledgerQuery();
+  loadLedgerOptions();
+  userListNoPageByTenantId()
+    .then((res) => {
+      userOptions.value = Array.isArray(res.data) ? res.data : [];
+    })
+    .catch(() => {
+      userOptions.value = [];
+    });
+});
+
+const handleExecutorChange = (userId) => {
+  recordForm.executorName = getUserDisplayName(userId);
+};
+
+const handleResponsibleUserChange = (userId) => {
+  rectForm.responsibleUserName = getUserDisplayName(userId);
+};
+</script>

--
Gitblit v1.9.3