From b9e53d7500e3497f8659fc373e6c707071b6dfb7 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期五, 27 二月 2026 13:57:56 +0800
Subject: [PATCH] 金鹰黄金 1.设备报修和保养添加审批流程 2.设备保养流程添加定任务管理

---
 src/views/equipmentManagement/upkeep/Form/ApproveModal.vue     |  137 ++
 src/views/equipmentManagement/upkeep/index.vue                 |  870 ++++++++++++----
 src/views/equipmentManagement/repair/Modal/MaintainModal.vue   |   99 +
 src/views/equipmentManagement/upkeep/Form/PlanModal.vue        |  188 +++
 src/views/equipmentManagement/repair/Modal/RepairModal.vue     |  198 ++-
 src/views/equipmentManagement/repair/Modal/ApproveModal.vue    |  135 ++
 src/views/equipmentManagement/repair/index.vue                 |  259 ++--
 src/api/equipmentManagement/upkeep.js                          |   33 
 src/api/equipmentManagement/maintenanceTaskFile.js             |   28 
 src/components/Dialog/ImportDialog.vue                         |  172 +++
 /dev/null                                                      |  162 ---
 src/views/equipmentManagement/upkeep/Form/formDia.vue          |  325 ++++++
 src/utils/index.js                                             |   13 
 src/components/Dialog/FileListDialog.vue                       |  328 ++++++
 src/components/Dialog/FormDialog.vue                           |   73 +
 src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue |  146 ++
 16 files changed, 2,548 insertions(+), 618 deletions(-)

diff --git a/src/api/equipmentManagement/maintenanceTaskFile.js b/src/api/equipmentManagement/maintenanceTaskFile.js
new file mode 100644
index 0000000..8373ae3
--- /dev/null
+++ b/src/api/equipmentManagement/maintenanceTaskFile.js
@@ -0,0 +1,28 @@
+import request from "@/utils/request";
+
+// 鏌ヨ淇濆吇浠诲姟闄勪欢鍒楄〃
+export function listMaintenanceTaskFiles(query) {
+  return request({
+    url: "/maintenanceTaskFile/listPage",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鏂板淇濆吇浠诲姟闄勪欢
+export function addMaintenanceTaskFile(data) {
+  return request({
+    url: "/maintenanceTaskFile/add",
+    method: "post",
+    data,
+  });
+}
+
+// 鍒犻櫎淇濆吇浠诲姟闄勪欢
+export function delMaintenanceTaskFile(id) {
+  return request({
+    url: "/maintenanceTaskFile/del",
+    method: "delete",
+    data: Array.isArray(id) ? id : [id],
+  });
+}
diff --git a/src/api/equipmentManagement/upkeep.js b/src/api/equipmentManagement/upkeep.js
index 82ce865..37f6e5f 100644
--- a/src/api/equipmentManagement/upkeep.js
+++ b/src/api/equipmentManagement/upkeep.js
@@ -70,6 +70,39 @@
     method: "delete",
   });
 };
+// 娣诲姞璁惧淇濆吇瀹氭椂浠诲姟
+export const deviceMaintenanceTaskAdd = (params) => {
+  return request({
+    url: '/deviceMaintenanceTask/add',
+    method: "post",
+    data: params,
+  });
+};
+// 淇敼璁惧淇濆吇瀹氭椂浠诲姟
+export const deviceMaintenanceTaskEdit = (params) => {
+  return request({
+    url: '/deviceMaintenanceTask/update',
+    method: "post",
+    data: params,
+  });
+};
+// 璁惧淇濆吇瀹氭椂浠诲姟鍒楄〃
+export const deviceMaintenanceTaskList = (params) => {
+  return request({
+    url: '/deviceMaintenanceTask/listPage',
+    method: "get",
+    params: params,
+  });
+};
+// 璁惧淇濆吇瀹氭椂浠诲姟鍒楄〃
+export const deviceMaintenanceTaskDel = (params) => {
+  return request({
+    url: '/deviceMaintenanceTask/delete',
+    method: "delete",
+    data: params,
+  });
+};
+
 // 鏌ヨ璁惧鐨勬姤淇噾棰�-姣忔湀
 export const monthlyAmount = (query) => {
   return request({
diff --git a/src/components/Dialog/FileListDialog.vue b/src/components/Dialog/FileListDialog.vue
new file mode 100644
index 0000000..fc82411
--- /dev/null
+++ b/src/components/Dialog/FileListDialog.vue
@@ -0,0 +1,328 @@
+<template>
+  <el-dialog v-model="dialogVisible"
+             :title="title"
+             :width="width"
+             :before-close="handleClose">
+    <div class="file-list-toolbar"
+         v-if="showToolbar">
+      <template v-if="useBuiltInUpload">
+        <el-upload v-model:file-list="uploadFileList"
+                   class="upload-demo"
+                   :action="uploadAction"
+                   :headers="uploadHeaders"
+                   :show-file-list="false"
+                   :on-success="handleDefaultUploadSuccess"
+                   :on-error="handleDefaultUploadError">
+          <el-button v-if="showUploadButton"
+                     type="primary"
+                     size="small">
+            涓婁紶闄勪欢
+          </el-button>
+        </el-upload>
+      </template>
+      <template v-else>
+        <el-button v-if="showUploadButton"
+                   type="primary"
+                   size="small"
+                   @click="handleUpload">
+          鏂板闄勪欢
+        </el-button>
+      </template>
+    </div>
+    <el-table :data="tableData"
+              border
+              :height="tableHeight">
+      <el-table-column :label="nameColumnLabel"
+                       :prop="nameColumnProp"
+                       :min-width="nameColumnMinWidth"
+                       show-overflow-tooltip />
+      <el-table-column v-if="showActions"
+                       fixed="right"
+                       label="鎿嶄綔"
+                       :width="actionColumnWidth"
+                       align="center">
+        <template #default="scope">
+          <el-button v-if="showDownload"
+                     link
+                     type="primary"
+                     size="small"
+                     @click="handleDownload(scope.row)">
+            涓嬭浇
+          </el-button>
+          <el-button v-if="showPreview"
+                     link
+                     type="primary"
+                     size="small"
+                     @click="handlePreview(scope.row)">
+            棰勮
+          </el-button>
+          <el-button v-if="showDeleteButton"
+                     link
+                     type="danger"
+                     size="small"
+                     @click="handleDelete(scope.row, scope.$index)">
+            鍒犻櫎
+          </el-button>
+          <slot name="actions"
+                :row="scope.row"></slot>
+        </template>
+      </el-table-column>
+      <slot name="columns"></slot>
+    </el-table>
+    <pagination v-if="isShowPagination"
+                style="margin-bottom: 20px;"
+                :total="page.total"
+                :page="page.current"
+                :limit="page.size"
+                @pagination="paginationSearch"
+                @change="handleChange" />
+  </el-dialog>
+  <filePreview v-if="showPreview"
+               ref="filePreviewRef" />
+</template>
+
+<script setup>
+  import { ref, computed, getCurrentInstance } from "vue";
+  import pagination from "@/components/Pagination/index.vue";
+  import { ElMessage } from "element-plus";
+  import filePreview from "@/components/filePreview/index.vue";
+  import { getToken } from "@/utils/auth";
+
+  const props = defineProps({
+    modelValue: {
+      type: Boolean,
+      default: false,
+    },
+    title: {
+      type: String,
+      default: "闄勪欢",
+    },
+    width: {
+      type: String,
+      default: "40%",
+    },
+    tableHeight: {
+      type: String,
+      default: "40vh",
+    },
+    nameColumnLabel: {
+      type: String,
+      default: "闄勪欢鍚嶇О",
+    },
+    nameColumnProp: {
+      type: String,
+      default: "name",
+    },
+    nameColumnMinWidth: {
+      type: [String, Number],
+      default: 400,
+    },
+    actionColumnWidth: {
+      type: [String, Number],
+      default: 160,
+    },
+    showActions: {
+      type: Boolean,
+      default: true,
+    },
+    showDownload: {
+      type: Boolean,
+      default: true,
+    },
+    showPreview: {
+      type: Boolean,
+      default: true,
+    },
+    showUploadButton: {
+      type: Boolean,
+      default: false,
+    },
+    showDeleteButton: {
+      type: Boolean,
+      default: false,
+    },
+    urlField: {
+      type: String,
+      default: "url",
+    },
+    downloadMethod: {
+      type: Function,
+      default: null,
+    },
+    previewMethod: {
+      type: Function,
+      default: null,
+    },
+    uploadMethod: {
+      type: Function,
+      default: null,
+    },
+    deleteMethod: {
+      type: Function,
+      default: null,
+    },
+    rulesRegulationsManagementId: {
+      type: [String, Number],
+      default: "",
+    },
+    uploadUrl: {
+      type: String,
+      default: `${import.meta.env.VITE_APP_BASE_API}/file/upload`,
+    },
+    isShowPagination: {
+      type: Boolean,
+      default: false,
+    },
+    page: {
+      type: Object,
+      default: () => ({
+        current: 1,
+        size: 10,
+        total: 0,
+      }),
+    },
+  });
+
+  const emit = defineEmits([
+    "update:modelValue",
+    "close",
+    "download",
+    "preview",
+    "upload",
+    "delete",
+  ]);
+
+  const { proxy } = getCurrentInstance();
+  const filePreviewRef = ref(null);
+  const uploadFileList = ref([]);
+
+  const dialogVisible = computed({
+    get: () => props.modelValue,
+    set: val => emit("update:modelValue", val),
+  });
+
+  const tableData = ref([]);
+  const showToolbar = computed(() => props.showUploadButton);
+  const useBuiltInUpload = computed(() => !props.uploadMethod);
+  const uploadAction = computed(() => props.uploadUrl);
+  const uploadHeaders = computed(() => ({
+    Authorization: `Bearer ${getToken()}`,
+  }));
+
+  const handleClose = () => {
+    emit("close");
+    dialogVisible.value = false;
+  };
+
+  const handleDownload = row => {
+    if (props.downloadMethod) {
+      props.downloadMethod(row);
+    } else {
+      // 榛樿涓嬭浇鏂规硶
+      proxy.$download.name(row[props.urlField]);
+    }
+    emit("download", row);
+  };
+
+  const handlePreview = row => {
+    if (props.previewMethod) {
+      props.previewMethod(row);
+    } else {
+      // 榛樿棰勮鏂规硶
+      if (filePreviewRef.value) {
+        filePreviewRef.value.open(row[props.urlField]);
+      }
+    }
+    emit("preview", row);
+  };
+  const paginationSearch = page => {
+    props.page.current = page.page;
+    props.page.size = page.limit;
+    emit("pagination", page.page, page.limit);
+  };
+
+  const open = list => {
+    dialogVisible.value = true;
+    tableData.value = list || [];
+  };
+
+  const handleUpload = async () => {
+    if (props.uploadMethod) {
+      // 濡傛灉鎻愪緵浜嗚嚜瀹氫箟涓婁紶鏂规硶锛岀敱鐖剁粍浠惰礋璐f洿鏂板垪琛紙閫氳繃 setList锛�
+      // 杩欓噷涓嶅啀鑷姩娣诲姞锛岄伩鍏嶄笌鐖剁粍浠剁殑 setList 閲嶅
+      await props.uploadMethod();
+    }
+    emit("upload");
+  };
+
+  const handleDelete = async (row, index) => {
+    if (props.deleteMethod) {
+      const result = await props.deleteMethod(row, index);
+      if (result === false) {
+        return;
+      }
+      // 濡傛灉鎻愪緵浜� deleteMethod锛岀敱鐖剁粍浠惰礋璐e埛鏂板垪琛紝涓嶅湪杩欓噷鍒犻櫎
+    } else {
+      // 濡傛灉娌℃湁鎻愪緵 deleteMethod锛屾墠鍦ㄧ粍浠跺唴閮ㄥ垹闄�
+      removeAttachment(index);
+    }
+    emit("delete", row);
+  };
+
+  const addAttachment = item => {
+    tableData.value = [...tableData.value, item];
+  };
+
+  const handleDefaultUploadSuccess = async (res, file) => {
+    if (res?.code !== 200) {
+      ElMessage.error(res?.msg || "鏂囦欢涓婁紶澶辫触");
+      return;
+    }
+    if (!props.rulesRegulationsManagementId) {
+      ElMessage.error("缂哄皯瑙勭珷鍒跺害ID锛屾棤娉曚繚瀛橀檮浠�");
+      return;
+    }
+    const fileName = res?.data?.originalName || file?.name;
+    const fileUrl = res?.data?.tempPath || res?.data?.url;
+    const payload = {
+      fileName,
+      fileUrl,
+      rulesRegulationsManagementId: props.rulesRegulationsManagementId,
+      raw: res?.data || {},
+    };
+    emit("upload", payload);
+  };
+
+  const handleDefaultUploadError = () => {
+    ElMessage.error("鏂囦欢涓婁紶澶辫触");
+  };
+
+  const removeAttachment = index => {
+    if (index > -1 && index < tableData.value.length) {
+      const newList = [...tableData.value];
+      newList.splice(index, 1);
+      tableData.value = newList;
+    }
+  };
+
+  const setList = list => {
+    tableData.value = list || [];
+  };
+
+  defineExpose({
+    open,
+    addAttachment,
+    removeAttachment,
+    setList,
+    handleUpload,
+    handleDelete,
+  });
+</script>
+
+<style scoped>
+  .file-list-toolbar {
+    margin-bottom: 8px;
+    text-align: right;
+  }
+</style>
+
diff --git a/src/components/Dialog/FormDialog.vue b/src/components/Dialog/FormDialog.vue
new file mode 100644
index 0000000..5e21b1d
--- /dev/null
+++ b/src/components/Dialog/FormDialog.vue
@@ -0,0 +1,73 @@
+<template>
+  <el-dialog
+    v-model="dialogVisible"
+    :title="computedTitle"
+    :width="width"
+    @close="handleClose"
+  >
+    <slot></slot>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary" @click="handleConfirm">纭</el-button>
+        <el-button @click="handleCancel">鍙栨秷</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { computed } from 'vue'
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false
+  },
+  title: {
+    type: [String, Function],
+    default: ''
+  },
+  operationType: {
+    type: String,
+    default: ''
+  },
+  width: {
+    type: String,
+    default: '70%'
+  }
+})
+
+const emit = defineEmits(['update:modelValue', 'close', 'confirm', 'cancel'])
+
+const dialogVisible = computed({
+  get: () => props.modelValue,
+  set: (val) => emit('update:modelValue', val)
+})
+
+const computedTitle = computed(() => {
+  if (typeof props.title === 'function') {
+    return props.title(props.operationType)
+  }
+  return props.title
+})
+
+const handleClose = () => {
+  emit('close')
+}
+
+const handleConfirm = () => {
+  emit('confirm')
+}
+
+const handleCancel = () => {
+  emit('cancel')
+  dialogVisible.value = false
+}
+</script>
+
+<style scoped>
+.dialog-footer {
+  text-align: center;
+}
+</style>
+
diff --git a/src/components/Dialog/ImportDialog.vue b/src/components/Dialog/ImportDialog.vue
new file mode 100644
index 0000000..5b126dc
--- /dev/null
+++ b/src/components/Dialog/ImportDialog.vue
@@ -0,0 +1,172 @@
+<template>
+  <el-dialog
+    :title="title"
+    v-model="dialogVisible"
+    :width="width"
+    :append-to-body="appendToBody"
+    @close="handleClose"
+  >
+    <el-upload
+      ref="uploadRef"
+      :limit="limit"
+      :accept="accept"
+      :headers="headers"
+      :action="action"
+      :disabled="disabled"
+      :before-upload="beforeUpload"
+      :on-progress="onProgress"
+      :on-success="onSuccess"
+      :on-error="onError"
+      :on-change="onChange"
+      :auto-upload="autoUpload"
+      drag
+    >
+      <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
+      <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+      <template #tip>
+        <div class="el-upload__tip text-center">
+          <span>{{ tipText }}</span>
+          <el-link
+            v-if="showDownloadTemplate"
+            type="primary"
+            :underline="false"
+            style="font-size: 12px; vertical-align: baseline; margin-left: 5px;"
+            @click="handleDownloadTemplate"
+            >涓嬭浇妯℃澘</el-link
+          >
+        </div>
+      </template>
+    </el-upload>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary" @click="handleConfirm">纭� 瀹�</el-button>
+        <el-button @click="handleCancel">鍙� 娑�</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { computed, ref } from 'vue'
+import { UploadFilled } from '@element-plus/icons-vue'
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false
+  },
+  title: {
+    type: String,
+    default: '瀵煎叆'
+  },
+  width: {
+    type: String,
+    default: '400px'
+  },
+  appendToBody: {
+    type: Boolean,
+    default: true
+  },
+  limit: {
+    type: Number,
+    default: 1
+  },
+  accept: {
+    type: String,
+    default: '.xlsx, .xls'
+  },
+  headers: {
+    type: Object,
+    default: () => ({})
+  },
+  action: {
+    type: String,
+    required: true
+  },
+  disabled: {
+    type: Boolean,
+    default: false
+  },
+  autoUpload: {
+    type: Boolean,
+    default: false
+  },
+  tipText: {
+    type: String,
+    default: '浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�'
+  },
+  showDownloadTemplate: {
+    type: Boolean,
+    default: true
+  },
+  beforeUpload: {
+    type: Function,
+    default: null
+  },
+  onProgress: {
+    type: Function,
+    default: null
+  },
+  onSuccess: {
+    type: Function,
+    default: null
+  },
+  onError: {
+    type: Function,
+    default: null
+  },
+  onChange: {
+    type: Function,
+    default: null
+  }
+})
+
+const emit = defineEmits(['update:modelValue', 'close', 'confirm', 'cancel', 'download-template'])
+
+const dialogVisible = computed({
+  get: () => props.modelValue,
+  set: (val) => emit('update:modelValue', val)
+})
+
+const uploadRef = ref(null)
+
+const handleClose = () => {
+  emit('close')
+}
+
+const handleConfirm = () => {
+  emit('confirm')
+}
+
+const submit = () => {
+  if (uploadRef.value) {
+    uploadRef.value.submit()
+  }
+}
+
+const handleCancel = () => {
+  emit('cancel')
+  dialogVisible.value = false
+}
+
+const handleDownloadTemplate = () => {
+  emit('download-template')
+}
+
+defineExpose({
+  uploadRef,
+  submit,
+  clearFiles: () => {
+    if (uploadRef.value) {
+      uploadRef.value.clearFiles()
+    }
+  }
+})
+</script>
+
+<style scoped>
+.dialog-footer {
+  text-align: center;
+}
+</style>
+
diff --git a/src/utils/index.js b/src/utils/index.js
index e522c3c..809593f 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -396,3 +396,16 @@
 export function isEqual(obj1, obj2) {
   return JSON.stringify(obj1) === JSON.stringify(obj2);
 }
+
+/**
+ * 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
+ * @returns {string} 鏍煎紡鍖栫殑鏃ユ湡瀛楃涓�
+ */
+export 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}`;
+}
+
diff --git a/src/views/equipmentManagement/repair/Modal/ApproveModal.vue b/src/views/equipmentManagement/repair/Modal/ApproveModal.vue
new file mode 100644
index 0000000..9077b84
--- /dev/null
+++ b/src/views/equipmentManagement/repair/Modal/ApproveModal.vue
@@ -0,0 +1,135 @@
+<template>
+  <FormDialog
+    v-model="visible"
+    title="鎶ヤ慨瀹℃壒"
+    width="800px"
+    @confirm="handleSubmit"
+    @cancel="handleClose"
+    @close="handleClose"
+  >
+    <el-descriptions :column="2" border>
+      <el-descriptions-item label="璁惧鍚嶇О">
+        {{ detail.deviceName || "-" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="瑙勬牸鍨嬪彿">
+        {{ detail.deviceModel || "-" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="鎶ヤ慨鏃ユ湡">
+        {{ detail.repairTime || "-" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="鎶ヤ慨浜�">
+        {{ detail.repairName || "-" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="瀹℃壒浜�">
+        {{ detail.auditName || "-" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="褰撳墠鐘舵��">
+        {{ statusText(detail.status) }}
+      </el-descriptions-item>
+      <el-descriptions-item label="鏁呴殰鐜拌薄" :span="2">
+        {{ detail.remark || "-" }}
+      </el-descriptions-item>
+    </el-descriptions>
+    <div style="margin-top: 16px">
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="瀹℃壒缁撴灉" prop="decision">
+          <el-radio-group v-model="form.decision">
+            <el-radio :value="0">閫氳繃</el-radio>
+            <el-radio :value="3">涓嶉�氳繃</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+    </div>
+  </FormDialog>
+</template>
+
+<script setup>
+import { nextTick, ref } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import { editRepair, getRepairById } from "@/api/equipmentManagement/repair";
+
+defineOptions({
+  name: "鎶ヤ慨瀹℃壒寮圭獥",
+});
+
+const emits = defineEmits(["ok"]);
+
+const visible = ref(false);
+const loading = ref(false);
+const id = ref();
+const detail = ref({});
+const formRef = ref();
+const form = ref({
+  decision: undefined, // 0 閫氳繃 3 涓嶉�氳繃
+});
+
+const rules = {
+  decision: [{ required: true, message: "璇烽�夋嫨瀹℃壒缁撴灉", trigger: "change" }],
+};
+
+const statusText = (status) => {
+  const map = {
+    0: "寰呯淮淇�",
+    1: "瀹岀粨",
+    2: "寰呭鏍�",
+    3: "瀹℃牳涓嶉�氳繃",
+  };
+  return map[status] ?? "-";
+};
+
+const loadDetail = async (repairId) => {
+  const { data } = await getRepairById(repairId);
+  detail.value = data ?? {};
+};
+
+const open = async (repairId) => {
+  id.value = repairId;
+  visible.value = true;
+  await nextTick();
+  await loadDetail(repairId);
+  form.value.decision = undefined;
+};
+
+const handleClose = () => {
+  visible.value = false;
+  id.value = undefined;
+  detail.value = {};
+  form.value.decision = undefined;
+};
+
+const updateStatus = async (status) => {
+  loading.value = true;
+  try {
+    const { code } = await editRepair({ id: id.value, status });
+    if (code === 200) {
+      ElMessage.success("瀹℃壒鎴愬姛");
+      emits("ok");
+      handleClose();
+    }
+  } finally {
+    loading.value = false;
+  }
+};
+
+const handleSubmit = async () => {
+  if (detail.value?.status !== 2) {
+    ElMessage.warning("浠呭緟瀹℃牳鐘舵�佸彲瀹℃壒");
+    return;
+  }
+  await formRef.value?.validate(async (valid) => {
+    if (!valid) return;
+    const isApprove = form.value.decision === 0;
+    ElMessageBox.confirm(`纭瀹℃壒${isApprove ? "閫氳繃" : "涓嶉�氳繃"}锛焋, "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    }).then(() => updateStatus(form.value.decision));
+  });
+};
+
+defineExpose({ open });
+</script>
+
+<style scoped></style>
+
diff --git a/src/views/equipmentManagement/repair/Modal/MaintainModal.vue b/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
index 169075b..7058a5a 100644
--- a/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
+++ b/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
@@ -1,5 +1,12 @@
 <template>
-  <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr" @close="handleDialogClose">
+  <FormDialog
+    v-model="visible"
+    :title="'璁惧缁翠慨'"
+    width="500px"
+    @confirm="sendForm"
+    @cancel="handleCancel"
+    @close="handleClose"
+  >
     <el-form :model="form" :rules="rules" label-width="110px" ref="formRef">
       <el-form-item label="缁翠慨浜�" prop="maintenanceName">
         <el-input v-model="form.maintenanceName" placeholder="璇疯緭鍏ョ淮淇汉" />
@@ -7,9 +14,9 @@
       <el-form-item label="缁翠慨缁撴灉" prop="maintenanceResult">
         <el-input v-model="form.maintenanceResult" placeholder="璇疯緭鍏ョ淮淇粨鏋�" />
       </el-form-item>
-      <el-form-item label="鏈缁翠慨閲戦" prop="repairPrice">
-        <el-input-number v-model="form.repairPrice" :min="0" :precision="2" style="width: 100%" />
-      </el-form-item>
+			<el-form-item label="鏈缁翠慨閲戦" prop="repairPrice">
+				<el-input-number v-model="form.repairPrice" :min="0" :precision="2" style="width: 100%" />
+			</el-form-item>
       <el-form-item label="缁翠慨鏃ユ湡" prop="maintenanceTime">
         <el-date-picker
           v-model="form.maintenanceTime"
@@ -22,22 +29,16 @@
         />
       </el-form-item>
     </el-form>
-    <template #footer>
-			<el-button type="primary" @click="sendForm" :loading="loading">
-				{{ modalOptions.confirmText }}
-			</el-button>
-      <el-button @click="handleDialogClose">{{ modalOptions.cancelText }}</el-button>
-    </template>
-  </el-dialog>
+  </FormDialog>
 </template>
 
 <script setup>
-import { ref, nextTick } from "vue";
-import { useModal } from "@/hooks/useModal";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import { addMaintain } from "@/api/equipmentManagement/repair";
 import useFormData from "@/hooks/useFormData";
 import useUserStore from "@/store/modules/user";
 import dayjs from "dayjs";
-import { addMaintain } from "@/api/equipmentManagement/repair";
+import { ElMessage } from "element-plus";
 
 defineOptions({
   name: "缁翠慨妯℃�佹",
@@ -45,23 +46,19 @@
 
 const emits = defineEmits(["ok"]);
 
-const {
-  id,
-  visible,
-  loading,
-  openModal,
-  modalOptions,
-  handleConfirm,
-  closeModal,
-} = useModal({ title: "璁惧缁翠慨" });
+// 淇濆瓨鎶ヤ慨璁板綍鐨刬d
+const repairId = ref();
+const visible = ref(false);
+const loading = ref(false);
+const formRef = ref();
 
 const userStore = useUserStore();
-const formRef = ref();
 const { form, resetForm } = useFormData({
   maintenanceName: undefined, // 缁翠慨鍚嶇О
   maintenanceResult: undefined, // 缁翠慨缁撴灉
   maintenanceTime: undefined, // 缁翠慨鏃ユ湡
-  repairPrice: undefined, // 鏈缁翠慨閲戦
+	repairPrice: undefined, // 缁翠慨閲戦
+  status: 0,
 });
 
 const rules = {
@@ -74,36 +71,46 @@
 const setForm = (data) => {
   form.maintenanceName = data.maintenanceName ?? userStore.nickName;
   form.maintenanceResult = data.maintenanceResult;
-  form.repairPrice = data.repairPrice;
   form.maintenanceTime =
-    dayjs(data.maintenanceTime).format("YYYY-MM-DD HH:mm:ss") ??
-    dayjs().format("YYYY-MM-DD HH:mm:ss");
+    data.maintenanceTime 
+      ? dayjs(data.maintenanceTime).format("YYYY-MM-DD HH:mm:ss")
+      : dayjs().format("YYYY-MM-DD HH:mm:ss");
+  form.status = 2; // 缁翠慨鍚庡浐瀹氳繘鍏ュ緟瀹℃牳锛堜笉鍦ㄧ晫闈㈠睍绀猴級
 };
 
 const sendForm = async () => {
-  await formRef.value.validate(async (valid) => {
-    if (!valid) return;
-    const { code } = await addMaintain({ id: id.value, ...form });
-    if (code == 200) {
-      emits("ok");
-      resetForm();
-      closeModal();
-    }
-  });
+	await formRef.value.validate(async (valid) => {
+		if (!valid) return;
+		loading.value = true;
+		try {
+			const { code } = await addMaintain({ id: repairId.value, ...form });
+			if (code == 200) {
+				ElMessage.success("缁翠慨鎴愬姛");
+				emits("ok");
+				resetForm();
+				visible.value = false;
+			}
+		} finally {
+			loading.value = false;
+		}
+	})
+};
+
+const handleCancel = () => {
+  resetForm();
+  visible.value = false;
+};
+
+const handleClose = () => {
+  resetForm();
+  visible.value = false;
 };
 
 const open = async (id, row) => {
-  openModal(id);
+  repairId.value = id; // 淇濆瓨鎶ヤ慨璁板綍鐨刬d
+  visible.value = true;
   await nextTick();
   setForm(row);
-};
-
-const handleDialogClose = () => {
-  resetForm();
-  nextTick(() => {
-    formRef.value?.clearValidate?.();
-  });
-  closeModal();
 };
 
 defineExpose({
diff --git a/src/views/equipmentManagement/repair/Modal/RepairModal.vue b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
index 9f86722..20c9c8d 100644
--- a/src/views/equipmentManagement/repair/Modal/RepairModal.vue
+++ b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -1,10 +1,17 @@
 <template>
-  <el-dialog v-model="visible" :title="modalOptions.title" @close="close">
-    <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
+  <FormDialog
+    v-model="visible"
+    :title="id ? '缂栬緫璁惧鎶ヤ慨' : '鏂板璁惧鎶ヤ慨'"
+    width="800px"
+    @confirm="sendForm"
+    @cancel="handleCancel"
+    @close="handleClose"
+  >
+    <el-form :model="form" :rules="rules" label-width="100px" ref="formRef">
       <el-row>
         <el-col :span="12">
-          <el-form-item label="璁惧鍚嶇О" prop="deviceLedgerId">
-            <el-select v-model="form.deviceLedgerId" filterable allow-create default-first-option @change="setDeviceModel">
+          <el-form-item label="璁惧鍚嶇О">
+            <el-select v-model="form.deviceLedgerId" @change="setDeviceModel" filterable>
               <el-option
                 v-for="(item, index) in deviceOptions"
                 :key="index"
@@ -16,11 +23,15 @@
         </el-col>
         <el-col :span="12">
           <el-form-item label="瑙勬牸鍨嬪彿">
-            <el-input v-model="form.deviceModel" placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�" disabled />
+            <el-input
+              v-model="form.deviceModel"
+              placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+              disabled
+            />
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="鎶ヤ慨鏃ユ湡" prop="repairTime">
+          <el-form-item label="鎶ヤ慨鏃ユ湡">
             <el-date-picker
               v-model="form.repairTime"
               placeholder="璇烽�夋嫨鎶ヤ慨鏃ユ湡"
@@ -33,82 +44,116 @@
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="鎶ヤ慨浜�" prop="repairName">
+          <el-form-item label="鎶ヤ慨浜�">
             <el-input v-model="form.repairName" placeholder="璇疯緭鍏ユ姤淇汉" />
           </el-form-item>
         </el-col>
+        <el-col :span="12">
+          <el-form-item label="瀹℃壒浜�" prop="auditName">
+            <el-select
+              v-model="form.auditName"
+              placeholder="璇烽�夋嫨瀹℃壒浜�"
+              filterable
+              clearable
+              style="width: 100%"
+            >
+              <el-option
+                v-for="user in userOptions"
+                :key="user.userId"
+                :label="user.nickName"
+                :value="user.nickName"
+              />
+            </el-select>
+          </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-option label="瀹℃牳涓嶉�氳繃" :value="3"></el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
         <el-col :span="24">
-          <el-form-item label="鏁呴殰鐜拌薄" prop="remark">
-            <el-input v-model="form.remark" :rows="2" type="textarea" placeholder="璇疯緭鍏ユ晠闅滅幇璞�" />
+          <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 #footer>
-			<el-button type="primary" @click="sendForm" :loading="loading">
-				{{ modalOptions.confirmText }}
-			</el-button>
-      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
-    </template>
-  </el-dialog>
-  
+  </FormDialog>
 </template>
 
 <script setup>
-import { ref, nextTick } from "vue";
-import { useModal } from "@/hooks/useModal";
-import useFormData from "@/hooks/useFormData";
-import useUserStore from "@/store/modules/user";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
 import {
   addRepair,
   editRepair,
   getRepairById,
 } from "@/api/equipmentManagement/repair";
-import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
 import { ElMessage } from "element-plus";
+import dayjs from "dayjs";
+import useFormData from "@/hooks/useFormData";
+import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
+import useUserStore from "@/store/modules/user";
+import { userListNoPageByTenantId } from "@/api/system/user";
 
 defineOptions({
   name: "璁惧鎶ヤ慨寮圭獥",
 });
 
 const emits = defineEmits(["ok"]);
-const {
-  id,
-  visible,
-  loading,
-  openModal,
-  modalOptions,
-  handleConfirm,
-  closeModal,
-} = useModal({ title: "璁惧鎶ヤ慨" });
+
+const id = ref();
+const visible = ref(false);
+const loading = ref(false);
+const formRef = ref();
 
 const userStore = useUserStore();
 const deviceOptions = ref([]);
-const formRef = ref();
-const { form, resetForm } = useFormData({
-  deviceLedgerId: undefined, // 璁惧Id
-  deviceName: undefined, // 璁惧鍚嶇О
-  deviceModel: undefined, // 瑙勬牸鍨嬪彿
-  repairTime: undefined, // 鎶ヤ慨鏃ユ湡
-  repairName: userStore.nickName, // 鎶ヤ慨浜�
-  remark: undefined, // 鏁呴殰鐜拌薄
-});
-
-const rules = {
-  deviceLedgerId: [{ required: true, message: "璇烽�夋嫨璁惧鍚嶇О", trigger: "change" }],
-  repairTime: [{ required: true, message: "璇烽�夋嫨鎶ヤ慨鏃ユ湡", trigger: "change" }],
-  repairName: [{ required: true, message: "璇疯緭鍏ユ姤淇汉", trigger: "blur" }],
-  remark: [{ required: true, message: "璇疯緭鍏ユ晠闅滅幇璞�", trigger: "blur" }],
-};
+const userOptions = ref([]);
 
 const loadDeviceName = async () => {
   const { data } = await getDeviceLedger();
   deviceOptions.value = data;
 };
 
-const setDeviceModel = (id) => {
-  const option = deviceOptions.value.find((item) => item.id === id);
-  form.deviceModel = option ? option.deviceModel : undefined;
+const loadUserOptions = async () => {
+  const res = await userListNoPageByTenantId();
+  userOptions.value = res?.data ?? [];
+};
+
+const { form, resetForm } = useFormData({
+  deviceLedgerId: undefined, // 璁惧Id
+  deviceName: undefined, // 璁惧鍚嶇О
+  deviceModel: undefined, // 瑙勬牸鍨嬪彿
+  repairTime: dayjs().format("YYYY-MM-DD"), // 鎶ヤ慨鏃ユ湡锛岄粯璁ゅ綋澶�
+  repairName: userStore.nickName, // 鎶ヤ慨浜�
+  auditName: undefined, // 瀹℃壒浜�
+  remark: undefined, // 鏁呴殰鐜拌薄
+  status: 0, // 鎶ヤ慨鐘舵��
+});
+
+const rules = {
+  auditName: [
+    { required: true, message: "璇烽�夋嫨瀹℃壒浜�", trigger: "change" },
+  ],
+};
+
+const setDeviceModel = (deviceId) => {
+  const option = deviceOptions.value.find((item) => item.id === deviceId);
+  form.deviceModel = option.deviceModel;
 };
 
 const setForm = (data) => {
@@ -116,44 +161,55 @@
   form.deviceName = data.deviceName;
   form.deviceModel = data.deviceModel;
   form.repairTime = data.repairTime;
-  form.repairName = data.repairName ?? userStore.nickName;
+  form.repairName = data.repairName;
+  form.auditName = data.auditName;
   form.remark = data.remark;
+  form.status = data.status;
 };
 
 const sendForm = async () => {
-  await formRef.value.validate(async (valid) => {
+  await formRef.value?.validate(async (valid) => {
     if (!valid) return;
     loading.value = true;
-    const payload = { ...form };
-    const { code } = id.value
-      ? await editRepair({ id: id.value, ...payload })
-      : await addRepair(payload);
-    if (code == 200) {
-      ElMessage.success(`${id.value ? "缂栬緫" : "鏂板"}鎶ヤ慨鎴愬姛`);
-      closeModal();
-      emits("ok");
+    try {
+      const { code } = id.value
+        ? await editRepair({ id: unref(id), ...form })
+        : await addRepair(form);
+      if (code == 200) {
+        ElMessage.success(`${id.value ? "缂栬緫" : "鏂板"}鎶ヤ慨鎴愬姛`);
+        visible.value = false;
+        emits("ok");
+      }
+    } finally {
+      loading.value = false;
     }
-    loading.value = false;
   });
 };
 
+const handleCancel = () => {
+  resetForm();
+  visible.value = false;
+};
+
+const handleClose = () => {
+  resetForm();
+  visible.value = false;
+};
+
 const openAdd = async () => {
-  openModal();
+  id.value = undefined;
+  visible.value = true;
   await nextTick();
-  await loadDeviceName();
+  await Promise.all([loadDeviceName(), loadUserOptions()]);
 };
 
 const openEdit = async (editId) => {
   const { data } = await getRepairById(editId);
-  openModal(editId);
+  id.value = editId;
+  visible.value = true;
   await nextTick();
-  await loadDeviceName();
+  await Promise.all([loadDeviceName(), loadUserOptions()]);
   setForm(data);
-};
-
-const close = () => {
-  resetForm();
-  closeModal();
 };
 
 defineExpose({
@@ -161,3 +217,5 @@
   openEdit,
 });
 </script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/equipmentManagement/repair/index.vue b/src/views/equipmentManagement/repair/index.vue
index 1302ef6..86e6848 100644
--- a/src/views/equipmentManagement/repair/index.vue
+++ b/src/views/equipmentManagement/repair/index.vue
@@ -14,7 +14,7 @@
         <el-input
             v-model="filters.deviceModel"
             style="width: 240px"
-            placeholder="璇烽�夋嫨瑙勬牸鍨嬪彿"
+            placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
             clearable
             @change="getTableData"
         />
@@ -64,16 +64,8 @@
       <div class="actions">
         <el-text class="mx-1" size="large">璁惧鎶ヤ慨</el-text>
         <div>
-					<el-button type="success" icon="Van" @click="addRepair">
-						鏂板鎶ヤ慨
-					</el-button>
-          <el-button
-            type="primary"
-            icon="Plus"
-            :disabled="multipleList.length !== 1"
-            @click="addMaintain"
-          >
-            鏂板缁翠慨
+          <el-button type="success" icon="Van" @click="addRepair">
+            鏂板鎶ヤ慨
           </el-button>
           <el-button @click="handleOut">
             瀵煎嚭
@@ -81,7 +73,7 @@
           <el-button
             type="danger"
             icon="Delete"
-            :disabled="multipleList.length <= 0"
+            :disabled="multipleList.length <= 0 || hasFinishedStatus"
             @click="delRepairByIds(multipleList.map((item) => item.id))"
           >
             鎵归噺鍒犻櫎
@@ -89,33 +81,53 @@
         </div>
       </div>
       <PIMTable
-        rowKey="id"
-        isSelection
-        :column="columns"
-        :tableData="dataList"
-        :page="{
+          rowKey="id"
+          isSelection
+          :column="columns"
+          :tableData="dataList"
+          :page="{
           current: pagination.currentPage,
           size: pagination.pageSize,
           total: pagination.total,
         }"
-        @selection-change="handleSelectionChange"
-        @pagination="changePage"
+          @selection-change="handleSelectionChange"
+          @pagination="changePage"
       >
         <template #statusRef="{ row }">
-          <el-tag v-if="row.status === 1" type="success">瀹岀粨</el-tag>
-          <el-tag v-if="row.status === 0" type="danger">寰呯淮淇�</el-tag>
+          <el-tag v-if="row.status === 0" type="warning">寰呯淮淇�</el-tag>
+          <el-tag v-else-if="row.status === 1" type="success">瀹岀粨</el-tag>
+          <el-tag v-else-if="row.status === 2" type="info">寰呭鏍�</el-tag>
+          <el-tag v-else-if="row.status === 3" type="danger">瀹℃牳涓嶉�氳繃</el-tag>
         </template>
         <template #operation="{ row }">
           <el-button
             type="primary"
             link
+            :disabled="row.status === 1"
             @click="editRepair(row.id)"
           >
             缂栬緫
           </el-button>
           <el-button
+            type="warning"
+            link
+            :disabled="row.status !== 2"
+            @click="openApprove(row.id)"
+          >
+            瀹℃壒
+          </el-button>
+          <el-button
+            type="success"
+            link
+            :disabled="row.status !== 0"
+            @click="addMaintain(row)"
+          >
+            缁翠慨
+          </el-button>
+          <el-button
             type="danger"
-						link
+            link
+            :disabled="row.status === 1"
             @click="delRepairByIds(row.id)"
           >
             鍒犻櫎
@@ -123,29 +135,32 @@
         </template>
       </PIMTable>
     </div>
-    <RepairModal ref="repairModalRef" @ok="getTableData" />
-    <MaintainModal ref="maintainModalRef" @ok="getTableData" />
+    <RepairModal ref="repairModalRef" @ok="getTableData"/>
+    <MaintainModal ref="maintainModalRef" @ok="getTableData"/>
+    <ApproveModal ref="approveModalRef" @ok="getTableData"/>
   </div>
 </template>
 
 <script setup>
-import { usePaginationApi } from "@/hooks/usePaginationApi";
-import { getRepairPage, delRepair } from "@/api/equipmentManagement/repair";
-import { onMounted, getCurrentInstance } from "vue";
+import { onMounted, getCurrentInstance, computed } from "vue";
+import {usePaginationApi} from "@/hooks/usePaginationApi";
+import {getRepairPage, delRepair} from "@/api/equipmentManagement/repair";
 import RepairModal from "./Modal/RepairModal.vue";
-import { ElMessageBox, ElMessage } from "element-plus";
+import {ElMessageBox, ElMessage} from "element-plus";
 import dayjs from "dayjs";
 import MaintainModal from "./Modal/MaintainModal.vue";
+import ApproveModal from "./Modal/ApproveModal.vue";
 
 defineOptions({
   name: "璁惧鎶ヤ慨",
 });
 
-const { proxy } = getCurrentInstance();
+const {proxy} = getCurrentInstance();
 
 // 妯℃�佹瀹炰緥
 const repairModalRef = ref();
 const maintainModalRef = ref();
+const approveModalRef = ref();
 
 // 琛ㄦ牸澶氶�夋閫変腑椤�
 const multipleList = ref([]);
@@ -160,85 +175,85 @@
   resetFilters,
   onCurrentChange,
 } = usePaginationApi(
-  getRepairPage,
-  {
-    deviceName: undefined,
-    deviceModel: undefined,
-    remark: undefined,
-    maintenanceName: undefined,
-    repairTimeStr: undefined,
-    maintenanceTimeStr: undefined,
-  },
-  [
+    getRepairPage,
     {
-      label: "璁惧鍚嶇О",
-      align: "center",
-      prop: "deviceName",
+      deviceName: undefined,
+      deviceModel: undefined,
+      remark: undefined,
+      maintenanceName: undefined,
+      repairTimeStr: undefined,
+      maintenanceTimeStr: undefined,
     },
-    {
-      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: "150px",
-    },
-  ]
+    [
+      {
+        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: "300px",
+      },
+    ]
 );
 
 // type === 1 缁翠慨 2鎶ヤ慨闂�
-const handleDateChange = (value,type) => {
+const handleDateChange = (value, type) => {
   filters.maintenanceTimeStr = null
   filters.c = null
-  if(type === 1){
+  if (type === 1) {
     if (value) {
       filters.maintenanceTimeStr = dayjs(value).format("YYYY-MM-DD");
     }
-  }else{
+  } else {
     if (value) {
       filters.repairTimeStr = dayjs(value).format("YYYY-MM-DD");
     }
@@ -251,6 +266,11 @@
   multipleList.value = selectionList;
 };
 
+// 妫�鏌ラ�変腑鐨勮褰曚腑鏄惁鏈夊畬缁撶姸鎬佺殑
+const hasFinishedStatus = computed(() => {
+  return multipleList.value.some(item => item.status === 1)
+})
+
 // 鏂板鎶ヤ慨
 const addRepair = () => {
   repairModalRef.value.openAdd();
@@ -262,25 +282,41 @@
 };
 
 // 鏂板缁翠慨
-const addMaintain = () => {
-  const row = multipleList.value[0];
+const addMaintain = (row) => {
   maintainModalRef.value.open(row.id, row);
 };
 
-const changePage = ({ page, limit }) => {
-	pagination.currentPage = page;
-	pagination.pageSize = limit;
-	onCurrentChange(page);
+// 瀹℃壒
+const openApprove = (id) => {
+  approveModalRef.value.open(id);
+};
+
+const changePage = ({page, limit}) => {
+  pagination.currentPage = page;
+  pagination.pageSize = limit;
+  onCurrentChange(page);
 };
 
 // 鍗曡鍒犻櫎
 const delRepairByIds = async (ids) => {
+  // 妫�鏌ユ槸鍚︽湁瀹岀粨鐘舵�佺殑璁板綍
+  const idsArray = Array.isArray(ids) ? ids : [ids];
+  const hasFinished = idsArray.some(id => {
+    const record = dataList.value.find(item => item.id === id);
+    return record && record.status === 1;
+  });
+
+  if (hasFinished) {
+    ElMessage.warning('涓嶈兘鍒犻櫎鐘舵�佷负瀹岀粨鐨勮褰�');
+    return;
+  }
+
   ElMessageBox.confirm("纭鍒犻櫎鎶ヤ慨鏁版嵁, 姝ゆ搷浣滀笉鍙��?", "璀﹀憡", {
     confirmButtonText: "纭畾",
     cancelButtonText: "鍙栨秷",
     type: "warning",
   }).then(async () => {
-    const { code } = await delRepair(ids);
+    const {code} = await delRepair(ids);
     if (code === 200) {
       ElMessage.success("鍒犻櫎鎴愬姛");
       getTableData();
@@ -295,12 +331,12 @@
     cancelButtonText: "鍙栨秷",
     type: "warning",
   })
-    .then(() => {
-      proxy.download("/device/repair/export", {}, "璁惧鎶ヤ慨.xlsx");
-    })
-    .catch(() => {
-      ElMessage.info("宸插彇娑�");
-    });
+      .then(() => {
+        proxy.download("/device/repair/export", {}, "璁惧鎶ヤ慨.xlsx");
+      })
+      .catch(() => {
+        ElMessage.info("宸插彇娑�");
+      });
 };
 
 onMounted(() => {
@@ -312,6 +348,7 @@
 .table_list {
   margin-top: unset;
 }
+
 .actions {
   display: flex;
   justify-content: space-between;
diff --git a/src/views/equipmentManagement/upkeep/Form/ApproveModal.vue b/src/views/equipmentManagement/upkeep/Form/ApproveModal.vue
new file mode 100644
index 0000000..eb74cec
--- /dev/null
+++ b/src/views/equipmentManagement/upkeep/Form/ApproveModal.vue
@@ -0,0 +1,137 @@
+<template>
+  <FormDialog
+    v-model="visible"
+    title="瀹氭椂浠诲姟瀹℃壒"
+    width="800px"
+    @confirm="handleSubmit"
+    @cancel="handleClose"
+    @close="handleClose"
+  >
+    <el-descriptions :column="2" border>
+      <el-descriptions-item label="浠诲姟鍚嶇О">
+        {{ detail.taskName || "-" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="瑙勬牸鍨嬪彿">
+        {{ detail.deviceModel || "-" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="棰戞">
+        {{ frequencyText(detail.frequencyType) }}
+      </el-descriptions-item>
+      <el-descriptions-item label="寮�濮嬫棩鏈熶笌鏃堕棿">
+        {{ detail.frequencyDetail || "-" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="鐧昏浜�">
+        {{ detail.registrant || "-" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="鐧昏鏃ユ湡">
+        {{ detail.registrationDate || "-" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="褰撳墠鐘舵��">
+        {{ statusText(detail.status) }}
+      </el-descriptions-item>
+      <el-descriptions-item label="澶囨敞" :span="2">
+        {{ detail.remarks || "-" }}
+      </el-descriptions-item>
+    </el-descriptions>
+    <div style="margin-top: 16px">
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="瀹℃壒缁撴灉" prop="decision">
+          <el-radio-group v-model="form.decision">
+            <el-radio label="瀹℃牳閫氳繃">瀹℃牳閫氳繃</el-radio>
+            <el-radio label="瀹℃牳涓嶉�氳繃">瀹℃牳涓嶉�氳繃</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+    </div>
+  </FormDialog>
+</template>
+
+<script setup>
+import { nextTick, ref } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import { deviceMaintenanceTaskEdit } from "@/api/equipmentManagement/upkeep";
+
+defineOptions({
+  name: "瀹氭椂浠诲姟瀹℃壒寮圭獥",
+});
+
+const emits = defineEmits(["ok"]);
+
+const visible = ref(false);
+const loading = ref(false);
+const detail = ref({});
+const formRef = ref();
+const form = ref({
+  decision: undefined, // 瀹℃牳閫氳繃 / 瀹℃牳涓嶉�氳繃
+});
+
+const rules = {
+  decision: [{ required: true, message: "璇烽�夋嫨瀹℃壒缁撴灉", trigger: "change" }],
+};
+
+const statusText = (status) => status || "-";
+
+const frequencyText = (type) => {
+  const map = {
+    DAILY: "姣忔棩",
+    WEEKLY: "姣忓懆",
+    MONTHLY: "姣忔湀",
+    QUARTERLY: "瀛e害",
+  };
+  return map[type] ?? "-";
+};
+
+const open = async (row) => {
+  detail.value = { ...(row || {}) };
+  visible.value = true;
+  await nextTick();
+  form.value.decision = undefined;
+};
+
+const handleClose = () => {
+  visible.value = false;
+  detail.value = {};
+  form.value.decision = undefined;
+};
+
+const updateStatus = async (status) => {
+  loading.value = true;
+  try {
+    const payload = { ...(detail.value || {}), status };
+    const { code } = await deviceMaintenanceTaskEdit(payload);
+    if (code === 200) {
+      ElMessage.success("瀹℃壒鎴愬姛");
+      emits("ok");
+      handleClose();
+    }
+  } finally {
+    loading.value = false;
+  }
+};
+
+const handleSubmit = async () => {
+  if (detail.value?.status === "瀹℃牳閫氳繃") {
+    ElMessage.warning("瀹℃牳閫氳繃鍚庝笉鍙啀娆″鎵�");
+    return;
+  }
+  await formRef.value?.validate(async (valid) => {
+    if (!valid) return;
+    const isApprove = form.value.decision === "瀹℃牳閫氳繃";
+    ElMessageBox.confirm(
+      `纭瀹℃壒${isApprove ? "閫氳繃" : "涓嶉�氳繃"}锛焋,
+      "鎻愮ず",
+      {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      }
+    ).then(() => updateStatus(form.value.decision));
+  });
+};
+
+defineExpose({ open });
+</script>
+
+<style scoped></style>
+
diff --git a/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
new file mode 100644
index 0000000..2951df7
--- /dev/null
+++ b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
@@ -0,0 +1,146 @@
+<template>
+  <FormDialog
+    v-model="visible"
+    :title="'璁惧淇濆吇'"
+    width="500px"
+    @confirm="sendForm"
+    @cancel="handleCancel"
+    @close="handleClose"
+  >
+    <el-form :model="form" :rules="rules" label-width="120px" ref="formRef">
+      <el-form-item label="瀹為檯淇濆吇浜�" prop="maintenanceActuallyName">
+        <el-input
+          v-model="form.maintenanceActuallyName"
+          placeholder="璇疯緭鍏ュ疄闄呬繚鍏讳汉"
+        ></el-input>
+      </el-form-item>
+      <el-form-item label="瀹為檯淇濆吇鏃ユ湡" prop="maintenanceActuallyTime">
+        <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="淇濆吇鐘舵��" prop="status">
+        <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="淇濆吇缁撴灉" prop="maintenanceResult">
+        <el-input
+          v-model="form.maintenanceResult"
+          placeholder="璇疯緭鍏ヤ繚鍏荤粨鏋�"
+          type="text" />
+      </el-form-item>
+			<el-form-item label="鏈淇濆吇閲戦" prop="maintenancePrice">
+				<el-input-number v-model="form.maintenancePrice" :min="0" :precision="2" style="width: 100%" />
+			</el-form-item>
+    </el-form>
+  </FormDialog>
+</template>
+
+<script setup>
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import { addMaintenance } from "@/api/equipmentManagement/upkeep";
+import useFormData from "@/hooks/useFormData";
+import dayjs from "dayjs";
+import useUserStore from "@/store/modules/user";
+import { ElMessage } from "element-plus";
+
+defineOptions({
+  name: "淇濆吇妯℃�佹",
+});
+
+const emits = defineEmits(["ok"]);
+
+// 淇濆瓨璁″垝淇濆吇璁板綍鐨刬d
+const planId = ref();
+const visible = ref(false);
+const loading = ref(false);
+const formRef = ref();
+const userStore = useUserStore();
+
+const { form, resetForm } = useFormData({
+  maintenanceActuallyName: undefined, // 瀹為檯淇濆吇浜�
+  maintenanceActuallyTime: undefined, // 瀹為檯淇濆吇鏃ユ湡
+  maintenanceResult: undefined, // 淇濆吇缁撴灉
+	maintenancePrice: undefined, // 淇濆吇閲戦
+  status: 0, // 淇濆吇鐘舵��
+});
+
+const rules = {
+  maintenanceActuallyName: [
+    { required: true, message: "璇疯緭鍏ュ疄闄呬繚鍏讳汉", trigger: "blur" },
+  ],
+  maintenanceActuallyTime: [
+    { required: true, message: "璇烽�夋嫨瀹為檯淇濆吇鏃ユ湡", trigger: "change" },
+  ],
+  maintenanceResult: [
+    { required: true, message: "璇疯緭鍏ヤ繚鍏荤粨鏋�", trigger: "blur" },
+  ],
+  maintenancePrice: [
+    { required: true, message: "璇疯緭鍏ユ湰娆′繚鍏婚噾棰�", trigger: "change" },
+  ],
+};
+
+const setForm = (data) => {
+  form.maintenanceActuallyName =
+    data.maintenanceActuallyName ?? userStore.nickName;
+  form.maintenanceActuallyTime =
+    data.maintenanceActuallyTime 
+      ? dayjs(data.maintenanceActuallyTime).format("YYYY-MM-DD HH:mm:ss")
+      : dayjs().format("YYYY-MM-DD HH:mm:ss");
+  form.maintenanceResult = data.maintenanceResult;
+  form.status = 1; // 榛樿鐘舵�佷负瀹岀粨
+};
+
+/**
+ * @desc 淇濆瓨淇濆吇
+ */
+const sendForm = async () => {
+  await formRef.value?.validate(async (valid) => {
+    if (!valid) return;
+    loading.value = true;
+    try {
+      const { code } = await addMaintenance({ id: planId.value, ...form });
+      if (code == 200) {
+        ElMessage.success("淇濆吇鎴愬姛");
+        emits("ok");
+        resetForm();
+        visible.value = false;
+      }
+    } finally {
+      loading.value = false;
+    }
+  });
+};
+
+const handleCancel = () => {
+  resetForm();
+  visible.value = false;
+};
+
+const handleClose = () => {
+  resetForm();
+  visible.value = false;
+};
+
+const open = async (id, row) => {
+  planId.value = id; // 淇濆瓨璁″垝淇濆吇璁板綍鐨刬d
+  visible.value = true;
+  await nextTick();
+  setForm(row);
+};
+
+defineExpose({
+  open,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/equipmentManagement/upkeep/Form/PlanModal.vue b/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
new file mode 100644
index 0000000..19095b9
--- /dev/null
+++ b/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
@@ -0,0 +1,188 @@
+<template>
+  <FormDialog
+    v-model="visible"
+    :title="id ? '缂栬緫璁惧淇濆吇璁″垝' : '鏂板璁惧淇濆吇璁″垝'"
+    width="500px"
+    @confirm="sendForm"
+    @cancel="handleCancel"
+    @close="handleClose"
+  >
+    <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.nickName"
+            :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>
+  </FormDialog>
+</template>
+
+<script setup>
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import {
+  addUpkeep,
+  editUpkeep,
+  getUpkeepById,
+} from "@/api/equipmentManagement/upkeep";
+import { ElMessage } from "element-plus";
+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 emits = defineEmits(["ok"]);
+
+const id = ref();
+const visible = ref(false);
+const loading = ref(false);
+
+const deviceOptions = ref([]);
+const loadDeviceName = async () => {
+  const { data } = await getDeviceLedger();
+  deviceOptions.value = data;
+};
+
+const { form, resetForm } = useFormData({
+  deviceLedgerId: undefined, // 璁惧Id
+  deviceName: undefined, // 璁惧鍚嶇О
+  deviceModel: undefined, // 瑙勬牸鍨嬪彿
+  maintenancePlanTime: undefined, // 璁″垝淇濆吇鏃ユ湡
+  createUser: undefined, // 褰曞叆浜�
+  status: 0, //淇濅慨鐘舵��
+});
+
+const setDeviceModel = (deviceId) => {
+  const option = deviceOptions.value.find((item) => item.id === deviceId);
+  form.deviceModel = option.deviceModel;
+};
+
+/**
+ * @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([]);
+
+onMounted(() => {
+  loadDeviceName();
+  userListNoPage().then((res) => {
+    userList.value = res.data;
+  });
+});
+
+const openEdit = async (editId) => {
+  const { data } = await getUpkeepById(editId);
+  id.value = editId;
+  visible.value = true;
+  await nextTick();
+  setForm(data);
+};
+
+const sendForm = async () => {
+  loading.value = true;
+  try {
+    const { code } = id.value
+      ? await editUpkeep({ id: unref(id), ...form })
+      : await addUpkeep(form);
+    if (code == 200) {
+      ElMessage.success(`${id.value ? "缂栬緫" : "鏂板"}璁″垝鎴愬姛`);
+      visible.value = false;
+      emits("ok");
+    }
+  } finally {
+    loading.value = false;
+  }
+};
+
+const handleCancel = () => {
+  resetForm();
+  visible.value = false;
+};
+
+const handleClose = () => {
+  resetForm();
+  visible.value = false;
+};
+
+const openModal = () => {
+  id.value = undefined;
+  visible.value = true;
+};
+
+defineExpose({
+  openModal,
+  openEdit,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/equipmentManagement/upkeep/Form/formDia.vue b/src/views/equipmentManagement/upkeep/Form/formDia.vue
new file mode 100644
index 0000000..5a4d7ef
--- /dev/null
+++ b/src/views/equipmentManagement/upkeep/Form/formDia.vue
@@ -0,0 +1,325 @@
+<template>
+	<FormDialog
+		v-model="dialogVisitable"
+		:title="operationType === 'add' ? '鏂板淇濆吇浠诲姟' : '缂栬緫淇濆吇浠诲姟'"
+		width="800px"
+		:operation-type="operationType"
+		@confirm="submitForm"
+		@cancel="cancel"
+		@close="cancel"
+	>
+		<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+			<el-row>
+				<el-col :span="12">
+					<el-form-item label="璁惧鍚嶇О" prop="taskId">
+						<el-select v-model="form.taskId" @change="setDeviceModel" filterable>
+							<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-row>
+			<el-row>
+				<el-col :span="12">
+					<el-form-item label="褰曞叆浜�" prop="inspector">
+						<el-select
+							v-model="form.inspector"
+							filterable
+							default-first-option
+							:reserve-keyword="false"
+							placeholder="璇烽�夋嫨"
+							clearable
+						>
+							<el-option
+								v-for="item in userList"
+								:label="item.nickName"
+								:value="item.userId"
+								:key="item.userId"
+							/>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="鐧昏鏃堕棿" prop="registrationDate">
+						<el-date-picker
+							v-model="form.registrationDate"
+							type="date"
+							placeholder="閫夋嫨鐧昏鏃ユ湡"
+							format="YYYY-MM-DD"
+							value-format="YYYY-MM-DD"
+							style="width: 100%"
+						/>
+					</el-form-item>
+				</el-col>
+			</el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="瀹℃壒浜�" prop="auditName">
+            <el-select
+              v-model="form.auditName"
+              filterable
+              placeholder="璇烽�夋嫨瀹℃壒浜�"
+              clearable
+            >
+              <el-option
+                v-for="item in userList"
+                :key="item.userId"
+                :label="item.nickName"
+                :value="item.nickName"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+			<el-row>
+				<el-col :span="12">
+					<el-form-item label="浠诲姟棰戠巼" prop="frequencyType">
+						<el-select v-model="form.frequencyType" placeholder="璇烽�夋嫨" clearable>
+							<el-option label="姣忔棩" value="DAILY"/>
+							<el-option label="姣忓懆" value="WEEKLY"/>
+							<el-option label="姣忔湀" value="MONTHLY"/>
+							<el-option label="瀛e害" value="QUARTERLY"/>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType">
+					<el-form-item label="鏃ユ湡" prop="frequencyDetail">
+						<el-time-picker v-model="form.frequencyDetail" placeholder="閫夋嫨鏃堕棿" format="HH:mm"
+														value-format="HH:mm" />
+					</el-form-item>
+				</el-col>
+				<el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType">
+					<el-form-item label="鏃ユ湡" prop="frequencyDetail">
+						<el-select v-model="form.week" placeholder="璇烽�夋嫨" clearable style="width: 50%">
+							<el-option label="鍛ㄤ竴" value="MON"/>
+							<el-option label="鍛ㄤ簩" value="TUE"/>
+							<el-option label="鍛ㄤ笁" value="WED"/>
+							<el-option label="鍛ㄥ洓" value="THU"/>
+							<el-option label="鍛ㄤ簲" value="FRI"/>
+							<el-option label="鍛ㄥ叚" value="SAT"/>
+							<el-option label="鍛ㄦ棩" value="SUN"/>
+						</el-select>
+						<el-time-picker v-model="form.time" placeholder="閫夋嫨鏃堕棿" format="HH:mm"
+														value-format="HH:mm"  style="width: 50%"/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType">
+					<el-form-item label="鏃ユ湡" prop="frequencyDetail">
+						<el-date-picker
+							v-model="form.frequencyDetail"
+							type="datetime"
+							clearable
+							placeholder="閫夋嫨寮�濮嬫棩鏈�"
+							format="DD,HH:mm"
+							value-format="DD,HH:mm"
+						/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType">
+					<el-form-item label="鏃ユ湡" prop="frequencyDetail">
+						<el-date-picker
+							v-model="form.frequencyDetail"
+							type="datetime"
+							clearable
+							placeholder="閫夋嫨寮�濮嬫棩鏈�"
+							format="MM,DD,HH:mm"
+							value-format="MM,DD,HH:mm"
+						/>
+					</el-form-item>
+				</el-col>
+			</el-row>
+			<el-row>
+				<el-col :span="12">
+					<el-form-item label="澶囨敞" prop="remarks">
+						<el-input v-model="form.remarks" placeholder="璇疯緭鍏ュ娉�" type="textarea" />
+					</el-form-item>
+				</el-col>
+			</el-row>
+		</el-form>
+	</FormDialog>
+</template>
+
+<script setup>
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import { reactive, ref, getCurrentInstance, toRefs } from "vue";
+import {userListNoPageByTenantId} from "@/api/system/user.js";
+import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
+import { deviceMaintenanceTaskAdd, deviceMaintenanceTaskEdit } from "@/api/equipmentManagement/upkeep";
+import { getCurrentDate } from "@/utils/index.js";
+import useUserStore from "@/store/modules/user.js";
+
+const { proxy } = getCurrentInstance()
+const emit = defineEmits()
+const dialogVisitable = ref(false);
+const operationType = ref('add');
+const deviceOptions = ref([]);
+const userStore = useUserStore();
+const data = reactive({
+	form: {
+		taskId: undefined,
+		taskName: undefined,
+		// 褰曞叆浜猴細鍗曢�変竴涓敤鎴� id
+		inspector: undefined,
+    auditName: undefined,
+		remarks: '',
+		frequencyType: '',
+		frequencyDetail: '',
+		week: '',
+		time: '',
+		deviceModel: undefined, // 瑙勬牸鍨嬪彿
+		registrationDate: ''
+	},
+	rules: {
+		taskId: [{ required: true, message: "璇烽�夋嫨璁惧", trigger: "change" },],
+		inspector: [{ required: true, message: "璇烽�夋嫨褰曞叆浜�", trigger: "blur" },],
+		registrationDate: [{ required: true, message: "璇烽�夋嫨鐧昏鏃堕棿", trigger: "change" }],
+    auditName: [{ required: true, message: "璇烽�夋嫨瀹℃壒浜�", trigger: "change" }],
+	}
+})
+const { form, rules } = toRefs(data)
+const userList = ref([])
+
+const loadDeviceName = async () => {
+	const { data } = await getDeviceLedger();
+	deviceOptions.value = data;
+};
+
+// 閫夋嫨璁惧鏃讹紝鍥炲~璁惧鍚嶇О(taskName)鍜岃鏍煎瀷鍙�(deviceModel)
+const setDeviceModel = (id) => {
+	const option = deviceOptions.value.find((item) => item.id === id);
+	if (option) {
+		form.value.taskId = option.id;
+		form.value.taskName = option.deviceName;
+		form.value.deviceModel = option.deviceModel;
+	}
+}
+
+// 鎵撳紑寮规
+const openDialog = async (type, row) => {
+	dialogVisitable.value = true
+	operationType.value = type
+	
+	// 閲嶇疆琛ㄥ崟
+	resetForm();
+	
+	// 鍔犺浇鐢ㄦ埛鍒楄〃
+	userListNoPageByTenantId().then((res) => {
+		userList.value = res.data;
+	});
+	
+	// 鍔犺浇璁惧鍒楄〃
+	await loadDeviceName();
+	
+	if (type === 'edit' && row) {
+		form.value = { ...row }
+		// 缂栬緫鏃剁敤鎺ュ彛杩斿洖鐨� registrantId 鍥炴樉褰曞叆浜�
+		if (row.registrantId) {
+			form.value.inspector = row.registrantId
+		}
+
+		// 濡傛灉鏈夎澶嘔D锛岃嚜鍔ㄨ缃澶囦俊鎭�
+		if (form.value.taskId) {
+			setDeviceModel(form.value.taskId);
+		}
+	} else if (type === 'add') {
+		// 鏂板鏃惰缃櫥璁版棩鏈熶负褰撳ぉ
+		form.value.registrationDate = getCurrentDate();
+		// 鏂板鏃惰缃綍鍏ヤ汉涓哄綋鍓嶇櫥褰曡处鎴�
+		form.value.inspector = userStore.id;
+	}
+}
+
+// 鍏抽棴瀵硅瘽妗�
+const cancel = () => {
+	resetForm()
+	dialogVisitable.value = false
+	emit('closeDia')
+}
+
+// 閲嶇疆琛ㄥ崟鍑芥暟
+const resetForm = () => {
+	if (proxy.$refs.formRef) {
+		proxy.$refs.formRef.resetFields()
+	}
+	// 閲嶇疆琛ㄥ崟鏁版嵁纭繚璁惧淇℃伅姝g‘閲嶇疆
+	form.value = {
+		taskId: undefined,
+		taskName: undefined,
+		inspector: undefined,
+		auditName: undefined,
+		remarks: '',
+		frequencyType: '',
+		frequencyDetail: '',
+		week: '',
+		time: '',
+		deviceModel: undefined,
+		registrationDate: ''
+	}
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+	proxy.$refs["formRef"].validate(async valid => {
+		if (valid) {
+			try {
+				const payload = { ...form.value }
+				// 涓嶅啀鍚戝悗绔紶淇濆吇浜哄瓧娈碉紝浠呬娇鐢ㄦ帴鍙h姹傜殑 registrant / registrantId
+				// 鏍规嵁閫夋嫨鐨�"褰曞叆浜�"璁剧疆 registrant / registrantId
+				if (payload.inspector) {
+					const selectedUser = userList.value.find(
+						(u) => String(u.userId) === String(payload.inspector)
+					)
+					if (selectedUser) {
+						payload.registrantId = selectedUser.userId
+						payload.registrant = selectedUser.nickName
+					}
+				}
+				delete payload.inspector
+				delete payload.inspectorIds
+				
+				if (payload.frequencyType === 'WEEKLY') {
+					let frequencyDetail = ''
+					frequencyDetail = payload.week + ',' + payload.time
+					payload.frequencyDetail = frequencyDetail
+				}
+				
+				// 褰曞叆鏃ユ湡锛氱洿鎺ヤ娇鐢ㄨ〃鍗曢噷鐨� registrationDate 瀛楁
+				// 涓�浜涢粯璁ょ姸鎬佸瓧娈�
+				if (payload.status === undefined || payload.status === null || payload.status === '') {
+					payload.status = '寰呭鏍�' // 榛樿鐘舵�侊細寰呭鏍�
+				}
+				payload.active = true
+				payload.deleted = 0
+				
+				if (operationType.value === 'edit') {
+					await deviceMaintenanceTaskEdit(payload)
+				} else {
+					await deviceMaintenanceTaskAdd(payload)
+				}
+				cancel()
+				proxy.$modal.msgSuccess('鎻愪氦鎴愬姛')
+			} catch (error) {
+				proxy.$modal.msgError('鎻愪氦澶辫触锛岃閲嶈瘯')
+			}
+		}
+	})
+}
+defineExpose({ openDialog })
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue b/src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue
deleted file mode 100644
index c58e413..0000000
--- a/src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue
+++ /dev/null
@@ -1,123 +0,0 @@
-<template>
-  <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr">
-    <el-form :model="form" :rules="rules" label-width="110px" ref="formRef">
-      <el-form-item label="瀹為檯淇濆吇浜�" prop="maintenanceActuallyName">
-        <el-input
-          v-model="form.maintenanceActuallyName"
-          placeholder="璇疯緭鍏ュ疄闄呬繚鍏讳汉"
-        ></el-input>
-      </el-form-item>
-      <el-form-item label="瀹為檯淇濆吇鏃ユ湡" prop="maintenanceActuallyTime">
-        <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="淇濆吇缁撴灉" prop="maintenanceResult">
-        <el-select v-model="form.maintenanceResult" placeholder="璇烽�夋嫨淇濆吇缁撴灉">
-          <el-option label="瀹屽ソ" :value="1"></el-option>
-          <el-option label="缁翠慨" :value="0"></el-option>
-        </el-select>
-      </el-form-item>
-			<el-form-item label="鏈淇濆吇閲戦" prop="maintenancePrice">
-				<el-input-number v-model="form.maintenancePrice" :min="0" :precision="2" style="width: 100%" />
-			</el-form-item>
-    </el-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 { ref, nextTick } from "vue";
-import { useModal } from "@/hooks/useModal";
-import useFormData from "@/hooks/useFormData";
-import useUserStore from "@/store/modules/user";
-import dayjs from "dayjs";
-import { addMaintenance } from "@/api/equipmentManagement/upkeep";
-
-defineOptions({
-  name: "淇濆吇妯℃�佹",
-});
-
-const emits = defineEmits(["ok"]);
-
-const {
-  id,
-  visible,
-  loading,
-  openModal,
-  modalOptions,
-  handleConfirm,
-  closeModal,
-} = useModal({ title: "璁惧淇濆吇" });
-
-/**
- * @desc 淇濆瓨淇濆吇
- */
-const userStore = useUserStore();
-const formRef = ref();
-const { form, resetForm } = useFormData({
-  maintenanceActuallyName: undefined, // 瀹為檯淇濆吇浜�
-  maintenanceActuallyTime: undefined, // 瀹為檯淇濆吇鏃ユ湡
-  maintenanceResult: undefined, // 淇濆吇缁撴灉
-	maintenancePrice: undefined, // 淇濆吇閲戦
-});
-
-const rules = {
-  maintenanceActuallyName: [
-    { required: true, message: "璇疯緭鍏ュ疄闄呬繚鍏讳汉", trigger: "blur" },
-  ],
-  maintenanceActuallyTime: [
-    { required: true, message: "璇烽�夋嫨瀹為檯淇濆吇鏃ユ湡", trigger: "change" },
-  ],
-  maintenanceResult: [
-    { required: true, message: "璇烽�夋嫨淇濆吇缁撴灉", trigger: "change" },
-  ],
-	maintenancePrice: [
-    { required: true, message: "璇疯緭鍏ユ湰娆′繚鍏婚噾棰�", trigger: "change" },
-  ],
-};
-
-const sendForm = async () => {
-  await formRef.value.validate(async (valid) => {
-    if (!valid) return;
-    const { code } = await addMaintenance({ id: id.value, ...form });
-    if (code == 200) {
-      emits("ok");
-      resetForm();
-      closeModal();
-    }
-  });
-};
-
-const open = async (id, row) => {
-  openModal(id);
-  await nextTick();
-  if (!row) {
-    resetForm();
-    form.maintenanceActuallyName = userStore.nickName;
-    return;
-  }
-  form.maintenanceActuallyName = row.maintenanceActuallyName ?? userStore.nickName;
-  form.maintenanceActuallyTime = row.maintenanceActuallyTime
-    ? dayjs(row.maintenanceActuallyTime).format("YYYY-MM-DD HH:mm:ss")
-    : undefined;
-  form.maintenanceResult = row.maintenanceResult;
-  form.maintenancePrice = row.maintenancePrice;
-};
-defineExpose({
-  open,
-});
-</script>
-
-<style lang="scss" scoped></style>
diff --git a/src/views/equipmentManagement/upkeep/Modal/PlanModal.vue b/src/views/equipmentManagement/upkeep/Modal/PlanModal.vue
deleted file mode 100644
index 3dbbb62..0000000
--- a/src/views/equipmentManagement/upkeep/Modal/PlanModal.vue
+++ /dev/null
@@ -1,162 +0,0 @@
-<template>
-  <el-dialog
-    v-model="visible"
-    :title="modalOptions.title"
-    width="30%"
-    @close="close"
-  >
-    <el-form :model="form" :rules="rules" ref="formRef" label-width="110px">
-      <el-form-item label="璁惧鍚嶇О" prop="deviceLedgerId">
-        <el-select
-          v-model="form.deviceLedgerId"
-          filterable allow-create default-first-option
-          @change="setDeviceModel"
-          placeholder="璇烽�夋嫨璁惧"
-        >
-          <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="璁″垝淇濆吇鏃ユ湡" prop="maintenancePlanTime">
-        <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 #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 { ref, nextTick, watch } from "vue";
-import { useModal } from "@/hooks/useModal";
-import useFormData from "@/hooks/useFormData";
-import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
-import dayjs from "dayjs";
-import {
-  addUpkeep,
-  editUpkeep,
-  getUpkeepById,
-} from "@/api/equipmentManagement/upkeep";
-import { ElMessage } from "element-plus";
-
-defineOptions({
-  name: "璁惧淇濆吇鏂板璁″垝",
-});
-
-const emits = defineEmits(["ok"]);
-const {
-  id,
-  visible,
-  loading,
-  openModal,
-  modalOptions,
-  handleConfirm,
-  closeModal,
-} = useModal({ title: "璁惧淇濆吇璁″垝" });
-
-const deviceOptions = ref([]);
-const formRef = ref();
-const { form, resetForm } = useFormData({
-  deviceLedgerId: undefined, // 璁惧Id
-  deviceName: undefined, // 璁惧鍚嶇О
-  deviceModel: undefined, // 瑙勬牸鍨嬪彿
-  maintenancePlanTime: undefined, // 璁″垝淇濆吇鏃ユ湡
-});
-
-const rules = {
-  deviceLedgerId: [{ required: true, message: "璇烽�夋嫨璁惧", trigger: "change" }],
-  maintenancePlanTime: [
-    { required: true, message: "璇烽�夋嫨璁″垝淇濆吇鏃ユ湡", trigger: "change" },
-  ],
-};
-
-const loadDeviceName = async () => {
-  const { data } = await getDeviceLedger();
-  deviceOptions.value = data;
-};
-
-const setDeviceModel = (id) => {
-  const option = deviceOptions.value.find((item) => item.id === id);
-  form.deviceModel = option ? option.deviceModel : undefined;
-};
-
-const setForm = (data) => {
-  form.deviceLedgerId = data.deviceLedgerId;
-  form.deviceName = data.deviceName;
-  form.deviceModel = data.deviceModel;
-  form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
-    "YYYY-MM-DD HH:mm:ss"
-  );
-};
-
-const openEdit = async (editId) => {
-  const { data } = await getUpkeepById(editId);
-  openModal(editId);
-  await nextTick();
-  await loadDeviceName();
-  setForm(data);
-};
-
-const sendForm = async () => {
-  await formRef.value.validate(async (valid) => {
-    if (!valid) return;
-    loading.value = true;
-    const payload = { ...form };
-    const { code } = id.value
-      ? await editUpkeep({ id: id.value, ...payload })
-      : await addUpkeep(payload);
-    if (code == 200) {
-      ElMessage.success(`${id.value ? "缂栬緫" : "鏂板"}璁″垝鎴愬姛`);
-      closeModal();
-      emits("ok");
-    }
-    loading.value = false;
-  });
-};
-
-const close = () => {
-  resetForm();
-  closeModal();
-};
-
-// load device options whenever the dialog opens (covers add and edit)
-watch(
-  () => visible.value,
-  async (val) => {
-    if (val) {
-      await nextTick();
-      await loadDeviceName();
-    }
-  }
-);
-
-defineExpose({
-  openModal,
-  openEdit,
-});
-</script>
-
-<style lang="scss" scoped></style>
diff --git a/src/views/equipmentManagement/upkeep/index.vue b/src/views/equipmentManagement/upkeep/index.vue
index 2659ae0..6102415 100644
--- a/src/views/equipmentManagement/upkeep/index.vue
+++ b/src/views/equipmentManagement/upkeep/index.vue
@@ -1,76 +1,163 @@
 <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
-            @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
-            @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="success" icon="Van" @click="addPlan">
-						鏂板璁″垝
-					</el-button>
-          <el-button
-            type="primary"
-            icon="Plus"
-            :disabled="multipleList.length !== 1"
-            @click="addMaintain"
-          >
-            鏂板淇濆吇
-          </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>
+    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+      <!-- 瀹氭椂浠诲姟绠$悊tab -->
+      <el-tab-pane label="瀹氭椂浠诲姟绠$悊" name="scheduled">
+        <div class="search_form">
+          <el-form :model="scheduledFilters" :inline="true">
+            <el-form-item label="浠诲姟鍚嶇О">
+              <el-input
+                  v-model="scheduledFilters.taskName"
+                  style="width: 240px"
+                  placeholder="璇疯緭鍏ヤ换鍔″悕绉�"
+                  clearable
+                  :prefix-icon="Search"
+                  @change="getScheduledTableData"
+              />
+            </el-form-item>
+            <el-form-item label="浠诲姟鐘舵��">
+              <el-select v-model="scheduledFilters.status" placeholder="璇烽�夋嫨浠诲姟鐘舵��" clearable style="width: 200px">
+                <el-option label="寰呭鏍�" value="寰呭鏍�" />
+                <el-option label="瀹℃牳閫氳繃" value="瀹℃牳閫氳繃" />
+                <el-option label="瀹℃牳涓嶉�氳繃" value="瀹℃牳涓嶉�氳繃" />
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" @click="getScheduledTableData">鎼滅储</el-button>
+              <el-button @click="resetScheduledFilters">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
         </div>
-      </div>
-      <PIMTable
+        <div class="table_list">
+          <div class="actions">
+            <el-text class="mx-1" size="large">瀹氭椂浠诲姟绠$悊</el-text>
+            <div>
+              <el-button type="primary" icon="Plus" @click="addScheduledTask">
+                鏂板浠诲姟
+              </el-button>
+              <el-button
+                type="danger"
+                icon="Delete"
+                :disabled="scheduledMultipleList.length <= 0"
+                @click="delScheduledTaskByIds(scheduledMultipleList.map((item) => item.id))"
+              >
+                鎵归噺鍒犻櫎
+              </el-button>
+            </div>
+          </div>
+          <PIMTable
+            rowKey="id"
+            isSelection
+            :column="scheduledColumns"
+            :tableData="scheduledDataList"
+            :page="{
+              current: scheduledPagination.currentPage,
+              size: scheduledPagination.pageSize,
+              total: scheduledPagination.total,
+            }"
+            @selection-change="handleScheduledSelectionChange"
+            @pagination="changeScheduledPage"
+          >
+            <template #statusRef="{ row }">
+              <el-tag v-if="row.status === '寰呭鏍�'" type="warning">寰呭鏍�</el-tag>
+              <el-tag v-else-if="row.status === '瀹℃牳閫氳繃'" type="success">瀹℃牳閫氳繃</el-tag>
+              <el-tag v-else-if="row.status === '瀹℃牳涓嶉�氳繃'" type="danger">瀹℃牳涓嶉�氳繃</el-tag>
+              <span v-else>{{ row.status }}</span>
+            </template>
+            <template #operation="{ row }">
+              <el-button
+                type="primary"
+                link
+                @click="editScheduledTask(row)"
+              >
+                缂栬緫
+              </el-button>
+              <el-button
+                type="warning"
+                link
+                :disabled="row.status === '瀹℃牳閫氳繃'"
+                @click="openScheduledApprove(row)"
+              >
+                瀹℃壒
+              </el-button>
+              <el-button
+                type="danger"
+                link
+                @click="delScheduledTaskByIds(row.id)"
+              >
+                鍒犻櫎
+              </el-button>
+            </template>
+          </PIMTable>
+        </div>
+      </el-tab-pane>
+
+      <!-- 浠诲姟璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛� -->
+      <el-tab-pane label="浠诲姟璁板綍" name="record">
+        <div class="search_form">
+          <el-form :model="filters" :inline="true">
+            <el-form-item label="璁惧鍚嶇О">
+              <el-input
+                  v-model="filters.deviceName"
+                  style="width: 240px"
+                  placeholder="璇疯緭鍏ヨ澶囧悕绉�"
+                  clearable
+                  :prefix-icon="Search"
+                  @change="getTableData"
+              />
+            </el-form-item>
+            <el-form-item label="璁″垝淇濆吇鏃ユ湡">
+              <el-date-picker
+                  v-model="filters.maintenancePlanTime"
+                  type="date"
+                  placeholder="璇烽�夋嫨璁″垝淇濆吇鏃ユ湡"
+                  size="default"
+                  @change="(date) => handleDateChange(date,2)"
+              />
+            </el-form-item>
+            <el-form-item label="瀹為檯淇濆吇鏃ユ湡">
+              <el-date-picker
+                  v-model="filters.maintenanceActuallyTime"
+                  type="date"
+                  placeholder="璇烽�夋嫨瀹為檯淇濆吇鏃ユ湡"
+                  size="default"
+                  @change="(date) => handleDateChange(date,1)"
+              />
+            </el-form-item>
+            <el-form-item label="瀹為檯淇濆吇浜�">
+              <el-input
+                  v-model="filters.maintenanceActuallyName"
+                  style="width: 240px"
+                  placeholder="璇疯緭鍏ュ疄闄呬繚鍏讳汉"
+                  clearable
+                  :prefix-icon="Search"
+                  @change="getTableData"
+              />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+              <el-button @click="resetFilters">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+        <div class="table_list">
+          <div class="actions">
+            <el-text class="mx-1" size="large">浠诲姟璁板綍</el-text>
+            <div>
+              <el-button @click="handleOut">
+                瀵煎嚭
+              </el-button>
+              <el-button
+                type="danger"
+                icon="Delete"
+                :disabled="multipleList.length <= 0 || hasFinishedStatus"
+                @click="delRepairByIds(multipleList.map((item) => item.id))"
+              >
+                鎵归噺鍒犻櫎
+              </el-button>
+            </div>
+          </div>
+         <PIMTable
         rowKey="id"
         isSelection
         :column="columns"
@@ -84,130 +171,186 @@
         @pagination="changePage"
       >
         <template #maintenanceResultRef="{ row }">
-          <el-tag v-if="row.maintenanceResult === 1" type="success">
-            瀹屽ソ
-          </el-tag>
-          <el-tag v-if="row.maintenanceResult === 0" type="danger">
-            缁翠慨
-          </el-tag>
+          <div>{{ row.maintenanceResult || '-' }}</div>
         </template>
         <template #statusRef="{ row }">
+          <el-tag v-if="row.status === 2" type="danger">澶辫触</el-tag>
           <el-tag v-if="row.status === 1" type="success">瀹岀粨</el-tag>
-          <el-tag v-if="row.status === 0" type="danger">寰呬繚鍏�</el-tag>
+          <el-tag v-if="row.status === 0" type="warning">寰呬繚鍏�</el-tag>
         </template>
         <template #operation="{ row }">
+          <!-- 杩欎釜鍔熻兘璺熸柊澧炰繚鍏诲姛鑳戒竴妯′竴鏍凤紝鏈夊暐鎰忎箟锛� -->
+          <!-- <el-button
+              type="primary"
+              text
+              @click="addMaintain(row)"
+          >
+            鏂板淇濆吇
+          </el-button> -->
           <el-button
             type="primary"
             link
+            :disabled="row.status === 1"
             @click="editPlan(row.id)"
           >
             缂栬緫
           </el-button>
           <el-button
+            type="success"
+            link
+            :disabled="row.status === 1"
+            @click="addMaintain(row)"
+          >
+            淇濆吇
+          </el-button>
+          <el-button
             type="danger"
-						link
+            link
+            :disabled="row.status === 1"
             @click="delRepairByIds(row.id)"
           >
             鍒犻櫎
           </el-button>
+          <el-button
+            type="primary"
+            link
+            @click="openFileDialog(row)"
+          >
+            闄勪欢
+          </el-button>
         </template>
       </PIMTable>
-    </div>
+        </div>
+      </el-tab-pane>
+    </el-tabs>
     <PlanModal ref="planModalRef" @ok="getTableData" />
     <MaintenanceModal ref="maintainModalRef" @ok="getTableData" />
+    <FormDia ref="formDiaRef" @closeDia="getScheduledTableData" />
+    <ApproveModal ref="approveModalRef" @ok="getScheduledTableData" />
+    <FileListDialog 
+      ref="fileListDialogRef"
+      v-model="fileDialogVisible"
+      :show-upload-button="true"
+      :show-delete-button="true"
+      :delete-method="handleAttachmentDelete"
+      :name-column-label="'闄勪欢鍚嶇О'"
+      :rulesRegulationsManagementId="currentMaintenanceTaskId"
+      @upload="handleAttachmentUpload" />
   </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";
+import { ref, onMounted, reactive, getCurrentInstance, nextTick, computed } from 'vue'
+import { Search } from '@element-plus/icons-vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import PlanModal from './Form/PlanModal.vue'
+import MaintenanceModal from './Form/MaintenanceModal.vue'
+import FormDia from './Form/formDia.vue'
+import ApproveModal from './Form/ApproveModal.vue'
+import FileListDialog from '@/components/Dialog/FileListDialog.vue'
+import {
+  getUpkeepPage,
+  delUpkeep,
+  deviceMaintenanceTaskList,
+  deviceMaintenanceTaskDel,
+} from '@/api/equipmentManagement/upkeep'
+import {
+  listMaintenanceTaskFiles,
+  addMaintenanceTaskFile,
+  delMaintenanceTaskFile,
+} from '@/api/equipmentManagement/maintenanceTaskFile'
+import dayjs from 'dayjs'
 
-defineOptions({
-  name: "璁惧淇濆吇",
-});
+const { proxy } = getCurrentInstance()
 
-const { proxy } = getCurrentInstance();
+// Tab鐩稿叧
+const activeTab = ref('scheduled')
 
 // 璁″垝寮圭獥鎺у埗鍣�
-const planModalRef = ref();
+const planModalRef = ref()
 // 淇濆吇寮圭獥鎺у埗鍣�
-const maintainModalRef = ref();
+const maintainModalRef = ref()
+// 瀹氭椂浠诲姟寮圭獥鎺у埗鍣�
+const formDiaRef = ref()
+// 淇濆吇瀹℃壒寮圭獥
+const approveModalRef = ref()
+// 闄勪欢寮圭獥
+const fileListDialogRef = ref(null)
+const fileDialogVisible = ref(false)
+const currentMaintenanceTaskId = ref(null)
 
-// 琛ㄦ牸澶氶�夋閫変腑椤�
-const multipleList = ref([]);
+// 浠诲姟璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛夌浉鍏冲彉閲�
+const filters = reactive({
+  deviceName: '',
+  maintenancePlanTime: '',
+  maintenanceActuallyTime: '',
+  maintenanceActuallyName: '',
+})
 
-// 澶氶�夊悗鍋氫粈涔�
-const handleSelectionChange = (selectionList) => {
-  multipleList.value = selectionList;
-};
+const dataList = ref([])
+const pagination = ref({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+})
+const multipleList = ref([])
 
-// 琛ㄦ牸閽╁瓙
-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",
-  },
+// 瀹氭椂浠诲姟绠$悊tab鐩稿叧鍙橀噺
+const scheduledFilters = reactive({
+  taskName: '',
+  status: '',
+})
+
+const scheduledDataList = ref([])
+const scheduledPagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+})
+const scheduledMultipleList = ref([])
+
+// 瀹氭椂浠诲姟绠$悊琛ㄦ牸鍒楅厤缃�
+const scheduledColumns = ref([
+	{ prop: "taskName", label: "璁惧鍚嶇О"},
+	{
+		label: "瑙勬牸鍨嬪彿",
+		prop: "deviceModel",
+	},
+	{
+		prop: "frequencyType",
+		label: "棰戞",
+		minWidth: 150,
+		// PIMTable 浣跨敤鐨勬槸 formatData锛岃�屼笉鏄� Element-Plus 鐨� formatter
+		formatData: (cell) => ({
+			DAILY: "姣忔棩",
+			WEEKLY: "姣忓懆",
+			MONTHLY: "姣忔湀",
+			QUARTERLY: "瀛e害"
+		}[cell] || "")
+	},
+	{
+		prop: "frequencyDetail",
+		label: "寮�濮嬫棩鏈熶笌鏃堕棿",
+		minWidth: 150,
+		// 鍚屾牱鏀圭敤 formatData锛孭IMTable 鍐呴儴浼氭妸鍗曞厓鏍煎�间紶杩涙潵
+		formatData: (cell) => {
+			if (typeof cell !== 'string') return '';
+			let val = cell;
+			const replacements = {
+				MON: '鍛ㄤ竴',
+				TUE: '鍛ㄤ簩',
+				WED: '鍛ㄤ笁',
+				THU: '鍛ㄥ洓',
+				FRI: '鍛ㄤ簲',
+				SAT: '鍛ㄥ叚',
+				SUN: '鍛ㄦ棩'
+			};
+			// 浣跨敤姝e垯涓�娆℃�ф浛鎹㈡墍鏈夊尮閰嶉」
+			return val.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]);
+		}
+	},
+	{ prop: "registrant", label: "鐧昏浜�", minWidth: 100 },
+	{ prop: "registrationDate", label: "鐧昏鏃ユ湡", minWidth: 100 },
   {
     label: "鐘舵��",
     align: "center",
@@ -215,86 +358,350 @@
     dataType: "slot",
     slot: "statusRef",
   },
-  {
-    fixed: "right",
-    label: "鎿嶄綔",
-    dataType: "slot",
-    slot: "operation",
-    align: "center",
-    width: "150px",
-  },
-]);
-// 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");
-    }
+	{
+		fixed: "right",
+		label: "鎿嶄綔",
+		dataType: "slot",
+		slot: "operation",
+		align: "center",
+		width: "200px",
+	},
+])
+
+// 浠诲姟璁板綍琛ㄦ牸鍒楅厤缃紙鍘熻澶囦繚鍏昏〃鏍煎垪锛�
+const columns = ref([
+	{
+		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: "350px",
+	},
+])
+
+// Tab鍒囨崲澶勭悊
+const handleTabChange = (tabName) => {
+  if (tabName === 'record') {
+    getTableData()
+  } else if (tabName === 'scheduled') {
+    getScheduledTableData()
   }
-  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 getScheduledTableData = async () => {
+  try {
+    const params = {
+      current: scheduledPagination.currentPage,
+      size: scheduledPagination.pageSize,
+      taskName: scheduledFilters.taskName || undefined,
+      status: scheduledFilters.status || undefined,
     }
-  });
-};
+    const { code, data } = await deviceMaintenanceTaskList(params)
+    if (code === 200) {
+      scheduledDataList.value = data?.records || []
+      scheduledPagination.total = data?.total || 0
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇瀹氭椂浠诲姟鍒楄〃澶辫触')
+  }
+}
 
-// 瀵煎嚭
+const resetScheduledFilters = () => {
+  scheduledFilters.taskName = ''
+  scheduledFilters.status = ''
+  getScheduledTableData()
+}
+
+const handleScheduledSelectionChange = (selection) => {
+  scheduledMultipleList.value = selection
+}
+
+const changeScheduledPage = (page) => {
+  scheduledPagination.currentPage = page.page
+  scheduledPagination.pageSize = page.limit
+  getScheduledTableData()
+}
+
+const addScheduledTask = () => {
+  nextTick(() => {
+		formDiaRef.value?.openDialog('add');
+	});
+}
+
+const editScheduledTask = (row) => {
+  if (row) {
+		nextTick(() => {
+			formDiaRef.value?.openDialog('edit', row);
+		});
+  }
+}
+
+const delScheduledTaskByIds = async (ids) => {
+  try {
+    await ElMessageBox.confirm('纭畾鍒犻櫎閫変腑鐨勫畾鏃朵换鍔″悧锛�', '鎻愮ず', {
+      type: 'warning',
+    })
+    const payload = Array.isArray(ids) ? ids : [ids]
+    await deviceMaintenanceTaskDel(payload)
+    ElMessage.success('鍒犻櫎瀹氭椂浠诲姟鎴愬姛')
+    getScheduledTableData()
+  } catch (error) {
+    // 鐢ㄦ埛鍙栨秷鍒犻櫎
+  }
+}
+
+const handleScheduledOut = () => {
+  ElMessage.info('瀵煎嚭瀹氭椂浠诲姟鍔熻兘寰呭疄鐜�')
+}
+
+// 浠诲姟璁板綍鐩稿叧鏂规硶锛堝師璁惧淇濆吇椤甸潰鏂规硶锛�
+const getTableData = async () => {
+  try {
+    const params = {
+      current: pagination.value.currentPage,
+      size: pagination.value.pageSize,
+      deviceName: filters.deviceName || undefined,
+      maintenancePlanTime: filters.maintenancePlanTime ? dayjs(filters.maintenancePlanTime).format('YYYY-MM-DD') : undefined,
+      maintenanceActuallyTime: filters.maintenanceActuallyTime ? dayjs(filters.maintenanceActuallyTime).format('YYYY-MM-DD') : undefined,
+      maintenanceActuallyName: filters.maintenanceActuallyName || undefined,
+    }
+
+    const { code, data } = await getUpkeepPage(params)
+    if (code === 200) {
+      dataList.value = data.records
+      pagination.value.total = data.total
+    }
+  } catch (error) {
+    console.log(error);
+    
+  }
+}
+
+const resetFilters = () => {
+  filters.deviceName = ''
+  filters.maintenancePlanTime = ''
+  filters.maintenanceActuallyTime = ''
+  filters.maintenanceActuallyName = ''
+  getTableData()
+}
+
+const handleSelectionChange = (selection) => {
+  multipleList.value = selection
+}
+
+// 妫�鏌ラ�変腑鐨勮褰曚腑鏄惁鏈夊畬缁撶姸鎬佺殑
+const hasFinishedStatus = computed(() => {
+  return multipleList.value.some(item => item.status === 1)
+})
+
+const changePage = (page) => {
+  pagination.value.currentPage = page.page
+  pagination.value.pageSize = page.limit
+  getTableData()
+}
+
+const addMaintain = (row) => {
+  maintainModalRef.value.open(row.id, row)
+}
+
+// 瀹氭椂浠诲姟瀹℃壒
+const openScheduledApprove = (row) => {
+  approveModalRef.value.open(row)
+}
+
+const addPlan = () => {
+  planModalRef.value.openModal()
+}
+
+const editPlan = (id) => {
+  planModalRef.value.openEdit(id)
+}
+
+const delRepairByIds = async (ids) => {
+  // 妫�鏌ユ槸鍚︽湁瀹岀粨鐘舵�佺殑璁板綍
+  const hasFinished = multipleList.value.some(item => item.status === 1)
+  if (hasFinished) {
+    ElMessage.warning('涓嶈兘鍒犻櫎鐘舵�佷负瀹岀粨鐨勮褰�')
+    return
+  }
+  
+  try {
+    await ElMessageBox.confirm('纭鍒犻櫎淇濆吇鏁版嵁, 姝ゆ搷浣滀笉鍙��?', '璀﹀憡', {
+      confirmButtonText: '纭畾',
+      cancelButtonText: '鍙栨秷',
+      type: 'warning',
+    })
+    
+    const { code } = await delUpkeep(ids)
+    if (code === 200) {
+      ElMessage.success('鍒犻櫎鎴愬姛')
+      getTableData()
+    }
+  } catch (error) {
+    // 鐢ㄦ埛鍙栨秷鍒犻櫎
+  }
+}
+
 const handleOut = () => {
-  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
-    confirmButtonText: "纭",
-    cancelButtonText: "鍙栨秷",
-    type: "warning",
+  ElMessageBox.confirm('閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�', '瀵煎嚭', {
+    confirmButtonText: '纭',
+    cancelButtonText: '鍙栨秷',
+    type: 'warning',
   })
     .then(() => {
-      proxy.download("/device/maintenance/export", {}, "璁惧淇濆吇.xlsx");
+      proxy.download('/device/maintenance/export', {}, '璁惧淇濆吇.xlsx')
     })
     .catch(() => {
-      ElMessage.info("宸插彇娑�");
-    });
-};
+      ElMessage.info('宸插彇娑�')
+    })
+}
+
+const handleDateChange = (date, type) => {
+  if (type === 1) {
+    filters.maintenanceActuallyTime = date ? dayjs(date).format('YYYY-MM-DD') : ''
+  } else {
+    filters.maintenancePlanTime = date ? dayjs(date).format('YYYY-MM-DD') : ''
+  }
+  getTableData()
+}
+
+// 闄勪欢鐩稿叧鏂规硶
+// 鏌ヨ闄勪欢鍒楄〃
+const fetchMaintenanceTaskFiles = async (deviceMaintenanceId) => {
+  try {
+    const params = {
+      current: 1,
+      size: 100,
+      deviceMaintenanceId,
+      rulesRegulationsManagementId:deviceMaintenanceId
+    }
+    const res = await listMaintenanceTaskFiles(params)
+    const records = res?.data?.records || []
+    const mapped = records.map(item => ({
+      id: item.id,
+      name: item.fileName || item.name,
+      url: item.fileUrl || item.url,
+      raw: item,
+    }))
+    fileListDialogRef.value?.setList(mapped)
+  } catch (error) {
+    ElMessage.error('鑾峰彇闄勪欢鍒楄〃澶辫触')
+  }
+}
+
+// 鎵撳紑闄勪欢寮圭獥
+const openFileDialog = async (row) => {
+  currentMaintenanceTaskId.value = row.id
+  fileDialogVisible.value = true
+  await fetchMaintenanceTaskFiles(row.id)
+}
+
+// 鍒锋柊闄勪欢鍒楄〃
+const refreshFileList = async () => {
+  if (!currentMaintenanceTaskId.value) return
+  await fetchMaintenanceTaskFiles(currentMaintenanceTaskId.value)
+}
+
+// 涓婁紶闄勪欢
+const handleAttachmentUpload = async (filePayload) => {
+  if (!currentMaintenanceTaskId.value) return
+  try {
+    const payload = {
+      name: filePayload?.fileName || filePayload?.name,
+      url: filePayload?.fileUrl || filePayload?.url,
+      deviceMaintenanceId: currentMaintenanceTaskId.value,
+    }
+    await addMaintenanceTaskFile(payload)
+    ElMessage.success('鏂囦欢涓婁紶鎴愬姛')
+    await refreshFileList()
+  } catch (error) {
+    ElMessage.error('鏂囦欢涓婁紶澶辫触')
+  }
+}
+
+// 鍒犻櫎闄勪欢
+const handleAttachmentDelete = async (row) => {
+  if (!row?.id) return false
+  try {
+    await ElMessageBox.confirm('纭鍒犻櫎璇ラ檮浠讹紵', '鎻愮ず', { type: 'warning' })
+  } catch {
+    return false
+  }
+  try {
+    await delMaintenanceTaskFile(row.id)
+    ElMessage.success('鍒犻櫎鎴愬姛')
+    await refreshFileList()
+    return true
+  } catch (error) {
+    ElMessage.error('鍒犻櫎澶辫触')
+    return false
+  }
+}
 
 onMounted(() => {
-  getTableData();
-});
+  // 鏍规嵁榛樿婵�娲荤殑 Tab 璋冪敤瀵瑰簲鐨勬煡璇㈡帴鍙�
+  if (activeTab.value === 'scheduled') {
+    getScheduledTableData()
+  } else {
+    getTableData()
+  }
+})
 </script>
 
 <style lang="scss" scoped>
@@ -307,3 +714,8 @@
   margin-bottom: 10px;
 }
 </style>
+
+
+
+
+

--
Gitblit v1.9.3