From deb5baea0b833c619cddc85f5409137fade95dca Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 06 一月 2026 10:52:46 +0800
Subject: [PATCH] 二级套餐包改造: 1.封装公用组件

---
 /dev/null                                       |   43 ----
 src/views/basicData/customerFile/index.vue      |   88 +++-----
 src/views/salesManagement/salesLedger/index.vue |   10 
 src/components/Dialog/FileListDialog.vue        |  164 ++++++++++++++++
 src/components/Dialog/FormDialog.vue            |   73 +++++++
 src/components/Dialog/ImportDialog.vue          |  172 +++++++++++++++++
 6 files changed, 451 insertions(+), 99 deletions(-)

diff --git a/src/components/Dialog/FileListDialog.vue b/src/components/Dialog/FileListDialog.vue
new file mode 100644
index 0000000..ea269d7
--- /dev/null
+++ b/src/components/Dialog/FileListDialog.vue
@@ -0,0 +1,164 @@
+<template>
+  <el-dialog 
+    v-model="dialogVisible" 
+    :title="title" 
+    :width="width" 
+    :before-close="handleClose"
+  >
+    <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>
+          <slot name="actions" :row="scope.row"></slot>
+        </template>
+      </el-table-column>
+      <slot name="columns"></slot>
+    </el-table>
+  </el-dialog>
+  <filePreview v-if="showPreview" ref="filePreviewRef" />
+</template>
+
+<script setup>
+import { ref, computed, getCurrentInstance } from 'vue'
+import filePreview from '@/components/filePreview/index.vue'
+
+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: 100
+  },
+  showActions: {
+    type: Boolean,
+    default: true
+  },
+  showDownload: {
+    type: Boolean,
+    default: true
+  },
+  showPreview: {
+    type: Boolean,
+    default: true
+  },
+  urlField: {
+    type: String,
+    default: 'url'
+  },
+  downloadMethod: {
+    type: Function,
+    default: null
+  },
+  previewMethod: {
+    type: Function,
+    default: null
+  }
+})
+
+const emit = defineEmits(['update:modelValue', 'close', 'download', 'preview'])
+
+const { proxy } = getCurrentInstance()
+const filePreviewRef = ref(null)
+
+const dialogVisible = computed({
+  get: () => props.modelValue,
+  set: (val) => emit('update:modelValue', val)
+})
+
+const tableData = ref([])
+
+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 open = (list) => {
+  dialogVisible.value = true
+  tableData.value = list || []
+}
+
+defineExpose({
+  open
+})
+</script>
+
+<style scoped>
+</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/views/basicData/customerFile/index.vue b/src/views/basicData/customerFile/index.vue
index 0f39e25..1552587 100644
--- a/src/views/basicData/customerFile/index.vue
+++ b/src/views/basicData/customerFile/index.vue
@@ -47,11 +47,14 @@
         @pagination="pagination"
       ></PIMTable>
     </div>
-    <el-dialog
+    <FormDialog
       v-model="dialogFormVisible"
-      :title="operationType === 'add' ? '鏂板瀹㈡埛淇℃伅' : '缂栬緫瀹㈡埛淇℃伅'"
+      :title="(type) => type === 'add' ? '鏂板瀹㈡埛淇℃伅' : '缂栬緫瀹㈡埛淇℃伅'"
+      :operation-type="operationType"
       width="70%"
       @close="closeDia"
+      @confirm="submitForm"
+      @cancel="closeDia"
     >
       <el-form
         :model="form"
@@ -193,63 +196,31 @@
           </el-col>
         </el-row>
       </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button type="primary" @click="submitForm">纭</el-button>
-          <el-button @click="closeDia">鍙栨秷</el-button>
-        </div>
-      </template>
-    </el-dialog>
+    </FormDialog>
     <!-- 鐢ㄦ埛瀵煎叆瀵硅瘽妗� -->
-    <el-dialog
-      :title="upload.title"
+    <ImportDialog
+      ref="importDialogRef"
       v-model="upload.open"
+      :title="upload.title"
       width="400px"
-      append-to-body
-    >
-      <el-upload
-        ref="uploadRef"
-        :limit="1"
-        accept=".xlsx, .xls"
-        :headers="upload.headers"
-        :action="upload.url + '?updateSupport=' + upload.updateSupport"
-        :disabled="upload.isUploading"
-        :before-upload="upload.beforeUpload"
-        :on-progress="upload.onProgress"
-        :on-success="upload.onSuccess"
-        :on-error="upload.onError"
-        :on-change="upload.onChange"
-        :auto-upload="false"
-        drag
-      >
-        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
-        <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
-        <template #tip>
-          <div class="el-upload__tip text-center">
-            <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
-            <el-link
-              type="primary"
-              :underline="false"
-              style="font-size: 12px; vertical-align: baseline"
-              @click="importTemplate"
-              >涓嬭浇妯℃澘</el-link
-            >
-          </div>
-        </template>
-      </el-upload>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
-          <el-button @click="upload.open = false">鍙� 娑�</el-button>
-        </div>
-      </template>
-    </el-dialog>
+      :action="upload.url"
+      :headers="upload.headers"
+      :disabled="upload.isUploading"
+      :before-upload="upload.beforeUpload"
+      :on-progress="upload.onProgress"
+      :on-success="upload.onSuccess"
+      :on-error="upload.onError"
+      :on-change="upload.onChange"
+      @confirm="submitFileForm"
+      @cancel="handleImportCancel"
+      @download-template="importTemplate"
+    />
   </div>
 </template>
 
 <script setup>
 import {onMounted, ref} from "vue";
-import { Search } from "@element-plus/icons-vue";
+import { Search, Close } from "@element-plus/icons-vue";
 import {
   addCustomer,
   delCustomer,
@@ -261,6 +232,8 @@
 import { userListNoPage } from "@/api/system/user.js";
 import useUserStore from "@/store/modules/user";
 import { getToken } from "@/utils/auth.js";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import ImportDialog from "@/components/Dialog/ImportDialog.vue";
 const { proxy } = getCurrentInstance();
 const userStore = useUserStore();
 
@@ -338,6 +311,7 @@
 const selectedRows = ref([]);
 const userList = ref([]);
 const tableLoading = ref(false);
+const importDialogRef = ref(null);
 const page = reactive({
   current: 1,
   size: 100,
@@ -426,7 +400,9 @@
     if(response.code === 200){
       proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
       upload.open = false;
-      proxy.$refs["uploadRef"].clearFiles();
+      if (importDialogRef.value) {
+        importDialogRef.value.clearFiles();
+      }
       getList();
     }else if(response.code === 500){
       proxy.$modal.msgError(response.msg);
@@ -484,7 +460,13 @@
 /** 鎻愪氦涓婁紶鏂囦欢 */
 function submitFileForm() {
   upload.isUploading = true;
-  proxy.$refs["uploadRef"].submit();
+  if (importDialogRef.value) {
+    importDialogRef.value.submit();
+  }
+}
+/** 鍙栨秷瀵煎叆 */
+function handleImportCancel() {
+  upload.open = false;
 }
 /** 瀵煎叆鎸夐挳鎿嶄綔 */
 function handleImport() {
diff --git a/src/views/salesManagement/salesLedger/fileList.vue b/src/views/salesManagement/salesLedger/fileList.vue
deleted file mode 100644
index da37db2..0000000
--- a/src/views/salesManagement/salesLedger/fileList.vue
+++ /dev/null
@@ -1,43 +0,0 @@
-<template>
-  <el-dialog v-model="dialogVisible" title="闄勪欢" width="40%" :before-close="handleClose">
-    <el-table :data="tableData" border height="40vh">
-      <el-table-column label="闄勪欢鍚嶇О" prop="name" min-width="400" show-overflow-tooltip />
-      <el-table-column fixed="right" label="鎿嶄綔" width="100" align="center">
-        <template #default="scope">
-          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">涓嬭浇</el-button>
-          <el-button link type="primary" size="small" @click="lookFile(scope.row)">棰勮</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-  </el-dialog>
-  <filePreview ref="filePreviewRef" />
-</template>
-
-<script setup>
-import { ref } from 'vue'
-import filePreview from '@/components/filePreview/index.vue'
-
-const dialogVisible = ref(false)
-const tableData = ref([])
-const { proxy } = getCurrentInstance();
-const filePreviewRef = ref()
-const handleClose = () => {
-  dialogVisible.value = false
-}
-const open = (list) => {
-  dialogVisible.value = true
-  tableData.value = list
-}
-const downLoadFile = (row) => {
-  proxy.$download.name(row.url);
-
-}
-const lookFile = (row) => {
-  filePreviewRef.value.open(row.url)
-}
-defineExpose({
-  open
-})
-</script>
-
-<style></style>
\ No newline at end of file
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index 94af430..efc365e 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -450,7 +450,7 @@
 				</div>
 			</template>
 		</el-dialog>
-    <FileList ref="fileListRef" />
+    <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" />
   </div>
 </template>
 
@@ -462,7 +462,7 @@
 import {ElMessage, ElMessageBox} from "element-plus";
 import useUserStore from "@/store/modules/user";
 import { userListNoPage } from "@/api/system/user.js";
-import FileList from "./fileList.vue";
+import FileListDialog from '@/components/Dialog/FileListDialog.vue';
 import {
   ledgerListPage,
   productList,
@@ -1516,9 +1516,13 @@
  * @param row 涓嬭浇鏂囦欢鐨勭浉鍏充俊鎭璞�
  */
 const fileListRef = ref(null)
+const fileListDialogVisible = ref(false)
 const downLoadFile = (row) => {
   getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
-    fileListRef.value.open(res.salesLedgerFiles)
+    if (fileListRef.value) {
+      fileListRef.value.open(res.salesLedgerFiles)
+      fileListDialogVisible.value = true
+    }
   });
 }
 

--
Gitblit v1.9.3