gaoluyang
12 小时以前 e2317a1bcab0134f0c1b1aec99eb4d78dfddf1b8
Merge remote-tracking branch 'origin/dev_New' into dev_New
已添加3个文件
已修改7个文件
2411 ■■■■ 文件已修改
pnpm-lock.yaml 274 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/customerService/index.js 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/projectManagement/role.js 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue 135 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/afterSalesHandling/index.vue 293 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/feedbackRegistration/components/ProductSelectDialog.vue 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/feedbackRegistration/components/formDia.vue 452 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/feedbackRegistration/index.vue 640 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/projectManagement/roles/index.vue 295 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/hazardousMaterialsControl/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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
src/api/customerService/index.js
@@ -99,4 +99,34 @@
    url: '/afterSalesNearExpiryService/delete?ids=' + ids,
    method: 'delete',
  })
}
}
// æŸ¥è¯¢æ‰€æœ‰å®¢æˆ·ä¿¡æ¯
// /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,
    })
}
src/api/projectManagement/role.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
import request from "@/utils/request";
// é¡¹ç›®è§’色
// åˆ†é¡µæŸ¥è¯¢
export function findRoleListPage(query) {
    return request({
        url: "/projectManagement/roles/listPage",
        method: "get",
        params: query,
    });
}
export function createRole(params) {
    return request({
        url: "/projectManagement/roles/add",
        method: "post",
        data: params,
    });
}
export function updateRole(params) {
    return request({
        url: "/projectManagement/roles/update",
        method: "post",
        data: params,
    });
}
export function deleteRoles(params) {
    return request({
        url: "/projectManagement/roles/delete",
        method: "delete",
        data: params,
    });
}
src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue
@@ -90,7 +90,7 @@
                  style="width: 100%"
              >
                <el-option
                    v-for="time in timeOptions"
                    v-for="time in startTimeOptions"
                    :key="time.value"
                    :label="time.label"
                    :value="time.value"
@@ -106,7 +106,7 @@
                  style="width: 100%"
              >
                <el-option
                    v-for="time in timeOptions"
                    v-for="time in endTimeOptions"
                    :key="time.value"
                    :label="time.label"
                    :value="time.value"
@@ -152,7 +152,7 @@
</template>
<script setup>
import {ref, reactive, onMounted} from 'vue'
import {ref, reactive, onMounted, computed, watch} from 'vue'
import {ElMessage} from 'element-plus'
import {Plus, Document, Promotion, Bell} from '@element-plus/icons-vue'
import {getRoomEnum, saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js'
@@ -196,17 +196,6 @@
  description: ''
})
// è¡¨å•校验规则
const rules = {
  title: [{required: true, message: '请输入会议主题', trigger: 'blur'}],
  roomId: [{required: true, message: '请选择会议室', trigger: 'change'}],
  host: [{required: true, message: '请输入主持人', trigger: 'blur'}],
  meetingDate: [{required: true, message: '请选择会议日期', trigger: 'change'}],
  startTime: [{required: true, message: '请选择开始时间', trigger: 'change'}],
  endTime: [{required: true, message: '请选择结束时间', trigger: 'change'}],
  participants: [{required: true, message: '请选择参会人员', trigger: 'change'}]
}
// è¡¨å•引用
const meetingFormRef = ref(null)
@@ -219,18 +208,106 @@
// æ—¶é—´é€‰é¡¹ï¼ˆä»¥åŠå°æ—¶ä¸ºé—´éš”)
const timeOptions = ref([])
const getTimeInMinutes = (time) => {
  if (!time) return -1
  const [hour, minute] = time.split(':').map(Number)
  return hour * 60 + minute
}
const isToday = (dateText) => {
  if (!dateText) return false
  const [year, month, day] = dateText.split('-').map(Number)
  const now = new Date()
  return year === now.getFullYear() && month === now.getMonth() + 1 && day === now.getDate()
}
const validateStartTime = (_rule, value, callback) => {
  if (!value) {
    callback()
    return
  }
  if (isToday(meetingForm.meetingDate)) {
    const now = new Date()
    const currentMinutes = now.getHours() * 60 + now.getMinutes()
    if (getTimeInMinutes(value) > currentMinutes) {
      callback(new Error('当天开始时间不能晚于当前时间'))
      return
    }
  }
  callback()
}
const validateEndTime = (_rule, value, callback) => {
  if (!value || !meetingForm.startTime) {
    callback()
    return
  }
  if (getTimeInMinutes(value) <= getTimeInMinutes(meetingForm.startTime)) {
    callback(new Error('结束时间必须大于开始时间'))
    return
  }
  callback()
}
// è¡¨å•校验规则
const rules = {
  title: [{required: true, message: '请输入会议主题', trigger: 'blur'}],
  roomId: [{required: true, message: '请选择会议室', trigger: 'change'}],
  host: [{required: true, message: '请输入主持人', trigger: 'blur'}],
  meetingDate: [{required: true, message: '请选择会议日期', trigger: 'change'}],
  startTime: [
    {required: true, message: '请选择开始时间', trigger: 'change'},
    {validator: validateStartTime, trigger: 'change'}
  ],
  endTime: [
    {required: true, message: '请选择结束时间', trigger: 'change'},
    {validator: validateEndTime, trigger: 'change'}
  ],
  participants: [{required: true, message: '请选择参会人员', trigger: 'change'}]
}
const startTimeOptions = computed(() => {
  if (!isToday(meetingForm.meetingDate)) {
    return timeOptions.value
  }
  const now = new Date()
  const currentMinutes = now.getHours() * 60 + now.getMinutes()
  return timeOptions.value.filter(item => getTimeInMinutes(item.value) <= currentMinutes)
})
const endTimeOptions = computed(() => {
  if (!meetingForm.startTime) {
    return timeOptions.value
  }
  const startMinutes = getTimeInMinutes(meetingForm.startTime)
  return timeOptions.value.filter(item => getTimeInMinutes(item.value) > startMinutes)
})
// åˆå§‹åŒ–时间选项
const initTimeOptions = () => {
  const options = []
  const now = new Date()
  const currentHour = now.getHours()
  const currentMinute = now.getMinutes()
  // meetingDate æ˜¯ "yyyy-MM-dd"
  const meetingDate = new Date(meetingForm.meetingDate)
  const isSameDay =
    now.getFullYear() === meetingDate.getFullYear() &&
    now.getMonth() === meetingDate.getMonth() &&
    now.getDate() === meetingDate.getDate()
  console.log('是否同一天:', isSameDay)
  for (let hour = 8; hour <= 18; hour++) {
    // å¼€å§‹æ—¶é—´å¿…须晚于当前时间
    if (hour < currentHour) {
    if (hour < currentHour && isSameDay) {
      continue
    }
    if (hour === currentHour && currentMinute > 30) {
    if (hour === currentHour && currentMinute > 30 && isSameDay) {
      continue
    }
    // æ¯ä¸ªå°æ—¶æ·»åŠ ä¸¤ä¸ªé€‰é¡¹ï¼šæ•´ç‚¹å’ŒåŠç‚¹
@@ -249,6 +326,32 @@
  timeOptions.value = options
}
watch(() => meetingForm.meetingDate, () => {
  if (meetingForm.startTime && !startTimeOptions.value.some(item => item.value === meetingForm.startTime)) {
    meetingForm.startTime = ''
  }
  if (meetingForm.endTime && !endTimeOptions.value.some(item => item.value === meetingForm.endTime)) {
    meetingForm.endTime = ''
  }
  if (meetingForm.startTime) {
    meetingFormRef.value?.validateField('startTime')
  }
  if (meetingForm.endTime) {
    meetingFormRef.value?.validateField('endTime')
  }
  initTimeOptions()
})
watch(() => meetingForm.startTime, () => {
  if (meetingForm.endTime && getTimeInMinutes(meetingForm.endTime) <= getTimeInMinutes(meetingForm.startTime)) {
    meetingForm.endTime = ''
  }
  if (meetingForm.endTime) {
    meetingFormRef.value?.validateField('endTime')
  }
})
// ç¦ç”¨æ—¥æœŸï¼ˆç¦ç”¨ä»Šå¤©ä¹‹å‰çš„æ—¥æœŸï¼‰
const disabledDate = (time) => {
  // ç¦ç”¨ä»Šå¤©ä¹‹å‰çš„æ—¥æœŸ
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>
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>
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>
.descriptions::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 4px;
  height: 1rem;
  background-color: #002FA7; /* Element é»˜è®¤çº¢è‰² */
  border-radius: 2px;
}
</style>
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>
.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>
src/views/projectManagement/roles/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,295 @@
<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryRef" v-show="showSearch" :inline="true" label-width="68px">
      <el-form-item label="角色名称" prop="roleName">
        <el-input
            v-model="queryParams.name"
            placeholder="请输入角色名称"
            clearable
            style="width: 240px"
            @keyup.enter="handleQuery"
        />
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-select
            v-model="queryParams.status"
            placeholder="角色状态"
            clearable
            style="width: 240px"
        >
          <el-option label="启用" value="0" />
          <el-option label="禁用" value="1" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>
    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
            type="primary"
            plain
            icon="Plus"
            @click="handleAdd"
            v-hasPermi="['system:role:add']"
        >新增</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
            type="success"
            plain
            icon="Edit"
            :disabled="single"
            @click="handleUpdate"
            v-hasPermi="['system:role:edit']"
        >修改</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
            type="danger"
            plain
            icon="Delete"
            :disabled="multiple"
            @click="handleDelete"
            v-hasPermi="['system:role:remove']"
        >删除</el-button>
      </el-col>
      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
    <!-- è¡¨æ ¼æ•°æ® -->
    <el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="角色编号" prop="no" />
      <el-table-column label="角色名称" prop="name" :show-overflow-tooltip="true" />
      <el-table-column label="状态" align="center" width="100">
        <template #default="scope">
          <el-switch
              v-model="scope.row.status"
              :active-value="0"
              :inactive-value="1"
              @change="handleStatusChange(scope.row)"
          ></el-switch>
        </template>
      </el-table-column>
      <el-table-column label="创建时间" align="center" prop="createTime">
        <template #default="scope">
          <span>{{ parseTime(scope.row.createTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column fixed="right" label="操作" align="center" width="120">
        <template #default="scope">
          <el-tooltip content="修改" placement="top" v-if="scope.row.roleId !== 1">
            <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
          </el-tooltip>
          <el-tooltip content="删除" placement="top" v-if="scope.row.roleId !== 1">
            <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button>
          </el-tooltip>
        </template>
      </el-table-column>
    </el-table>
    <pagination
        v-show="total > 0"
        :total="total"
        v-model:page="queryParams.current"
        v-model:limit="queryParams.size"
        @pagination="getList"
    />
    <!-- æ·»åŠ æˆ–ä¿®æ”¹è§’è‰²é…ç½®å¯¹è¯æ¡† -->
    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
      <el-form ref="roleRef" :model="form" :rules="rules" label-width="100px">
        <el-form-item label="角色编号" prop="no">
          <el-input
              v-model="form.no"
              style="max-width: 600px"
              :placeholder="form.isDefaultNo ? '使用系统编号' : '请输入角色编号'"
              :disabled="form.isDefaultNo"
          >
            <template #append>
              <el-checkbox v-model="form.isDefaultNo" size="large" />
            </template>
          </el-input>
        </el-form-item>
        <el-form-item label="角色名称" prop="name">
          <el-input v-model="form.name" placeholder="请输入角色名称" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">ç¡® å®š</el-button>
          <el-button @click="cancel">取 æ¶ˆ</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup name="Role">
import {onMounted} from "vue";
import {createRole, deleteRoles, findRoleListPage, updateRole} from "@/api/projectManagement/role.js";
const { proxy } = getCurrentInstance()
const { sys_normal_disable } = proxy.useDict("sys_normal_disable")
const roleList = ref([])
const open = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const title = ref("")
const editingId = ref(undefined)
const form = reactive({
  isDefaultNo: true,
  no: "",
  name: "",
});
const rules = computed(() => ({
  no: [{ required: !form.isDefaultNo, message: "角色编号不能为空", trigger: "blur" }],
  name: [{ required: true, message: "角色名称不能为空", trigger: "blur" }],
}));
const data = reactive({
  queryParams: {
    current: 1,
    size: 10,
    name: undefined,
    status: undefined
  },
})
const { queryParams } = toRefs(data)
/** æŸ¥è¯¢è§’色列表 */
function getList() {
  loading.value = true
  findRoleListPage(queryParams.value).then(res => {
    roleList.value = res.data.records
    total.value = res.data.total
    loading.value = false
  })
}
/** æœç´¢æŒ‰é’®æ“ä½œ */
function handleQuery() {
  queryParams.value.current = 1
  getList()
}
/** é‡ç½®æŒ‰é’®æ“ä½œ */
function resetQuery() {
  proxy.resetForm("queryRef")
  queryParams.value.name = undefined
  queryParams.value.status = undefined
  handleQuery()
}
/** åˆ é™¤æŒ‰é’®æ“ä½œ */
function handleDelete(row) {
  let roleIds = []
  if (row.id) {
    roleIds = [row.id]
  } else {
    roleIds = ids.value
  }
  proxy.$modal.confirm('确定要删除该数据吗?').then(function () {
    return deleteRoles(roleIds)
  }).then(() => {
    getList()
    proxy.$modal.msgSuccess("删除成功")
  }).catch(() => {})
}
/** å¤šé€‰æ¡†é€‰ä¸­æ•°æ® */
function handleSelectionChange(selection) {
  ids.value = selection.map(item => item.id)
  single.value = selection.length != 1
  multiple.value = !selection.length
}
/** è§’色状态修改 */
function handleStatusChange(row) {
  let text = row.status === 0 ? "启用" : "停用"
  proxy.$modal.confirm('确认要"' + text + '""' + row.name + '"角色吗?').then(function () {
    return updateRole(row)
  }).then(() => {
    proxy.$modal.msgSuccess(text + "成功")
  }).catch(function () {
    row.status = row.status === 0 ? 1 : 0
  })
}
/** é‡ç½®æ–°å¢žçš„表单以及其他数据  */
function reset() {
  // é‡ç½®è¡¨å•数据
  Object.assign(form, {
    isDefaultNo: true,
    no: "",
    name: "",
  })
  editingId.value = undefined
}
/** æ·»åŠ è§’è‰² */
function handleAdd() {
  reset()
  open.value = true
  title.value = "添加角色"
}
/** ä¿®æ”¹è§’色 */
function handleUpdate(row) {
  reset()
  editingId.value = row.id
  form.no = row.no
  form.name = row.name
  title.value = "修改角色"
  open.value = true
}
/** æäº¤æŒ‰é’® */
function submitForm() {
  proxy.$refs["roleRef"].validate(valid => {
    if (valid) {
      if (editingId.value) {
        const data = {
          id: editingId.value,
          ...form
        }
        updateRole(data).then(response => {
          proxy.$modal.msgSuccess("修改成功")
        }).finally(() => {
          proxy.$refs["roleRef"].resetFields()
          open.value = false
          getList()
        })
      } else {
        createRole(form).then(response => {
          proxy.$modal.msgSuccess("新增成功")
        }).finally(() => {
          proxy.$refs["roleRef"].resetFields()
          open.value = false
          getList()
        })
      }
    }
  })
}
/** å–消按钮 */
function cancel() {
  open.value = false
  reset()
}
onMounted(() => {
  getList();
});
</script>
src/views/safeProduction/hazardousMaterialsControl/index.vue
@@ -653,15 +653,7 @@
  // é€‰æ‹©å˜åŒ–处理
  const handleSelectionChange = selection => {
    // ä¸»è¡¨æ ¼ä¹Ÿåªä¿ç•™æœ€åŽä¸€ä¸ªé€‰ä¸­çš„项
    if (selection.length > 1) {
      const lastSelected = selection[selection.length - 1];
      selectedIds.value = [lastSelected.id];
    } else if (selection.length === 1) {
      selectedIds.value = [selection[0].id];
    } else {
      selectedIds.value = [];
    }
    selectedIds.value = selection.map(item => item.id);
  };
  // æ‰“开表单