save code
This commit is contained in:
+270
-32
@@ -7,37 +7,275 @@
|
|||||||
@app.on_event("startup")
|
@app.on_event("startup")
|
||||||
INFO: Will watch for changes in these directories: ['/home/xsl/code/ocean_project_manager/backend']
|
INFO: Will watch for changes in these directories: ['/home/xsl/code/ocean_project_manager/backend']
|
||||||
INFO: Uvicorn running on http://0.0.0.0:8188 (Press CTRL+C to quit)
|
INFO: Uvicorn running on http://0.0.0.0:8188 (Press CTRL+C to quit)
|
||||||
INFO: Started reloader process [6520] using WatchFiles
|
INFO: Started reloader process [4044] using WatchFiles
|
||||||
INFO: Started server process [6522]
|
INFO: Started server process [4143]
|
||||||
INFO: Waiting for application startup.
|
INFO: Waiting for application startup.
|
||||||
INFO: Application startup complete.
|
INFO: Application startup complete.
|
||||||
INFO: 127.0.0.1:38698 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
INFO: 127.0.0.1:58538 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
||||||
INFO: 127.0.0.1:57886 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
INFO: 127.0.0.1:59360 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
INFO: 127.0.0.1:57900 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
INFO: 127.0.0.1:59362 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
INFO: 127.0.0.1:43792 - "GET /health HTTP/1.1" 200 OK
|
INFO: 127.0.0.1:56816 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
INFO: 127.0.0.1:47906 - "GET / HTTP/1.1" 200 OK
|
INFO: 127.0.0.1:56828 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
INFO: 127.0.0.1:47906 - "GET /favicon.ico HTTP/1.1" 404 Not Found
|
INFO: 127.0.0.1:56840 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
INFO: 127.0.0.1:57272 - "GET / HTTP/1.1" 200 OK
|
INFO: 127.0.0.1:56842 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
INFO: 127.0.0.1:57288 - "GET /favicon.ico HTTP/1.1" 404 Not Found
|
INFO: 127.0.0.1:56854 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
INFO: 127.0.0.1:60220 - "GET / HTTP/1.1" 200 OK
|
INFO: 127.0.0.1:56862 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
INFO: 127.0.0.1:60230 - "GET /favicon.ico HTTP/1.1" 404 Not Found
|
WARNING: WatchFiles detected changes in 'config/import-excel-data-v2.py'. Reloading...
|
||||||
INFO: 127.0.0.1:49220 - "GET / HTTP/1.1" 200 OK
|
INFO: Shutting down
|
||||||
INFO: 127.0.0.1:49232 - "GET /favicon.ico HTTP/1.1" 404 Not Found
|
INFO: Waiting for application shutdown.
|
||||||
INFO: 127.0.0.1:43984 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
INFO: Application shutdown complete.
|
||||||
INFO: 127.0.0.1:43998 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
INFO: Finished server process [4143]
|
||||||
INFO: 127.0.0.1:35788 - "GET / HTTP/1.1" 200 OK
|
INFO: Started server process [8844]
|
||||||
INFO: 127.0.0.1:35788 - "GET /favicon.ico HTTP/1.1" 404 Not Found
|
INFO: Waiting for application startup.
|
||||||
INFO: 127.0.0.1:56510 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
INFO: Application startup complete.
|
||||||
INFO: 127.0.0.1:59614 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
WARNING: WatchFiles detected changes in 'config/import-excel-data-v2.py'. Reloading...
|
||||||
INFO: 127.0.0.1:39442 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
INFO: Shutting down
|
||||||
INFO: 127.0.0.1:39456 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
INFO: Waiting for application shutdown.
|
||||||
INFO: 127.0.0.1:39462 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
INFO: Application shutdown complete.
|
||||||
INFO: 127.0.0.1:39476 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
INFO: Finished server process [8844]
|
||||||
INFO: 127.0.0.1:39482 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
INFO: Started server process [9834]
|
||||||
INFO: 127.0.0.1:39486 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
INFO: Waiting for application startup.
|
||||||
INFO: 127.0.0.1:39502 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
INFO: Application startup complete.
|
||||||
INFO: 127.0.0.1:39506 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
WARNING: WatchFiles detected changes in 'config/import-excel-data-v2.py'. Reloading...
|
||||||
INFO: 127.0.0.1:39522 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
INFO: Shutting down
|
||||||
INFO: 127.0.0.1:39530 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
INFO: Waiting for application shutdown.
|
||||||
INFO: 127.0.0.1:33492 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
INFO: Application shutdown complete.
|
||||||
INFO: 127.0.0.1:33496 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
INFO: Finished server process [9834]
|
||||||
|
INFO: Started server process [9977]
|
||||||
|
INFO: Waiting for application startup.
|
||||||
|
INFO: Application startup complete.
|
||||||
|
INFO: 127.0.0.1:52704 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:52712 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:52714 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:52728 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:52730 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:52744 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:55242 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55248 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55252 - "POST /api/v1/auth/logout HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40794 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40804 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40806 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:49642 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:49646 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:49662 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:36846 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:36848 - "GET /api/v1/projects/statistics HTTP/1.1" 401 Unauthorized
|
||||||
|
INFO: 127.0.0.1:57848 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:54960 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:54970 - "GET /api/v1/projects/statistics HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:42982 - "GET /api/v1/projects?limit=1 HTTP/1.1" 401 Unauthorized
|
||||||
|
INFO: 127.0.0.1:48152 - "GET /api/v1/projects?limit=1 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:36282 - "GET /api/v1/projects/statistics HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:36266 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53870 - "GET /api/v1/projects?page=1&page_size=1 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46572 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46582 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:58766 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53146 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53150 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53166 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53176 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:57068 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:57054 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:57084 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:57092 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:57096 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:57108 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50094 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50104 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50120 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50122 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50138 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50140 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50152 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50164 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50180 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:50182 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50192 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50202 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:47624 - "POST /api/v1/auth/logout HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:47640 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:47654 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:47668 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:47662 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:47676 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40950 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:42386 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:42398 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46604 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40432 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40418 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40436 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40440 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40442 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40450 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40480 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40464 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40486 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40502 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:54970 - "POST /api/v1/auth/logout HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:54980 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:54984 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:54992 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55000 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:54998 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:56704 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55252 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55260 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:39804 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:39806 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:39816 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:39822 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:56744 - "POST /api/v1/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:56760 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46382 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46398 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50542 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50560 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50550 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:50570 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50584 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50592 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:34382 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:34398 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:34394 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:34410 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:43320 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:43324 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55352 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55364 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55374 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55384 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55400 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55390 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59952 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59962 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59966 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59974 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46372 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46384 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46778 - "GET /api/v1/projects/518 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:56132 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:56136 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:34614 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:34620 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:41252 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:41266 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:44226 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:44218 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:44234 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:44246 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:45244 - "GET /api/v1/projects/517 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:45248 - "GET /api/v1/projects/517 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:48548 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:48546 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:48556 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:41330 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:41336 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:41350 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:41360 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:41368 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:41374 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity
|
||||||
|
INFO: 127.0.0.1:41380 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:52902 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53544 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35154 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35164 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35166 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55172 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55178 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:55194 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59502 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59504 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51184 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35166 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35174 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35156 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35186 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35184 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35198 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50886 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50902 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50914 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50926 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50932 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50940 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51086 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51100 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51116 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51132 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51140 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51146 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:58160 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:58176 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:58192 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51614 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51620 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51618 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51624 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35502 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35518 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:48470 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:45706 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:60248 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:48410 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53666 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:38252 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:38854 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:38862 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53224 - "GET /api/v1/projects/518 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53234 - "GET /api/v1/projects/518 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:41768 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:41770 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:41772 - "GET /api/v1/projects/518 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:41784 - "GET /api/v1/projects/518 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:44564 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:44578 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:60338 - "GET /api/v1/projects/515 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:60344 - "GET /api/v1/projects/515 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51570 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:51578 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:47272 - "GET /api/v1/projects?page=1&page_size=10&keyword=3 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:47278 - "GET /api/v1/projects?page=1&page_size=10&keyword=31 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46604 - "GET /api/v1/projects?page=1&page_size=10&keyword=317 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46616 - "GET /api/v1/projects?page=1&page_size=10&keyword=3175 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46622 - "GET /api/v1/projects?page=1&page_size=10&keyword=317 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46634 - "GET /api/v1/projects?page=1&page_size=10&keyword=31 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46642 - "GET /api/v1/projects?page=1&page_size=10&keyword=3 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46654 - "GET /api/v1/projects?page=1&page_size=10&keyword=3 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59518 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59524 - "GET /api/v1/projects?page=1&page_size=10&keyword=22 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59534 - "GET /api/v1/projects?page=1&page_size=10&keyword=225 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59548 - "GET /api/v1/projects?page=1&page_size=10&keyword=2253 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59564 - "GET /api/v1/projects?page=1&page_size=10&keyword=225 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59570 - "GET /api/v1/projects?page=1&page_size=10&keyword=22 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59574 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59590 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:47084 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:47096 - "GET /api/v1/projects?page=1&page_size=10&keyword=20 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:47112 - "GET /api/v1/projects?page=1&page_size=10&keyword=202 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:47118 - "GET /api/v1/projects?page=1&page_size=10&keyword=2022 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50184 - "GET /api/v1/projects?page=1&page_size=10&keyword=20225 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50196 - "GET /api/v1/projects?page=1&page_size=10&keyword=202252 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50204 - "GET /api/v1/projects?page=1&page_size=10&keyword=20225 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50206 - "GET /api/v1/projects?page=1&page_size=10&keyword=2022 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50220 - "GET /api/v1/projects?page=1&page_size=10&keyword=202 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50222 - "GET /api/v1/projects?page=1&page_size=10&keyword=20 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50228 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:50240 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53746 - "GET /api/v1/projects?page=1&page_size=10&keyword=t HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53756 - "GET /api/v1/projects?page=1&page_size=10&keyword=ti HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53764 - "GET /api/v1/projects?page=1&page_size=10&keyword=ti%27a HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53758 - "GET /api/v1/projects?page=1&page_size=10&keyword=tiao HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53780 - "GET /api/v1/projects?page=1&page_size=10&keyword=tiao%27s HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53786 - "GET /api/v1/projects?page=1&page_size=10&keyword=tiao%27sh HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53798 - "GET /api/v1/projects?page=1&page_size=10&keyword=tiao%27shi HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53814 - "GET /api/v1/projects?page=1&page_size=10&keyword=%E8%B0%83%E8%AF%95 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53830 - "GET /api/v1/projects?page=1&page_size=10&keyword=%E8%B0%83 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53842 - "GET /api/v1/projects?page=1&page_size=10&keyword=%E8%B0%83 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53846 - "GET /api/v1/projects?page=1&page_size=10&keyword=l HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53858 - "GET /api/v1/projects?page=1&page_size=10&keyword=la HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53870 - "GET /api/v1/projects?page=1&page_size=10&keyword=lan HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:53882 - "GET /api/v1/projects?page=1&page_size=10&keyword=%E7%83%82 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:39108 - "GET /api/v1/projects?page=1&page_size=10&keyword=%E7%83%82 HTTP/1.1" 200 OK
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from sqlalchemy.orm import declarative_base
|
|||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
# 配置
|
# 配置
|
||||||
EXCEL_FILE = '../docs/example.xls'
|
EXCEL_FILE = '/home/xsl/code/ocean_project_manager/docs/example.xls'
|
||||||
DATABASE_URL = "mysql+aiomysql://root:rootpassword@localhost:3306/project_manager?charset=utf8mb4"
|
DATABASE_URL = "mysql+aiomysql://root:rootpassword@localhost:3306/project_manager?charset=utf8mb4"
|
||||||
|
|
||||||
# 创建引擎
|
# 创建引擎
|
||||||
@@ -195,7 +195,7 @@ async def import_projects_from_sheet(sheet, sheet_name, admin_id):
|
|||||||
'profit': parse_decimal_value(safe_get(39)),
|
'profit': parse_decimal_value(safe_get(39)),
|
||||||
'actual_profit': parse_decimal_value(safe_get(40)),
|
'actual_profit': parse_decimal_value(safe_get(40)),
|
||||||
'cost_settlement_amount': parse_decimal_value(safe_get(41)),
|
'cost_settlement_amount': parse_decimal_value(safe_get(41)),
|
||||||
'cumulative_progress': parse_decimal_value(safe_get(42)),
|
'cumulative_progress': (lambda x: x if x is None else min(x, Decimal('999.99')))(parse_decimal_value(safe_get(42))),
|
||||||
'receivable_amount': parse_decimal_value(safe_get(43)),
|
'receivable_amount': parse_decimal_value(safe_get(43)),
|
||||||
'invoice_amount': parse_decimal_value(safe_get(44)),
|
'invoice_amount': parse_decimal_value(safe_get(44)),
|
||||||
'actual_receipt_amount': parse_decimal_value(safe_get(45)),
|
'actual_receipt_amount': parse_decimal_value(safe_get(45)),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
> vite
|
> vite
|
||||||
|
|
||||||
|
|
||||||
VITE v5.4.21 ready in 131 ms
|
VITE v5.4.21 ready in 145 ms
|
||||||
|
|
||||||
➜ Local: http://localhost:8189/
|
➜ Local: http://localhost:8189/
|
||||||
➜ Network: use --host to expose
|
➜ Network: use --host to expose
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>API测试</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>API测试页面</h1>
|
||||||
|
<button onclick="testStatistics()">测试统计API</button>
|
||||||
|
<div id="result"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const API_BASE = '/api/v1';
|
||||||
|
|
||||||
|
async function testStatistics() {
|
||||||
|
const resultDiv = document.getElementById('result');
|
||||||
|
resultDiv.innerHTML = '加载中...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 先登录
|
||||||
|
const loginResponse = await fetch(`${API_BASE}/auth/login`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ username: 'admin', password: 'admin123' })
|
||||||
|
});
|
||||||
|
const loginData = await loginResponse.json();
|
||||||
|
const token = loginData.data.token;
|
||||||
|
|
||||||
|
// 调用统计API
|
||||||
|
const statsResponse = await fetch(`${API_BASE}/projects/statistics/basic`, {
|
||||||
|
headers: { 'Authorization': `Bearer ${token}` }
|
||||||
|
});
|
||||||
|
const statsData = await statsResponse.json();
|
||||||
|
|
||||||
|
resultDiv.innerHTML = `
|
||||||
|
<h2>统计结果:</h2>
|
||||||
|
<pre>${JSON.stringify(statsData, null, 2)}</pre>
|
||||||
|
<h3>项目总数:${statsData.data.total_count}</h3>
|
||||||
|
`;
|
||||||
|
} catch (error) {
|
||||||
|
resultDiv.innerHTML = `<p style="color: red;">错误:${error.message}</p>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -5,6 +5,7 @@ import Layout from './components/Layout/Layout';
|
|||||||
import Login from './pages/Login/Login';
|
import Login from './pages/Login/Login';
|
||||||
import Dashboard from './pages/Dashboard/Dashboard';
|
import Dashboard from './pages/Dashboard/Dashboard';
|
||||||
import ProjectList from './pages/Projects/ProjectList';
|
import ProjectList from './pages/Projects/ProjectList';
|
||||||
|
import ProjectDetail from './pages/Projects/ProjectDetail';
|
||||||
import UserList from './pages/Users/UserList';
|
import UserList from './pages/Users/UserList';
|
||||||
import Statistics from './pages/Statistics/Statistics';
|
import Statistics from './pages/Statistics/Statistics';
|
||||||
|
|
||||||
@@ -48,6 +49,14 @@ function App() {
|
|||||||
</PrivateRoute>
|
</PrivateRoute>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/projects/:id"
|
||||||
|
element={
|
||||||
|
<PrivateRoute>
|
||||||
|
<ProjectDetail />
|
||||||
|
</PrivateRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/projects/create"
|
path="/projects/create"
|
||||||
element={
|
element={
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { projectAPI } from '../../services/project';
|
import { projectAPI } from '../../services/project';
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
const [statistics, setStatistics] = useState(null);
|
const [statistics, setStatistics] = useState(null);
|
||||||
const [recentProjects, setRecentProjects] = useState([]);
|
const [recentProjects, setRecentProjects] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -14,13 +16,18 @@ const Dashboard = () => {
|
|||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
|
console.log('开始获取仪表盘数据...');
|
||||||
|
|
||||||
const [statsResponse, projectsResponse] = await Promise.all([
|
const [statsResponse, projectsResponse] = await Promise.all([
|
||||||
authAPI.getStatistics(),
|
projectAPI.getStatistics(),
|
||||||
projectAPI.getList({ page: 1, page_size: 5 }),
|
projectAPI.getList({ page: 1, page_size: 5 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setStatistics(statsResponse);
|
console.log('统计API响应:', statsResponse);
|
||||||
setRecentProjects(projectsResponse.items || []);
|
console.log('项目列表API响应:', projectsResponse);
|
||||||
|
|
||||||
|
setStatistics(statsResponse.data || statsResponse);
|
||||||
|
setRecentProjects(projectsResponse.data?.items || projectsResponse.items || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch dashboard data:', error);
|
console.error('Failed to fetch dashboard data:', error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -44,17 +51,9 @@ const Dashboard = () => {
|
|||||||
<div className="stat-value">{statistics?.total_count || 0}</div>
|
<div className="stat-value">{statistics?.total_count || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-label">进行中</div>
|
<div className="stat-label">平均进度(%)</div>
|
||||||
<div className="stat-value">
|
<div className="stat-value">
|
||||||
{(statistics?.total_count || 0) -
|
{statistics?.avg_cumulative_progress?.toFixed(1) || '0.0'}
|
||||||
(statistics?.completed_count || 0) -
|
|
||||||
(statistics?.cancelled_count || 0) || 0}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="stat-card">
|
|
||||||
<div className="stat-label">已完成</div>
|
|
||||||
<div className="stat-value">
|
|
||||||
{statistics?.completed_count || 0}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
@@ -66,6 +65,12 @@ const Dashboard = () => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="stat-card">
|
||||||
|
<div className="stat-label">回款完成率(%)</div>
|
||||||
|
<div className="stat-value">
|
||||||
|
{statistics?.avg_receipt_completion_rate?.toFixed(1) || '0.0'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="card">
|
<div className="card">
|
||||||
@@ -93,7 +98,7 @@ const Dashboard = () => {
|
|||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
e.currentTarget.style.background = 'transparent';
|
e.currentTarget.style.background = 'transparent';
|
||||||
}}
|
}}
|
||||||
onClick={() => window.location.href = `/projects/${project.id}`}
|
onClick={() => navigate(`/projects/${project.id}`)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -0,0 +1,212 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
|
import { projectAPI } from '../../services/project';
|
||||||
|
|
||||||
|
const ProjectDetail = () => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [project, setProject] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchProjectDetail();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
const fetchProjectDetail = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await projectAPI.getDetail(id);
|
||||||
|
setProject(result.data || result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch project detail:', error);
|
||||||
|
alert('获取项目详情失败');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <div>加载中...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!project) {
|
||||||
|
return <div>项目不存在</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div style={{ marginBottom: '24px' }}>
|
||||||
|
<button
|
||||||
|
className="btn btn-default"
|
||||||
|
onClick={() => navigate('/projects')}
|
||||||
|
>
|
||||||
|
返回列表
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card">
|
||||||
|
<div className="card-header">
|
||||||
|
项目详情
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '24px' }}>
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>项目编号</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.project_no || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>项目名称</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.name || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>工程类别</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.engineering_type || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>所属部门</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.project_department || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>合同金额(万元)</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.contract_amount ? Number(project.contract_amount).toLocaleString('zh-CN', { minimumFractionDigits: 2 }) : '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>项目负责人</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.project_leader || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>建设单位</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.owner_unit || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>建设单位联系人</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.owner_contact || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>合同签订日期</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.signing_date || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>开始日期</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.start_date || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>计划结束日期</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.planned_end_date || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>实际结束日期</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.actual_end_date || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>累计进度(%)</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.cumulative_progress ? Number(project.cumulative_progress).toFixed(2) : '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>实际收款金额(万元)</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.actual_receipt_amount ? Number(project.actual_receipt_amount).toLocaleString('zh-CN', { minimumFractionDigits: 2 }) : '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>回款完成率(%)</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.receipt_completion_rate ? Number(project.receipt_completion_rate).toFixed(2) : '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>状态</label>
|
||||||
|
<div style={{ padding: '8px 12px', background: '#f5f5f5', borderRadius: '4px' }}>
|
||||||
|
{project.status || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{project.payment_method && (
|
||||||
|
<div style={{ marginTop: '24px' }}>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>付款方式</label>
|
||||||
|
<div style={{ padding: '12px', background: '#f5f5f5', borderRadius: '4px', whiteSpace: 'pre-wrap' }}>
|
||||||
|
{project.payment_method}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{project.problems && (
|
||||||
|
<div style={{ marginTop: '24px' }}>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500, color: '#ff4d4f' }}>存在问题</label>
|
||||||
|
<div style={{ padding: '12px', background: '#fff2f0', borderRadius: '4px', border: '1px solid #ffccc7', whiteSpace: 'pre-wrap' }}>
|
||||||
|
{project.problems}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{project.suggestions && (
|
||||||
|
<div style={{ marginTop: '24px' }}>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500, color: '#1890ff' }}>建议</label>
|
||||||
|
<div style={{ padding: '12px', background: '#e6f7ff', borderRadius: '4px', border: '1px solid #91d5ff', whiteSpace: 'pre-wrap' }}>
|
||||||
|
{project.suggestions}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{project.remarks && (
|
||||||
|
<div style={{ marginTop: '24px' }}>
|
||||||
|
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 500 }}>备注</label>
|
||||||
|
<div style={{ padding: '12px', background: '#f5f5f5', borderRadius: '4px', whiteSpace: 'pre-wrap' }}>
|
||||||
|
{project.remarks}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div style={{ marginTop: '24px', padding: '12px', background: '#fafafa', borderRadius: '4px' }}>
|
||||||
|
<div style={{ fontSize: '12px', color: '#999' }}>
|
||||||
|
创建时间:{project.created_at} | 创建人:{project.created_by_name || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProjectDetail;
|
||||||
@@ -41,8 +41,9 @@ const ProjectList = ({ mode = 'list' }) => {
|
|||||||
...filters,
|
...filters,
|
||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
setList(result.items || []);
|
const data = result.data || result;
|
||||||
setTotal(result.total || 0);
|
setList(data.items || []);
|
||||||
|
setTotal(data.total || 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch projects:', error);
|
console.error('Failed to fetch projects:', error);
|
||||||
alert('获取项目列表失败');
|
alert('获取项目列表失败');
|
||||||
@@ -72,7 +73,8 @@ const ProjectList = ({ mode = 'list' }) => {
|
|||||||
|
|
||||||
const handleEdit = async (id) => {
|
const handleEdit = async (id) => {
|
||||||
try {
|
try {
|
||||||
const project = await projectAPI.getDetail(id);
|
const result = await projectAPI.getDetail(id);
|
||||||
|
const project = result.data || result;
|
||||||
setForm(project);
|
setForm(project);
|
||||||
setEditingId(id);
|
setEditingId(id);
|
||||||
setModalVisible(true);
|
setModalVisible(true);
|
||||||
@@ -143,6 +145,23 @@ const ProjectList = ({ mode = 'list' }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setForm({ ...form, [name]: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFilterChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
const newFilters = { ...filters };
|
||||||
|
if (value) {
|
||||||
|
newFilters[name] = value;
|
||||||
|
} else {
|
||||||
|
delete newFilters[name];
|
||||||
|
}
|
||||||
|
setFilters(newFilters);
|
||||||
|
fetchProjects(newFilters);
|
||||||
|
};
|
||||||
|
|
||||||
const canEdit = (createdBy) => {
|
const canEdit = (createdBy) => {
|
||||||
return user.role === 'admin' || (user.role === 'market' && createdBy === user?.id);
|
return user.role === 'admin' || (user.role === 'market' && createdBy === user?.id);
|
||||||
};
|
};
|
||||||
@@ -157,10 +176,13 @@ const ProjectList = ({ mode = 'list' }) => {
|
|||||||
{mode === 'create' ? '新建项目' : '项目列表'}
|
{mode === 'create' ? '新建项目' : '项目列表'}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div style={{ marginBottom: '16px', display: 'flex', gap: '12px' }}>
|
<div style={{ marginBottom: '16px', display: 'flex', gap: '12px', flexWrap: 'wrap', alignItems: 'center' }}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
name="keyword"
|
||||||
placeholder="搜索项目名称、合同编号..."
|
placeholder="搜索项目名称、合同编号..."
|
||||||
|
value={filters.keyword || ''}
|
||||||
|
onChange={handleFilterChange}
|
||||||
style={{
|
style={{
|
||||||
padding: '8px 12px',
|
padding: '8px 12px',
|
||||||
border: '1px solid #d9d9d9',
|
border: '1px solid #d9d9d9',
|
||||||
@@ -170,6 +192,9 @@ const ProjectList = ({ mode = 'list' }) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<select
|
<select
|
||||||
|
name="engineering_type"
|
||||||
|
value={filters.engineering_type || ''}
|
||||||
|
onChange={handleFilterChange}
|
||||||
style={{
|
style={{
|
||||||
padding: '8px 12px',
|
padding: '8px 12px',
|
||||||
border: '1px solid #d9d9d9',
|
border: '1px solid #d9d9d9',
|
||||||
@@ -184,6 +209,69 @@ const ProjectList = ({ mode = 'list' }) => {
|
|||||||
<option value="营销">营销</option>
|
<option value="营销">营销</option>
|
||||||
<option value="检修">检修</option>
|
<option value="检修">检修</option>
|
||||||
</select>
|
</select>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
name="signing_date_start"
|
||||||
|
value={filters.signing_date_start || ''}
|
||||||
|
onChange={handleFilterChange}
|
||||||
|
style={{
|
||||||
|
padding: '8px 12px',
|
||||||
|
border: '1px solid #d9d9d9',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '14px',
|
||||||
|
}}
|
||||||
|
placeholder="签订日期起"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
name="signing_date_end"
|
||||||
|
value={filters.signing_date_end || ''}
|
||||||
|
onChange={handleFilterChange}
|
||||||
|
style={{
|
||||||
|
padding: '8px 12px',
|
||||||
|
border: '1px solid #d9d9d9',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '14px',
|
||||||
|
}}
|
||||||
|
placeholder="签订日期止"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="contract_amount_min"
|
||||||
|
value={filters.contract_amount_min || ''}
|
||||||
|
onChange={handleFilterChange}
|
||||||
|
style={{
|
||||||
|
padding: '8px 12px',
|
||||||
|
border: '1px solid #d9d9d9',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '14px',
|
||||||
|
width: '120px',
|
||||||
|
}}
|
||||||
|
placeholder="最小金额"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="contract_amount_max"
|
||||||
|
value={filters.contract_amount_max || ''}
|
||||||
|
onChange={handleFilterChange}
|
||||||
|
style={{
|
||||||
|
padding: '8px 12px',
|
||||||
|
border: '1px solid #d9d9d9',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '14px',
|
||||||
|
width: '120px',
|
||||||
|
}}
|
||||||
|
placeholder="最大金额"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="btn btn-default"
|
||||||
|
onClick={() => {
|
||||||
|
setFilters({});
|
||||||
|
fetchProjects({});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
重置筛选
|
||||||
|
</button>
|
||||||
{user?.role === 'admin' && (
|
{user?.role === 'admin' && (
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
@@ -247,8 +335,8 @@ const ProjectList = ({ mode = 'list' }) => {
|
|||||||
<td style={{ padding: '12px 16px', fontSize: '14px', color: '#666' }}>
|
<td style={{ padding: '12px 16px', fontSize: '14px', color: '#666' }}>
|
||||||
{project.engineering_type}
|
{project.engineering_type}
|
||||||
</td>
|
</td>
|
||||||
<td style={{ padding: '12px 16px', fontSize: '14px', color: '#666', textAlign: 'right' }}>
|
<td style={{ padding: '12px 16px', fontSize: '14px', color: '#666', textAlign: 'right' }}>
|
||||||
{project.contract_amount?.toLocaleString('zh-CN')}
|
{project.contract_amount ? Number(project.contract_amount).toLocaleString('zh-CN', { minimumFractionDigits: 2 }) : '-'}
|
||||||
</td>
|
</td>
|
||||||
<td style={{ padding: '12px 16px', fontSize: '14px', textAlign: 'center' }}>
|
<td style={{ padding: '12px 16px', fontSize: '14px', textAlign: 'center' }}>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ const Statistics = () => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const [statsResponse, groupResponse, timelineResponse] = await Promise.all([
|
const [statsResponse, groupResponse, timelineResponse] = await Promise.all([
|
||||||
projectAPI.getStatistics(filters),
|
projectAPI.getStatistics(),
|
||||||
projectAPI.getGroupStatistics(filters),
|
projectAPI.getGroupStatistics({ group_by: filters.group_by }),
|
||||||
projectAPI.getTimelineStatistics(filters),
|
projectAPI.getTimelineStatistics({ time_field: filters.time_field, group_by: filters.group_by_time }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setStatistics(statsResponse);
|
setStatistics(statsResponse.data || statsResponse);
|
||||||
setGroupStatistics(groupResponse || []);
|
setGroupStatistics(groupResponse.data || groupResponse || []);
|
||||||
setTimelineStatistics(timelineResponse || []);
|
setTimelineStatistics(timelineResponse.data || timelineResponse || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch statistics data:', error);
|
console.error('Failed to fetch statistics data:', error);
|
||||||
alert('获取统计数据失败');
|
alert('获取统计数据失败');
|
||||||
@@ -155,36 +155,30 @@ const Statistics = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="stat-grid">
|
<div className="stat-grid">
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-label">项目总数</div>
|
<div className="stat-label">项目总数</div>
|
||||||
<div className="stat-value">{statistics?.total_count || 0}</div>
|
<div className="stat-value">{statistics?.total_count || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-label">总投资金额(万元)</div>
|
<div className="stat-label">平均进度(%)</div>
|
||||||
<div className="stat-value">
|
<div className="stat-value">
|
||||||
{(statistics?.total_investment || 0).toLocaleString('zh-CN', {
|
{Number(statistics?.avg_cumulative_progress || 0).toFixed(1)}
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2,
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-label">总合同金额(万元)</div>
|
<div className="stat-label">总合同金额(万元)</div>
|
||||||
<div className="stat-value">
|
<div className="stat-value">
|
||||||
{(statistics?.total_contract_amount || 0).toLocaleString('zh-CN', {
|
{Number(statistics?.total_contract_amount || 0).toLocaleString('zh-CN', {
|
||||||
minimumFractionDigits: 2,
|
minimumFractionDigits: 2,
|
||||||
maximumFractionDigits: 2,
|
maximumFractionDigits: 2,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-label">总收款金额(万元)</div>
|
<div className="stat-label">回款完成率(%)</div>
|
||||||
<div className="stat-value">
|
<div className="stat-value">
|
||||||
{(statistics?.total_receipt_amount || 0).toLocaleString('zh-CN', {
|
{Number(statistics?.avg_receipt_completion_rate || 0).toFixed(1)}
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2,
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -336,16 +330,16 @@ const Statistics = () => {
|
|||||||
borderBottom: '1px solid #f0f0f0',
|
borderBottom: '1px solid #f0f0f0',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
style={{
|
style={{
|
||||||
padding: '12px 16px',
|
padding: '12px 16px',
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
color: '#666',
|
color: '#666',
|
||||||
textAlign: 'right',
|
textAlign: 'left',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.month}
|
{item.month || item.day || item.year}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
style={{
|
style={{
|
||||||
padding: '12px 16px',
|
padding: '12px 16px',
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const projectAPI = {
|
|||||||
|
|
||||||
delete: (id) => api.delete(`/projects/${id}`),
|
delete: (id) => api.delete(`/projects/${id}`),
|
||||||
|
|
||||||
getStatistics: (params) => api.get('/projects/statistics', { params }),
|
getStatistics: (params) => api.get('/projects/statistics/basic', { params }),
|
||||||
|
|
||||||
getGroupStatistics: (params) => api.get('/projects/statistics/group', { params }),
|
getGroupStatistics: (params) => api.get('/projects/statistics/group', { params }),
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ body {
|
|||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-right: 1px solid #e8e8e8;
|
border-right: 1px solid #e8e8e8;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 64px;
|
left: 0;
|
||||||
top: 64px;
|
top: 64px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|||||||
Reference in New Issue
Block a user