From 9ff9962d8d2d4cb1cd5d6abcb902e916c44cda03 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 08 五月 2025 14:28:29 +0800
Subject: [PATCH] 客户档案页面开发

---
 src/assets/styles/ruoyi.scss               |    3 
 src/views/tool/build/index.vue             |    2 
 src/api/system/user.js                     |    7 
 src/views/basicData/customerFile/index.vue |  282 +++++++++++++++++++++++++++++++++-
 src/views/login.vue                        |    2 
 src/main.js                                |    1 
 src/api/basicData/customerFile.js          |   51 ++++++
 src/assets/styles/element-ui.scss          |   50 ++++++
 src/components/PIMTable/PIMTable.vue       |   66 +------
 src/components/PIMTable/Pagination.vue     |    4 
 src/store/modules/settings.js              |    2 
 11 files changed, 401 insertions(+), 69 deletions(-)

diff --git a/src/api/basicData/customerFile.js b/src/api/basicData/customerFile.js
new file mode 100644
index 0000000..4386a80
--- /dev/null
+++ b/src/api/basicData/customerFile.js
@@ -0,0 +1,51 @@
+// 瀹㈡埛妗f椤甸潰鎺ュ彛
+import request from '@/utils/request'
+
+// 鍒嗛〉鏌ヨ
+export function listCustomer(query) {
+    return request({
+        url: '/basic/customer/list',
+        method: 'get',
+        params: query
+    })
+}
+// 鏌ヨ瀹㈡埛妗f璇︾粏
+export function getCustomer(id) {
+    return request({
+        url: '/basic/customer/' + id,
+        method: 'get'
+    })
+}
+// 鏂板瀹㈡埛妗f
+export function addCustomer(data) {
+    return request({
+        url: '/basic/customer/addCustomer',
+        method: 'post',
+        data: data
+    })
+}
+// 淇敼瀹㈡埛妗f
+export function updateCustomer(data) {
+    return request({
+        url: '/basic/customer/updateCustomer',
+        method: 'post',
+        data: data
+    })
+}
+// 瀵煎嚭瀹㈡埛妗f
+export function exportCustomer(query) {
+    return request({
+        url: '/basic/customer/export',
+        method: 'get',
+        params: query,
+        responseType: 'blob'
+    })
+}
+// 鍒犻櫎瀹㈡埛妗f
+export function delCustomer(id) {
+    return request({
+        url: '/basic/customer/' + id,
+        method: 'delete'
+    })
+}
+
diff --git a/src/api/system/user.js b/src/api/system/user.js
index 2da13d4..3a45768 100644
--- a/src/api/system/user.js
+++ b/src/api/system/user.js
@@ -134,3 +134,10 @@
     method: 'get'
   })
 }
+// 鏌ヨ鐢ㄦ埛鍒楄〃
+export function userListNoPage() {
+  return request({
+    url: '/system/user/userListNoPage',
+    method: 'get'
+  })
+}
diff --git a/src/assets/styles/element-ui.scss b/src/assets/styles/element-ui.scss
index bb5414d..82712af 100644
--- a/src/assets/styles/element-ui.scss
+++ b/src/assets/styles/element-ui.scss
@@ -52,6 +52,56 @@
   left: 0;
   position: relative;
   margin: 0 auto;
+  border-radius: 8px;
+  padding: 0 !important;
+}
+.el-dialog__header {
+  background: #F5F6F7;
+  padding: 12px 16px;
+  border-radius: 8px 8px 0 0;
+}
+.el-dialog__title {
+  font-weight: 400;
+  font-size: 16px;
+  color: #2E3033;
+}
+.el-dialog__body {
+  padding: 16px 40px 0 40px;
+}
+.el-dialog__footer {
+  text-align: center;
+  padding: 16px;
+}
+.el-message-box {
+  padding: 0 !important;
+  border-radius: 8px;
+}
+.el-message-box__header {
+  background: #F5F6F7;
+  padding: 12px 16px;
+  border-radius: 8px 8px 0 0;
+}
+.el-message-box__title {
+  font-weight: 400;
+  font-size: 16px;
+  color: #2E3033;
+}
+.el-message-box__content {
+  padding: 16px 40px 0 40px;
+}
+.el-message-box__container {
+  justify-content: center;
+}
+.el-message-box__btns {
+  text-align: center;
+  padding: 16px;
+  display: flex;
+  flex-direction: row-reverse;
+  justify-content: center;
+  align-items: center;
+  .el-button--primary {
+    margin-right: 12px;
+  }
 }
 
 // refine element ui upload
diff --git a/src/assets/styles/ruoyi.scss b/src/assets/styles/ruoyi.scss
index 4ef1c78..c1549f5 100644
--- a/src/assets/styles/ruoyi.scss
+++ b/src/assets/styles/ruoyi.scss
@@ -71,7 +71,6 @@
   overflow: auto;
   overflow-x: hidden;
   max-height: 70vh;
-  padding: 10px 20px 0;
 }
 
 .el-table {
@@ -151,7 +150,7 @@
 /** 琛ㄦ牸鏇村鎿嶄綔涓嬫媺鏍峰紡 */
 .el-table .el-dropdown-link {
   cursor: pointer;
-  color: #3472D7;
+  color: #2C51D9;
   margin-left: 10px;
 }
 
diff --git a/src/components/PIMTable/PIMTable.vue b/src/components/PIMTable/PIMTable.vue
index 747ef53..c76cf45 100644
--- a/src/components/PIMTable/PIMTable.vue
+++ b/src/components/PIMTable/PIMTable.vue
@@ -1,11 +1,11 @@
 <template>
   <el-table ref="multipleTable" v-loading="tableLoading" :border="border" :data="tableData"
-            :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" :height="height"
+            :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" height="calc(100vh - 18.5em)"
             :highlight-current-row="highlightCurrentRow" :row-class-name="rowClassName" :row-style="rowStyle"
-            :row-key="rowKey" :span-method="spanMethod" stripe style="width: 100%" tooltip-effect="dark" @row-click="rowClick"
+            :row-key="rowKey" stripe style="width: 100%" tooltip-effect="dark" @row-click="rowClick"
             @current-change="currentChange" @selection-change="handleSelectionChange" 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" :index="indexMethod" />
+    <el-table-column align="center" type="selection" width="55" />
+    <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"
@@ -51,12 +51,11 @@
         </div>
         
         <!-- 鎸夐挳 -->
-        <div v-else-if="item.dataType == 'action'"
-             :style="`min-width:${getWidth(item.operation, scope.row)}`">
+        <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"
-                       :style="{ color: (o.name === '鍒犻櫎' || o.name === 'delete') ? '#f56c6c' : o.color }" :type="typeFn(o.type, scope.row)"
+                       :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>
@@ -71,7 +70,7 @@
                        :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'" type="text"
+              <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>
@@ -89,7 +88,7 @@
       </template>
     </el-table-column>
   </el-table>
-  <pagination v-show="page.total > 0" :total="page.total" :layout="page.layout" :page="page.current"
+  <pagination v-show="total > 0" :total="total" :layout="page.layout" :page="page.current"
               :limit="page.size" @pagination="paginationSearch" />
 </template>
 
@@ -103,6 +102,8 @@
 const uploadHeader = proxy.uploadHeader
 const javaApi = proxy.javaApi
 
+const emit = defineEmits(["pagination"]);
+
 // Filters
 const typeFn = (val, row) => {
   return typeof val === 'function' ? val(row) : val
@@ -114,14 +115,6 @@
 
 // Props锛堜娇鐢� defineProps 鐨勯潪 TS 褰㈠紡锛�
 const props = defineProps({
-  isSelection: {
-    type: Boolean,
-    default: false
-  },
-  height: {
-    type: [String, null],
-    default: null
-  },
   tableLoading: {
     type: Boolean,
     default: false
@@ -178,50 +171,21 @@
       size: 10,
       layout: 'total, sizes, prev, pager, next, jumper'
     })
+  },
+  total: {
+    type: Number,
+    default: 0
   }
 })
 
 // Data
-const spanList = ref([])
 const btnWidth = ref('120px')
 const uploadRefs = ref([])
 const currentFiles = ref({})
 const uploadKeys = ref({})
 
-// 鍚堝苟鍗曞厓鏍兼柟娉�
-const spanMethod = ({ row, column, rowIndex, columnIndex }) => {
-  if (column.find((m) => m.mergeCol)) {
-    let i = Number(rowIndex)
-    const obj = spanList.value.find((item, index) => {
-      i = index
-      return item.index == columnIndex
-    })
-    if (obj) {
-      const _row = spanList[i].arr[rowIndex]
-      const _col = _row > 0 ? 1 : 0
-      return {
-        rowspan: _row,
-        colspan: _col
-      }
-    }
-  }
-}
-
 const indexMethod = (index) => {
   return (props.page.current - 1) * props.page.size + index + 1
-}
-
-const getWidth = (row, row0) => {
-  let count = 0
-  row.forEach((a) => {
-    if (a.showHide !== undefined && a.showHide(row0)) {
-      count += a.name.length
-    } else if (!a.showHide) {
-      count += a.name.length
-    }
-  })
-  btnWidth.value = count * 15 + 60 + "px"
-  return count * 15 + 60 + "px"
 }
 
 // 鐐瑰嚮 link 浜嬩欢
diff --git a/src/components/PIMTable/Pagination.vue b/src/components/PIMTable/Pagination.vue
index 26d4dbc..7639e64 100644
--- a/src/components/PIMTable/Pagination.vue
+++ b/src/components/PIMTable/Pagination.vue
@@ -91,8 +91,8 @@
 <style scoped>
 .pagination-container {
   background: #fff;
-  padding: 28px 16px;
-  margin-top: 10px;
+  padding: 16px 0;
+  margin-top: 0;
 }
 .pagination-container.hidden {
   display: none;
diff --git a/src/main.js b/src/main.js
index 8112604..93b940f 100644
--- a/src/main.js
+++ b/src/main.js
@@ -90,5 +90,6 @@
   // 鏀寔 large銆乨efault銆乻mall
   size: Cookies.get('size') || 'default'
 })
+app._context.components.ElDialog.props.closeOnClickModal.default = false
 
 app.mount('#app')
diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js
index 2e9c1d1..76a1136 100644
--- a/src/store/modules/settings.js
+++ b/src/store/modules/settings.js
@@ -14,7 +14,7 @@
   {
     state: () => ({
       title: '',
-      theme: storageSetting.theme || '#3472D7',
+      theme: storageSetting.theme || '#2C51D9',
       sideTheme: storageSetting.sideTheme || sideTheme,
       showSettings: showSettings,
       topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
diff --git a/src/views/basicData/customerFile/index.vue b/src/views/basicData/customerFile/index.vue
index b4d6199..1a2b476 100644
--- a/src/views/basicData/customerFile/index.vue
+++ b/src/views/basicData/customerFile/index.vue
@@ -4,38 +4,298 @@
     <div>
       <span class="search_title">瀹㈡埛鍚嶇О锛�</span>
       <el-input
-          v-model="input2"
+          v-model="searchForm.customerName"
           style="width: 240px"
           placeholder="璇疯緭鍏�"
+          @change="handleQuery"
+          clearable
           :prefix-icon="Search"
       />
+      <el-button type="primary" @click="handleQuery" style="margin-left: 10px">鎼滅储</el-button>
     </div>
     <div>
-      <el-button type="primary">鏂板瀹㈡埛</el-button>
-      <el-button>瀵煎嚭</el-button>
-      <el-button type="danger" plain>鍒犻櫎</el-button>
+      <el-button type="primary" @click="openForm('add')">鏂板瀹㈡埛</el-button>
+      <el-button @click="handleOut">瀵煎嚭</el-button>
+      <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
     </div>
   </div>
   <div class="table_list">
-    <PIMTable :column="tableColumn"></PIMTable>
+    <PIMTable :column="tableColumn" :tableData="tableData" :page="page" :handleSelectionChange="handleSelectionChange"
+              :tableLoading="tableLoading" @pagination="pagination" :total="total"></PIMTable>
   </div>
+  <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '鏂板瀹㈡埛淇℃伅' : '缂栬緫瀹㈡埛淇℃伅'" width="70%" @close="closeDia">
+    <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
+      <el-row :gutter="30">
+        <el-col :span="12">
+          <el-form-item label="瀹㈡埛鍚嶇О锛�" prop="customerName">
+            <el-input v-model="form.customerName" placeholder="璇疯緭鍏�" clearable/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="绾崇◣浜鸿瘑鍒彿锛�" prop="taxpayerIdentificationNumber">
+            <el-input v-model="form.taxpayerIdentificationNumber" placeholder="璇疯緭鍏�" clearable/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="30">
+        <el-col :span="12">
+          <el-form-item label="鍏徃鍦板潃锛�" prop="companyAddress">
+            <el-input v-model="form.companyAddress" placeholder="璇疯緭鍏�" clearable/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鍏徃鐢佃瘽锛�" prop="companyPhone">
+            <el-input v-model="form.companyPhone" placeholder="璇疯緭鍏�" clearable/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="30">
+        <el-col :span="12">
+          <el-form-item label="鑱旂郴浜猴細" prop="contactPerson">
+            <el-input v-model="form.contactPerson" placeholder="璇疯緭鍏�" clearable/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鑱旂郴鐢佃瘽锛�" prop="contactPhone">
+            <el-input v-model="form.contactPhone" placeholder="璇疯緭鍏�" clearable/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="30">
+        <el-col :span="12">
+          <el-form-item label="缁存姢浜猴細" prop="maintainer">
+            <el-select v-model="form.maintainer" placeholder="璇烽�夋嫨" clearable>
+              <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="缁存姢鏃堕棿锛�" prop="maintenanceTime">
+            <el-date-picker
+                style="width: 100%"
+                v-model="form.maintenanceTime"
+                value-format="YYYY-MM-DD"
+                format="YYYY-MM-DD"
+                type="date"
+                placeholder="璇烽�夋嫨"
+                clearable
+            />
+          </el-form-item>
+        </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>
 </div>
 </template>
 
 <script setup>
 import { ref } from 'vue'
 import {Search} from "@element-plus/icons-vue";
+import {addCustomer, delCustomer, getCustomer, listCustomer, updateCustomer} from "@/api/basicData/customerFile.js";
+import {ElMessageBox } from "element-plus";
+import {userListNoPage} from "@/api/system/user.js";
+const { proxy } = getCurrentInstance()
 
-const input2 = ref('')
 const tableColumn = ref([
   {
-    label: '鎵瑰噯鍐呭',
-    prop: 'ratifyRemark'
-  }, {
-    label: '鎵瑰噯浜�',
-    prop: 'ratifyName',
+    label: '瀹㈡埛鍚嶇О',
+    prop: 'customerName'
+  },
+  {
+    label: '绾崇◣浜鸿瘑鍒爜',
+    prop: 'taxpayerIdentificationNumber'
+  },
+  {
+    label: '鍦板潃鍙婅仈绯绘柟寮�',
+    prop: 'addressPhone'
+  },
+  {
+    label: '鑱旂郴浜�',
+    prop: 'contactPerson'
+  },
+  {
+    label: '鑱旂郴鐢佃瘽',
+    prop: 'contactPhone',
+  },
+  {
+    label: '缁存姢浜�',
+    prop: 'maintainer',
+  },
+  {
+    label: '缁存姢鏃堕棿',
+    prop: 'maintenanceTime',
+  },
+  {
+    dataType: "action",
+    label: "鎿嶄綔",
+    operation: [
+      {
+        name: "缂栬緫",
+        type: "text",
+        clickFun: (row) => {
+          openForm('edit', row);
+        },
+      },
+    ],
   },
 ])
+const tableData = ref([])
+const selectedRows = ref([])
+const userList = ref([])
+const tableLoading = ref(false)
+const page = reactive({
+  current: 1,
+  size: 10,
+})
+const total = ref(0)
+
+// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
+const operationType = ref('')
+const dialogFormVisible = ref(false)
+const data = reactive({
+  searchForm: {
+    customerName: '',
+  },
+  form: {
+    customerName: '',
+    taxpayerIdentificationNumber: '',
+    companyAddress: '',
+    companyPhone: '',
+    contactPerson: '',
+    contactPhone: '',
+    maintainer: '',
+    maintenanceTime: '',
+  },
+  rules: {
+    customerName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    taxpayerIdentificationNumber: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    companyAddress: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    companyPhone: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    contactPerson: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    contactPhone: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    maintainer: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
+    maintenanceTime: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
+  }
+})
+const { searchForm, form, rules } = toRefs(data)
+
+// 鏌ヨ鍒楄〃
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  page.current = 1
+  getList()
+}
+const pagination = ({ current, limit }) => {
+  page.current = current;
+  page.size = limit;
+  getList()
+}
+const getList = () => {
+  tableLoading.value = true
+  listCustomer({...searchForm.value, ...page}).then(res => {
+    tableLoading.value = false
+    tableData.value = res.rows
+    total.value = res.total
+  })
+}
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection
+}
+// 鎵撳紑寮规
+const openForm = (type, row) => {
+  operationType.value = type
+  form.value = {}
+  userListNoPage().then(res => {
+    userList.value = res.data
+  })
+  if (type === 'edit') {
+    getCustomer(row.id).then(res => {
+      form.value = {...res.data}
+    })
+  }
+  dialogFormVisible.value = true
+}
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+  proxy.$refs["formRef"].validate(valid => {
+    if (valid) {
+      if (operationType.value === "edit") {
+        submitEdit()
+      } else {
+        submitAdd()
+      }
+    }
+  })
+}
+// 鎻愪氦鏂板
+const submitAdd = () => {
+  addCustomer(form.value).then(res => {
+    proxy.$modal.msgSuccess("鎻愪氦鎴愬姛")
+    closeDia()
+    getList()
+  })
+}
+// 鎻愪氦淇敼
+const submitEdit = () => {
+  updateCustomer(form.value).then(res => {
+    proxy.$modal.msgSuccess("鎻愪氦鎴愬姛")
+    closeDia()
+    getList()
+  })
+}
+// 鍏抽棴寮规
+const closeDia = () => {
+  proxy.resetForm("formRef")
+  dialogFormVisible.value = false
+}
+// 瀵煎嚭
+const handleOut = () => {
+  ElMessageBox.confirm(
+      '閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�',
+      '瀵煎嚭', {
+        confirmButtonText: '纭',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning',
+      }
+  ).then(() => {
+    proxy.download("/basic/customer/export", {}, '瀹㈡埛妗f.xlsx')
+  }).catch(() => {
+    proxy.$modal.msg("宸插彇娑�")
+  })
+}
+// 鍒犻櫎
+const handleDelete = () => {
+  let ids = []
+  if (selectedRows.value.length > 0) {
+    ids = selectedRows.value.map(item => item.id);
+  } else {
+    proxy.$modal.msgWarning('璇烽�夋嫨鏁版嵁')
+    return
+  }
+  ElMessageBox.confirm(
+      '閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�',
+      '瀵煎嚭', {
+        confirmButtonText: '纭',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning',
+      }
+  ).then(() => {
+    delCustomer(ids).then(res => {
+      proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛")
+    })
+    getList()
+  }).catch(() => {
+    proxy.$modal.msg("宸插彇娑�")
+  })
+}
+getList()
 </script>
 
 <style scoped lang="scss">
diff --git a/src/views/login.vue b/src/views/login.vue
index 88fb248..29d691b 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -175,7 +175,7 @@
 .title {
   margin: 20px auto 14px auto;
   text-align: center;
-  color: #3472D7;
+  color: #2C51D9;
   font-size: 28px;
   font-weight: 700;
 }
diff --git a/src/views/tool/build/index.vue b/src/views/tool/build/index.vue
index e4c319a..0895955 100644
--- a/src/views/tool/build/index.vue
+++ b/src/views/tool/build/index.vue
@@ -307,7 +307,7 @@
 </script>
 
 <style lang='scss'>
-$lighterBlue: #3472D7;
+$lighterBlue: #2C51D9;
 
 .container {
   position: relative;

--
Gitblit v1.9.3