From 2307a3125c23ad79ee92306603a418085cee194d Mon Sep 17 00:00:00 2001
From: ZN <zhang_12370@163.com>
Date: 星期四, 05 三月 2026 15:50:02 +0800
Subject: [PATCH] feat: 更新售后管理模块,新增产品选择弹窗和统计卡片
---
src/views/customerService/feedbackRegistration/components/formDia.vue | 452 +++++++++++--
src/views/customerService/feedbackRegistration/index.vue | 640 ++++++++++++++-----
src/api/customerService/index.js | 32
src/views/customerService/afterSalesHandling/index.vue | 293 ++++++--
src/views/customerService/feedbackRegistration/components/ProductSelectDialog.vue | 245 +++++++
pnpm-lock.yaml | 274 ++++++++
6 files changed, 1,582 insertions(+), 354 deletions(-)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 871e5c2..2b3e76c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,12 +11,21 @@
'@element-plus/icons-vue':
specifier: 2.3.1
version: 2.3.1(vue@3.4.31)
+ '@vue-office/docx':
+ specifier: ^1.6.3
+ version: 1.6.3(vue-demi@0.14.10(vue@3.4.31))(vue@3.4.31)
+ '@vue-office/excel':
+ specifier: ^1.7.14
+ version: 1.7.14(vue-demi@0.14.10(vue@3.4.31))(vue@3.4.31)
'@vueup/vue-quill':
specifier: 1.2.0
version: 1.2.0(vue@3.4.31)
'@vueuse/core':
specifier: 10.11.0
version: 10.11.0(vue@3.4.31)
+ autofit.js:
+ specifier: ^3.2.8
+ version: 3.2.8
axios:
specifier: 0.28.1
version: 0.28.1
@@ -53,6 +62,12 @@
pinia:
specifier: 2.1.7
version: 2.1.7(vue@3.4.31)
+ print-js:
+ specifier: ^1.6.0
+ version: 1.6.0
+ qrcode:
+ specifier: ^1.5.4
+ version: 1.5.4
sortablejs:
specifier: ^1.15.6
version: 1.15.6
@@ -65,6 +80,12 @@
vue-cropper:
specifier: 1.1.1
version: 1.1.1
+ vue-easy-lightbox:
+ specifier: ^1.19.0
+ version: 1.19.0(vue@3.4.31)
+ vue-esign:
+ specifier: ^1.1.4
+ version: 1.1.4
vue-router:
specifier: 4.4.0
version: 4.4.0(vue@3.4.31)
@@ -341,56 +362,67 @@
resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.40.2':
resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==}
cpu: [arm]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.40.2':
resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.40.2':
resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-loongarch64-gnu@4.40.2':
resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==}
cpu: [loong64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-powerpc64le-gnu@4.40.2':
resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.40.2':
resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.40.2':
resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==}
cpu: [riscv64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.40.2':
resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.40.2':
resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.40.2':
resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-win32-arm64-msvc@4.40.2':
resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==}
@@ -442,6 +474,26 @@
vite: ^5.0.0
vue: ^3.2.25
+ '@vue-office/docx@1.6.3':
+ resolution: {integrity: sha512-Cs+3CAaRBOWOiW4XAhTwwxJ0dy8cPIf6DqfNvYcD3YACiLwO4kuawLF2IAXxyijhbuOeoFsfvoVbOc16A/4bZA==}
+ peerDependencies:
+ '@vue/composition-api': ^1.7.1
+ vue: ^2.0.0 || >=3.0.0
+ vue-demi: ^0.14.6
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
+ '@vue-office/excel@1.7.14':
+ resolution: {integrity: sha512-pVUgt+emDQUnW7q22CfnQ+jl43mM/7IFwYzOg7lwOwPEbiVB4K4qEQf+y/bc4xGXz75w1/e3Kz3G6wAafmFBFg==}
+ peerDependencies:
+ '@vue/composition-api': ^1.7.1
+ vue: ^2.0.0 || >=3.0.0
+ vue-demi: ^0.14.6
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
'@vue/compiler-core@3.4.31':
resolution: {integrity: sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==}
@@ -453,6 +505,9 @@
'@vue/compiler-dom@3.5.14':
resolution: {integrity: sha512-1aOCSqxGOea5I80U2hQJvXYpPm/aXo95xL/m/mMhgyPUsKe9jhjwWpziNAw7tYRnbz1I61rd9Mld4W9KmmRoug==}
+
+ '@vue/compiler-sfc@2.7.16':
+ resolution: {integrity: sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==}
'@vue/compiler-sfc@3.4.31':
resolution: {integrity: sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==}
@@ -592,6 +647,9 @@
engines: {node: '>= 4.5.0'}
hasBin: true
+ autofit.js@3.2.8:
+ resolution: {integrity: sha512-albZNwDIXvcRneEDyZLW3uAIOH0cUQG/TnCGQ7jpfnL0gPn/+1ZNVRuEz3ZuzZvVkQ4HQRplGHjUeMRtPNxjLQ==}
+
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
@@ -646,6 +704,10 @@
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'}
+ camelcase@5.3.1:
+ resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+ engines: {node: '>=6'}
+
chalk@1.1.3:
resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==}
engines: {node: '>=0.10.0'}
@@ -664,6 +726,9 @@
clipboard@2.0.11:
resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==}
+
+ cliui@6.0.0:
+ resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
clone@2.1.2:
resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==}
@@ -766,6 +831,10 @@
supports-color:
optional: true
+ decamelize@1.2.0:
+ resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
+ engines: {node: '>=0.10.0'}
+
decode-uri-component@0.2.2:
resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
engines: {node: '>=0.10'}
@@ -800,6 +869,9 @@
delegate@3.2.0:
resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==}
+
+ dijkstrajs@1.0.3:
+ resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
dom-serializer@0.2.2:
resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==}
@@ -965,6 +1037,10 @@
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
+ find-up@4.1.0:
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+ engines: {node: '>=8'}
+
follow-redirects@1.15.9:
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
engines: {node: '>=4.0'}
@@ -1016,6 +1092,10 @@
fuse.js@6.6.2:
resolution: {integrity: sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==}
engines: {node: '>=10'}
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
@@ -1351,6 +1431,10 @@
resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
engines: {node: '>=14'}
+ locate-path@5.0.0:
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+ engines: {node: '>=8'}
+
lodash-es@4.17.21:
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
@@ -1514,6 +1598,18 @@
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
engines: {node: '>= 0.4'}
+ p-limit@2.3.0:
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+ engines: {node: '>=6'}
+
+ p-locate@4.1.0:
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+ engines: {node: '>=8'}
+
+ p-try@2.2.0:
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+ engines: {node: '>=6'}
+
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
@@ -1523,6 +1619,10 @@
pascalcase@0.1.1:
resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==}
engines: {node: '>=0.10.0'}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
@@ -1567,6 +1667,10 @@
pkg-types@2.1.0:
resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==}
+ pngjs@5.0.0:
+ resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
+ engines: {node: '>=10.13.0'}
+
posix-character-classes@0.1.1:
resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==}
engines: {node: '>=0.10.0'}
@@ -1605,11 +1709,24 @@
resolution: {integrity: sha512-spBB5sgC4cv2YcW03f/IAUN1pgDJWNWD8FzkyY4mArLUMJW+KlQhlmUdKAHQuPfb00Jl5xIfImeOsf6YL8QK7Q==}
engines: {node: '>=0.10.0'}
+ prettier@2.8.8:
+ resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
+
+ print-js@1.6.0:
+ resolution: {integrity: sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg==}
+
proto-list@1.2.4:
resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+ qrcode@1.5.4:
+ resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
quansync@0.2.10:
resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
@@ -1658,6 +1775,13 @@
repeat-string@1.6.1:
resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==}
engines: {node: '>=0.10'}
+
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
+ require-main-filename@2.0.0:
+ resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
resolve-url@0.2.1:
resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==}
@@ -1712,6 +1836,9 @@
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
engines: {node: '>=10'}
hasBin: true
+
+ set-blocking@2.0.0:
+ resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
@@ -2033,10 +2160,23 @@
'@vue/composition-api':
optional: true
+ vue-easy-lightbox@1.19.0:
+ resolution: {integrity: sha512-YxLXgjEn91UF3DuK1y8u3Pyx2sJ7a/MnBpkyrBSQkvU1glzEJASyAZ7N+5yDpmxBQDVMwCsL2VmxWGIiFrWCgA==}
+ engines: {node: '>=14.18.3'}
+ peerDependencies:
+ vue: ^3.0.0
+
+ vue-esign@1.1.4:
+ resolution: {integrity: sha512-7Ix5PdcyyhVfsvrT9a+yp5+36gbQ0/bpDO+QSLT58IgJ5t164PEptOy5Nslw8bZbk3n3Hc7SP5B8eXQ8X8W+OA==}
+
vue-router@4.4.0:
resolution: {integrity: sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==}
peerDependencies:
vue: ^3.2.0
+
+ vue@2.7.16:
+ resolution: {integrity: sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==}
+ deprecated: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.
vue@3.4.31:
resolution: {integrity: sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==}
@@ -2066,6 +2206,9 @@
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
engines: {node: '>= 0.4'}
+ which-module@2.0.1:
+ resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
+
which-typed-array@1.1.19:
resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
engines: {node: '>= 0.4'}
@@ -2075,6 +2218,10 @@
engines: {node: '>= 8'}
hasBin: true
+ wrap-ansi@6.2.0:
+ resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
+ engines: {node: '>=8'}
+
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
@@ -2082,6 +2229,17 @@
wrap-ansi@8.1.0:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
+
+ y18n@4.0.3:
+ resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
+
+ yargs-parser@18.1.3:
+ resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
+ engines: {node: '>=6'}
+
+ yargs@15.4.1:
+ resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
+ engines: {node: '>=8'}
zrender@5.6.0:
resolution: {integrity: sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==}
@@ -2314,6 +2472,16 @@
vite: 5.3.2(@types/node@22.15.18)(sass@1.77.5)
vue: 3.4.31
+ '@vue-office/docx@1.6.3(vue-demi@0.14.10(vue@3.4.31))(vue@3.4.31)':
+ dependencies:
+ vue: 3.4.31
+ vue-demi: 0.14.10(vue@3.4.31)
+
+ '@vue-office/excel@1.7.14(vue-demi@0.14.10(vue@3.4.31))(vue@3.4.31)':
+ dependencies:
+ vue: 3.4.31
+ vue-demi: 0.14.10(vue@3.4.31)
+
'@vue/compiler-core@3.4.31':
dependencies:
'@babel/parser': 7.27.2
@@ -2339,6 +2507,14 @@
dependencies:
'@vue/compiler-core': 3.5.14
'@vue/shared': 3.5.14
+
+ '@vue/compiler-sfc@2.7.16':
+ dependencies:
+ '@babel/parser': 7.27.2
+ postcss: 8.5.3
+ source-map: 0.6.1
+ optionalDependencies:
+ prettier: 2.8.8
'@vue/compiler-sfc@3.4.31':
dependencies:
@@ -2502,6 +2678,8 @@
atob@2.1.2: {}
+ autofit.js@3.2.8: {}
+
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.1.0
@@ -2586,6 +2764,8 @@
call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.3.0
+ camelcase@5.3.1: {}
+
chalk@1.1.3:
dependencies:
ansi-styles: 2.2.1
@@ -2623,6 +2803,12 @@
good-listener: 1.2.2
select: 1.1.2
tiny-emitter: 2.1.0
+
+ cliui@6.0.0:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 6.2.0
clone@2.1.2: {}
@@ -2718,6 +2904,8 @@
dependencies:
ms: 2.1.3
+ decamelize@1.2.0: {}
+
decode-uri-component@0.2.2: {}
deep-equal@1.1.2:
@@ -2757,6 +2945,8 @@
delayed-stream@1.0.0: {}
delegate@3.2.0: {}
+
+ dijkstrajs@1.0.3: {}
dom-serializer@0.2.2:
dependencies:
@@ -3029,6 +3219,11 @@
dependencies:
to-regex-range: 5.0.1
+ find-up@4.1.0:
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+
follow-redirects@1.15.9: {}
for-each@0.3.5:
@@ -3076,6 +3271,8 @@
functions-have-names@1.2.3: {}
fuse.js@6.6.2: {}
+
+ get-caller-file@2.0.5: {}
get-intrinsic@1.3.0:
dependencies:
@@ -3423,6 +3620,10 @@
pkg-types: 2.1.0
quansync: 0.2.10
+ locate-path@5.0.0:
+ dependencies:
+ p-locate: 4.1.0
+
lodash-es@4.17.21: {}
lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21):
@@ -3594,11 +3795,23 @@
object-keys: 1.1.1
safe-push-apply: 1.0.0
+ p-limit@2.3.0:
+ dependencies:
+ p-try: 2.2.0
+
+ p-locate@4.1.0:
+ dependencies:
+ p-limit: 2.3.0
+
+ p-try@2.2.0: {}
+
package-json-from-dist@1.0.1: {}
parchment@1.1.4: {}
pascalcase@0.1.1: {}
+
+ path-exists@4.0.0: {}
path-key@3.1.1: {}
@@ -3634,6 +3847,8 @@
confbox: 0.2.2
exsolve: 1.0.5
pathe: 2.0.3
+
+ pngjs@5.0.0: {}
posix-character-classes@0.1.1: {}
@@ -3679,9 +3894,20 @@
posthtml-parser: 0.2.1
posthtml-render: 1.4.0
+ prettier@2.8.8:
+ optional: true
+
+ print-js@1.6.0: {}
+
proto-list@1.2.4: {}
proxy-from-env@1.1.0: {}
+
+ qrcode@1.5.4:
+ dependencies:
+ dijkstrajs: 1.0.3
+ pngjs: 5.0.0
+ yargs: 15.4.1
quansync@0.2.10: {}
@@ -3751,6 +3977,10 @@
repeat-element@1.1.4: {}
repeat-string@1.6.1: {}
+
+ require-directory@2.1.1: {}
+
+ require-main-filename@2.0.0: {}
resolve-url@0.2.1: {}
@@ -3824,6 +4054,8 @@
select@1.1.2: {}
semver@7.7.2: {}
+
+ set-blocking@2.0.0: {}
set-function-length@1.2.2:
dependencies:
@@ -4234,10 +4466,23 @@
dependencies:
vue: 3.4.31
+ vue-easy-lightbox@1.19.0(vue@3.4.31):
+ dependencies:
+ vue: 3.4.31
+
+ vue-esign@1.1.4:
+ dependencies:
+ vue: 2.7.16
+
vue-router@4.4.0(vue@3.4.31):
dependencies:
'@vue/devtools-api': 6.6.4
vue: 3.4.31
+
+ vue@2.7.16:
+ dependencies:
+ '@vue/compiler-sfc': 2.7.16
+ csstype: 3.1.3
vue@3.4.31:
dependencies:
@@ -4285,6 +4530,8 @@
is-weakmap: 2.0.2
is-weakset: 2.0.4
+ which-module@2.0.1: {}
+
which-typed-array@1.1.19:
dependencies:
available-typed-arrays: 1.0.7
@@ -4299,6 +4546,12 @@
dependencies:
isexe: 2.0.0
+ wrap-ansi@6.2.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
@@ -4311,6 +4564,27 @@
string-width: 5.1.2
strip-ansi: 7.1.0
+ y18n@4.0.3: {}
+
+ yargs-parser@18.1.3:
+ dependencies:
+ camelcase: 5.3.1
+ decamelize: 1.2.0
+
+ yargs@15.4.1:
+ dependencies:
+ cliui: 6.0.0
+ decamelize: 1.2.0
+ find-up: 4.1.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ require-main-filename: 2.0.0
+ set-blocking: 2.0.0
+ string-width: 4.2.3
+ which-module: 2.0.1
+ y18n: 4.0.3
+ yargs-parser: 18.1.3
+
zrender@5.6.0:
dependencies:
tslib: 2.3.0
diff --git a/src/api/customerService/index.js b/src/api/customerService/index.js
index 560ffe6..571601a 100644
--- a/src/api/customerService/index.js
+++ b/src/api/customerService/index.js
@@ -99,4 +99,34 @@
url: '/afterSalesNearExpiryService/delete?ids=' + ids,
method: 'delete',
})
-}
\ No newline at end of file
+}
+
+// 鏌ヨ鎵�鏈夊鎴蜂俊鎭�
+// /basic/customer/list
+export function getAllCustomerList(query) {
+ return request({
+ url: '/basic/customer/list',
+ method: 'get',
+ params: query,
+ })
+}
+
+// 鏍规嵁瀹㈡埛鏌ヨ閿�鍞鍗曞彿
+// afterSalesService/listSalesLedger
+export function getSalesLedger(query) {
+ return request({
+ url: '/afterSalesService/listSalesLedger',
+ method: 'get',
+ params: query,
+ })
+}
+
+// 鏍规嵁閿�鍞鍗曞彿鏌ヨ閿�鍞鍗曡鎯�
+// afterSalesService/count
+export function getSalesLedgerDetail(query) {
+ return request({
+ url: '/afterSalesService/count',
+ method: 'get',
+ params: query,
+ })
+}
diff --git a/src/views/customerService/afterSalesHandling/index.vue b/src/views/customerService/afterSalesHandling/index.vue
index dd07ddb..d95e327 100644
--- a/src/views/customerService/afterSalesHandling/index.vue
+++ b/src/views/customerService/afterSalesHandling/index.vue
@@ -1,38 +1,94 @@
<template>
<div class="app-container">
- <div class="search_form">
- <div>
- <span class="search_title">鍙嶉鏃ユ湡锛�</span>
- <el-date-picker
- v-model="searchForm.feedbackDate"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- type="date"
- placeholder="璇烽�夋嫨"
- clearable
- @change="handleQuery"
- />
- <span class="search_title ml10">澶勭悊鏃ユ湡锛�</span>
- <el-date-picker
- v-model="searchForm.disDate"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- type="date"
- placeholder="璇烽�夋嫨"
- clearable
- @change="handleQuery"
- />
- <span style = "margin-left: 10px;" class="search_title">澶勭悊鐘舵�侊細</span>
- <el-select v-model="searchForm.status" placeholder="璇烽�夋嫨鐘舵��" @change="handleQuery" style="width: 140px" clearable>
- <el-option label="寰呭鐞�" :value="1"></el-option>
- <el-option label="宸插鐞�" :value="2"></el-option>
- </el-select>
- <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
- >鎼滅储</el-button
- >
- <el-button @click="handleOut" style="margin-left: 10px">瀵煎嚭</el-button>
- </div>
- </div>
+ <div class="search-wrapper">
+ <el-form
+ :model="searchForm"
+ class="demo-form-inline"
+ >
+ <el-row :gutter="20">
+ <el-col :span="4">
+ <el-form-item>
+ <el-input
+ v-model="searchForm.afterSalesServiceNo"
+ placeholder="璇疯緭鍏ュ伐鍗曠紪鍙�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item>
+ <el-select
+ v-model="searchForm.status"
+ placeholder="璇烽�夋嫨宸ュ崟鐘舵��"
+ clearable
+ >
+ <el-option
+ v-for="dict in workOrderStatusOptions"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item>
+ <el-select
+ v-model="searchForm.urgency"
+ placeholder="璇烽�夋嫨绱ф�ョ▼搴�"
+ clearable
+ >
+ <el-option
+ v-for="dict in degreeOfUrgencyOptions"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item>
+ <el-select
+ v-model="searchForm.serviceType"
+ placeholder="璇烽�夋嫨鍞悗绫诲瀷"
+ clearable
+ >
+ <el-option
+ v-for="dict in classificationOptions"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item>
+ <el-input
+ v-model="searchForm.orderNo"
+ placeholder="璇疯緭鍏ラ攢鍞崟鍙�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+
+
+
+ <!-- 鎸夐挳 -->
+ <el-col :span="4">
+ <el-form-item>
+ <el-button type="primary" @click="handleQuery">
+ 鎼滅储
+ </el-button>
+ <el-button @click="handleReset">
+ 閲嶇疆
+ </el-button>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ </div>
<div class="table_list">
<PIMTable
rowKey="id"
@@ -81,69 +137,107 @@
},
});
const { searchForm } = toRefs(data);
+/*
+post_sale_waiting_list 鏂板鐨勫敭鍚庡垎绫�
+degree_of_urgency 鏂板鐨勭揣鎬ョ▼搴�
+work_order_status 涓婚〉鐨勫伐鍗曠姸鎬�
+*/
+const { post_sale_waiting_list, degree_of_urgency, work_order_status } = proxy.useDict(
+ "post_sale_waiting_list",
+ "degree_of_urgency",
+ "work_order_status",
+);
+
+const classificationOptions = computed(() => post_sale_waiting_list?.value || []);
+const degreeOfUrgencyOptions = computed(() => degree_of_urgency?.value || []);
+const workOrderStatusOptions = computed(() => work_order_status?.value || []);
const tableColumn = ref([
{
- label: "澶勭悊鐘舵��",
- prop: "status",
- dataType: "tag",
- formatData: (params) => {
- if (params == 1) {
- return "寰呭鐞�";
- } else if (params == 2) {
- return "宸插鐞�";
- } else {
- return null;
- }
- },
- formatType: (params) => {
- if (params == 1) {
- return "danger";
- } else if (params == 2) {
- return "success";
- } else {
- return null;
- }
- },
- },
- {
- label: "鍙嶉鏃ユ湡",
- prop: "feedbackDate",
- width: 150,
- },
- {
- label: "鐧昏浜�",
- prop: "checkNickName",
- },
- {
- label: "瀹㈡埛鍚嶇О",
- prop: "customerName",
- width: 200,
- },
- {
- label: "闂鎻忚堪",
- prop: "proDesc",
- width:300
- },
- {
- label: "鍏宠仈閮ㄩ棬",
- prop: "deptName",
- width: 200,
- },
- {
- label: "澶勭悊浜�",
- prop: "disposeNickName",
- },
- {
- label: "澶勭悊缁撴灉",
- prop: "disRes",
- width: 200,
- },
- {
- label: "澶勭悊鏃ユ湡",
- prop: "disDate",
- width: 150,
- },
+ label: "宸ュ崟缂栧彿",
+ prop:"afterSalesServiceNo",
+ width: 150,
+ align: "center"
+ },
+ {
+ label: "閿�鍞崟鍙�",
+ prop:"salesContractNo",
+ width: 150,
+ align: "center"
+ },
+ {
+ label: "澶勭悊鐘舵��",
+ prop: "status",
+ dataType: "tag",
+
+ formatData: (params) => {
+ if (params === 1) {
+ return "寰呭鐞�";
+ } else if (params === 2) {
+ return "宸插鐞�";
+ } else {
+ return null;
+ }
+ },
+ formatType: (params) => {
+ if (params === 1) {
+ return "danger";
+ } else if (params === 2) {
+ return "success";
+ } else {
+ return null;
+ }
+ },
+ align: "center"
+ },
+ {
+ label: "鍙嶉鏃ユ湡",
+ prop: "feedbackDate",
+ width: 150,
+ align: "center"
+ },
+ {
+ label: "鐧昏浜�",
+ prop: "checkNickName",
+ align: "center"
+ },
+ {
+ label: "绱ф�ョ▼搴�",
+ prop: "urgency",
+ // 鏍规嵁degreeOfUrgencyOptions瀛楀吀鍘昏嚜鍔ㄥ尮閰�
+ formatData: (params) => {
+ if (params) {
+ const item = degreeOfUrgencyOptions.value.find(item => item.value === params);
+ return item?.label || params;
+ }
+ return null;
+ },
+ align: "center"
+ },
+ {
+ label: "鍞悗绫诲瀷",
+ prop: "serviceType",
+ // 鏍规嵁classificationOptions瀛楀吀鍘昏嚜鍔ㄥ尮閰�
+ formatData: (params) => {
+ if (params) {
+ const item = classificationOptions.value.find(item => item.value === params);
+ return item?.label || params;
+ }
+ return null;
+ },
+ align: "center"
+ },
+ {
+ label: "闂鎻忚堪",
+ prop: "disRes",
+ width:300,
+ },
+ {
+ label: "鍏宠仈閮ㄩ棬",
+ prop: "deptName",
+ width: 200,
+ align: "center"
+ },
{
dataType: "action",
label: "鎿嶄綔",
@@ -197,6 +291,12 @@
const fileListDialogVisible = ref(false)
const currentFileRow = ref(null)
+// 閲嶇疆
+const handleReset = () => {
+ Object.keys(searchForm.value).forEach(key => {
+ searchForm.value[key] = ""
+ })
+}
// 鎵撳紑闄勪欢寮规
const openFilesFormDia = async (row) => {
@@ -386,5 +486,10 @@
</script>
<style scoped>
-
+.search-wrapper {
+ background: white;
+ padding: 1rem 1rem 0 1rem;
+ border: 8px;
+ border-radius: 16px;
+}
</style>
\ No newline at end of file
diff --git a/src/views/customerService/feedbackRegistration/components/ProductSelectDialog.vue b/src/views/customerService/feedbackRegistration/components/ProductSelectDialog.vue
new file mode 100644
index 0000000..4df22bc
--- /dev/null
+++ b/src/views/customerService/feedbackRegistration/components/ProductSelectDialog.vue
@@ -0,0 +1,245 @@
+<template>
+ <el-dialog v-model="visible" title="閫夋嫨浜у搧" width="900px" destroy-on-close :close-on-click-modal="false">
+ <el-form :inline="true" :model="query" class="mb-2">
+ <el-form-item label="浜у搧鍒嗙被">
+ <el-input v-model="query.productCategory" placeholder="杈撳叆浜у搧鍒嗙被" clearable @keyup.enter="onSearch" />
+ </el-form-item>
+
+ <el-form-item label="鍩烘湰鍗曚綅">
+ <el-input v-model="query.unit" placeholder="杈撳叆鍩烘湰鍗曚綅" clearable @keyup.enter="onSearch" />
+ </el-form-item>
+
+ <el-form-item>
+ <el-button type="primary" @click="onSearch">鎼滅储</el-button>
+ <el-button @click="columnsDialogVisible = true">鍒楄〃瀛楁</el-button>
+ </el-form-item>
+ </el-form>
+
+ <!-- 鍒楄〃 -->
+ <el-table ref="tableRef" v-loading="loading" :data="tableData" height="420" highlight-current-row row-key="id"
+ @selection-change="handleSelectionChange" @select="handleSelect">
+ <el-table-column type="selection" width="55" />
+ <el-table-column type="index" label="搴忓彿" width="60" />
+ <template v-for="column in visibleColumns" :key="column.prop">
+ <el-table-column :prop="column.prop" :label="column.label" :min-width="column.minWidth" show-overflow-tooltip align="center" />
+ </template>
+ </el-table>
+
+ <div class="mt-3" style="margin-top: 10px;display: flex; justify-content: flex-end;">
+
+
+ <el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total"
+ v-model:page-size="page.pageSize" v-model:current-page="page.pageNum" :page-sizes="[10, 20, 50, 100]"
+ @size-change="onPageChange" @current-change="onPageChange" />
+ </div>
+
+ <template #footer>
+ <el-button type="primary" :disabled="multipleSelection.length === 0" @click="onConfirm">
+ 纭畾
+ </el-button>
+ <el-button @click="close()">鍙栨秷</el-button>
+ </template>
+ </el-dialog>
+
+ <el-dialog v-model="columnsDialogVisible" title="鑷畾涔夋樉绀哄垪椤�" width="600px">
+ <el-checkbox-group v-model="selectedColumns">
+ <el-checkbox v-for="column in allColumns" :key="column.prop" :label="column.prop" :disabled="column.disabled">
+ {{ column.label }}
+ </el-checkbox>
+ </el-checkbox-group>
+ <template #footer>
+ <el-button @click="resetColumns">鎭㈠榛樿</el-button>
+ <el-button type="primary" @click="saveColumns">淇濆瓨</el-button>
+ </template>
+ </el-dialog>
+</template>
+
+<script setup>
+import { computed, onMounted, reactive, ref, watch, nextTick } from "vue";
+import { ElMessage } from "element-plus";
+
+const props = defineProps({
+ modelValue: Boolean,
+ single: Boolean, // 鏄惁鍙兘閫夋嫨涓�涓紝榛樿false锛堝彲閫夋嫨澶氫釜锛�
+ products: {
+ type: Array,
+ default: () => []
+ },
+ selectedIds: {
+ type: Array,
+ default: () => []
+ }
+});
+
+const emit = defineEmits(['update:modelValue', 'confirm']);
+
+const visible = computed({
+ get: () => props.modelValue,
+ set: (v) => emit("update:modelValue", v),
+});
+
+const query = reactive({
+ productName: "",
+ model: "",
+});
+
+const page = reactive({
+ pageNum: 1,
+ pageSize: 10,
+});
+
+const loading = ref(false);
+const tableData = ref([]);
+const total = ref(0);
+const multipleSelection = ref([]);
+const tableRef = ref();
+
+const columnsDialogVisible = ref(false);
+
+const allColumns = ref([
+ { prop: 'productCategory', label: '浜у搧鍒嗙被', selected: true, disabled: false },
+ { prop: 'unit', label: '鍩烘湰鍗曚綅', selected: true, disabled: false },
+]);
+
+const selectedColumns = ref(allColumns.value.filter(c => c.selected).map(c => c.prop));
+
+const visibleColumns = computed(() => {
+ return allColumns.value.filter(c => selectedColumns.value.includes(c.prop));
+});
+
+const resetColumns = () => {
+ selectedColumns.value = allColumns.value.filter(c => c.selected).map(c => c.prop);
+};
+
+const saveColumns = () => {
+ if (selectedColumns.value.length < 1) {
+ ElMessage.warning("鍒楄〃椤规樉绀轰笉寰楀皯浜�1椤�");
+ return;
+ }
+ columnsDialogVisible.value = false;
+};
+
+function close() {
+ visible.value = false;
+}
+
+const handleSelectionChange = (val) => {
+ if (props.single && val.length > 1) {
+ // 濡傛灉闄愬埗涓哄崟涓�夋嫨锛屽彧淇濈暀鏈�鍚庝竴涓�変腑鐨�
+ const lastSelected = val[val.length - 1];
+ multipleSelection.value = [lastSelected];
+ // 娓呯┖琛ㄦ牸閫変腑鐘舵�侊紝鐒跺悗閲嶆柊閫変腑鏈�鍚庝竴涓�
+ nextTick(() => {
+ if (tableRef.value) {
+ tableRef.value.clearSelection();
+ tableRef.value.toggleRowSelection(lastSelected, true);
+ }
+ });
+ } else {
+ multipleSelection.value = val;
+ }
+}
+
+// 澶勭悊鍗曚釜閫夋嫨
+const handleSelect = (selection, row) => {
+ if (props.single) {
+ // 濡傛灉闄愬埗涓哄崟涓紝娓呯┖鍏朵粬閫夋嫨锛屽彧淇濈暀褰撳墠琛�
+ if (selection.includes(row)) {
+ // 閫変腑褰撳墠琛屾椂锛屾竻绌哄叾浠栭�変腑
+ multipleSelection.value = [row];
+ nextTick(() => {
+ if (tableRef.value) {
+ tableData.value.forEach((item) => {
+ if (item.id !== row.id) {
+ tableRef.value.toggleRowSelection(item, false);
+ }
+ });
+ }
+ });
+ }
+ }
+}
+
+function onSearch() {
+ page.pageNum = 1;
+ loadData();
+}
+
+function onReset() {
+ query.productName = "";
+ query.model = "";
+ page.pageNum = 1;
+ loadData();
+}
+
+function onPageChange() {
+ loadData();
+}
+
+function onConfirm() {
+ if (multipleSelection.value.length === 0) {
+ ElMessage.warning("璇烽�夋嫨涓�鏉′骇鍝�");
+ return;
+ }
+ if (props.single && multipleSelection.value.length > 1) {
+ ElMessage.warning("鍙兘閫夋嫨涓�涓骇鍝�");
+ return;
+ }
+ emit("confirm", props.single ? [multipleSelection.value[0]] : multipleSelection.value);
+ close();
+}
+
+async function loadData() {
+ loading.value = true;
+ try {
+ multipleSelection.value = []; // 缈婚〉/鎼滅储鍚庢竻绌洪�夋嫨鏇寸鍚堥鏈�
+
+ let filtered = props.products || [];
+ // 鏈湴鎼滅储杩囨护
+ if (query.productName) {
+ filtered = filtered.filter(item => item.productName && item.productName.includes(query.productName));
+ }
+ if (query.model) {
+ filtered = filtered.filter(item => item.model && item.model.includes(query.model));
+ }
+
+ total.value = filtered.length;
+ // 鍓嶇鍒嗛〉
+ const start = (page.pageNum - 1) * page.pageSize;
+ const end = start + page.pageSize;
+ tableData.value = filtered.slice(start, end);
+
+ // 鑷姩鍥炴樉閫変腑鐘舵��
+ nextTick(() => {
+ if (tableRef.value) {
+ tableData.value.forEach(row => {
+ if (props.selectedIds && props.selectedIds.includes(row.id)) {
+ tableRef.value.toggleRowSelection(row, true);
+ }
+ });
+ }
+ });
+ } finally {
+ loading.value = false;
+ }
+}
+
+// 鐩戝惉寮圭獥鎵撳紑锛岄噸缃�夋嫨
+watch(() => props.modelValue, (visible) => {
+ if (visible) {
+ // 姣忔鎵撳紑鏃堕噸鏂板垵濮嬪寲閫変腑鐘舵�侊紙multipleSelection 浼氶�氳繃 loadData 涓殑鍥炴樉閫昏緫鑷姩鏇存柊锛屼絾鍒濆闇�缃┖閬垮厤閲嶅锛�
+ multipleSelection.value = [];
+ page.pageNum = 1;
+ loadData();
+ }
+});
+
+// 鐩戝惉鏁版嵁婧愬彉鍖�
+watch(() => props.products, () => {
+ loadData();
+}, { deep: true });
+
+onMounted(() => {
+ loadData()
+})
+</script>
diff --git a/src/views/customerService/feedbackRegistration/components/formDia.vue b/src/views/customerService/feedbackRegistration/components/formDia.vue
index 41c8ac6..2af71da 100644
--- a/src/views/customerService/feedbackRegistration/components/formDia.vue
+++ b/src/views/customerService/feedbackRegistration/components/formDia.vue
@@ -2,70 +2,118 @@
<div>
<el-dialog
v-model="dialogFormVisible"
- title="鍞悗鐧昏"
- width="70%"
+ title="鏂板鍞悗鍗�"
+ width="90%"
@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="feedbackDate">
- <el-date-picker
- style="width: 100%"
- v-model="form.feedbackDate"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- type="date"
- placeholder="璇烽�夋嫨"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="鐧昏浜猴細" prop="checkUserId">
- <el-select
- v-model="form.checkUserId"
- placeholder="璇烽�夋嫨"
- clearable
- >
- <el-option
- v-for="item in userList"
- :key="item.userId"
- :label="item.nickName"
- :value="item.userId"
- ></el-option>
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
- <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="proDesc">
- <el-input
- v-model="form.proDesc"
- placeholder="璇疯緭鍏�"
- clearable
- type="textarea"
- />
- </el-form-item>
- </el-col>
- </el-row>
- </el-form>
+ <div>
+ <span class="descriptions">鍩虹璧勬枡</span>
+ <el-form
+ :model="form"
+ label-width="140px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef"
+ >
+ <el-row :gutter="30">
+ <el-col :span="4">
+ <el-form-item label="瀹㈡埛鍚嶇О锛�" prop="customerName">
+ <el-select
+ v-model="form.customerName"
+ filterable
+ @change="customerNameChange"
+ >
+ <el-option
+ v-for="item in customerNameOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item label="鍞悗绫诲瀷锛�" prop="serviceType">
+ <el-select
+ v-model="form.serviceType"
+ filterable
+ >
+ <el-option
+ v-for="dict in serviceTypeOptions"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item label="鍏宠仈閿�鍞崟鍙凤細" prop="salesContractNo">
+ <el-select
+ v-model="form.salesContractNo"
+ @change="associatedSalesOrderNumberChange"
+ filterable
+ >
+ <el-option
+ v-for="item in associatedSalesOrderNumberOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item label="绱ф�ョ▼搴︼細" prop="urgency">
+ <el-select
+ v-model="form.urgency"
+ filterable
+ >
+ <el-option
+ v-for="dict in urgencyOptions"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item label="闂鎻忚堪锛�" prop="disRes">
+ <el-input
+ v-model="form.disRes"
+ placeholder="璇疯緭鍏ラ棶棰樻弿杩�"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <hr>
+ <div style="padding-top: 20px">
+ <div style="display: flex; justify-content: space-between">
+ <span class="descriptions">鍏宠仈浜у搧</span>
+ <el-button
+ type="primary"
+ style="margin-right: 12px; margin-bottom: 10px"
+ @click="isShowProductSelectDialog = true"
+ >
+ 閫夋嫨浜у搧
+ </el-button>
+ </div>
+ <PIMTable
+ :isShowPagination="false"
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ >
+ <template #shippingStatus="{ row }">
+ <el-tag :type="getShippingStatusType(row)" size="small">
+ {{ getShippingStatusText(row) }}
+ </el-tag>
+ </template>
+ </PIMTable>
+ </div>
+ </div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">纭</el-button>
@@ -73,63 +121,286 @@
</div>
</template>
</el-dialog>
+ <!-- 閫夋嫨浜у搧寮圭獥 -->
+ <ProductSelectDialog
+ v-model="isShowProductSelectDialog"
+ :products="currentSalesOrderProducts"
+ :selected-ids="currentSelectedProductIds"
+ @confirm="handleSelectProducts"
+ />
</div>
</template>
<script setup>
-import {ref} from "vue";
+import { ref, reactive, toRefs, getCurrentInstance, computed } from "vue";
+import ProductSelectDialog from "./ProductSelectDialog.vue";
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
-import {afterSalesServiceAdd, afterSalesServiceUpdate} from "@/api/customerService/index.js";
+import {afterSalesServiceAdd, afterSalesServiceUpdate, getAllCustomerList, getSalesLedger } from "@/api/customerService/index.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
const operationType = ref('')
+const formRef = ref(null)
+const customerNameOptions = ref([])
const userStore = useUserStore();
const data = reactive({
form: {
- feedbackDate: "",
- checkUserId: "",
- customerName: "",
- proDesc: "",
+ topic: "",
+ serviceType: "",
+ urgency: "",
+ salesLedgerId: null,
+ productModelIds: "",
+ customerId: null,
+ salesContractNo: "",
+ disRes: "",
+ customerName: ""
},
rules: {
+ customerName: [{required: true, message: "璇烽�夋嫨瀹㈡埛鍚嶇О", trigger: "change"}],
+ serviceType: [{required: true, message: "璇烽�夋嫨鍞悗绫诲瀷", trigger: "change"}],
+ urgency: [{required: true, message: "璇烽�夋嫨绱ф�ョ▼搴�", trigger: "change"}],
feedbackDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
- checkUserId: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
- customerName: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
- proDesc: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
}
})
+
+// 鑷畾涔夋牎楠屽嚱鏁帮細鍒ゆ柇鏄惁闇�瑕佹牎楠屽敭鍚庣紪鍙�
+
const { form, rules } = toRefs(data);
const userList = ref([])
+const formatCurrency = (val) => {
+ if (val === null || val === undefined || val === '') return '-'
+ const num = Number(val)
+ return Number.isFinite(num) ? num.toFixed(2) : '-'
+}
+
+const { post_sale_waiting_list, degree_of_urgency } = proxy.useDict(
+ "post_sale_waiting_list",
+ "degree_of_urgency"
+);
+
+const serviceTypeOptions = computed(() => post_sale_waiting_list?.value || []);
+const urgencyOptions = computed(() => degree_of_urgency?.value || []);
+
+const tableColumn = ref([
+ { label: "浜у搧澶х被", prop: "productCategory" },
+ { label: "瑙勬牸鍨嬪彿", prop: "specificationModel" },
+ { label: "鍗曚綅", prop: "unit" },
+ {
+ label: "浜у搧鐘舵��",
+ prop: "approveStatus",
+ width: 100,
+ align: "center",
+ dataType: "tag",
+ formatData: (v) => (v === 1 ? "鍏呰冻" : "涓嶈冻"),
+ formatType: (v) => (v === 1 ? "success" : "danger"),
+ },
+ {
+ label: "鍙戣揣鐘舵��",
+ align: "center",
+ width: 140,
+ dataType: "slot",
+ slot: "shippingStatus",
+ },
+ { label: "蹇�掑叕鍙�", prop: "expressCompany", width: 140 },
+ { label: "蹇�掑崟鍙�", prop: "expressNumber", width: 160 },
+ { label: "鍙戣揣杞︾墝", prop: "shippingCarNumber", minWidth: 100, align: "center" },
+ { label: "鍙戣揣鏃ユ湡", prop: "shippingDate", minWidth: 100, align: "center" },
+ { label: "鏁伴噺", prop: "quantity", width: 100 },
+ { label: "绋庣巼(%)", prop: "taxRate", width: 100 },
+ {
+ label: "鍚◣鍗曚环(鍏�)",
+ prop: "taxInclusiveUnitPrice",
+ width: 160,
+ formatData: formatCurrency,
+ },
+ {
+ label: "鍚◣鎬讳环(鍏�)",
+ prop: "taxInclusiveTotalPrice",
+ width: 160,
+ formatData: formatCurrency,
+ },
+ {
+ label: "涓嶅惈绋庢�讳环(鍏�)",
+ prop: "taxExclusiveTotalPrice",
+ width: 160,
+ formatData: formatCurrency,
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: 'right',
+ operation: [
+ {
+ name: "鍒犻櫎",
+ type: "text",
+ clickFun: (row) => {
+ tableData.value = tableData.value.filter(i => i.id !== row.id)
+ },
+
+ },
+ ],
+ },
+])
+const tableData = ref([])
+// 閫夋嫨浜у搧寮圭獥
+const isShowProductSelectDialog = ref(false)
+const handleSelectProducts = (rows) => {
+ if (!Array.isArray(rows)) return
+ const existingIds = new Set(tableData.value.map(i => i.id))
+ const mapped = rows
+ .filter(r => !existingIds.has(r.id))
+ .map(r => ({
+ id: r.id,
+ productCategory: r.productName,
+ specificationModel: r.model,
+ unit: r.unit || '',
+ approveStatus: null,
+ shippingStatus: '',
+ expressCompany: '',
+ expressNumber: '',
+ shippingCarNumber: '',
+ shippingDate: '',
+ quantity: 0,
+ taxRate: 0,
+ taxInclusiveUnitPrice: 0,
+ taxInclusiveTotalPrice: 0,
+ taxExclusiveTotalPrice: 0,
+ }))
+ tableData.value = tableData.value.concat(mapped)
+}
+const currentSelectedProductIds = computed(() => {
+ return tableData.value.map(item => item.id)
+})
+
+const associatedSalesOrderNumberChange = () => {
+ const opt = associatedSalesOrderNumberOptions.value.find(
+ (item) => item.value === form.value.salesContractNo
+ )
+ tableData.value = opt?.productData || []
+ form.value.salesLedgerId = opt?.id || null
+}
+
+const associatedSalesOrderNumberOptions = ref([])
+
+const currentSalesOrderProducts = computed(() => {
+ const opt = associatedSalesOrderNumberOptions.value.find(
+ (item) => item.value === form.value.salesContractNo
+ )
+ return opt?.productData || []
+})
+
+const customerNameChange = (val) => {
+ const opt = customerNameOptions.value.find(item => item.value === val);
+ if (opt) {
+ form.value.customerId = opt.id;
+ }
+ getSalesLedger({
+ customerName: form.value.customerName
+ }).then(res => {
+ if(res.code === 200){
+ associatedSalesOrderNumberOptions.value = res.data.records.map(item => ({
+ label: item.salesContractNo,
+ value: item.salesContractNo,
+ productData:item.productData,
+ id: item.id
+ }))
+ }
+ })
+}
+
+const getShippingStatusText = (row) => {
+ if (!row) return '寰呭彂璐�'
+ if (row.shippingDate || row.shippingCarNumber) {
+ return '宸插彂璐�'
+ }
+ const status = row.shippingStatus
+ if (status === null || status === undefined || status === '') {
+ return '寰呭彂璐�'
+ }
+ const map = {
+ '寰呭彂璐�': '寰呭彂璐�',
+ '寰呭鏍�': '寰呭鏍�',
+ '瀹℃牳涓�': '瀹℃牳涓�',
+ '瀹℃牳鎷掔粷': '瀹℃牳鎷掔粷',
+ '瀹℃牳閫氳繃': '瀹℃牳閫氳繃',
+ '宸插彂璐�': '宸插彂璐�'
+ }
+ return map[String(status).trim()] || '寰呭彂璐�'
+}
+
+const getShippingStatusType = (row) => {
+ if (!row) return 'info'
+ if (row.shippingDate || row.shippingCarNumber) {
+ return 'success'
+ }
+ const status = row.shippingStatus
+ if (status === null || status === undefined || status === '') {
+ return 'info'
+ }
+ const map = {
+ '寰呭彂璐�': 'info',
+ '寰呭鏍�': 'warning',
+ '瀹℃牳涓�': 'warning',
+ '瀹℃牳鎷掔粷': 'danger',
+ '瀹℃牳閫氳繃': 'success',
+ '宸插彂璐�': 'success'
+ }
+ return map[String(status).trim()] || 'info'
+}
+
// 鎵撳紑寮规
-const openDialog = (type, row) => {
+const openDialog =async (type, row) => {
+ // 璇锋眰澶氫釜鎺ュ彛锛岃幏鍙栨暟鎹�
+ let res = await getAllCustomerList();
+ if(res.records){
+ customerNameOptions.value = res.records.map(item => ({
+ label: item.customerName,
+ value: item.customerName,
+ id: item.id
+ }));
+ }
+
+
operationType.value = type;
dialogFormVisible.value = true;
form.value = {}
proxy.resetForm("formRef");
form.value.checkUserId = userStore.id;
form.value.feedbackDate = getCurrentDate();
+ // 鏂板鏃舵竻绌哄凡閫夊叧鑱斾骇鍝�
+ if (type === "add") {
+ tableData.value = []
+ }
userListNoPageByTenantId().then((res) => {
userList.value = res.data;
});
if (type === "edit") {
form.value = {...row}
+ if (form.value.customerName) {
+ const res = await getSalesLedger({ customerName: form.value.customerName })
+ if (res?.code === 200) {
+ console.log(res)
+ associatedSalesOrderNumberOptions.value = (res.data?.records || []).map(item => ({
+ label: item.salesContractNo,
+ value: item.salesContractNo,
+ productData: item.productData,
+ id: item.id
+ }))
+ }
+ }
+ console.log(form.value)
}
}
-// const setName = (code) => {
-// const index = userList.value.findIndex(item => item.deviceModel === code);
-// if (index > -1) {
-// console.log(userList)
-// form.value.name = userList.value[index].deviceName;
-// }
-// }
const submitForm = () => {
proxy.$refs["formRef"].validate(valid => {
if (valid) {
+ // 鍖归厤浜у搧鍨嬪彿IDs
+ form.value.productModelIds = tableData.value.map(item => item.id).join(",")
if (operationType.value === "add") {
afterSalesServiceAdd(form.value).then(response => {
proxy.$modal.msgSuccess("鏂板鎴愬姛")
@@ -155,6 +426,25 @@
});
</script>
-<style scoped>
+<style scoped lang="scss">
+.descriptions {
+ margin-bottom: 20px;
+ display: inline-block;
+ font-size: 1rem;
+ font-weight: 600;
+ padding-left: 12px;
+ position: relative;
+}
-</style>
\ No newline at end of file
+.descriptions::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 4px;
+ height: 1rem;
+ background-color: #002FA7; /* Element 榛樿绾㈣壊 */
+ border-radius: 2px;
+}
+</style>
diff --git a/src/views/customerService/feedbackRegistration/index.vue b/src/views/customerService/feedbackRegistration/index.vue
index 3d97ef8..db76b1c 100644
--- a/src/views/customerService/feedbackRegistration/index.vue
+++ b/src/views/customerService/feedbackRegistration/index.vue
@@ -1,229 +1,513 @@
<template>
- <div class="app-container">
- <div class="search_form">
- <div>
- <span class="search_title">鍙嶉鏃ユ湡锛�</span>
- <el-date-picker
- v-model="searchForm.feedbackDate"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- type="date"
- placeholder="璇烽�夋嫨"
- clearable
- @change="handleQuery"
- />
- <span style="margin-left: 10px;" class="search_title">澶勭悊鐘舵�侊細</span>
- <el-select v-model="searchForm.status" placeholder="璇烽�夋嫨鐘舵��" @change="handleQuery" style="width: 140px" clearable>
- <el-option label="寰呭鐞�" :value="1"></el-option>
- <el-option label="宸插鐞�" :value="2"></el-option>
- </el-select>
- <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
- >鎼滅储</el-button
- >
- </div>
- <div>
- <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
- rowKey="id"
- :column="tableColumn"
- :tableData="tableData"
- :page="page"
- :isSelection="true"
- @selection-change="handleSelectionChange"
- :tableLoading="tableLoading"
- @pagination="pagination"
- ></PIMTable>
- </div>
- <form-dia ref="formDia" @close="handleQuery"></form-dia>
- </div>
+ <div class="app-container">
+ <div class="workorder-stats">
+ <div
+ v-for="(item, index) in statsList"
+ :key="index"
+ class="stat-card"
+ >
+ <div class="stat-icon" :style="{ backgroundColor: item.bgColor }">
+ <el-icon :color="item.color" :size="20">
+ <component :is="item.icon" />
+ </el-icon>
+ </div>
+ <div class="stat-info">
+ <div class="stat-number">{{ item.count }}</div>
+ <div class="stat-label">{{ item.label }}</div>
+ </div>
+ </div>
+ </div>
+ <div class="search-wrapper">
+ <el-form
+ :model="searchForm"
+ class="demo-form-inline"
+ >
+ <el-row :gutter="20">
+ <el-col :span="4">
+ <el-form-item>
+ <el-input
+ v-model="searchForm.afterSalesServiceNo"
+ placeholder="璇疯緭鍏ュ伐鍗曠紪鍙�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item>
+ <el-select
+ v-model="searchForm.status"
+ placeholder="璇烽�夋嫨宸ュ崟鐘舵��"
+ clearable
+ >
+ <el-option
+ v-for="dict in workOrderStatusOptions"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item>
+ <el-select
+ v-model="searchForm.urgency"
+ placeholder="璇烽�夋嫨绱ф�ョ▼搴�"
+ clearable
+ >
+ <el-option
+ v-for="dict in degreeOfUrgencyOptions"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item>
+ <el-select
+ v-model="searchForm.serviceType"
+ placeholder="璇烽�夋嫨鍞悗绫诲瀷"
+ clearable
+ >
+ <el-option
+ v-for="dict in classificationOptions"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item>
+ <el-input
+ v-model="searchForm.orderNo"
+ placeholder="璇疯緭鍏ラ攢鍞崟鍙�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+
+
+
+ <!-- 鎸夐挳 -->
+ <el-col :span="4">
+ <el-form-item>
+ <el-button type="primary" @click="handleQuery">
+ 鎼滅储
+ </el-button>
+ <el-button @click="handleReset">
+ 閲嶇疆
+ </el-button>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ </div>
+ <div class="table_list">
+ <div class="table_header" style="display: flex; justify-content: space-between; align-items: center;">
+ <div>
+ <el-button type="primary" @click="openForm('add')">鏂板鍞悗鍗�</el-button>
+ </div>
+ <div>
+ <el-button @click="handleOut">瀵煎嚭</el-button>
+ <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+ </div>
+ </div>
+ <PIMTable
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :height="tableHeight"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination"
+ ></PIMTable>
+ </div>
+ <form-dia ref="formDia" @close="handleQuery"></form-dia>
+ </div>
</template>
<script setup>
-import {Search} from "@element-plus/icons-vue";
-import {onMounted, ref, getCurrentInstance, nextTick} from "vue";
+import {onMounted, reactive, ref, toRefs, computed, getCurrentInstance, nextTick} from "vue";
import FormDia from "@/views/customerService/feedbackRegistration/components/formDia.vue";
import {ElMessageBox} from "element-plus";
-import {afterSalesServiceDelete, afterSalesServiceListPage} from "@/api/customerService/index.js";
+import {afterSalesServiceDelete, afterSalesServiceListPage, getSalesLedgerDetail} from "@/api/customerService/index.js";
import useUserStore from "@/store/modules/user.js";
const { proxy } = getCurrentInstance();
const userStore = useUserStore()
+import { Document, FolderOpened, UserFilled } from "@element-plus/icons-vue"
+import { markRaw } from 'vue'
+
+const statsList = ref([
+ {
+ icon: markRaw(Document),
+ count: 0,
+ label: '鍏ㄩ儴宸ュ崟',
+ color: '#4080ff',
+ bgColor: '#eaf2ff'
+ },
+ {
+ icon: markRaw(FolderOpened),
+ count: 0,
+ label: '宸插鐞�',
+ color: '#ff9a2e',
+ bgColor: '#fff5e6'
+ },
+ {
+ icon: markRaw(UserFilled),
+ count: 0,
+ label: '宸插畬鎴�',
+ color: '#00b42a',
+ bgColor: '#e6f7ed'
+ },
+])
const data = reactive({
- searchForm: {
- feedbackDate: "",
- },
+ searchForm : {
+ customerName: "",
+ status: "",
+ urgency: "",
+ serviceType: "",
+ reviewStatus: "",
+ orderNo: "",
+ }
});
const { searchForm } = toRefs(data);
const tableColumn = ref([
- {
- label: "澶勭悊鐘舵��",
- prop: "status",
- dataType: "tag",
- formatData: (params) => {
- if (params == 1) {
- return "寰呭鐞�";
- } else if (params == 2) {
- return "宸插鐞�";
- } else {
- return null;
- }
- },
- formatType: (params) => {
- if (params == 1) {
- return "danger";
- } else if (params == 2) {
- return "success";
- } else {
- return null;
- }
- },
- },
- {
- label: "鍙嶉鏃ユ湡",
- prop: "feedbackDate",
- width: 150,
- },
- {
- label: "鐧昏浜�",
- prop: "checkNickName",
- },
- {
- label: "瀹㈡埛鍚嶇О",
- prop: "customerName",
- width: 200,
- },
- {
- label: "闂鎻忚堪",
- prop: "proDesc",
- width:300
- },
- {
- label: "鍏宠仈閮ㄩ棬",
- prop: "deptName",
- width: 200,
- },
- {
- dataType: "action",
- label: "鎿嶄綔",
- align: "center",
- fixed: 'right',
- operation: [
- {
- name: "缂栬緫",
- type: "text",
- clickFun: (row) => {
- openForm("edit", row);
- },
- disabled: (row) => {
- return row.status !== 1
- }
- },
- ],
- },
+ {
+ label: "宸ュ崟缂栧彿",
+ prop:"afterSalesServiceNo",
+ width: 150,
+ align: "center"
+ },
+ {
+ label: "閿�鍞崟鍙�",
+ prop:"salesContractNo",
+ width: 150,
+ align: "center"
+ },
+ {
+ label: "澶勭悊鐘舵��",
+ prop: "status",
+ dataType: "tag",
+
+ formatData: (params) => {
+ if (params === 1) {
+ return "寰呭鐞�";
+ } else if (params === 2) {
+ return "宸插鐞�";
+ } else {
+ return null;
+ }
+ },
+ formatType: (params) => {
+ if (params === 1) {
+ return "danger";
+ } else if (params === 2) {
+ return "success";
+ } else {
+ return null;
+ }
+ },
+ align: "center"
+ },
+ {
+ label: "鍙嶉鏃ユ湡",
+ prop: "feedbackDate",
+ width: 150,
+ align: "center"
+ },
+ {
+ label: "鐧昏浜�",
+ prop: "checkNickName",
+ align: "center"
+ },
+ {
+ label: "绱ф�ョ▼搴�",
+ prop: "urgency",
+ // 鏍规嵁degreeOfUrgencyOptions瀛楀吀鍘昏嚜鍔ㄥ尮閰�
+ formatData: (params) => {
+ if (params) {
+ const item = degreeOfUrgencyOptions.value.find(item => item.value === params);
+ return item?.label || params;
+ }
+ return null;
+ },
+ align: "center"
+ },
+ {
+ label: "鍞悗绫诲瀷",
+ prop: "serviceType",
+ // 鏍规嵁classificationOptions瀛楀吀鍘昏嚜鍔ㄥ尮閰�
+ formatData: (params) => {
+ if (params) {
+ const item = classificationOptions.value.find(item => item.value === params);
+ return item?.label || params;
+ }
+ return null;
+ },
+ align: "center"
+ },
+ {
+ label: "闂鎻忚堪",
+ prop: "disRes",
+ width:300,
+ },
+ {
+ label: "鍏宠仈閮ㄩ棬",
+ prop: "deptName",
+ width: 200,
+ align: "center"
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: 'right',
+ operation: [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: (row) => {
+ console.log(row)
+ openForm("edit", row);
+ },
+ disabled: (row) => {
+ return row.status !== 1
+ }
+ },
+ ],
+ align: "center"
+ },
]);
const tableData = ref([]);
const tableLoading = ref(false);
const page = reactive({
- current: 1,
- size: 100,
- total: 0,
+ current: 1,
+ size: 100,
+ total: 0,
});
const selectedRows = ref([]);
+const tableHeight = computed(() => "calc(100% -80px)");
+const handleReset = () => {
+ Object.keys(searchForm.value).forEach(key => {
+ searchForm.value[key] = ""
+ })
+}
// 琛ㄦ牸閫夋嫨鏁版嵁
const handleSelectionChange = (selection) => {
- selectedRows.value = selection;
+ selectedRows.value = selection;
};
const formDia = ref()
+
+// 瀛楀吀鑾峰彇
+/*
+post_sale_waiting_list 鏂板鐨勫敭鍚庡垎绫�
+degree_of_urgency 鏂板鐨勭揣鎬ョ▼搴�
+work_order_status 涓婚〉鐨勫伐鍗曠姸鎬�
+review_status 棣栭〉鐨勫鏍哥姸鎬�
+*/
+const { post_sale_waiting_list, degree_of_urgency, work_order_status, review_status } = proxy.useDict(
+ "post_sale_waiting_list",
+ "degree_of_urgency",
+ "work_order_status",
+ "review_status"
+);
+
+const classificationOptions = computed(() => post_sale_waiting_list?.value || []);
+const degreeOfUrgencyOptions = computed(() => degree_of_urgency?.value || []);
+const workOrderStatusOptions = computed(() => work_order_status?.value || []);
// 鏌ヨ鍒楄〃
/** 鎼滅储鎸夐挳鎿嶄綔 */
const handleQuery = () => {
- page.current = 1;
- getList();
+ page.current = 1;
+ getList();
};
const pagination = (obj) => {
- page.current = obj.page;
- page.size = obj.limit;
- getList();
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
};
const getList = () => {
- tableLoading.value = true;
- afterSalesServiceListPage({ ...searchForm.value, ...page }).then((res) => {
- tableLoading.value = false;
- tableData.value = res.data.records;
- page.total = res.data.total;
- });
+ tableLoading.value = true;
+ getSalesLedgerDetails()
+ afterSalesServiceListPage({ ...searchForm.value, ...page }).then((res) => {
+ tableLoading.value = false;
+ tableData.value = res.data.records;
+ page.total = res.data.total;
+ });
};
// 鎵撳紑寮规
const openForm = (type, row) => {
- nextTick(() => {
- formDia.value?.openDialog(type, row)
- })
+ nextTick(() => {
+ formDia.value?.openDialog(type, row)
+ })
};
-const handleDelete = () => {
- let ids = [];
- if (selectedRows.value.length > 0) {
- // 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
- const unauthorizedData = selectedRows.value.filter(item => item.checkUserId !== userStore.id);
- if (unauthorizedData.length > 0) {
- proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
- return;
- }
- ids = selectedRows.value.map((item) => item.id);
- } else {
- proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
- return;
- }
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- tableLoading.value = true;
- afterSalesServiceDelete(ids)
- .then((res) => {
- proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
- getList();
- })
- .finally(() => {
- tableLoading.value = false;
- });
- })
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
- });
+function handleDelete() {
+ let ids = [];
+ if (selectedRows.value.length > 0) {
+ // 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
+ const unauthorizedData = selectedRows.value.filter(item => item.checkUserId !== userStore.id);
+ if (unauthorizedData.length > 0) {
+ proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
+ return;
+ }
+ ids = selectedRows.value.map((item) => item.id);
+ } else {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ tableLoading.value = true;
+ afterSalesServiceDelete(ids)
+ .then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
};
// 瀵煎嚭
const handleOut = () => {
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- proxy.download("/afterSalesService/export", {}, "鍙嶉鐧昏.xlsx");
- })
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
- });
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ proxy.download("/afterSalesService/export", {}, "鍙嶉鐧昏.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
};
+ // 鑾峰彇缁熻鏁版嵁骞跺埛鏂伴《閮ㄥ崱鐗�
+ const getSalesLedgerDetails = () => {
+ getSalesLedgerDetail({}).then((res) => {
+ if (res.code === 200) {
+ statsList.value[0].count = res.data.filter((item) => item.status === 3)[0].count;
+ statsList.value[1].count = res.data.filter((item) => item.status === 2)[0].count;
+ statsList.value[2].count = res.data.filter((item) => item.status === 1)[0].count;
+
+ // });
+ }
+ });
+ }
+
+
+
onMounted(() => {
- getList();
+ getList();
+
});
</script>
-<style scoped>
+<style scoped lang="scss">
+.search-wrapper {
+ background: white;
+ padding: 1rem 1rem 0 1rem;
+ border: 8px;
+ border-radius: 16px;
+}
-</style>
\ No newline at end of file
+.expand-btn {
+ width: 100%;
+ padding: 20px; /* 涓婁笅宸﹀彸鍚�20px锛岀偣鍑昏繖涓寖鍥撮兘鑳借Е鍙戜簨浠� */
+ cursor: pointer; /* 榧犳爣鎮诞鏄剧ず鎵嬪瀷锛屾彁鍗囦綋楠� */
+ text-align: center;
+}
+
+.workorder-stats {
+ display: flex;
+ gap: 16px;
+ padding-bottom:1rem;
+ border-radius: 8px;
+}
+
+.stat-card {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 20px;
+ background-color: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
+}
+
+.stat-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 48px;
+ height: 48px;
+ border-radius: 8px;
+}
+
+.stat-info {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.stat-number {
+ font-size: 24px;
+ font-weight: 600;
+ color: #303133;
+ line-height: 1;
+}
+
+.stat-label {
+ font-size: 14px;
+ color: #909399;
+ line-height: 1;
+}
+.table_header{
+ padding-bottom: 10px;
+}
+
+.table_list {
+ height: calc(100vh - 380px);
+ min-height: 360px;
+ background: #fff;
+ margin-top: 20px;
+ display: flex;
+ flex-direction: column;
+}
+
+:deep(.table_list .pagination-container) {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ margin-top: auto;
+ padding: 12px 0 0;
+}
+
+:deep(.table_list .el-pagination) {
+ flex-wrap: nowrap;
+ justify-content: flex-end;
+ width: 100%;
+}
+</style>
--
Gitblit v1.9.3