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