zouyu
19 小时以前 32f10392ab00b07e85c0b4229037c30af7c3f28e
浪潮对接单点登录:mis调整2
已添加7个文件
已重命名2个文件
已修改17个文件
已删除1个文件
5406 ■■■■ 文件已修改
package.json 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pnpm-lock.yaml 274 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/personnelManagement/onboarding.js 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue 134 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index.vue 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/freeApprovalProcess/components/approvalDia.vue 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/freeApprovalProcess/components/infoFormDia.vue 466 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/freeApprovalProcess/fileList.vue 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/freeApprovalProcess/index.vue 362 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/freeApprovalProcess/index2.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/dispatchLog/index.vue 393 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/issueManagement/index.vue 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/index.vue 228 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/index.vue 398 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/contractManagement/components/formDia.vue 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/contractManagement/filesDia.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/contractManagement/index.vue 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/dimission/components/formDia.vue 445 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/dimission/index.vue 69 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/employeeRecord/components/RenewContract.vue 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/employeeRecord/components/formDia.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/employeeRecord/index.vue 108 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/onboarding/components/formDia.vue 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/onboarding/index.vue 348 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/payrollManagement/components/formDia.vue 184 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/payrollManagement/index.vue 497 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json
@@ -21,6 +21,7 @@
    "@vue-office/excel": "^1.7.14",
    "@vueup/vue-quill": "1.2.0",
    "@vueuse/core": "10.11.0",
    "autofit.js": "^3.2.8",
    "axios": "0.28.1",
    "clipboard": "2.0.11",
    "dayjs": "^1.11.13",
@@ -33,11 +34,13 @@
    "jsencrypt": "3.3.2",
    "nprogress": "0.2.0",
    "pinia": "2.1.7",
    "print-js": "^1.6.0",
    "qrcode": "^1.5.4",
    "sortablejs": "^1.15.6",
    "splitpanes": "3.1.5",
    "vue": "3.4.31",
    "vue-cropper": "1.1.1",
    "vue-easy-lightbox": "^1.19.0",
    "vue-esign": "^1.1.4",
    "vue-router": "4.4.0",
    "vuedraggable": "4.1.0"
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/personnelManagement/onboarding.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
import request from "@/utils/request";
// æŸ¥è¯¢äººå‘˜å…¥èŒåˆ—表
export function staffJoinListPage(query) {
    return request({
        url: "/staff/staffJoinLeaveRecord/listPage",
        method: "get",
        params: query,
    });
}
// æ–°å¢žäººå‘˜å…¥èŒ
export function staffJoinAdd(query) {
    return request({
        url: "/staff/staffJoinLeaveRecord/add",
        method: "post",
        data: query,
    });
}
// ä¿®æ”¹äººå‘˜å…¥èŒ
export function staffJoinUpdate(query) {
    return request({
        url: "/staff/staffJoinLeaveRecord/update",
        method: "post",
        data: query,
    });
}
// æŸ¥è¯¢å‘˜å·¥å…¥èŒä¿¡æ¯
export function getStaffJoinInfo(query) {
    return request({
        url: "/staff/staffJoinLeaveRecord/" + query,
        method: "get",
        data: query,
    });
}
// åˆ é™¤å‘˜å·¥
export function staffJoinDel(query) {
    return request({
        url: "/staff/staffJoinLeaveRecord/del",
        method: "delete",
        data: query,
    });
}
export function getStaffOnJob() {
    return request({
        url: "/staff/staffOnJob/list",
        method: "get",
    });
}
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -1,78 +1,78 @@
<template>
  <div>
    <el-dialog
      v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新增审批流程' : '编辑审批流程'"
      width="700px"
      @close="closeDia"
        v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增审批流程' : '编辑审批流程'"
        width="700px"
        @close="closeDia"
    >
            <el-form :model="form" :rules="rules" label-width="140px" label-position="top" ref="formRef">
                <el-row>
                    <el-col :span="24">
                        <el-form-item label="流程编号:" prop="approveId">
                            <el-input v-model="form.approveId" placeholder="自动编号" clearable disabled/>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="24">
                        <el-form-item label="申请部门:" prop="approveDeptId">
                            <el-select
                                disabled
                                v-model="form.approveDeptId"
                                placeholder="选择部门"
                            >
                                <el-option
                                    v-for="user in productOptions"
                                    :key="user.deptId"
                                    :label="user.deptName"
                                    :value="user.deptId"
                                />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row v-if="!isQuotationApproval">
                    <el-col :span="24">
                        <el-form-item label="审批事由:" prop="approveReason">
                            <el-input v-model="form.approveReason" placeholder="请输入" clearable type="textarea" disabled/>
                        </el-form-item>
                    </el-col>
                </el-row>
                <!-- å®¡æ‰¹äººé€‰æ‹©ï¼ˆåŠ¨æ€èŠ‚ç‚¹ï¼‰ -->
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="申请人:" prop="approveUser">
                            <el-select
                                v-model="form.approveUser"
                                placeholder="选择人员"
                                disabled
                            >
                                <el-option
                                    v-for="user in userList"
                                    :key="user.userId"
                                    :label="user.nickName"
                                    :value="user.userId"
                                />
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="申请日期:" prop="approveTime">
                            <el-date-picker
                                v-model="form.approveTime"
                                type="date"
                                placeholder="请选择日期"
                                value-format="YYYY-MM-DD"
                                format="YYYY-MM-DD"
                                clearable
                                style="width: 100%"
                                disabled
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
      <el-form :model="form" :rules="rules" label-width="140px" label-position="top" ref="formRef">
        <el-row>
          <el-col :span="24">
            <el-form-item label="流程编号:" prop="approveId">
              <el-input v-model="form.approveId" placeholder="自动编号" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="申请部门:" prop="approveDeptId">
              <el-select
                  disabled
                  v-model="form.approveDeptId"
                  placeholder="选择部门"
              >
                <el-option
                    v-for="user in productOptions"
                    :key="user.deptId"
                    :label="user.deptName"
                    :value="user.deptId"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row v-if="!isQuotationApproval">
          <el-col :span="24">
            <el-form-item label="审批事由:" prop="approveReason">
              <el-input v-model="form.approveReason" placeholder="请输入" clearable type="textarea" disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <!-- å®¡æ‰¹äººé€‰æ‹©ï¼ˆåŠ¨æ€èŠ‚ç‚¹ï¼‰ -->
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="申请人:" prop="approveUser">
              <el-select
                  v-model="form.approveUser"
                  placeholder="选择人员"
                  disabled
              >
                <el-option
                    v-for="user in userList"
                    :key="user.userId"
                    :label="user.nickName"
                    :value="user.userId"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="申请日期:" prop="approveTime">
              <el-date-picker
                  v-model="form.approveTime"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
                  disabled
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <!-- æŠ¥ä»·å®¡æ‰¹ï¼šå±•示报价详情(复用销售报价“查看详情对话框”内容结构) -->
      <div v-if="isQuotationApproval" style="margin: 10px 0 18px;">
@@ -124,19 +124,19 @@
      <el-form :model="{ activities }" ref="formRef" label-position="top">
        <el-steps :active="getActiveStep()" finish-status="success" process-status="process" align-center direction="vertical">
          <el-step
            v-for="(activity, index) in activities"
            :key="index"
                        finish-status="success"
            :title="getNodeTitle(index, activities.length)"
            :description="activity.approveNodeUser"
            :icon="getNodeIcon(activity, index)"
              v-for="(activity, index) in activities"
              :key="index"
              finish-status="success"
              :title="getNodeTitle(index, activities.length)"
              :description="activity.approveNodeUser"
              :icon="getNodeIcon(activity, index)"
          >
                        <template #icon>
                            <el-icon v-if="activity.approveNodeStatus === 2" color="red" :size="22"><WarningFilled /></el-icon>
                            <el-icon v-else-if="activity.isShen" color="#1890ff" :size="22"><Edit /></el-icon>
                            <el-icon v-else-if="activity.approveNodeStatus === 1" color="#67C23A" :size="26"><Check /></el-icon>
                            <el-icon v-else color="#C0C4CC" :size="22"><MoreFilled /></el-icon>
                        </template>
            <template #icon>
              <el-icon v-if="activity.approveNodeStatus === 2" color="red" :size="22"><WarningFilled /></el-icon>
              <el-icon v-else-if="activity.isShen" color="#1890ff" :size="22"><Edit /></el-icon>
              <el-icon v-else-if="activity.approveNodeStatus === 1" color="#67C23A" :size="26"><Check /></el-icon>
              <el-icon v-else color="#C0C4CC" :size="22"><MoreFilled /></el-icon>
            </template>
            <template #title>
              <span style="color: #000000">{{ getNodeTitle(index, activities.length) }}</span>
            </template>
@@ -152,12 +152,12 @@
              </div>
              <div v-if="!activity.isShen" class="node-reason">
                <span>签名:</span>
                                <img :src="activity.urlTem" class="signImg" alt="" v-if="activity.urlTem"/>
                <img :src="activity.urlTem" class="signImg" alt="" v-if="activity.urlTem"/>
              </div>
              <div v-else-if="activity.isShen">
                <el-form-item
                  :prop="'activities.' + index + '.approveNodeReason'"
                  :rules="[{ required: true, message: '审批意见不能为空', trigger: 'blur' }]"
                    :prop="'activities.' + index + '.approveNodeReason'"
                    :rules="[{ required: true, message: '审批意见不能为空', trigger: 'blur' }]"
                >
                  <el-input v-model="activity.approveNodeReason" clearable type="textarea" :disabled="operationType === 'view'"></el-input>
                </el-form-item>
@@ -178,11 +178,11 @@
</template>
<script setup>
import { computed, getCurrentInstance, nextTick, reactive, ref, toRefs } from "vue";
import { computed, getCurrentInstance, reactive, ref, toRefs } from "vue";
import {
    approveProcessDetails,
    getDept,
    updateApproveNode
  approveProcessDetails,
  getDept,
  updateApproveNode
} from "@/api/collaborativeApproval/approvalProcess.js";
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
@@ -210,14 +210,14 @@
const isQuotationApproval = computed(() => Number(props.approveType) === 6)
const data = reactive({
    form: {
        approveTime: "",
        approveId: "",
        approveUser: "",
        approveDeptId: "",
        approveReason: "",
        checkResult: "",
    },
  form: {
    approveTime: "",
    approveId: "",
    approveUser: "",
    approveDeptId: "",
    approveReason: "",
    checkResult: "",
  },
  rules: {
    // ä½¿ç”¨éƒ¨é—¨ID做必填校验,避免名称未同步导致误报
    approveDeptId: [{ required: true, message: "请选择申请部门", trigger: "change" }],
@@ -251,35 +251,11 @@
  operationType.value = type;
  dialogFormVisible.value = true;
  currentQuotation.value = {}
    userListNoPageByTenantId().then((res) => {
        userList.value = res.data;
    });
    form.value = {...row}
    // ç«‹å³æ¸…除表单验证状态(因为字段是disabled的,不需要验证)
    nextTick(() => {
        if (formRef.value) {
            formRef.value.clearValidate();
        }
    });
    // ç¡®ä¿é€‰é¡¹åŠ è½½å®ŒæˆåŽå†åŒ¹é…å€¼ç±»åž‹
    getProductOptions().then(() => {
        // ç¡®ä¿å€¼ç±»åž‹åŒ¹é…ï¼ˆå¦‚果选项已加载)
        if (productOptions.value.length > 0 && form.value.approveDeptId) {
            const matchedOption = productOptions.value.find(opt =>
                opt.deptId == form.value.approveDeptId ||
                String(opt.deptId) === String(form.value.approveDeptId)
            );
            if (matchedOption) {
                form.value.approveDeptId = matchedOption.deptId;
            }
        }
        // å†æ¬¡æ¸…除验证,确保选项加载后值匹配正确
        nextTick(() => {
            if (formRef.value) {
                formRef.value.clearValidate();
            }
        });
    });
  userListNoPageByTenantId().then((res) => {
    userList.value = res.data;
  });
  form.value = {...row}
  getProductOptions()
  // æŠ¥ä»·å®¡æ‰¹ï¼šç”¨å®¡æ‰¹äº‹ç”±å­—段承载的“报价单号”去查报价列表
  if (isQuotationApproval.value) {
@@ -299,11 +275,11 @@
    activities.value = res.data
    // å¢žåŠ isApproval字段
    activities.value.forEach(item => {
            if (item.url && item.url.includes('word')) {
                item.urlTem = item.url.replaceAll('word', 'img')
            } else {
                item.urlTem = item.url
            }
      if (item.url && item.url.includes('word')) {
        item.urlTem = item.url.replaceAll('word', 'img')
      } else {
        item.urlTem = item.url
      }
      if (item.approveNodeStatus === 2) {
        item.isApproval = '已驳回';
      } else if (item.approveNodeStatus === 1) {
@@ -315,26 +291,17 @@
  })
}
const getProductOptions = () => {
    return getDept().then((res) => {
        productOptions.value = res.data;
    });
  getDept().then((res) => {
    productOptions.value = res.data;
  });
};
// æäº¤å®¡æ‰¹
const submitForm = (status) => {
  const filteredActivities = activities.value.filter(activity => activity.isShen);
  if (!filteredActivities || filteredActivities.length === 0) {
    proxy.$modal.msgError("未找到待审批的节点");
    return;
  }
  const currentActivity = filteredActivities[0];
  if (!currentActivity) {
    proxy.$modal.msgError("未找到待审批的节点");
    return;
  }
  currentActivity.approveNodeStatus = status;
  filteredActivities[0].approveNodeStatus = status;
  // åˆ¤æ–­æ˜¯å¦ä¸ºæœ€åŽä¸€æ­¥
  const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length-1;
  updateApproveNode({ ...currentActivity, isLast }).then(() => {
  updateApproveNode({ ...filteredActivities[0], isLast }).then(() => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
  });
@@ -373,14 +340,14 @@
  margin: 10px 0;
}
.user-avatar {
    cursor: pointer;
    width: 30px;
    height: 30px;
    border-radius: 50px;
  cursor: pointer;
  width: 30px;
  height: 30px;
  border-radius: 50px;
}
.signImg {
    cursor: pointer;
    width: 200px;
    height: 60px;
  cursor: pointer;
  width: 200px;
  height: 60px;
}
</style>
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
@@ -16,20 +16,21 @@
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="申请部门:" prop="approveDeptName">
<!--              <el-input v-model="form.approveDeptName" placeholder="请输入" clearable/>-->
                            <el-select
                                v-model="form.approveDeptId"
                                placeholder="选择部门"
                @change="handleDeptChange"
                            >
                                <el-option
                                    v-for="user in productOptions"
                                    :key="user.deptId"
                                    :label="user.deptName"
                                    :value="user.deptId"
                                />
                            </el-select>
            <!-- ç”³è¯·éƒ¨é—¨ï¼šæ ¡éªŒä½¿ç”¨éƒ¨é—¨ID,便于下拉选择后立即通过校验 -->
            <el-form-item label="申请部门:" prop="approveDeptId">
              <!--              <el-input v-model="form.approveDeptName" placeholder="请输入" clearable/>-->
              <el-select
                  v-model="form.approveDeptId"
                  placeholder="选择部门"
                  @change="handleDeptChange"
              >
                <el-option
                    v-for="user in productOptions"
                    :key="user.deptId"
                    :label="user.deptName"
                    :value="user.deptId"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
@@ -88,10 +89,10 @@
        <!-- å‡ºå·®åœ°ç‚¹ï¼ˆä»…当 approveType ä¸º 3 æ—¶æ˜¾ç¤ºï¼‰ -->
        <el-row v-if="props.approveType == 3">
          <el-col :span="24">
            <el-form-item label="出差地点:" prop="location">
            <el-form-item label="备注:" prop="location">
              <el-input
                  v-model="form.location"
                  placeholder="请输入出差地点"
                  placeholder="请输入备注"
                  clearable
              />
            </el-form-item>
@@ -107,32 +108,32 @@
              </template>
              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
                <div
                  v-for="(node, index) in approverNodes"
                  :key="node.id"
                  style="margin-right: 30px; text-align: center; margin-bottom: 10px;"
                    v-for="(node, index) in approverNodes"
                    :key="node.id"
                    style="margin-right: 30px; text-align: center; margin-bottom: 10px;"
                >
                  <div>
                    <span>审批人</span>
                    â†’
                  </div>
                  <el-select
                    v-model="node.userId"
                    placeholder="选择人员"
                    style="width: 120px; margin-bottom: 8px;"
                      v-model="node.userId"
                      placeholder="选择人员"
                      style="width: 120px; margin-bottom: 8px;"
                  >
                    <el-option
                      v-for="user in userList"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                        v-for="user in userList"
                        :key="user.userId"
                        :label="user.nickName"
                        :value="user.userId"
                    />
                  </el-select>
                  <div>
                    <el-button
                      type="danger"
                      size="small"
                      @click="removeApproverNode(index)"
                      v-if="approverNodes.length > 1"
                        type="danger"
                        size="small"
                        @click="removeApproverNode(index)"
                        v-if="approverNodes.length > 1"
                    >删除</el-button>
                  </div>
                </div>
@@ -143,20 +144,20 @@
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="申请人:" prop="approveUser">
                            <el-select
                                v-model="form.approveUser"
                                placeholder="选择人员"
                filterable
                default-first-option
                :reserve-keyword="false"
                            >
                                <el-option
                                    v-for="user in userList"
                                    :key="user.userId"
                                    :label="user.nickName"
                                    :value="user.userId"
                                />
                            </el-select>
              <el-select
                  v-model="form.approveUser"
                  placeholder="选择人员"
                  filterable
                  default-first-option
                  :reserve-keyword="false"
              >
                <el-option
                    v-for="user in userList"
                    :key="user.userId"
                    :label="user.nickName"
                    :value="user.userId"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -234,7 +235,7 @@
    approveTime: "",
    approveId: "",
    approveUser: "",
        approveDeptId: "",
    approveDeptId: "",
    approveDeptName: "",
    approveReason: "",
    checkResult: "",
@@ -246,10 +247,11 @@
    location: "" // å‡ºå·®åœ°ç‚¹
  },
  rules: {
    approveTime: [{ required: false, message: "请输入", trigger: "change" },],
    approveTime: [{ required: false, message: "请输入", trigger: "change" }],
    approveId: [{ required: false, message: "请输入", trigger: "blur" }],
    approveUser: [{ required: false, message: "请输入", trigger: "blur" }],
    approveDeptName: [{ required: true, message: "请输入", trigger: "blur" }],
    // ä½¿ç”¨éƒ¨é—¨ID做必填校验,避免名称未同步导致误报
    approveDeptId: [{ required: true, message: "请选择申请部门", trigger: "change" }],
    approveReason: [{ required: true, message: "请输入", trigger: "blur" }],
    checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
    startDate: [{ required: true, message: "请选择请假开始时间", trigger: "change" }],
@@ -295,32 +297,30 @@
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
    userListNoPageByTenantId().then((res) => {
  userListNoPageByTenantId().then((res) => {
    userList.value = res.data;
  });
    form.value = {}
    approverNodes.value = [
        { id: 1, userId: null }
    ]
  getProductOptions();
  form.value = {}
  approverNodes.value = [
    { id: 1, userId: null }
  ]
  form.value.approveUser = userStore.id;
  form.value.approveTime = getCurrentDate();
  // èŽ·å–å½“å‰ç”¨æˆ·ä¿¡æ¯å¹¶è®¾ç½®éƒ¨é—¨ID
  form.value.approveDeptId = userStore.currentDeptId
  // åŠ è½½éƒ¨é—¨é€‰é¡¹ï¼Œå¹¶åœ¨åŠ è½½å®ŒæˆåŽè®¾ç½®éƒ¨é—¨åç§°
  getProductOptions();
  if (operationType.value === 'edit') {
    fileList.value = row.commonFileList
    form.value.tempFileIds = fileList.value.map(file => file.id)
        currentApproveStatus.value = row.approveStatus
    currentApproveStatus.value = row.approveStatus
    approveProcessGetInfo({id: row.approveId,approveReason: '1'}).then(res => {
            form.value = {...res.data}
      form.value = {...res.data}
      // åæ˜¾å®¡æ‰¹äºº
      if (res.data && res.data.approveUserIds) {
        const userIds = res.data.approveUserIds.split(',')
        approverNodes.value = userIds.map((userId, idx) => ({
          id: idx + 1,
          id: idx + 1,
          userId: parseInt(userId.trim())
        }))
        nextApproverId = userIds.length + 1
@@ -332,18 +332,8 @@
  }
}
const getProductOptions = () => {
  return getDept().then((res) => {
  getDept().then((res) => {
    productOptions.value = res.data;
    // å¦‚果已有部门ID,自动设置部门名称(用于验证)
    if (form.value.approveDeptId && productOptions.value.length > 0) {
      const matchedDept = productOptions.value.find(dept =>
        dept.deptId == form.value.approveDeptId ||
        String(dept.deptId) === String(form.value.approveDeptId)
      );
      if (matchedDept) {
        form.value.approveDeptName = matchedDept.deptName;
      }
    }
  });
};
function convertIdToValue(data) {
@@ -356,7 +346,7 @@
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
}
@@ -473,4 +463,4 @@
<style scoped>
</style>
</style>
src/views/collaborativeApproval/approvalProcess/index.vue
@@ -2,15 +2,11 @@
  <div class="app-container">
    <!-- æ ‡ç­¾é¡µåˆ‡æ¢ä¸åŒçš„审批类型 -->
    <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="approval-tabs">
      <el-tab-pane label="公出管理" name="1"></el-tab-pane>
      <el-tab-pane label="请假管理" name="2"></el-tab-pane>
      <el-tab-pane label="出差管理" name="3"></el-tab-pane>
      <el-tab-pane label="报销管理" name="4"></el-tab-pane>
      <el-tab-pane label="协同审批" name="1"></el-tab-pane>
      <el-tab-pane label="销售审批" name="3"></el-tab-pane>
      <el-tab-pane label="采购审批" name="5"></el-tab-pane>
      <el-tab-pane label="报价审批" name="6"></el-tab-pane>
      <el-tab-pane label="发货审批" name="7"></el-tab-pane>
    </el-tabs>
    <div class="search_form">
      <div>
        <span class="search_title">流程编号:</span>
@@ -23,30 +19,21 @@
            :prefix-icon="Search"
        />
        <span class="search_title ml10">审批状态:</span>
                <el-select v-model="searchForm.approveStatus" clearable @change="handleQuery" style="width: 240px">
                    <el-option label="待审核" :value="0" />
                    <el-option label="审核中" :value="1" />
                    <el-option label="审核完成" :value="2" />
                    <el-option label="审核未通过" :value="3" />
                    <el-option label="已重新提交" :value="4" />
                </el-select>
        <el-select v-model="searchForm.approveStatus" clearable @change="handleQuery" style="width: 240px">
          <el-option label="待审核" :value="0" />
          <el-option label="审核中" :value="1" />
          <el-option label="审核完成" :value="2" />
          <el-option label="审核未通过" :value="3" />
          <el-option label="已重新提交" :value="4" />
        </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
        >搜索</el-button
        >
      </div>
      <div>
        <el-button
          type="primary"
          @click="openForm('add')"
          v-if="currentApproveType !== 6 && currentApproveType !== 7"
        >新增</el-button>
        <el-button type="primary" @click="openForm('add')" v-if="currentApproveType !== 6">新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button
          type="danger"
          plain
          @click="handleDelete"
          v-if="currentApproveType !== 7"
        >删除</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
@@ -102,8 +89,8 @@
const data = reactive({
  searchForm: {
        approveId: "",
        approveStatus: "",
    approveId: "",
    approveStatus: "",
  },
});
const { searchForm } = toRefs(data);
@@ -113,7 +100,7 @@
  const isLeaveType = currentApproveType.value === 2; // è¯·å‡ç®¡ç†
  const isReimburseType = currentApproveType.value === 4; // æŠ¥é”€ç®¡ç†
  const isQuotationType = currentApproveType.value === 6; // æŠ¥ä»·å®¡æ‰¹
  // åŸºç¡€åˆ—配置
  const baseColumns = [
    {
@@ -169,7 +156,7 @@
      width: 120
    }
  ];
  // é‡‘额列(仅报销管理显示)
  if (isReimburseType) {
    baseColumns.push({
@@ -178,28 +165,28 @@
      width: 120
    });
  }
  // æ—¥æœŸåˆ—(根据类型动态配置)
  baseColumns.push(
    {
      label: isLeaveType ? "开始日期" : "申请日期",
      prop: isLeaveType ? "startDate" : "approveTime",
      width: 200
    },
    {
      label: "结束日期",
      prop: isLeaveType ? "endDate" : "approveOverTime",
      width: 120
    }
      {
        label: isLeaveType ? "开始日期" : "申请日期",
        prop: isLeaveType ? "startDate" : "approveTime",
        width: 200
      },
      {
        label: "结束日期",
        prop: isLeaveType ? "endDate" : "approveOverTime",
        width: 120
      }
  );
  // å½“前审批人列
  baseColumns.push({
    label: "当前审批人",
    prop: "approveUserCurrentName",
    width: 120
  });
  // æ“ä½œåˆ—
  baseColumns.push({
    dataType: "action",
@@ -214,12 +201,7 @@
        clickFun: (row) => {
          openForm("edit", row);
        },
        disabled: (row) =>
          currentApproveType.value === 6 ||
          currentApproveType.value === 7 ||
          row.approveStatus == 2 ||
          row.approveStatus == 1 ||
          row.approveStatus == 4
        disabled: (row) => currentApproveType.value === 6 || row.approveStatus == 2 || row.approveStatus == 1 || row.approveStatus == 4
      },
      {
        name: "审核",
@@ -245,7 +227,7 @@
      },
    ],
  });
  return baseColumns;
});
const tableData = ref([]);
@@ -308,7 +290,7 @@
    4: "报销管理审批表",
    5: "采购申请审批表",
    6: "报价审批表",
    7: "发货审批表",
    7: "出库审批表",
  }
  const fileName = nameMap[type] || nameMap[0]
  proxy.download(url, {}, `${fileName}.xlsx`)
@@ -359,17 +341,17 @@
  // æ ¹æ®URL参数设置标签页和查询条件
  const approveType = route.query.approveType;
  const approveId = route.query.approveId;
  if (approveType) {
    // è®¾ç½®æ ‡ç­¾é¡µï¼ˆapproveType å¯¹åº” activeTab çš„ name)
    activeTab.value = String(approveType);
  }
  if (approveId) {
    // è®¾ç½®æµç¨‹ç¼–号查询条件
    searchForm.value.approveId = String(approveId);
  }
  // æŸ¥è¯¢åˆ—表
  getList();
});
src/views/collaborativeApproval/freeApprovalProcess/components/approvalDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,353 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增审批流程' : '编辑审批流程'"
        width="700px"
        @close="closeDia"
    >
      <el-form :model="form" :rules="rules" label-width="140px" label-position="top" ref="formRef">
        <el-row>
          <el-col :span="24">
            <el-form-item label="流程编号:" prop="approveId">
              <el-input v-model="form.approveId" placeholder="自动编号" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="申请部门:" prop="approveDeptId">
              <el-select
                  disabled
                  v-model="form.approveDeptId"
                  placeholder="选择部门"
              >
                <el-option
                    v-for="user in productOptions"
                    :key="user.deptId"
                    :label="user.deptName"
                    :value="user.deptId"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row v-if="!isQuotationApproval">
          <el-col :span="24">
            <el-form-item label="审批事由:" prop="approveReason">
              <el-input v-model="form.approveReason" placeholder="请输入" clearable type="textarea" disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <!-- å®¡æ‰¹äººé€‰æ‹©ï¼ˆåŠ¨æ€èŠ‚ç‚¹ï¼‰ -->
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="申请人:" prop="approveUser">
              <el-select
                  v-model="form.approveUser"
                  placeholder="选择人员"
                  disabled
              >
                <el-option
                    v-for="user in userList"
                    :key="user.userId"
                    :label="user.nickName"
                    :value="user.userId"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="申请日期:" prop="approveTime">
              <el-date-picker
                  v-model="form.approveTime"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
                  disabled
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <!-- æŠ¥ä»·å®¡æ‰¹ï¼šå±•示报价详情(复用销售报价“查看详情对话框”内容结构) -->
      <div v-if="isQuotationApproval" style="margin: 10px 0 18px;">
        <el-divider content-position="left">报价详情</el-divider>
        <el-skeleton :loading="quotationLoading" animated>
          <template #template>
            <el-skeleton-item variant="h3" style="width: 30%" />
            <el-skeleton-item variant="text" style="width: 100%" />
            <el-skeleton-item variant="text" style="width: 100%" />
          </template>
          <template #default>
            <el-empty v-if="!currentQuotation || !currentQuotation.quotationNo" description="未查询到对应报价详情" />
            <template v-else>
              <el-descriptions :column="2" border>
                <el-descriptions-item label="报价单号">{{ currentQuotation.quotationNo }}</el-descriptions-item>
                <el-descriptions-item label="客户名称">{{ currentQuotation.customer }}</el-descriptions-item>
                <el-descriptions-item label="业务员">{{ currentQuotation.salesperson }}</el-descriptions-item>
                <el-descriptions-item label="报价日期">{{ currentQuotation.quotationDate }}</el-descriptions-item>
                <el-descriptions-item label="有效期至">{{ currentQuotation.validDate }}</el-descriptions-item>
                <el-descriptions-item label="付款方式">{{ currentQuotation.paymentMethod }}</el-descriptions-item>
                <el-descriptions-item label="报价总额" :span="2">
                  <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
                    Â¥{{ Number(currentQuotation.totalAmount ?? 0).toFixed(2) }}
                  </span>
                </el-descriptions-item>
              </el-descriptions>
              <div style="margin-top: 20px;">
                <h4>产品明细</h4>
                <el-table :data="currentQuotation.products || []" border style="width: 100%">
                  <el-table-column prop="product" label="产品名称" />
                  <el-table-column prop="specification" label="规格型号" />
                  <el-table-column prop="unit" label="单位" />
                  <el-table-column prop="unitPrice" label="单价">
                    <template #default="scope">Â¥{{ Number(scope.row.unitPrice ?? 0).toFixed(2) }}</template>
                  </el-table-column>
                </el-table>
              </div>
              <div v-if="currentQuotation.remark" style="margin-top: 20px;">
                <h4>备注</h4>
                <p>{{ currentQuotation.remark }}</p>
              </div>
            </template>
          </template>
        </el-skeleton>
      </div>
      <el-form :model="{ activities }" ref="formRef" label-position="top">
        <el-steps :active="getActiveStep()" finish-status="success" process-status="process" align-center direction="vertical">
          <el-step
              v-for="(activity, index) in activities"
              :key="index"
              finish-status="success"
              :title="getNodeTitle(index, activities.length)"
              :description="activity.approveNodeUser"
              :icon="getNodeIcon(activity, index)"
          >
            <template #icon>
              <el-icon v-if="activity.approveNodeStatus === 2" color="red" :size="22"><WarningFilled /></el-icon>
              <el-icon v-else-if="activity.isShen" color="#1890ff" :size="22"><Edit /></el-icon>
              <el-icon v-else-if="activity.approveNodeStatus === 1" color="#67C23A" :size="26"><Check /></el-icon>
              <el-icon v-else color="#C0C4CC" :size="22"><MoreFilled /></el-icon>
            </template>
            <template #title>
              <span style="color: #000000">{{ getNodeTitle(index, activities.length) }}</span>
            </template>
            <template #description>
              <div class="node-user">
                <div class="avatar-wrapper">
                  <img :src="userStore.avatar" class="user-avatar" alt=""/>
                </div>
                <span style="color: #000000">{{ activity.approveNodeUser }}-{{activity.isApproval}}</span>
              </div>
              <div v-if="!activity.isShen" class="node-reason">
                <span>审批意见:</span>{{ activity.approveNodeReason }}
              </div>
              <div v-if="!activity.isShen" class="node-reason">
                <span>签名:</span>
                <img :src="activity.urlTem" class="signImg" alt="" v-if="activity.urlTem"/>
              </div>
              <div v-else-if="activity.isShen">
                <el-form-item
                    :prop="'activities.' + index + '.approveNodeReason'"
                    :rules="[{ required: true, message: '审批意见不能为空', trigger: 'blur' }]"
                >
                  <el-input v-model="activity.approveNodeReason" clearable type="textarea" :disabled="operationType === 'view'"></el-input>
                </el-form-item>
              </div>
            </template>
          </el-step>
        </el-steps>
      </el-form>
      <template #footer v-if="operationType === 'approval'">
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm(2)">不通过</el-button>
          <el-button type="primary" @click="submitForm(1)">通过</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { computed, getCurrentInstance, reactive, ref, toRefs } from "vue";
import {
  approveProcessDetails,
  getDept,
  updateApproveNode
} from "@/api/collaborativeApproval/approvalProcess.js";
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue'
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
const emit = defineEmits(['close'])
const { proxy } = getCurrentInstance()
const props = defineProps({
  approveType: {
    type: [Number, String],
    default: 0
  }
})
const dialogFormVisible = ref(false);
const operationType = ref('')
const activities = ref([])
const formRef = ref(null);
const userStore = useUserStore()
const productOptions = ref([]);
const userList = ref([])
const quotationLoading = ref(false)
const currentQuotation = ref({})
const isQuotationApproval = computed(() => Number(props.approveType) === 6)
const data = reactive({
  form: {
    approveTime: "",
    approveId: "",
    approveUser: "",
    approveDeptId: "",
    approveReason: "",
    checkResult: "",
  },
  rules: {
    // ä½¿ç”¨éƒ¨é—¨ID做必填校验,避免名称未同步导致误报
    approveDeptId: [{ required: true, message: "请选择申请部门", trigger: "change" }],
  },
});
const { form, rules } = toRefs(data);
// èŠ‚ç‚¹æ ‡é¢˜
const getNodeTitle = (index, len) => {
  if (index === len - 1) return '结束';
  return '审批';
};
// èŽ·å–å½“å‰æ¿€æ´»æ­¥éª¤
const getActiveStep = () => {
  // å¦‚果所有 isShen éƒ½ä¸º false,返回最后一个步骤(全部完成)
  const hasActive = activities.value.some(a => a.isShen === true);
  if (!hasActive) return activities.value.length;
  // å½“前节点索引
  return activities.value.findIndex(a => a.isShen  == true);
};
// æ­¥éª¤icon
const getNodeIcon = (activity, index) => {
  if (activity.approveNodeStatus === 2) return 'el-icon-warning'; // ä¸é€šè¿‡
  if (activity.isShen) return 'Edit';
  return '';
};
// æ‰“开弹框
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  currentQuotation.value = {}
  userListNoPageByTenantId().then((res) => {
    userList.value = res.data;
  });
  form.value = {...row}
  getProductOptions()
  // æŠ¥ä»·å®¡æ‰¹ï¼šç”¨å®¡æ‰¹äº‹ç”±å­—段承载的“报价单号”去查报价列表
  if (isQuotationApproval.value) {
    const quotationNo = row?.approveReason;
    if (quotationNo) {
      quotationLoading.value = true
      getQuotationList({ quotationNo }).then((res) => {
        const records = res?.data?.records || []
        currentQuotation.value = records[0] || {}
      }).finally(() => {
        quotationLoading.value = false
      })
    }
  }
  approveProcessDetails(row.approveId).then((res) => {
    activities.value = res.data
    // å¢žåŠ isApproval字段
    activities.value.forEach(item => {
      if (item.url && item.url.includes('word')) {
        item.urlTem = item.url.replaceAll('word', 'img')
      } else {
        item.urlTem = item.url
      }
      if (item.approveNodeStatus === 2) {
        item.isApproval = '已驳回';
      } else if (item.approveNodeStatus === 1) {
        item.isApproval = '已同意';
      } else {
        item.isApproval = '未审批';
      }
    })
  })
}
const getProductOptions = () => {
  getDept().then((res) => {
    productOptions.value = res.data;
  });
};
// æäº¤å®¡æ‰¹
const submitForm = (status) => {
  const filteredActivities = activities.value.filter(activity => activity.isShen);
  filteredActivities[0].approveNodeStatus = status;
  // åˆ¤æ–­æ˜¯å¦ä¸ºæœ€åŽä¸€æ­¥
  const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length-1;
  updateApproveNode({ ...filteredActivities[0], isLast }).then(() => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
  });
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
  quotationLoading.value = false
  currentQuotation.value = {}
  emit('close')
};
defineExpose({
  openDialog,
});
</script>
<style scoped>
.node-user {
  margin: 10px 0;
  font-size: 16px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
}
.node-status {
  color: #1890ff;
  margin-left: 8px;
  font-size: 14px;
}
.node-reason {
  font-size: 15px;
  color: #333;
  margin: 10px 0;
}
.user-avatar {
  cursor: pointer;
  width: 30px;
  height: 30px;
  border-radius: 50px;
}
.signImg {
  cursor: pointer;
  width: 200px;
  height: 60px;
}
</style>
src/views/collaborativeApproval/freeApprovalProcess/components/infoFormDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,466 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增审批流程' : '编辑审批流程'"
        width="50%"
        @close="closeDia"
    >
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-row>
          <el-col :span="24">
            <el-form-item label="流程编号:" prop="approveId">
              <el-input v-model="form.approveId" placeholder="自动编号" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <!-- ç”³è¯·éƒ¨é—¨ï¼šæ ¡éªŒä½¿ç”¨éƒ¨é—¨ID,便于下拉选择后立即通过校验 -->
            <el-form-item label="申请部门:" prop="approveDeptId">
              <!--              <el-input v-model="form.approveDeptName" placeholder="请输入" clearable/>-->
              <el-select
                  v-model="form.approveDeptId"
                  placeholder="选择部门"
                  @change="handleDeptChange"
              >
                <el-option
                    v-for="user in productOptions"
                    :key="user.deptId"
                    :label="user.deptName"
                    :value="user.deptId"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item :label="props.approveType == 5 ? '采购说明:' : '审批事由:'" prop="approveReason">
              <el-input v-model="form.approveReason" placeholder="请输入" clearable type="textarea" />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- è¯·å‡æ—¶é—´ï¼ˆä»…当 approveType ä¸º 2 æ—¶æ˜¾ç¤ºï¼‰ -->
        <el-row :gutter="30" v-if="props.approveType == 2">
          <el-col :span="12">
            <el-form-item label="请假开始时间:" prop="startDate">
              <el-date-picker
                  v-model="form.startDate"
                  type="date"
                  placeholder="请选择开始日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="请假结束时间:" prop="endDate">
              <el-date-picker
                  v-model="form.endDate"
                  type="date"
                  placeholder="请选择结束日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- æŠ¥é”€é‡‘额(仅当 approveType ä¸º 4 æ—¶æ˜¾ç¤ºï¼‰ -->
        <el-row v-if="props.approveType == 4">
          <el-col :span="24">
            <el-form-item label="报销金额:" prop="price">
              <el-input-number
                  v-model="form.price"
                  placeholder="请输入报销金额"
                  :min="0"
                  :precision="2"
                  :step="0.01"
                  style="width: 100%"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- å‡ºå·®åœ°ç‚¹ï¼ˆä»…当 approveType ä¸º 3 æ—¶æ˜¾ç¤ºï¼‰ -->
        <el-row v-if="props.approveType == 3">
          <el-col :span="24">
            <el-form-item label="备注:" prop="location">
              <el-input
                  v-model="form.location"
                  placeholder="请输入备注"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- å®¡æ‰¹äººé€‰æ‹©ï¼ˆåŠ¨æ€èŠ‚ç‚¹ï¼‰ -->
        <el-row>
          <el-col :span="24">
            <el-form-item>
              <template #label>
                <span>审批人选择:</span>
                <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">新增节点</el-button>
              </template>
              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
                <div
                    v-for="(node, index) in approverNodes"
                    :key="node.id"
                    style="margin-right: 30px; text-align: center; margin-bottom: 10px;"
                >
                  <div>
                    <span>审批人</span>
                    â†’
                  </div>
                  <el-select
                      v-model="node.userId"
                      placeholder="选择人员"
                      style="width: 120px; margin-bottom: 8px;"
                  >
                    <el-option
                        v-for="user in userList"
                        :key="user.userId"
                        :label="user.nickName"
                        :value="user.userId"
                    />
                  </el-select>
                  <div>
                    <el-button
                        type="danger"
                        size="small"
                        @click="removeApproverNode(index)"
                        v-if="approverNodes.length > 1"
                    >删除</el-button>
                  </div>
                </div>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="申请人:" prop="approveUser">
              <el-select
                  v-model="form.approveUser"
                  placeholder="选择人员"
                  filterable
                  default-first-option
                  :reserve-keyword="false"
              >
                <el-option
                    v-for="user in userList"
                    :key="user.userId"
                    :label="user.nickName"
                    :value="user.userId"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="申请日期:" prop="approveTime">
              <el-date-picker
                  v-model="form.approveTime"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="附件材料:" prop="remark">
              <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                         :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                         :on-success="handleUploadSuccess" :on-remove="handleRemove">
                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                <template #tip v-if="operationType !== 'view'">
                  <div class="el-upload__tip">
                    æ–‡ä»¶æ ¼å¼æ”¯æŒ
                    doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
                  </div>
                </template>
              </el-upload>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {ref, reactive, toRefs, getCurrentInstance} from "vue";
import {
  approveProcessAdd, approveProcessGetInfo,
  approveProcessUpdate,
  getDept
} from "@/api/collaborativeApproval/approvalProcess.js";
import {
  delLedgerFile,
} from "@/api/salesManagement/salesLedger.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import { getToken } from "@/utils/auth";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
import useUserStore from "@/store/modules/user";
import { getCurrentDate } from "@/utils/index.js";
import log from "@/views/monitor/job/log.vue";
const userStore = useUserStore();
const dialogFormVisible = ref(false);
const operationType = ref('')
const fileList = ref([]);
const upload = reactive({
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
});
const data = reactive({
  form: {
    approveTime: "",
    approveId: "",
    approveUser: "",
    approveDeptId: "",
    approveDeptName: "",
    approveReason: "",
    checkResult: "",
    tempFileIds: [],
    approverList: [], // æ–°å¢žå­—段,存储所有节点的审批人id
    startDate: "", // è¯·å‡å¼€å§‹æ—¶é—´
    endDate: "", // è¯·å‡ç»“束时间
    price: null, // æŠ¥é”€é‡‘额
    location: "" // å‡ºå·®åœ°ç‚¹
  },
  rules: {
    approveTime: [{ required: false, message: "请输入", trigger: "change" }],
    approveId: [{ required: false, message: "请输入", trigger: "blur" }],
    approveUser: [{ required: false, message: "请输入", trigger: "blur" }],
    // ä½¿ç”¨éƒ¨é—¨ID做必填校验,避免名称未同步导致误报
    approveDeptId: [{ required: true, message: "请选择申请部门", trigger: "change" }],
    approveReason: [{ required: true, message: "请输入", trigger: "blur" }],
    checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
    startDate: [{ required: true, message: "请选择请假开始时间", trigger: "change" }],
    endDate: [{ required: true, message: "请选择请假结束时间", trigger: "change" }],
    price: [{ required: true, message: "请输入报销金额", trigger: "blur" }],
    location: [{ required: true, message: "请输入出差地点", trigger: "blur" }],
  },
});
const { form, rules } = toRefs(data);
const productOptions = ref([]);
const currentApproveStatus = ref(0)
const props = defineProps({
  approveType: {
    type: [Number, String],
    default: 0
  }
})
// å®¡æ‰¹äººèŠ‚ç‚¹ç›¸å…³
const approverNodes = ref([
  { id: 1, userId: null }
])
let nextApproverId = 2
const userList = ref([])
function addApproverNode() {
  approverNodes.value.push({ id: nextApproverId++, userId: null })
}
function removeApproverNode(index) {
  approverNodes.value.splice(index, 1)
}
// å¤„理部门选择变化
const handleDeptChange = (deptId) => {
  if (deptId) {
    const selectedDept = productOptions.value.find(dept => dept.deptId === deptId);
    if (selectedDept) {
      form.value.approveDeptName = selectedDept.deptName;
    }
  } else {
    form.value.approveDeptName = '';
  }
};
// æ‰“开弹框
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  userListNoPageByTenantId().then((res) => {
    userList.value = res.data;
  });
  getProductOptions();
  form.value = {}
  approverNodes.value = [
    { id: 1, userId: null }
  ]
  form.value.approveUser = userStore.id;
  form.value.approveTime = getCurrentDate();
  // èŽ·å–å½“å‰ç”¨æˆ·ä¿¡æ¯å¹¶è®¾ç½®éƒ¨é—¨ID
  form.value.approveDeptId = userStore.currentDeptId
  if (operationType.value === 'edit') {
    fileList.value = row.commonFileList
    form.value.tempFileIds = fileList.value.map(file => file.id)
    currentApproveStatus.value = row.approveStatus
    approveProcessGetInfo({id: row.approveId,approveReason: '1'}).then(res => {
      form.value = {...res.data}
      // åæ˜¾å®¡æ‰¹äºº
      if (res.data && res.data.approveUserIds) {
        const userIds = res.data.approveUserIds.split(',')
        approverNodes.value = userIds.map((userId, idx) => ({
          id: idx + 1,
          userId: parseInt(userId.trim())
        }))
        nextApproverId = userIds.length + 1
      } else {
        approverNodes.value = [{ id: 1, userId: null }]
        nextApproverId = 2
      }
    })
  }
}
const getProductOptions = () => {
  getDept().then((res) => {
    productOptions.value = res.data;
  });
};
function convertIdToValue(data) {
  return data.map((item) => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
      value: id, // å°† id æ”¹ä¸º value
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
}
// æäº¤äº§å“è¡¨å•
const submitForm = () => {
  // æ”¶é›†æ‰€æœ‰èŠ‚ç‚¹çš„å®¡æ‰¹äººid
  form.value.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
  form.value.approveType = props.approveType
  // å®¡æ‰¹äººå¿…填校验
  const hasEmptyApprover = approverNodes.value.some(node => !node.userId)
  if (hasEmptyApprover) {
    proxy.$modal.msgError("请为所有审批节点选择审批人!")
    return
  }
  // å½“ approveType ä¸º 2 æ—¶ï¼Œæ ¡éªŒè¯·å‡æ—¶é—´
  if (props.approveType == 2) {
    if (!form.value.startDate) {
      proxy.$modal.msgError("请选择请假开始时间!")
      return
    }
    if (!form.value.endDate) {
      proxy.$modal.msgError("请选择请假结束时间!")
      return
    }
    // æ ¡éªŒç»“束时间不能早于开始时间
    if (new Date(form.value.endDate) < new Date(form.value.startDate)) {
      proxy.$modal.msgError("请假结束时间不能早于开始时间!")
      return
    }
  }
  // å½“ approveType ä¸º 3 æ—¶ï¼Œæ ¡éªŒå‡ºå·®åœ°ç‚¹
  if (props.approveType == 3) {
    if (!form.value.location || form.value.location.trim() === '') {
      proxy.$modal.msgError("请输入出差地点!")
      return
    }
  }
  // å½“ approveType ä¸º 4 æ—¶ï¼Œæ ¡éªŒæŠ¥é”€é‡‘额
  if (props.approveType == 4) {
    if (!form.value.price || form.value.price <= 0) {
      proxy.$modal.msgError("请输入有效的报销金额!")
      return
    }
  }
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      if (operationType.value === "add" || currentApproveStatus.value == 3) {
        approveProcessAdd(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      } else {
        approveProcessUpdate(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      }
    }
  })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  fileList.value = []
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
  emit('close')
};
// ä¸Šä¼ å‰æ ¡æ£€
function handleBeforeUpload(file) {
  // æ ¡æ£€æ–‡ä»¶å¤§å°
  // if (file.size > 1024 * 1024 * 10) {
  //   proxy.$modal.msgError("上传文件大小不能超过10MB!");
  //   return false;
  // }
  proxy.$modal.loading("正在上传文件,请稍候...");
  return true;
}
// ä¸Šä¼ å¤±è´¥
function handleUploadError(err) {
  proxy.$modal.msgError("上传文件失败");
  proxy.$modal.closeLoading();
}
// ä¸Šä¼ æˆåŠŸå›žè°ƒ
function handleUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
  if (res.code === 200) {
    // ç¡®ä¿ tempFileIds å­˜åœ¨ä¸”为数组
    if (!form.value.tempFileIds) {
      form.value.tempFileIds = [];
    }
    form.value.tempFileIds.push(res.data.tempId);
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.fileUpload.handleRemove(file);
  }
}
// ç§»é™¤æ–‡ä»¶
function handleRemove(file) {
  if (operationType.value === "edit") {
    let ids = [];
    ids.push(file.id);
    delLedgerFile(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
    });
  }
}
defineExpose({
  openDialog,
});
</script>
<style scoped>
</style>
src/views/collaborativeApproval/freeApprovalProcess/fileList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
<template>
  <el-dialog v-model="dialogVisible" title="附件" width="40%" :before-close="handleClose" draggable>
    <el-table :data="tableData" border height="40vh">
      <el-table-column label="附件名称" prop="name" min-width="400" show-overflow-tooltip />
      <el-table-column fixed="right" label="操作" width="150" align="center">
        <template #default="scope">
          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">下载</el-button>
          <el-button link type="primary" size="small" @click="lookFile(scope.row)">预览</el-button>
          <el-button link type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-dialog>
  <filePreview ref="filePreviewRef" />
</template>
<script setup>
import { ref } from 'vue'
import filePreview from '@/components/filePreview/index.vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import { delCommonFile } from '@/api/publicApi/commonFile.js'
const dialogVisible = ref(false)
const tableData = ref([])
const { proxy } = getCurrentInstance();
const filePreviewRef = ref()
const handleClose = () => {
  dialogVisible.value = false
}
const open = (list) => {
  dialogVisible.value = true
  tableData.value = list
}
const downLoadFile = (row) => {
  proxy.$download.name(row.url);
}
const lookFile = (row) => {
  filePreviewRef.value.open(row.url)
}
// åˆ é™¤é™„ä»¶
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除附件"${row.name}"吗?`, '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    delCommonFile([row.id]).then(() => {
      ElMessage.success('删除成功')
      // ä»Žåˆ—表中移除已删除的附件
      const index = tableData.value.findIndex(item => item.id === row.id)
      if (index !== -1) {
        tableData.value.splice(index, 1)
      }
    }).catch(() => {
      ElMessage.error('删除失败')
    })
  }).catch(() => {
    ElMessage.info('已取消删除')
  })
}
defineExpose({
  open
})
</script>
<style></style>
src/views/collaborativeApproval/freeApprovalProcess/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,362 @@
<template>
  <div class="app-container">
    <!-- æ ‡ç­¾é¡µåˆ‡æ¢ä¸åŒçš„审批类型 -->
    <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="approval-tabs">
      <el-tab-pane label="自由协同审批" name="2"></el-tab-pane>
    </el-tabs>
    <div class="search_form">
      <div>
        <span class="search_title">流程编号:</span>
        <el-input
            v-model="searchForm.approveId"
            style="width: 240px"
            placeholder="请输入流程编号搜索"
            @change="handleQuery"
            clearable
            :prefix-icon="Search"
        />
        <span class="search_title ml10">审批状态:</span>
        <el-select v-model="searchForm.approveStatus" clearable @change="handleQuery" style="width: 240px">
          <el-option label="待审核" :value="0" />
          <el-option label="审核中" :value="1" />
          <el-option label="审核完成" :value="2" />
          <el-option label="审核未通过" :value="3" />
          <el-option label="已重新提交" :value="4" />
        </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
        >搜索</el-button
        >
      </div>
      <div>
        <el-button type="primary" @click="openForm('add')" v-if="currentApproveType !== 6">新增</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="tableColumnCopy"
          :tableData="tableData"
          :page="page"
          :isSelection="true"
          @selection-change="handleSelectionChange"
          :tableLoading="tableLoading"
          @pagination="pagination"
          :total="page.total"
      ></PIMTable>
    </div>
    <info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="currentApproveType"></info-form-dia>
    <approval-dia ref="approvalDia" @close="handleQuery" :approveType="currentApproveType"></approval-dia>
    <FileList ref="fileListRef" />
  </div>
</template>
<script setup>
import FileList from "./fileList.vue";
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref, computed, reactive, toRefs, nextTick, getCurrentInstance} from "vue";
import {ElMessageBox} from "element-plus";
import { useRoute } from 'vue-router';
import InfoFormDia from "@/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue";
import ApprovalDia from "@/views/collaborativeApproval/approvalProcess/components/approvalDia.vue";
import {approveProcessDelete, approveProcessListPage} from "@/api/collaborativeApproval/approvalProcess.js";
import useUserStore from "@/store/modules/user";
const userStore = useUserStore();
const route = useRoute();
// å½“前选中的标签页,默认为公出管理
const activeTab = ref('2');
// å½“前审批类型,根据选中的标签页计算
const currentApproveType = computed(() => {
  return Number(activeTab.value);
});
// æ ‡ç­¾é¡µåˆ‡æ¢å¤„理
const handleTabChange = (tabName) => {
  // åˆ‡æ¢æ ‡ç­¾é¡µæ—¶é‡ç½®æœç´¢æ¡ä»¶å’Œåˆ†é¡µï¼Œå¹¶é‡æ–°åŠ è½½æ•°æ®
  searchForm.value.approveId = '';
  searchForm.value.approveStatus = '';
  page.current = 1;
  getList();
};
const data = reactive({
  searchForm: {
    approveId: "",
    approveStatus: "",
  },
});
const { searchForm } = toRefs(data);
// åŠ¨æ€è¡¨æ ¼åˆ—é…ç½®ï¼Œæ ¹æ®å®¡æ‰¹ç±»åž‹ç”Ÿæˆåˆ—
const tableColumnCopy = computed(() => {
  const isLeaveType = currentApproveType.value === 2; // è¯·å‡ç®¡ç†
  const isReimburseType = currentApproveType.value === 4; // æŠ¥é”€ç®¡ç†
  const isQuotationType = currentApproveType.value === 6; // æŠ¥ä»·å®¡æ‰¹
  // åŸºç¡€åˆ—配置
  const baseColumns = [
    {
      label: "审批状态",
      prop: "approveStatus",
      dataType: "tag",
      width: 100,
      formatData: (params) => {
        if (params == 0) {
          return "待审核";
        } else if (params == 1) {
          return "审核中";
        } else if (params == 2) {
          return "审核完成";
        } else if (params == 4) {
          return "已重新提交";
        } else {
          return '不通过';
        }
      },
      formatType: (params) => {
        if (params == 0) {
          return "warning";
        } else if (params == 1) {
          return "primary";
        } else if (params == 2) {
          return "success";
        } else if (params == 4) {
          return "info";
        } else {
          return 'danger';
        }
      },
    },
    {
      label: "流程编号",
      prop: "approveId",
      width: 170
    },
    {
      label: "申请部门",
      prop: "approveDeptName",
      width: 220
    },
    {
      label: isQuotationType ? "报价单号" : "审批事由",
      prop: "approveReason",
      width: 200
    },
    {
      label: "申请人",
      prop: "approveUserName",
      width: 120
    }
  ];
  // é‡‘额列(仅报销管理显示)
  if (isReimburseType) {
    baseColumns.push({
      label: "金额(元)",
      prop: "price",
      width: 120
    });
  }
  // æ—¥æœŸåˆ—(根据类型动态配置)
  baseColumns.push(
      {
        label: isLeaveType ? "开始日期" : "申请日期",
        prop: isLeaveType ? "startDate" : "approveTime",
        width: 200
      },
      {
        label: "结束日期",
        prop: isLeaveType ? "endDate" : "approveOverTime",
        width: 120
      }
  );
  // å½“前审批人列
  baseColumns.push({
    label: "当前审批人",
    prop: "approveUserCurrentName",
    width: 120
  });
  // æ“ä½œåˆ—
  baseColumns.push({
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 230,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
        },
        disabled: (row) => currentApproveType.value === 6 || row.approveStatus == 2 || row.approveStatus == 1 || row.approveStatus == 4
      },
      {
        name: "审核",
        type: "text",
        clickFun: (row) => {
          openApprovalDia("approval", row);
        },
        disabled: (row) => row.approveUserCurrentId == null || row.approveStatus == 2 || row.approveStatus == 3 || row.approveStatus == 4 || row.approveUserCurrentId !== userStore.id
      },
      {
        name: "详情",
        type: "text",
        clickFun: (row) => {
          openApprovalDia('view', row);
        },
      },
      {
        name: "附件",
        type: "text",
        clickFun: (row) => {
          downLoadFile(row);
        },
      },
    ],
  });
  return baseColumns;
});
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
  total: 0
});
const infoFormDia = ref()
const approvalDia = ref()
const { proxy } = getCurrentInstance()
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
const fileListRef = ref(null)
const downLoadFile = (row) => {
  fileListRef.value.open(row.commonFileList)
}
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  approveProcessListPage({...page, ...searchForm.value, approveType: currentApproveType.value}).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
};
// å¯¼å‡º
const handleOut = () => {
  const type = currentApproveType.value
  const urlMap = {
    0: "/approveProcess/exportZero",
    1: "/approveProcess/exportOne",
    2: "/approveProcess/exportTwo",
    3: "/approveProcess/exportThree",
    4: "/approveProcess/exportFour",
    5: "/approveProcess/exportFive",
    6: "/approveProcess/exportSix",
    7: "/approveProcess/exportSeven",
  }
  const url = urlMap[type] || urlMap[0]
  const nameMap = {
    0: "协同审批管理表",
    1: "公出管理审批表",
    2: "请假管理审批表",
    3: "出差管理审批表",
    4: "报销管理审批表",
    5: "采购申请审批表",
    6: "报价审批表",
    7: "出库审批表",
  }
  const fileName = nameMap[type] || nameMap[0]
  proxy.download(url, {}, `${fileName}.xlsx`)
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// æ‰“开新增、编辑弹框
const openForm = (type, row) => {
  nextTick(() => {
    infoFormDia.value?.openDialog(type, row)
  })
};
// æ‰“开新增检验弹框
const openApprovalDia = (type, row) => {
  nextTick(() => {
    approvalDia.value?.openDialog(type, row)
  })
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.approveId);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        approveProcessDelete(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
onMounted(() => {
  // æ ¹æ®URL参数设置标签页和查询条件
  const approveType = route.query.approveType;
  const approveId = route.query.approveId;
  if (approveType) {
    // è®¾ç½®æ ‡ç­¾é¡µï¼ˆapproveType å¯¹åº” activeTab çš„ name)
    activeTab.value = String(approveType);
  }
  if (approveId) {
    // è®¾ç½®æµç¨‹ç¼–号查询条件
    searchForm.value.approveId = String(approveId);
  }
  // æŸ¥è¯¢åˆ—表
  getList();
});
</script>
<style scoped>
.approval-tabs {
  margin-bottom: 10px;
}
</style>
src/views/collaborativeApproval/freeApprovalProcess/index2.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
<template>
  <div class="container">
    <!-- å¼•å…¥index.vue组件并传递参数 -->
    <FreeApprovalProcessIndex :approveType="2" />
  </div>
</template>
<script setup>
import FreeApprovalProcessIndex from './index.vue'
// å®šä¹‰ç»„件名称
defineOptions({
  name: 'FreeApprovalProcessIndex1'
})
</script>
<style scoped>
.container {
  width: 100%;
  height: 100%;
}
</style>
src/views/inventoryManagement/dispatchLog/index.vue
@@ -1,38 +1,371 @@
<!-- åœ¨ä½ çš„主页面中 -->
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <el-tab-pane v-for="tab in tabs"
                   :label="tab.label"
                   :name="tab.name"
                   :key="tab.name">
        <record :type="tab.type" v-if="activeTab === tab.name" />
      </el-tab-pane>
    </el-tabs>
    <div class="search_form">
      <div>
        <span class="search_title ml10">发料日期:</span>
        <el-date-picker
            v-model="searchForm.timeStr"
            type="date"
            placeholder="请选择日期"
            value-format="YYYY-MM-DD"
            format="YYYY-MM-DD"
            clearable
            @change="handleQuery"
        />
        <span class="search_title ml10">产品大类:</span>
        <el-input
            v-model="searchForm.productCategory"
            style="width: 240px"
            placeholder="请输入"
            clearable
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <el-table
          :data="tableData"
          border
          v-loading="tableLoading"
          @selection-change="handleSelectionChange"
          :expand-row-keys="expandedRowKeys"
          :row-key="(row) => row.id"
          show-summary
          style="width: 100%"
          :summary-method="summarizeMainTable"
          height="calc(100vh - 18.5em)"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="发料日期" prop="createTime" show-overflow-tooltip width="130"/>
        <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" show-overflow-tooltip />
        <el-table-column label="物品类型" prop="itemType" show-overflow-tooltip />
        <el-table-column label="发料数量" prop="inboundNum" show-overflow-tooltip />
        <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
        <el-table-column label="总价(元)" prop="taxInclusiveTotalPrice" width="150"></el-table-column>
        <el-table-column label="发料人" prop="createBy" show-overflow-tooltip />
      </el-table>
      <pagination
          v-show="total > 0"
          :total="total"
          layout="total, sizes, prev, pager, next, jumper"
          :page="page.current"
          :limit="page.size"
          @pagination="paginationChange"
      />
    </div>
  </div>
</template>
<script setup>
import Record from "@/views/inventoryManagement/dispatchLog/Record.vue";
const activeTab = ref('qualified')
const type = ref(0)
const tabs = computed(() => {
  return [
    {
      label: '合格出库',
      name: 'qualified',
      type: '0'
    },
    {
      label: '不合格出库',
      name: 'unqualified',
      type: '1'
    }
  ]
})
import pagination from "@/components/PIMTable/Pagination.vue";
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue";
import { ElMessageBox } from "element-plus";
import {
  delStockOut,
} from "@/api/inventoryManagement/stockOut.js";
import {
  getStockInPageByCustom,
} from "@/api/inventoryManagement/stockIn.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
});
const total = ref(0);
const handleTabChange = (tabName) => {
  activeTab.value = tabName;
  type.value = tabName === 'qualified' ? 0 : 1
}
// æŸ¥è¯¢è¡¨å•数据
const data = reactive({
  searchForm: {
    supplierName: "",
    customerName: "",
    productCategory:'',
    timeStr: getCurrentDate(),
  },
});
const { searchForm } = toRefs(data);
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  const params = { ...page }
  params.supplierName = searchForm.value.supplierName
  params.timeStr = searchForm.value.timeStr
  params.productCategory = searchForm.value.productCategory
  // ææ–™å‡ºåº“:调用自定义出库记录接口
  const apiCall = getStockInPageByCustom(params)
  apiCall
      .then((res) => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        tableData.value.map((item) => {
          item.children = [];
          // å‰ç«¯è®¡ç®—总价
          const inboundNum = Number(item.inboundNum) || 0;
          // ææ–™å‡ºåº“:总价 = taxInclusiveUnitPrice Ã— inboundNum
          const taxInclusiveUnitPrice = Number(item.taxInclusiveUnitPrice) || 0;
          item.taxInclusiveTotalPrice = (taxInclusiveUnitPrice * inboundNum).toFixed(2);
        });
        total.value = res.data.total;
      })
      .catch(() => {
        tableLoading.value = false;
      });
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  // è¿‡æ»¤æŽ‰å­æ•°æ®
  selectedRows.value = selection.filter((item) => item.id);
  console.log("selection", selectedRows.value);
};
const expandedRowKeys = ref([]);
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(param, [
    "contractAmount",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
  ]);
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        // æ ¹æ®ä¸åŒçš„ tab ç±»åž‹è°ƒç”¨ä¸åŒçš„导出接口
        const exportUrl = "/stockmanagement/exportTwo"
        proxy.download(exportUrl, {}, "出库台账.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        delStockOut({ids:ids}).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
onMounted(() => {
  getList();
});
</script>
<style scoped lang="scss">
.print-preview-dialog {
  .el-dialog__body {
    padding: 0;
    max-height: 80vh;
    overflow-y: auto;
  }
}
.print-preview-container {
  .print-preview-header {
    padding: 15px;
    border-bottom: 1px solid #e4e7ed;
    text-align: center;
    .el-button {
      margin: 0 10px;
    }
  }
  .print-preview-content {
    padding: 20px;
    background-color: #f5f5f5;
    min-height: 400px;
  }
}
.print-page {
  width: 220mm;
  height: 90mm;
  padding: 10mm;
  margin: 0 auto;
  background: white;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  margin-bottom: 10px;
  box-sizing: border-box;
}
.delivery-note {
  width: 100%;
  height: 100%;
  font-family: "SimSun", serif;
  font-size: 10px;
  line-height: 1.2;
  display: flex;
  flex-direction: column;
}
.header {
  text-align: center;
  margin-bottom: 8px;
  .company-name {
    font-size: 18px;
    font-weight: bold;
    margin-bottom: 4px;
  }
  .document-title {
    font-size: 16px;
    font-weight: bold;
  }
}
.info-section {
  margin-bottom: 8px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  .info-row {
    line-height: 20px;
    .label {
      font-weight: bold;
      width: 60px;
      font-size: 14px;
    }
    .value {
      margin-right: 20px;
      min-width: 80px;
      font-size: 14px;
    }
  }
}
.table-section {
  margin-bottom: 4px;
  flex: 1;
  .product-table {
    width: 100%;
    border-collapse: collapse;
    border: 1px solid #000;
    th, td {
      border: 1px solid #000;
      padding: 6px;
      text-align: center;
      font-size: 14px;
      line-height: 1.4;
    }
    th {
      font-weight: bold;
    }
    .total-label {
      text-align: right;
      font-weight: bold;
    }
    .total-value {
      font-weight: bold;
    }
  }
}
.footer-section {
  .footer-row {
    display: flex;
    margin-bottom: 3px;
    line-height: 20px;
    justify-content: space-between;
    .footer-item {
      display: flex;
      margin-right: 20px;
      .label {
        font-weight: bold;
        width: 80px;
        font-size: 14px;
      }
      .value {
        min-width: 80px;
        font-size: 14px;
      }
      &.address-item {
        .address-value {
          min-width: 200px;
        }
      }
    }
  }
}
@media print {
  .app-container {
    display: none;
  }
  .print-page {
    box-shadow: none;
    margin: 0;
    padding: 10mm;
    padding-left: 20mm;
    page-break-inside: avoid;
    page-break-after: always;
  }
  .print-page:last-child {
    page-break-after: avoid;
  }
}
</style>
src/views/inventoryManagement/issueManagement/index.vue
@@ -2,66 +2,70 @@
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">供应商名称:</span>
        <el-input v-model="searchForm.supplierName" style="width: 240px" placeholder="请输入" @change="handleQuery"
          clearable prefix-icon="Search" />
                <span class="search_title ml10">入库日期:</span>
                <el-date-picker
                    v-model="searchForm.timeStr"
                    type="date"
                    placeholder="请选择日期"
                    value-format="YYYY-MM-DD"
                    format="YYYY-MM-DD"
                    clearable
                    @change="handleQuery"
                />
        <span class="search_title ml10">产品大类:</span>
        <el-input
            v-model="searchForm.productCategory"
            style="width: 240px"
            placeholder="请输入"
            clearable
        />
        <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">
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
      <el-table
          :data="tableData"
          border
          v-loading="tableLoading"
          @selection-change="handleSelectionChange"
          :expand-row-keys="expandedRowKeys"
          :row-key="row => row.id"
          show-summary
          style="width: 100%"
          :summary-method="summarizeMainTable"
          height="calc(100vh - 18.5em)"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="入库时间" prop="createTime" width="100" show-overflow-tooltip />
        <el-table-column label="入库批次" prop="inboundBatches" width="160" show-overflow-tooltip />
        <el-table-column label="供应商名称" prop="supplierName" width="240" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" width="100" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" width="200" show-overflow-tooltip />
        <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
        <el-table-column label="入库数量" prop="inboundNum" width="90" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="inboundNum0" width="90" show-overflow-tooltip />
        <el-table-column label="含税单价" prop="taxInclusiveUnitPrice" width="100" show-overflow-tooltip />
        <el-table-column label="含税总价" prop="taxInclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="税率(%)" prop="taxRate" width="80" show-overflow-tooltip />
        <el-table-column label="不含税总价" prop="taxExclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="入库人" prop="createBy" width="80" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="60" align="center">
        <el-table-column label="物品类型" prop="itemType" show-overflow-tooltip />
        <el-table-column label="剩余库存" prop="inboundNum0" width="90" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" width="100" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm(scope.row);">领用</el-button>
            <el-button link type="primary" size="small" @click="openForm(scope.row);">发料</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
      <pagination
          v-show="total > 0"
          :total="total"
          layout="total, sizes, prev, pager, next, jumper"
          :page="page.current"
          :limit="page.size"
          @pagination="paginationChange"
      />
    </div>
    <el-dialog v-model="dialogFormVisible" :title="'新增出库'" width="40%" @close="closeDia">
    <el-dialog v-model="dialogFormVisible" :title="getDialogTitle()" width="40%" @close="closeDia" draggable>
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-form-item label="出库数量:" prop="salesContractNo">
          <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.inboundQuantity" placeholder="请输入" clearable />
        <div>{{getAvailableQuantityText()}}:{{currentRowNum}}</div>
        <el-form-item :label="getQuantityLabel()" prop="inboundQuantity">
          <el-input-number :step="0.01" :min="0" :max="currentRowNum" style="width: 100%" v-model="form.inboundQuantity" placeholder="请输入" clearable />
        </el-form-item>
        <el-form-item label="出库日期:" prop="projectName">
        <el-form-item :label="getDateLabel()" prop="inboundTime">
          <el-date-picker style="width: 100%" v-model="form.inboundTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
            type="date" placeholder="请选择" clearable />
                          type="date" placeholder="请选择" clearable />
        </el-form-item>
        <el-form-item label="出库人:" prop="entryPerson">
          <el-select v-model="form.nickName" placeholder="请选择" clearable>
        <el-form-item :label="getPersonLabel()" prop="nickName">
          <el-select v-model="form.nickName"
                     filterable
                     default-first-option
                     :reserve-keyword="false" placeholder="请选择" clearable>
            <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
          </el-select>
        </el-form-item>
@@ -78,18 +82,18 @@
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref } from 'vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
import { ElMessageBox } from "element-plus";
import useUserStore from '@/store/modules/user'
import { userListNoPageByTenantId } from "@/api/system/user.js";
import {
  getStockInPage
  getInPageByCustom
} from "@/api/inventoryManagement/stockIn.js";
import {
  getStockManagePage,
    delStockManage,
    stockOut,
  delStockManage,
  stockOut,
} from "@/api/inventoryManagement/stockManage.js";
import { getCurrentDate } from "@/utils/index.js";
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
@@ -108,12 +112,12 @@
const dialogFormVisible = ref(false)
const data = reactive({
  searchForm: {
    supplierName: '',
    inboundQuantity:'',
    inboundTime:'',
    nickName: '',
    userId: '',
        timeStr: '',
    productCategory:'',
    // timeStr: getCurrentDate(),
  },
  form: {
    productrecordId: '',
@@ -121,7 +125,7 @@
  rules: {
    inboundTime: [{ required: true, message: "请选择", trigger: "change" }],
    inboundQuantity: [{ required: true, message: "请输入", trigger: "blur" }],
    nickname: [{ required: true, message: "请选择", trigger: "change" }]
    nickName: [{ required: true, message: "请选择", trigger: "change" }]
  }
})
const { searchForm, form, rules } = toRefs(data)
@@ -139,11 +143,14 @@
}
const getList = () => {
  tableLoading.value = true
  getStockInPage({ ...searchForm.value, ...page }).then(res => {
  const params = { ...page }
  params.productCategory = searchForm.value.productCategory
  params.flag = true
  const apiCall = getInPageByCustom(params)
  apiCall.then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
    console.log('res', res.data.records)
  }).catch(() => {
    tableLoading.value = false
  })
@@ -187,20 +194,34 @@
  currentRowNum.value = row.inboundNum0
  salesLedgerProductId.value = row.salesLedgerProductId
  form.value = {}
  // åˆå§‹åŒ–表单数据
  form.value = {
    productrecordId: '',
    inboundQuantity: '', // å‡ºåº“数量清空
    inboundTime: getCurrentDate(), // é»˜è®¤å½“前日期
    nickName: '', // é»˜è®¤å½“前用户
  }
  console.log('form',form.value)
  // åŠ è½½ç”¨æˆ·åˆ—è¡¨
  try {
    const userLists = await userListNoPageByTenantId()
    userList.value = userLists.data
    // ææ–™å‡ºåº“默认发料人
    const defaultPersonName = '吴玉梅'
    const defaultPerson = userList.value.find(user => user.nickName === defaultPersonName)
    const defaultUserId = defaultPerson ? defaultPerson.userId : ''
    // åˆå§‹åŒ–表单数据
    form.value = {
      productrecordId: '',
      inboundQuantity: currentRowNum.value, // ä½¿ç”¨currentRowNum作为默认值
      inboundTime: getCurrentDate(), // é»˜è®¤å½“前日期
      nickName: defaultUserId, // æ ¹æ®tab类型设置默认发货人
    }
    console.log('form',form.value)
  } catch (error) {
    console.error('加载用户列表失败:', error)
    // å¦‚果加载失败,使用空值初始化
    form.value = {
      productrecordId: '',
      inboundQuantity: currentRowNum.value,
      inboundTime: getCurrentDate(),
      nickName: '',
    }
  }
}
@@ -214,10 +235,11 @@
    if (valid && currentRowId.value) {
      const outData = {
        id: currentRowId.value, // åŽŸå§‹è®°å½•ID
        salesLedgerProductId: salesLedgerProductId.value,
        salesLedgerProductId: 0,
        quantity: form.value.inboundQuantity, // å‡ºåº“数量
        time: form.value.inboundTime, // å‡ºåº“æ—¶é—´
        userId: form.value.nickName // æ“ä½œäºº
        userId: form.value.nickName, // æ“ä½œäºº
        type: 3 // å‡ºåº“类型:自定义/材料
      }
      console.log(outData)
@@ -240,14 +262,15 @@
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm(
    '是否确认导出?',
    '导出', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }
      '是否确认导出?',
      '导出', {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      }
  ).then(() => {
    proxy.download("/stockin/export", {}, '入库台账.xlsx')
    const exportUrl = "/stockin/exportTwo"
    proxy.download(exportUrl, {}, '出库台账.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
@@ -262,12 +285,12 @@
    return
  }
  ElMessageBox.confirm(
    '选中的内容将被删除,是否确认删除?',
    '导出', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }
      '选中的内容将被删除,是否确认删除?',
      '导出', {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      }
  ).then(() => {
    delStockManage(ids).then(res => {
      proxy.$modal.msgSuccess("删除成功")
@@ -277,9 +300,38 @@
    proxy.$modal.msg("已取消")
  })
}
// æ ¹æ®tab类型获取弹框标题
const getDialogTitle = () => {
  return '新增发料';
};
// æ ¹æ®tab类型获取可出库数量文本
const getAvailableQuantityText = () => {
  return '可发料数量';
};
// æ ¹æ®tab类型获取数量字段标签
const getQuantityLabel = () => {
  return '发料数量:';
};
// æ ¹æ®tab类型获取日期字段标签
const getDateLabel = () => {
  return '发料日期:';
};
// æ ¹æ®tab类型获取人员字段标签
const getPersonLabel = () => {
  return '发料人:';
};
onMounted(() => {
  getList()
})
</script>
<style scoped lang="scss"></style>
src/views/inventoryManagement/receiptManagement/index.vue
@@ -1,36 +1,212 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <el-tab-pane v-for="tab in tabs"
                   :label="tab.label"
                   :name="tab.name"
                   :key="tab.name">
        <record :type="tab.type" v-if="activeTab === tab.name" />
      </el-tab-pane>
    </el-tabs>
    <div class="search_form">
      <div>
        <span class="search_title ml10">入库日期:</span>
        <el-date-picker
            v-model="searchForm.timeStr"
            type="date"
            placeholder="请选择日期"
            value-format="YYYY-MM-DD"
            format="YYYY-MM-DD"
            clearable
            @change="handleQuery"
        />
        <span class="search_title ml10">产品大类:</span>
        <el-input
            v-model="searchForm.productCategory"
            style="width: 240px"
            placeholder="请输入"
            clearable
        />
        <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">
      <el-table
          :data="tableData"
          border
          v-loading="tableLoading"
          @selection-change="handleSelectionChange"
          :expand-row-keys="expandedRowKeys"
          :row-key="row => row.id"
          show-summary
          style="width: 100%"
          :summary-method="summarizeMainTable"
          height="calc(100vh - 18.5em)"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="入库时间" prop="inboundDate" width="100" show-overflow-tooltip />
        <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
        <el-table-column label="物品类型" prop="itemType" show-overflow-tooltip />
        <el-table-column label="入库数量" prop="inboundNum" width="100" show-overflow-tooltip />
        <el-table-column label="剩余库存" prop="inboundNum0" show-overflow-tooltip />
        <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150" />
        <el-table-column label="总价(元)" prop="taxInclusiveTotalPrice" width="150" />
        <el-table-column label="入库人" prop="createBy" width="80" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" width="100" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">编辑</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
          v-show="total > 0"
          :total="total"
          layout="total, sizes, prev, pager, next, jumper"
          :page="page.current"
          :limit="page.size"
          @pagination="paginationChange"
      />
    </div>
    <form-dia-manual ref="formDiaManual" @close="handleQuery" @success="handleQuery"></form-dia-manual>
  </div>
</template>
<script setup>
import Record from "@/views/inventoryManagement/receiptManagement/Record.vue";
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance, nextTick } from 'vue'
import { ElMessageBox } from "element-plus";
import useUserStore from '@/store/modules/user'
import dayjs from 'dayjs'
import {
  delStockInCustom,
  getInPageByCustom,
} from "@/api/inventoryManagement/stockIn.js";
import FormDiaManual from './components/formDiaManual.vue'
import { getCurrentDate } from "@/utils/index.js";
const activeTab = ref('qualified')
const type = ref(0)
const tabs = ref([
  {
    label: '合格入库',
    name: 'qualified',
    type: '0'
const { proxy } = getCurrentInstance()
const tableData = ref([])
const selectedRows = ref([])
const tableLoading = ref(false)
const formDiaManual = ref()
const page = reactive({
  current: 1,
  size: 100,
})
const total = ref(0)
const data = reactive({
  searchForm: {
    productCategory:'',
    timeStr: getCurrentDate(),
  },
  {
    label: '不合格入库',
    name: 'unqualified',
    type: '1'
  }
])
const handleTabChange = (tabName) => {
  activeTab.value = tabName;
  type.value = tabName === 'qualified' ? 0 : 1
})
const { searchForm } = toRefs(data)
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1
  getList()
}
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList()
}
const getList = () => {
  tableLoading.value = true
  const params = { ...page }
  params.timeStr = searchForm.value.timeStr
  params.productCategory = searchForm.value.productCategory
  params.flag = false
  const apiCall = getInPageByCustom(params)
  apiCall.then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    // å‰ç«¯è®¡ç®—含税总价:含税总价 = taxInclusiveUnitPrice * å…¥åº“数量
    tableData.value = tableData.value.map(item => {
      const inboundNum = Number(item.inboundNum) || 0
      const taxInclusiveUnitPrice = Number(item.taxInclusiveUnitPrice) || 0
      item.taxInclusiveTotalPrice = (taxInclusiveUnitPrice * inboundNum).toFixed(2)
      return item
    })
    total.value = res.data.total
  }).catch(() => {
    tableLoading.value = false
  })
}
// æ‰“开弹框
const openForm = async (type, row) => {
  await nextTick(() => {
    formDiaManual.value?.openDialog(type, row)
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection.filter(item => item.id)
}
const expandedRowKeys = ref([])
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(param, ['contractAmount', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice'])
}
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm('是否确认导出?', '导出', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }).then(() => {
    const exportUrl = "/stockin/exportTwo"
    proxy.download(exportUrl, {}, '入库台账.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
// åˆ é™¤
const handleDelete = () => {
  if (selectedRows.value.length === 0) {
    proxy.$modal.msgWarning('请选择数据')
    return
  }
  const ids = selectedRows.value.map(item => item.id)
  ElMessageBox.confirm('选中的内容将被删除,是否确认删除?', '删除', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }).then(() => {
    // ææ–™å…¥åº“删除
    delStockInCustom(ids).then(() => {
      proxy.$modal.msgSuccess("删除成功")
      getList()
    }).catch(() => {
      proxy.$modal.msgError("删除失败")
    })
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
onMounted(() => {
  getList()
})
</script>
<style scoped lang="scss"></style>
src/views/inventoryManagement/stockManagement/index.vue
@@ -1,33 +1,389 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <el-tab-pane v-for="tab in tabs"
                   :label="tab.label"
                   :name="tab.name"
                   :key="tab.name">
        <component :is="tab.name === 'qualified' ? QualifiedRecord : UnqualifiedRecord" />
      </el-tab-pane>
    </el-tabs>
    <div class="search_form">
      <div>
        <span class="search_title ml10">入库日期:</span>
        <el-date-picker
            v-model="searchForm.timeStr"
            type="date"
            placeholder="请选择日期"
            value-format="YYYY-MM-DD"
            format="YYYY-MM-DD"
            clearable
            @change="handleQuery"
        />
        <span class="search_title ml10">产品大类:</span>
        <el-input
            v-model="searchForm.productCategory"
            style="width: 240px"
            placeholder="请输入"
            clearable
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        <el-button @click="handleOut">导出</el-button>
      </div>
    </div>
    <div class="table_list">
      <el-table
          :data="tableData"
          border
          v-loading="tableLoading"
          @selection-change="handleSelectionChange"
          :expand-row-keys="expandedRowKeys"
          :row-key="row => row.id"
          show-summary
          style="width: 100%"
          :row-class-name="tableRowClassName"
          :summary-method="summarizeMainTable"
          height="calc(100vh - 18.5em)"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="入库日期" prop="inboundDate" width="100" show-overflow-tooltip />
        <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" width="80" show-overflow-tooltip />
        <el-table-column label="物品类型" prop="itemType" width="120" show-overflow-tooltip />
        <el-table-column label="已发料数量" prop="totalInboundNum" width="100" show-overflow-tooltip />
        <el-table-column label="剩余库存" prop="inboundNum0" width="100" show-overflow-tooltip />
        <el-table-column label="预警值" prop="warnNum" width="100" show-overflow-tooltip />
        <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150" />
        <el-table-column label="总价(元)" prop="taxInclusiveTotalPrice" width="150" />
        <el-table-column fixed="right" label="操作" width="100" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row);">编辑</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
          v-show="total > 0"
          :total="total"
          layout="total, sizes, prev, pager, next, jumper"
          :page="page.current"
          :limit="page.size"
          @pagination="paginationChange"
      />
    </div>
    <!-- ææ–™åº“存弹框 -->
    <FormDiaManual
        v-model:dialogFormVisible="manualDialogVisible"
        :operationType="operationType"
        :formData="form"
        @submit="submitForm"
        @close="closeDia"
    />
  </div>
</template>
<script setup>
import QualifiedRecord from "@/views/inventoryManagement/stockManagement/Qualified.vue";
import UnqualifiedRecord from "@/views/inventoryManagement/stockManagement/Unqualified.vue";
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
import { ElMessageBox } from "element-plus";
import useUserStore from '@/store/modules/user'
import { userListNoPageByTenantId } from "@/api/system/user.js";
import { productTreeList,modelList } from "@/api/basicData/product.js"
import {
  getStockManagePageByCustom,
  delStockManage,
} from "@/api/inventoryManagement/stockManage.js";
import {
  updateManagement, updateManagementByCustom, updateStockIn
} from "@/api/inventoryManagement/stockIn.js";
import { getCurrentDate } from "@/utils/index.js";
const activeTab = ref('qualified')
const tabs = ref([
  {
    label: '合格库存',
    name: 'qualified'
// å¯¼å…¥ææ–™åº“存弹框组件
import FormDiaManual from './components/FormDiaManual.vue'
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const tableData = ref([])
const productData = ref([])
const selectedRows = ref([])
const userList = ref([])
const productList = ref([])
const productModelList = ref([])
// const customerOption = ref([])
const tableLoading = ref(false)
const page = reactive({
  current: 1,
  size: 100,
})
const total = ref(0)
const fileList = ref([])
const loading = ref(false);
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref('')
// ææ–™åº“存弹框显示状态
const manualDialogVisible = ref(false)
const data = reactive({
  searchForm: {
    // supplierName: '',
    productCategory:'',
    customerName: '',
    timeStr: getCurrentDate(),
  },
  {
    label: '不合格库存',
    name: 'unqualified'
  form: {
    supplierId: null,
    productId: null,
    productName: '',
    userId: userStore.userId,
    nickName: '',
    productModelId: null,
    model: '',
    unit: '',
    productrecordId: null,
    taxInclusiveUnitPrice: '',
    taxInclusiveTotalPrice: '',
    taxRate: '',
    taxExclusiveTotalPrice: '',
    inboundTime: '',
    inboundBatch: '',
    stockQuantity: '',
    boundTime: '',
    warnNum: '', // æ–°å¢žæœ€ä½Žåº“存字段
    salesLedgerProductId: null,
  },
  rules: {
    // supplierName: [{ required: true, message: '请输入供应商名称', trigger: 'blur' }],
    productCategory: [{ required: true, message: '请选择产品大类', trigger: 'change' }],
    specificationModel: [{ required: true, message: '请输入规格型号', trigger: 'blur' }],
    unit: [{ required: true, message: '请输入单位', trigger: 'blur' }],
    stockQuantity: [{ required: true, message: '请输入出库数量', trigger: 'blur' }],
    unitPrice: [{ required: true, message: '请输入单价', trigger: 'blur' }], // æ·»åŠ æˆå“åº“å­˜å•ä»·çš„éªŒè¯è§„åˆ™
    taxInclusiveUnitPrice: [{ required: true, message: '请输入含税单价', trigger: 'blur' }],
    taxInclusiveTotalPrice: [{ required: true, message: '请输入含税总价', trigger: 'blur' }],
    taxRate: [{ required: true, message: '请输入税率', trigger: 'blur' }],
    taxExclusiveTotalPrice: [{ required: true, message: '请输入不含税总价', trigger: 'blur' }],
    boundTime: [{ required: true, message: '请选择库存时间', trigger: 'change' }],
    inboundTime: [{ required: true, message: '请选择入库时间', trigger: 'change' }],
    inboundPerson: [{ required: true, message: '请选择出库人', trigger: 'change' }],
    warnNum: [{ required: true, message: '请输入最低库存', trigger: 'blur' }],
  }
])
})
const { searchForm, form, rules } = toRefs(data)
const handleTabChange = (tabName) => {
  activeTab.value = tabName;
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1
  getList()
}
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList()
}
const buildQueryParams = () => {
  const params = {
    ...page,
    timeStr: searchForm.value.timeStr,
  }
  params.productCategory = searchForm.value.productCategory
  return params
}
const getList = () => {
  tableLoading.value = true
  const params = buildQueryParams()
  getStockManagePageByCustom(params).then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    // ä¸ºè¡¨æ ¼æ•°æ®è‡ªåŠ¨è®¡ç®—æ€»ä»·
    tableData.value = tableData.value.map(item => {
      // è®¡ç®—剩余库存
      const stockQuantity = parseFloat(item.inboundNum) || 0
      const outboundQuantity = parseFloat(item.totalInboundNum) || 0
      const remainingStock = Math.max(stockQuantity - outboundQuantity, 0)
      // ææ–™åº“存:含税总价 = å«ç¨Žå•ä»· Ã— å‰©ä½™åº“å­˜
      const taxInclusiveUnitPrice = parseFloat(item.taxInclusiveUnitPrice) || 0
      item.taxInclusiveTotalPrice = (taxInclusiveUnitPrice * remainingStock).toFixed(2)
      return item
    })
    total.value = res.data.total
    // æ•°æ®åŠ è½½å®ŒæˆåŽæ£€æŸ¥åº“å­˜
    // checkStockAndCreatePurchase();
  }).catch(() => {
    tableLoading.value = false
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  // è¿‡æ»¤æŽ‰å­æ•°æ®
  selectedRows.value = selection.filter(item => item.id);
  console.log('selection', selectedRows.value)
}
const expandedRowKeys = ref([])
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(param, ['contractAmount', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']);
};
// è¡¨æ ¼è¡Œç±»å
const tableRowClassName = ({ row }) => {
  const stock = Number(row?.inboundNum0 ?? 0);
  const warn = Number(row?.warnNum ?? 0);
  if (!Number.isFinite(stock) || !Number.isFinite(warn)) {
    return '';
  }
  return stock < warn ? 'row-low-stock' : '';
};
// æ‰“开弹框
const openForm = async (type, row) => {
  console.log(row)
  operationType.value = type
  form.value = {}
  productData.value = []
  let userLists = await userListNoPageByTenantId()
  userList.value = userLists.data
  if (type === 'edit') {
    form.value = { ...row }
    productTreeList().then(res =>{
      productList.value = res
      productList.value.forEach(i =>{
        if (i.label === row.productCategory) {
          modelList({ id: i.id }).then((res) => {
            productModelList.value = res;
          });
        }
      })
    })
  }
  form.value.entryDate = getCurrentDate() // è®¾ç½®é»˜è®¤å½•入日期为当前日期
  // ä»…材料库存弹框
  manualDialogVisible.value = true
}
// æäº¤è¡¨å•
const submitForm = (submittedData) => {
  console.log('子组件提交的数据:', submittedData)
  // ä½¿ç”¨å­ç»„件提交的数据,而不是父组件的form对象
  const submitData = { ...submittedData }
  // ææ–™åº“存:移除含税总价字段
  delete submitData.taxInclusiveTotalPrice
  // ç§»é™¤å…¶ä»–可能的总价字段
  delete submitData.taxExclusiveTotalPrice
  console.log('提交给后端的数据(已移除总价字段):', submitData)
  // ææ–™åº“存使用 updateManagementByCustom æŽ¥å£
  updateManagementByCustom(submitData).then(res => {
    proxy.$modal.msgSuccess("提交成功")
    closeDia()
    getList()
    // æäº¤åŽæ£€æŸ¥åº“存并尝试创建请购单
    // checkStockAndCreatePurchase();
  }).catch(error => {
    console.error('提交失败:', error)
    proxy.$modal.msgError("提交失败,请重试")
  })
}
// æ£€æŸ¥åº“存并创建请购单
// const checkStockAndCreatePurchase = async () => {
//   const stockList = tableData.value;
//   // handList()
//   for (const item of stockList) {
//     if (item.inboundNum0 < item.warnNum) {
//       try {
//                 const stockInData = {
//                     id: item.id,
//                     quantityStock: item.warnNum + item.totalInboundNum,// ä½¿ç”¨æ–°æ ¼å¼åŒ–函数
//                 };
//                 loading.value = true
//                 await updateStockIn(stockInData)
//                 proxy.$modal.msgSuccess(`产品 ${item.productCategory} ä¿®æ”¹å…¥åº“成功`)
//                 loading.value = false
//       } catch (error) {
//         proxy.$modal.msgError(`产品 ${item.productCategory} ç”Ÿæˆè¯·è´­å•失败,请手动处理`);
//
//       }
//     }
//   }
// };
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef")
  manualDialogVisible.value = false
}
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm(
      '是否确认导出?',
      '导出', {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      }
  ).then(() => {
    const exportParams = buildQueryParams()
    const exportUrl = "/stockin/exportCopyTwo"
    proxy.download(exportUrl, exportParams, '库存信息.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
// åˆ é™¤
const handleDelete = () => {
  let ids = []
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning('请选择数据')
    return
  }
  ElMessageBox.confirm(
      '选中的内容将被删除,是否确认删除?',
      '导出', {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      }
  ).then(() => {
    delStockManage({ids:ids}).then(res => {
      proxy.$modal.msgSuccess("删除成功")
      getList()
    })
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
onMounted(() => {
  getList()
  // checkStockAndCreatePurchase();
  // æ¯å°æ—¶æ£€æŸ¥ä¸€æ¬¡åº“å­˜
  // const intervalId = setInterval(checkStockAndCreatePurchase, 60 * 60 * 1000);
// onUnmounted(() => {
//   // ç»„件卸载时清除定时器
//   clearInterval(intervalId);
// });
})
</script>
<style scoped lang="scss">
:deep(.row-low-stock td) {
  background-color: #fde2e2;
  color: #c45656;
}
:deep(.row-low-stock:hover > td) {
  background-color: #fcd4d4;
}
</style>
src/views/personnelManagement/contractManagement/components/formDia.vue
@@ -19,17 +19,15 @@
        </div>
      </template>
    </el-dialog>
    <Files ref="filesDia"></Files>
  </div>
</template>
<script setup>
import {ref} from "vue";
import {findStaffContractListPage} from "@/api/personnelManagement/staffContract.js";
const Files = defineAsyncComponent(() => import( "@/views/personnelManagement/contractManagement/filesDia.vue"));
import {staffOnJobInfo} from "@/api/personnelManagement/employeeRecord.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const filesDia = ref()
const dialogFormVisible = ref(false);
const operationType = ref('')
const tableColumn = ref([
@@ -45,22 +43,6 @@
    label: "合同结束日期",
    prop: "contractEndTime",
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: 'right',
    width: 120,
    operation: [
      {
        name: "上传附件",
        type: "text",
        clickFun: (row) => {
          filesDia.value.openDialog( row,'合同')
        },
      }
    ],
  },
]);
const tableData = ref([]);
const tableLoading = ref(false);
@@ -70,15 +52,10 @@
  operationType.value = type;
  dialogFormVisible.value = true;
  if (operationType.value === 'edit') {
    findStaffContractListPage({staffOnJobId: row.id}).then(res => {
      tableData.value = res.data.records
    staffOnJobInfo({staffNo: row.staffNo}).then(res => {
      tableData.value = res.data
    })
  }
}
const openUploadFile = (row) => {
  filesDia.value.open = true
  filesDia.value.row = row
}
// å…³é—­å¼¹æ¡†
@@ -93,4 +70,4 @@
<style scoped>
</style>
</style>
src/views/personnelManagement/contractManagement/filesDia.vue
@@ -30,10 +30,16 @@
          :isSelection="true"
          @selection-change="handleSelectionChange"
          height="500"
          @pagination="paginationSearch"
          :total="page.total"
      >
      </PIMTable>
      <pagination
          style="margin: 10px 0"
          v-show="total > 0"
          @pagination="paginationSearch"
          :total="total"
          :page="page.current"
          :limit="page.size"
      />
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
@@ -90,8 +96,8 @@
  },
]);
const page = reactive({
    current: 1,
    size: 100,
  current: 1,
  size: 100,
});
const total = ref(0);
const tableData = ref([]);
@@ -111,14 +117,14 @@
  getList()
}
const paginationSearch = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  fileListPage({accountId: currentId.value,accountType:accountType.value, ...page}).then(res => {
    tableData.value = res.data.records;
        total.value = res.data.total;
    total.value = res.data.total;
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
@@ -193,4 +199,4 @@
<style scoped>
</style>
</style>
src/views/personnelManagement/contractManagement/index.vue
@@ -4,44 +4,44 @@
      <div>
        <span class="search_title">姓名:</span>
        <el-input v-model="searchForm.staffName" style="width: 240px" placeholder="请输入姓名搜索" @change="handleQuery"
          clearable :prefix-icon="Search" />
                  clearable :prefix-icon="Search" />
        <span style="margin-left: 10px" class="search_title">合同结束日期:</span>
        <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
          placeholder="请选择" clearable @change="changeDaterange" />
                        placeholder="请选择" clearable @change="changeDaterange" />
        <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 type="info" @click="handleImport">导入</el-button>-->
        <!--        <el-button type="info" @click="handleImport">导入</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"
        :total="page.total"></PIMTable>
                @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination"
                :total="page.total"></PIMTable>
    </div>
    <form-dia ref="formDia" @close="handleQuery"></form-dia>
    <!-- åˆåŒå¯¼å…¥å¯¹è¯æ¡† -->
    <el-dialog
      :title="upload.title"
      v-model="upload.open"
      width="400px"
      append-to-body
        :title="upload.title"
        v-model="upload.open"
        width="400px"
        append-to-body
    >
      <el-upload
        ref="uploadRef"
        :limit="1"
        accept=".xlsx, .xls"
        :headers="upload.headers"
        :action="upload.url + '?updateSupport=' + upload.updateSupport"
        :disabled="upload.isUploading"
        :on-progress="handleFileUploadProgress"
        :on-success="handleFileSuccess"
        :auto-upload="false"
        drag
          ref="uploadRef"
          :limit="1"
          accept=".xlsx, .xls"
          :headers="upload.headers"
          :action="upload.url + '?updateSupport=' + upload.updateSupport"
          :disabled="upload.isUploading"
          :on-progress="handleFileUploadProgress"
          :on-success="handleFileSuccess"
          :auto-upload="false"
          drag
      >
        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
@@ -74,16 +74,19 @@
import { onMounted, ref } from "vue";
import FormDia from "@/views/personnelManagement/contractManagement/components/formDia.vue";
import { ElMessageBox } from "element-plus";
import { staffOnJobListPage } from "@/api/personnelManagement/staffOnJob.js";
import { staffOnJobListPage } from "@/api/personnelManagement/employeeRecord.js";
import dayjs from "dayjs";
import { getToken } from "@/utils/auth.js";
import FilesDia from "./filesDia.vue";
const data = reactive({
  searchForm: {
    staffName: "",
    entryDate: null, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
    entryDate: [
      dayjs().format("YYYY-MM-DD"),
      dayjs().add(1, "day").format("YYYY-MM-DD"),
    ], // å½•入日期
    entryDateStart: dayjs().format("YYYY-MM-DD"),
    entryDateEnd: dayjs().add(1, "day").format("YYYY-MM-DD"),
  },
});
const { searchForm } = toRefs(data);
@@ -124,7 +127,7 @@
    prop: "sex",
  },
  {
    label: "户籍住址",
    label: "籍贯",
    prop: "nativePlace",
  },
  {
@@ -132,7 +135,7 @@
    prop: "postJob",
  },
  {
    label: "现住址",
    label: "家庭住址",
    prop: "adress",
    width: 200
  },
@@ -144,6 +147,11 @@
    label: "专业",
    prop: "profession",
    width: 100
  },
  {
    label: "身份证号",
    prop: "identityCard",
    width: 200
  },
  {
    label: "年龄",
@@ -164,10 +172,10 @@
    prop: "emergencyContactPhone",
    width: 150
  },
  // {
  //   label: "合同年限",
  //   prop: "contractTerm",
  // },
  {
    label: "合同年限",
    prop: "contractTerm",
  },
  // {
  //   label: "合同开始日期",
  //   prop: "contractStartTime",
@@ -191,7 +199,14 @@
        clickFun: (row) => {
          openForm("edit", row);
        },
      }
      },
      {
        name: "附件",
        type: "text",
        clickFun: (row) => {
          openFilesFormDia(row);
        },
      },
    ],
  },
]);
@@ -238,7 +253,6 @@
  tableLoading.value = true;
  const params = { ...searchForm.value, ...page };
  params.entryDate = undefined
  params.staffState = 1
  staffOnJobListPage(params).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
@@ -265,12 +279,12 @@
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/staff/staffOnJob/export", {staffState: 1}, "合同管理.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
      .then(() => {
        proxy.download("/staff/staffOnJob/export", {}, "合同管理.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
const upload = reactive({
  // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(合同导入)
src/views/personnelManagement/dimission/components/formDia.vue
@@ -6,146 +6,133 @@
        width="70%"
        @close="closeDia"
    >
      <!-- å‘˜å·¥ä¿¡æ¯å±•示区域 -->
      <div class="info-section">
        <div class="info-title">员工信息</div>
        <el-form :model="form" label-width="200px" label-position="left" :rules="rules" ref="formRef" style="margin-top: 20px">
          <el-row :gutter="30">
            <el-col :span="12">
              <el-form-item label="姓名:" prop="staffOnJobId">
                <el-select v-model="form.staffOnJobId"
                           placeholder="请选择人员"
                           style="width: 100%"
                           :disabled="operationType === 'edit'"
                           @change="handleSelect">
                  <el-option
                      v-for="item in personList"
                      :key="item.id"
                      :label="item.staffName"
                      :value="item.id"
                  />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="员工编号:">
                {{ currentStaffRecord.staffNo || '-' }}
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="30">
            <el-col :span="12">
              <el-form-item label="性别:">
                {{ currentStaffRecord.sex || '-' }}
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="户籍住址:">
                {{ currentStaffRecord.nativePlace || '-' }}
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="30">
            <el-col :span="12">
              <el-form-item label="岗位:">
                {{ currentStaffRecord.postName || '-' }}
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="现住址:">
                {{ currentStaffRecord.adress || '-' }}
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="30">
            <el-col :span="12">
              <el-form-item label="第一学历:">
                {{ currentStaffRecord.firstStudy || '-' }}
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="专业:">
                {{ currentStaffRecord.profession || '-' }}
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="30">
            <el-col :span="12">
              <el-form-item label="年龄:">
                {{ currentStaffRecord.age || '-' }}
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="30">
            <el-col :span="12">
              <el-form-item label="联系电话:">
                {{ currentStaffRecord.phone || '-' }}
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="紧急联系人:">
                {{ currentStaffRecord.emergencyContact || '-' }}
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="30">
            <el-col :span="12">
              <el-form-item label="紧急联系人联系电话:">
                {{ currentStaffRecord.emergencyContactPhone || '-' }}
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="30">
            <el-col :span="12">
              <el-form-item label="离职原因:" prop="reason">
                <el-select v-model="form.reason" placeholder="请选择离职原因" style="width: 100%" @change="handleSelectDimissionReason">
                  <el-option
                      v-for="(item, index) in dimissionReasonOptions"
                      :key="index"
                      :label="item.label"
                      :value="item.value"
                  />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="备注:" prop="remark" v-if="form.reason === 'other'">
                <el-input
                    v-model="form.remark"
                    type="textarea"
                    :rows="3"
                    placeholder="备注"
                    maxlength="500"
                    show-word-limit
      <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="staffName">
              <!-- <el-input v-model="form.staffName" placeholder="请输入" clearable/> -->
              <el-select v-model="form.staffName" placeholder="请选择人员" style="width: 100%" @change="handleSelect">
                <el-option
                    v-for="item in personList"
                    :key="item.id"
                    :label="item.staffName"
                    :value="item.staffName"
                />
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
<!--        <el-row :gutter="30">-->
<!--          <el-col :span="12">-->
<!--            <div class="info-item">-->
<!--              <span class="info-label">离职原因:</span>-->
<!--              <el-select v-model="form.reason" placeholder="请选择人员" style="width: 100%" @change="handleSelect">-->
<!--                <el-option-->
<!--                    v-for="(item, index) in dimissionReasonOptions"-->
<!--                    :key="index"-->
<!--                    :label="item.label"-->
<!--                    :value="item.value"-->
<!--                />-->
<!--              </el-select>-->
<!--            </div>-->
<!--          </el-col>-->
<!--          <el-col :span="12">-->
<!--            <div class="info-item">-->
<!--              <span class="info-label">员工编号:</span>-->
<!--              <span class="info-value">{{ form.staffNo || '-' }}</span>-->
<!--            </div>-->
<!--          </el-col>-->
<!--        </el-row>-->
      </div>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="员工编号:" prop="staffNo">
              <el-input v-model="form.staffNo" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="性别:" prop="sex">
              <el-select v-model="form.sex" disabled>
                <el-option label="男" value="男" />
                <el-option label="女" value="女" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="籍贯:" prop="nativePlace">
              <el-input v-model="form.nativePlace" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="岗位:" prop="postJob">
              <el-input v-model="form.postJob" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="家庭住址:" prop="adress">
              <el-input v-model="form.adress" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="第一学历:" prop="firstStudy">
              <el-input v-model="form.firstStudy" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="专业:" prop="profession">
              <el-input v-model="form.profession" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="身份证号:" prop="identityCard">
              <el-input v-model="form.identityCard" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="年龄:" prop="age">
              <el-input-number v-model="form.age" :precision="0" :step="1" style="width: 100%" disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="联系电话:" prop="phone">
              <el-input v-model="form.phone" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="紧急联系人:" prop="emergencyContact">
              <el-input v-model="form.emergencyContact" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="紧急联系人联系电话:" prop="emergencyContactPhone">
              <el-input v-model="form.emergencyContactPhone" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="合同年限:" prop="contractTerm">
              <el-input-number v-model="form.contractTerm" :precision="0" :step="1" style="width: 100%" disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="合同开始日期:" prop="contractStartTime">
              <el-date-picker
                  disabled
                  v-model="form.contractStartTime"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="合同结束日期:" prop="contractEndTime">
              <el-date-picker
                  disabled
                  v-model="form.contractEndTime"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
@@ -157,9 +144,8 @@
</template>
<script setup>
import {ref, reactive, toRefs, getCurrentInstance} from "vue";
import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js";
import {createStaffLeave, updateStaffLeave} from "@/api/personnelManagement/staffLeave.js";
import {ref} from "vue";
import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate,getStaffOnJob} from "@/api/personnelManagement/onboarding.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
@@ -167,81 +153,78 @@
const operationType = ref('')
const data = reactive({
  form: {
    staffOnJobId: undefined,
    reason: "",
    remark: "",
    staffNo: "",
    staffName: "",
    sex: "",
    nativePlace: "",
    postJob: "",
    adress: "",
    firstStudy: "",
    profession: "",
    identityCard: "",
    age: 0,
    phone: "",
    emergencyContact: "",
    emergencyContactPhone: "",
    contractTerm: 0,
    contractStartTime: "",
    contractEndTime: "",
    staffState: "",
  },
  rules: {
    staffName: [{ required: true, message: "请选择人员" }],
    reason: [{ required: true, message: "请选择离职原因"}],
    staffNo: [{ required: true, message: "请输入", trigger: "blur" },],
    staffName: [{ required: true, message: "请输入", trigger: "blur" }],
    sex: [{ required: true, message: "请输入", trigger: "blur" }],
    nativePlace: [{ required: true, message: "请输入", trigger: "blur" }],
    postJob: [{ required: true, message: "请输入", trigger: "blur" }],
    adress: [{ required: true, message: "请输入", trigger: "blur" }],
    firstStudy: [{ required: true, message: "请输入", trigger: "blur" }],
    profession: [{ required: true, message: "请输入", trigger: "blur" }],
    identityCard: [{ required: true, message: "请输入", trigger: "blur" }],
    age: [{ required: true, message: "请输入", trigger: "blur" }],
    phone: [{ required: true, message: "请输入", trigger: "blur" }],
    emergencyContact: [{ required: true, message: "请输入", trigger: "blur" }],
    emergencyContactPhone: [{ required: true, message: "请输入", trigger: "blur" }],
    contractTerm: [{ required: true, message: "请输入", trigger: "blur" }],
    contractStartTime: [{ required: true, message: "请输入", trigger: "blur" }],
    contractEndTime: [{ required: true, message: "请输入", trigger: "blur" }],
  },
  dimissionReasonOptions: [
      {label: '薪资待遇', value: 'salary'},
      {label: '职业发展', value: 'career_development'},
      {label: '工作环境', value: 'work_environment'},
      {label: '个人原因', value: 'personal_reason'},
      {label: '其他', value: 'other'},
  ],
  currentStaffRecord: {},
});
const { form, rules, dimissionReasonOptions, currentStaffRecord } = toRefs(data);
const { form, rules } = toRefs(data);
// æ‰“开弹框
const openDialog = (type, row) => {
  getList()
  operationType.value = type;
  dialogFormVisible.value = true;
  if (operationType.value === 'edit') {
    currentStaffRecord.value = row
    form.value.staffOnJobId = row.staffOnJobId
    form.value.reason = row.reason
    form.value.remark = row.remark
    personList.value = [
      {
        staffName: row.staffName,
        id: row.staffOnJobId,
      }
    ]
  } else {
    getList()
  }
}
const handleSelectDimissionReason = (val) => {
  if (val === 'other') {
    form.value.remark = ''
    getStaffJoinInfo(row.id).then(res => {
      form.value = {...res.data}
    })
  }
}
// æäº¤äº§å“è¡¨å•
const submitForm = () => {
  form.value.staffState = 0
  if (form.value.reason !== 'other') {
    form.value.remark = ''
  }
  proxy.$refs["formRef"].validate(valid => {
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      form.value.staffState = 0
      if (operationType.value === "add") {
        createStaffLeave(form.value).then(res => {
        staffJoinAdd(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      } else {
        updateStaffLeave(currentStaffRecord.value.id, form.value).then(res => {
        staffJoinUpdate(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      }
    }
  })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  // è¡¨å•已注释,手动重置表单数据
  form.value = {
    staffOnJobId: undefined,
    reason: "",
    remark: "",
  };
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
  emit('close')
};
@@ -252,22 +235,50 @@
 * èŽ·å–å½“å‰åœ¨èŒäººå‘˜åˆ—è¡¨
 */
const getList = () => {
  staffOnJobListPage({
    current: -1,
    size: -1,
        staffState: 1
  }).then(res => {
    personList.value = res.data.records || []
  getStaffOnJob().then(res => {
    personList.value = res.data
  })
};
const handleSelect = (val) => {
  let obj = personList.value.find(item => item.id === val)
  currentStaffRecord.value = {}
  if (obj) {
    // ä¿ç•™ç¦»èŒæ—¥æœŸå’Œç¦»èŒåŽŸå› ï¼Œåªæ›´æ–°å‘˜å·¥ä¿¡æ¯
    currentStaffRecord.value = obj
  let obj = personList.value.find(item => item.staffName === val)
  let {
    sex,
    phone,
    staffNo,
    nativePlace,
    postJob,
    adress,
    firstStudy,
    profession,
    identityCard,
    age,
    emergencyContact,
    emergencyContactPhone,
    contractTerm,
    contractStartTime,
    contractEndTime,
    staffName
  } = obj
  form.value = {
    sex,
    phone,
    staffNo,
    nativePlace,
    postJob,
    adress,
    firstStudy,
    profession,
    identityCard,
    age,
    emergencyContact,
    emergencyContactPhone,
    contractTerm,
    contractStartTime,
    contractEndTime,
    staffName
  }
}
defineExpose({
  openDialog,
@@ -275,39 +286,5 @@
</script>
<style scoped>
.info-section {
  background: #f5f7fa;
  padding: 20px;
  border-radius: 8px;
  margin-bottom: 20px;
}
.info-title {
  font-size: 16px;
  font-weight: 600;
  color: #303133;
  margin-bottom: 20px;
  padding-bottom: 10px;
  border-bottom: 1px solid #e4e7ed;
}
.info-item {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
  min-height: 32px;
}
.info-label {
  min-width: 140px;
  color: #606266;
  font-size: 14px;
  font-weight: 500;
}
.info-value {
  flex: 1;
  color: #303133;
  font-size: 14px;
}
</style>
</style>
src/views/personnelManagement/dimission/index.vue
@@ -11,6 +11,22 @@
            clearable
            :prefix-icon="Search"
        />
        <span style="margin-left: 10px;"  class="search_title">合同开始日期:</span>
        <el-date-picker
            v-model="searchForm.entryDateStart"
            type="date"
            placeholder="请选择合同开始日期"
            size="default"
            @change="(date) => handleDateChange(date,1)"
        />
        <span style="margin-left: 10px;" class="search_title">合同结束日期:</span>
        <el-date-picker
            v-model="searchForm.entryDateEnd"
            type="date"
            placeholder="请选择合同结束日期"
            size="default"
            @change="(date) => handleDateChange(date,2)"
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
        >搜索</el-button
        >
@@ -42,8 +58,9 @@
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import FormDia from "@/views/personnelManagement/dimission/components/formDia.vue";
import {findStaffLeaveListPage, batchDeleteStaffLeaves} from "@/api/personnelManagement/staffLeave.js";
import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js";
import {ElMessageBox} from "element-plus";
import dayjs from "dayjs";
const data = reactive({
  searchForm: {
@@ -88,19 +105,15 @@
    prop: "sex",
  },
  {
    label: "户籍住址",
    label: "籍贯",
    prop: "nativePlace",
  },
  {
    label: "部门",
    prop: "deptName",
  },
  {
    label: "岗位",
    prop: "postName",
    prop: "postJob",
  },
  {
    label: "现住址",
    label: "家庭住址",
    prop: "adress",
    width:200
  },
@@ -112,6 +125,11 @@
    label: "专业",
    prop: "profession",
    width:100
  },
  {
    label: "身份证号",
    prop: "identityCard",
    width:200
  },
  {
    label: "年龄",
@@ -133,10 +151,23 @@
    width:150
  },
  {
    label: "合同年限",
    prop: "contractTerm",
  },
  {
    label: "合同开始日期",
    prop: "contractStartTime",
    width: 120
  },
  {
    label: "合同结束日期",
    prop: "contractEndTime",
    width: 120
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: 'right',
    operation: [
      {
        name: "编辑",
@@ -160,6 +191,20 @@
const { proxy } = getCurrentInstance()
const handleDateChange = (value,type) => {
  searchForm.value.entryDateEnd = null
  searchForm.value.entryDateStart = null
  if(type === 1){
    if (value) {
      searchForm.value.entryDateStart = dayjs(value).format("YYYY-MM-DD");
    }
  }else{
    if (value) {
      searchForm.value.entryDateEnd = dayjs(value).format("YYYY-MM-DD");
    }
  }
  getList();
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
@@ -173,7 +218,7 @@
};
const getList = () => {
  tableLoading.value = true;
  findStaffLeaveListPage({...page, ...searchForm.value}).then(res => {
  staffJoinListPage({...page, ...searchForm.value, staffState: 0}).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
@@ -208,7 +253,7 @@
    type: "warning",
  })
      .then(() => {
        batchDeleteStaffLeaves(ids).then((res) => {
        staffJoinDel(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
@@ -225,7 +270,7 @@
    type: "warning",
  })
      .then(() => {
        proxy.download("/staff/staffLeave/export", {}, "人员离职.xlsx");
        proxy.download("/staff/staffJoinLeaveRecord/export", {staffState: 0}, "人员离职.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
src/views/personnelManagement/employeeRecord/components/RenewContract.vue
ÎļþÒÑɾ³ý
src/views/personnelManagement/employeeRecord/components/formDia.vue
ÎļþÃû´Ó src/views/personnelManagement/employeeRecord/components/Show.vue ÐÞ¸Ä
@@ -24,17 +24,17 @@
<script setup>
import {ref} from "vue";
import {staffOnJobInfo} from "@/api/personnelManagement/staffOnJob.js";
import {staffOnJobInfo} from "@/api/personnelManagement/employeeRecord.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
const operationType = ref('')
const tableColumn = ref([
  // {
  //   label: "合同年限",
  //   prop: "contractTerm",
  // },
  {
    label: "合同年限",
    prop: "contractTerm",
  },
  {
    label: "合同开始日期",
    prop: "contractStartTime",
@@ -70,4 +70,4 @@
<style scoped>
</style>
</style>
src/views/personnelManagement/employeeRecord/index.vue
@@ -19,9 +19,9 @@
        >
      </div>
      <div>
        <el-button type="primary" @click="openFormNewOrEditFormDia('add')">新增入职</el-button>
        <!--        <el-button type="primary" @click="openForm('add')">新增入职</el-button>-->
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
        <!--        <el-button type="danger" plain @click="handleDelete">删除</el-button>-->
      </div>
    </div>
    <div class="table_list">
@@ -37,38 +37,30 @@
          :total="page.total"
      ></PIMTable>
    </div>
    <show-form-dia ref="formDia" @close="handleQuery"></show-form-dia>
    <new-or-edit-form-dia ref="formDiaNewOrEditFormDia" @close="handleQuery"></new-or-edit-form-dia>
    <renew-contract
        v-if="isShowRenewContractModal"
        v-model:visible="isShowRenewContractModal"
        :id="id"
        @completed="handleQuery"
    />
    <form-dia ref="formDia" @close="handleQuery"></form-dia>
  </div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import FormDia from "@/views/personnelManagement/employeeRecord/components/formDia.vue";
import {ElMessageBox} from "element-plus";
import {batchDeleteStaffOnJobs, staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js";
import {staffOnJobListPage} from "@/api/personnelManagement/employeeRecord.js";
import dayjs from "dayjs";
const NewOrEditFormDia = defineAsyncComponent(() => import("@/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue"));
const ShowFormDia = defineAsyncComponent(() => import( "@/views/personnelManagement/employeeRecord/components/Show.vue"));
const RenewContract = defineAsyncComponent(() => import( "@/views/personnelManagement/employeeRecord/components/RenewContract.vue"));
const data = reactive({
  searchForm: {
    staffName: "",
    entryDate: undefined, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
    entryDate: [
      dayjs().format("YYYY-MM-DD"),
      dayjs().add(1, "day").format("YYYY-MM-DD"),
    ], // å½•入日期
    entryDateStart: dayjs().format("YYYY-MM-DD"),
    entryDateEnd: dayjs().add(1, "day").format("YYYY-MM-DD"),
  },
});
const { searchForm } = toRefs(data);
const isShowRenewContractModal = ref(false);
const id = ref(0);
const tableColumn = ref([
  {
    label: "状态",
@@ -106,19 +98,15 @@
    prop: "sex",
  },
  {
    label: "户籍住址",
    label: "籍贯",
    prop: "nativePlace",
  },
  {
    label: "部门",
    prop: "deptName",
  },
  {
    label: "岗位",
    prop: "postJob",
  },
  {
    label: "现住址",
    label: "家庭住址",
    prop: "adress",
    width:200
  },
@@ -130,6 +118,11 @@
    label: "专业",
    prop: "profession",
    width:100
  },
  {
    label: "身份证号",
    prop: "identityCard",
    width:200
  },
  {
    label: "年龄",
@@ -150,10 +143,10 @@
    prop: "emergencyContactPhone",
    width:150
  },
  // {
  //   label: "合同年限",
  //   prop: "contractTerm",
  // },
  {
    label: "合同年限",
    prop: "contractTerm",
  },
  // {
  //   label: "合同开始日期",
  //   prop: "contractStartTime",
@@ -169,31 +162,14 @@
    label: "操作",
    align: "center",
    fixed: 'right',
    width: 180,
    operation: [
      {
        name: "编辑",
        name: "详情",
        type: "text",
        clickFun: (row) => {
          openFormNewOrEditFormDia("edit", row);
          openForm("edit", row);
        },
      },
      {
        name: "续签合同",
        type: "text",
        showHide: row => row.staffState === 1,
        clickFun: (row) => {
          isShowRenewContractModal.value = true;
          id.value = row.id;
        },
      },
      // {
      //   name: "详情",
      //   type: "text",
      //   clickFun: (row) => {
      //     openForm("edit", row);
      //   },
      // },
    ],
  },
]);
@@ -206,7 +182,6 @@
  total: 0
});
const formDia = ref()
const formDiaNewOrEditFormDia = ref()
const { proxy } = getCurrentInstance()
const changeDaterange = (value) => {
@@ -233,7 +208,7 @@
  tableLoading.value = true;
  const params = { ...searchForm.value, ...page };
  params.entryDate = undefined
  staffOnJobListPage({...params}).then(res => {
  staffOnJobListPage({...params, staffState: 1}).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
@@ -252,37 +227,6 @@
    formDia.value?.openDialog(type, row)
  })
};
const openFormNewOrEditFormDia = (type, row) => {
  nextTick(() => {
    formDiaNewOrEditFormDia.value?.openDialog(type, row)
  })
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        batchDeleteStaffOnJobs(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
@@ -291,7 +235,7 @@
    type: "warning",
  })
      .then(() => {
        proxy.download("/staff/staffOnJob/export", {staffState: 1}, "员工台账.xlsx");
        proxy.download("/staff/staffOnJob/export", {staffState: 1}, "在职员工台账.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
src/views/personnelManagement/onboarding/components/formDia.vue
ÎļþÃû´Ó src/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue ÐÞ¸Ä
@@ -29,47 +29,20 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="户籍住址:" prop="nativePlace">
            <el-form-item label="籍贯:" prop="nativePlace">
              <el-input v-model="form.nativePlace" placeholder="请输入" clearable/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="岗位:" prop="sysPostId">
              <el-select v-model="form.sysPostId" placeholder="请选择岗位" clearable>
                <el-option
                    v-for="item in postOptions"
                    :key="item.postId"
                    :label="item.postName"
                    :value="item.postId"
                    :disabled="item.status === '1'"
                />
              </el-select>
            <el-form-item label="岗位:" prop="postJob">
              <el-input v-model="form.postJob" placeholder="请输入" clearable/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="现住址:" prop="adress">
            <el-form-item label="家庭住址:" prop="adress">
              <el-input v-model="form.adress" placeholder="请输入" clearable/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="部门:" prop="sysDeptId">
              <el-tree-select
                  v-model="form.sysDeptId"
                  :data="deptOptions"
                  :props="{ value: 'id', label: 'label', children: 'children' }"
                  value-key="id"
                  placeholder="请选择部门"
                  check-strictly
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="年龄:" prop="age">
              <el-input-number v-model="form.age" :precision="0" :step="1" style="width: 100%"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -82,6 +55,18 @@
          <el-col :span="12">
            <el-form-item label="专业:" prop="profession">
              <el-input v-model="form.profession" placeholder="请输入" clearable/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="身份证号:" prop="identityCard">
              <el-input v-model="form.identityCard" placeholder="请输入" clearable/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="年龄:" prop="age">
              <el-input-number v-model="form.age" :precision="0" :step="1" style="width: 100%"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -120,7 +105,7 @@
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
                                    @change="calculateContractTerm"
                  @change="calculateContractTerm"
              />
            </el-form-item>
          </el-col>
@@ -134,10 +119,30 @@
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
                                    @change="calculateContractTerm"
                  @change="calculateContractTerm"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="试用期(月):" prop="probationPeriod">
              <el-input-number v-model="form.probationPeriod" :precision="0" :step="1" min="0" style="width: 100%"/>
            </el-form-item>
          </el-col>
          <!-- <el-col :span="12">
            <el-form-item label="入职日期:" prop="entryDate">
              <el-date-picker
                  v-model="form.entryDate"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col> -->
        </el-row>
      </el-form>
      <template #footer>
@@ -151,17 +156,13 @@
</template>
<script setup>
import {ref, onMounted} from "vue";
import {findPostOptions} from "@/api/system/post.js";
import {listDept} from "@/api/system/dept.js";
import {staffOnJobInfo, createStaffOnJob, updateStaffOnJob} from "@/api/personnelManagement/staffOnJob.js";
import {deptTreeSelect} from "@/api/system/user.js";
import {ref, reactive, toRefs} from "vue";
import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
const operationType = ref('')
const id = ref(0)
const data = reactive({
  form: {
    staffNo: "",
@@ -172,6 +173,7 @@
    adress: "",
    firstStudy: "",
    profession: "",
    identityCard: "",
    age: 0,
    phone: "",
    emergencyContact: "",
@@ -180,8 +182,7 @@
    contractStartTime: "",
    contractEndTime: "",
    staffState: "",
    sysPostId: undefined,
    sysDeptId: undefined,
    probationPeriod: 3, // é»˜è®¤è¯•用期3个月
  },
  rules: {
    staffNo: [{ required: true, message: "请输入", trigger: "blur" },],
@@ -192,6 +193,7 @@
    adress: [{ required: true, message: "请输入", trigger: "blur" }],
    firstStudy: [{ required: true, message: "请输入", trigger: "blur" }],
    profession: [{ required: true, message: "请输入", trigger: "blur" }],
    identityCard: [{ required: true, message: "请输入", trigger: "blur" }],
    age: [{ required: true, message: "请输入", trigger: "blur" }],
    phone: [{ required: true, message: "请输入", trigger: "blur" }],
    emergencyContact: [{ required: true, message: "请输入", trigger: "blur" }],
@@ -200,76 +202,33 @@
    contractStartTime: [{ required: true, message: "请输入", trigger: "blur" }],
    contractEndTime: [{ required: true, message: "请输入", trigger: "blur" }],
  },
  postOptions: [], // å²—位选项
  deptOptions: [], // éƒ¨é—¨é€‰é¡¹
});
const { form, rules, postOptions, deptOptions } = toRefs(data);
const { form, rules } = toRefs(data);
// æ‰“开弹框
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  if (operationType.value === 'edit') {
    id.value = row.id
    staffOnJobInfo(id.value, {}).then(res => {
    getStaffJoinInfo(row.id).then(res => {
      form.value = {...res.data}
      if (form.value.sysPostId === 0) {
        form.value.sysPostId = undefined
      }
      // ç¼–辑时也计算一次合同年限
      calculateContractTerm();
    })
  } else {
        form.value.id = ''
    }
  }
}
onMounted(() => {
  fetchPostOptions()
  fetchDeptOptions()
})
const fetchPostOptions = () => {
  findPostOptions().then(res => {
    postOptions.value = res.data
  })
}
// æŸ¥è¯¢éƒ¨é—¨åˆ—表
const fetchDeptOptions = () => {
  deptTreeSelect().then(response => {
    deptOptions.value = filterDisabledDept(JSON.parse(JSON.stringify(response.data)))
  })
}
/** è¿‡æ»¤ç¦ç”¨çš„部门 */
function filterDisabledDept(deptList) {
  return deptList.filter(dept => {
    if (dept.disabled) {
      return false
    }
    if (dept.children && dept.children.length) {
      dept.children = filterDisabledDept(dept.children)
    }
    return true
  })
}
// æäº¤äº§å“è¡¨å•
const submitForm = () => {
  if (!form.value.sysPostId) {
    form.value.sysPostId = 0;
  }
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      form.value.staffState = 1
      if (operationType.value === "add") {
        createStaffOnJob(form.value).then(res => {
        staffJoinAdd(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      } else {
        updateStaffOnJob(id.value, form.value).then(res => {
        staffJoinUpdate(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
@@ -282,20 +241,20 @@
  if (form.value.contractStartTime && form.value.contractEndTime) {
    const startDate = new Date(form.value.contractStartTime);
    const endDate = new Date(form.value.contractEndTime);
    if (endDate > startDate) {
      // è®¡ç®—年份差
      const yearDiff = endDate.getFullYear() - startDate.getFullYear();
      const monthDiff = endDate.getMonth() - startDate.getMonth();
      const dayDiff = endDate.getDate() - startDate.getDate();
      let years = yearDiff;
      // å¦‚果结束日期的月日小于开始日期的月日,则减去1å¹´
      if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) {
        years = yearDiff - 1;
      }
      form.value.contractTerm = Math.max(0, years);
    } else {
      form.value.contractTerm = 0;
@@ -318,4 +277,4 @@
<style scoped>
</style>
</style>
src/views/personnelManagement/onboarding/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,348 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">姓名:</span>
        <el-input
            v-model="searchForm.staffName"
            style="width: 240px"
            placeholder="请输入姓名搜索"
            @change="handleQuery"
            clearable
            :prefix-icon="Search"
        />
        <span style="margin-left: 10px;"  class="search_title">合同开始日期:</span>
        <el-date-picker
            v-model="searchForm.entryDateStart"
            type="date"
            placeholder="请选择合同开始日期"
            size="default"
            @change="(date) => handleDateChange(date,1)"
        />
        <span style="margin-left: 10px;" class="search_title">合同结束日期:</span>
        <el-date-picker
            v-model="searchForm.entryDateEnd"
            type="date"
            placeholder="请选择合同结束日期"
            size="default"
            @change="(date) => handleDateChange(date,2)"
        />
        <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"
          :total="page.total"
      ></PIMTable>
    </div>
    <form-dia ref="formDia" @close="handleQuery"></form-dia>
  </div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import FormDia from "@/views/personnelManagement/onboarding/components/formDia.vue";
import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js";
import {ElMessageBox} from "element-plus";
import dayjs from "dayjs";
const data = reactive({
  searchForm: {
    staffName: "",
  },
});
const { searchForm } = toRefs(data);
const tableColumn = ref([
  {
    label: "状态",
    prop: "staffState",
    dataType: "tag",
    formatData: (params) => {
      if (params == 0) {
        return "离职";
      } else if (params == 1) {
        return "入职";
      } else {
        return null;
      }
    },
    formatType: (params) => {
      if (params == 0) {
        return "danger";
      } else if (params == 1) {
        return "primary";
      } else {
        return null;
      }
    },
  },
  {
    label: "员工编号",
    prop: "staffNo",
  },
  {
    label: "姓名",
    prop: "staffName",
  },
  {
    label: "性别",
    prop: "sex",
  },
  {
    label: "籍贯",
    prop: "nativePlace",
  },
  {
    label: "岗位",
    prop: "postJob",
  },
  {
    label: "家庭住址",
    prop: "adress",
    width:200
  },
  {
    label: "第一学历",
    prop: "firstStudy",
  },
  {
    label: "专业",
    prop: "profession",
    width:100
  },
  {
    label: "身份证号",
    prop: "identityCard",
    width:200
  },
  {
    label: "年龄",
    prop: "age",
  },
  {
    label: "联系电话",
    prop: "phone",
    width:150
  },
  {
    label: "紧急联系人",
    prop: "emergencyContact",
    width: 120
  },
  {
    label: "联系电话",
    prop: "emergencyContactPhone",
    width:150
  },
  {
    label: "试用期(月)",
    prop: "probationPeriod",
    width: 120,
  },
  // {
  //   label: "转正日期",
  //   prop: "probationEndDate",
  //   width: 120,
  //   formatData: (row) => {
  //     // ä¿®æ”¹ä¸ºä½¿ç”¨åˆåŒå¼€å§‹æ—¥æœŸè®¡ç®—转正日期
  //     if (row.contractStartTime && row.probationPeriod) {
  //       // è®¡ç®—转正日期(合同开始日期加上试用期月数)
  //       return dayjs(row.contractStartTime).add(row.probationPeriod, 'month').format('YYYY-MM-DD');
  //     }
  //     return '';
  //   },
  //   formatType: (row) => {
  //     // ä¿®æ”¹ä¸ºä½¿ç”¨åˆåŒå¼€å§‹æ—¥æœŸæ£€æŸ¥æ˜¯å¦ä¸´è¿‘转正(7天内)
  //     if (row.contractStartTime && row.probationPeriod) {
  //       const probationEndDate = dayjs(row.contractStartTime).add(row.probationPeriod, 'month');
  //       const daysUntilProbationEnd = probationEndDate.diff(dayjs(), 'day');
  //       if (daysUntilProbationEnd >= 0 && daysUntilProbationEnd <= 7) {
  //         return 'warning'; // ä½¿ç”¨è­¦å‘Šæ ·å¼æ ‡è®°ä¸´è¿‘转正的员工
  //       }
  //     }
  //     return '';
  //   }
  // },
  {
    label: "合同年限(年)",
    prop: "contractTerm",
    width: 120,
  },
  {
    label: "合同开始日期",
    prop: "contractStartTime",
    width: 120
  },
  {
    label: "合同结束日期",
    prop: "contractEndTime",
    width: 120
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: 'right',
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
        },
      },
    ],
  },
]);
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
  total: 0,
});
const formDia = ref()
const { proxy } = getCurrentInstance()
const handleDateChange = (value,type) => {
  searchForm.value.entryDateEnd = null
  searchForm.value.entryDateStart = null
  if(type === 1){
    if (value) {
      searchForm.value.entryDateStart = dayjs(value).format("YYYY-MM-DD");
    }
  }else{
    if (value) {
      searchForm.value.entryDateEnd = dayjs(value).format("YYYY-MM-DD");
    }
  }
  getList();
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  staffJoinListPage({...page, ...searchForm.value, staffState: 1}).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
    // æ£€æŸ¥æ˜¯å¦æœ‰ä¸´è¿‘转正的员工并提醒
    checkProbationEnding(tableData.value);
  }).catch(err => {
    tableLoading.value = false;
  })
};
// æ£€æŸ¥ä¸´è¿‘转正的员工并提醒
const checkProbationEnding = (data) => {
  const probationEndingSoon = [];
  data.forEach(item => {
    // ä¿®æ”¹ä¸ºä½¿ç”¨åˆåŒå¼€å§‹æ—¥æœŸæ£€æŸ¥
    if (item.contractStartTime && item.probationPeriod) {
      const probationEndDate = dayjs(item.contractStartTime).add(item.probationPeriod, 'month');
      const daysUntilProbationEnd = probationEndDate.diff(dayjs(), 'day');
      if (daysUntilProbationEnd >= 0 && daysUntilProbationEnd <= 7) {
        probationEndingSoon.push({
          staffName: item.staffName,
          probationEndDate: probationEndDate.format('YYYY-MM-DD'),
          daysLeft: daysUntilProbationEnd
        });
      }
    }
  });
  if (probationEndingSoon.length > 0) {
    let message = '以下员工将在7天内转正:\n';
    probationEndingSoon.forEach(item => {
      message += `${item.staffName}(${item.probationEndDate},还有${item.daysLeft}天)\n`;
    });
    // æ˜¾ç¤ºæé†’消息
    proxy.$modal.msgInfo(message);
  }
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// æ‰“开弹框
const openForm = (type, row) => {
  nextTick(() => {
    formDia.value?.openDialog(type, row)
  })
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        staffJoinDel(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        proxy.download("/staff/staffJoinLeaveRecord/export", {staffState: 1}, "人员入职.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
onMounted(() => {
  getList();
});
</script>
<style scoped></style>
src/views/personnelManagement/payrollManagement/components/formDia.vue
@@ -10,35 +10,35 @@
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="月份:" prop="payDate">
                            <el-date-picker
                                v-model="form.payDate"
                                type="month"
                                value-format="YYYY-MM-DD"
                                format="YYYY-MM"
                                placeholder="请选择月份"
                                clearable
                                :disabled="operationType === 'edit'"
                                style="width: 100%"
                            />
              <el-date-picker
                  v-model="form.payDate"
                  type="month"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM"
                  placeholder="请选择月份"
                  clearable
                  :disabled="operationType === 'edit'"
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="姓名:" prop="staffId">
                            <el-select v-model="form.staffId" placeholder="请选择人员" style="width: 100%" @change="handleSelect" :disabled="operationType === 'edit'">
                                <el-option
                                    v-for="item in personList"
                                    :key="item.id"
                                    :label="item.staffName"
                                    :value="item.id"
                                />
                            </el-select>
              <el-select v-model="form.staffId" placeholder="请选择人员" style="width: 100%" @change="handleSelect" :disabled="operationType === 'edit'">
                <el-option
                    v-for="item in personList"
                    :key="item.id"
                    :label="item.staffName"
                    :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="应出勤天数:" prop="shouldAttendedNum">
                            <el-input v-model="form.shouldAttendedNum" placeholder="请输入" clearable type="number"/>
              <el-input v-model="form.shouldAttendedNum" placeholder="请输入" clearable type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -110,48 +110,48 @@
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="社保公司:" prop="socialSecurityCompanies">
                            <el-input v-model="form.socialSecurityCompanies" :precision="0" :step="1" style="width: 100%" type="number"/>
              <el-input v-model="form.socialSecurityCompanies" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="社保合计:" prop="socialSecurityTotal">
                            <el-input v-model="form.socialSecurityTotal" :precision="0" :step="1" style="width: 100%" type="number"/>
              <el-input v-model="form.socialSecurityTotal" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="公积金个人:" prop="providentFundIndividuals">
                            <el-input v-model="form.providentFundIndividuals" :precision="0" :step="1" style="width: 100%" type="number"/>
              <el-input v-model="form.providentFundIndividuals" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="公积金公司:" prop="providentFundCompany">
                            <el-input v-model="form.providentFundCompany" :precision="0" :step="1" style="width: 100%" type="number"/>
              <el-input v-model="form.providentFundCompany" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="公积金合计:" prop="providentFundTotal">
                            <el-input v-model="form.providentFundTotal" :precision="0" :step="1" style="width: 100%" type="number"/>
              <el-input v-model="form.providentFundTotal" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="应税工资:" prop="taxableWaget">
                            <el-input v-model="form.taxableWaget" :precision="0" :step="1" style="width: 100%" type="number"/>
              <el-input v-model="form.taxableWaget" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="个人所得税:" prop="personalIncomeTax">
                            <el-input v-model="form.personalIncomeTax" :step="0.1" style="width: 100%" type="number"/>
              <el-input v-model="form.personalIncomeTax" :step="0.1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="实发工资:" prop="actualWages">
                            <el-input v-model="form.actualWages" style="width: 100%" type="number"/>
              <el-input v-model="form.actualWages" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -168,8 +168,8 @@
<script setup>
import {ref} from "vue";
import {getStaffJoinInfo, getStaffOnJob, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js";
import {compensationAdd, compensationUpdate} from "@/api/personnelManagement/payrollManagement.js";
import {staffOnJobInfo, staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
@@ -177,54 +177,54 @@
const operationType = ref('')
const data = reactive({
  form: {
        payDate: "",
    payDate: "",
    staffId: "",
        name: "",
        shouldAttendedNum: "",
        actualAttendedNum: "",
        basicSalary: "",
        postSalary: "",
        deductionAbsenteeism: "",
        sickLeaveDeductions: "",
        deductionPersonalLeave: "",
        forgetClockDeduct: "",
        performanceScore: "",
        performancePay: "",
        payableWages: "",
        socialSecurityIndividuals: "",
        socialSecurityCompanies: "",
        socialSecurityTotal: "",
        providentFundIndividuals: "",
        providentFundCompany: "",
        providentFundTotal: "",
        taxableWaget: "",
        personalIncomeTax: "",
        actualWages: "",
    name: "",
    shouldAttendedNum: "",
    actualAttendedNum: "",
    basicSalary: "",
    postSalary: "",
    deductionAbsenteeism: "",
    sickLeaveDeductions: "",
    deductionPersonalLeave: "",
    forgetClockDeduct: "",
    performanceScore: "",
    performancePay: "",
    payableWages: "",
    socialSecurityIndividuals: "",
    socialSecurityCompanies: "",
    socialSecurityTotal: "",
    providentFundIndividuals: "",
    providentFundCompany: "",
    providentFundTotal: "",
    taxableWaget: "",
    personalIncomeTax: "",
    actualWages: "",
  },
  rules: {
        payDate: [{ required: true, message: "请选择", trigger: "change" },],
        staffId: [{ required: true, message: "请选择", trigger: "change" },],
    payDate: [{ required: true, message: "请选择", trigger: "change" },],
    staffId: [{ required: true, message: "请选择", trigger: "change" },],
    staffName: [{ required: true, message: "请输入", trigger: "blur" }],
        shouldAttendedNum: [{ required: true, message: "请输入", trigger: "blur" }],
        actualAttendedNum: [{ required: true, message: "请输入", trigger: "blur" }],
        basicSalary: [{ required: true, message: "请输入", trigger: "blur" }],
        postSalary: [{ required: true, message: "请输入", trigger: "blur" }],
        deductionAbsenteeism: [{ required: true, message: "请输入", trigger: "blur" }],
        sickLeaveDeductions: [{ required: true, message: "请输入", trigger: "blur" }],
        deductionPersonalLeave: [{ required: true, message: "请输入", trigger: "blur" }],
        forgetClockDeduct: [{ required: true, message: "请输入", trigger: "blur" }],
        performanceScore: [{ required: true, message: "请输入", trigger: "blur" }],
        performancePay: [{ required: true, message: "请输入", trigger: "blur" }],
        payableWages: [{ required: true, message: "请输入", trigger: "blur" }],
        socialSecurityIndividuals: [{ required: true, message: "请输入", trigger: "blur" }],
        socialSecurityCompanies: [{ required: true, message: "请输入", trigger: "blur" }],
        socialSecurityTotal: [{ required: true, message: "请输入", trigger: "blur" }],
        providentFundIndividuals: [{ required: true, message: "请输入", trigger: "blur" }],
        providentFundCompany: [{ required: true, message: "请输入", trigger: "blur" }],
        providentFundTotal: [{ required: true, message: "请输入", trigger: "blur" }],
        taxableWaget: [{ required: true, message: "请输入", trigger: "blur" }],
        personalIncomeTax: [{ required: true, message: "请输入", trigger: "blur" }],
        actualWages: [{ required: true, message: "请输入", trigger: "blur" }],
    shouldAttendedNum: [{ required: true, message: "请输入", trigger: "blur" }],
    actualAttendedNum: [{ required: true, message: "请输入", trigger: "blur" }],
    basicSalary: [{ required: true, message: "请输入", trigger: "blur" }],
    postSalary: [{ required: true, message: "请输入", trigger: "blur" }],
    deductionAbsenteeism: [{ required: true, message: "请输入", trigger: "blur" }],
    sickLeaveDeductions: [{ required: true, message: "请输入", trigger: "blur" }],
    deductionPersonalLeave: [{ required: true, message: "请输入", trigger: "blur" }],
    forgetClockDeduct: [{ required: true, message: "请输入", trigger: "blur" }],
    performanceScore: [{ required: true, message: "请输入", trigger: "blur" }],
    performancePay: [{ required: true, message: "请输入", trigger: "blur" }],
    payableWages: [{ required: true, message: "请输入", trigger: "blur" }],
    socialSecurityIndividuals: [{ required: true, message: "请输入", trigger: "blur" }],
    socialSecurityCompanies: [{ required: true, message: "请输入", trigger: "blur" }],
    socialSecurityTotal: [{ required: true, message: "请输入", trigger: "blur" }],
    providentFundIndividuals: [{ required: true, message: "请输入", trigger: "blur" }],
    providentFundCompany: [{ required: true, message: "请输入", trigger: "blur" }],
    providentFundTotal: [{ required: true, message: "请输入", trigger: "blur" }],
    taxableWaget: [{ required: true, message: "请输入", trigger: "blur" }],
    personalIncomeTax: [{ required: true, message: "请输入", trigger: "blur" }],
    actualWages: [{ required: true, message: "请输入", trigger: "blur" }],
  },
});
const { form, rules } = toRefs(data);
@@ -234,27 +234,23 @@
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  staffOnJobListPage({
    current: -1,
    size: -1,
    staffState: 1
  }).then(res => {
    personList.value = res.data.records || []
  getStaffOnJob().then(res => {
    personList.value = res.data
  })
    form.value = {}
  form.value = {}
  if (operationType.value === 'edit') {
    staffOnJobInfo(row.staffId).then(res => {
            form.value = {...row}
            form.value.payDate = form.value.payDate + '-01'
    getStaffJoinInfo(row.id).then(res => {
      form.value = {...row}
      form.value.payDate = form.value.payDate + '-01'
    })
  }
}
const handleSelect = (value) => {
    console.log('value', value)
    const index = personList.value.findIndex(row => row.id === value)
    if (index > -1) {
        form.value.name = personList.value[index].staffName
    }
  console.log('value', value)
  const index = personList.value.findIndex(row => row.id === value)
  if (index > -1) {
    form.value.name = personList.value[index].staffName
  }
}
// æäº¤äº§å“è¡¨å•
const submitForm = () => {
@@ -262,12 +258,12 @@
    if (valid) {
      form.value.staffState = 1
      if (operationType.value === "add") {
                compensationAdd(form.value).then(res => {
        compensationAdd(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      } else {
                compensationUpdate(form.value).then(res => {
        compensationUpdate(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
@@ -280,20 +276,20 @@
  if (form.value.contractStartTime && form.value.contractEndTime) {
    const startDate = new Date(form.value.contractStartTime);
    const endDate = new Date(form.value.contractEndTime);
    if (endDate > startDate) {
      // è®¡ç®—年份差
      const yearDiff = endDate.getFullYear() - startDate.getFullYear();
      const monthDiff = endDate.getMonth() - startDate.getMonth();
      const dayDiff = endDate.getDate() - startDate.getDate();
      let years = yearDiff;
      // å¦‚果结束日期的月日小于开始日期的月日,则减去1å¹´
      if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) {
        years = yearDiff - 1;
      }
      form.value.contractTerm = Math.max(0, years);
    } else {
      form.value.contractTerm = 0;
@@ -316,4 +312,4 @@
<style scoped>
</style>
</style>
src/views/personnelManagement/payrollManagement/index.vue
@@ -1,305 +1,306 @@
<template>
    <div class="app-container">
        <div class="search_form">
            <div>
                <span class="search_title">姓名:</span>
                <el-input
                    v-model="searchForm.name"
                    style="width: 240px"
                    placeholder="请输入姓名搜索"
                    @change="handleQuery"
                    clearable
                    :prefix-icon="Search"
                />
                <span class="search_title ml10">月份:</span>
                <el-date-picker
                    v-model="searchForm.payDateStr"
                    type="month"
                    @change="handleQuery"
                    value-format="YYYY-MM"
                    format="YYYY-MM"
                    placeholder="请选择月份"
                    style="width: 240px"
                    clearable
                />
                <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
                >搜索</el-button
                >
            </div>
            <div>
                <el-button @click="handleExport" style="margin-right: 10px">导出</el-button>
                <el-button type="primary" @click="openForm('add')">新增薪资</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"
                :total="page.total"
            ></PIMTable>
        </div>
        <form-dia ref="formDia" @close="handleQuery"></form-dia>
    </div>
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">姓名:</span>
        <el-input
            v-model="searchForm.name"
            style="width: 240px"
            placeholder="请输入姓名搜索"
            @change="handleQuery"
            clearable
            :prefix-icon="Search"
        />
        <span class="search_title ml10">月份:</span>
        <el-date-picker
            v-model="searchForm.payDateStr"
            type="month"
            @change="handleQuery"
            value-format="YYYY-MM"
            format="YYYY-MM"
            placeholder="请选择月份"
            style="width: 240px"
            clearable
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
        >搜索</el-button
        >
      </div>
      <div>
        <el-button @click="handleExport" style="margin-right: 10px">导出</el-button>
        <el-button type="primary" @click="openForm('add')">新增薪资</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"
          :total="page.total"
      ></PIMTable>
    </div>
    <form-dia ref="formDia" @close="handleQuery"></form-dia>
  </div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
import FormDia from "@/views/personnelManagement/payrollManagement/components/formDia.vue";
import {staffJoinDel} from "@/api/personnelManagement/onboarding.js";
import {ElMessageBox} from "element-plus";
import dayjs from "dayjs";
import {compensationDelete, compensationListPage} from "@/api/personnelManagement/payrollManagement.js";
const data = reactive({
    searchForm: {
        name: "",
        payDateStr: "",
    },
  searchForm: {
    name: "",
    payDateStr: "",
  },
});
const { searchForm } = toRefs(data);
const tableColumn = ref([
    {
        label: "薪资月份",
        prop: "payDate",
    },
    {
        label: "姓名",
        prop: "name",
    },
    {
        label: "应出勤天数",
        prop: "shouldAttendedNum",
        width:100
    },
    {
        label: "实际出勤天数",
        prop: "actualAttendedNum",
        width:110
    },
    {
        label: "基本工资",
        prop: "basicSalary",
    },
    {
        label: "岗位工资",
        prop: "postSalary",
        width:100
    },
    {
        label: "入离职缺勤扣款",
        prop: "deductionAbsenteeism",
        width:130
    },
    {
        label: "病假扣款",
        prop: "sickLeaveDeductions",
        width:100
    },
    {
        label: "事假扣款",
        prop: "deductionPersonalLeave",
        width:100
    },
    {
        label: "忘记打卡扣款",
        prop: "forgetClockDeduct",
        width:110
    },
    {
        label: "绩效得分",
        prop: "performanceScore",
        width:150
    },
    {
        label: "绩效工资",
        prop: "performancePay",
        width: 120
    },
    {
        label: "应发合计",
        prop: "payableWages",
        width:150
    },
    {
        label: "社保个人",
        prop: "socialSecurityIndividuals",
    },
    {
        label: "社保公司",
        prop: "socialSecurityCompanies",
        width: 120
    },
    {
        label: "社保合计",
        prop: "socialSecurityTotal",
        width: 120
    },
    {
        label: "公积金个人",
        prop: "providentFundIndividuals",
        width: 120
    },
    {
        label: "公积金公司",
        prop: "providentFundCompany",
        width: 120
    },
    {
        label: "公积金合计",
        prop: "providentFundTotal",
        width: 120
    },
    {
        label: "应税工资",
        prop: "taxableWaget",
    },
    {
        label: "个人所得税",
        prop: "personalIncomeTax",
        width: 120
    },
    {
        label: "实发工资",
        prop: "actualWages",
        width: 120
    },
    {
        dataType: "action",
        label: "操作",
        align: "center",
        fixed: 'right',
        operation: [
            {
                name: "编辑",
                type: "text",
                clickFun: (row) => {
                    openForm("edit", row);
                },
            },
        ],
    },
  {
    label: "薪资月份",
    prop: "payDate",
  },
  {
    label: "姓名",
    prop: "name",
  },
  {
    label: "应出勤天数",
    prop: "shouldAttendedNum",
    width:100
  },
  {
    label: "实际出勤天数",
    prop: "actualAttendedNum",
    width:110
  },
  {
    label: "基本工资",
    prop: "basicSalary",
  },
  {
    label: "岗位工资",
    prop: "postSalary",
    width:100
  },
  {
    label: "入离职缺勤扣款",
    prop: "deductionAbsenteeism",
    width:130
  },
  {
    label: "病假扣款",
    prop: "sickLeaveDeductions",
    width:100
  },
  {
    label: "事假扣款",
    prop: "deductionPersonalLeave",
    width:100
  },
  {
    label: "忘记打卡扣款",
    prop: "forgetClockDeduct",
    width:110
  },
  {
    label: "绩效得分",
    prop: "performanceScore",
    width:150
  },
  {
    label: "绩效工资",
    prop: "performancePay",
    width: 120
  },
  {
    label: "应发合计",
    prop: "payableWages",
    width:150
  },
  {
    label: "社保个人",
    prop: "socialSecurityIndividuals",
  },
  {
    label: "社保公司",
    prop: "socialSecurityCompanies",
    width: 120
  },
  {
    label: "社保合计",
    prop: "socialSecurityTotal",
    width: 120
  },
  {
    label: "公积金个人",
    prop: "providentFundIndividuals",
    width: 120
  },
  {
    label: "公积金公司",
    prop: "providentFundCompany",
    width: 120
  },
  {
    label: "公积金合计",
    prop: "providentFundTotal",
    width: 120
  },
  {
    label: "应税工资",
    prop: "taxableWaget",
  },
  {
    label: "个人所得税",
    prop: "personalIncomeTax",
    width: 120
  },
  {
    label: "实发工资",
    prop: "actualWages",
    width: 120
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: 'right',
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
        },
      },
    ],
  },
]);
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const page = reactive({
    current: 1,
    size: 100,
    total: 0,
  current: 1,
  size: 100,
  total: 0,
});
const formDia = ref()
const { proxy } = getCurrentInstance()
const handleDateChange = (value,type) => {
    searchForm.value.entryDateEnd = null
    searchForm.value.entryDateStart = null
    if(type === 1){
        if (value) {
            searchForm.value.entryDateStart = dayjs(value).format("YYYY-MM-DD");
        }
    }else{
        if (value) {
            searchForm.value.entryDateEnd = dayjs(value).format("YYYY-MM-DD");
        }
    }
    getList();
  searchForm.value.entryDateEnd = null
  searchForm.value.entryDateStart = null
  if(type === 1){
    if (value) {
      searchForm.value.entryDateStart = dayjs(value).format("YYYY-MM-DD");
    }
  }else{
    if (value) {
      searchForm.value.entryDateEnd = dayjs(value).format("YYYY-MM-DD");
    }
  }
  getList();
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
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;
    compensationListPage({...page, ...searchForm.value, staffState: 1}).then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records
        page.total = res.data.total;
    }).catch(err => {
        tableLoading.value = false;
    })
  tableLoading.value = true;
  compensationListPage({...page, ...searchForm.value, staffState: 1}).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
    selectedRows.value = selection;
  selectedRows.value = selection;
};
// æ‰“开弹框
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) {
        ids = selectedRows.value.map((item) => item.id);
    } else {
        proxy.$modal.msgWarning("请选择数据");
        return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            compensationDelete(ids).then((res) => {
                proxy.$modal.msgSuccess("删除成功");
                getList();
            });
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        compensationDelete(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
// å¯¼å‡º
const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            proxy.download("/staff/staffJoinLeaveRecord/export", {staffState: 1}, "人员入职.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        proxy.download("/staff/staffJoinLeaveRecord/export", {staffState: 1}, "人员入职.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
// å¯¼å‡ºè–ªèµ„管理
const handleExport = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            proxy.download("/compensationPerformance/export", { ...searchForm.value, ...page }, "薪资管理.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        proxy.download("/compensationPerformance/export", { ...searchForm.value, ...page }, "薪资管理.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
onMounted(() => {
    getList();
  getList();
});
</script>