薪资管理和考勤分析
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "2.3.1",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@vueup/vue-quill": "1.2.0",
|
||||
"@vueuse/core": "13.3.0",
|
||||
"axios": "1.9.0",
|
||||
@@ -32,6 +33,7 @@
|
||||
"pinia": "3.0.2",
|
||||
"sortablejs": "^1.15.6",
|
||||
"splitpanes": "4.0.4",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"vue": "3.5.16",
|
||||
"vue-cropper": "1.1.1",
|
||||
"vue-router": "4.5.1",
|
||||
|
||||
419
gear-ui3/pnpm-lock.yaml
generated
419
gear-ui3/pnpm-lock.yaml
generated
@@ -11,6 +11,9 @@ importers:
|
||||
'@element-plus/icons-vue':
|
||||
specifier: 2.3.1
|
||||
version: 2.3.1(vue@3.5.16)
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.1.11
|
||||
version: 4.1.11(vite@6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))
|
||||
'@vueup/vue-quill':
|
||||
specifier: 1.2.0
|
||||
version: 1.2.0(vue@3.5.16)
|
||||
@@ -56,6 +59,9 @@ importers:
|
||||
splitpanes:
|
||||
specifier: 4.0.4
|
||||
version: 4.0.4(vue@3.5.16)
|
||||
tailwindcss:
|
||||
specifier: ^4.1.11
|
||||
version: 4.1.11
|
||||
vue:
|
||||
specifier: 3.5.16
|
||||
version: 3.5.16
|
||||
@@ -71,7 +77,7 @@ importers:
|
||||
devDependencies:
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: 5.2.4
|
||||
version: 5.2.4(vite@6.3.5(@types/node@24.2.0)(sass-embedded@1.89.1))(vue@3.5.16)
|
||||
version: 5.2.4(vite@6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))(vue@3.5.16)
|
||||
sass-embedded:
|
||||
specifier: 1.89.1
|
||||
version: 1.89.1
|
||||
@@ -83,16 +89,20 @@ importers:
|
||||
version: 1.0.1
|
||||
vite:
|
||||
specifier: 6.3.5
|
||||
version: 6.3.5(@types/node@24.2.0)(sass-embedded@1.89.1)
|
||||
version: 6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)
|
||||
vite-plugin-compression:
|
||||
specifier: 0.5.1
|
||||
version: 0.5.1(vite@6.3.5(@types/node@24.2.0)(sass-embedded@1.89.1))
|
||||
version: 0.5.1(vite@6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))
|
||||
vite-plugin-svg-icons:
|
||||
specifier: 2.0.1
|
||||
version: 2.0.1(vite@6.3.5(@types/node@24.2.0)(sass-embedded@1.89.1))
|
||||
version: 2.0.1(vite@6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))
|
||||
|
||||
packages:
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@antfu/utils@0.7.10':
|
||||
resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
|
||||
|
||||
@@ -294,9 +304,23 @@ packages:
|
||||
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@isaacs/fs-minipass@4.0.1':
|
||||
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.12':
|
||||
resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2':
|
||||
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.4':
|
||||
resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.29':
|
||||
resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -439,6 +463,100 @@ packages:
|
||||
'@sxzz/popperjs-es@2.11.7':
|
||||
resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
|
||||
|
||||
'@tailwindcss/node@4.1.11':
|
||||
resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==}
|
||||
|
||||
'@tailwindcss/oxide-android-arm64@4.1.11':
|
||||
resolution: {integrity: sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@tailwindcss/oxide-darwin-arm64@4.1.11':
|
||||
resolution: {integrity: sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@tailwindcss/oxide-darwin-x64@4.1.11':
|
||||
resolution: {integrity: sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@tailwindcss/oxide-freebsd-x64@4.1.11':
|
||||
resolution: {integrity: sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
|
||||
resolution: {integrity: sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
|
||||
resolution: {integrity: sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-musl@4.1.11':
|
||||
resolution: {integrity: sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-gnu@4.1.11':
|
||||
resolution: {integrity: sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-musl@4.1.11':
|
||||
resolution: {integrity: sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@tailwindcss/oxide-wasm32-wasi@4.1.11':
|
||||
resolution: {integrity: sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [wasm32]
|
||||
bundledDependencies:
|
||||
- '@napi-rs/wasm-runtime'
|
||||
- '@emnapi/core'
|
||||
- '@emnapi/runtime'
|
||||
- '@tybys/wasm-util'
|
||||
- '@emnapi/wasi-threads'
|
||||
- tslib
|
||||
|
||||
'@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
|
||||
resolution: {integrity: sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@tailwindcss/oxide-win32-x64-msvc@4.1.11':
|
||||
resolution: {integrity: sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@tailwindcss/oxide@4.1.11':
|
||||
resolution: {integrity: sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
'@tailwindcss/vite@4.1.11':
|
||||
resolution: {integrity: sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==}
|
||||
peerDependencies:
|
||||
vite: ^5.2.0 || ^6 || ^7
|
||||
|
||||
'@trysound/sax@0.2.0':
|
||||
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
@@ -694,6 +812,10 @@ packages:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
chownr@3.0.0:
|
||||
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
class-utils@0.3.6:
|
||||
resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -844,6 +966,10 @@ packages:
|
||||
delegate@3.2.0:
|
||||
resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==}
|
||||
|
||||
detect-libc@2.0.4:
|
||||
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
dom-serializer@0.2.2:
|
||||
resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==}
|
||||
|
||||
@@ -899,6 +1025,10 @@ packages:
|
||||
resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
enhanced-resolve@5.18.3:
|
||||
resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
entities@1.1.2:
|
||||
resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==}
|
||||
|
||||
@@ -1356,6 +1486,10 @@ packages:
|
||||
jackspeak@3.4.3:
|
||||
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
|
||||
|
||||
jiti@2.5.1:
|
||||
resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==}
|
||||
hasBin: true
|
||||
|
||||
js-base64@2.6.4:
|
||||
resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
|
||||
|
||||
@@ -1397,6 +1531,74 @@ packages:
|
||||
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
lightningcss-darwin-arm64@1.30.1:
|
||||
resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-darwin-x64@1.30.1:
|
||||
resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-freebsd-x64@1.30.1:
|
||||
resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.30.1:
|
||||
resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.30.1:
|
||||
resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-arm64-musl@1.30.1:
|
||||
resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-linux-x64-gnu@1.30.1:
|
||||
resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-x64-musl@1.30.1:
|
||||
resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.30.1:
|
||||
resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss-win32-x64-msvc@1.30.1:
|
||||
resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss@1.30.1:
|
||||
resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
loader-utils@1.4.2:
|
||||
resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
@@ -1496,6 +1698,10 @@ packages:
|
||||
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
minizlib@3.0.2:
|
||||
resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
@@ -1503,6 +1709,11 @@ packages:
|
||||
resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
mkdirp@3.0.1:
|
||||
resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
mlly@1.7.4:
|
||||
resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
|
||||
|
||||
@@ -2057,6 +2268,17 @@ packages:
|
||||
resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
tailwindcss@4.1.11:
|
||||
resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==}
|
||||
|
||||
tapable@2.2.2:
|
||||
resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tar@7.4.3:
|
||||
resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tiny-emitter@2.1.0:
|
||||
resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
|
||||
|
||||
@@ -2286,11 +2508,20 @@ packages:
|
||||
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yallist@5.0.0:
|
||||
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
zrender@5.6.1:
|
||||
resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.12
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
|
||||
'@antfu/utils@0.7.10': {}
|
||||
|
||||
'@babel/helper-string-parser@7.27.1': {}
|
||||
@@ -2412,8 +2643,24 @@ snapshots:
|
||||
wrap-ansi: 8.1.0
|
||||
wrap-ansi-cjs: wrap-ansi@7.0.0
|
||||
|
||||
'@isaacs/fs-minipass@4.0.1':
|
||||
dependencies:
|
||||
minipass: 7.1.2
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.12':
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.4
|
||||
'@jridgewell/trace-mapping': 0.3.29
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2': {}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.4': {}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.29':
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.4
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
dependencies:
|
||||
'@nodelib/fs.stat': 2.0.5
|
||||
@@ -2501,6 +2748,77 @@ snapshots:
|
||||
|
||||
'@sxzz/popperjs-es@2.11.7': {}
|
||||
|
||||
'@tailwindcss/node@4.1.11':
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
enhanced-resolve: 5.18.3
|
||||
jiti: 2.5.1
|
||||
lightningcss: 1.30.1
|
||||
magic-string: 0.30.17
|
||||
source-map-js: 1.2.1
|
||||
tailwindcss: 4.1.11
|
||||
|
||||
'@tailwindcss/oxide-android-arm64@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-darwin-arm64@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-darwin-x64@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-freebsd-x64@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-arm64-musl@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-gnu@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-linux-x64-musl@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-wasm32-wasi@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide-win32-x64-msvc@4.1.11':
|
||||
optional: true
|
||||
|
||||
'@tailwindcss/oxide@4.1.11':
|
||||
dependencies:
|
||||
detect-libc: 2.0.4
|
||||
tar: 7.4.3
|
||||
optionalDependencies:
|
||||
'@tailwindcss/oxide-android-arm64': 4.1.11
|
||||
'@tailwindcss/oxide-darwin-arm64': 4.1.11
|
||||
'@tailwindcss/oxide-darwin-x64': 4.1.11
|
||||
'@tailwindcss/oxide-freebsd-x64': 4.1.11
|
||||
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.11
|
||||
'@tailwindcss/oxide-linux-arm64-gnu': 4.1.11
|
||||
'@tailwindcss/oxide-linux-arm64-musl': 4.1.11
|
||||
'@tailwindcss/oxide-linux-x64-gnu': 4.1.11
|
||||
'@tailwindcss/oxide-linux-x64-musl': 4.1.11
|
||||
'@tailwindcss/oxide-wasm32-wasi': 4.1.11
|
||||
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.11
|
||||
'@tailwindcss/oxide-win32-x64-msvc': 4.1.11
|
||||
|
||||
'@tailwindcss/vite@4.1.11(vite@6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))':
|
||||
dependencies:
|
||||
'@tailwindcss/node': 4.1.11
|
||||
'@tailwindcss/oxide': 4.1.11
|
||||
tailwindcss: 4.1.11
|
||||
vite: 6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)
|
||||
|
||||
'@trysound/sax@0.2.0': {}
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
@@ -2523,9 +2841,9 @@ snapshots:
|
||||
|
||||
'@types/web-bluetooth@0.0.21': {}
|
||||
|
||||
'@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@24.2.0)(sass-embedded@1.89.1))(vue@3.5.16)':
|
||||
'@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))(vue@3.5.16)':
|
||||
dependencies:
|
||||
vite: 6.3.5(@types/node@24.2.0)(sass-embedded@1.89.1)
|
||||
vite: 6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)
|
||||
vue: 3.5.16
|
||||
|
||||
'@vue/compiler-core@3.5.16':
|
||||
@@ -2822,6 +3140,8 @@ snapshots:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
chownr@3.0.0: {}
|
||||
|
||||
class-utils@0.3.6:
|
||||
dependencies:
|
||||
arr-union: 3.1.0
|
||||
@@ -2975,6 +3295,8 @@ snapshots:
|
||||
|
||||
delegate@3.2.0: {}
|
||||
|
||||
detect-libc@2.0.4: {}
|
||||
|
||||
dom-serializer@0.2.2:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
@@ -3056,6 +3378,11 @@ snapshots:
|
||||
|
||||
emojis-list@3.0.0: {}
|
||||
|
||||
enhanced-resolve@5.18.3:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
tapable: 2.2.2
|
||||
|
||||
entities@1.1.2: {}
|
||||
|
||||
entities@2.2.0: {}
|
||||
@@ -3599,6 +3926,8 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@pkgjs/parseargs': 0.11.0
|
||||
|
||||
jiti@2.5.1: {}
|
||||
|
||||
js-base64@2.6.4: {}
|
||||
|
||||
js-beautify@1.14.11:
|
||||
@@ -3636,6 +3965,51 @@ snapshots:
|
||||
|
||||
kind-of@6.0.3: {}
|
||||
|
||||
lightningcss-darwin-arm64@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-darwin-x64@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-freebsd-x64@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-musl@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-gnu@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-musl@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-x64-msvc@1.30.1:
|
||||
optional: true
|
||||
|
||||
lightningcss@1.30.1:
|
||||
dependencies:
|
||||
detect-libc: 2.0.4
|
||||
optionalDependencies:
|
||||
lightningcss-darwin-arm64: 1.30.1
|
||||
lightningcss-darwin-x64: 1.30.1
|
||||
lightningcss-freebsd-x64: 1.30.1
|
||||
lightningcss-linux-arm-gnueabihf: 1.30.1
|
||||
lightningcss-linux-arm64-gnu: 1.30.1
|
||||
lightningcss-linux-arm64-musl: 1.30.1
|
||||
lightningcss-linux-x64-gnu: 1.30.1
|
||||
lightningcss-linux-x64-musl: 1.30.1
|
||||
lightningcss-win32-arm64-msvc: 1.30.1
|
||||
lightningcss-win32-x64-msvc: 1.30.1
|
||||
|
||||
loader-utils@1.4.2:
|
||||
dependencies:
|
||||
big.js: 5.2.2
|
||||
@@ -3736,6 +4110,10 @@ snapshots:
|
||||
|
||||
minipass@7.1.2: {}
|
||||
|
||||
minizlib@3.0.2:
|
||||
dependencies:
|
||||
minipass: 7.1.2
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
mixin-deep@1.3.2:
|
||||
@@ -3743,6 +4121,8 @@ snapshots:
|
||||
for-in: 1.0.2
|
||||
is-extendable: 1.0.1
|
||||
|
||||
mkdirp@3.0.1: {}
|
||||
|
||||
mlly@1.7.4:
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
@@ -4369,6 +4749,19 @@ snapshots:
|
||||
|
||||
sync-message-port@1.1.3: {}
|
||||
|
||||
tailwindcss@4.1.11: {}
|
||||
|
||||
tapable@2.2.2: {}
|
||||
|
||||
tar@7.4.3:
|
||||
dependencies:
|
||||
'@isaacs/fs-minipass': 4.0.1
|
||||
chownr: 3.0.0
|
||||
minipass: 7.1.2
|
||||
minizlib: 3.0.2
|
||||
mkdirp: 3.0.1
|
||||
yallist: 5.0.0
|
||||
|
||||
tiny-emitter@2.1.0: {}
|
||||
|
||||
tinyglobby@0.2.14:
|
||||
@@ -4530,16 +4923,16 @@ snapshots:
|
||||
|
||||
vary@1.1.2: {}
|
||||
|
||||
vite-plugin-compression@0.5.1(vite@6.3.5(@types/node@24.2.0)(sass-embedded@1.89.1)):
|
||||
vite-plugin-compression@0.5.1(vite@6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)):
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.1
|
||||
fs-extra: 10.1.0
|
||||
vite: 6.3.5(@types/node@24.2.0)(sass-embedded@1.89.1)
|
||||
vite: 6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-plugin-svg-icons@2.0.1(vite@6.3.5(@types/node@24.2.0)(sass-embedded@1.89.1)):
|
||||
vite-plugin-svg-icons@2.0.1(vite@6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)):
|
||||
dependencies:
|
||||
'@types/svgo': 2.6.4
|
||||
cors: 2.8.5
|
||||
@@ -4549,11 +4942,11 @@ snapshots:
|
||||
pathe: 0.2.0
|
||||
svg-baker: 1.7.0
|
||||
svgo: 2.8.0
|
||||
vite: 6.3.5(@types/node@24.2.0)(sass-embedded@1.89.1)
|
||||
vite: 6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite@6.3.5(@types/node@24.2.0)(sass-embedded@1.89.1):
|
||||
vite@6.3.5(@types/node@24.2.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.89.1):
|
||||
dependencies:
|
||||
esbuild: 0.25.8
|
||||
fdir: 6.4.6(picomatch@4.0.3)
|
||||
@@ -4564,6 +4957,8 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 24.2.0
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.5.1
|
||||
lightningcss: 1.30.1
|
||||
sass-embedded: 1.89.1
|
||||
|
||||
vue-cropper@1.1.1: {}
|
||||
@@ -4649,6 +5044,8 @@ snapshots:
|
||||
string-width: 5.1.2
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
yallist@5.0.0: {}
|
||||
|
||||
zrender@5.6.1:
|
||||
dependencies:
|
||||
tslib: 2.3.0
|
||||
|
||||
@@ -13,3 +13,7 @@ onMounted(() => {
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* @import "tailwindcss"; */
|
||||
</style>
|
||||
|
||||
53
gear-ui3/src/api/oa/salaryRecords.js
Normal file
53
gear-ui3/src/api/oa/salaryRecords.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询工资发放记录列表
|
||||
export function listSalaryRecords(query) {
|
||||
return request({
|
||||
url: '/oa/salaryRecords/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询工资发放记录详细
|
||||
export function getSalaryRecords(salaryId) {
|
||||
return request({
|
||||
url: '/oa/salaryRecords/' + salaryId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增工资发放记录
|
||||
export function addSalaryRecords(data) {
|
||||
return request({
|
||||
url: '/oa/salaryRecords',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改工资发放记录
|
||||
export function updateSalaryRecords(data) {
|
||||
return request({
|
||||
url: '/oa/salaryRecords',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除工资发放记录
|
||||
export function delSalaryRecords(salaryId) {
|
||||
return request({
|
||||
url: '/oa/salaryRecords/' + salaryId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 批量发放薪资
|
||||
export function batchPaySalary(data) {
|
||||
return request({
|
||||
url: '/oa/salaryRecords/batchSendSalary',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="120px">
|
||||
<!-- <el-form-item label="关联汇报概述ID" prop="summaryId">
|
||||
<el-input
|
||||
v-model="queryParams.summaryId"
|
||||
@@ -93,26 +93,25 @@
|
||||
|
||||
<el-table v-loading="loading" :data="reportDetailList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="序号" align="center" type="index" v-if="true"/>
|
||||
<!-- <el-table-column label="关联汇报概述ID" align="center" prop="summaryId" /> -->
|
||||
<el-table-column label="设备编号" align="center" prop="deviceCode" />
|
||||
<el-table-column label="设备类别" align="center" prop="category" />
|
||||
<!-- <el-table-column label="设备生产说明" align="center" prop="deviceDescription" /> -->
|
||||
<el-table-column label="汇报详情内容" align="center" prop="reportDetail" />
|
||||
<el-table-column label="图片概况" align="center" prop="ossIds" width="100">
|
||||
<template slot-scope="scope">
|
||||
<!-- <el-table-column label="图片概况" align="center" prop="ossIds" width="100">
|
||||
<template #default="scope">
|
||||
<image-preview :src="scope.row.ossIds" :width="50" :height="50"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
<template #default="scope">
|
||||
<!-- <el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-view"
|
||||
@click="handleImageDetail(scope.row)"
|
||||
>图片详情</el-button>
|
||||
>图片详情</el-button> -->
|
||||
|
||||
|
||||
<el-button
|
||||
@@ -142,7 +141,7 @@
|
||||
/>
|
||||
|
||||
<!-- 添加或修改设计项目汇报详情对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="50%" append-to-body>
|
||||
<el-dialog :title="title" v-model="open" width="50%" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="设备编号" prop="deviceCode">
|
||||
<el-input v-model="form.deviceCode" placeholder="请输入设备唯一编号" />
|
||||
@@ -162,14 +161,14 @@
|
||||
<el-form-item label="详情内容" prop="reportDetail">
|
||||
<el-input v-model="form.reportDetail" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
<el-form-item label="图像" prop="ossIds">
|
||||
<!-- <el-form-item label="图像" prop="ossIds">
|
||||
<image-upload v-model="form.ossIds"/>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="汇报标题" prop="reportTitle">
|
||||
<el-input
|
||||
v-model="queryParams.reportTitle"
|
||||
@@ -75,26 +75,25 @@
|
||||
|
||||
<el-table v-loading="loading" :data="reportSummaryList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="序号" align="center" type="index" v-if="true"/>
|
||||
<el-table-column label="汇报标题" align="center" prop="reportTitle">
|
||||
<template slot-scope="scope">
|
||||
<router-link class="link-type" :to="'/produce/construction/detail/' + scope.row.summaryId">{{ scope.row.reportTitle }}</router-link>
|
||||
<template #default="scope">
|
||||
<router-link class="link-type" :to="'/info/construction/detail/' + scope.row.summaryId">{{ scope.row.reportTitle }}</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最近汇报时间" align="center" prop="lastUpdateTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.lastUpdateTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="汇报日期" align="center" prop="reportDate" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.reportDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="汇报人" align="center" prop="reporter" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@@ -129,7 +128,7 @@
|
||||
<el-date-picker clearable
|
||||
v-model="form.reportDate"
|
||||
type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择汇报日期"
|
||||
style="width: 100%;">
|
||||
</el-date-picker>
|
||||
@@ -141,7 +140,7 @@
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="96px">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="96px">
|
||||
<el-form-item label="物流编号" prop="expressCode">
|
||||
<el-input
|
||||
v-model="queryParams.expressCode"
|
||||
@@ -42,12 +42,12 @@
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="物流公司" prop="expressType">
|
||||
<el-select v-model="queryParams.expressType" placeholder="请选择物流公司标识" clearable>
|
||||
<el-select style="width: 200px;" v-model="queryParams.expressType" placeholder="请选择物流公司标识" clearable>
|
||||
<el-option
|
||||
v-for="dict in oa_express_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -117,7 +117,7 @@
|
||||
<el-table-column type="selection" width="55" align="center"/>
|
||||
<el-table-column label="物流编号" align="center" prop="expressCode"/>
|
||||
<el-table-column label="数据状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status===0" type="warning">未确认</el-tag>
|
||||
<el-tag v-if="scope.row.status===1" type="primary">进行中</el-tag>
|
||||
<el-tag v-if="scope.row.status===2" type="success">已完成</el-tag>
|
||||
@@ -125,7 +125,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物流状态" align="center" prop="status" width="200">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<ExpressStatusEditor
|
||||
:lastStatus.sync="scope.row.lastStatus"
|
||||
:lastUpdateTime.sync="scope.row.lastUpdateTime"
|
||||
@@ -135,7 +135,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="剩余时间" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<ExpressRemainTime :planDate="scope.row.planDate" :status="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -143,23 +143,23 @@
|
||||
<el-table-column label="负责人" align="center" prop="ownerName"/>
|
||||
<el-table-column label="负责人手机" align="center" prop="ownerPhone"/>
|
||||
<el-table-column label="计划到货时间" align="center" prop="planDate" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<span>{{parseTime(scope.row.planDate,'{y}-{m}-{d}')}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="更新时间" align="center" prop="planDate" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<span>{{scope.row.updateTime}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物流公司标识" align="center" prop="expressType">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="oa_express_type" :value="scope.row.expressType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark"/>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@@ -228,7 +228,7 @@
|
||||
/>
|
||||
|
||||
<!-- 添加或修改物流预览对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="物流编号" prop="expressCode">
|
||||
<el-input v-model="form.expressCode" placeholder="请输入物流编号"/>
|
||||
@@ -249,8 +249,8 @@
|
||||
<el-form-item label="计划到货时间" prop="planDate">
|
||||
<el-date-picker clearable
|
||||
v-model="form.planDate"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="请选择计划到货时间">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
@@ -265,17 +265,6 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="发货记录" prop="detailId">
|
||||
<el-select v-model="form.detailId" placeholder="请选择发货记录">
|
||||
<el-option
|
||||
v-for="item in reportDetailList"
|
||||
:key="item.detailId"
|
||||
:label="item.reportDetail"
|
||||
:value="item.detailId"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"/>
|
||||
</el-form-item>
|
||||
@@ -289,7 +278,7 @@
|
||||
<!-- 物流详情弹窗 -->
|
||||
<el-dialog
|
||||
title="物流详情"
|
||||
:visible.sync="detailOpen"
|
||||
v-model="detailOpen"
|
||||
width="600px"
|
||||
append-to-body
|
||||
>
|
||||
@@ -324,7 +313,7 @@
|
||||
<!-- 异常登记弹窗表单 -->
|
||||
<el-dialog
|
||||
title="异常问题登记"
|
||||
:visible.sync="questionDialogVisible"
|
||||
v-model="questionDialogVisible"
|
||||
width="500px"
|
||||
append-to-body
|
||||
>
|
||||
@@ -375,7 +364,13 @@ import ExpressStatusEditor from './components/ExpressStatusEditor.vue';
|
||||
export default {
|
||||
name: "Express",
|
||||
components: {UserSelect, ExpressStatusEditor, ExpressRemainTime},
|
||||
dicts: ['oa_express_type'],
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { oa_express_type } = proxy.useDict("oa_express_type")
|
||||
return {
|
||||
oa_express_type
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 按钮loading
|
||||
@@ -444,9 +439,6 @@ export default {
|
||||
expressType: [
|
||||
{required: true, message: "物流公司标识不能为空", trigger: "change"}
|
||||
],
|
||||
remark: [
|
||||
{required: true, message: "备注不能为空", trigger: "blur"}
|
||||
]
|
||||
},
|
||||
allProject:[],
|
||||
reportDetailList:[],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="关联快递" prop="expressId">
|
||||
<el-input
|
||||
v-model="queryParams.expressId"
|
||||
@@ -40,15 +40,15 @@
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-check"
|
||||
icon="Check"
|
||||
size="mini"
|
||||
@click="handleUpdateStatus"
|
||||
>修复</el-button>
|
||||
</el-col>
|
||||
</el-col> -->
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
@@ -87,20 +87,20 @@
|
||||
<el-table-column label="快递单号" align="center" prop="expressCode" />
|
||||
<el-table-column label="问题描述" align="center" prop="description" />
|
||||
<el-table-column label="汇报时间" align="center" prop="reportTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.reportTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="汇报人" align="center" prop="reportBy" />
|
||||
<el-table-column label="是否解决" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status===0" type="danger">未解决</el-tag>
|
||||
<el-tag v-if="scope.row.status===1" type="success">完成</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@@ -126,7 +126,7 @@
|
||||
/>
|
||||
|
||||
<!-- 添加或修改快递问题对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="问题描述" prop="description">
|
||||
<el-input v-model="form.description" type="textarea" placeholder="请输入内容" />
|
||||
@@ -146,7 +146,7 @@
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<!-- 对话框:新增 Feedback -->
|
||||
<el-dialog
|
||||
title="新增反馈"
|
||||
:visible.sync="dialogVisible"
|
||||
v-model="dialogVisible"
|
||||
width="800px"
|
||||
@close="resetForm"
|
||||
>
|
||||
@@ -82,7 +82,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleAddFeedback">提交</el-button>
|
||||
</span>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:visible.sync="visible"
|
||||
v-model="visible"
|
||||
width="860px"
|
||||
custom-class="project-report-detail"
|
||||
:before-close="handleClose"
|
||||
append-to-body
|
||||
>
|
||||
<template #title>
|
||||
<template #header>
|
||||
<div class="dialog-title flex items-center gap-2">
|
||||
<i class="el-icon-document"></i>
|
||||
<span>项目报工详情</span>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
@@ -13,30 +14,20 @@ export default {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
handler() {
|
||||
this.renderChart();
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.chart = echarts.init(this.$refs.chartRef);
|
||||
this.renderChart();
|
||||
window.addEventListener('resize', this.resizeChart);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.resizeChart);
|
||||
this.chart && this.chart.dispose();
|
||||
},
|
||||
methods: {
|
||||
renderChart() {
|
||||
// 如果数据为空,清空图表并显示暂无数据 loading
|
||||
if (!this.data || this.data.length === 0) {
|
||||
this.chart.clear();
|
||||
this.chart.showLoading({
|
||||
setup(props) {
|
||||
const chartRef = ref(null);
|
||||
let chart = null;
|
||||
|
||||
const renderChart = () => {
|
||||
// 确保 chart 已初始化
|
||||
if (!chart && chartRef.value) {
|
||||
chart = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
// 如果没有数据
|
||||
if (!props.data || props.data.length === 0) {
|
||||
chart?.clear();
|
||||
chart?.showLoading({
|
||||
text: '暂无数据',
|
||||
color: '#999',
|
||||
textColor: '#999',
|
||||
@@ -45,20 +36,18 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// 有数据时,隐藏 loading 并渲染图表
|
||||
this.chart.hideLoading();
|
||||
|
||||
// 有数据时渲染图表
|
||||
const option = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: this.data.map(i => i.date)
|
||||
data: props.data.map(i => i.date)
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: this.data.map(i => i.count),
|
||||
data: props.data.map(i => i.count),
|
||||
type: 'line',
|
||||
areaStyle: {}
|
||||
}
|
||||
@@ -67,12 +56,36 @@ export default {
|
||||
trigger: 'axis'
|
||||
}
|
||||
};
|
||||
// 第二个参数 true 避免与之前配置合并
|
||||
this.chart.setOption(option, true);
|
||||
},
|
||||
resizeChart() {
|
||||
this.chart && this.chart.resize();
|
||||
}
|
||||
chart?.setOption(option, true);
|
||||
};
|
||||
|
||||
const resizeChart = () => {
|
||||
chart?.resize();
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
renderChart();
|
||||
window.addEventListener('resize', resizeChart);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', resizeChart);
|
||||
chart?.dispose();
|
||||
});
|
||||
|
||||
// 监听 props.data 变化
|
||||
watch(
|
||||
() => props.data,
|
||||
() => {
|
||||
renderChart();
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
return {
|
||||
chartRef
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -90,4 +103,4 @@ export default {
|
||||
top: 50% !important;
|
||||
transform: translateY(-50%) !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -18,7 +18,7 @@
|
||||
</el-row>
|
||||
|
||||
<!-- Summary Cards with Skeleton -->
|
||||
<el-skeleton :loading="loading" animated>
|
||||
<!-- <el-skeleton :loading="loading" animated>
|
||||
<template #template>
|
||||
<el-row :gutter="20" class="summary-cards">
|
||||
<el-col :span="6" v-for="n in 4" :key="n">
|
||||
@@ -48,20 +48,20 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-skeleton>
|
||||
</el-skeleton> -->
|
||||
|
||||
<!-- Charts & Ranking with Skeleton -->
|
||||
<!-- 图表部分 -->
|
||||
<el-skeleton :loading="loading" animated>
|
||||
<template #template>
|
||||
<el-row :gutter="20" class="charts-ranking">
|
||||
<el-col :span="8" v-for="n in 3" :key="n">
|
||||
<el-col :span="12" v-for="n in 2" :key="n">
|
||||
<el-skeleton-item variant="rect" style="width: 100%; height: 300px" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<template #default>
|
||||
<el-row type="flex" :gutter="20" class="charts-ranking">
|
||||
<el-col :span="8">
|
||||
<el-col :span="12">
|
||||
<el-card
|
||||
class="charts-card"
|
||||
:body-style="{ height: '300px', padding: '16px' }"
|
||||
@@ -70,16 +70,7 @@
|
||||
<TrendChart :data="trendData" style="height: 100%;" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card
|
||||
class="charts-card"
|
||||
:body-style="{ height: '300px', padding: '16px' }"
|
||||
>
|
||||
<template #header>项目分布</template>
|
||||
<PieChart :data="pieData" style="height: 100%;" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-col :span="12">
|
||||
<el-card
|
||||
class="charts-card"
|
||||
:body-style="{ height: '300px', padding: '16px' }"
|
||||
@@ -96,7 +87,6 @@
|
||||
<el-card class="table-card">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="数据总结" name="summary" />
|
||||
<el-tab-pane label="项目进度" name="progress" />
|
||||
<el-tab-pane label="历史记录" name="history" />
|
||||
</el-tabs>
|
||||
|
||||
@@ -135,55 +125,11 @@
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab==='progress'">
|
||||
<el-table :data="projectList" stripe>
|
||||
<el-table-column label="项目代号">
|
||||
<template #default="{row}">
|
||||
<el-tag v-if="row.projectCode==null" type="danger">无</el-tag>
|
||||
<el-tag v-else>{{ row.projectCode }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="projectNum" label="项目编号" />
|
||||
<el-table-column prop="projectName" label="项目名称" />
|
||||
<el-table-column prop="reportCount" label="参与人天" />
|
||||
<el-table-column label="剩余时间" align="center" prop="remainTime">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.remainTime>=0">
|
||||
<div v-if="scope.row.projectStatus===0">
|
||||
<span v-if="scope.row.remainTime>5">剩余{{ scope.row.remainTime }}天</span>
|
||||
<el-tag v-else-if="scope.row.remainTime<=5 &&scope.row.remainTime>3" type="warning">
|
||||
剩余{{ scope.row.remainTime }}天
|
||||
</el-tag>
|
||||
<el-tag v-else type="danger">剩余{{ scope.row.remainTime }}天</el-tag>
|
||||
</div>
|
||||
<div v-else>-</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-tag type="danger" v-if="scope.row.projectStatus===0">过期{{
|
||||
Math.abs(scope.row.remainTime)
|
||||
}}天
|
||||
</el-tag>
|
||||
<div v-else>-</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="projectStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_project_status" :value="scope.row.projectStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab==='history'">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<export-dialog />
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-button icon="Download" type="text" size="small" @click="loadMore">更多</el-button>
|
||||
</el-col>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table :data="filteredHistory" stripe style="margin-top:16px">
|
||||
@@ -227,28 +173,22 @@
|
||||
|
||||
<script>
|
||||
import {
|
||||
getCardData,
|
||||
getPieData,
|
||||
getProjectData,
|
||||
getProjectReport,
|
||||
getRankData, getSummaryList,
|
||||
getTrendData,
|
||||
listClearProjectReport
|
||||
} from '@/api/oa/projectReport';
|
||||
import ExportDialog from './components/ExportDialog.vue';
|
||||
import PieChart from './components/PieChart.vue';
|
||||
import ProjectReportDetail from "./components/ProjectReportDetail.vue";
|
||||
import RankList from "./components/RankList.vue";
|
||||
import TrendChart from './components/TrendChart.vue';
|
||||
|
||||
export default {
|
||||
name: 'ReportDashboard',
|
||||
dicts:['sys_project_status'],
|
||||
name: 'ReportDashboard',
|
||||
components: {
|
||||
ProjectReportDetail,
|
||||
RankList,
|
||||
TrendChart,
|
||||
PieChart,
|
||||
ExportDialog
|
||||
},
|
||||
data() {
|
||||
@@ -307,10 +247,6 @@ export default {
|
||||
this.summaryList = res.data
|
||||
})
|
||||
},
|
||||
|
||||
loadMore(){
|
||||
this.$router.push('/hint/projectReport')
|
||||
},
|
||||
/** 查询详情 */
|
||||
openDetail(row) {
|
||||
getProjectReport(row.reportId).then(response => {
|
||||
@@ -324,33 +260,27 @@ export default {
|
||||
},
|
||||
async getBaseData() {
|
||||
this.fetchSummary()
|
||||
const [ cardRes, trendRes, pieRes,rankRes,ProjRes,reportRes ] = await Promise.all([
|
||||
getCardData(),
|
||||
const [ trendRes,rankRes,reportRes ] = await Promise.all([
|
||||
getTrendData(this.dateRange[0], this.dateRange[1]),
|
||||
getPieData(this.dateRange[0], this.dateRange[1]),
|
||||
getRankData(this.dateRange[0], this.dateRange[1]),
|
||||
getProjectData(this.dateRange[0], this.dateRange[1]),
|
||||
listClearProjectReport(this.dateRange[0], this.dateRange[1])
|
||||
]);
|
||||
// 处理卡片
|
||||
const { todayCount, todayCountChange: yc,
|
||||
inProgressProjects, projectChange: yp,
|
||||
completionRate, completionChange: ycr,
|
||||
exceptions
|
||||
} = cardRes.data;
|
||||
const pct = (t,y) => y===0 ? '—' : (((t-y)/y)*100).toFixed(1) + '%';
|
||||
this.summaryCards = [
|
||||
{ title:'今日报工人数', value:todayCount, displayChange:`${pct(todayCount,yc)} 较昨日`, changeClass: todayCount-yc>=0?'up':'down', icon:'el-icon-user', iconColor:'#67C23A' },
|
||||
{ title:'进行中项目', value:inProgressProjects, displayChange:`${pct(inProgressProjects,yp)} 较昨日`, changeClass: inProgressProjects-yp>=0?'up':'down', icon:'el-icon-document', iconColor:'#409EFF' },
|
||||
{ title:'本月报工', value:completionRate, displayChange:`${pct(completionRate,ycr)} 较上月`, changeClass: completionRate-ycr>=0?'up':'down', icon:'el-icon-data-analysis', iconColor:'#E6A23C' },
|
||||
{ title:'异常预警', value:exceptions, exception:true, icon:'el-icon-warning', iconColor:'#F56C6C' }
|
||||
];
|
||||
// const { todayCount, todayCountChange: yc,
|
||||
// inProgressProjects, projectChange: yp,
|
||||
// completionRate, completionChange: ycr,
|
||||
// exceptions
|
||||
// } = cardRes.data;
|
||||
// const pct = (t,y) => y===0 ? '—' : (((t-y)/y)*100).toFixed(1) + '%';
|
||||
// this.summaryCards = [
|
||||
// { title:'今日报工人数', value:todayCount, displayChange:`${pct(todayCount,yc)} 较昨日`, changeClass: todayCount-yc>=0?'up':'down', icon:'el-icon-user', iconColor:'#67C23A' },
|
||||
// { title:'进行中项目', value:inProgressProjects, displayChange:`${pct(inProgressProjects,yp)} 较昨日`, changeClass: inProgressProjects-yp>=0?'up':'down', icon:'el-icon-document', iconColor:'#409EFF' },
|
||||
// { title:'本月报工', value:completionRate, displayChange:`${pct(completionRate,ycr)} 较上月`, changeClass: completionRate-ycr>=0?'up':'down', icon:'el-icon-data-analysis', iconColor:'#E6A23C' },
|
||||
// { title:'异常预警', value:exceptions, exception:true, icon:'el-icon-warning', iconColor:'#F56C6C' }
|
||||
// ];
|
||||
// 赋值图表数据
|
||||
this.trendData = trendRes.data;
|
||||
this.pieData = pieRes.data;
|
||||
this.ranking = rankRes.data;
|
||||
this.projectList = ProjRes.data;
|
||||
console.log(this.projectList)
|
||||
this.historyList = reportRes.data;
|
||||
this.loading = false;
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="96px">
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="96px">
|
||||
<el-form-item label="报工人" prop="nickName">
|
||||
<el-input
|
||||
v-model="queryParams.nickName"
|
||||
@@ -87,21 +87,8 @@
|
||||
|
||||
<el-table v-loading="loading" :data="projectReportList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center"/>
|
||||
<el-table-column label="项目代号" prop="projectCode" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.projectCode==null" type="danger">无</el-tag>
|
||||
<el-tag v-else>{{ scope.row.projectCode }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目名称" align="center" width="150" prop="projectName">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.prePay>0">⭐</span>
|
||||
<span>{{ scope.row.projectName }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目编号" align="left" prop="projectNum"/>
|
||||
<el-table-column label="经办人" align="center" prop="nickName">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.nickName }}
|
||||
<template v-if="scope.row.deptName!=null">
|
||||
({{ scope.row.deptName }})
|
||||
@@ -111,19 +98,19 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="工作地点" align="center" prop="workPlace"/>
|
||||
<el-table-column label="国内/国外" align="center" prop="workType">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.workType===0?'':'warning'">{{ scope.row.workType===0?'国内':'国外' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="报工时间" align="center" prop="createTime">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<span>{{parseTime(scope.row.createTime,'{y}-{m}-{d}')}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark"/>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@@ -338,7 +325,6 @@ export default {
|
||||
this.projectReportList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
this.getProjectList()
|
||||
this.getDeptList();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,948 @@
|
||||
<template>
|
||||
考勤分析
|
||||
<div class="page-container">
|
||||
<!-- 顶部筛选区 -->
|
||||
<div class="filter-bar">
|
||||
<div class="container flex items-center justify-between gap-4">
|
||||
<!-- 日期选择 -->
|
||||
<div class="date-filter flex items-center gap-2">
|
||||
<el-radio-group v-model="selectedDateRange">
|
||||
<el-radio-button v-for="btn in dateButtons" :key="btn.value" :label="btn.value">
|
||||
{{ btn.label }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-date-picker :disabled="selectedDateRange !== 'custom'" v-model="selectedDate" type="datetimerange"
|
||||
format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" placeholder="选择日期" />
|
||||
</div>
|
||||
<!-- 搜索框 -->
|
||||
<!-- <div class="search-container relative flex-1 max-w-xs">
|
||||
<el-input v-model="searchQuery" placeholder="搜索用户 ID" class="search-input">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Search />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div> -->
|
||||
<!-- 筛选器 -->
|
||||
<div class="filters flex items-center gap-4">
|
||||
<el-dropdown trigger="hover">
|
||||
<el-button class="filter-btn whitespace-nowrap">
|
||||
记录类型<el-icon class="el-icon--right">
|
||||
<ArrowDown />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="type in recordTypes" :key="type.value">
|
||||
<el-checkbox v-model="selectedTypes" :label="type.value">
|
||||
{{ type.label }}
|
||||
</el-checkbox>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<!-- <el-dropdown trigger="click">
|
||||
<el-button class="filter-btn whitespace-nowrap">
|
||||
状态筛选<el-icon class="el-icon--right">
|
||||
<ArrowDown />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="status in statusList" :key="status.value">
|
||||
<el-checkbox v-model="selectedStatus" :label="status.value">
|
||||
{{ status.label }}
|
||||
</el-checkbox>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown> -->
|
||||
<el-button type="primary" icon="Search" @click="listData">
|
||||
查询
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 核心指标看板 -->
|
||||
<div class="container mb-6" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="6" v-for="(stat, index) in statistics" :key="index">
|
||||
<el-card shadow="hover" class="stat-card h-full">
|
||||
<div class="stat-header flex items-center justify-between">
|
||||
<h3 class="stat-label text-gray-500 text-sm">{{ stat.label }}</h3>
|
||||
<el-icon :class="stat.iconColor" :size="20">
|
||||
<component :is="stat.icon" />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="stat-value-container mt-2">
|
||||
<span class="stat-value text-2xl font-semibold">{{ stat.value }}</span>
|
||||
<!-- <span class="stat-trend text-sm ml-2" :class="stat.trend >= 0 ? 'text-green-500' : 'text-red-500'">
|
||||
<el-icon>
|
||||
<component :is="stat.trend >= 0 ? 'ArrowUp' : 'ArrowDown'" />
|
||||
</el-icon>
|
||||
{{ Math.abs(stat.trend) }}%
|
||||
</span> -->
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<!-- 图表区域 -->
|
||||
<div class="container mb-6" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-card shadow="hover" class="chart-card">
|
||||
<template #header>
|
||||
<h3 class="chart-title text-lg font-medium">记录趋势</h3>
|
||||
</template>
|
||||
<div id="trendChart" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card shadow="hover" class="chart-card">
|
||||
<template #header>
|
||||
<h3 class="chart-title text-lg font-medium">出勤排行</h3>
|
||||
</template>
|
||||
<!-- 使用条形图 -->
|
||||
<div class="chart-container" id="attendanceRankChart" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mt-6">
|
||||
<el-card shadow="hover" class="chart-card">
|
||||
<template #header>
|
||||
<h3 class="chart-title text-lg font-medium">出勤时长分布</h3>
|
||||
</template>
|
||||
<div id="durationChart" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mt-6">
|
||||
<el-card shadow="hover" class="chart-card">
|
||||
<template #header>
|
||||
<h3 class="chart-title text-lg font-medium">出勤记录分布</h3>
|
||||
</template>
|
||||
<div id="heatmapChart" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="container" v-loading="loading">
|
||||
<el-card shadow="hover" class="table-card">
|
||||
<el-table :data="tableList" class="full-width-table">
|
||||
<el-table-column v-for="col in tableColumns" :key="col.key" :prop="col.key" :label="col.label">
|
||||
|
||||
<template #default="{ row }" v-if="col.key === 'recordType'">
|
||||
<div class="flex items-center">
|
||||
<el-icon :class="getTypeIconColor(row.recordType)">
|
||||
<component :is="getTypeIcon(row.recordType)" />
|
||||
</el-icon>
|
||||
<span class="ml-2">{{ getTypeLabel(row.recordType) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ row }" v-else-if="col.key === 'timeRange'">
|
||||
{{ row.startTime }} - {{ row.endTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination-container flex justify-between items-center mt-4">
|
||||
<el-pagination v-model:current-page="pager.pageNum" :page-size="pager.pageSize" :total="pager.total"
|
||||
layout="prev, pager, next" />
|
||||
<span class="total-records text-sm text-gray-500">共 {{ pager.total }} 条记录</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { listAttendanceRecord } from '@/api/oa/attendanceRecord';
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import {
|
||||
ArrowDown,
|
||||
Document,
|
||||
Check,
|
||||
Clock,
|
||||
Minus,
|
||||
} from '@element-plus/icons-vue';
|
||||
import { reactive } from 'vue';
|
||||
|
||||
</script>
|
||||
const { proxy } = getCurrentInstance();
|
||||
const selectedDateRange = ref('today');
|
||||
const selectedDate = ref([
|
||||
proxy.parseTime(new Date(new Date().setHours(0, 0, 0, 0)), '{y}-{m}-{d} 00:00:00'),
|
||||
proxy.parseTime(new Date(new Date().setHours(23, 59, 59, 999)), '{y}-{m}-{d} 23:59:59')
|
||||
]);
|
||||
const selectedTypes = ref([]);
|
||||
|
||||
|
||||
watch(selectedDateRange, (newVal) => {
|
||||
switch (newVal) {
|
||||
case 'today':
|
||||
// 将日期设置为今天整天的范围,00:00:00 - 23:59:59
|
||||
// 设置YYYY-MM-DD HH:MM:SS
|
||||
selectedDate.value = [
|
||||
proxy.parseTime(new Date(new Date().setHours(0, 0, 0, 0)), '{y}-{m}-{d} 00:00:00'),
|
||||
proxy.parseTime(new Date(new Date().setHours(23, 59, 59, 999)), '{y}-{m}-{d} 23:59:59')
|
||||
];
|
||||
break;
|
||||
case 'week':
|
||||
// 将日期设置为本周的开始和结束日期
|
||||
const today = new Date();
|
||||
const startOfWeek = new Date(today);
|
||||
startOfWeek.setDate(today.getDate() - today.getDay());
|
||||
startOfWeek.setHours(0, 0, 0, 0);
|
||||
const endOfWeek = new Date(today);
|
||||
endOfWeek.setDate(today.getDate() + (6 - today.getDay()));
|
||||
selectedDate.value = [
|
||||
proxy.parseTime(startOfWeek, '{y}-{m}-{d} 00:00:00'),
|
||||
proxy.parseTime(endOfWeek, '{y}-{m}-{d} 23:59:59')
|
||||
];
|
||||
break;
|
||||
case 'month':
|
||||
// 将日期设置为本月开始和结束日期
|
||||
const startOfMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 1);
|
||||
const endOfMonth = new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0);
|
||||
selectedDate.value = [
|
||||
proxy.parseTime(startOfMonth, '{y}-{m}-{d} 00:00:00'),
|
||||
proxy.parseTime(endOfMonth, '{y}-{m}-{d} 23:59:59')
|
||||
];
|
||||
break;
|
||||
case 'custom':
|
||||
selectedDate.value = [];
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const dateButtons = [
|
||||
{ label: '今日', value: 'today' },
|
||||
{ label: '本周', value: 'week' },
|
||||
{ label: '本月', value: 'month' },
|
||||
{ label: '自定义', value: 'custom' }
|
||||
];
|
||||
|
||||
const recordTypes = [
|
||||
{ label: '考勤', value: 'attendance' },
|
||||
{ label: '加班', value: 'overtime' },
|
||||
{ label: '出差', value: 'travel' }
|
||||
];
|
||||
|
||||
const statistics = ref([
|
||||
{
|
||||
label: '总记录数',
|
||||
value: '1,286',
|
||||
trend: 12.5,
|
||||
icon: 'Document',
|
||||
iconColor: 'text-blue-500'
|
||||
},
|
||||
{
|
||||
label: '平均考勤时长',
|
||||
value: '2.5h',
|
||||
trend: -5.2,
|
||||
icon: 'Clock',
|
||||
iconColor: 'text-yellow-500'
|
||||
},
|
||||
{
|
||||
label: '考勤总时长',
|
||||
value: '526h',
|
||||
trend: 8.3,
|
||||
icon: 'Document',
|
||||
iconColor: 'text-green-500'
|
||||
}
|
||||
]);
|
||||
|
||||
const setStatistics = ({ totalDuration, totalRecords, averageDuration }) => {
|
||||
statistics.value[0].value = totalRecords
|
||||
statistics.value[1].value = averageDuration
|
||||
statistics.value[2].value = totalDuration
|
||||
}
|
||||
|
||||
const setTrend = ({ attendanceData, overtimeData, travelData, xAxis }) => {
|
||||
trendChart.value.setOption({
|
||||
animation: false,
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['考勤', '加班', '请假', '出差']
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xAxis
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '考勤',
|
||||
type: 'line',
|
||||
data: attendanceData.map(item => item.duration)
|
||||
},
|
||||
{
|
||||
name: '加班',
|
||||
type: 'line',
|
||||
data: overtimeData.map(item => item.duration)
|
||||
},
|
||||
{
|
||||
name: '出差',
|
||||
type: 'line',
|
||||
data: travelData.map(item => item.duration)
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
const setHeatmap = ({ heatmapData, hours, days }) => {
|
||||
console.log(heatmapData, hours, days, heatmapChart.value)
|
||||
|
||||
// 计算数据中的最大值和最小值,用于visualMap映射
|
||||
const values = heatmapData.map(item => item[2]);
|
||||
const minValue = Math.min(...values);
|
||||
const maxValue = Math.max(...values);
|
||||
|
||||
heatmapChart.value.setOption({
|
||||
animation: false,
|
||||
tooltip: {
|
||||
position: 'top',
|
||||
// 格式化提示信息,显示更友好的内容
|
||||
formatter: (params) => {
|
||||
const hour = hours[params.data[0]];
|
||||
const day = days[params.data[1]];
|
||||
const value = params.data[2].toFixed(1);
|
||||
return `${day} ${hour}<br/>时长: ${value}小时`;
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
height: '50%',
|
||||
top: '10%',
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
bottom: '20%'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: hours,
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: days,
|
||||
splitArea: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
// 添加visualMap配置,解决错误
|
||||
visualMap: {
|
||||
min: minValue,
|
||||
max: maxValue,
|
||||
calculable: true,
|
||||
orient: 'horizontal',
|
||||
left: 'center',
|
||||
bottom: '5%',
|
||||
// 设置颜色渐变范围
|
||||
inRange: {
|
||||
color: ['#f0f9e8', '#bae4bc', '#7bccc4', '#43a2ca', '#0868ac']
|
||||
},
|
||||
// 显示数值标签
|
||||
label: {
|
||||
show: true,
|
||||
formatter: (value) => `${value}h`
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
name: '出勤分布',
|
||||
type: 'heatmap',
|
||||
data: heatmapData,
|
||||
label: {
|
||||
show: true,
|
||||
// 只显示有值的单元格
|
||||
formatter: (params) => params.data[2] > 0 ? params.data[2].toFixed(1) : ''
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.3)'
|
||||
}
|
||||
}
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
const setAttendanceRank = ({ users, durations }) => {
|
||||
attendanceRankChart.value.setOption({
|
||||
animation: false,
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
rotate: 45
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: users,
|
||||
axisLabel: {
|
||||
rotate: 45
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
data: durations
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
const setDuration = ({ durationData, xAxis }) => {
|
||||
durationChart.value.setOption({
|
||||
animation: false,
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow' // 阴影指示器,提升交互体验
|
||||
},
|
||||
// 简化提示框样式
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
borderColor: '#eee',
|
||||
borderWidth: 1,
|
||||
padding: 8,
|
||||
formatter: (params) => {
|
||||
// 只显示基础的人数信息
|
||||
return `人数:${params[0].value}`;
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xAxis
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '人数' // 明确y轴含义
|
||||
},
|
||||
// 将具体数字展示在柱子上
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow' // 阴影指示器,提升交互体验
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
data: durationData,
|
||||
itemStyle: {
|
||||
color: '#4361ee' // 统一柱状图颜色
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
const getTypeIcon = (type) => {
|
||||
const icons = {
|
||||
attendance: Check,
|
||||
overtime: Clock,
|
||||
leave: Minus,
|
||||
travel: Minus
|
||||
};
|
||||
return icons[type] || Document;
|
||||
};
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
// 创建多个ref记录init的chart
|
||||
const trendChart = ref(null);
|
||||
const durationChart = ref(null);
|
||||
const heatmapChart = ref(null);
|
||||
const attendanceRankChart = ref(null);
|
||||
|
||||
const initCharts = () => {
|
||||
trendChart.value = echarts.init(document.querySelector('#trendChart'));
|
||||
// 横向的条形图,显示用户名和出勤时长
|
||||
attendanceRankChart.value = echarts.init(document.querySelector('#attendanceRankChart'));
|
||||
durationChart.value = echarts.init(document.querySelector('#durationChart'));
|
||||
heatmapChart.value = echarts.init(document.querySelector('#heatmapChart'));
|
||||
};
|
||||
|
||||
const updateCharts = () => {
|
||||
setStatistics(formatters.status(list.value))
|
||||
const trendData = formatters.trend(list.value)
|
||||
setTrend(trendData)
|
||||
const heatmapData = formatters.heatmap(list.value)
|
||||
setHeatmap(heatmapData)
|
||||
const attendanceRankData = formatters.rank(list.value)
|
||||
setAttendanceRank(attendanceRankData)
|
||||
const durationData = formatters.duration(list.value)
|
||||
setDuration(durationData)
|
||||
}
|
||||
|
||||
const formatters = {
|
||||
status: (list) => {
|
||||
// 统计出勤时长,计算总记录数,平均出勤时长和总时长
|
||||
const totalDuration = list.reduce((acc, item) => acc + item.durationHour, 0)
|
||||
const totalRecords = list.length
|
||||
// 可能出现NAN,所以需要处理
|
||||
const averageDuration = totalRecords > 0 ? (totalDuration / totalRecords).toFixed(2) : 0
|
||||
console.log(totalDuration, totalRecords, averageDuration)
|
||||
return {
|
||||
totalDuration,
|
||||
totalRecords,
|
||||
averageDuration
|
||||
}
|
||||
},
|
||||
trend: (list) => {
|
||||
// 用折线图按照时间汇总, 根据recordType分组给考勤, 加班和出差,给出echarts所需的数据格式
|
||||
const attendanceData = []
|
||||
const overtimeData = []
|
||||
const travelData = []
|
||||
const xAxis = []
|
||||
for (const item of list) {
|
||||
// 如果是相同的recordData,则将durationHour相加, xAxis单独去重
|
||||
const date = item.recordDate
|
||||
const duration = item.durationHour
|
||||
if (item.recordType === 'attendance') {
|
||||
const index = attendanceData.findIndex(item => item.date === date)
|
||||
if (index !== -1) {
|
||||
attendanceData[index].duration += duration
|
||||
} else {
|
||||
attendanceData.push({ date, duration })
|
||||
xAxis.push(date)
|
||||
}
|
||||
} else if (item.recordType === 'overtime') {
|
||||
const index = overtimeData.findIndex(item => item.date === date)
|
||||
if (index !== -1) {
|
||||
overtimeData[index].duration += duration
|
||||
} else {
|
||||
overtimeData.push({ date, duration })
|
||||
xAxis.push(date)
|
||||
}
|
||||
} else if (item.recordType === 'travel') {
|
||||
const index = travelData.findIndex(item => item.date === date)
|
||||
if (index !== -1) {
|
||||
travelData[index].duration += duration
|
||||
} else {
|
||||
travelData.push({ date, duration })
|
||||
xAxis.push(date)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 对xAxis去重
|
||||
const xAxisSet = [...new Set(xAxis)]
|
||||
console.log(attendanceData, overtimeData, travelData)
|
||||
return {
|
||||
attendanceData,
|
||||
overtimeData,
|
||||
travelData,
|
||||
xAxis: xAxisSet
|
||||
}
|
||||
},
|
||||
// 出勤排行整理, 只保留排名前10的用户
|
||||
rank: (list) => {
|
||||
const map = {};
|
||||
for (const item of list) {
|
||||
const user = item.nickName
|
||||
const duration = item.durationHour
|
||||
if (map[user]) {
|
||||
map[user] += duration
|
||||
} else {
|
||||
map[user] = duration
|
||||
}
|
||||
}
|
||||
const users = Object.keys(map).sort((a, b) => map[a] - map[b]).slice(0, 10)
|
||||
const durations = users.map(user => map[user])
|
||||
return { users, durations }
|
||||
},
|
||||
// 出勤时长分布热力图
|
||||
heatmap: (list) => {
|
||||
// 定义小时和星期的映射关系
|
||||
const hours = ['12a', '1a', '2a', '3a', '4a', '5a', '6a',
|
||||
'7a', '8a', '9a', '10a', '11a',
|
||||
'12p', '1p', '2p', '3p', '4p', '5p',
|
||||
'6p', '7p', '8p', '9p', '10p', '11p'];
|
||||
const days = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
|
||||
|
||||
// 初始化7天×24小时的时长矩阵(初始值为0)
|
||||
const durationMatrix = Array.from({ length: 7 }, () => Array(24).fill(0));
|
||||
|
||||
// 辅助函数:将日期字符串转换为星期几索引(0=周一,6=周日)
|
||||
const getDayIndex = (dateStr) => {
|
||||
const date = new Date(dateStr);
|
||||
let day = date.getDay(); // getDay()返回0=周日,1=周一,...,6=周六
|
||||
return day === 0 ? 6 : day - 1; // 转换为0=周一,6=周日
|
||||
};
|
||||
|
||||
// 辅助函数:将时间字符串转换为小时索引(0=12a,23=11p)
|
||||
const getHourIndex = (timeStr) => {
|
||||
const hour = parseInt(timeStr.split(' ')[1].split(':')[0], 10);
|
||||
return hour; // 0-23直接对应小时索引
|
||||
};
|
||||
|
||||
// 辅助函数:计算两个时间之间每个小时的时长(单位:小时)
|
||||
const calculateHourlyDurations = (startTime, endTime) => {
|
||||
const start = new Date(startTime);
|
||||
const end = new Date(endTime);
|
||||
|
||||
// 如果开始时间晚于结束时间,视为当天内的记录(忽略跨天情况)
|
||||
if (start > end) return {};
|
||||
|
||||
const startHour = start.getHours();
|
||||
const endHour = end.getHours();
|
||||
const hourly = {};
|
||||
|
||||
// 同一小时内
|
||||
if (startHour === endHour) {
|
||||
const durationHours = (end - start) / (1000 * 60 * 60);
|
||||
hourly[startHour] = (hourly[startHour] || 0) + durationHours;
|
||||
return hourly;
|
||||
}
|
||||
|
||||
// 跨多个小时
|
||||
// 开始小时的时长
|
||||
const startEndOfHour = new Date(start);
|
||||
startEndOfHour.setHours(startHour + 1, 0, 0, 0);
|
||||
hourly[startHour] = (startEndOfHour - start) / (1000 * 60 * 60);
|
||||
|
||||
// 中间完整小时的时长
|
||||
for (let h = startHour + 1; h < endHour; h++) {
|
||||
hourly[h] = 1; // 完整小时为1小时
|
||||
}
|
||||
|
||||
// 结束小时的时长
|
||||
const endStartOfHour = new Date(end);
|
||||
endStartOfHour.setHours(endHour, 0, 0, 0);
|
||||
hourly[endHour] = (end - endStartOfHour) / (1000 * 60 * 60);
|
||||
|
||||
return hourly;
|
||||
};
|
||||
|
||||
// 处理每条记录
|
||||
for (const item of list) {
|
||||
try {
|
||||
// 获取星期索引(y轴)
|
||||
const dayIndex = getDayIndex(item.recordDate);
|
||||
|
||||
// 计算每个小时的时长分布
|
||||
const hourlyDurations = calculateHourlyDurations(item.startTime, item.endTime);
|
||||
|
||||
// 累加时长到矩阵中
|
||||
Object.entries(hourlyDurations).forEach(([hourStr, durationHours]) => {
|
||||
const hourIndex = parseInt(hourStr, 10); // x轴索引
|
||||
if (hourIndex >= 0 && hourIndex < 24 && dayIndex >= 0 && dayIndex < 7) {
|
||||
durationMatrix[dayIndex][hourIndex] += parseFloat(durationHours.toFixed(2));
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('处理记录出错:', item, '错误:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为热力图所需格式 [[x, y, value], ...]
|
||||
const heatmapData = [];
|
||||
for (let y = 0; y < 7; y++) {
|
||||
for (let x = 0; x < 24; x++) {
|
||||
heatmapData.push([x, y, durationMatrix[y][x]]);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算最大值和最小值用于热力图颜色映射
|
||||
const allValues = heatmapData.map(item => item[2]);
|
||||
const minValue = Math.min(...allValues);
|
||||
const maxValue = Math.max(...allValues);
|
||||
|
||||
return {
|
||||
heatmapData,
|
||||
minValue,
|
||||
maxValue,
|
||||
hours,
|
||||
days
|
||||
};
|
||||
},
|
||||
// 出勤时长分布条形图, 汇总每个人的出勤时长,按照时长分成5组,统计每一组的人数
|
||||
duration: (list) => {
|
||||
const map = {};
|
||||
for (const item of list) {
|
||||
const duration = item.durationHour
|
||||
if (map[duration]) {
|
||||
map[duration]++
|
||||
} else {
|
||||
map[duration] = 1
|
||||
}
|
||||
}
|
||||
const durationData = Object.keys(map)
|
||||
// 按照durationData的值,统计最大和最小的数字,均分成5组
|
||||
const min = Math.min(...durationData)
|
||||
const max = Math.max(...durationData)
|
||||
const step = (max - min) / 5
|
||||
const groups = []
|
||||
for (let i = 0; i < 5; i++) {
|
||||
groups.push(min + i * step)
|
||||
}
|
||||
// 记录每个分组所在的数量
|
||||
Object.keys(map).forEach(item => {
|
||||
// 判断item数据哪个分组
|
||||
const index = groups.findIndex(group => item <= group)
|
||||
groups[index] += map[item]
|
||||
})
|
||||
return {
|
||||
xAxis: Object.keys(map),
|
||||
durationData: Object.values(map)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const params = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 9999,
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
recordType: ''
|
||||
})
|
||||
|
||||
// 请求到的原始数据
|
||||
const list = ref([]);
|
||||
|
||||
// 查询数据
|
||||
const listData = async () => {
|
||||
loading.value = true
|
||||
const { rows, total } = await listAttendanceRecord({
|
||||
...params,
|
||||
recordType: selectedTypes.value.join(','),
|
||||
// YYYY-MM-DD HH:MM:SS
|
||||
startTime: selectedDate.value[0],
|
||||
endTime: selectedDate.value[1]
|
||||
})
|
||||
list.value = rows
|
||||
pager.total = total;
|
||||
updateCharts();
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
// 表格的分页和表格的展示数据
|
||||
const pager = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
const tableList = computed(() => {
|
||||
return list.value.slice((pager.pageNum - 1) * pager.pageSize, pager.pageNum * pager.pageSize)
|
||||
})
|
||||
// 表格相关数据
|
||||
const tableColumns = [
|
||||
{ key: 'nickName', label: '用户' },
|
||||
{ key: 'recordDate', label: '记录日期' },
|
||||
{ key: 'recordType', label: '类型' },
|
||||
{ key: 'timeRange', label: '时间范围' },
|
||||
{ key: 'durationHour', label: '时长(小时)' },
|
||||
];
|
||||
const getTypeIconColor = (type) => {
|
||||
const colors = {
|
||||
attendance: 'text-blue-500',
|
||||
overtime: 'text-yellow-500',
|
||||
leave: 'text-red-500',
|
||||
travel: 'text-green-500'
|
||||
};
|
||||
return colors[type] || '';
|
||||
};
|
||||
const getTypeLabel = (type) => {
|
||||
const labels = {
|
||||
attendance: '考勤',
|
||||
overtime: '加班',
|
||||
leave: '请假',
|
||||
travel: '出差'
|
||||
};
|
||||
return labels[type] || type;
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
initCharts();
|
||||
await listData()
|
||||
updateCharts();
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
/* 基础样式 */
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin-left: 30px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.gap-4 {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.mb-6 {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.mt-6 {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.text-2xl {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.font-medium {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text-gray-500 {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.text-green-500 {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.text-red-500 {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.text-blue-500 {
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.text-yellow-500 {
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.whitespace-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.full-width-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 筛选区样式 */
|
||||
.filter-bar {
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
padding: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
/* 统计卡片样式 */
|
||||
.stat-card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.stat-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.stat-value-container {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* 图表样式 */
|
||||
.chart-card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.table-card {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: #888;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
/* 数字输入框样式 */
|
||||
:deep(input[type="number"]::-webkit-inner-spin-button),
|
||||
:deep(input[type="number"]::-webkit-outer-spin-button) {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
326
gear-ui3/src/views/peoples/salary/index.vue
Normal file
326
gear-ui3/src/views/peoples/salary/index.vue
Normal file
@@ -0,0 +1,326 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
|
||||
<el-form-item label="员工" prop="employeeName">
|
||||
<user-select v-model="queryParams.employeeId" placeholder="请选择员工" />
|
||||
</el-form-item>
|
||||
<el-form-item label="发薪月份" prop="payPeriod">
|
||||
<!-- 只选择年和月拼接 -01 -->
|
||||
<el-date-picker clearable
|
||||
v-model="queryParams.payPeriod"
|
||||
type="month"
|
||||
value-format="YYYY-MM-01 00:00:00"
|
||||
placeholder="请选择发薪月份">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="salaryRecordsList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="唯一记录ID" align="center" prop="salaryId" v-if="false"/>
|
||||
<el-table-column label="员工" align="center" prop="employeeName" />
|
||||
<el-table-column label="发薪月份" align="center" prop="payPeriod" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ formatterTime(scope.row.payPeriod) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="基本工资" align="center" prop="baseSalary" />
|
||||
<el-table-column label="绩效奖金" align="center" prop="performanceBonus" />
|
||||
<el-table-column label="加班工资" align="center" prop="overtimePay" />
|
||||
<el-table-column label="各类补贴" align="center" prop="allowance" />
|
||||
<el-table-column label="社保个人部分" align="center" prop="socialSecurity" />
|
||||
<el-table-column label="公积金个人部分" align="center" prop="housingFund" />
|
||||
<el-table-column label="个人所得税" align="center" prop="incomeTax" />
|
||||
<!-- <el-table-column label="应发工资" align="center" prop="grossSalary" />
|
||||
<el-table-column label="实发工资" align="center" prop="netSalary" /> -->
|
||||
<el-table-column label="发放状态" align="center" prop="payStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="oa_salary_status" :value="scope.row.payStatus"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 添加或修改工资发放记录对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="600px" append-to-body>
|
||||
<el-form ref="salaryRecordsRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="员工" prop="employeeId">
|
||||
<user-select v-model="form.employeeId" placeholder="请选择员工" />
|
||||
</el-form-item>
|
||||
<el-form-item label="发薪月份" prop="payPeriod">
|
||||
<el-date-picker clearable
|
||||
v-model="form.payPeriod"
|
||||
type="month"
|
||||
value-format="YYYY-MM-01 00:00:00"
|
||||
placeholder="请选择发薪月份">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="基本工资" prop="baseSalary">
|
||||
<el-input v-model="form.baseSalary" placeholder="请输入基本工资" />
|
||||
</el-form-item>
|
||||
<el-form-item label="绩效奖金" prop="performanceBonus">
|
||||
<el-input v-model="form.performanceBonus" placeholder="请输入绩效奖金" />
|
||||
</el-form-item>
|
||||
<el-form-item label="加班工资" prop="overtimePay">
|
||||
<el-input v-model="form.overtimePay" placeholder="请输入加班工资" />
|
||||
</el-form-item>
|
||||
<el-form-item label="各类补贴" prop="allowance">
|
||||
<el-input v-model="form.allowance" placeholder="请输入各类补贴" />
|
||||
</el-form-item>
|
||||
<el-form-item label="社保个人部分" prop="socialSecurity">
|
||||
<el-input v-model="form.socialSecurity" placeholder="请输入社保个人部分" />
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金个人部分" prop="housingFund">
|
||||
<el-input v-model="form.housingFund" placeholder="请输入公积金个人部分" />
|
||||
</el-form-item>
|
||||
<el-form-item label="个人所得税" prop="incomeTax">
|
||||
<el-input v-model="form.incomeTax" placeholder="请输入个人所得税" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
<el-form-item label="发放状态" prop="payStatus">
|
||||
<el-select v-model="form.payStatus" placeholder="请选择发放状态">
|
||||
<el-option
|
||||
v-for="dict in oa_salary_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="SalaryRecords">
|
||||
import { listSalaryRecords, getSalaryRecords, delSalaryRecords, addSalaryRecords, updateSalaryRecords, batchPaySalary } from "@/api/oa/salaryRecords";
|
||||
import UserSelect from "@/components/UserSelect/index.vue";
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { oa_salary_status } = proxy.useDict('oa_salary_status');
|
||||
|
||||
const salaryRecordsList = ref([]);
|
||||
const open = ref(false);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const title = ref("");
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
employeeId: undefined,
|
||||
payPeriod: undefined,
|
||||
},
|
||||
rules: {
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
const formatterTime = (time) => {
|
||||
return time ? proxy.parseTime(time, '{y}-{m}') : null;
|
||||
}
|
||||
|
||||
/** 查询工资发放记录列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
listSalaryRecords(queryParams.value).then(response => {
|
||||
salaryRecordsList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 取消按钮
|
||||
function cancel() {
|
||||
open.value = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
// 表单重置
|
||||
function reset() {
|
||||
form.value = {
|
||||
salaryId: null,
|
||||
employeeId: null,
|
||||
payPeriod: null,
|
||||
baseSalary: null,
|
||||
performanceBonus: null,
|
||||
overtimePay: null,
|
||||
allowance: null,
|
||||
socialSecurity: null,
|
||||
housingFund: null,
|
||||
incomeTax: null,
|
||||
grossSalary: null,
|
||||
netSalary: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
delFlag: null,
|
||||
remark: null,
|
||||
payStatus: null
|
||||
};
|
||||
proxy.resetForm("salaryRecordsRef");
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
// 多选框选中数据
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.salaryId);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
|
||||
/** 新增按钮操作 */
|
||||
function handleAdd() {
|
||||
reset();
|
||||
open.value = true;
|
||||
title.value = "添加工资发放记录";
|
||||
}
|
||||
|
||||
/** 修改按钮操作 */
|
||||
function handleUpdate(row) {
|
||||
loading.value = true
|
||||
reset();
|
||||
const _salaryId = row.salaryId || ids.value
|
||||
getSalaryRecords(_salaryId).then(response => {
|
||||
loading.value = false;
|
||||
form.value = response.data;
|
||||
open.value = true;
|
||||
title.value = "修改工资发放记录";
|
||||
});
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs["salaryRecordsRef"].validate(valid => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
if (form.value.salaryId != null) {
|
||||
updateSalaryRecords(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
}).finally(() => {
|
||||
buttonLoading.value = false;
|
||||
});
|
||||
} else {
|
||||
addSalaryRecords(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("新增成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
}).finally(() => {
|
||||
buttonLoading.value = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const _salaryIds = row.salaryId || ids.value;
|
||||
proxy.$modal.confirm('是否确认删除工资发放记录编号为"' + _salaryIds + '"的数据项?').then(function() {
|
||||
loading.value = true;
|
||||
return delSalaryRecords(_salaryIds);
|
||||
}).then(() => {
|
||||
loading.value = true;
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
}).finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download('oa/salaryRecords/export', {
|
||||
...queryParams.value
|
||||
}, `salaryRecords_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
getList();
|
||||
</script>
|
||||
@@ -4,12 +4,14 @@ import createAutoImport from './auto-import'
|
||||
import createSvgIcon from './svg-icon'
|
||||
import createCompression from './compression'
|
||||
import createSetupExtend from './setup-extend'
|
||||
import createTailwindcss from './tailwind'
|
||||
|
||||
export default function createVitePlugins(viteEnv, isBuild = false) {
|
||||
const vitePlugins = [vue()]
|
||||
vitePlugins.push(createAutoImport())
|
||||
vitePlugins.push(createSetupExtend())
|
||||
vitePlugins.push(createSvgIcon(isBuild))
|
||||
// vitePlugins.push(createTailwindcss())
|
||||
isBuild && vitePlugins.push(...createCompression(viteEnv))
|
||||
return vitePlugins
|
||||
}
|
||||
|
||||
5
gear-ui3/vite/plugins/tailwind.js
Normal file
5
gear-ui3/vite/plugins/tailwind.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
export default function createTailwindcss() {
|
||||
return tailwindcss()
|
||||
}
|
||||
BIN
node_modules/.pnpm/@esbuild+win32-x64@0.25.8/node_modules/@esbuild/win32-x64/esbuild.exe
generated
vendored
Normal file
BIN
node_modules/.pnpm/@esbuild+win32-x64@0.25.8/node_modules/@esbuild/win32-x64/esbuild.exe
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/.pnpm/@rollup+rollup-win32-x64-msvc@4.46.2/node_modules/@rollup/rollup-win32-x64-msvc/rollup.win32-x64-msvc.node
generated
vendored
Normal file
BIN
node_modules/.pnpm/@rollup+rollup-win32-x64-msvc@4.46.2/node_modules/@rollup/rollup-win32-x64-msvc/rollup.win32-x64-msvc.node
generated
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
node_modules/.pnpm/lightningcss-win32-x64-msvc@1.30.1/node_modules/lightningcss-win32-x64-msvc/lightningcss.win32-x64-msvc.node
generated
vendored
Normal file
BIN
node_modules/.pnpm/lightningcss-win32-x64-msvc@1.30.1/node_modules/lightningcss-win32-x64-msvc/lightningcss.win32-x64-msvc.node
generated
vendored
Normal file
Binary file not shown.
Reference in New Issue
Block a user