From f36f2f20bfb06dc3ca1b69c8a6d260d09d7d70ba Mon Sep 17 00:00:00 2001
From: 曹睿 <360930172@qq.com>
Date: 星期五, 27 六月 2025 17:29:13 +0800
Subject: [PATCH] feat: 新增设备报修模块。

---
 src/views/procurementManagement/paymentHistory/index.vue                |    2 
 src/views/procurementManagement/procurementInvoiceLedger/index.vue      |    6 
 src/views/procurementManagement/invoiceEntry/components/ExpandTable.vue |   10 
 src/views/equipmentManagement/repair/Modal/MaintainModal.vue            |   58 +++
 src/views/equipmentManagement/repair/Modal/RepairModal.vue              |   70 ++++
 src/views/equipmentManagement/repair/index.vue                          |  207 ++++++++++++
 src/views/procurementManagement/invoiceEntry/index.vue                  |    6 
 src/views/equipmentManagement/repair/Form/RepairForm.vue                |  112 +++++++
 src/views/procurementManagement/paymentEntry/index.vue                  |    6 
 src/views/salesManagement/invoiceRegistration/index.vue                 |    1 
 src/api/equipmentManagement/ledger.js                                   |    7 
 src/store/modules/settings.js                                           |  103 +++--
 src/views/procurementManagement/paymentLedger/index.vue                 |    4 
 src/api/equipmentManagement/repair.js                                   |   72 ++++
 src/views/equipmentManagement/repair/Form/MaintainForm.vue              |   53 +++
 src/api/procurementManagement/paymentLedger.js                          |   20 
 src/views/equipmentManagement/ledger/Modal.vue                          |    2 
 src/layout/components/Settings/index.vue                                |  164 ++++++---
 src/views/procurementManagement/invoiceEntry/components/Modal.vue       |    5 
 src/views/salesManagement/receiptPaymentHistory/index.vue               |    2 
 20 files changed, 783 insertions(+), 127 deletions(-)

diff --git a/src/api/equipmentManagement/ledger.js b/src/api/equipmentManagement/ledger.js
index 1eac3e7..d1b65b0 100644
--- a/src/api/equipmentManagement/ledger.js
+++ b/src/api/equipmentManagement/ledger.js
@@ -35,3 +35,10 @@
     method: "delete",
   });
 };
+
+export const getDeviceLedger = () => {
+  return request({
+    url: "/device/ledger/getDeviceLedger",
+    method: "get",
+  });
+};
diff --git a/src/api/equipmentManagement/repair.js b/src/api/equipmentManagement/repair.js
new file mode 100644
index 0000000..0233ae6
--- /dev/null
+++ b/src/api/equipmentManagement/repair.js
@@ -0,0 +1,72 @@
+import request from "@/utils/request";
+
+/**
+ * @desc 璁惧鎶ヤ慨鍒楄〃
+ * @param {鍒嗛〉鏌ヨ} params
+ * @returns
+ */
+export const getRepairPage = (params) => {
+  return request({
+    url: "/device/repair/page",
+    method: "get",
+    params,
+  });
+};
+
+/**
+ * @desc 鏂板鎶ヤ慨
+ * @param {鎶ヤ慨鍙傛暟} data
+ * @returns
+ */
+export const addRepair = (data) => {
+  return request({
+    url: "/device/repair",
+    method: "post",
+    data,
+  });
+};
+
+/**
+ * @desc 缂栬緫鎶ヤ慨
+ * @param {鎶ヤ慨鍙傛暟} data
+ * @returns
+ */
+export const editRepair = (data) => {
+  return request({
+    url: "/device/repair",
+    method: "put",
+    data,
+  });
+};
+
+/**
+ * @desc 鏍规嵁id鏌ヨ涓�鏉℃姤淇�
+ * @param {鎶ヤ慨id} id
+ * @returns
+ */
+export const getRepairById = (id) => {
+  return request({
+    url: `/device/repair/${id}`,
+    method: "get",
+  });
+};
+
+/**
+ * @desc 鍒犻櫎鎶ヤ慨
+ * @param {缂栧彿} ids
+ * @returns
+ */
+export const delRepair = (ids) => {
+  return request({
+    url: `/device/repair/${ids}`,
+    method: "delete",
+  });
+};
+
+export const addMaintain = (data) => {
+  return request({
+    url: `/device/repair/repair`,
+    method: "post",
+    data,
+  });
+};
diff --git a/src/api/procurementManagement/paymentLedger.js b/src/api/procurementManagement/paymentLedger.js
index 250774b..3e20b78 100644
--- a/src/api/procurementManagement/paymentLedger.js
+++ b/src/api/procurementManagement/paymentLedger.js
@@ -1,19 +1,19 @@
 // 閲囪喘鍙拌处椤甸潰鎺ュ彛
-import request from '@/utils/request'
+import request from "@/utils/request";
 
 // 鍒嗛〉鏌ヨ
 export function paymentLedgerList(query) {
-    return request({
-        url: '/purchase/paymentRegistration/paymentLedgerList',
-        method: 'get',
-        params: query
-    })
+  return request({
+    url: "/purchase/paymentRegistration/paymentLedgerList",
+    method: "get",
+    params: query,
+  });
 }
 
 // 鍒嗛〉鏌ヨ
 export function paymentRecordList(supplierId) {
-    return request({
-        url: '/purchase/paymentRegistration/getPaymentRecordList/'+ supplierId,
-        method: 'get'
-    })
+  return request({
+    url: "/purchase/paymentRegistration/getPaymentRecordList/" + supplierId,
+    method: "get",
+  });
 }
diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue
index 55ccec2..e107218 100644
--- a/src/layout/components/Settings/index.vue
+++ b/src/layout/components/Settings/index.vue
@@ -4,22 +4,58 @@
       <h3 class="drawer-title">涓婚椋庢牸璁剧疆</h3>
     </div>
     <div class="setting-drawer-block-checbox">
-      <div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')">
+      <div
+        class="setting-drawer-block-checbox-item"
+        @click="handleTheme('theme-dark')"
+      >
         <img src="@/assets/images/dark.svg" alt="dark" />
-        <div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
+        <div
+          v-if="sideTheme === 'theme-dark'"
+          class="setting-drawer-block-checbox-selectIcon"
+          style="display: block"
+        >
           <i aria-label="鍥炬爣: check" class="anticon anticon-check">
-            <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
-              <path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
+            <svg
+              viewBox="64 64 896 896"
+              data-icon="check"
+              width="1em"
+              height="1em"
+              :fill="theme"
+              aria-hidden="true"
+              focusable="false"
+              class
+            >
+              <path
+                d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"
+              />
             </svg>
           </i>
         </div>
       </div>
-      <div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-light')">
+      <div
+        class="setting-drawer-block-checbox-item"
+        @click="handleTheme('theme-light')"
+      >
         <img src="@/assets/images/light.svg" alt="light" />
-        <div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
+        <div
+          v-if="sideTheme === 'theme-light'"
+          class="setting-drawer-block-checbox-selectIcon"
+          style="display: block"
+        >
           <i aria-label="鍥炬爣: check" class="anticon anticon-check">
-            <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
-              <path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
+            <svg
+              viewBox="64 64 896 896"
+              data-icon="check"
+              width="1em"
+              height="1em"
+              :fill="theme"
+              aria-hidden="true"
+              focusable="false"
+              class
+            >
+              <path
+                d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"
+              />
             </svg>
           </i>
         </div>
@@ -28,7 +64,11 @@
     <div class="drawer-item">
       <span>涓婚棰滆壊</span>
       <span class="comp-style">
-        <el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange"/>
+        <el-color-picker
+          v-model="theme"
+          :predefine="predefineColors"
+          @change="themeChange"
+        />
       </span>
     </div>
     <el-divider />
@@ -38,7 +78,11 @@
     <div class="drawer-item">
       <span>寮�鍚� TopNav</span>
       <span class="comp-style">
-        <el-switch v-model="settingsStore.topNav" @change="topNavChange" class="drawer-switch" />
+        <el-switch
+          v-model="settingsStore.topNav"
+          @change="topNavChange"
+          class="drawer-switch"
+        />
       </span>
     </div>
 
@@ -72,81 +116,93 @@
 
     <el-divider />
 
-    <el-button type="primary" plain icon="DocumentAdd" @click="saveSetting">淇濆瓨閰嶇疆</el-button>
+    <el-button type="primary" plain icon="DocumentAdd" @click="saveSetting"
+      >淇濆瓨閰嶇疆</el-button
+    >
     <el-button plain icon="Refresh" @click="resetSetting">閲嶇疆閰嶇疆</el-button>
   </el-drawer>
-
 </template>
 
 <script setup>
-import variables from '@/assets/styles/variables.module.scss'
-import axios from 'axios'
-import { ElLoading, ElMessage } from 'element-plus'
-import { useDynamicTitle } from '@/utils/dynamicTitle'
-import useAppStore from '@/store/modules/app'
-import useSettingsStore from '@/store/modules/settings'
-import usePermissionStore from '@/store/modules/permission'
-import { handleThemeStyle } from '@/utils/theme'
+import variables from "@/assets/styles/variables.module.scss";
+import axios from "axios";
+import { ElLoading, ElMessage } from "element-plus";
+import { useDynamicTitle } from "@/utils/dynamicTitle";
+import useAppStore from "@/store/modules/app";
+import useSettingsStore from "@/store/modules/settings";
+import usePermissionStore from "@/store/modules/permission";
+import { handleThemeStyle } from "@/utils/theme";
 
-const { proxy } = getCurrentInstance()
-const appStore = useAppStore()
-const settingsStore = useSettingsStore()
-const permissionStore = usePermissionStore()
-const showSettings = ref(false)
-const theme = ref(settingsStore.theme)
-const sideTheme = ref(settingsStore.sideTheme)
-const storeSettings = computed(() => settingsStore)
-const predefineColors = ref(["#800020", "#81D8D0", "#E85827", "#008C8C", "#002FA7", "#F9DC24", "#B05923", "#003153", "#8F4B28", "#4C0009"])
+const { proxy } = getCurrentInstance();
+const appStore = useAppStore();
+const settingsStore = useSettingsStore();
+const permissionStore = usePermissionStore();
+const showSettings = ref(false);
+const theme = ref(settingsStore.theme);
+const sideTheme = ref(settingsStore.sideTheme);
+const storeSettings = computed(() => settingsStore);
+const predefineColors = ref([
+  "#002fa7",
+  "#81D8D0",
+  "#E85827",
+  "#008C8C",
+  "#002FA7",
+  "#F9DC24",
+  "#B05923",
+  "#003153",
+  "#8F4B28",
+  "#4C0009",
+]);
 
 /** 鏄惁闇�瑕乼opnav */
 function topNavChange(val) {
   if (!val) {
-    appStore.toggleSideBarHide(false)
-    permissionStore.setSidebarRouters(permissionStore.defaultRoutes)
+    appStore.toggleSideBarHide(false);
+    permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
   }
 }
 
 function themeChange(val) {
-  settingsStore.theme = val
-  handleThemeStyle(val)
+  settingsStore.theme = val;
+  handleThemeStyle(val);
 }
 
 function handleTheme(val) {
-  settingsStore.sideTheme = val
-  sideTheme.value = val
+  settingsStore.sideTheme = val;
+  sideTheme.value = val;
 }
 
 function saveSetting() {
-  proxy.$modal.loading("姝e湪淇濆瓨鍒版湰鍦帮紝璇风◢鍊�...")
+  proxy.$modal.loading("姝e湪淇濆瓨鍒版湰鍦帮紝璇风◢鍊�...");
   let layoutSetting = {
-    "topNav": storeSettings.value.topNav,
-    "tagsView": storeSettings.value.tagsView,
-    "fixedHeader": storeSettings.value.fixedHeader,
-    "sidebarLogo": storeSettings.value.sidebarLogo,
-    "dynamicTitle": storeSettings.value.dynamicTitle,
-    "sideTheme": storeSettings.value.sideTheme,
-    "theme": storeSettings.value.theme
-  }
-  localStorage.setItem("layout-setting", JSON.stringify(layoutSetting))
-  setTimeout(proxy.$modal.closeLoading(), 1000)
+    topNav: storeSettings.value.topNav,
+    tagsView: storeSettings.value.tagsView,
+    fixedHeader: storeSettings.value.fixedHeader,
+    sidebarLogo: storeSettings.value.sidebarLogo,
+    dynamicTitle: storeSettings.value.dynamicTitle,
+    sideTheme: storeSettings.value.sideTheme,
+    theme: storeSettings.value.theme,
+  };
+  localStorage.setItem("layout-setting", JSON.stringify(layoutSetting));
+  setTimeout(proxy.$modal.closeLoading(), 1000);
 }
 
 function resetSetting() {
-  proxy.$modal.loading("姝e湪娓呴櫎璁剧疆缂撳瓨骞跺埛鏂帮紝璇风◢鍊�...")
-  localStorage.removeItem("layout-setting")
-  setTimeout("window.location.reload()", 1000)
+  proxy.$modal.loading("姝e湪娓呴櫎璁剧疆缂撳瓨骞跺埛鏂帮紝璇风◢鍊�...");
+  localStorage.removeItem("layout-setting");
+  setTimeout("window.location.reload()", 1000);
 }
 
 function openSetting() {
-  showSettings.value = true
+  showSettings.value = true;
 }
 
 defineExpose({
-  openSetting
-})
+  openSetting,
+});
 </script>
 
-<style lang='scss' scoped>
+<style lang="scss" scoped>
 .setting-drawer-title {
   margin-bottom: 12px;
   color: var(--el-text-color-primary, rgba(0, 0, 0, 0.85));
@@ -201,4 +257,4 @@
     margin: -3px 8px 0px 0px;
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js
index 76a1136..e526da0 100644
--- a/src/store/modules/settings.js
+++ b/src/store/modules/settings.js
@@ -1,48 +1,67 @@
-import defaultSettings from '@/settings'
-import { useDark, useToggle } from '@vueuse/core'
-import { useDynamicTitle } from '@/utils/dynamicTitle'
+import defaultSettings from "@/settings";
+import { useDark, useToggle } from "@vueuse/core";
+import { useDynamicTitle } from "@/utils/dynamicTitle";
 
-const isDark = useDark()
-const toggleDark = useToggle(isDark)
+const isDark = useDark();
+const toggleDark = useToggle(isDark);
 
-const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings
+const {
+  sideTheme,
+  showSettings,
+  topNav,
+  tagsView,
+  fixedHeader,
+  sidebarLogo,
+  dynamicTitle,
+} = defaultSettings;
 
-const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
+const storageSetting = JSON.parse(localStorage.getItem("layout-setting")) || "";
 
-const useSettingsStore = defineStore(
-  'settings',
-  {
-    state: () => ({
-      title: '',
-      theme: storageSetting.theme || '#2C51D9',
-      sideTheme: storageSetting.sideTheme || sideTheme,
-      showSettings: showSettings,
-      topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
-      tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
-      fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,
-      sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo,
-      dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle,
-      isDark: isDark.value
-    }),
-    actions: {
-      // 淇敼甯冨眬璁剧疆
-      changeSetting(data) {
-        const { key, value } = data
-        if (this.hasOwnProperty(key)) {
-          this[key] = value
-        }
-      },
-      // 璁剧疆缃戦〉鏍囬
-      setTitle(title) {
-        this.title = title
-        useDynamicTitle()
-      },
-      // 鍒囨崲鏆楅粦妯″紡
-      toggleTheme() {
-        this.isDark = !this.isDark
-        toggleDark()
+const useSettingsStore = defineStore("settings", {
+  state: () => ({
+    title: "",
+    theme: storageSetting.theme || "#002fa7",
+    sideTheme: storageSetting.sideTheme || sideTheme,
+    showSettings: showSettings,
+    topNav:
+      storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
+    tagsView:
+      storageSetting.tagsView === undefined
+        ? tagsView
+        : storageSetting.tagsView,
+    fixedHeader:
+      storageSetting.fixedHeader === undefined
+        ? fixedHeader
+        : storageSetting.fixedHeader,
+    sidebarLogo:
+      storageSetting.sidebarLogo === undefined
+        ? sidebarLogo
+        : storageSetting.sidebarLogo,
+    dynamicTitle:
+      storageSetting.dynamicTitle === undefined
+        ? dynamicTitle
+        : storageSetting.dynamicTitle,
+    isDark: isDark.value,
+  }),
+  actions: {
+    // 淇敼甯冨眬璁剧疆
+    changeSetting(data) {
+      const { key, value } = data;
+      if (this.hasOwnProperty(key)) {
+        this[key] = value;
       }
-    }
-  })
+    },
+    // 璁剧疆缃戦〉鏍囬
+    setTitle(title) {
+      this.title = title;
+      useDynamicTitle();
+    },
+    // 鍒囨崲鏆楅粦妯″紡
+    toggleTheme() {
+      this.isDark = !this.isDark;
+      toggleDark();
+    },
+  },
+});
 
-export default useSettingsStore
+export default useSettingsStore;
diff --git a/src/views/equipmentManagement/ledger/Modal.vue b/src/views/equipmentManagement/ledger/Modal.vue
index d1562dd..7bfe46c 100644
--- a/src/views/equipmentManagement/ledger/Modal.vue
+++ b/src/views/equipmentManagement/ledger/Modal.vue
@@ -31,7 +31,7 @@
   modalOptions,
   handleConfirm,
   closeModal,
-} = useModal();
+} = useModal({ title: "璁惧鍙拌处" });
 
 const sendForm = async () => {
   loading.value = true;
diff --git a/src/views/equipmentManagement/repair/Form/MaintainForm.vue b/src/views/equipmentManagement/repair/Form/MaintainForm.vue
new file mode 100644
index 0000000..f946e7e
--- /dev/null
+++ b/src/views/equipmentManagement/repair/Form/MaintainForm.vue
@@ -0,0 +1,53 @@
+<template>
+  <el-form :model="form" label-width="80px">
+    <el-form-item label="缁翠慨浜�">
+      <el-input v-model="form.maintenanceName" placeholder="璇疯緭鍏ョ淮淇汉" />
+    </el-form-item>
+    <el-form-item label="缁翠慨缁撴灉">
+      <el-input v-model="form.maintenanceResult" placeholder="璇疯緭鍏ョ淮淇粨鏋�" />
+    </el-form-item>
+    <el-form-item label="缁翠慨缁撴灉">
+      <el-date-picker
+        v-model="form.maintenanceTime"
+        placeholder="璇烽�夋嫨缁翠慨鏃ユ湡"
+        format="YYYY-MM-DD"
+        value-format="YYYY-MM-DD"
+        type="date"
+        clearable
+        style="width: 100%"
+      />
+    </el-form-item>
+  </el-form>
+</template>
+
+<script setup>
+import useFormData from "@/hooks/useFormData";
+
+defineOptions({
+  name: "璁惧缁翠慨琛ㄥ崟",
+});
+
+const { form, resetForm } = useFormData({
+  maintenanceName: undefined, // 缁翠慨鍚嶇О
+  maintenanceResult: undefined, // 缁翠慨缁撴灉
+  maintenanceTime: undefined, // 缁翠慨鏃ユ湡
+});
+
+const setForm = (data) => {
+  form.maintenanceName = data.maintenanceName;
+  form.maintenanceResult = data.maintenanceResult;
+  form.maintenanceTime = data.maintenanceTime;
+};
+
+const getForm = () => {
+  return form;
+};
+
+defineExpose({
+  getForm,
+  setForm,
+  resetForm,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/equipmentManagement/repair/Form/RepairForm.vue b/src/views/equipmentManagement/repair/Form/RepairForm.vue
new file mode 100644
index 0000000..6ded5fe
--- /dev/null
+++ b/src/views/equipmentManagement/repair/Form/RepairForm.vue
@@ -0,0 +1,112 @@
+<template>
+  <el-form :model="form" label-width="100px">
+    <el-row>
+      <el-col :span="12">
+        <el-form-item label="璁惧鍚嶇О">
+          <el-select v-model="form.deviceLedgerId" @change="setDeviceModel">
+            <el-option
+              v-for="(item, index) in deviceOptions"
+              :key="index"
+              :label="item.deviceName"
+              :value="item.id"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="瑙勬牸鍨嬪彿">
+          <el-input
+            v-model="form.deviceModel"
+            placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+            disabled
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="鎶ヤ慨鏃ユ湡">
+          <el-date-picker
+            v-model="form.repairTime"
+            placeholder="璇烽�夋嫨鎶ヤ慨鏃ユ湡"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            type="date"
+            clearable
+            style="width: 100%"
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="鎶ヤ慨浜�">
+          <el-input v-model="form.repairName" placeholder="璇疯緭鍏ユ姤淇汉" />
+        </el-form-item>
+      </el-col>
+      <el-col :span="24">
+        <el-form-item label="鏁呴殰鐜拌薄">
+          <el-input
+            v-model="form.remark"
+            :rows="2"
+            type="textarea"
+            placeholder="璇疯緭鍏ユ晠闅滅幇璞�"
+          />
+        </el-form-item>
+      </el-col>
+    </el-row>
+  </el-form>
+</template>
+
+<script setup>
+import useFormData from "@/hooks/useFormData";
+import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
+import { onMounted } from "vue";
+
+defineOptions({
+  name: "璁惧鎶ヤ慨琛ㄥ崟",
+});
+
+const deviceOptions = ref([]);
+
+const loadDeviceName = async () => {
+  const { data } = await getDeviceLedger();
+  deviceOptions.value = data;
+};
+
+const { form, resetForm } = useFormData({
+  deviceLedgerId: undefined, // 璁惧Id
+  deviceName: undefined, // 璁惧鍚嶇О
+  deviceModel: undefined, // 瑙勬牸鍨嬪彿
+  repairTime: undefined, // 鎶ヤ慨鏃ユ湡
+  repairName: undefined, // 鎶ヤ慨浜�
+  remark: undefined, // 鏁呴殰鐜拌薄
+});
+
+const setDeviceModel = (id) => {
+  const option = deviceOptions.value.find((item) => item.id === id);
+  form.deviceModel = option.deviceModel;
+};
+
+const getForm = () => {
+  return form;
+};
+
+const setForm = (data) => {
+  form.deviceLedgerId = data.deviceLedgerId;
+  form.deviceName = data.deviceName;
+  form.deviceModel = data.deviceModel;
+  form.repairTime = data.repairTime;
+  form.repairName = data.repairName;
+  form.remark = data.remark;
+};
+
+onMounted(() => {
+  loadDeviceName();
+});
+
+defineExpose({
+  loadDeviceName,
+  resetForm,
+  getForm,
+  setForm,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/equipmentManagement/repair/Modal/MaintainModal.vue b/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
new file mode 100644
index 0000000..695773e
--- /dev/null
+++ b/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
@@ -0,0 +1,58 @@
+<template>
+  <el-drawer v-model="visible" :title="modalOptions.title" direction="ltr">
+    <MaintainForm ref="maintainFormRef" />
+    <template #footer>
+      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
+      <el-button type="primary" @click="sendForm" :loading="loading">
+        {{ modalOptions.confirmText }}
+      </el-button>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup>
+import { useModal } from "@/hooks/useModal";
+import MaintainForm from "../Form/MaintainForm.vue";
+import { addMaintain } from "@/api/equipmentManagement/repair";
+
+defineOptions({
+  name: "缁翠慨妯℃�佹",
+});
+
+const maintainFormRef = ref();
+const emits = defineEmits(["ok"]);
+
+const {
+  id,
+  visible,
+  loading,
+  openModal,
+  modalOptions,
+  handleConfirm,
+  closeModal,
+} = useModal({ title: "璁惧缁翠慨" });
+
+const sendForm = async () => {
+  loading.value = true;
+  const form = await maintainFormRef.value.getForm();
+  const { code } = await addMaintain({ id: id.value, ...form });
+  if (code == 200) {
+    emits("ok");
+    maintainFormRef.value.resetForm();
+    closeModal();
+  }
+  loading.value = false;
+};
+
+const open = async (id, row) => {
+  openModal(id);
+  await nextTick();
+  maintainFormRef.value.setForm(row);
+};
+
+defineExpose({
+  open,
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/views/equipmentManagement/repair/Modal/RepairModal.vue b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
new file mode 100644
index 0000000..586960d
--- /dev/null
+++ b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -0,0 +1,70 @@
+<template>
+  <el-dialog v-model="visible" :title="modalOptions.title" @close="close">
+    <RepairForm ref="repairFormRef" />
+    <template #footer>
+      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
+      <el-button type="primary" @click="sendForm" :loading="loading">
+        {{ modalOptions.confirmText }}
+      </el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { useModal } from "@/hooks/useModal";
+import RepairForm from "../Form/RepairForm.vue";
+import {
+  addRepair,
+  editRepair,
+  getRepairById,
+} from "@/api/equipmentManagement/repair";
+import { ElMessage } from "element-plus";
+
+defineOptions({
+  name: "璁惧鎶ヤ慨寮圭獥",
+});
+
+const emits = defineEmits(["ok"]);
+
+const repairFormRef = ref();
+const {
+  id,
+  visible,
+  loading,
+  openModal,
+  modalOptions,
+  handleConfirm,
+  closeModal,
+} = useModal({ title: "璁惧鎶ヤ慨" });
+
+const sendForm = async () => {
+  loading.value = true;
+  const form = await repairFormRef.value.getForm();
+  const { code } = id.value
+    ? await editRepair({ id: unref(id), ...form })
+    : await addRepair(form);
+  if (code == 200) {
+    ElMessage.success(`${id ? "缂栬緫" : "鏂板"}鎶ヤ慨鎴愬姛`);
+    closeModal();
+    emits("ok");
+  }
+  loading.value = false;
+};
+
+const openEdit = async (id) => {
+  const { data } = await getRepairById(id);
+  openModal(id);
+  await nextTick();
+  await repairFormRef.value.setForm(data);
+};
+
+const close = () => {
+  repairFormRef.value.resetForm();
+  closeModal();
+};
+
+defineExpose({
+  openModal,
+  openEdit,
+});
+</script>
diff --git a/src/views/equipmentManagement/repair/index.vue b/src/views/equipmentManagement/repair/index.vue
new file mode 100644
index 0000000..96fb91b
--- /dev/null
+++ b/src/views/equipmentManagement/repair/index.vue
@@ -0,0 +1,207 @@
+<template>
+  <div class="app-container">
+    <div class="table_list">
+      <div class="actions">
+        <el-text class="mx-1" size="large">璁惧鎶ヤ慨</el-text>
+        <div>
+          <el-button
+            type="primary"
+            icon="Plus"
+            :disabled="multipleList.length !== 1"
+            @click="addMaintain"
+          >
+            鏂板缁翠慨
+          </el-button>
+          <el-button type="success" icon="Van" @click="addRepair">
+            鏂板鎶ヤ慨
+          </el-button>
+          <el-button
+            type="danger"
+            icon="Delete"
+            :disabled="multipleList.length <= 0"
+            @click="delRepairByIds(multipleList.map((item) => item.id))"
+          >
+            鎵归噺鍒犻櫎
+          </el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        isSelection
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @selection-change="handleSelectionChange"
+      >
+        <template #statusRef="{ row }">
+          <el-tag v-if="row.status === 1" type="success">瀹岀粨</el-tag>
+          <el-tag v-if="row.status === 0" type="error">寰呯淮淇�</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button
+            type="primary"
+            text
+            icon="editPen"
+            @click="editRepair(row.id)"
+          >
+            缂栬緫
+          </el-button>
+          <el-button
+            type="danger"
+            text
+            icon="delete"
+            @click="delRepairByIds(row.id)"
+          >
+            鍒犻櫎
+          </el-button>
+        </template>
+      </PIMTable>
+    </div>
+    <RepairModal ref="repairModalRef" @ok="getTableData" />
+    <MaintainModal ref="maintainModalRef" @ok="getTableData" />
+  </div>
+</template>
+
+<script setup>
+import { usePaginationApi } from "@/hooks/usePaginationApi";
+import { getRepairPage, delRepair } from "@/api/equipmentManagement/repair";
+import { onMounted } from "vue";
+import RepairModal from "./Modal/RepairModal.vue";
+import { ElMessageBox, ElMessage } from "element-plus";
+import dayjs from "dayjs";
+import MaintainModal from "./Modal/MaintainModal.vue";
+
+defineOptions({
+  name: "璁惧鎶ヤ慨",
+});
+
+// 妯℃�佹瀹炰緥
+const repairModalRef = ref();
+const maintainModalRef = ref();
+
+// 琛ㄦ牸澶氶�夋閫変腑椤�
+const multipleList = ref([]);
+
+// 琛ㄦ牸閽╁瓙
+const { filters, columns, dataList, pagination, getTableData, resetFilters } =
+  usePaginationApi(
+    getRepairPage,
+    {
+      searchText: undefined,
+    },
+    [
+      {
+        label: "璁惧鍚嶇О",
+        align: "center",
+        prop: "deviceName",
+      },
+      {
+        label: "瑙勬牸鍨嬪彿",
+        align: "center",
+        prop: "deviceModel",
+      },
+      {
+        label: "鎶ヤ慨鏃ユ湡",
+        align: "center",
+        prop: "repairTime",
+        formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"),
+      },
+      {
+        label: "鎶ヤ慨浜�",
+        align: "center",
+        prop: "repairName",
+      },
+      {
+        label: "鏁呴殰鐜拌薄",
+        align: "center",
+        prop: "remark",
+      },
+      {
+        label: "缁翠慨浜�",
+        align: "center",
+        prop: "maintenanceName",
+      },
+      {
+        label: "缁翠慨缁撴灉",
+        align: "center",
+        prop: "maintenanceResult",
+      },
+      {
+        label: "缁翠慨鏃ユ湡",
+        align: "center",
+        prop: "maintenanceTime",
+        formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
+      },
+      {
+        label: "鐘舵��",
+        align: "center",
+        prop: "status",
+        dataType: "slot",
+        slot: "statusRef",
+      },
+      {
+        fixed: "right",
+        label: "鎿嶄綔",
+        dataType: "slot",
+        slot: "operation",
+        align: "center",
+        width: "200px",
+      },
+    ]
+  );
+
+// 澶氶�夊悗鍋氫粈涔�
+const handleSelectionChange = (selectionList) => {
+  multipleList.value = selectionList;
+};
+
+// 鏂板鎶ヤ慨
+const addRepair = () => {
+  repairModalRef.value.openModal();
+};
+
+// 缂栬緫鎶ヤ慨
+const editRepair = (id) => {
+  repairModalRef.value.openEdit(id);
+};
+
+// 鏂板缁翠慨
+const addMaintain = () => {
+  const row = multipleList.value[0];
+  maintainModalRef.value.open(row.id, row);
+};
+
+// 鍗曡鍒犻櫎
+const delRepairByIds = async (ids) => {
+  ElMessageBox.confirm("纭鍒犻櫎鎶ヤ慨鏁版嵁, 姝ゆ搷浣滀笉鍙��?", "璀﹀憡", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(async () => {
+    const { code } = await delRepair(ids);
+    if (code === 200) {
+      ElMessage.success("鍒犻櫎鎴愬姛");
+      getTableData();
+    }
+  });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.table_list {
+  margin-top: unset;
+}
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 10px;
+}
+</style>
diff --git a/src/views/procurementManagement/invoiceEntry/components/ExpandTable.vue b/src/views/procurementManagement/invoiceEntry/components/ExpandTable.vue
index 0523e40..7596e6d 100644
--- a/src/views/procurementManagement/invoiceEntry/components/ExpandTable.vue
+++ b/src/views/procurementManagement/invoiceEntry/components/ExpandTable.vue
@@ -55,28 +55,28 @@
       label: "鍚◣鍗曚环(鍏�)",
       prop: "taxInclusiveUnitPrice",
       formatData: (val) => {
-        return parseFloat(val).toFixed(2) ?? 0;
+        return val ? parseFloat(val).toFixed(2) : "-";
       },
     },
     {
       label: "鍚◣鎬讳环(鍏�)",
       prop: "taxInclusiveTotalPrice",
       formatData: (val) => {
-        return parseFloat(val).toFixed(2) ?? 0;
+        return val ? parseFloat(val).toFixed(2) : "-";
       },
     },
     {
       label: "涓嶅惈绋庢�讳环(鍏�)",
       prop: "taxExclusiveTotalPrice",
       formatData: (val) => {
-        return parseFloat(val).toFixed(2) ?? 0;
+        return val ? parseFloat(val).toFixed(2) : "-";
       },
     },
     {
       label: "鏈鏉ョエ閲戦(鍏�)",
       prop: "ticketsAmount",
       formatData: (val) => {
-        return parseFloat(val).toFixed(2) ?? 0;
+        return val ? parseFloat(val).toFixed(2) : "-";
       },
     },
     {
@@ -87,7 +87,7 @@
       label: "鏈潵绁ㄩ噾棰�(鍏�)",
       prop: "futureTicketsAmount",
       formatData: (val) => {
-        return parseFloat(val).toFixed(2) ?? 0;
+        return val ? parseFloat(val).toFixed(2) : "-";
       },
     },
   ],
diff --git a/src/views/procurementManagement/invoiceEntry/components/Modal.vue b/src/views/procurementManagement/invoiceEntry/components/Modal.vue
index 1fd32e9..2e0f3f3 100644
--- a/src/views/procurementManagement/invoiceEntry/components/Modal.vue
+++ b/src/views/procurementManagement/invoiceEntry/components/Modal.vue
@@ -235,7 +235,7 @@
     prop: "taxInclusiveUnitPrice",
     width: 150,
     formatData: (val) => {
-      return parseFloat(val).toFixed(2) ?? 0;
+      return val ? parseFloat(val).toFixed(2) : 0;
     },
   },
   {
@@ -385,7 +385,8 @@
         salesContractNo: form.salesContractNo,
         projectName: form.projectName,
         productData: form.productData,
-        issUerId: form.issUerId, // 褰曞叆浜�
+        issueDate: form.entryDate,
+        issUerId: form.issUerId, // 褰曞叆浜篿d
         issUer: form.issUer, // 褰曞叆浜�
         salesContractNoId: form.salesContractNoId,
         supplierName: form.supplierName,
diff --git a/src/views/procurementManagement/invoiceEntry/index.vue b/src/views/procurementManagement/invoiceEntry/index.vue
index 6de82f0..3f8fcf9 100644
--- a/src/views/procurementManagement/invoiceEntry/index.vue
+++ b/src/views/procurementManagement/invoiceEntry/index.vue
@@ -114,21 +114,21 @@
       label: "鍚堝悓閲戦(鍏�)",
       prop: "contractAmount",
       formatData: (val) => {
-        return parseFloat(val).toFixed(2) ?? 0;
+        return val ? parseFloat(val).toFixed(2) : 0;
       },
     },
     {
       label: "宸插紑绁ㄩ噾棰�(鍏�)",
       prop: "receiptPaymentAmount",
       formatData: (val) => {
-        return parseFloat(val).toFixed(2) ?? 0;
+        return val ? parseFloat(val).toFixed(2) : 0;
       },
     },
     {
       label: "寰呭紑绁ㄩ噾棰�(鍏�)",
       prop: "unReceiptPaymentAmount",
       formatData: (val) => {
-        return parseFloat(val).toFixed(2) ?? 0;
+        return val ? parseFloat(val).toFixed(2) : 0;
       },
     },
     // {
diff --git a/src/views/procurementManagement/paymentEntry/index.vue b/src/views/procurementManagement/paymentEntry/index.vue
index 3dc9bb7..109200d 100644
--- a/src/views/procurementManagement/paymentEntry/index.vue
+++ b/src/views/procurementManagement/paymentEntry/index.vue
@@ -254,21 +254,21 @@
     label: "鍙戠エ閲戦(鍏�)",
     prop: "invoiceAmount",
     formatData: (params) => {
-      return parseFloat(params).toFixed(2);
+      return params ? parseFloat(params).toFixed(2) : 0;
     },
   },
   {
     label: "宸蹭粯娆鹃噾棰�(鍏�)",
     prop: "paymentAmountTotal",
     formatData: (params) => {
-      return parseFloat(params).toFixed(2);
+      return params ? parseFloat(params).toFixed(2) : 0;
     },
   },
   {
     label: "寰呬粯娆鹃噾棰�(鍏�)",
     prop: "unPaymentAmountTotal",
     formatData: (params) => {
-      return parseFloat(params).toFixed(2);
+      return params ? parseFloat(params).toFixed(2) : 0;
     },
   },
 ]);
diff --git a/src/views/procurementManagement/paymentHistory/index.vue b/src/views/procurementManagement/paymentHistory/index.vue
index 58e5f92..8811129 100644
--- a/src/views/procurementManagement/paymentHistory/index.vue
+++ b/src/views/procurementManagement/paymentHistory/index.vue
@@ -74,7 +74,7 @@
     label: "浠樻閲戦",
     prop: "currentPaymentAmount",
     formatData: (params) => {
-      return parseFloat(params).toFixed(2);
+      return params ? parseFloat(params).toFixed(2) : 0;
     },
   },
   {
diff --git a/src/views/procurementManagement/paymentLedger/index.vue b/src/views/procurementManagement/paymentLedger/index.vue
index 665242c..61e6f98 100644
--- a/src/views/procurementManagement/paymentLedger/index.vue
+++ b/src/views/procurementManagement/paymentLedger/index.vue
@@ -168,14 +168,14 @@
     label: "鍙戠エ閲戦(鍏�)",
     prop: "invoiceAmount",
     formatData: (params) => {
-      return parseFloat(params).toFixed(2);
+      return params ? parseFloat(params).toFixed(2) : 0;
     },
   },
   {
     label: "浠樻閲戦(鍏�)",
     prop: "currentPaymentAmount",
     formatData: (params) => {
-      return parseFloat(params).toFixed(2);
+      return params ? parseFloat(params).toFixed(2) : 0;
     },
   },
   {
diff --git a/src/views/procurementManagement/procurementInvoiceLedger/index.vue b/src/views/procurementManagement/procurementInvoiceLedger/index.vue
index b71bec0..e5565a5 100644
--- a/src/views/procurementManagement/procurementInvoiceLedger/index.vue
+++ b/src/views/procurementManagement/procurementInvoiceLedger/index.vue
@@ -136,7 +136,7 @@
       prop: "taxInclusiveTotalPrice",
       align: "center",
       formatData: (cell) => {
-        return parseFloat(cell).toFixed(2);
+        return cell ? parseFloat(cell).toFixed(2) : 0;
       },
     },
     {
@@ -149,7 +149,7 @@
       prop: "ticketsAmount",
       align: "center",
       formatData: (cell) => {
-        return parseFloat(cell).toFixed(2);
+        return cell ? parseFloat(cell).toFixed(2) : 0;
       },
     },
     {
@@ -157,7 +157,7 @@
       prop: "unTicketsPrice",
       align: "center",
       formatData: (cell) => {
-        return parseFloat(cell).toFixed(2);
+        return cell ? parseFloat(cell).toFixed(2) : 0;
       },
     },
     {
diff --git a/src/views/salesManagement/invoiceRegistration/index.vue b/src/views/salesManagement/invoiceRegistration/index.vue
index 528ba0c..25c2f01 100644
--- a/src/views/salesManagement/invoiceRegistration/index.vue
+++ b/src/views/salesManagement/invoiceRegistration/index.vue
@@ -424,6 +424,7 @@
     productData: [],
     invoiceNo: "",
     createUer: "",
+    issueDate: "",
   },
   rules: {
     salesLedgerId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
diff --git a/src/views/salesManagement/receiptPaymentHistory/index.vue b/src/views/salesManagement/receiptPaymentHistory/index.vue
index 7dd8f18..f240041 100644
--- a/src/views/salesManagement/receiptPaymentHistory/index.vue
+++ b/src/views/salesManagement/receiptPaymentHistory/index.vue
@@ -97,7 +97,7 @@
     label: "鍥炴閲戦锛堝厓锛�",
     prop: "receiptPaymentAmount",
     formatData: (params) => {
-      return parseFloat(params).toFixed(2);
+      return params ? parseFloat(params).toFixed(2) : 0;
     },
   },
   {

--
Gitblit v1.9.3