Compare commits

..

No commits in common. "main" and "ccd" have entirely different histories.
main ... ccd

22 changed files with 417 additions and 5237 deletions

View File

@ -4,7 +4,8 @@ VITE_API_PREFIX = '/dev-api'
# 接口地址 # 接口地址
# VITE_API_BASE_URL = 'http://pms.dtyx.net:9158/' # VITE_API_BASE_URL = 'http://pms.dtyx.net:9158/'
VITE_API_BASE_URL = 'http://localhost:8888/' # VITE_API_BASE_URL = 'http://localhost:8888/'
VITE_API_BASE_URL = 'http://10.18.34.213:8888/'
# 接口地址 (WebSocket) # 接口地址 (WebSocket)
VITE_API_WS_URL = 'ws://localhost:8000' VITE_API_WS_URL = 'ws://localhost:8000'

View File

@ -68,15 +68,9 @@ importers:
echarts: echarts:
specifier: ^5.4.2 specifier: ^5.4.2
version: 5.5.0 version: 5.5.0
html2canvas:
specifier: ^1.4.1
version: 1.4.1
jsencrypt: jsencrypt:
specifier: ^3.3.2 specifier: ^3.3.2
version: 3.3.2 version: 3.3.2
jspdf:
specifier: ^3.0.1
version: 3.0.1
lint-staged: lint-staged:
specifier: ^15.2.10 specifier: ^15.2.10
version: 15.2.10 version: 15.2.10
@ -469,10 +463,6 @@ packages:
resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/runtime@7.28.2':
resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==}
engines: {node: '>=6.9.0'}
'@babel/template@7.24.0': '@babel/template@7.24.0':
resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@ -1202,18 +1192,12 @@ packages:
resolution: {integrity: sha512-yuIv/WRffRzL7cBW+sla4HwBZrEXRNf1MKQ5SklPEadth+BKbDxiVG8A3iISN5B3yC4EeSCzMZP8llHTcUhOzQ==} resolution: {integrity: sha512-yuIv/WRffRzL7cBW+sla4HwBZrEXRNf1MKQ5SklPEadth+BKbDxiVG8A3iISN5B3yC4EeSCzMZP8llHTcUhOzQ==}
deprecated: This is a stub types definition. query-string provides its own type definitions, so you do not need this installed. deprecated: This is a stub types definition. query-string provides its own type definitions, so you do not need this installed.
'@types/raf@3.4.3':
resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==}
'@types/sortablejs@1.15.8': '@types/sortablejs@1.15.8':
resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==} resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==}
'@types/svgo@2.6.4': '@types/svgo@2.6.4':
resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==} resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
'@types/unist@2.0.10': '@types/unist@2.0.10':
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
@ -1624,10 +1608,6 @@ packages:
balanced-match@1.0.2: balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
base64-arraybuffer@1.0.2:
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
engines: {node: '>= 0.6.0'}
base@0.11.2: base@0.11.2:
resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -1672,11 +1652,6 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true hasBin: true
btoa@1.2.1:
resolution: {integrity: sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==}
engines: {node: '>= 0.4.0'}
hasBin: true
buffer-from@1.1.2: buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
@ -1717,10 +1692,6 @@ packages:
caniuse-lite@1.0.30001620: caniuse-lite@1.0.30001620:
resolution: {integrity: sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==} resolution: {integrity: sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==}
canvg@3.0.11:
resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==}
engines: {node: '>=10.0.0'}
capital-case@1.0.4: capital-case@1.0.4:
resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==}
@ -1912,9 +1883,6 @@ packages:
crypto-js@4.2.0: crypto-js@4.2.0:
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
css-line-break@2.1.0:
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
css-select@4.3.0: css-select@4.3.0:
resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==}
@ -2091,9 +2059,6 @@ packages:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'} engines: {node: '>= 4'}
dompurify@3.2.6:
resolution: {integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==}
domutils@1.7.0: domutils@1.7.0:
resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
@ -2647,9 +2612,6 @@ packages:
fastq@1.17.1: fastq@1.17.1:
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
fflate@0.8.2:
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
file-entry-cache@8.0.0: file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'} engines: {node: '>=16.0.0'}
@ -2903,10 +2865,6 @@ packages:
resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
html2canvas@1.4.1:
resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
engines: {node: '>=8.0.0'}
htmlparser2@3.10.1: htmlparser2@3.10.1:
resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==} resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==}
@ -3210,9 +3168,6 @@ packages:
jsonfile@6.1.0: jsonfile@6.1.0:
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
jspdf@3.0.1:
resolution: {integrity: sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==}
keyv@4.5.4: keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@ -3678,9 +3633,6 @@ packages:
perfect-debounce@1.0.0: perfect-debounce@1.0.0:
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
picocolors@1.0.1: picocolors@1.0.1:
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
@ -3862,9 +3814,6 @@ packages:
queue-microtask@1.2.3: queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
raf@3.4.1:
resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
randombytes@2.1.0: randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
@ -3888,9 +3837,6 @@ packages:
resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
regenerator-runtime@0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
regenerator-runtime@0.14.1: regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
@ -3965,10 +3911,6 @@ packages:
rfdc@1.4.1: rfdc@1.4.1:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
rgbcolor@1.0.1:
resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==}
engines: {node: '>= 0.8.15'}
rollup@4.17.2: rollup@4.17.2:
resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==} resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'} engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@ -4196,10 +4138,6 @@ packages:
resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==}
deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility'
stackblur-canvas@2.7.0:
resolution: {integrity: sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==}
engines: {node: '>=0.1.14'}
static-extend@0.1.2: static-extend@0.1.2:
resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -4299,10 +4237,6 @@ packages:
svg-baker@1.7.0: svg-baker@1.7.0:
resolution: {integrity: sha512-nibslMbkXOIkqKVrfcncwha45f97fGuAOn1G99YwnwTj8kF9YiM6XexPcUso97NxOm6GsP0SIvYVIosBis1xLg==} resolution: {integrity: sha512-nibslMbkXOIkqKVrfcncwha45f97fGuAOn1G99YwnwTj8kF9YiM6XexPcUso97NxOm6GsP0SIvYVIosBis1xLg==}
svg-pathdata@6.0.3:
resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==}
engines: {node: '>=12.0.0'}
svg-tags@1.0.0: svg-tags@1.0.0:
resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
@ -4340,9 +4274,6 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
text-segmentation@1.0.3:
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
text-table@0.2.0: text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
@ -4560,9 +4491,6 @@ packages:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'} engines: {node: '>= 0.4.0'}
utrie@1.0.2:
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
v-viewer@3.0.13: v-viewer@3.0.13:
resolution: {integrity: sha512-T8pgGzlF0ZCHVpD/32OKsD8MlpI6tqYP3n1XLcSjvGQMc0ABn8nJ4AumxvzAKVQrLRWtDTG6qRGAyCPCmi7ceA==} resolution: {integrity: sha512-T8pgGzlF0ZCHVpD/32OKsD8MlpI6tqYP3n1XLcSjvGQMc0ABn8nJ4AumxvzAKVQrLRWtDTG6qRGAyCPCmi7ceA==}
peerDependencies: peerDependencies:
@ -5143,8 +5071,6 @@ snapshots:
dependencies: dependencies:
regenerator-runtime: 0.14.1 regenerator-runtime: 0.14.1
'@babel/runtime@7.28.2': {}
'@babel/template@7.24.0': '@babel/template@7.24.0':
dependencies: dependencies:
'@babel/code-frame': 7.24.2 '@babel/code-frame': 7.24.2
@ -5871,18 +5797,12 @@ snapshots:
dependencies: dependencies:
query-string: 9.0.0 query-string: 9.0.0
'@types/raf@3.4.3':
optional: true
'@types/sortablejs@1.15.8': {} '@types/sortablejs@1.15.8': {}
'@types/svgo@2.6.4': '@types/svgo@2.6.4':
dependencies: dependencies:
'@types/node': 20.12.12 '@types/node': 20.12.12
'@types/trusted-types@2.0.7':
optional: true
'@types/unist@2.0.10': {} '@types/unist@2.0.10': {}
'@types/web-bluetooth@0.0.20': {} '@types/web-bluetooth@0.0.20': {}
@ -6427,8 +6347,6 @@ snapshots:
balanced-match@1.0.2: {} balanced-match@1.0.2: {}
base64-arraybuffer@1.0.2: {}
base@0.11.2: base@0.11.2:
dependencies: dependencies:
cache-base: 1.0.1 cache-base: 1.0.1
@ -6497,8 +6415,6 @@ snapshots:
node-releases: 2.0.14 node-releases: 2.0.14
update-browserslist-db: 1.0.16(browserslist@4.23.0) update-browserslist-db: 1.0.16(browserslist@4.23.0)
btoa@1.2.1: {}
buffer-from@1.1.2: {} buffer-from@1.1.2: {}
builtin-modules@3.3.0: {} builtin-modules@3.3.0: {}
@ -6542,18 +6458,6 @@ snapshots:
caniuse-lite@1.0.30001620: {} caniuse-lite@1.0.30001620: {}
canvg@3.0.11:
dependencies:
'@babel/runtime': 7.28.2
'@types/raf': 3.4.3
core-js: 3.40.0
raf: 3.4.1
regenerator-runtime: 0.13.11
rgbcolor: 1.0.1
stackblur-canvas: 2.7.0
svg-pathdata: 6.0.3
optional: true
capital-case@1.0.4: capital-case@1.0.4:
dependencies: dependencies:
no-case: 3.0.4 no-case: 3.0.4
@ -6770,10 +6674,6 @@ snapshots:
crypto-js@4.2.0: {} crypto-js@4.2.0: {}
css-line-break@2.1.0:
dependencies:
utrie: 1.0.2
css-select@4.3.0: css-select@4.3.0:
dependencies: dependencies:
boolbase: 1.0.0 boolbase: 1.0.0
@ -6943,11 +6843,6 @@ snapshots:
dependencies: dependencies:
domelementtype: 2.3.0 domelementtype: 2.3.0
dompurify@3.2.6:
optionalDependencies:
'@types/trusted-types': 2.0.7
optional: true
domutils@1.7.0: domutils@1.7.0:
dependencies: dependencies:
dom-serializer: 0.2.2 dom-serializer: 0.2.2
@ -7635,8 +7530,6 @@ snapshots:
dependencies: dependencies:
reusify: 1.0.4 reusify: 1.0.4
fflate@0.8.2: {}
file-entry-cache@8.0.0: file-entry-cache@8.0.0:
dependencies: dependencies:
flat-cache: 4.0.1 flat-cache: 4.0.1
@ -7884,11 +7777,6 @@ snapshots:
html-tags@3.3.1: {} html-tags@3.3.1: {}
html2canvas@1.4.1:
dependencies:
css-line-break: 2.1.0
text-segmentation: 1.0.3
htmlparser2@3.10.1: htmlparser2@3.10.1:
dependencies: dependencies:
domelementtype: 1.3.1 domelementtype: 1.3.1
@ -8151,18 +8039,6 @@ snapshots:
optionalDependencies: optionalDependencies:
graceful-fs: 4.2.11 graceful-fs: 4.2.11
jspdf@3.0.1:
dependencies:
'@babel/runtime': 7.28.2
atob: 2.1.2
btoa: 1.2.1
fflate: 0.8.2
optionalDependencies:
canvg: 3.0.11
core-js: 3.40.0
dompurify: 3.2.6
html2canvas: 1.4.1
keyv@4.5.4: keyv@4.5.4:
dependencies: dependencies:
json-buffer: 3.0.1 json-buffer: 3.0.1
@ -8672,9 +8548,6 @@ snapshots:
perfect-debounce@1.0.0: {} perfect-debounce@1.0.0: {}
performance-now@2.1.0:
optional: true
picocolors@1.0.1: {} picocolors@1.0.1: {}
picocolors@1.1.1: {} picocolors@1.1.1: {}
@ -8891,11 +8764,6 @@ snapshots:
queue-microtask@1.2.3: {} queue-microtask@1.2.3: {}
raf@3.4.1:
dependencies:
performance-now: 2.1.0
optional: true
randombytes@2.1.0: randombytes@2.1.0:
dependencies: dependencies:
safe-buffer: 5.2.1 safe-buffer: 5.2.1
@ -8927,9 +8795,6 @@ snapshots:
dependencies: dependencies:
'@eslint-community/regexpp': 4.10.0 '@eslint-community/regexpp': 4.10.0
regenerator-runtime@0.13.11:
optional: true
regenerator-runtime@0.14.1: {} regenerator-runtime@0.14.1: {}
regex-not@1.0.2: regex-not@1.0.2:
@ -8990,9 +8855,6 @@ snapshots:
rfdc@1.4.1: {} rfdc@1.4.1: {}
rgbcolor@1.0.1:
optional: true
rollup@4.17.2: rollup@4.17.2:
dependencies: dependencies:
'@types/estree': 1.0.5 '@types/estree': 1.0.5
@ -9247,9 +9109,6 @@ snapshots:
stable@0.1.8: {} stable@0.1.8: {}
stackblur-canvas@2.7.0:
optional: true
static-extend@0.1.2: static-extend@0.1.2:
dependencies: dependencies:
define-property: 0.2.5 define-property: 0.2.5
@ -9366,9 +9225,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
svg-pathdata@6.0.3:
optional: true
svg-tags@1.0.0: {} svg-tags@1.0.0: {}
svgo@2.8.0: svgo@2.8.0:
@ -9403,10 +9259,6 @@ snapshots:
commander: 2.20.3 commander: 2.20.3
source-map-support: 0.5.21 source-map-support: 0.5.21
text-segmentation@1.0.3:
dependencies:
utrie: 1.0.2
text-table@0.2.0: {} text-table@0.2.0: {}
tippy.js@6.3.7: tippy.js@6.3.7:
@ -9654,10 +9506,6 @@ snapshots:
utils-merge@1.0.1: {} utils-merge@1.0.1: {}
utrie@1.0.2:
dependencies:
base64-arraybuffer: 1.0.2
v-viewer@3.0.13(viewerjs@1.11.6)(vue@3.5.12(typescript@5.0.4)): v-viewer@3.0.13(viewerjs@1.11.6)(vue@3.5.12(typescript@5.0.4)):
dependencies: dependencies:
lodash-es: 4.17.21 lodash-es: 4.17.21

View File

@ -1,62 +0,0 @@
import http from '@/utils/http'
const { request } = http
// 文件信息
export interface KnowledgeFile {
id: string
name: string
size: string
type: string
uploadTime: string
}
// 文件夹信息
export interface KnowledgeFolder {
id: string
name: string
children?: KnowledgeFolder[]
}
// 获取文件夹树
export function getFolderTreeApi() {
return request<KnowledgeFolder[]>({
url: '/knowledge/folders',
method: 'get',
})
}
// 获取文件列表(按文件夹)
export function getFilesApi(folderId: string) {
return request<KnowledgeFile[]>({
url: '/knowledge/files',
method: 'get',
params: { folderId },
})
}
// 创建文件夹
export function createFolderApi(data: { name: string; parentId?: string }) {
return request({
url: '/knowledge/create-folder',
method: 'post',
data,
})
}
// 删除文件
export function deleteFileApi(fileId: string) {
return request({
url: `/knowledge/delete-file/${fileId}`,
method: 'delete',
})
}
// 下载文件
export function downloadFileApi(fileId: string) {
return request<Blob>({
url: `/knowledge/download/${fileId}`,
method: 'get',
responseType: 'blob',
})
}

View File

@ -1,49 +0,0 @@
import http from '@/utils/http'
import type * as T from '@/types/equipment.d'
const BASE_URL = '/equipment'
/** @desc 分页查询设备列表 */
export function pageEquipment(query: T.EquipmentPageQuery) {
return http.get<T.EquipmentResp[]>(`${BASE_URL}/page`, query)
}
/** @desc 查询设备列表 */
export function listEquipment(query?: T.EquipmentPageQuery) {
return http.get<T.EquipmentResp[]>(`${BASE_URL}/list`, query)
}
/** @desc 查询设备详情 */
export function getEquipmentDetail(equipmentId: string) {
return http.get<T.EquipmentResp>(`${BASE_URL}/detail/${equipmentId}`)
}
/** @desc 新增设备 */
export function createEquipment(data: T.EquipmentReq) {
return http.post(`${BASE_URL}`, data)
}
/** @desc 更新设备 */
export function updateEquipment(equipmentId: string, data: T.EquipmentReq) {
return http.put(`${BASE_URL}/${equipmentId}`, data)
}
/** @desc 删除设备 */
export function deleteEquipment(equipmentId: string) {
return http.del(`${BASE_URL}/${equipmentId}`)
}
/** @desc 设备状态变更 */
export function changeEquipmentStatus(equipmentId: string, status: string) {
return http.put(`${BASE_URL}/${equipmentId}/status`, { status })
}
/** @desc 设备分配 */
export function assignEquipment(equipmentId: string, userId: string) {
return http.put(`${BASE_URL}/${equipmentId}/assign`, { userId })
}
/** @desc 设备归还 */
export function returnEquipment(equipmentId: string) {
return http.put(`${BASE_URL}/${equipmentId}/return`)
}

View File

@ -1,277 +0,0 @@
/**
*
*/
export interface EquipmentListReq {
/** 最低价格 */
minPrice?: number
/** 最高价格 */
maxPrice?: number
/** 设备名称 */
equipmentName?: string
/** 设备类型 */
equipmentType?: string
/** 设备状态 */
equipmentStatus?: string
/** 设备序列号 */
equipmentSn?: string
/** 资产编号 */
assetCode?: string
/** 品牌 */
brand?: string
/** 位置状态 */
locationStatus?: string
/** 健康状态 */
healthStatus?: string
/** 负责人 */
responsiblePerson?: string
/** 使用状态 */
useStatus?: string
/** 项目ID */
projectId?: string
/** 使用人ID */
userId?: string
/** 设备型号 */
equipmentModel?: string
/** 配置规格/参数 */
specification?: string
/** 设备当前物理位置 */
physicalLocation?: string
/** 供应商名称 */
supplierName?: string
/** 采购订单号 */
purchaseOrder?: string
/** 维护人员 */
maintenancePerson?: string
/** 次户号 */
accountNumber?: string
/** 数量 */
quantity?: number
/** 单价 */
unitPrice?: number
/** 总价 */
totalPrice?: number
/** 盘点依据 */
inventoryBasis?: string
/** 动态记录 */
dynamicRecord?: string
/** 资产备注 */
assetRemark?: string
/** 采购时间开始 */
purchaseTimeStart?: string
/** 采购时间结束 */
purchaseTimeEnd?: string
/** 入库时间开始 */
inStockTimeStart?: string
/** 入库时间结束 */
inStockTimeEnd?: string
/** 启用时间开始 */
activationTimeStart?: string
/** 启用时间结束 */
activationTimeEnd?: string
/** 当前页码 */
pageNum?: number
/** 每页大小 */
pageSize?: number
/** 排序字段 */
orderBy?: string
/** 排序方向 */
orderDirection?: string
/** 页码 */
page?: number
/** 库存条码 */
inventoryBarcode?: string
}
/**
*
*/
export interface PageResult<T> {
code: number
msg: string
rows: T[]
total: number
}
/**
*
*/
export interface EquipmentResp {
/** 设备ID */
equipmentId: string
/** 资产编号 */
assetCode?: string
/** 设备名称 */
equipmentName: string
/** 设备类型 */
equipmentType: string
/** 设备类型描述 */
equipmentTypeLabel?: string
/** 设备型号 */
equipmentModel: string
/** 设备SN */
equipmentSn: string
/** 品牌 */
brand?: string
/** 配置规格/参数 */
specification?: string
/** 设备状态 */
equipmentStatus: string
/** 设备状态描述 */
equipmentStatusLabel?: string
/** 使用状态 */
useStatus: string
/** 位置状态 */
locationStatus?: string
/** 位置状态描述 */
locationStatusLabel?: string
/** 设备当前物理位置 */
physicalLocation?: string
/** 负责人 */
responsiblePerson?: string
/** 健康状态 */
healthStatus?: string
/** 健康状态描述 */
healthStatusLabel?: string
/** 采购时间 */
purchaseTime?: string
/** 入库时间 */
inStockTime?: string
/** 启用时间 */
activationTime?: string
/** 预计报废时间 */
expectedScrapTime?: string
/** 实际报废时间 */
actualScrapTime?: string
/** 状态变更时间 */
statusChangeTime?: string
/** 采购订单 */
purchaseOrder?: string
/** 供应商名称 */
supplierName?: string
/** 采购价格 */
purchasePrice?: number
/** 当前净值 */
currentNetValue?: number
/** 折旧方法 */
depreciationMethod?: string
/** 折旧年限 */
depreciationYears?: number
/** 残值 */
salvageValue?: number
/** 保修截止日期 */
warrantyExpireDate?: string
/** 上次维护日期 */
lastMaintenanceDate?: string
/** 下次维护日期 */
nextMaintenanceDate?: string
/** 维护人员 */
maintenancePerson?: string
/** 库存条码 */
inventoryBarcode?: string
/** 资产备注 */
assetRemark?: string
/** 项目ID */
projectId?: string
/** 项目名称 */
projectName?: string
/** 使用人ID */
userId?: string
/** 使用人 */
name?: string
/** 创建时间 */
createTime?: string
/** 更新时间 */
updateTime?: string
/** 次户号 */
accountNumber?: string
/** 数量 */
quantity?: number
/** 单价 */
unitPrice?: number
/** 总价 */
totalPrice?: number
/** 盘点依据 */
inventoryBasis?: string
/** 动态记录 */
dynamicRecord?: string
}
/**
*
*/
export interface EquipmentReq {
/** 设备名称 */
equipmentName: string
/** 设备型号 */
equipmentModel: string
/** 设备类型 */
equipmentType: string
/** 设备状态 */
equipmentStatus: string
/** 使用状态 */
useStatus: string
/** 设备序列号 */
equipmentSn: string
/** 资产编号 */
assetCode?: string
/** 品牌 */
brand?: string
/** 配置规格/参数 */
specification?: string
/** 位置状态 */
locationStatus?: string
/** 设备当前物理位置 */
physicalLocation?: string
/** 负责人 */
responsiblePerson?: string
/** 健康状态 */
healthStatus?: string
/** 采购时间 */
purchaseTime?: string
/** 入库时间 */
inStockTime?: string
/** 启用时间 */
activationTime?: string
/** 预计报废时间 */
expectedScrapTime?: string
/** 实际报废时间 */
actualScrapTime?: string
/** 采购订单 */
purchaseOrder?: string
/** 供应商名称 */
supplierName?: string
/** 采购价格 */
purchasePrice?: number
/** 当前净值 */
currentNetValue?: number
/** 折旧方法 */
depreciationMethod?: string
/** 折旧年限 */
depreciationYears?: number
/** 残值 */
salvageValue?: number
/** 保修截止日期 */
warrantyExpireDate?: string
/** 上次维护日期 */
lastMaintenanceDate?: string
/** 下次维护日期 */
nextMaintenanceDate?: string
/** 维护人员 */
maintenancePerson?: string
/** 库存条码 */
inventoryBarcode?: string
/** 资产备注 */
assetRemark?: string
/** 次户号 */
accountNumber?: string
/** 数量 */
quantity?: number
/** 单价 */
unitPrice?: number
/** 总价 */
totalPrice?: number
/** 盘点依据 */
inventoryBasis?: string
/** 动态记录 */
dynamicRecord?: string
}

View File

@ -42,11 +42,5 @@ export function updatePost(postId: string, data: T.PostUpdateReq) {
* *
*/ */
export function deletePost(postId: string) { export function deletePost(postId: string) {
} return http.del<any>(`${BASE_URL}/${postId}`);
}
/**
*
*/
export function getPostUsers(postId: string) {
return http.get<T.UserNewResp[]>(`${BASE_URL}/${postId}/user`);
}

View File

@ -26,52 +26,53 @@ export const systemRoutes: RouteRecordRaw[] = [
// } // }
// ], // ],
// }, // },
// { {
// path: '/regulation', path: '/regulation',
// name: 'Regulation', name: 'Regulation',
// component: Layout, component: Layout,
// redirect: '/regulation/system-regulation', redirect: '/regulation/system-regulation',
// meta: { title: '制度管理', icon: 'file-text', hidden: false, sort: 1.5 }, meta: { title: '制度管理', icon: 'file-text', hidden: false, sort: 1.5 },
// children: [ children: [
// { {
// path: '/regulation/system-regulation', path: '/regulation/system-regulation',
// name: 'SystemRegulation', name: 'SystemRegulation',
// component: () => import('@/views/regulation/repository.vue'), component: () => import('@/views/regulation/repository.vue'),
// meta: { title: '制度公告', icon: 'file-text', hidden: false }, meta: { title: '制度公告', icon: 'file-text', hidden: false },
// }, },
// { {
// path: '/regulation/process-management', path: '/regulation/process-management',
// name: 'ProcessManagement', name: 'ProcessManagement',
// component: () => import('@/views/regulation/confirm.vue'), component: () => import('@/views/regulation/confirm.vue'),
// meta: { title: '制度公示', icon: 'workflow', hidden: false }, meta: { title: '制度公示', icon: 'workflow', hidden: false },
// }, },
// { {
// path: '/regulation/proposal', path: '/regulation/proposal',
// name: 'RegulationProposal', name: 'RegulationProposal',
// component: () => import('@/views/regulation/proposal/index.vue'), component: () => import('@/views/regulation/proposal/index.vue'),
// meta: { title: '制度提案', icon: 'edit', hidden: false }, meta: { title: '制度提案', icon: 'edit', hidden: false },
// }, },
// { {
// path: '/regulation/type', path: '/regulation/type',
// name: 'RegulationType', name: 'RegulationType',
// component: () => import('@/views/regulation/type/index.vue'), component: () => import('@/views/regulation/type/index.vue'),
// meta: { title: '制度类型', icon: 'tag', hidden: false }, meta: { title: '制度类型', icon: 'tag', hidden: false },
// }, },
// ],
// }, ],
},
{ {
path: '/organization', path: '/organization',
name: 'Organization', name: 'Organization',
component: Layout, component: Layout,
redirect: '/organization/dept', redirect: '/organization/dept',
meta: { title: '企业管理', icon: 'user-group', hidden: false, sort: 2 }, meta: { title: '组织架构', icon: 'user-group', hidden: false, sort: 2 },
children: [ children: [
// { {
// path: '/organization/user', path: '/organization/user',
// name: 'OrganizationUser', name: 'OrganizationUser',
// component: () => import('@/views/system/user/index.vue'), component: () => import('@/views/system/user/index.vue'),
// meta: { title: '用户管理', icon: 'user', hidden: false, sort: 2.25 }, meta: { title: '用户管理', icon: 'user', hidden: false, sort: 2.25 },
// }, },
{ {
path: '/organization/dept', path: '/organization/dept',
name: 'OrganizationDept', name: 'OrganizationDept',
@ -85,44 +86,11 @@ export const systemRoutes: RouteRecordRaw[] = [
meta: { title: '岗位管理', icon: 'nav', hidden: false, sort: 2.75 }, meta: { title: '岗位管理', icon: 'nav', hidden: false, sort: 2.75 },
}, },
{ {
path: '/regulation', path: '/organization/hr/workload',
name: 'Regulation', name: 'HRWorkload',
// component: Layout, component: () => import('@/views/hr/workload/index.vue'),
redirect: '/regulation/system-regulation', meta: { title: '任务管理', icon: 'bookmark', hidden: false },
meta: { title: '制度管理', icon: 'file-text', hidden: false, sort: 1.5 },
children: [
{
path: '/regulation/system-regulation',
name: 'SystemRegulation',
component: () => import('@/views/regulation/repository.vue'),
meta: { title: '制度公告', icon: 'file-text', hidden: false },
},
{
path: '/regulation/process-management',
name: 'ProcessManagement',
component: () => import('@/views/regulation/confirm.vue'),
meta: { title: '制度公示', icon: 'workflow', hidden: false },
},
{
path: '/regulation/proposal',
name: 'RegulationProposal',
component: () => import('@/views/regulation/proposal/index.vue'),
meta: { title: '制度提案', icon: 'edit', hidden: false },
},
{
path: '/regulation/type',
name: 'RegulationType',
component: () => import('@/views/regulation/type/index.vue'),
meta: { title: '制度类型', icon: 'tag', hidden: false },
},
],
}, },
// {
// path: '/organization/hr/workload',
// name: 'HRWorkload',
// component: () => import('@/views/hr/workload/index.vue'),
// meta: { title: '任务管理', icon: 'bookmark', hidden: false },
// },
], ],
}, },
// { // {
@ -533,86 +501,99 @@ export const systemRoutes: RouteRecordRaw[] = [
], ],
}, },
{ {
path: '/project-marketing', path: '/project-management',
name: 'Projectmarketing', name: 'ProjectManagement',
component: Layout, component: Layout,
redirect: '/project-marketing/project-marketing', redirect: '/project-management/project-template/project-management',
meta: { title: '营销管理', icon: 'file-text', hidden: false, sort: 3 }, meta: { title: '项目管理', icon: 'apps', hidden: false, sort: 4 },
children: [ children: [
{ {
path: '/project-marketing/contract/revenue-contract2', path: '/project-management/contract/project-source',
name: 'RevenueContract2', name: 'ProjectSource',
component: () => import('@/views/default/error/404.vue'),
meta: {
title: '市场营销(N)',
icon: 'dollar',
hidden: false,
},
},
{
path: '/project-marketing/contract/procurement-business',
name: 'ProcurementBusiness',
component: () => import('@/components/ParentView/index.vue'), component: () => import('@/components/ParentView/index.vue'),
meta: { meta: {
title: '招采业务', title: '项目来源',
icon: 'dollar', icon: 'dollar',
hidden: false, hidden: false,
}, },
children: [ children: [
{ {
path: '/project-marketing/project-template/tender-documents', path: '/project-management/contract/procurement-business',
name: 'TenderDocuments', name: 'ProcurementBusiness',
component: () => import('@/views/project-management/bidding/tender-documents/index.vue'), component: () => import('@/components/ParentView/index.vue'),
meta: { meta: {
title: '招标文件', title: '招采业务',
icon: 'file-text', icon: 'dollar',
hidden: false,
},
children: [
{
path: '/project-management/project-template/tender-documents',
name: 'TenderDocuments',
component: () => import('@/views/project-management/bidding/tender-documents/index.vue'),
meta: {
title: '招标文件',
icon: 'file-text',
hidden: false,
},
},
{
path: '/project-management/project-template/bid-documents',
name: 'BidDocuments',
component: () => import('@/views/project-management/bidding/bid-documents/index.vue'),
meta: {
title: '投标文件',
icon: 'file-text',
hidden: false,
},
},
{
path: '/project-management/project-template/award-notice',
name: 'AwardNotice',
component: () => import('@/views/project-management/bidding/award-notice/index.vue'),
meta: {
title: '中标通知书',
icon: 'trophy',
hidden: false,
},
},
{
path: '/project-management/project-template/information-retrieval',
name: 'InformationRetrieval',
component: () => import ('@/views/project-management/bidding/information-retrieval/index.vue'),
meta: {
title: '信息检索(N)',
icon: 'trophy',
hidden: false,
},
},
],
},
{
path: '/project-management/contract/revenue-contract2',
name: 'RevenueContract2',
component: () => import('@/views/default/error/404.vue'),
meta: {
title: '市场营销(N)',
icon: 'dollar',
hidden: false, hidden: false,
}, },
}, },
{ {
path: '/project-marketing/project-template/bid-documents', path: '/project-management/project-source/privateproject',
name: 'BidDocuments', name: 'PrivateProject',
component: () => import('@/views/project-management/bidding/bid-documents/index.vue'), component: () => import('@/views/default/error/404.vue'),
meta: { meta: {
title: '投标文件', title: '自建项目(N)',
icon: 'file-text', icon: 'dollar',
hidden: false,
},
},
{
path: '/project-marketing/project-template/award-notice',
name: 'AwardNotice',
component: () => import('@/views/project-management/bidding/award-notice/index.vue'),
meta: {
title: '中标通知书',
icon: 'trophy',
hidden: false,
},
},
{
path: '/project-marketing/project-template/information-retrieval',
name: 'InformationRetrieval',
component: () => import ('@/views/project-management/bidding/information-retrieval/index.vue'),
meta: {
title: '信息检索(N)',
icon: 'trophy',
hidden: false, hidden: false,
}, },
}, },
], ],
}, },
{
path: '/project-marketing/order-management/order-management',
name: 'OrderManagement',
component: () => import('@/views/project-management/order-management/index.vue'),
meta: {
title: '订单管理',
icon: 'file-text',
hidden: false,
},
},
{ path: '/project-management/contract', name: 'ProjectContract', component: () => import('@/components/ParentView/index.vue'), redirect: '/project-management/contract/revenue-contract', meta: { { path: '/project-management/contract', name: 'ProjectContract', component: () => import('@/components/ParentView/index.vue'), redirect: '/project-management/contract/revenue-contract', meta: {
title: '合同管理', title: '项目合同',
icon: 'file-text', icon: 'file-text',
hidden: false, hidden: false,
}, children: [ }, children: [
@ -647,136 +628,6 @@ export const systemRoutes: RouteRecordRaw[] = [
}, },
}, },
] }, ] },
],
},
{
path: '/project-management',
name: 'ProjectManagement',
component: Layout,
redirect: '/project-management/project-template/project-management',
meta: { title: '项目管理', icon: 'apps', hidden: false, sort: 4 },
children: [
{
path: '/project-management/contract/project-source',
name: 'ProjectSource',
component: () => import('@/components/ParentView/index.vue'),
meta: {
title: '项目来源',
icon: 'dollar',
hidden: false,
},
children: [
// {
// path: '/project-management/contract/procurement-business',
// name: 'ProcurementBusiness',
// component: () => import('@/components/ParentView/index.vue'),
// meta: {
// title: '招采业务',
// icon: 'dollar',
// hidden: false,
// },
// children: [
// {
// path: '/project-management/project-template/tender-documents',
// name: 'TenderDocuments',
// component: () => import('@/views/project-management/bidding/tender-documents/index.vue'),
// meta: {
// title: '招标文件',
// icon: 'file-text',
// hidden: false,
// },
// },
// {
// path: '/project-management/project-template/bid-documents',
// name: 'BidDocuments',
// component: () => import('@/views/project-management/bidding/bid-documents/index.vue'),
// meta: {
// title: '投标文件',
// icon: 'file-text',
// hidden: false,
// },
// },
// {
// path: '/project-management/project-template/award-notice',
// name: 'AwardNotice',
// component: () => import('@/views/project-management/bidding/award-notice/index.vue'),
// meta: {
// title: '中标通知书',
// icon: 'trophy',
// hidden: false,
// },
// },
// {
// path: '/project-management/project-template/information-retrieval',
// name: 'InformationRetrieval',
// component: () => import ('@/views/project-management/bidding/information-retrieval/index.vue'),
// meta: {
// title: '信息检索(N)',
// icon: 'trophy',
// hidden: false,
// },
// },
//
// ],
// },
// {
// path: '/project-management/contract/revenue-contract2',
// name: 'RevenueContract2',
// component: () => import('@/views/default/error/404.vue'),
// meta: {
// title: '市场营销(N)',
// icon: 'dollar',
// hidden: false,
// },
// },
{
path: '/project-management/project-source/privateproject',
name: 'PrivateProject',
component: () => import('@/views/default/error/404.vue'),
meta: {
title: '自建项目(N)',
icon: 'dollar',
hidden: false,
},
},
],
},
// { path: '/project-management/contract', name: 'ProjectContract', component: () => import('@/components/ParentView/index.vue'), redirect: '/project-management/contract/revenue-contract', meta: {
// title: '项目合同',
// icon: 'file-text',
// hidden: false,
// }, children: [
// {
// path: '/project-management/contract/revenue-contract',
// name: 'RevenueContract',
// component: () => import('@/views/project-management/contract/revenue-contract/index.vue'),
// meta: {
// title: '收入合同',
// icon: 'dollar',
// hidden: false,
// },
// },
// {
// path: '/project-management/contract/expense-contract',
// name: 'ExpenseContract',
// component: () => import('@/views/project-management/contract/expense-contract/index.vue'),
// meta: {
// title: '支出合同',
// icon: 'credit-card',
// hidden: false,
// },
// },
// {
// path: '/project-management/contract/cost-management',
// name: 'CostManagement',
// component: () => import('@/views/project-management/contract/cost-management/index.vue'),
// meta: {
// title: '成本费用',
// icon: 'bar-chart',
// hidden: false,
// },
// },
// ] },
{ {
path: '/project-management/project-template/project-aproval', path: '/project-management/project-template/project-aproval',
name: 'ProjectTemplate', name: 'ProjectTemplate',
@ -852,26 +703,6 @@ export const systemRoutes: RouteRecordRaw[] = [
], ],
}, },
// 添加商务知识库
{
path: '/bussiness-knowledge',
name: 'bussinesskonwledge',
component: Layout,
redirect: '/bussiness-knowledge/data',
meta: { title: '商务资料知识库', icon: 'message', hidden: false, sort: 6 },
children: [
{
path: '/bussiness-konwledge/data',
name: 'bussiness-knowledge',
component: () => import('@/views/bussiness-data/bussiness.vue'),
meta: {
title: '商务数据库信息',
icon: 'info-circle',
hidden: false,
},
},
],
},
{ {
path: 'project-management/project-implementation/', path: 'project-management/project-implementation/',
name: 'Project-Implementation', name: 'Project-Implementation',
@ -1185,30 +1016,30 @@ export const systemRoutes: RouteRecordRaw[] = [
// } // }
], ],
}, },
// { {
// path: '/user/profile', path: '/user/profile',
// name: 'UserProfile', name: 'UserProfile',
// component: Layout, component: Layout,
// redirect: '/user/profile', redirect: '/user/profile',
// meta: { meta: {
// title: '个人中心', title: '个人中心',
// icon: 'user', icon: 'user',
// hidden: false, hidden: false,
// sort: 100, sort: 100,
// }, },
// children: [ children: [
// { {
// path: '/user/profile', path: '/user/profile',
// name: 'UsersProfile', name: 'UserProfile',
// component: () => import('@/views/user/profile/index.vue'), component: () => import('@/views/user/profile/index.vue'),
// meta: { meta: {
// title: '个人中心', title: '个人中心',
// icon: 'user', icon: 'user',
// hidden: false, hidden: false,
// }, },
// }, },
// ], ],
// }, },
{ {
path: '/enterprise-settings', path: '/enterprise-settings',
name: 'EnterpriseSettings', name: 'EnterpriseSettings',

View File

@ -149,20 +149,6 @@ const storeSetup = () => {
isHidden: false, isHidden: false,
sort: 3, sort: 3,
}, },
{
id: 1070,
parentId: 1000,
title: '个人中心',
type: 2,
path: '/user/profile',
name: 'UserProfile',
component: 'user/profile/index',
icon: 'user',
isExternal: false,
isCache: false,
isHidden: false,
sort: 4,
},
], ],
}] }]
// 使用已转换的数据生成路由 // 使用已转换的数据生成路由

View File

@ -1,179 +0,0 @@
export interface EquipmentPageQuery {
equipmentName?: string
equipmentType?: string
equipmentStatus?: string
equipmentSn?: string
assetCode?: string
brand?: string
locationStatus?: string
healthStatus?: string
responsiblePerson?: string
useStatus?: string
projectId?: string
userId?: string
equipmentModel?: string
specification?: string
physicalLocation?: string
supplierName?: string
maintenancePerson?: string
inventoryBarcode?: string
assetRemark?: string
// 新增搜索字段
usingDepartment?: string
invoice?: string
barcode?: string
importer?: string
page?: number
pageSize?: number
orderBy?: string
orderDirection?: string
}
export interface EquipmentReq {
equipmentName: string
equipmentModel: string
equipmentType: string
equipmentStatus: string
useStatus: string
equipmentSn: string
assetCode?: string
brand?: string
specification?: string
locationStatus?: string
physicalLocation?: string
responsiblePerson?: string
healthStatus?: string
purchaseTime?: string
inStockTime?: string
activationTime?: string
expectedScrapTime?: string
actualScrapTime?: string
statusChangeTime?: string
purchaseOrder?: string
supplierName?: string
purchasePrice?: number
currentNetValue?: number
depreciationMethod?: string
depreciationYears?: number
salvageValue?: number
warrantyExpireDate?: string
lastMaintenanceDate?: string
nextMaintenanceDate?: string
maintenancePerson?: string
inventoryBarcode?: string
assetRemark?: string
// 新增字段
usingDepartment?: string
borrowingTime?: string
returnTime?: string
outStockTime?: string
totalUsageTime?: string
depreciationRate?: number
depreciationMethodDesc?: string
invoice?: string
invoiceStatus?: string
attachments?: string
photos?: string
barcode?: string
importer?: string
inventoryTimeStatus1?: string
inventoryTimeStatus2?: string
inventoryTimeStatus3?: string
inventoryCheckTimeStatus1?: string
inventoryCheckTimeStatus2?: string
inventoryCheckTimeStatus3?: string
}
export interface EquipmentResp {
equipmentId: string
assetCode?: string
equipmentName: string
equipmentType: string
equipmentTypeLabel?: string
equipmentModel: string
equipmentSn: string
brand?: string
specification?: string
equipmentStatus: string
equipmentStatusLabel?: string
useStatus: string
locationStatus?: string
locationStatusLabel?: string
physicalLocation?: string
responsiblePerson?: string
healthStatus?: string
healthStatusLabel?: string
purchaseTime?: string
inStockTime?: string
activationTime?: string
expectedScrapTime?: string
actualScrapTime?: string
statusChangeTime?: string
purchaseOrder?: string
supplierName?: string
purchasePrice?: number
currentNetValue?: number
depreciationMethod?: string
depreciationYears?: number
salvageValue?: number
warrantyExpireDate?: string
lastMaintenanceDate?: string
nextMaintenanceDate?: string
maintenancePerson?: string
inventoryBarcode?: string
assetRemark?: string
// 新增字段
usingDepartment?: string
borrowingTime?: string
returnTime?: string
outStockTime?: string
totalUsageTime?: string
depreciationRate?: number
depreciationMethodDesc?: string
invoice?: string
invoiceStatus?: string
attachments?: string
photos?: string
barcode?: string
importer?: string
inventoryTimeStatus1?: string
inventoryTimeStatus2?: string
inventoryTimeStatus3?: string
inventoryCheckTimeStatus1?: string
inventoryCheckTimeStatus2?: string
inventoryCheckTimeStatus3?: string
projectId?: string
projectName?: string
userId?: string
name?: string
createTime?: string
updateTime?: string
}
export interface EquipmentTypeOption {
label: string
value: string
}
export interface EquipmentStatusOption {
label: string
value: string
color: string
}
export interface LocationStatusOption {
label: string
value: string
color: string
}
export interface HealthStatusOption {
label: string
value: string
color: string
}
export interface DepreciationMethodOption {
label: string
value: string
}

File diff suppressed because it is too large Load Diff

View File

@ -73,7 +73,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import type { BiddingDetail } from '../types' import type { BiddingDetail } from './types'
const props = defineProps<{ const props = defineProps<{
visible: boolean visible: boolean

View File

@ -1,344 +0,0 @@
<template>
<div class="project-table-container">
<a-table
:data="data"
:columns="columns"
:loading="loading"
:pagination="false"
:scroll="{ x: '100%', y: '100%' }"
row-key="id"
:stripe="false"
size="large"
>
<!-- 招标项目 -->
<template #projectName="{ record }">
<div class="project-name">
<span class="name-text">{{ record.projectName }}</span>
</div>
</template>
<!-- 招标单位 -->
<template #biddingUnit="{ record }">
<span class="bidding-unit">{{ record.biddingUnit }}</span>
</template>
<!-- 预算金额 -->
<template #budgetAmount="{ record }">
<span class="budget-text">{{ record.budgetAmount }}</span>
</template>
<!-- 截止时间 -->
<template #deadline="{ record }">
<span class="date-text">{{ record.deadline }}</span>
</template>
<!-- 爬取时间 -->
<template #crawlingTime="{ record }">
<span class="date-text">{{ record.crawlingTime }}</span>
</template>
<!-- 来源平台 -->
<template #sourcePlatform="{ record }">
<span class="source-text">{{ record.sourcePlatform }}</span>
</template>
<!-- 招标文件 -->
<template #biddingDocuments="{ record }">
<span class="doc-text">{{ record.biddingDocuments }}</span>
</template>
<!-- 状态 -->
<template #status="{ record }">
<a-tag :color="getStatusColor(record.status)">
{{ getStatusText(record.status) }}
</a-tag>
</template>
<!-- 操作 -->
<template #action="{ record }">
<div class="action-buttons">
<a-button size="small" @click="handleView(record)">
详情
</a-button>
<a-button
type="primary"
size="small"
@click="handleEnter(record)"
>
报名
</a-button>
</div>
</template>
</a-table>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import type { TableColumnData } from '@arco-design/web-vue'
import type { ProjectData } from '../types'
interface Props {
data: ProjectData[]
loading?: boolean
}
interface Emits {
(e: 'view', project: ProjectData): void
(e: 'enter', project: ProjectData): void
}
const props = withDefaults(defineProps<Props>(), {
loading: false
})
const emit = defineEmits<Emits>()
//
const columns: TableColumnData[] = [
{
title: '招标项目',
dataIndex: 'projectName',
slotName: 'projectName',
width: 300,
ellipsis: true,
tooltip: true
},
{
title: '招标单位',
dataIndex: ' biddingUnit',
slotName: 'biddingUnit',
width: 120,
align: 'center'
},
{
title: '预算金额',
dataIndex: 'budgetAmount',
slotName: 'budgetAmount',
width: 120,
align: 'center'
},
{
title: '截止时间',
dataIndex: 'deadline',
slotName: 'deadline',
width: 150,
align: 'center'
},
{
title: '爬取时间',
dataIndex: 'crawlingTime',
slotName: 'crawlingTime',
width: 150,
align: 'center'
},
{
title: '来源平台',
dataIndex: 'sourcePlatform',
slotName: 'sourcePlatform',
width: 150,
align: 'center'
},
{
title: '招标文件',
dataIndex: ' biddingDocuments',
slotName: 'biddingDocuments',
width: 150,
align: 'center'
},
{
title: '状态',
dataIndex: 'status',
slotName: 'status',
width: 120,
align: 'center'
},
{
title: '操作',
dataIndex: 'action',
slotName: 'action',
width: 150,
align: 'center',
fixed: 'right'
}
]
//
const getStatusColor = (status: string) => {
const colorMap: Record<string, string> = {
'not_started': 'gray',
'in_progress': 'blue',
'completed': 'green',
'paused': 'orange',
'cancelled': 'red'
}
return colorMap[status] || 'gray'
}
//
const getStatusText = (status: string) => {
const textMap: Record<string, string> = {
'not_started': '未开始',
'in_progress': '进行中',
'completed': '已完成',
'paused': '已暂停',
'cancelled': '已取消'
}
return textMap[status] || status
}
//
const getProgressColor = (progress: number) => {
if (progress === 0) return '#d9d9d9'
if (progress === 100) return '#52c41a'
if (progress >= 70) return '#1890ff'
if (progress >= 40) return '#faad14'
return '#ff4d4f'
}
//
const handleView = (record: ProjectData) => {
emit('view', record)
}
const handleEnter = (record: ProjectData) => {
emit('enter', record)
}
</script>
<style scoped lang="less">
.project-table-container {
height: 100%;
background: #fff;
border-radius: 8px;
}
.project-name {
.name-text {
font-weight: 500;
color: #333;
}
}
.unit-count {
font-weight: 600;
color: #1890ff;
}
.date-text {
color: #666;
font-size: 13px;
}
.progress-container {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
.progress-text {
font-size: 12px;
color: #666;
font-weight: 500;
min-width: 35px;
text-align: right;
}
}
.action-buttons {
display: flex;
gap: 8px;
justify-content: center;
}
:deep(.arco-table) {
.arco-table-thead {
.arco-table-th {
background-color: #fafafa;
font-weight: 600;
color: #333;
border-bottom: 1px solid #e8e8e8;
}
}
.arco-table-tbody {
.arco-table-tr {
&:hover {
background-color: #f5f5f5;
}
}
.arco-table-td {
border-bottom: 1px solid #f0f0f0;
padding: 16px 12px;
}
}
}
:deep(.arco-tag) {
border-radius: 4px;
font-size: 12px;
padding: 2px 8px;
}
:deep(.arco-progress-line-outer) {
border-radius: 4px;
}
:deep(.arco-progress-line-inner) {
border-radius: 4px;
transition: all 0.3s ease;
}
//
:deep(.arco-tag-gray) {
background-color: #f5f5f5;
border-color: #d9d9d9;
color: #666;
}
:deep(.arco-tag-blue) {
background-color: #e6f7ff;
border-color: #91d5ff;
color: #1890ff;
}
:deep(.arco-tag-green) {
background-color: #f6ffed;
border-color: #b7eb8f;
color: #52c41a;
}
:deep(.arco-tag-orange) {
background-color: #fff7e6;
border-color: #ffd591;
color: #fa8c16;
}
:deep(.arco-tag-red) {
background-color: #fff1f0;
border-color: #ffccc7;
color: #ff4d4f;
}
//
@media (max-width: 768px) {
:deep(.arco-table-td) {
padding: 12px 8px;
font-size: 14px;
}
.action-buttons {
flex-direction: column;
gap: 4px;
}
.progress-container {
gap: 8px;
.progress-text {
font-size: 11px;
}
}
}
</style>

View File

@ -1,415 +0,0 @@
<template>
<GiPageLayout>
<div class="project-list-container">
<!-- 顶部搜索和操作区域 -->
<div class="header-section">
<div class="search-area">
<a-input-search
v-model="searchKeyword"
placeholder="搜索项目名称"
allow-clear
@search="handleSearch"
@clear="handleClear"
style="width: 300px"
/>
</div>
<div class="action-area">
<a-space>
<a-button type="primary" @click="startCrawler">
<template #icon><icon-play-arrow /></template>
开始爬虫
</a-button>
<a-button @click="refreshData">
<template #icon><icon-refresh /></template>
刷新数据
</a-button>
<a-button @click="exportData">
<template #icon><icon-download /></template>
导出数据
</a-button>
<a-button @click="openCrawlerSettings">
<template #icon><icon-settings /></template>
爬虫设置
</a-button>
</a-space>
</div>
</div>
<!-- 选项卡区域 -->
<div class="tabs-section">
<a-tabs
v-model:active-key="activeTab"
type="line"
size="large"
@change="handleTabChange"
>
<a-tab-pane key="all" tab="信息检索" title="信息检索">
<ProjectTable
:data="pagedData"
:loading="loading"
@view="handleView"
@enter="handleEnter"
/>
<div class="pagination-wrapper">
<a-pagination
v-model:current="currentPage"
v-model:page-size="pageSize"
:total="totalItems"
show-total
show-jumper
show-page-size
:page-size-options="[10, 20, 50, 100]"
@change="handlePageChange"
/>
</div>
</a-tab-pane>
<a-tab-pane key="my" tab="投标响应" title="投标响应">
<ProjectTable
:data="myPagedData"
:loading="loading"
@view="handleView"
@enter="handleEnter"
/>
<div class="pagination-wrapper">
<a-pagination
v-model:current="myCurrentPage"
v-model:page-size="pageSize"
:total="myTotalItems"
show-total
show-jumper
show-page-size
:page-size-options="[10, 20, 50, 100]"
@change="handleMyPageChange"
/>
</div>
</a-tab-pane>
</a-tabs>
</div>
<CrawlerSettings
v-model:visible="crawlerSettingsVisible"
@save="handleSaveSettings"
/>
</div>
</GiPageLayout>
<BiddingDetailModal
v-model:visible="detailVisible"
:detail="currentDetail"
:uploading="uploading"
@upload="handleUploadDocument"
/>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { Message } from '@arco-design/web-vue'
import ProjectTable from './components/InformationTable.vue'
import CrawlerSettings from './components/CrawlerSettings.vue'
import BiddingDetailModal from './components/BiddingDetailModal.vue'
import type { ProjectData, TabKey,BiddingDetail } from './types'
import { ProjectStatus } from './types'
defineOptions({ name: 'ProjectList' })
//
const loading = ref(false)
const activeTab = ref<TabKey>('all')
const searchKeyword = ref('')
const crawlerSettingsVisible = ref(false)
//
const currentPage = ref(1)
const myCurrentPage = ref(1)
const pageSize = ref(10)
//
const detailVisible = ref(false)
const uploading = ref(false)
const currentDetail = ref<BiddingDetail>({
id: 0,
projectName: '',
biddingUnit: '',
budgetAmount: 0,
deadline: '',
crawlingTime: '',
projectLocation: '',
projectDuration: '',
biddingScope: '',
qualificationRequirements: '',
sourcePlatform: '',
biddingDocuments: '',
biddingContent: '',
contentItems: []
})
//
const projectList = ref<ProjectData[]>([
{
id: 1,
projectName: 'A风场2023年检查',
biddingUnit: 15,
budgetAmount:111,
deadline: '2023-10-01',
crawlingTime: '2023-12-31',
sourcePlatform:"中国招标投标网",
biddingDocuments:"333.pdf",
status: ProjectStatus.IN_PROGRESS,
manager: '张三',
isMyProject: true
},
//
...Array.from({ length: 25 }, (_, i) => ({
id: i + 2,
projectName: `项目${i + 2}`,
biddingUnit: Math.floor(Math.random() * 20) + 1,
budgetAmount: Math.floor(Math.random() * 1000) + 100,
deadline: `2023-${(Math.floor(Math.random() * 12) + 1).toString().padStart(2, '0')}-${(Math.floor(Math.random() * 28) + 1).toString().padStart(2, '0')}`,
crawlingTime: `2023-${(Math.floor(Math.random() * 12) + 1).toString().padStart(2, '0')}-${(Math.floor(Math.random() * 28) + 1).toString().padStart(2, '0')}`,
sourcePlatform: "中国招标投标网",
biddingDocuments: `文档${i + 2}.pdf`,
status: Math.random() > 0.5 ? ProjectStatus.IN_PROGRESS : ProjectStatus.COMPLETED,
manager: ['张三', '李四', '王五'][Math.floor(Math.random() * 3)],
isMyProject: Math.random() > 0.7
}))
])
const handleSaveSettings = (settings: any) => {
console.log('Saved settings:', settings)
}
//
const handleView = (project: ProjectData) => {
// ID
currentDetail.value = {
id: project.id,
projectName: project.projectName,
biddingUnit: project.biddingUnit.toString(),
budgetAmount: project.budgetAmount,
deadline: project.deadline,
crawlingTime: project.crawlingTime,
projectLocation: '内蒙古自治区', //
projectDuration: '6个月', //
biddingScope: '风电场50台机组叶片检查服务', //
qualificationRequirements: '具备风电叶片检查资质', //
sourcePlatform: project.sourcePlatform,
biddingDocuments: project.biddingDocuments,
biddingContent: '本项目为某风电场2023年叶片检查服务采购包括但不限于',
contentItems: [
'叶片外观检查',
'无损检测',
'缺陷记录与评估',
'检查报告编制'
]
}
detailVisible.value = true
}
// -
const filteredProjects = computed(() => {
if (!searchKeyword.value) {
return projectList.value
}
return projectList.value.filter(project =>
project.projectName.toLowerCase().includes(searchKeyword.value.toLowerCase())
)
})
// -
const myProjects = computed(() => {
return filteredProjects.value.filter(project => project.isMyProject)
})
//
const totalItems = computed(() => filteredProjects.value.length)
const myTotalItems = computed(() => myProjects.value.length)
const pagedData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return filteredProjects.value.slice(start, end)
})
const myPagedData = computed(() => {
const start = (myCurrentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return myProjects.value.slice(start, end)
})
//
const handleSearch = (value: string) => {
searchKeyword.value = value
currentPage.value = 1 //
myCurrentPage.value = 1
}
const handleClear = () => {
searchKeyword.value = ''
currentPage.value = 1 //
myCurrentPage.value = 1
}
//
const handleTabChange = (key: string) => {
activeTab.value = key as TabKey
}
//
const handlePageChange = (page: number) => {
currentPage.value = page
}
const handleMyPageChange = (page: number) => {
myCurrentPage.value = page
}
//
const refreshData = async () => {
loading.value = true
try {
// API
await new Promise(resolve => setTimeout(resolve, 800))
Message.success('数据已刷新')
} catch (error) {
Message.error('刷新失败')
} finally {
loading.value = false
}
}
//
const startCrawler = () => {
Message.info('开始爬虫任务')
//
}
//
const exportData = () => {
Message.info('导出数据')
//
}
//
const openCrawlerSettings = () => {
crawlerSettingsVisible.value = true
}
const handleUploadDocument = async (file: File) => {
uploading.value = true
try {
const formData = new FormData()
formData.append('file', file)
formData.append('projectId', currentDetail.value.id.toString())
// API
const response = await uploadBiddingDocument(formData)
Message.success('文件上传成功')
currentDetail.value.biddingDocuments = response.data.fileUrl
} catch (error) {
Message.error(`文件上传失败: ${error.message}`)
} finally {
uploading.value = false
}
}
// API
const uploadBiddingDocument = (formData: FormData) => {
return new Promise((resolve) => {
setTimeout(() => {
const file = formData.get('file') as File
const projectId = formData.get('projectId')
const extension = file.name.split('.').pop()
resolve({
data: {
fileUrl: `https://your-api-domain.com/uploads/${projectId}.${extension}`
}
})
}, 1500)
})
}
onMounted(() => {
//
})
</script>
<style scoped lang="less">
.project-list-container {
height: 100%;
display: flex;
flex-direction: column;
}
.header-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding: 0 4px;
}
.search-area {
flex: 1;
}
.action-area {
display: flex;
gap: 12px;
align-items: center;
}
.tabs-section {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.pagination-wrapper {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
:deep(.arco-tabs-content) {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
:deep(.arco-tabs-pane) {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
:deep(.arco-tabs-nav-tab) {
font-weight: 500;
font-size: 16px;
}
:deep(.arco-tabs-nav-tab-list) {
padding: 0 4px;
}
//
@media (max-width: 768px) {
.header-section {
flex-direction: column;
gap: 16px;
align-items: stretch;
}
.search-area {
flex: none;
}
.action-area {
justify-content: center;
}
}
</style>

View File

@ -1,64 +0,0 @@
// 项目状态枚举
export enum ProjectStatus {
NOT_STARTED = 'not_started',
IN_PROGRESS = 'in_progress',
COMPLETED = 'completed',
PAUSED = 'paused',
CANCELLED = 'cancelled'
}
// 项目数据接口
export interface ProjectData {
id: number
projectName: string
unitCount: number
startDate: string
endDate: string
status: ProjectStatus
progress: number
manager: string
isMyProject: boolean
description?: string
location?: string
client?: string
budget?: number
team?: string[]
}
// 选项卡类型
export type TabKey = 'all' | 'my' | 'pending'
// 项目筛选条件
export interface ProjectFilter {
keyword?: string
status?: ProjectStatus
manager?: string
dateRange?: [string, string]
}
// 项目统计信息
export interface ProjectStats {
total: number
inProgress: number
completed: number
notStarted: number
myProjects: number
}
// 招标详情
export interface BiddingDetail {
id: number
projectName: string
biddingUnit: string
budgetAmount: number
deadline: string
crawlingTime: string
projectLocation: string
projectDuration: string
biddingScope: string
qualificationRequirements: string
sourcePlatform: string
biddingDocuments: string
biddingContent: string
contentItems?: string[]
}

View File

@ -1,28 +1,28 @@
<template> <template>
<GiPageLayout> <GiPageLayout>
<GiTable <GiTable
row-key="contractId" row-key="id"
title="收入合同管理" title="收入合同管理"
:data="dataList" :data="dataList"
:columns="tableColumns" :columns="tableColumns"
:loading="loading" :loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1600 }" :scroll="{ x: '100%', y: '100%', minWidth: 1600 }"
:pagination="pagination" :pagination="pagination"
@page-change="onPageChange" @page-change="onPageChange"
@page-size-change="onPageSizeChange" @page-size-change="onPageSizeChange"
@refresh="search" @refresh="search"
> >
<template #top> <template #top>
<GiForm <GiForm
v-model="searchForm" v-model="searchForm"
search search
:columns="queryFormColumns" :columns="queryFormColumns"
size="medium" size="medium"
@search="search" @search="search"
@reset="reset" @reset="reset"
/> />
</template> </template>
<template #toolbar-left> <template #toolbar-left>
<a-space> <a-space>
<a-button type="primary" @click="openAddModal"> <a-button type="primary" @click="openAddModal">
@ -35,35 +35,30 @@
</a-button> </a-button>
</a-space> </a-space>
</template> </template>
<!-- 合同状态 --> <!-- 合同状态 -->
<template #contractStatus="{ record }"> <template #status="{ record }">
<a-tag :color="getStatusColor(record.contractStatus)"> <a-tag :color="getStatusColor(record.status)">
{{ getStatusText(record.contractStatus) }} {{ getStatusText(record.status) }}
</a-tag> </a-tag>
</template> </template>
<!-- 合同金额 --> <!-- 合同金额 -->
<template #amount="{ record }"> <template #contractAmount="{ record }">
<span class="font-medium text-green-600">{{ record.amount.toLocaleString() }}</span> <span class="font-medium text-green-600">{{ record.contractAmount.toLocaleString() }}</span>
</template> </template>
<!-- 已收款金额 --> <!-- 已收款金额 -->
<template #receivedAmount="{ record }"> <template #receivedAmount="{ record }">
<span class="font-medium text-blue-600">{{ record.receivedAmount.toLocaleString() }}</span> <span class="font-medium text-blue-600">{{ record.receivedAmount.toLocaleString() }}</span>
</template> </template>
<!-- 已结算金额 -->
<template #settlementAmount="{ record }">
<span class="font-medium text-orange-600">{{ record.settlementAmount.toLocaleString() }}</span>
</template>
<!-- 操作列 --> <!-- 操作列 -->
<template #action="{ record }"> <template #action="{ record }">
<a-space> <a-space>
<a-link @click="viewDetail(record)">详情</a-link> <a-link @click="viewDetail(record)">详情</a-link>
<a-link @click="editRecord(record)" v-if="record.contractStatus === 'draft'">编辑</a-link> <a-link @click="editRecord(record)" v-if="record.status === 'draft'">编辑</a-link>
<a-link @click="approveContract(record)" v-if="record.contractStatus === 'pending'">审批</a-link> <a-link @click="approveContract(record)" v-if="record.status === 'pending'">审批</a-link>
<a-link @click="viewPayment(record)">收款记录</a-link> <a-link @click="viewPayment(record)">收款记录</a-link>
</a-space> </a-space>
</template> </template>
@ -75,38 +70,38 @@
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
import { getContractList, type ContractQueryParams, type ContractData } from '@/apis/contract'
// //
let searchForm = reactive({ let searchForm = reactive({
code: '', contractName: '',
customer: '', contractCode: '',
contractStatus: '', client: '',
status: '',
signDate: '', signDate: '',
page: 1, page: 1,
pageSize: 10 size: 10
}) })
// //
const queryFormColumns = [ const queryFormColumns = [
{ {
field: 'code', field: 'contractName',
label: '合同编号', label: '合同名称',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入合同编号' placeholder: '请输入合同名称'
} }
}, },
{ {
field: 'customer', field: 'client',
label: '客户名称', label: '客户',
type: 'input' as const, type: 'input' as const,
props: { props: {
placeholder: '请输入客户名称' placeholder: '请输入客户名称'
} }
}, },
{ {
field: 'contractStatus', field: 'status',
label: '合同状态', label: '合同状态',
type: 'select' as const, type: 'select' as const,
props: { props: {
@ -125,33 +120,83 @@ const queryFormColumns = [
// //
const tableColumns: TableColumnData[] = [ const tableColumns: TableColumnData[] = [
{ title: '合同编号', dataIndex: 'code', width: 150 }, { title: '合同编号', dataIndex: 'contractCode', width: 150 },
{ title: '合同内容', dataIndex: 'contractText', width: 250, ellipsis: true, tooltip: true }, { title: '合同名称', dataIndex: 'contractName', width: 250, ellipsis: true, tooltip: true },
{ title: '客户名称', dataIndex: 'customer', width: 200, ellipsis: true, tooltip: true }, { title: '客户名称', dataIndex: 'client', width: 200, ellipsis: true, tooltip: true },
{ title: '合同金额', dataIndex: 'amount', slotName: 'amount', width: 120 }, { title: '合同金额', dataIndex: 'contractAmount', slotName: 'contractAmount', width: 120 },
{ title: '已收款金额', dataIndex: 'receivedAmount', slotName: 'receivedAmount', width: 120 }, { title: '已收款金额', dataIndex: 'receivedAmount', slotName: 'receivedAmount', width: 120 },
{ title: '已结算金额', dataIndex: 'settlementAmount', slotName: 'settlementAmount', width: 120 }, { title: '未收款金额', dataIndex: 'pendingAmount', width: 120 },
{ title: '签订日期', dataIndex: 'signDate', width: 120 }, { title: '签署日期', dataIndex: 'signDate', width: 120 },
{ title: '付款日期', dataIndex: 'paymentDate', width: 120 }, { title: '开始日期', dataIndex: 'startDate', width: 120 },
{ title: '履约期限', dataIndex: 'performanceDeadline', width: 120 }, { title: '结束日期', dataIndex: 'endDate', width: 120 },
{ title: '合同状态', dataIndex: 'contractStatus', slotName: 'contractStatus', width: 100 }, { title: '合同状态', dataIndex: 'status', slotName: 'status', width: 100 },
{ title: '业务员', dataIndex: 'salespersonName', width: 100 }, { title: '项目经理', dataIndex: 'projectManager', width: 100 },
{ title: '部门', dataIndex: 'salespersonDeptName', width: 120 }, { title: '销售经理', dataIndex: 'salesManager', width: 100 },
{ title: '项目名称', dataIndex: 'projectName', width: 200, ellipsis: true, tooltip: true }, { title: '完成进度', dataIndex: 'progress', width: 100 },
{ title: '产品或服务', dataIndex: 'productService', width: 200, ellipsis: true, tooltip: true }, { title: '备注', dataIndex: 'remark', width: 200, ellipsis: true, tooltip: true },
{ title: '期限', dataIndex: 'duration', width: 100 },
{ title: '备注', dataIndex: 'notes', width: 200, ellipsis: true, tooltip: true },
{ title: '操作', slotName: 'action', width: 200, fixed: 'right' } { title: '操作', slotName: 'action', width: 200, fixed: 'right' }
] ]
// //
const loading = ref(false) const loading = ref(false)
const dataList = ref<ContractData[]>([]) const dataList = ref([
{
id: 1,
contractCode: 'RC2024001',
contractName: '华能新能源风电场叶片检测服务合同',
client: '华能新能源股份有限公司',
contractAmount: 320,
receivedAmount: 192,
pendingAmount: 128,
signDate: '2024-02-20',
startDate: '2024-03-01',
endDate: '2024-04-30',
status: 'executing',
projectManager: '张项目经理',
salesManager: '李销售经理',
progress: '60%',
remark: '项目进展顺利,客户满意度高'
},
{
id: 2,
contractCode: 'RC2024002',
contractName: '大唐风电场防雷检测项目合同',
client: '大唐新能源股份有限公司',
contractAmount: 268,
receivedAmount: 134,
pendingAmount: 134,
signDate: '2024-02-25',
startDate: '2024-03-05',
endDate: '2024-04-20',
status: 'executing',
projectManager: '王项目经理',
salesManager: '赵销售经理',
progress: '45%',
remark: '按计划执行中'
},
{
id: 3,
contractCode: 'RC2024003',
contractName: '中广核风电场设备维护服务合同',
client: '中广核新能源投资有限公司',
contractAmount: 450,
receivedAmount: 450,
pendingAmount: 0,
signDate: '2024-01-15',
startDate: '2024-01-20',
endDate: '2024-01-31',
status: 'completed',
projectManager: '刘项目经理',
salesManager: '孙销售经理',
progress: '100%',
remark: '项目已完成,客户验收通过'
}
])
const pagination = reactive({ const pagination = reactive({
current: 1, current: 1,
pageSize: 10, pageSize: 10,
total: 0, total: 3,
showTotal: true, showTotal: true,
showPageSize: true showPageSize: true
}) })
@ -185,38 +230,20 @@ const getStatusText = (status: string) => {
// //
const search = async () => { const search = async () => {
loading.value = true loading.value = true
try { setTimeout(() => {
const params: ContractQueryParams = {
...searchForm,
type: 'revenue', //
page: searchForm.page,
pageSize: searchForm.pageSize
}
const response = await getContractList(params)
if (response.code === 0) {
dataList.value = response.rows || []
pagination.total = response.total || 0
} else {
Message.error(response.msg || '获取合同列表失败')
}
} catch (error) {
console.error('获取合同列表失败:', error)
Message.error('获取合同列表失败')
} finally {
loading.value = false loading.value = false
} }, 1000)
} }
const reset = () => { const reset = () => {
Object.assign(searchForm, { Object.assign(searchForm, {
code: '', contractName: '',
customer: '', contractCode: '',
contractStatus: '', client: '',
status: '',
signDate: '', signDate: '',
page: 1, page: 1,
pageSize: 10 size: 10
}) })
pagination.current = 1 pagination.current = 1
search() search()
@ -230,7 +257,7 @@ const onPageChange = (page: number) => {
} }
const onPageSizeChange = (size: number) => { const onPageSizeChange = (size: number) => {
searchForm.pageSize = size searchForm.size = size
searchForm.page = 1 searchForm.page = 1
pagination.pageSize = size pagination.pageSize = size
pagination.current = 1 pagination.current = 1
@ -246,20 +273,20 @@ const exportContract = () => {
Message.info('导出合同功能开发中...') Message.info('导出合同功能开发中...')
} }
const viewDetail = (record: ContractData) => { const viewDetail = (record: any) => {
Message.info(`查看合同详情: ${record.contractText}`) Message.info(`查看合同详情: ${record.contractName}`)
} }
const editRecord = (record: ContractData) => { const editRecord = (record: any) => {
Message.info(`编辑合同: ${record.contractText}`) Message.info(`编辑合同: ${record.contractName}`)
} }
const approveContract = (record: ContractData) => { const approveContract = (record: any) => {
Message.info(`审批合同: ${record.contractText}`) Message.info(`审批合同: ${record.contractName}`)
} }
const viewPayment = (record: ContractData) => { const viewPayment = (record: any) => {
Message.info(`查看收款记录: ${record.contractText}`) Message.info(`查看收款记录: ${record.contractName}`)
} }
onMounted(() => { onMounted(() => {

View File

@ -1,11 +0,0 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss">
</style>

View File

@ -22,6 +22,21 @@
/> />
</a-form-item> </a-form-item>
<a-form-item label="提案类型">
<a-select
v-model="searchForm.type"
placeholder="请选择类型"
allow-clear
style="width: 150px"
>
<a-option value="">全部</a-option>
<a-option value="管理规范">管理规范</a-option>
<a-option value="操作流程">操作流程</a-option>
<a-option value="安全制度">安全制度</a-option>
<a-option value="其他">其他</a-option>
</a-select>
</a-form-item>
<a-form-item label="状态"> <a-form-item label="状态">
<a-select <a-select
v-model="searchForm.status" v-model="searchForm.status"
@ -30,8 +45,8 @@
style="width: 150px" style="width: 150px"
> >
<a-option value="">全部</a-option> <a-option value="">全部</a-option>
<a-option value="PUBLISHED">已公</a-option> <a-option value="PUBLISHED">已公</a-option>
<a-option value="APPROVED">公示</a-option> <a-option value="APPROVED">通过</a-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
@ -168,6 +183,7 @@ const columns = [
const searchForm = reactive({ const searchForm = reactive({
title: '', title: '',
proposer: '', proposer: '',
type: '',
status: '' status: ''
}) })
@ -197,19 +213,22 @@ const regulationStore = useRegulationStore()
// //
const getStatusColor = (status: RegulationStatus) => { const getStatusColor = (status: RegulationStatus) => {
const colors = { const colors = {
[RegulationStatus.DRAFT]: 'blue',
[RegulationStatus.PUBLISHED]: 'purple', [RegulationStatus.PUBLISHED]: 'purple',
[RegulationStatus.APPROVED]: 'green', [RegulationStatus.APPROVED]: 'green',
} }
return colors[status] || 'purple' return colors[status] || 'blue'
} }
// //
const getStatusText = (status: RegulationStatus) => { const getStatusText = (status: RegulationStatus) => {
const texts = { const texts = {
[RegulationStatus.DRAFT]: '草稿',
[RegulationStatus.PUBLISHED]: '已公告', [RegulationStatus.PUBLISHED]: '已公告',
[RegulationStatus.APPROVED]: '已公示', [RegulationStatus.APPROVED]: '已公示',
} }
return texts[status] || '已公告' return texts[status] || '草稿'
} }
// //
@ -249,56 +268,21 @@ const getLevelText = (level: RegulationLevel) => {
const getTableData = async () => { const getTableData = async () => {
loading.value = true loading.value = true
try { try {
// const response = await regulationApi.getRegulationList({
const requestParams: any = {
page: pagination.current, page: pagination.current,
size: pagination.pageSize, size: pagination.pageSize,
title: searchForm.title || undefined, title: searchForm.title || undefined,
proposer: searchForm.proposer || undefined proposer: searchForm.proposer || undefined,
} type: searchForm.type || undefined,
status: searchForm.status || undefined
})
if (searchForm.status) { if (response.status === 200) {
// tableData.value = response.data.records
requestParams.status = searchForm.status pagination.total = response.data.total
const response = await regulationApi.getRegulationList(requestParams) pagination.current = response.data.current
if (response.status === 200) {
tableData.value = response.data.records
pagination.total = response.data.total
pagination.current = response.data.current
} else {
Message.error('搜索失败')
}
} else { } else {
// Message.error('搜索失败')
//
const [publishedResponse, approvedResponse] = await Promise.all([
regulationApi.getRegulationList({
...requestParams,
status: RegulationStatus.PUBLISHED
}),
regulationApi.getRegulationList({
...requestParams,
status: RegulationStatus.APPROVED
})
])
if (publishedResponse.status === 200 && approvedResponse.status === 200) {
//
const allRecords = [
...publishedResponse.data.records,
...approvedResponse.data.records
]
//
allRecords.sort((a, b) => new Date(b.createTime).getTime() - new Date(a.createTime).getTime())
tableData.value = allRecords
pagination.total = allRecords.length
pagination.current = 1
} else {
Message.error('搜索失败')
}
} }
} catch (error) { } catch (error) {
console.error('搜索制度公示失败:', error) console.error('搜索制度公示失败:', error)
@ -319,6 +303,7 @@ const reset = () => {
Object.assign(searchForm, { Object.assign(searchForm, {
title: '', title: '',
proposer: '', proposer: '',
type: '',
status: '' status: ''
}) })
pagination.current = 1 pagination.current = 1

View File

@ -31,6 +31,21 @@
/> />
</a-form-item> </a-form-item>
<a-form-item label="提案类型">
<a-select
v-model="searchForm.type"
placeholder="请选择类型"
allow-clear
style="width: 150px"
>
<a-option value="">全部</a-option>
<a-option value="管理规范">管理规范</a-option>
<a-option value="操作流程">操作流程</a-option>
<a-option value="安全制度">安全制度</a-option>
<a-option value="其他">其他</a-option>
</a-select>
</a-form-item>
<a-form-item label="状态"> <a-form-item label="状态">
<a-select <a-select
v-model="searchForm.status" v-model="searchForm.status"
@ -337,6 +352,7 @@ const columns = [
const searchForm = reactive({ const searchForm = reactive({
title: '', title: '',
proposer: '', proposer: '',
type: '',
status: '' status: ''
}) })
@ -450,7 +466,7 @@ const getLevelText = (level: RegulationLevel) => {
// //
const getRegulationTypes = async () => { const getRegulationTypes = async () => {
try { try {
const response = await regulationApi.searchRegulationTypes() const response = await regulationApi.getRegulationTypes()
if (response.status === 200) { if (response.status === 200) {
regulationTypes.value = response.data.records || response.data regulationTypes.value = response.data.records || response.data
} }
@ -468,6 +484,7 @@ const getTableData = async () => {
size: pagination.pageSize, size: pagination.pageSize,
title: searchForm.title || undefined, title: searchForm.title || undefined,
proposer: searchForm.proposer || undefined, proposer: searchForm.proposer || undefined,
type: searchForm.type || undefined,
status: searchForm.status || undefined status: searchForm.status || undefined
}) })
@ -497,6 +514,7 @@ const reset = () => {
Object.assign(searchForm, { Object.assign(searchForm, {
title: '', title: '',
proposer: '', proposer: '',
type: '',
status: '' status: ''
}) })
pagination.current = 1 pagination.current = 1

View File

@ -30,8 +30,8 @@
style="width: 150px" style="width: 150px"
> >
<a-option value="">全部</a-option> <a-option value="">全部</a-option>
<a-option value="CONFIRMED">已确认</a-option> <a-option value="confirmed">已确认</a-option>
<a-option value="PENDING">待确认</a-option> <a-option value="pending">待确认</a-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
@ -64,8 +64,8 @@
</span> </span>
</template> </template>
<template #confirmStatus="{ record }"> <template #confirmStatus="{ record }">
<a-tag :color="record.confirmStatus === 'CONFIRMED' ? 'green' : 'orange'"> <a-tag :color="record.confirmStatus === 'confirmed' ? 'green' : 'orange'">
{{ record.confirmStatus === 'CONFIRMED' ? '已确认' : '待确认' }} {{ record.confirmStatus === 'confirmed' ? '已确认' : '待确认' }}
</a-tag> </a-tag>
</template> </template>
@ -96,11 +96,6 @@
<span>公示人: {{ currentRegulation.createByName }}</span> <span>公示人: {{ currentRegulation.createByName }}</span>
<span>公示时间: {{ currentRegulation.publishTime }}</span> <span>公示时间: {{ currentRegulation.publishTime }}</span>
<span>生效日期: {{ currentRegulation.effectiveTime }}</span> <span>生效日期: {{ currentRegulation.effectiveTime }}</span>
<span>确认状态:
<a-tag :color="currentRegulation.confirmStatus === 'CONFIRMED' ? 'green' : 'orange'">
{{ currentRegulation.confirmStatus === 'CONFIRMED' ? '已确认' : '待确认' }}
</a-tag>
</span>
</div> </div>
</div> </div>
@ -123,20 +118,9 @@
<a-divider /> <a-divider />
<div class="detail-footer"> <div class="detail-footer">
<a-button <a-button type="primary" @click="handleConfirm(currentRegulation)">
v-if="currentRegulation.confirmStatus !== 'CONFIRMED'" 确认知晓并遵守
type="primary" </a-button>
@click="handleConfirm(currentRegulation)"
>
确认知晓并遵守
</a-button>
<a-button
v-else
type="primary"
disabled
>
已确认知晓
</a-button>
<a-button @click="handleDownload(currentRegulation)"> <a-button @click="handleDownload(currentRegulation)">
下载制度文件 下载制度文件
</a-button> </a-button>
@ -232,20 +216,10 @@ const getTableData = async () => {
}) })
if (response.status === 200) { if (response.status === 200) {
tableData.value = response.data.records
// confirmStatus
const records = response.data.records.map((record: any) => ({
...record,
confirmStatus: record.confirmStatus || 'PENDING'
}))
tableData.value = records
pagination.pageSize = response.data.size pagination.pageSize = response.data.size
pagination.current = response.data.current pagination.current = response.data.current
pagination.total = response.data.total pagination.total = response.data.total
} else { } else {
Message.error('搜索失败') Message.error('搜索失败')
} }
@ -337,16 +311,11 @@ const submitConfirm = async () => {
Message.success('确认成功,您已承诺遵守该制度') Message.success('确认成功,您已承诺遵守该制度')
confirmModalVisible.value = false confirmModalVisible.value = false
// //
await getTableData() const index = tableData.value.findIndex(item => item.regulationId === currentRegulation.value.regulationId)
if (index !== -1) {
// tableData.value[index].confirmStatus = 'confirmed'
if (detailModalVisible.value && currentRegulation.value) { }
const updatedRecord = tableData.value.find(item => item.regulationId === currentRegulation.value.regulationId)
if (updatedRecord) {
currentRegulation.value = updatedRecord
}
}
} }
} catch (error) { } catch (error) {
console.error('确认失败:', error) console.error('确认失败:', error)

View File

@ -1,313 +0,0 @@
<template>
<div class="device-detail-container">
<a-card title="设备基本信息">
<a-descriptions :data="basicInfo" layout="vertical" bordered />
</a-card>
<a-card title="设备状态信息" style="margin-top: 16px">
<a-descriptions :data="statusInfo" layout="vertical" bordered />
</a-card>
<a-card title="设备详细信息" style="margin-top: 16px">
<a-tabs>
<a-tab-pane key="maintenance" tab="维护记录">
<div class="maintenance-section">
<a-button type="primary" @click="handleAddMaintenance">
添加维护记录
</a-button>
<a-divider />
<a-table :data="maintenanceRecords" :columns="maintenanceColumns" row-key="id" size="small" />
</div>
</a-tab-pane>
<a-tab-pane key="usage" tab="使用记录">
<div class="usage-section">
<a-table :data="usageRecords" :columns="usageColumns" row-key="id" size="small" />
</div>
</a-tab-pane>
<a-tab-pane key="location" tab="位置变更">
<div class="location-section">
<a-table :data="locationRecords" :columns="locationColumns" row-key="id" size="small" />
</div>
</a-tab-pane>
<a-tab-pane key="files" tab="相关文件">
<div class="files-section">
<a-upload
:file-list="fileList"
:custom-request="handleFileUpload"
:show-upload-list="{ showPreviewIcon: true, showRemoveIcon: true }"
>
<a-button>
<template #icon>
<IconUpload />
</template>
上传文件
</a-button>
</a-upload>
</div>
</a-tab-pane>
</a-tabs>
</a-card>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { IconUpload } from '@arco-design/web-vue/es/icon'
import { getEquipmentDetail } from '@/apis/equipment'
import type { EquipmentResp } from '@/types/equipment.d'
defineOptions({ name: 'DeviceDetail' })
const route = useRoute()
const deviceId = route.params.id as string
//
const basicInfo = ref([
{ label: '设备名称', value: '' },
{ label: '设备类型', value: '' },
{ label: '设备型号', value: '' },
{ label: '序列号', value: '' },
{ label: '品牌', value: '' },
{ label: '资产编号', value: '' },
{ label: '负责人', value: '' },
{ label: '物理位置', value: '' },
{ label: '配置规格', value: '' },
{ label: '备注', value: '' },
])
//
const statusInfo = ref([
{ label: '设备状态', value: '' },
{ label: '使用状态', value: '' },
{ label: '位置状态', value: '' },
{ label: '健康状态', value: '' },
{ label: '当前使用人', value: '' },
{ label: '所属项目', value: '' },
{ label: '创建时间', value: '' },
{ label: '更新时间', value: '' },
])
//
const maintenanceRecords = ref([
{
id: 1,
maintenanceType: '定期保养',
maintenancePerson: '张师傅',
maintenanceTime: '2024-06-01 10:00:00',
description: '更换机油,检查设备运行状态',
cost: 500,
status: '已完成',
},
{
id: 2,
maintenanceType: '故障维修',
maintenancePerson: '李师傅',
maintenanceTime: '2024-05-15 14:30:00',
description: '修复传感器故障',
cost: 1200,
status: '已完成',
},
])
const maintenanceColumns = [
{ title: '维护类型', dataIndex: 'maintenanceType', key: 'maintenanceType' },
{ title: '维护人员', dataIndex: 'maintenancePerson', key: 'maintenancePerson' },
{ title: '维护时间', dataIndex: 'maintenanceTime', key: 'maintenanceTime' },
{ title: '维护描述', dataIndex: 'description', key: 'description' },
{ title: '维护费用', dataIndex: 'cost', key: 'cost' },
{ title: '状态', dataIndex: 'status', key: 'status' },
]
// 使
const usageRecords = ref([
{
id: 1,
userName: '张三',
projectName: '项目A',
startTime: '2024-06-01 09:00:00',
endTime: '2024-06-01 18:00:00',
usagePurpose: '现场检测',
},
{
id: 2,
userName: '李四',
projectName: '项目B',
startTime: '2024-05-30 08:00:00',
endTime: '2024-05-30 17:00:00',
usagePurpose: '安全巡检',
},
])
const usageColumns = [
{ title: '使用人', dataIndex: 'userName', key: 'userName' },
{ title: '项目名称', dataIndex: 'projectName', key: 'projectName' },
{ title: '开始时间', dataIndex: 'startTime', key: 'startTime' },
{ title: '结束时间', dataIndex: 'endTime', key: 'endTime' },
{ title: '使用目的', dataIndex: 'usagePurpose', key: 'usagePurpose' },
]
//
const locationRecords = ref([
{
id: 1,
oldLocation: '仓库A',
newLocation: '现场B',
changeTime: '2024-06-01 08:00:00',
changeReason: '项目需要',
operator: '管理员',
},
{
id: 2,
oldLocation: '现场B',
newLocation: '仓库A',
changeTime: '2024-05-30 18:00:00',
changeReason: '项目结束',
operator: '管理员',
},
])
const locationColumns = [
{ title: '原位置', dataIndex: 'oldLocation', key: 'oldLocation' },
{ title: '新位置', dataIndex: 'newLocation', key: 'newLocation' },
{ title: '变更时间', dataIndex: 'changeTime', key: 'changeTime' },
{ title: '变更原因', dataIndex: 'changeReason', key: 'changeReason' },
{ title: '操作人', dataIndex: 'operator', key: 'operator' },
]
//
const fileList = ref([
{
uid: '1',
name: '设备说明书.pdf',
status: 'done',
url: 'https://example.com/manual.pdf',
},
{
uid: '2',
name: '维护记录.xlsx',
status: 'done',
url: 'https://example.com/maintenance.xlsx',
},
])
//
const getEquipmentTypeText = (type: string) => {
const typeMap: Record<string, string> = {
1: '检测设备',
2: '安全设备',
3: '车辆',
}
return typeMap[type] || type
}
//
const getEquipmentStatusText = (status: string) => {
const statusMap: Record<string, string> = {
normal: '正常',
maintenance: '维修中',
scrapped: '报废',
}
return statusMap[status] || status
}
// 使
const getUseStatusText = (status: string) => {
return status === '1' ? '使用中' : '空闲'
}
//
const getLocationStatusText = (status: string) => {
const statusMap: Record<string, string> = {
in_stock: '库存中',
allocated: '已分配',
repair: '维修中',
scrap: '待报废',
scrapped: '已报废',
borrowed: '外借中',
lost: '丢失',
}
return statusMap[status] || status
}
//
const getHealthStatusText = (status: string) => {
const statusMap: Record<string, string> = {
excellent: '优秀',
good: '良好',
normal: '一般',
poor: '较差',
bad: '差',
}
return statusMap[status] || status
}
//
const loadDeviceDetail = async () => {
try {
const res = await getEquipmentDetail(deviceId)
const device = res.data as EquipmentResp
//
basicInfo.value = [
{ label: '设备名称', value: device.equipmentName },
{ label: '设备类型', value: getEquipmentTypeText(device.equipmentType) },
{ label: '设备型号', value: device.equipmentModel },
{ label: '序列号', value: device.equipmentSn },
{ label: '品牌', value: device.brand || '-' },
{ label: '资产编号', value: device.assetCode || '-' },
{ label: '负责人', value: device.responsiblePerson || '-' },
{ label: '物理位置', value: device.physicalLocation || '-' },
{ label: '配置规格', value: device.specification || '-' },
{ label: '备注', value: device.assetRemark || '-' },
]
//
statusInfo.value = [
{ label: '设备状态', value: getEquipmentStatusText(device.equipmentStatus) },
{ label: '使用状态', value: getUseStatusText(device.useStatus) },
{ label: '位置状态', value: getLocationStatusText(device.locationStatus || '') },
{ label: '健康状态', value: getHealthStatusText(device.healthStatus || '') },
{ label: '当前使用人', value: device.name || '-' },
{ label: '所属项目', value: device.projectName || '-' },
{ label: '创建时间', value: device.createTime || '-' },
{ label: '更新时间', value: device.updateTime || '-' },
]
} catch (error) {
console.error('加载设备详情失败:', error)
}
}
//
const handleAddMaintenance = () => {
// TODO:
console.log('添加维护记录')
}
//
const handleFileUpload = (options: any) => {
// TODO:
console.log('文件上传:', options)
}
onMounted(() => {
if (deviceId) {
loadDeviceDetail()
}
})
</script>
<style scoped>
.device-detail-container {
padding: 16px;
background: #f5f5f5;
min-height: 100vh;
}
.maintenance-section,
.usage-section,
.location-section,
.files-section {
padding: 16px 0;
}
</style>

View File

@ -2,14 +2,13 @@
<a-drawer <a-drawer
v-model:visible="visible" v-model:visible="visible"
title="岗位详情" title="岗位详情"
width="720px" width="620px"
unmount-on-close unmount-on-close
class="post-detail-drawer" class="post-detail-drawer"
@close="() => visible = false" @close="() => visible = false"
> >
<a-spin :loading="loading" class="detail-container"> <a-spin :loading="loading" class="detail-container">
<div class="detail-card"> <div class="detail-card">
<!-- 岗位标题和状态 -->
<div class="detail-header"> <div class="detail-header">
<div class="post-name">{{ primaryInfo?.name || '-' }}</div> <div class="post-name">{{ primaryInfo?.name || '-' }}</div>
<a-tag class="status-tag" :color="getStatusColor(primaryInfo?.status)"> <a-tag class="status-tag" :color="getStatusColor(primaryInfo?.status)">
@ -17,198 +16,40 @@
</a-tag> </a-tag>
</div> </div>
<!-- 标签页菜单 --> <div class="detail-group">
<a-tabs v-model:active-key="activeTab" class="detail-tabs" type="card"> <div class="group-title">基本信息</div>
<!-- 1. 基本信息 --> <div class="info-grid">
<a-tab-pane key="basic" title="基本信息"> <div class="info-item">
<div class="tab-content"> <div class="info-label">岗位ID</div>
<div class="info-grid"> <div class="info-value">{{ primaryInfo?.id || '-' }}</div>
<div class="info-item">
<div class="info-label">岗位名称</div>
<div class="info-value">{{ primaryInfo?.name || '-' }}</div>
</div>
<div class="info-item">
<div class="info-label">所属部门</div>
<div class="info-value">{{ primaryInfo?.deptName || '技术部' }}</div>
</div>
<div class="info-item">
<div class="info-label">直接上级岗位</div>
<div class="info-value">{{ primaryInfo?.superior || '技术总监' }}</div>
</div>
<div class="info-item">
<div class="info-label">岗位等级/职级</div>
<div class="info-value">{{ primaryInfo?.level || 'P5' }}</div>
</div>
<div class="info-item">
<div class="info-label">编写日期/版本号</div>
<div class="info-value">{{ primaryInfo?.version || '2024-06-01 / V1.0' }}</div>
</div>
<div class="info-item">
<div class="info-label">岗位编号</div>
<div class="info-value">{{ primaryInfo?.id || '10001' }}</div>
</div>
<div class="info-item">
<div class="info-label">工作地点</div>
<div class="info-value">{{ primaryInfo?.location || '上海/远程' }}</div>
</div>
</div>
<!-- 岗位目的 -->
<div class="section-title">岗位目的</div>
<div class="content-container">
<div class="content-text">{{ primaryInfo?.summary || '负责公司核心产品开发,支撑业务增长。' }}</div>
</div>
</div> </div>
</a-tab-pane> <div class="info-item">
<div class="info-label">岗位排序</div>
<!-- 2. 工作职责 --> <div class="info-value">{{ primaryInfo?.sort || '-' }}</div>
<a-tab-pane key="tasks" title="工作职责">
<div class="tab-content">
<!-- 主要职责与工作任务 -->
<div class="section-title">主要职责与工作任务</div>
<div class="content-container">
<ul>
<li v-for="(task, idx) in (primaryInfo?.tasks || ['负责XXX产品的需求分析、架构设计、核心模块编码和单元测试。', '制定并执行季度社交媒体营销计划,提升品牌曝光度和用户互动率。'])" :key="idx">{{ task }}</li>
</ul>
</div>
<!-- 工作权限 -->
<div class="section-title">工作权限</div>
<div class="content-container">
<ul>
<li v-for="(perm, idx) in (primaryInfo?.permissions || ['有权审批部门内5000元以下的采购申请。', '有权对项目团队成员的工作任务进行分配和调整。'])" :key="idx">{{ perm }}</li>
</ul>
</div>
<!-- 汇报关系 -->
<div class="section-title">汇报关系</div>
<div class="info-grid">
<div class="info-item">
<div class="info-label">直接上级</div>
<div class="info-value">{{ primaryInfo?.superior || '技术总监' }}</div>
</div>
<div class="info-item">
<div class="info-label">直接下级</div>
<div class="info-value">{{ primaryInfo?.subordinates || '无' }}</div>
</div>
<div class="info-item">
<div class="info-label">协作关系</div>
<div class="info-value">{{ primaryInfo?.collaboration || '与产品部、销售部、客服部紧密合作' }}</div>
</div>
</div>
</div> </div>
</a-tab-pane> </div>
</div>
<!-- 3. 任职资格 --> <div v-if="primaryInfo?.remark" class="detail-group">
<a-tab-pane key="qualifications" title="任职资格"> <div class="group-title">岗位说明</div>
<div class="tab-content"> <div class="remark-container">
<div class="info-grid"> <div class="remark-content">{{ primaryInfo.remark }}</div>
<div class="info-item"> </div>
<div class="info-label">教育背景</div> </div>
<div class="info-value">{{ primaryInfo?.education || '本科及以上学历,计算机相关专业' }}</div>
</div> <div class="detail-group">
<div class="info-item"> <div class="group-title">时间信息</div>
<div class="info-label">工作经验</div> <div class="info-grid">
<div class="info-value">{{ primaryInfo?.experience || '5年以上相关工作经验' }}</div> <div class="info-item">
</div> <div class="info-label">创建时间</div>
<div class="info-item"> <div class="info-value">{{ primaryInfo?.createTime || '-' }}</div>
<div class="info-label">专业知识与技能</div>
<div class="info-value">{{ primaryInfo?.skills || '精通Java/Python良好的沟通与团队协作能力' }}</div>
</div>
<div class="info-item">
<div class="info-label">证书/执照</div>
<div class="info-value">{{ primaryInfo?.certificates || 'PMP、注册会计师等' }}</div>
</div>
<div class="info-item">
<div class="info-label">其他要求</div>
<div class="info-value">{{ primaryInfo?.otherRequirements || '英语流利,能适应短期出差' }}</div>
</div>
</div>
</div> </div>
</a-tab-pane> <div class="info-item">
<div class="info-label">更新时间</div>
<!-- 4. 工作条件 --> <div class="info-value">{{primaryInfo?.updateTime || '-' }}</div>
<a-tab-pane key="conditions" title="工作条件">
<div class="tab-content">
<div class="info-grid">
<div class="info-item">
<div class="info-label">工作环境</div>
<div class="info-value">{{ primaryInfo?.workEnv || '办公室/远程' }}</div>
</div>
<div class="info-item">
<div class="info-label">工作时间</div>
<div class="info-value">{{ primaryInfo?.workTime || '标准工时,偶有加班' }}</div>
</div>
<div class="info-item">
<div class="info-label">出差要求</div>
<div class="info-value">{{ primaryInfo?.travel || '偶尔出差' }}</div>
</div>
<div class="info-item">
<div class="info-label">体力要求</div>
<div class="info-value">{{ primaryInfo?.physical || '无特殊要求' }}</div>
</div>
<div class="info-item">
<div class="info-label">特殊设备/工具</div>
<div class="info-value">{{ primaryInfo?.tools || '电脑、办公软件' }}</div>
</div>
<div class="info-item">
<div class="info-label">健康与安全</div>
<div class="info-value">{{ primaryInfo?.health || '注意用眼卫生' }}</div>
</div>
</div>
</div> </div>
</a-tab-pane> </div>
</div>
<!-- 5. 绩效与薪酬 -->
<a-tab-pane key="performance" title="绩效与薪酬">
<div class="tab-content">
<!-- 绩效衡量标准 -->
<div class="section-title">绩效衡量标准</div>
<div class="content-container">
<div class="content-text">{{ primaryInfo?.performance || '根据项目按时交付率、代码质量、产品性能指标提升情况进行评估。' }}</div>
</div>
<!-- 薪酬范围 -->
<div class="section-title">薪酬范围</div>
<div class="content-container">
<div class="content-text salary-value">{{ primaryInfo?.salaryRange || '20-30万/年' }}</div>
</div>
</div>
</a-tab-pane>
<!-- 6. 企业文化与发展 -->
<a-tab-pane key="culture" title="企业文化与发展">
<div class="tab-content">
<!-- 公司文化与价值观要求 -->
<div class="section-title">公司文化与价值观要求</div>
<div class="content-container">
<div class="content-text">{{ primaryInfo?.culture || '客户第一、创新、诚信、合作' }}</div>
</div>
<!-- 职业发展路径 -->
<div class="section-title">职业发展路径</div>
<div class="content-container">
<div class="content-text">{{ primaryInfo?.careerPath || '可晋升为高级工程师、技术经理,或横向发展至产品/项目管理岗位。' }}</div>
</div>
</div>
</a-tab-pane>
<!-- 7. 时间信息 -->
<a-tab-pane key="time" title="时间信息">
<div class="tab-content">
<div class="info-grid">
<div class="info-item">
<div class="info-label">创建时间</div>
<div class="info-value">{{ primaryInfo?.createTime || '-' }}</div>
</div>
<div class="info-item">
<div class="info-label">更新时间</div>
<div class="info-value">{{ primaryInfo?.updateTime || '-' }}</div>
</div>
</div>
</div>
</a-tab-pane>
</a-tabs>
</div> </div>
</a-spin> </a-spin>
@ -224,57 +65,30 @@
import { nextTick, ref } from 'vue' import { nextTick, ref } from 'vue'
import { getPostDetail } from '@/apis/system/post' import { getPostDetail } from '@/apis/system/post'
import { useLoading } from '@/hooks' import { useLoading } from '@/hooks'
//import { formatDate } from '@/utils/date'
defineOptions({ name: 'PostDetailDrawer' }) defineOptions({ name: 'PostDetailDrawer' })
const visible = ref(false) const visible = ref(false)
const { loading, setLoading } = useLoading() const { loading, setLoading } = useLoading()
const primaryInfo = ref<any>(null) const primaryInfo = ref<any>(null)
const activeTab = ref('basic')
// //
const getDetail = async (id: string) => { const getDetail = async (id: string) => {
try { try {
setLoading(true) setLoading(true)
primaryInfo.value = null primaryInfo.value = null
const response = await getPostDetail(id) const response = await getPostDetail(id)
const data = response?.data || response const data = response?.data || response
if (data && typeof data === 'object') { if (data && typeof data === 'object') {
primaryInfo.value = { primaryInfo.value = {
id: data.postId ?? data.id ?? '10001', id: data.postId ?? data.id,
name: data.postName ?? data.name ?? '-', name: data.postName ?? data.name,
sort: data.postSort,
status: data.status, status: data.status,
department: data.department ?? data.deptName ?? '技术部', remark: data.remark,
superior: data.superior ?? '技术总监',
level: data.level ?? 'P5',
version: data.version ?? '2024-06-01 / V1.0',
location: data.location ?? '上海/远程',
summary: data.summary ?? '负责公司核心产品开发,支撑业务增长。',
tasks: data.tasks ?? [
'负责XXX产品的需求分析、架构设计、核心模块编码和单元测试。',
'制定并执行季度社交媒体营销计划,提升品牌曝光度和用户互动率。',
],
permissions: data.permissions ?? [
'有权审批部门内5000元以下的采购申请。',
'有权对项目团队成员的工作任务进行分配和调整。',
],
subordinates: data.subordinates ?? '无',
collaboration: data.collaboration ?? '与产品部、销售部、客服部紧密合作',
education: data.education ?? '本科及以上学历,计算机相关专业',
experience: data.experience ?? '5年以上相关工作经验',
skills: data.skills ?? '精通Java/Python良好的沟通与团队协作能力',
certificates: data.certificates ?? 'PMP、注册会计师等',
otherRequirements: data.otherRequirements ?? '英语流利,能适应短期出差',
workEnv: data.workEnv ?? '办公室/远程',
workTime: data.workTime ?? '标准工时,偶有加班',
travel: data.travel ?? '偶尔出差',
physical: data.physical ?? '无特殊要求',
tools: data.tools ?? '电脑、办公软件',
health: data.health ?? '注意用眼卫生',
performance: data.performance ?? '根据项目按时交付率、代码质量、产品性能指标提升情况进行评估。',
careerPath: data.careerPath ?? '可晋升为高级工程师、技术经理,或横向发展至产品/项目管理岗位。',
salaryRange: data.salaryRange ?? '20-30万/年',
culture: data.culture ?? '客户第一、创新、诚信、合作',
createTime: data.createTime, createTime: data.createTime,
updateTime: data.updateTime, updateTime: data.updateTime,
} }
@ -300,14 +114,15 @@ const getStatusColor = (status: number | string) => {
return 'rgb(150, 150, 150)' return 'rgb(150, 150, 150)'
} }
//
const handleClose = () => { const handleClose = () => {
visible.value = false visible.value = false
} }
//
const onDetail = async (id: string) => { const onDetail = async (id: string) => {
if (!id) return if (!id) return
visible.value = true visible.value = true
activeTab.value = 'basic' //
await nextTick() await nextTick()
await getDetail(id) await getDetail(id)
} }
@ -343,13 +158,13 @@ defineExpose({
.detail-header { .detail-header {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 20px; margin-bottom: 28px;
padding-bottom: 15px; padding-bottom: 20px;
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
} }
.post-name { .post-name {
font-size: 20px; font-size: 22px;
font-weight: 600; font-weight: 600;
color: var(--value-color); color: var(--value-color);
margin-right: 15px; margin-right: 15px;
@ -365,34 +180,37 @@ defineExpose({
color: white !important; color: white !important;
} }
.detail-tabs { .detail-group {
margin-top: 10px; margin-bottom: 25px;
position: relative;
background-color: var(--group-bg);
border-radius: 8px;
padding: 12px 16px;
} }
.tab-content { .detail-group:last-child {
padding: 20px 0; margin-bottom: 0;
max-height: 500px;
overflow-y: auto;
overflow-x: hidden;
} }
/* 自定义滚动条样式 */ .group-title {
.tab-content::-webkit-scrollbar { font-size: 16px;
width: 6px; font-weight: 600;
color: var(--group-title-color);
padding-bottom: 12px;
border-bottom: 1px solid var(--border-color);
margin-bottom: 15px;
display: flex;
align-items: center;
} }
.tab-content::-webkit-scrollbar-track { .group-title::before {
background: #f1f1f1; content: '';
border-radius: 3px; display: inline-block;
} width: 4px;
height: 16px;
.tab-content::-webkit-scrollbar-thumb { background-color: var(--primary-color);
background: #c1c1c1; margin-right: 8px;
border-radius: 3px; border-radius: 2px;
}
.tab-content::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
} }
.info-grid { .info-grid {
@ -438,38 +256,17 @@ defineExpose({
word-break: break-word; word-break: break-word;
} }
.salary-value { .remark-container {
color: #e74c3c;
font-weight: 600;
}
.content-container {
padding: 16px; padding: 16px;
background: white; background: white;
border-radius: 6px; border-radius: 6px;
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
} }
.content-text { .remark-content {
color: var(--value-color); color: var(--value-color);
line-height: 1.7; line-height: 1.7;
font-size: 15px; font-size: 15px;
white-space: pre-wrap;
word-break: break-word;
}
.section-title {
font-size: 18px;
font-weight: 600;
color: var(--group-title-color);
margin-top: 20px;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
}
.section-title:first-child {
margin-top: 0;
} }
.footer-actions { .footer-actions {
@ -477,52 +274,4 @@ defineExpose({
justify-content: flex-end; justify-content: flex-end;
padding: 16px 24px; padding: 16px 24px;
} }
/* 标签页样式优化 */
:deep(.arco-tabs-nav) {
background-color: var(--light-bg);
border-radius: 8px;
padding: 4px;
margin-bottom: 16px;
}
:deep(.arco-tabs-nav-tab) {
background-color: transparent;
border: none;
}
:deep(.arco-tabs-tab) {
border-radius: 6px;
margin: 0 2px;
padding: 8px 16px;
font-weight: 400;
font-size: 14px;
transition: all 0.3s ease;
border: 2px solid transparent;
color: #666;
}
:deep(.arco-tabs-tab:hover) {
background-color: rgba(52, 152, 219, 0.1);
color: var(--primary-color);
font-weight: 500;
}
:deep(.arco-tabs-tab-active) {
background-color: var(--primary-color);
color: white !important;
border-color: var(--primary-color);
box-shadow: 0 2px 8px rgba(52, 152, 219, 0.3);
font-weight: 700 !important;
font-size: 15px !important;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
:deep(.arco-tabs-content) {
padding: 0;
}
:deep(.arco-tabs-tabpane) {
padding: 0;
}
</style> </style>

View File

@ -1,14 +1,14 @@
<template> <template>
<GiPageLayout> <GiPageLayout>
<GiTable <GiTable
row-key="postId" row-key="postId"
:data="dataList" :data="dataList"
:columns="tableColumns" :columns="tableColumns"
:loading="loading" :loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1200 }" :scroll="{ x: '100%', y: '100%', minWidth: 1200 }"
:pagination="pagination" :pagination="pagination"
:disabled-tools="['size']" :disabled-tools="['size']"
@refresh="search" @refresh="search"
> >
<template #top> <template #top>
<GiForm v-model="queryForm" search :columns="queryFormColumns" size="medium" @search="search" @reset="reset"></GiForm> <GiForm v-model="queryForm" search :columns="queryFormColumns" size="medium" @search="search" @reset="reset"></GiForm>
@ -24,59 +24,15 @@
{{ Number(record.status) === 1 ? '正常' : '停用' }} {{ Number(record.status) === 1 ? '正常' : '停用' }}
</a-tag> </a-tag>
</template> </template>
<template #postName="{ record }">
<a-dropdown trigger="hover" @visible-change="(visible) => onDropdownVisibleChange(visible, record)">
<a-link type="primary" :title="`点击查询 ${record.postName} 岗位下的用户信息`" @click="onPostClick(record)">
{{ record.postName }}
<template #icon>
<icon-user style="margin-right: 4px; font-size: 12px; color: #666;" />
<icon-down />
</template>
</a-link>
<template #content>
<a-doption
:value="record.postId"
:disabled="userLoadingMap[record.postId]"
>
<div v-if="userLoadingMap[record.postId]" class="loading-container">
<a-spin size="mini" />
<span style="margin-left: 8px;">加载中...</span>
</div>
<div v-else-if="postUsersMap[record.postId]?.length">
<div class="user-list">
<div class="user-list-header">
<span class="user-count">该岗位下有 {{ postUsersMap[record.postId].length }} 个用户</span>
</div>
<div class="user-item" v-for="user in postUsersMap[record.postId]" :key="user.userId">
<a-avatar :size="24" :src="user.avatar">
{{ user.name?.charAt(0) || user.account?.charAt(0) || 'U' }}
</a-avatar>
<div class="user-info">
<div class="user-name clickable" @click="onUserClick(user)" :title="`点击查看 ${user.name || user.account} 的详细信息`">{{ user.name || user.account }}</div>
<div class="user-detail">
<span v-if="user.mobile">手机: {{ user.mobile }}</span>
<span v-if="user.email">邮箱: {{ user.email }}</span>
</div>
</div>
</div>
</div>
</div>
<div v-else class="no-users">
该岗位暂无用户
</div>
</a-doption>
</template>
</a-dropdown>
</template>
<template #action="{ record }"> <template #action="{ record }">
<a-space> <a-space>
<a-link v-permission="['system:post:query']" title="详情" @click="onDetail(record)">详情</a-link> <a-link v-permission="['system:post:query']" title="详情" @click="onDetail(record)">详情</a-link>
<a-link v-permission="['system:post:update']" title="修改" @click="onUpdate(record)">修改</a-link> <a-link v-permission="['system:post:update']" title="修改" @click="onUpdate(record)">修改</a-link>
<a-link <a-link
v-permission="['system:post:delete']" v-permission="['system:post:delete']"
status="danger" status="danger"
title="删除" title="删除"
@click="onDelete(record)" @click="onDelete(record)"
> >
删除 删除
</a-link> </a-link>
@ -86,7 +42,6 @@
<PostAddModal ref="PostAddModalRef" @save-success="search" /> <PostAddModal ref="PostAddModalRef" @save-success="search" />
<PostDetailDrawer ref="PostDetailDrawerRef" /> <PostDetailDrawer ref="PostDetailDrawerRef" />
<UserDetailDrawer ref="UserDetailDrawerRef" />
</GiPageLayout> </GiPageLayout>
</template> </template>
@ -94,10 +49,8 @@
import type { TableColumnData } from '@arco-design/web-vue' import type { TableColumnData } from '@arco-design/web-vue'
import PostAddModal from './PostAddModal.vue' import PostAddModal from './PostAddModal.vue'
import PostDetailDrawer from './PostDetailDrawer.vue' import PostDetailDrawer from './PostDetailDrawer.vue'
import UserDetailDrawer from '../user/UserDetailDrawer.vue' import { deletePost, listPost } from '@/apis/system/post'
import { deletePost, listPost, getPostUsers } from '@/apis/system/post'
import type { PostVO } from '@/apis/system/type' import type { PostVO } from '@/apis/system/type'
import type { UserNewResp } from '@/apis/system/type'
import { useResetReactive, useTable } from '@/hooks' import { useResetReactive, useTable } from '@/hooks'
import { isMobile } from '@/utils' import { isMobile } from '@/utils'
import has from '@/utils/has' import has from '@/utils/has'
@ -145,10 +98,6 @@ const {
...queryForm, ...queryForm,
}), { immediate: true }) }), { immediate: true })
//
const postUsersMap = ref<Record<string, UserNewResp[]>>({})
const userLoadingMap = ref<Record<string, boolean>>({})
const tableColumns = ref<TableColumnData[]>([ const tableColumns = ref<TableColumnData[]>([
{ {
title: '序号', title: '序号',
@ -160,7 +109,6 @@ const tableColumns = ref<TableColumnData[]>([
{ {
title: '岗位名称', title: '岗位名称',
dataIndex: 'postName', dataIndex: 'postName',
slotName: 'postName',
minWidth: 140, minWidth: 140,
ellipsis: true, ellipsis: true,
tooltip: true, tooltip: true,
@ -221,66 +169,8 @@ const onDelete = (record: PostVO) => {
}) })
} }
//
const onDropdownVisibleChange = async (visible: boolean, record: PostVO) => {
if (visible && !postUsersMap.value[record.postId] && !userLoadingMap.value[record.postId]) {
//
userLoadingMap.value[record.postId] = true
try {
const response = await getPostUsers(record.postId)
if (response.success && response.data) {
postUsersMap.value[record.postId] = response.data
} else {
postUsersMap.value[record.postId] = []
}
} catch (error) {
console.error('获取岗位用户信息失败:', error)
postUsersMap.value[record.postId] = []
} finally {
userLoadingMap.value[record.postId] = false
}
}
}
//
const onPostClick = async (record: PostVO) => {
//
if (postUsersMap.value[record.postId]) {
return
}
//
userLoadingMap.value[record.postId] = true
try {
const response = await getPostUsers(record.postId)
if (response.success && response.data) {
postUsersMap.value[record.postId] = response.data
} else {
postUsersMap.value[record.postId] = []
}
} catch (error) {
console.error('获取岗位用户信息失败:', error)
postUsersMap.value[record.postId] = []
} finally {
userLoadingMap.value[record.postId] = false
}
}
//
const onUserClick = (user: UserNewResp) => {
UserDetailDrawerRef.value?.onOpen(user.userId)
}
const PostAddModalRef = ref<InstanceType<typeof PostAddModal>>() const PostAddModalRef = ref<InstanceType<typeof PostAddModal>>()
const PostDetailDrawerRef = ref<InstanceType<typeof PostDetailDrawer>>() const PostDetailDrawerRef = ref<InstanceType<typeof PostDetailDrawer>>()
const UserDetailDrawerRef = ref<InstanceType<typeof UserDetailDrawer>>()
// //
const onAdd = () => { const onAdd = () => {
@ -298,80 +188,4 @@ const onDetail = (record: PostVO) => {
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss"></style>
.loading-container {
display: flex;
align-items: center;
padding: 8px;
}
.user-list {
min-width: 300px;
max-width: 400px;
.user-list-header {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
margin-bottom: 8px;
.user-count {
font-size: 12px;
color: #666;
}
}
.user-item {
display: flex;
align-items: center;
padding: 8px 12px;
border-bottom: 1px solid #f5f5f5;
&:last-child {
border-bottom: none;
}
.user-info {
margin-left: 8px;
flex: 1;
.user-name {
font-size: 14px;
font-weight: 500;
color: #333;
margin-bottom: 2px;
&.clickable {
cursor: pointer;
color: #165dff;
transition: color 0.2s ease;
&:hover {
color: #0e42d2;
text-decoration: underline;
}
}
}
.user-detail {
font-size: 12px;
color: #666;
span {
margin-right: 8px;
&:last-child {
margin-right: 0;
}
}
}
}
}
}
.no-users {
padding: 16px;
text-align: center;
color: #999;
font-size: 14px;
}
</style>