From fba474712a68a9c85ac4bd5006f16194e13c17bb Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 02 七月 2025 09:12:47 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev' into dev

---
 src/hooks/useFormData.js                                  |    7 
 src/views/equipment/management/index.vue                  |  676 ++++++++++++++++++++++++
 src/hooks/useModal.js                                     |   41 +
 src/components/Table/Pagination.vue                       |  100 +++
 src/api/equipment/management/index.js                     |   32 +
 src/hooks/usePaginationApi.jsx                            |   16 
 src/components/Table/PIMTable.vue                         |  432 +++++++++++++++
 src/components/Table/table.vue                            |   39 +
 src/views/equipment/management/mould/managementDialog.vue |  290 ++++++++++
 src/utils/index.js                                        |    5 
 src/views/archiveManagement/index.vue                     |   11 
 11 files changed, 1,634 insertions(+), 15 deletions(-)

diff --git a/src/api/equipment/management/index.js b/src/api/equipment/management/index.js
new file mode 100644
index 0000000..f7d7725
--- /dev/null
+++ b/src/api/equipment/management/index.js
@@ -0,0 +1,32 @@
+// 璁惧绠$悊
+import request from '@/utils/request'
+
+// /equipmentManagement/list
+// 鏌ヨ璁惧绠$悊鍒楄〃
+export function getManagementList(query) {
+  return request({
+    url: '/equipmentManagement/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// /equipmentManagement/addOrEditEquipment
+// 娣诲姞鎴栫紪杈戣澶�
+export function addOrEditEquipment(data) {
+  return request({
+    url: '/equipmentManagement/addOrEditEquipment',
+    method: 'post',
+    data
+  })
+}
+
+// /equipmentManagement/delEquipment
+// 鍒犻櫎璁惧
+export function delEquipment(data) {
+  return request({
+    url: '/equipmentManagement/delEquipment',
+    method: 'delete',
+    data
+  })
+}
\ No newline at end of file
diff --git a/src/components/Table/PIMTable.vue b/src/components/Table/PIMTable.vue
new file mode 100644
index 0000000..fb472fb
--- /dev/null
+++ b/src/components/Table/PIMTable.vue
@@ -0,0 +1,432 @@
+<template>
+  <el-table
+    ref="multipleTable"
+    v-loading="tableLoading"
+    :border="border"
+    :data="tableData"
+    :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
+    :height="height"
+    :highlight-current-row="highlightCurrentRow"
+    :row-class-name="rowClassName"
+    :row-style="rowStyle"
+    :row-key="rowKey"
+    style="width: 100%"
+    tooltip-effect="dark"
+    :expand-row-keys="expandRowKeys"
+    :show-summary="isShowSummary"
+    :summary-method="summaryMethod"
+    @row-click="rowClick"
+    @current-change="currentChange"
+    @selection-change="handleSelectionChange"
+    @expand-change="expandChange"
+    class="lims-table"
+  >
+    <el-table-column
+      align="center"
+      type="selection"
+      width="55"
+      v-if="isSelection"
+    />
+    <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+
+    <el-table-column
+      v-for="(item, index) in column"
+      :key="index"
+      :column-key="item.columnKey"
+      :filter-method="item.filterHandler"
+      :filter-multiple="item.filterMultiple"
+      :filtered-value="item.filteredValue"
+      :filters="item.filters"
+      :fixed="item.fixed"
+      :label="item.label"
+      :prop="item.prop"
+      show-overflow-tooltip
+      :align="item.align"
+      :sortable="!!item.sortable"
+      :type="item.type"
+      :width="item.width"
+    >
+      <template
+        v-if="item.hasOwnProperty('colunmTemplate')"
+        #[item.colunmTemplate]="scope"
+      >
+        <slot
+          v-if="item.theadSlot"
+          :name="item.theadSlot"
+          :index="scope.$index"
+          :row="scope.row"
+        />
+      </template>
+
+      <template #default="scope">
+        <!-- 鎻掓Ы -->
+        <div v-if="item.dataType == 'slot'">
+          <slot
+            v-if="item.slot"
+            :index="scope.$index"
+            :name="item.slot"
+            :row="scope.row"
+          />
+        </div>
+        <!-- 杩涘害鏉� -->
+        <div v-else-if="item.dataType == 'progress'">
+          <el-progress :percentage="Number(scope.row[item.prop])" />
+        </div>
+        <!-- 鍥剧墖 -->
+        <div v-else-if="item.dataType == 'image'">
+          <img
+            :src="javaApi + '/img/' + scope.row[item.prop]"
+            alt=""
+            style="width: 40px; height: 40px; margin-top: 10px"
+          />
+        </div>
+
+        <!-- tag -->
+        <div v-else-if="item.dataType == 'tag'">
+          <el-tag
+            v-if="
+              typeof dataTypeFn(scope.row[item.prop], item.formatData) ===
+              'string'
+            "
+            :title="formatters(scope.row[item.prop], item.formatData)"
+            :type="formatType(scope.row[item.prop], item.formatType)"
+          >
+            {{ formatters(scope.row[item.prop], item.formatData) }}
+          </el-tag>
+
+          <el-tag
+            v-for="(tag, index) in dataTypeFn(
+              scope.row[item.prop],
+              item.formatData
+            )"
+            v-else-if="
+              typeof dataTypeFn(scope.row[item.prop], item.formatData) ===
+              'object'
+            "
+            :key="index"
+            :title="formatters(scope.row[item.prop], item.formatData)"
+            :type="formatType(tag, item.formatType)"
+          >
+            {{ item.tagGroup ? tag[item.tagGroup.label] ?? tag : tag }}
+          </el-tag>
+
+          <el-tag
+            v-else
+            :title="formatters(scope.row[item.prop], item.formatData)"
+            :type="formatType(scope.row[item.prop], item.formatType)"
+          >
+            {{ formatters(scope.row[item.prop], item.formatData) }}
+          </el-tag>
+        </div>
+
+        <!-- 鎸夐挳 -->
+        <div v-else-if="item.dataType == 'action'">
+          <template v-for="(o, key) in item.operation" :key="key">
+            <el-button
+              v-show="o.type != 'upload'"
+              size="small"
+              v-if="o.showHide ? o.showHide(scope.row) : true"
+              :disabled="o.disabled ? o.disabled(scope.row) : false"
+              :plain="o.plain"
+              type="primary"
+              :style="{
+                color:
+                  o.name === '鍒犻櫎' || o.name === 'delete'
+                    ? '#f56c6c'
+                    : o.color,
+              }"
+              link
+              @click="o.clickFun(scope.row)"
+              :key="key"
+            >
+              {{ o.name }}
+            </el-button>
+            <el-upload
+              :action="
+                javaApi +
+                o.url +
+                '?id=' +
+                (o.uploadIdFun ? o.uploadIdFun(scope.row) : scope.row.id)
+              "
+              ref="uploadRef"
+              size="small"
+              :multiple="o.multiple ? o.multiple : false"
+              :limit="1"
+              :disabled="o.disabled ? o.disabled(scope.row) : false"
+              :accept="
+                o.accept
+                  ? o.accept
+                  : '.jpg,.jpeg,.png,.gif,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip,.rar'
+              "
+              v-if="o.type == 'upload'"
+              style="display: inline-block; width: 50px"
+              v-show="o.showHide ? o.showHide(scope.row) : true"
+              :headers="uploadHeader"
+              :before-upload="(file) => beforeUpload(file, scope.$index)"
+              :on-change="
+                (file, fileList) => handleChange(file, fileList, scope.$index)
+              "
+              :on-error="
+                (error, file, fileList) =>
+                  onError(error, file, fileList, scope.$index)
+              "
+              :on-success="
+                (response, file, fileList) =>
+                  handleSuccessUp(response, file, fileList, scope.$index)
+              "
+              :on-exceed="onExceed"
+              :show-file-list="false"
+            >
+              <el-button
+                :size="o.size ? o.size : 'small'"
+                link
+                type="primary"
+                :disabled="o.disabled ? o.disabled(scope.row) : false"
+                >{{ o.name }}</el-button
+              >
+            </el-upload>
+          </template>
+        </div>
+        <!-- 鍙偣鍑荤殑鏂囧瓧 -->
+        <div
+          v-else-if="item.dataType == 'link'"
+          class="cell link"
+          style="width: 100%"
+          @click="goLink(scope.row, item.linkMethod)"
+        >
+          <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
+        </div>
+        <!-- 榛樿绾睍绀烘暟鎹� -->
+        <div v-else class="cell" style="width: 100%">
+          <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
+          <span v-else>{{
+            formatters(scope.row[item.prop], item.formatData)
+          }}</span>
+        </div>
+      </template>
+    </el-table-column>
+  </el-table>
+  <pagination
+    v-if="page.total > 0"
+    :total="page.total"
+    :layout="page.layout"
+    :page="page.current"
+    :limit="page.size"
+    @pagination="paginationSearch"
+  />
+</template>
+
+<script setup>
+import pagination from "./Pagination.vue";
+import { ref, inject, getCurrentInstance } from "vue";
+import { ElMessage } from "element-plus";
+
+// 鑾峰彇鍏ㄥ眬鐨� uploadHeader
+const { proxy } = getCurrentInstance();
+const uploadHeader = proxy.uploadHeader;
+const javaApi = proxy.javaApi;
+
+const emit = defineEmits(["pagination", "expand-change", "selection-change"]);
+
+// Filters
+const typeFn = (val, row) => {
+  return typeof val === "function" ? val(row) : val;
+};
+
+const formatters = (val, format) => {
+  return typeof format === "function" ? format(val) : val;
+};
+
+// Props锛堜娇鐢� defineProps 鐨勯潪 TS 褰㈠紡锛�
+const props = defineProps({
+  tableLoading: {
+    type: Boolean,
+    default: false,
+  },
+  height: {
+    type: [Number, String],
+    default: "calc(100vh - 22em)",
+  },
+  expandRowKeys: {
+    type: Array,
+    default: () => [],
+  },
+  summaryMethod: {
+    type: Function,
+    default: () => {},
+  },
+  rowClick: {
+    type: Function,
+    default: () => {},
+  },
+  currentChange: {
+    type: Function,
+    default: () => {},
+  },
+  border: {
+    type: Boolean,
+    default: true,
+  },
+  isSelection: {
+    type: Boolean,
+    default: false,
+  },
+  isShowSummary: {
+    type: Boolean,
+    default: false,
+  },
+  highlightCurrentRow: {
+    type: Boolean,
+    default: false,
+  },
+  headerCellStyle: {
+    type: Object,
+    default: () => ({}),
+  },
+  column: {
+    type: Array,
+    default: () => [],
+  },
+  rowClassName: {
+    type: Function,
+    default: () => "",
+  },
+  rowStyle: {
+    type: [Object, Function],
+    default: () => ({}),
+  },
+  tableData: {
+    type: Array,
+    default: () => [],
+  },
+  rowKey: {
+    type: String,
+    default: undefined,
+  },
+  page: {
+    type: Object,
+    default: () => ({
+      total: 0,
+      current: 0,
+      size: 10,
+      layout: "total, sizes, prev, pager, next, jumper",
+    }),
+  },
+  total: {
+    type: Number,
+    default: 0,
+  },
+});
+
+// Data
+const uploadRefs = ref([]);
+const currentFiles = ref({});
+const uploadKeys = ref({});
+
+const indexMethod = (index) => {
+  return (props.page.current - 1) * props.page.size + index + 1;
+};
+
+// 鐐瑰嚮 link 浜嬩欢
+const goLink = (row, linkMethod) => {
+  if (!linkMethod) {
+    return ElMessage.warning("璇烽厤缃� link 浜嬩欢");
+  }
+  const parentMethod = getParentMethod(linkMethod);
+  if (typeof parentMethod === "function") {
+    parentMethod(row);
+  } else {
+    console.warn(`鐖剁粍浠朵腑鏈壘鍒版柟娉�: ${linkMethod}`);
+  }
+};
+
+// 鑾峰彇鐖剁粍浠舵柟娉曪紙绀轰緥瀹炵幇锛�
+const getParentMethod = (methodName) => {
+  const parentMethods = inject("parentMethods", {});
+  return parentMethods[methodName];
+};
+
+const dataTypeFn = (val, format) => {
+  if (typeof format === "function") {
+    return format(val);
+  } else return val;
+};
+
+const formatType = (val, format) => {
+  if (typeof format === "function") {
+    return format(val);
+  } else return "";
+};
+
+// 鏂囦欢鍙樺寲澶勭悊
+const handleChange = (file, fileList, index) => {
+  if (fileList.length > 1) {
+    const earliestFile = fileList[0];
+    uploadRefs.value[index]?.handleRemove(earliestFile);
+  }
+  currentFiles.value[index] = file;
+};
+
+// 鏂囦欢涓婁紶鍓嶆牎楠�
+const beforeUpload = (rawFile, index) => {
+  currentFiles.value[index] = {};
+  if (rawfile.size > 1024 * 1024 * 10 * 10) {
+    ElMessage.error("涓婁紶鏂囦欢涓嶈秴杩�10M");
+    return false;
+  }
+  return true;
+};
+
+// 涓婁紶鎴愬姛
+const handleSuccessUp = (response, file, fileList, index) => {
+  if (response.code == 200) {
+    if (uploadRefs[index]) {
+      uploadRefs[index].clearFiles();
+    }
+    currentFiles[index] = file;
+    ElMessage.success("涓婁紶鎴愬姛");
+    resetUploadComponent(index);
+  } else {
+    ElMessage.error(response.message);
+  }
+};
+
+const resetUploadComponent = (index) => {
+  uploadKeys[index] = Date.now();
+};
+
+// 涓婁紶澶辫触
+const onError = (error, file, fileList, index) => {
+  ElMessage.error("鏂囦欢涓婁紶澶辫触锛岃閲嶈瘯");
+  if (uploadRefs.value[index]) {
+    uploadRefs.value[index].clearFiles();
+  }
+};
+
+// 鏂囦欢鏁伴噺瓒呴檺鎻愮ず
+const onExceed = () => {
+  ElMessage.warning("瓒呭嚭鏂囦欢涓暟");
+};
+
+const paginationSearch = ({ page, limit }) => {
+  emit("pagination", { page: page, limit: limit });
+};
+
+const expandChange = (row, expandedRows) => {
+  emit("expand-change", row, expandedRows);
+};
+
+const handleSelectionChange = (newSelection) => {
+  emit("selection-change", newSelection);
+};
+</script>
+
+<style scoped lang="scss">
+.cell {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  padding-right: 0 !important;
+  padding-left: 0 !important;
+}
+</style>
diff --git a/src/components/Table/Pagination.vue b/src/components/Table/Pagination.vue
new file mode 100644
index 0000000..7639e64
--- /dev/null
+++ b/src/components/Table/Pagination.vue
@@ -0,0 +1,100 @@
+<template>
+  <div :class="{ hidden }" class="pagination-container">
+    <el-pagination
+      :background="background"
+      v-model:current-page="currentPage"
+      v-model:page-size="pageSize"
+      :layout="layout"
+      :page-sizes="pageSizes"
+      :pager-count="pagerCount"
+      :total="total"
+      v-bind="$attrs"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+    />
+  </div>
+</template>
+
+<script setup>
+import { computed } from 'vue'
+import { scrollTo } from '@/utils/scroll-to'
+
+const props = defineProps({
+  total: {
+    type: Number,
+    required: true
+  },
+  page: {
+    type: Number,
+    default: 1
+  },
+  limit: {
+    type: Number,
+    default: 20
+  },
+  pageSizes: {
+    type: Array,
+    default: () => [10, 20, 30, 50, 100]
+  },
+  pagerCount: {
+    type: Number,
+    default: () => (document.body.clientWidth < 992 ? 5 : 7)
+  },
+  layout: {
+    type: String,
+    default: 'total, sizes, prev, pager, next, jumper'
+  },
+  background: {
+    type: Boolean,
+    default: true
+  },
+  autoScroll: {
+    type: Boolean,
+    default: true
+  },
+  hidden: {
+    type: Boolean,
+    default: false
+  }
+})
+
+const emit = defineEmits(['update:page', 'update:limit', 'pagination'])
+
+const currentPage = computed({
+  get: () => props.page,
+  set: (val) => emit('update:page', val)
+})
+
+const pageSize = computed({
+  get: () => props.limit,
+  set: (val) => emit('update:limit', val)
+})
+
+const handleSizeChange = (val) => {
+  if (currentPage.value * val > props.total) {
+    currentPage.value = 1
+  }
+  emit('pagination', { page: currentPage.value, limit: val })
+  if (props.autoScroll) {
+    scrollTo(0, 800)
+  }
+}
+
+const handleCurrentChange = (val) => {
+  emit('pagination', { page: val, limit: pageSize.value })
+  if (props.autoScroll) {
+    scrollTo(0, 800)
+  }
+}
+</script>
+
+<style scoped>
+.pagination-container {
+  background: #fff;
+  padding: 16px 0;
+  margin-top: 0;
+}
+.pagination-container.hidden {
+  display: none;
+}
+</style>
\ No newline at end of file
diff --git a/src/components/Table/table.vue b/src/components/Table/table.vue
new file mode 100644
index 0000000..01509b1
--- /dev/null
+++ b/src/components/Table/table.vue
@@ -0,0 +1,39 @@
+<template>
+  <el-table :data="tableData" :column="columnss" style="width: 100%">
+    <el-table-column type="index" width="50">
+      <template #default="scope">
+        {{ getRowIndex(scope.$index) }}
+      </template>
+    </el-table-column>
+    <el-table-column
+      v-for="(item, index) in columnss"
+      :key="index"
+      :prop="item.prop"
+      :label="item.label"
+      :align="item.align || 'center'"
+      :min-width="item.minWidth || '100px'"
+      :width="item.width"
+      :fixed="item.fixed || false"
+      :show-overflow-tooltip="showOverflowTooltip || true"
+    >
+    </el-table-column>
+  </el-table>
+</template>
+<script setup>
+import { ref, onMounted, watch, nextTick } from "vue";
+
+const props = defineProps({
+  tableData: {
+    type: Array,
+    default: () => [],
+  },
+  columnss: {
+    type: Array,
+    default: () => [],
+  },
+});
+const getRowIndex = (index) => {
+  return index + 1;
+};
+</script>
+<style scoped></style>
diff --git a/src/hooks/useFormData.js b/src/hooks/useFormData.js
index 325e981..e065382 100644
--- a/src/hooks/useFormData.js
+++ b/src/hooks/useFormData.js
@@ -4,7 +4,8 @@
  */
 import { ref, reactive, computed, nextTick } from "vue";
 import { ElMessage, ElMessageBox } from "element-plus";
-import { clone } from "lodash";
+import { deepClone } from "@/utils/index.js"
+
 
 /**
  * 鍒涘缓琛ㄥ崟鏁版嵁绠$悊鍔熻兘
@@ -324,8 +325,8 @@
 }
 
 // 鍚戝悗鍏煎鐨勯粯璁ゅ鍑�
-export default function useFormDataSimple(initData) {
-  const form = reactive(clone(initData, true));
+export default function useFormDatas(initData) {
+  const form = reactive(deepClone(initData, true));
 
   function resetForm() {
     const initData2 = JSON.parse(JSON.stringify(initData));
diff --git a/src/hooks/useModal.js b/src/hooks/useModal.js
new file mode 100644
index 0000000..0d443a1
--- /dev/null
+++ b/src/hooks/useModal.js
@@ -0,0 +1,41 @@
+import { ref } from "vue";
+export function useModal(options) {
+  const id = ref();
+  const visible = ref(false);
+  const loading = ref(false);
+  const modalOptions = ref({});
+
+  const openModal = (e) => {
+    id.value = e;
+    modalOptions.value = {
+      title: e ? `淇敼${options.title}` : `鏂板${options.title}`,
+      content: "纭畾鎵ц姝ゆ搷浣滃悧锛�",
+      confirmText: "纭畾",
+      cancelText: "鍙栨秷",
+    };
+    visible.value = true;
+  };
+
+  // 鍏抽棴妯℃�佹
+  const closeModal = () => {
+    visible.value = false;
+    loading.value = false;
+  };
+
+  // 纭鎿嶄綔
+  const handleConfirm = async (callback) => {
+    loading.value = true;
+    callback();
+    closeModal();
+  };
+
+  return {
+    id,
+    visible,
+    loading,
+    modalOptions,
+    openModal,
+    closeModal,
+    handleConfirm,
+  };
+}
diff --git a/src/hooks/usePaginationApi.jsx b/src/hooks/usePaginationApi.jsx
index aa995b0..749b078 100644
--- a/src/hooks/usePaginationApi.jsx
+++ b/src/hooks/usePaginationApi.jsx
@@ -1,8 +1,8 @@
 import { ref, reactive, watchEffect, unref } from "vue";
-import useFormData from "./useFormData.js";
-// import { message } from "@/utils/message";
+import useFormData from "@/hooks/useFormData";
+import { deepClone, isEqual } from "@/utils/index.js"
+import { ElMessage } from 'element-plus'
 
-import { clone, isEqual } from "lodash";
 /**
  * 鍒嗛〉api
  * @param api 鎺ュ彛
@@ -20,7 +20,7 @@
 ) {
   const dataList = ref([]);
   const { form: filters, resetForm } = useFormData(initalFilters);
-  let lastFilters = clone(initalFilters);
+  let lastFilters = deepClone(initalFilters);
   const sorter = reactive(sorters || {});
   const others = ref({});
   const loading = ref(true);
@@ -79,14 +79,14 @@
     // 濡傛灉杩欐鍜屼笂娆$殑filter涓嶅悓锛岄偅涔堝氨閲嶇疆椤电爜
     if (!isEqual(unref(filters), lastFilters)) {
       pagination.currentPage = 1;
-      lastFilters = clone(unref(filters));
+      lastFilters = deepClone(unref(filters));
     }
     loading.value = true;
     api({
       ...getFinalParams(),
       current: pagination.currentPage,
       size: pagination.pageSize
-    }).then(({ code, data, ...rest }) => {
+    }).then(({ code, data, msg, ...rest }) => {
       if (code == 200) {
         // pagination.currentPage = meta.current_page;
         // pagination.pageSize = meta.per_page;
@@ -97,7 +97,7 @@
         loading.value = false;
       } else {
         loading.value = false;
-        // message(data.msg, { type: "error" });
+        ElMessage({ message: msg, type: "error" });
       }
     });
   }
@@ -120,7 +120,7 @@
   }
 
   watchEffect(() => {
-    pagination.align = paginationAlign.value;
+    pagination.align = paginationAlign.value
   });
 
   // onMounted(() => {
diff --git a/src/utils/index.js b/src/utils/index.js
index 9329fe2..3c2e031 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -387,4 +387,7 @@
 export function isNumberStr(str) {
   return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
 }
- 
+
+export function isEqual(obj1, obj2) {
+  return JSON.stringify(obj1) === JSON.stringify(obj2);
+}
diff --git a/src/views/archiveManagement/index.vue b/src/views/archiveManagement/index.vue
index ac6274d..81d3a18 100644
--- a/src/views/archiveManagement/index.vue
+++ b/src/views/archiveManagement/index.vue
@@ -118,12 +118,12 @@
             </el-input>
           </div>
         </el-col>
-        <el-col :offset="10" :span="2">
+        <el-col :offset="8" :span="3">
           <el-button :icon="Delete" type="danger" @click="delHandler"
             >鍒犻櫎</el-button
           >
         </el-col>
-        <el-col :span="2">
+        <el-col :span="3">
           <el-button
             :disabled="!tableSwitch"
             :icon="Plus"
@@ -142,6 +142,7 @@
         :table-data="tableData"
         @edit="handleEdit"
         @selection-change="handleSelectionChange"
+        style="height: calc(65vh);"
       >
       </ETable>
       <Pagination
@@ -288,6 +289,7 @@
       treeId: queryParams.treeId,
       current: queryParams.current,
       size: queryParams.pageSize,
+      searchAll: queryParams.searchAll,
     });
 
     if (res.code !== 200) {
@@ -556,7 +558,10 @@
 };
 
 // ===== 鐢熷懡鍛ㄦ湡 =====
-onMounted(getList);
+onMounted(()=>{
+  getList();
+  getArchiveListData();
+});
 </script>
 <style lang="scss" scoped>
 .custom-tree-node {
diff --git a/src/views/equipment/management/index.vue b/src/views/equipment/management/index.vue
new file mode 100644
index 0000000..3481d2f
--- /dev/null
+++ b/src/views/equipment/management/index.vue
@@ -0,0 +1,676 @@
+<template>
+  <div class="app-container">
+    <el-form :inline="true" :model="queryParams" class="search-form">
+      <el-form-item v-if="shouldShowSearch" label="鎼滅储">
+        <el-input
+          v-model="queryParams.searchAll"
+          :placeholder="searchPlaceholder"
+          clearable
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="search">鏌ヨ</el-button>
+        <el-button @click="resetQuery">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <el-card>
+      <!-- 鏍囩椤� -->
+      <el-tabs
+        v-model="activeTab"
+        class="info-tabs"
+        @tab-click="handleTabClick"
+      >
+        <el-tab-pane
+          v-for="tab in tabs"
+          :key="tab.name"
+          :label="tab.label"
+          :name="tab.name"
+        />
+      </el-tabs>
+
+      <!-- 鎿嶄綔鎸夐挳鍖� -->
+      <el-row :gutter="24" class="table-toolbar">
+        <el-button :icon="Plus" type="primary" @click="handleAdd"
+          >鏂板缓</el-button
+        >
+        <el-button :icon="Delete" type="danger" @click="handleDelete"
+          >鍒犻櫎</el-button
+        >
+        <!-- <el-button
+          v-show="canExport"
+          :icon="Download"
+          type="info"
+          @click="handleExport"
+          >瀵煎嚭</el-button
+        > -->
+      </el-row>
+      <!-- 琛ㄦ牸缁勪欢 -->
+      <div>
+        <data-table
+          :border="true"
+          :columns="columns"
+          :loading="loading"
+          style="width: 100%; height: calc(100vh - 29em)"
+          :show-selection="true"
+          :table-data="tableData"
+          @edit="handleEdit"
+          @viewRow="handleView"
+          @selection-change="handleSelectionChange"
+          :operations="['edit', 'viewRow']"
+          :operationsWidth="200"
+        >
+          <!-- 瀛楁鍚嶇О鍒楃殑鑷畾涔夋彃妲� - 鏄剧ず涓烘爣绛� -->
+          <template
+            v-if="tabName === 'coalQualityMaintenance'"
+            #fieldIds="{ row }"
+          >
+            <template
+              v-if="
+                typeof row.fieldIds === 'string' && row.fieldIds.includes(',')
+              "
+            >
+              <el-tag
+                v-for="(field, index) in row.fieldIds.split(',')"
+                :key="index"
+                size="small"
+                style="margin-right: 4px; margin-bottom: 2px"
+                type="primary"
+              >
+                {{ getFieldDisplayName(field.trim()) }}
+              </el-tag>
+            </template>
+            <template v-else>
+              <el-tag size="small" type="primary">
+                {{ getFieldDisplayName(row.fieldIds) || "--" }}
+              </el-tag>
+            </template>
+          </template>
+        </data-table>
+      </div>
+      <pagination
+        v-if="total > 0"
+        :layout="'total, prev, pager, next, jumper'"
+        :limit="pageSizes"
+        :page="pageNum"
+        :total="total"
+        @pagination="handPagination"
+      />
+      <managementDialog
+      
+      ></managementDialog>
+    </el-card>
+  </div>
+</template>
+
+<script setup>
+import {
+  computed,
+  getCurrentInstance,
+  onMounted,
+  reactive,
+  ref,
+  nextTick,
+} from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { Delete, Download, Plus } from "@element-plus/icons-vue";
+
+// ===== 缁勪欢瀵煎叆 =====
+import DataTable from "@/components/Table/ETable.vue";
+import Pagination from "@/components/Pagination";
+import managementDialog from "./mould/managementDialog.vue";
+// ===== API 鏈嶅姟瀵煎叆 =====
+import { delSupply, getSupply } from "@/api/basicInformation/supplier.js";
+import { useDelete } from "@/hooks/useDelete.js";
+import { testUserList } from "@/api/tool/publicInterface.js";
+const { proxy } = getCurrentInstance();
+
+// ===== 鍝嶅簲寮忕姸鎬佺鐞� =====
+const dialogFormVisible = ref(false);
+const form = ref({});
+const title = ref("");
+const copyForm = ref({});
+const addOrEdit = ref("add");
+
+// 鏁版嵁缂撳瓨鏄犲皠
+const userList = ref([]);
+const userMap = ref({}); // 鐢ㄦ埛ID -> 鐢ㄦ埛鍚嶆槧灏勮〃
+const addressMap = ref({}); // 鍦板潃ID -> 鍦板潃淇℃伅鏄犲皠琛�
+const coalFieldList = ref([]); // 鐓よ川瀛楁鍒楄〃
+
+// 椤甸潰鐘舵�佹帶鍒�
+const tabName = ref("management");
+const loading = ref(false);
+const activeTab = ref("management");
+
+// 鍒嗛〉鐘舵�佺鐞�
+const pageNum = ref(1);
+const pageSizes = ref(10);
+const total = ref(0);
+
+// 琛ㄦ牸鐘舵�佺鐞�
+const selectedRows = ref([]);
+const tableData = ref([]);
+const columns = ref();
+
+// 鏌ヨ鍙傛暟
+const queryParams = reactive({});
+
+// 鍦板潃閫夋嫨鏁版嵁
+const addressSelectOptions = ref([]);
+
+// ===== 閰嶇疆甯搁噺 =====
+
+// 鏍囩椤甸厤缃�
+const tabs = reactive([
+  { name: "management", label: "渚涘簲鍟嗕俊鎭�" },
+  { name: "customer", label: "瀹㈡埛淇℃伅" },
+]);
+
+// ===== 宸ュ叿鍑芥暟 =====
+
+
+/**
+ * 鏋勫缓鍦板潃鏄犲皠琛�
+ * @param {Array} areaData - 鍦板潃鏁版嵁
+ * @description 閫掑綊鏋勫缓鍦板潃鏄犲皠琛紝鏀寔澶氱骇鍦板潃鏌ユ壘
+ */
+const buildAddressMap = (areaData) => {
+  const buildMap = (list, pathList = []) => {
+    list.forEach((item) => {
+      const currentPath = [...pathList, item.label];
+      addressMap.value[item.id] = {
+        name: item.label,
+        fullPath: currentPath.join(" / "),
+      };
+      if (item.children && item.children.length > 0) {
+        buildMap(item.children, currentPath);
+      }
+    });
+  };
+  buildMap(areaData);
+};
+
+/**
+ * 鏍煎紡鍖栧湴鍧�鏁扮粍涓烘樉绀哄瓧绗︿覆
+ * @param {Array} addressIds - 鍦板潃ID鏁扮粍
+ * @returns {string} 鏍煎紡鍖栧悗鐨勫湴鍧�瀛楃涓�
+ * @description 灏嗗湴鍧�ID鏁扮粍杞崲涓哄彲璇荤殑鍦板潃瀛楃涓�
+ */
+const formatAddressArray = (addressIds) => {
+  if (
+    !addressMap.value ||
+    Object.keys(addressMap.value).length === 0 ||
+    !addressIds ||
+    !Array.isArray(addressIds) ||
+    addressIds.length === 0 ||
+    addressIds.every((id) => !id)
+  ) {
+    return "--";
+  }
+
+  const addressNames = addressIds.map(
+    (id) => addressMap.value[id]?.name || "--"
+  );
+
+  if (addressNames.every((name) => name === "--")) {
+    return "--";
+  }
+
+  return addressNames.filter((name) => name !== "--").join(" / ");
+};
+
+/**
+ * 鑾峰彇鐢ㄦ埛鍒楄〃鏁版嵁骞舵瀯寤烘槧灏勮〃
+ * @description 鑾峰彇鐢ㄦ埛鏁版嵁骞舵瀯寤篒D鍒扮敤鎴峰悕鐨勬槧灏勫叧绯�
+ */
+const getUserList = async () => {
+  try {
+    const res = await testUserList();
+    console.log("鑾峰彇鐢ㄦ埛鍒楄〃鏁版嵁:", res);
+    console.log("userMap:", userMap.value);
+    if (res && res.data) {
+      userList.value = res.data;
+      userList.value.forEach((user) => {
+        userMap.value[user.userId] = user.nickName;
+      });
+    }
+  } catch (error) {
+    console.error("鑾峰彇鐢ㄦ埛鍒楄〃澶辫触:", error);
+  }
+};
+
+/**
+ * 鏍规嵁瀛楁ID鑾峰彇瀛楁鏄剧ず鍚嶇О
+ * @param {string|number} fieldId - 瀛楁ID
+ * @returns {string} 瀛楁鏄剧ず鍚嶇О
+ * @description 閫氳繃瀛楁ID鍖归厤瀵瑰簲鐨勫瓧娈靛悕绉�
+ */
+const getFieldDisplayName = (fieldId) => {
+  if (!fieldId) return "--";
+
+  const numId = parseInt(fieldId);
+  const matchedField = coalFieldList.value.find((item) => item.id === numId);
+
+  return matchedField ? matchedField.fieldName : numId;
+};
+
+/**
+ * 褰撳墠鏍囩椤垫槸鍚︽敮鎸佸鍑哄姛鑳�
+ */
+const canExport = computed(() => {
+  return ["management"].includes(tabName.value);
+});
+
+/**
+ * 鎼滅储妗嗗崰浣嶇鏂囨湰
+ */
+const searchPlaceholder = computed(() => {
+  const placeholderMap = {
+    management: "渚涘簲鍟�/缁熶竴璇嗗埆鐮�/璇︾粏鍦板潃",
+  };
+  return placeholderMap[tabName.value] || "璇疯緭鍏ユ悳绱俊鎭�";
+});
+
+/**
+ * 鏄惁鏄剧ず鎼滅储妗�
+ */
+const shouldShowSearch = computed(() => {
+  return [
+    "management",
+  ].includes(tabName.value);
+});
+
+/**
+ * 褰撳墠閫変腑琛屾暟閲�
+ */
+const selectedCount = computed(() => selectedRows.value.length);
+
+// ===== 琛ㄦ牸鍒楅厤缃� =====
+
+/**
+ * 渚涘簲鍟嗚〃鏍煎垪閰嶇疆
+ */
+const management = ref([
+  { prop: "equipmentId", label: "璁惧缂栧彿", minWidth: 100 },
+  { prop: "equipmentName", label: "璁惧鍚嶇О", minWidth: 100 },
+  { prop: "quantity", label: "鏁伴噺", minWidth: 100 },
+  { prop: "specification", label: "瑙勬牸鍨嬪彿", minWidth: 100 },
+  { prop: "usageStatus", label: "浣跨敤鐘舵��", minWidth: 100 },
+  { prop: "usingDepartment", label: "浣跨敤閮ㄩ棬", minWidth: 100 },
+  { prop: "userId", label: "浣跨敤浜�", minWidth: 100 },
+  { prop: "purchaseDate", label: "閲囪喘鏃ユ湡", minWidth: 100 },
+  { prop: "purchasePrice", label: "閲囪喘浠锋牸", minWidth: 100 },
+]);
+
+
+// ===== 浜嬩欢澶勭悊鍑芥暟 =====
+
+/**
+ * 鏍囩椤靛垏鎹簨浠跺鐞�
+ * @param {Object} tab - 鏍囩椤靛璞�
+ * @description 澶勭悊鏍囩椤靛垏鎹紝閲嶇疆琛ㄥ崟鍜岀姸鎬侊紝鍔犺浇瀵瑰簲鏁版嵁
+ */
+const handleTabClick = (tab) => {
+  // 閲嶇疆琛ㄥ崟鍜岀姸鎬�
+  form.value = {};
+  addOrEdit.value = "add";
+  loading.value = true;
+  tabName.value = tab.props.name;
+  tableData.value = [];
+  pageNum.value = 1;
+  pageSizes.value = 10;
+  total.value = 0;
+  queryParams.searchAll = "";
+  // 鏍规嵁鏍囩椤电被鍨嬭缃搴旂殑鍒楅厤缃�
+  const tabConfig = {
+    management: () => {
+      columns.value = management.value;
+      getList();
+    },
+  };
+
+  // 鎵ц瀵瑰簲鐨勯厤缃嚱鏁�
+  const configFn = tabConfig[tabName.value];
+  if (configFn) {
+    configFn();
+  }
+};
+
+/**
+ * 閲嶇疆鏌ヨ鏉′欢
+ * @description 閲嶇疆鏌ヨ鍙傛暟骞堕噸鏂板姞杞芥暟鎹�
+ */
+const resetQuery = () => {
+  Object.keys(queryParams).forEach((key) => {
+    if (key !== "pageNum" && key !== "pageSizes") {
+      queryParams[key] = "";
+    }
+  });
+  getList();
+};
+
+/**
+ * 鎼滅储鍔熻兘
+ * @description 閲嶇疆椤电爜骞舵墽琛屾悳绱�
+ */
+const search = () => {
+  pageNum.value = 1;
+  getList();
+};
+
+/**
+ * 鏂板鎸夐挳鐐瑰嚮澶勭悊
+ */
+const handleAdd = () => {
+  addOrEdit.value = "add";
+  handleAddEdit(tabName.value);
+};
+
+/**
+ * 鏂板/缂栬緫寮圭獥澶勭悊
+ * @param {string} currentTabName - 褰撳墠鏍囩椤靛悕绉�
+ * @description 鏍规嵁鏍囩椤电被鍨嬭缃脊绐楁爣棰樺苟鎵撳紑寮圭獥
+ */
+const handleAddEdit = (currentTabName) => {
+  const actionText =
+    addOrEdit.value === "add"
+      ? "鏂板"
+      : addOrEdit.value === "edit"
+      ? "缂栬緫"
+      : "鏌ョ湅";
+
+  const tabTitleMap = {
+    supplier: "渚涘簲鍟嗕俊鎭�",
+    customer: "瀹㈡埛淇℃伅",
+  };
+
+  title.value = `${actionText}${tabTitleMap[currentTabName] || ""}`;
+  openDialog();
+};
+
+/**
+ * 鎵撳紑寮圭獥
+ * @description 鏍规嵁缂栬緫鐘舵�佸喅瀹氭槸鍚﹀鍒惰〃鍗曟暟鎹�
+ */
+const openDialog = () => {
+  if (addOrEdit.value === "edit" || addOrEdit.value === "viewRow") {
+    copyForm.value = JSON.parse(JSON.stringify(form.value));
+  } else {
+    form.value = {};
+  }
+  dialogFormVisible.value = true;
+};
+
+/**
+ * 鍒嗛〉澶勭悊
+ * @param {Object} val - 鍒嗛〉鍙傛暟瀵硅薄
+ */
+const handPagination = (val) => {
+  pageNum.value = val.page;
+  pageSizes.value = val.limit;
+  getList();
+};
+
+/**
+ * 琛ㄥ崟鎻愪氦澶勭悊
+ * @param {Object} val - 鎻愪氦缁撴灉瀵硅薄
+ */
+const handleSubmit = async (val) => {
+  if (val.result.code !== 200) {
+    ElMessage.error("鎿嶄綔澶辫触锛�" + val.result.msg);
+    return;
+  }
+  ElMessage.success(val.title + val.result.msg);
+  dialogFormVisible.value = false;
+  getList();
+};
+
+/**
+ * 寮圭獥鏄剧ず鐘舵�佸鐞�
+ * @param {boolean} value - 鏄剧ず鐘舵��
+ */
+const handleDialogFormVisible = (value) => {
+  dialogFormVisible.value = value;
+};
+
+/**
+ * 琛ㄦ牸琛岄�夋嫨澶勭悊
+ * @param {Array} selection - 閫変腑鐨勮鏁版嵁
+ */
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection;
+};
+/**
+ * 缂栬緫鎸夐挳鐐瑰嚮澶勭悊
+ * @param {Object} row - 琛屾暟鎹�
+ * @description 澶勭悊缂栬緫鎿嶄綔锛屾瀯寤哄湴鍧�鏁扮粍骞舵墦寮�缂栬緫寮圭獥
+ */
+const handleEdit = (row) => {
+  form.value = JSON.parse(JSON.stringify(row));
+
+  // 鏋勫缓渚涘簲鍟嗕笟鍔″湴鍧�鏁扮粍
+  if (form.value.bprovinceId && form.value.bdistrictId && form.value.bcityId) {
+    form.value.bids = [row.bprovinceId, row.bcityId, row.bdistrictId];
+  }
+
+  // 鏋勫缓渚涘簲鍟嗚仈绯诲湴鍧�鏁扮粍
+  if (form.value.cprovinceId && form.value.cdistrictId && form.value.ccityId) {
+    form.value.cids = [row.cprovinceId, row.ccityId, row.cdistrictId];
+  }
+
+  // 鏋勫缓瀹㈡埛涓氬姟鍦板潃鏁扮粍
+  if (
+    form.value.businessCityId &&
+    form.value.businessDistrictId &&
+    form.value.businessProvinceId
+  ) {
+    form.value.bids = [
+      row.businessProvinceId,
+      row.businessCityId,
+      row.businessDistrictId,
+    ];
+  }
+
+  // 鏋勫缓瀹㈡埛鑱旂郴鍦板潃鏁扮粍
+  if (form.value.cityId && form.value.districtId && form.value.provinceId) {
+    form.value.cids = [row.provinceId, row.cityId, row.districtId];
+  }
+
+  addOrEdit.value = "edit";
+  handleAddEdit(tabName.value);
+};
+
+
+/**
+ * 鎵归噺鍒犻櫎澶勭悊
+ * @description 鎵归噺鍒犻櫎閫変腑鐨勮褰�
+ */
+  const deleteApiMap = {
+      management: delSupply,
+    };
+const {handleDeleteBatch :handleDelete} = useDelete({
+  deleteApi: () => deleteApiMap[tabName.value],
+  selectedRows: selectedRows,
+  getList: () => getList,
+  tableData: tableData,
+  total: total,
+  confirmText: "纭鍒犻櫎閫変腑鐨勬暟鎹悧锛�",
+  successText: "鍒犻櫎鎴愬姛",
+})
+
+/**
+ * 鍏抽棴寮圭獥澶勭悊
+ */
+const handleBeforeClose = () => {
+  dialogFormVisible.value = false;
+  form.value = {};
+};
+
+/**
+ * 瀵煎嚭鏁版嵁
+ * @param {string} api - 瀵煎嚭鎺ュ彛璺緞
+ * @param {string} name - 瀵煎嚭鏂囦欢鍚嶅墠缂�
+ */
+const exportData = (api, name) => {
+  proxy.download(
+    api,
+    { ...queryParams },
+    `${name}${new Date().getTime()}.xlsx`
+  );
+  ElMessage.success("瀵煎嚭鏁版嵁锛�" + name);
+};
+// ===== 鏁版嵁鑾峰彇鍑芥暟 =====
+
+/**
+ * 鏍规嵁褰撳墠鏍囩椤甸�夋嫨瀵瑰簲鐨凙PI鎺ュ彛
+ * @returns {Promise} API璋冪敤Promise
+ * @description 缁熶竴鐨勬帴鍙i�夋嫨鍑芥暟锛屾牴鎹爣绛鹃〉绫诲瀷璋冪敤瀵瑰簲鐨凙PI
+ */
+const selectInterface = () => {
+  const apiParams = {
+    current: pageNum.value,
+    pageSize: pageSizes.value,
+    searchAll: queryParams.searchAll,
+  };
+
+  const apiMap = {
+    management: () => getSupply(apiParams),
+    customer: () => getCustomerList(apiParams),
+    coal: () => getCoalInfo(apiParams),
+    coalQualityMaintenance: () => getCoalPlanList(apiParams),
+    coalMeiZhiZiDuanWeiHu: () => coalField(apiParams),
+  };
+
+  const apiFunction = apiMap[tabName.value];
+  return apiFunction
+    ? apiFunction()
+    : Promise.reject(new Error("鏈壘鍒板搴旂殑API鎺ュ彛"));
+};
+
+/**
+ * 鑾峰彇鍒楄〃鏁版嵁
+ * @description 缁熶竴鐨勬暟鎹幏鍙栧嚱鏁帮紝澶勭悊鍔犺浇鐘舵�佸拰閿欒澶勭悊
+ */
+const getList = async () => {
+  try {
+    loading.value = true;
+    const { data, code } = await selectInterface();
+
+    if (code !== 200) {
+      ElMessage.error("鑾峰彇鏁版嵁澶辫触锛�" + (data?.msg || "鏈煡閿欒"));
+      return;
+    }
+
+    tableData.value = data.records || [];
+    total.value = data.total || 0;
+  } catch (error) {
+    console.error("鑾峰彇鍒楄〃鏁版嵁澶辫触:", error);
+    ElMessage.error("鑾峰彇鏁版嵁澶辫触锛岃绋嶅悗鍐嶈瘯");
+  } finally {
+    loading.value = false;
+  }
+};
+
+const handleView = (row) => {
+  form.value = JSON.parse(JSON.stringify(row));
+  // 鏋勫缓渚涘簲鍟嗕笟鍔″湴鍧�鏁扮粍
+  if (form.value.bprovinceId && form.value.bdistrictId && form.value.bcityId) {
+    form.value.bids = [row.bprovinceId, row.bcityId, row.bdistrictId];
+  }
+
+  // 鏋勫缓渚涘簲鍟嗚仈绯诲湴鍧�鏁扮粍
+  if (form.value.cprovinceId && form.value.cdistrictId && form.value.ccityId) {
+    form.value.cids = [row.cprovinceId, row.ccityId, row.cdistrictId];
+  }
+
+  // 鏋勫缓瀹㈡埛涓氬姟鍦板潃鏁扮粍
+  if (
+    form.value.businessCityId &&
+    form.value.businessDistrictId &&
+    form.value.businessProvinceId
+  ) {
+    form.value.bids = [
+      row.businessProvinceId,
+      row.businessCityId,
+      row.businessDistrictId,
+    ];
+  }
+
+  // 鏋勫缓瀹㈡埛鑱旂郴鍦板潃鏁扮粍
+  if (form.value.cityId && form.value.districtId && form.value.provinceId) {
+    form.value.cids = [row.provinceId, row.cityId, row.districtId];
+  }
+  addOrEdit.value = "viewRow";
+  handleAddEdit(tabName.value);
+};
+
+
+// ===== 鐢熷懡鍛ㄦ湡閽╁瓙 =====
+
+/**
+ * 缁勪欢鎸傝浇鍚庣殑鍒濆鍖栨搷浣�
+ */
+onMounted(async () => {
+  try {
+    // 骞惰鎵ц鍒濆鍖栨搷浣�
+    await Promise.all([
+      handleTabClick({ props: { name: "management" } }),
+      getUserList(),
+    ]);
+  } catch (error) {
+    console.error("缁勪欢鍒濆鍖栧け璐�:", error);
+    ElMessage.error("椤甸潰鍒濆鍖栧け璐ワ紝璇峰埛鏂伴噸璇�");
+  }
+});
+</script>
+
+<style scoped>
+/* 鍝嶅簲寮忓竷灞� */
+@media screen and (min-width: 768px) {
+  .search-form :deep(.el-form-item) {
+    width: 50%;
+  }
+}
+
+@media screen and (min-width: 1200px) {
+  .search-form :deep(.el-form-item) {
+    width: 16%;
+  }
+}
+
+.table-toolbar {
+  margin-bottom: 20px;
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+/* 鍝嶅簲寮忚〃鏍� */
+@media screen and (max-width: 768px) {
+  .table-toolbar {
+    flex-direction: column;
+  }
+
+  .table-toolbar .el-button {
+    width: 100%;
+  }
+}
+
+/* 琛ㄦ牸宸ュ叿鏍� */
+.table-toolbar,
+.table-toolbar > * {
+  margin: 0 0 0 0 !important;
+}
+
+.table-toolbar {
+  margin-bottom: 20px !important;
+}
+
+.el-form--inline .el-form-item {
+  margin-right: 25px;
+}
+
+.main-container {
+  background: red !important;
+}
+</style>
diff --git a/src/views/equipment/management/mould/managementDialog.vue b/src/views/equipment/management/mould/managementDialog.vue
new file mode 100644
index 0000000..f245902
--- /dev/null
+++ b/src/views/equipment/management/mould/managementDialog.vue
@@ -0,0 +1,290 @@
+<template>
+  <div>
+    <el-dialog
+      v-model="dialogVisible"
+      :title="title"
+      width="600"
+      :close-on-click-modal="false"
+      :before-close="handleClose"
+    >
+      <el-form
+        ref="formRef"
+        style="max-width: 400px; margin: 0 auto"
+        :model="formData"
+        :rules="rules"
+        label-width="auto"
+      >        <el-form-item
+          label="渚涘簲鍟嗗悕绉�"
+          prop="supplierName"
+        >
+          <el-input
+            v-model="formData.supplierName"
+            placeholder="璇疯緭鍏ヤ緵璐у晢鍚嶇О"
+            :disabled="isViewMode"
+          />
+        </el-form-item>
+        <el-form-item
+          label="绾崇◣浜鸿瘑鍒彿"
+          prop="taxpayerId"
+        >
+          <el-input
+            v-model="formData.taxpayerId"
+            placeholder="璇疯緭鍏ョ撼绋庝汉璇嗗埆鍙�"
+            :disabled="isViewMode"
+          />
+        </el-form-item>
+        <el-form-item
+          label="缁忚惀鍦板潃"
+          prop="bids"
+        >
+          <el-cascader
+            placeholder="璇烽�夋嫨缁忚惀鍦板潃"
+            size="default"
+            :options="addressSelectOptions"
+            v-model="formData.bids"
+            :props="cascaderProps"
+            @change="handleChange"
+            :disabled="isViewMode"
+          >
+          </el-cascader>
+        </el-form-item>
+        <el-form-item
+          label="璇︾粏鍦板潃"
+          prop="businessAddress"
+        >
+          <el-input
+            v-model="formData.businessAddress"
+            placeholder="璇疯緭鍏ュ鎴疯缁嗗湴鍧�"
+            :disabled="isViewMode"
+          />
+        </el-form-item>
+        <el-form-item
+          label="寮�鎴疯"
+          prop="bankAccount"
+        >
+          <el-input 
+            v-model="formData.bankAccount" 
+            placeholder="璇疯緭鍏ュ紑鎴疯" 
+            :disabled="isViewMode"
+          />
+        </el-form-item>
+        <el-form-item
+          label="閾惰璐︽埛"
+          prop="bankName"
+        >
+          <el-input 
+            v-model="formData.bankName" 
+            placeholder="璇疯緭鍏ラ摱琛岃处鎴�" 
+            :disabled="isViewMode"
+          />
+        </el-form-item>
+        <el-form-item
+          label="鑱旂郴浜�"
+          prop="contactPerson"
+        >
+          <el-input
+            v-model="formData.contactPerson"
+            placeholder="璇疯緭鍏ヨ仈绯讳汉"
+            :disabled="isViewMode"
+          />
+        </el-form-item>
+        <el-form-item
+          label="鑱旂郴浜虹數璇�"
+          prop="contactPhone"
+        >
+          <el-input
+            v-model="formData.contactPhone"
+            placeholder="璇疯緭鍏ヨ仈绯讳汉鐢佃瘽"
+            :disabled="isViewMode"
+          />
+        </el-form-item>
+        <el-form-item
+          label="鑱旂郴浜哄湴鍧�"
+          prop="cids"
+        >
+          <el-cascader
+            placeholder="璇烽�夋嫨鑱旂郴浜哄湴鍧�"
+            size="default"
+            :options="addressSelectOptions"
+            v-model="formData.cids"
+            :props="cascaderProps"
+            @change="handleChange"
+            :disabled="isViewMode"
+          >
+          </el-cascader>
+        </el-form-item>
+        <el-form-item
+          label="鑱旂郴浜鸿缁嗗湴鍧�"
+          prop="contactAddress"
+        >
+          <el-input
+            v-model="formData.contactAddress"
+            placeholder="璇疯緭鍏ヨ仈绯讳汉鍦板潃"
+            :disabled="isViewMode"
+          />
+        </el-form-item>
+        <el-form-item class="dialog-footer">
+          <el-button v-if="addOrEdit === 'edit'" @click="resetForm"
+            >閲嶇疆</el-button
+          >
+          <el-button v-if="addOrEdit !== 'edit'||addOrEdit.includes('viewRow')" @click="cancelForm"
+            >鍙栨秷</el-button
+          >
+          <el-button type="primary" @click="submitForm" v-if="!isViewMode"> 纭畾</el-button>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch, defineProps, onMounted, computed, reactive } from "vue";
+import { addOrEditSupply } from "@/api/basicInformation/supplier";
+import { getAreaOptions } from "@/api/system/area.js";
+
+const props = defineProps({
+  beforeClose: {
+    type: Function,
+    default: () => {},
+  },
+  form: {
+    type: Object,
+    default: () => ({}),
+  },
+  addOrEdit: {
+    type: String,
+    default: "add",
+  },
+  title: {
+    type: String,
+    default: "",
+  },
+});
+
+// 璁$畻灞炴�э細缁熶竴鎺у埗鏄惁绂佺敤
+const isViewMode = computed(() => props.addOrEdit === 'viewRow');
+
+const emit = defineEmits(["submit", "handleBeforeClose"]);
+const copyForm = defineModel("copyForm", {
+  required: true,
+  type: Object,
+});
+onMounted(() => {
+  fetchAreaOptions();
+});
+
+// 淇敼鏍戝舰閫夋嫨鐨勬槧灏�
+const cascaderProps = ref({
+  value: "id", // 鎸囧畾value瀛楁涓篿d
+  label: "label", // 鎸囧畾label瀛楁
+  children: "children", // 鎸囧畾瀛愯妭鐐瑰瓧娈�
+});
+
+// 鍦板潃閫夋嫨鏁版嵁
+const addressSelectOptions = ref([]);
+const fetchAreaOptions = async () => {
+  addressSelectOptions.value = [];
+
+  const res = await getAreaOptions();
+  if (res.code === 200) {
+    addressSelectOptions.value = res.data;
+  }
+};
+
+// 澶勭悊鍦板潃鏁版嵁杞崲
+function mapAddress(list) {
+  return list.map((item) => ({
+    value: item.id,
+    label: item.name,
+    children: item.children ? mapAddress(item.children) : undefined,
+  }));
+}
+
+// 琛ㄥ崟寮曠敤
+const formRef = ref(null);
+// 琛ㄥ崟鏁版嵁
+const formData = ref({ ...props.form });
+// 寮圭獥鍙鎬�
+const dialogVisible = defineModel("supplierDialogFormVisible", {
+  required: true,
+  type: Boolean,
+});
+// 鐩戝惉澶栭儴浼犲叆鐨勮〃鍗曟暟鎹彉鍖�
+watch(
+  () => props.form,
+  (newVal) => {
+    formData.value = { ...newVal };
+  },
+  { deep: true }
+);
+
+// 鐩戝惉鍐呴儴寮圭獥鐘舵�佸彉鍖�
+watch(
+  () => dialogVisible.value,
+  (newVal) => {
+    emit("update:supplierDialogFormVisible", newVal);
+  }
+);
+// 澶勭悊鍦板潃閫夋嫨鍙樺寲
+const handleChange = (value) => {
+  console.log(value);
+};
+// 鎻愪氦琛ㄥ崟
+const submitForm = async () => {
+  if (!formRef.value) return;
+  await formRef.value.validate(async (valid, fields) => {
+    if (valid) {
+      const obj = ref({});
+      if (props.title.includes("鏂板")) {
+        let result = await addOrEditSupply({
+          ...formData.value,
+        });
+        obj.value = {
+          title: "鏂板",
+          ...formData.value,
+          result,
+        };
+      } else {
+        let result = await addOrEditSupply({
+          ...formData.value,
+        });
+        obj.value = {
+          title: "缂栬緫",
+          ...formData.value,
+          result,
+        };
+      }
+      emit("submit", obj.value);
+    }
+  });
+};
+// 鍙栨秷琛ㄥ崟
+const cancelForm = () => {
+  emit("update:supplierDialogFormVisible", false);
+  formData.value = {};
+};
+// 閲嶇疆琛ㄥ崟
+const resetForm = () => {
+  if (!formRef.value) return;
+  formData.value = JSON.parse(JSON.stringify(copyForm.value));
+};
+// 鍏抽棴寮圭獥
+const handleClose = () => {
+  // 瑙﹀彂鐖剁粍浠剁殑鍏抽棴鍑芥暟
+  emit("handleBeforeClose");
+  emit("update:supplierDialogFormVisible", false);
+};
+const rules = reactive({
+  supplierName: [
+    { required: true, message: "璇疯緭鍏ヤ緵璐у晢鍚嶇О", trigger: "blur" },
+  ],
+});
+</script>
+<style lang="scss" scoped>
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  margin-top: 20px;
+  flex-direction: column;
+}
+</style>

--
Gitblit v1.9.3