diff --git a/docs/智慧环境.md b/docs/智慧环境.md index 6135676..0d6a9a2 100644 --- a/docs/智慧环境.md +++ b/docs/智慧环境.md @@ -2,7 +2,7 @@ title: 智慧环境 language_tabs: - shell: Shell - - http: HTTP + - http: HTTP· - javascript: JavaScript - ruby: Ruby - python: Python @@ -3664,7 +3664,7 @@ GET /api/education/knowledge |»» created_by|integer|false|none||none| |»» updated_by|integer|false|none||none| -## GET +## GET 获取知识详情 GET /api/education/knowledge/1 @@ -4193,5 +4193,2539 @@ POST /api/device/update ### 返回数据结构 +# 课程报名/管理端接口 + +## GET 获取报名记录列表 + +GET /api/admin/courses/{courseId}/enrollments + +> Body 请求参数 + +```json +"// GET /api/admin/courses/:courseId/enrollments\r\n\r\n// 请求参数:\r\n// - courseId: 课程ID(路径参数,必填)\r\n\r\n// 响应数据:\r\n// {\r\n// \"success\": true,\r\n// \"message\": \"获取报名记录列表成功\",\r\n// \"data\": [\r\n// {\r\n// \"id\": \"报名记录ID\",\r\n// \"course_id\": \"课程ID\",\r\n// \"user_id\": \"用户ID\",\r\n// \"username\": \"用户名\",\r\n// \"real_name\": \"真实姓名\",\r\n// \"enrollment_time\": \"报名时间\",\r\n// \"start_time\": \"开始学习时间\",\r\n// \"complete_time\": \"完成时间\",\r\n// \"progress\": \"学习进度\",\r\n// \"status\": \"状态\"\r\n// }\r\n// ]\r\n// }" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|courseId|path|string| 是 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +## GET 获取报名统计信息 + +GET /api/admin/courses/{courseId}/enrollments/statistics + +> Body 请求参数 + +```json +"// 请求参数:\r\n// - courseId: 课程ID(路径参数,必填)\r\n\r\n// 响应数据:\r\n// {\r\n// \"success\": true,\r\n// \"message\": \"获取报名统计信息成功\",\r\n// \"data\": {\r\n// \"total_enrollments\": \"总报名人数\",\r\n// \"in_progress_count\": \"学习中人数\",\r\n// \"completed_count\": \"已完成人数\",\r\n// \"cancelled_count\": \"已取消人数\",\r\n// \"average_progress\": \"平均学习进度\"\r\n// }\r\n// }" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|courseId|path|string| 是 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +## GET 导出报名数据 + +GET /api/admin/courses/{courseId}/enrollments/export + +> Body 请求参数 + +```json +"// 请求参数:\r\n// - courseId: 课程ID(路径参数,必填)\r\n\r\n// 响应数据:\r\n// {\r\n// \"success\": true,\r\n// \"message\": \"导出报名数据成功\",\r\n// \"data\": {\r\n// \"file_path\": \"导出文件路径\"\r\n// }\r\n// }" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|courseId|path|string| 是 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +# 课程报名/客户端接口 + +## GET 获取我的报名记录 + +GET /api/client/courses/enrollments/my + +> Body 请求参数 + +```json +"\r\n// 请求参数:\r\n// - 无(通过token获取用户ID)\r\n\r\n// 响应数据:\r\n// {\r\n// \"success\": true,\r\n// \"message\": \"获取报名记录列表成功\",\r\n// \"data\": [\r\n// {\r\n// \"id\": \"报名记录ID\",\r\n// \"course_id\": \"课程ID\",\r\n// \"course_title\": \"课程标题\",\r\n// \"course_category\": \"课程类别\",\r\n// \"course_cover\": \"课程封面\",\r\n// \"enrollment_time\": \"报名时间\",\r\n// \"start_time\": \"开始学习时间\",\r\n// \"complete_time\": \"完成时间\",\r\n// \"progress\": \"学习进度\",\r\n// \"status\": \"状态\"\r\n// }\r\n// ]\r\n// }" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +## POST 报名课程 + +POST /api/client/courses/{courseId}/enroll + +> Body 请求参数 + +```json +"// 请求参数:\r\n// - courseId: 课程ID(路径参数,必填)\r\n// - 用户ID通过token获取\r\n\r\n// 响应数据:\r\n// {\r\n// \"success\": true,\r\n// \"message\": \"报名课程成功\",\r\n// \"data\": {\r\n// \"id\": \"报名记录ID\"\r\n// }\r\n// }" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|courseId|path|string| 是 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +## GET 获取报名记录详情 + +GET /api/client/courses/enrollments/{enrollmentId} + +> Body 请求参数 + +```json +"// 请求参数:\r\n// - enrollmentId: 报名记录ID(路径参数,必填)\r\n// - 用户ID通过token获取\r\n\r\n// 响应数据:\r\n// {\r\n// \"success\": true,\r\n// \"message\": \"获取报名记录详情成功\",\r\n// \"data\": {\r\n// \"id\": \"报名记录ID\",\r\n// \"course_id\": \"课程ID\",\r\n// \"course_title\": \"课程标题\",\r\n// \"enrollment_time\": \"报名时间\",\r\n// \"start_time\": \"开始学习时间\",\r\n// \"complete_time\": \"完成时间\",\r\n// \"progress\": \"学习进度\",\r\n// \"status\": \"状态\"\r\n// }\r\n// }" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|enrollmentId|path|string| 是 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +## POST 取消报名 + +POST /api/client/courses/enrollments/{enrollmentId}/cancel + +> Body 请求参数 + +```json +"// 请求参数:\r\n// - enrollmentId: 报名记录ID(路径参数,必填)\r\n// - 用户ID通过token获取\r\n\r\n// 响应数据:\r\n// {\r\n// \"success\": true,\r\n// \"message\": \"取消报名成功\"\r\n// }" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|enrollmentId|path|string| 是 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +## PUT 更新学习进度 + +PUT /api/client/courses/enrollments/{enrollmentId}/progress + +> Body 请求参数 + +```json +"// 请求参数:\r\n// - enrollmentId: 报名记录ID(路径参数,必填)\r\n// - 用户ID通过token获取\r\n// 请求体:\r\n// {\r\n// \"progress\": \"学习进度(0-100的数字)\",\r\n// \"duration\": \"本次学习时长(秒,可选)\"\r\n// }\r\n\r\n// 响应数据:\r\n// {\r\n// \"success\": true,\r\n// \"message\": \"更新学习进度成功\"\r\n// }" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|enrollmentId|path|string| 是 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +# 意见反馈/管理端 + +## GET 获取反馈列表 + +GET /api/admin/feedbacks + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "list": [ + "string" + ], + "pagination": { + "total": 0, + "total_pages": null + } + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» list|[string]|true|none||none| +|»» pagination|object|true|none||none| +|»»» total|integer|true|none||none| +|»»» total_pages|null|true|none||none| + +## GET 获取反馈详情 + +GET /api/admin/feedbacks/1 + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0, + "user_id": 0, + "feedback_type": "string", + "title": "string", + "content": "string", + "image_urls": [ + "string" + ], + "location_point": { + "x": 0, + "y": 0 + }, + "location_description": "string", + "status": 0, + "handler_id": null, + "handling_time": null, + "handling_result": null, + "created_at": "string", + "updated_at": "string", + "longitude": 0, + "latitude": 0, + "user_name": "string", + "user_real_name": "string", + "handler_name": null, + "handler_real_name": null + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| +|»» user_id|integer|true|none||none| +|»» feedback_type|string|true|none||none| +|»» title|string|true|none||none| +|»» content|string|true|none||none| +|»» image_urls|[string]|true|none||none| +|»» location_point|object|true|none||none| +|»»» x|number|true|none||none| +|»»» y|number|true|none||none| +|»» location_description|string|true|none||none| +|»» status|integer|true|none||none| +|»» handler_id|null|true|none||none| +|»» handling_time|null|true|none||none| +|»» handling_result|null|true|none||none| +|»» created_at|string|true|none||none| +|»» updated_at|string|true|none||none| +|»» longitude|number|true|none||none| +|»» latitude|number|true|none||none| +|»» user_name|string|true|none||none| +|»» user_real_name|string|true|none||none| +|»» handler_name|null|true|none||none| +|»» handler_real_name|null|true|none||none| + +## PUT 更新反馈状态 + +PUT /api/admin/feedbacks/1/status + +> Body 请求参数 + +```json +"// 更新为处理中\r\n{\r\n \"status\": 1\r\n}\r\n\r\n// // 更新为已处理\r\n// {\r\n// \"status\": 2,\r\n// \"handling_result\": \"问题已解决:...\"\r\n// }\r\n\r\n// // 更新为已关闭\r\n// {\r\n// \"status\": 3\r\n// }" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string" +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| + +# 意见反馈/客户端 + +## POST 创建反馈 + +POST /api/client/feedbacks + +> Body 请求参数 + +```json +"{\r\n \"feedback_type\": \"problem\", // 必填,反馈类型:problem-问题报告/suggestion-建议/complaint-投诉/other-其他\r\n \"title\": \"标题\", // 必填,2-200个字符\r\n \"content\": \"反馈内容\", // 必填\r\n \"image_urls\": [ // 可选,图片URL数组\r\n \"http://example.com/image1.jpg\",\r\n \"http://example.com/image2.jpg\"\r\n ],\r\n \"location_point\": { // 必填,位置坐标\r\n \"latitude\": 30.123456, // 纬度,范围:-90到90\r\n \"longitude\": 120.123456 // 经度,范围:-180到180\r\n },\r\n \"location_description\": \"位置描述\" // 可选,最大255个字符\r\n}" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0 + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| + +## GET 获取用户的反馈列表 + +GET /api/client/feedbacks/my + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "list": [ + { + "id": 0, + "user_id": 0, + "feedback_type": "string", + "title": "string", + "content": "string", + "image_urls": [ + "string" + ], + "location_point": { + "x": 0, + "y": 0 + }, + "location_description": "string", + "status": 0, + "handler_id": null, + "handling_time": null, + "handling_result": null, + "created_at": "string", + "updated_at": "string", + "longitude": 0, + "latitude": 0, + "handler_name": null, + "handler_real_name": null + } + ], + "pagination": { + "total": 0, + "page": 0, + "page_size": 0, + "total_pages": 0 + } + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» list|[object]|true|none||none| +|»»» id|integer|false|none||none| +|»»» user_id|integer|false|none||none| +|»»» feedback_type|string|false|none||none| +|»»» title|string|false|none||none| +|»»» content|string|false|none||none| +|»»» image_urls|[string]|false|none||none| +|»»» location_point|object|false|none||none| +|»»»» x|number|true|none||none| +|»»»» y|number|true|none||none| +|»»» location_description|string|false|none||none| +|»»» status|integer|false|none||none| +|»»» handler_id|null|false|none||none| +|»»» handling_time|null|false|none||none| +|»»» handling_result|null|false|none||none| +|»»» created_at|string|false|none||none| +|»»» updated_at|string|false|none||none| +|»»» longitude|number|false|none||none| +|»»» latitude|number|false|none||none| +|»»» handler_name|null|false|none||none| +|»»» handler_real_name|null|false|none||none| +|»» pagination|object|true|none||none| +|»»» total|integer|true|none||none| +|»»» page|integer|true|none||none| +|»»» page_size|integer|true|none||none| +|»»» total_pages|integer|true|none||none| + +## GET 获取反馈详情 + +GET /api/client/feedbacks/1 + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0, + "user_id": 0, + "feedback_type": "string", + "title": "string", + "content": "string", + "image_urls": [ + "string" + ], + "location_point": { + "x": 0, + "y": 0 + }, + "location_description": "string", + "status": 0, + "handler_id": null, + "handling_time": null, + "handling_result": null, + "created_at": "string", + "updated_at": "string", + "longitude": 0, + "latitude": 0, + "user_name": "string", + "user_real_name": "string", + "handler_name": null, + "handler_real_name": null + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| +|»» user_id|integer|true|none||none| +|»» feedback_type|string|true|none||none| +|»» title|string|true|none||none| +|»» content|string|true|none||none| +|»» image_urls|[string]|true|none||none| +|»» location_point|object|true|none||none| +|»»» x|number|true|none||none| +|»»» y|number|true|none||none| +|»» location_description|string|true|none||none| +|»» status|integer|true|none||none| +|»» handler_id|null|true|none||none| +|»» handling_time|null|true|none||none| +|»» handling_result|null|true|none||none| +|»» created_at|string|true|none||none| +|»» updated_at|string|true|none||none| +|»» longitude|number|true|none||none| +|»» latitude|number|true|none||none| +|»» user_name|string|true|none||none| +|»» user_real_name|string|true|none||none| +|»» handler_name|null|true|none||none| +|»» handler_real_name|null|true|none||none| + +# 观察记录 + +## POST 创建观察记录 + +POST /api/admin/observations + +> Body 请求参数 + +```json +{ + "title": "发现一只黑脸琵鹭", + "content": "今天在湿地公园观察到一只黑脸琵鹭,正在觅食。这是今年第一次在此区域发现此物种。", + "species_id": 1, + "image_urls": [ + "http://example.com/images/bird1.jpg", + "http://example.com/images/bird2.jpg" + ], + "video_urls": [ + "http://example.com/videos/bird_feeding.mp4" + ], + "location_point": { + "latitude": 23.1291, + "longitude": 113.2644 + }, + "location_description": "湿地公园北区浅水区", + "weather": "晴朗", + "temperature": 26.5 +} +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0 + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| + +## GET 获取观察记录列表 + +GET /api/admin/observations + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "list": [ + { + "id": 0, + "user_id": 0, + "title": "string", + "content": "string", + "species_id": 0, + "image_urls": [ + "string" + ], + "video_urls": [ + "string" + ], + "location_point": { + "x": 0, + "y": 0 + }, + "location_description": "string", + "weather": "string", + "temperature": "string", + "status": 0, + "reviewer_id": 0, + "review_time": "string", + "review_comment": null, + "created_at": "string", + "updated_at": "string", + "longitude": 0, + "latitude": 0, + "user_name": "string", + "user_real_name": "string", + "species_name": "string", + "species_latin_name": "string", + "reviewer_name": "string", + "reviewer_real_name": "string" + } + ], + "pagination": { + "total": 0, + "page": 0, + "page_size": 0, + "total_pages": 0 + } + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» list|[object]|true|none||none| +|»»» id|integer|false|none||none| +|»»» user_id|integer|false|none||none| +|»»» title|string|false|none||none| +|»»» content|string|false|none||none| +|»»» species_id|integer|false|none||none| +|»»» image_urls|[string]|false|none||none| +|»»» video_urls|[string]|false|none||none| +|»»» location_point|object|false|none||none| +|»»»» x|number|true|none||none| +|»»»» y|number|true|none||none| +|»»» location_description|string|false|none||none| +|»»» weather|string|false|none||none| +|»»» temperature|string|false|none||none| +|»»» status|integer|false|none||none| +|»»» reviewer_id|integer|false|none||none| +|»»» review_time|string|false|none||none| +|»»» review_comment|null|false|none||none| +|»»» created_at|string|false|none||none| +|»»» updated_at|string|false|none||none| +|»»» longitude|number|false|none||none| +|»»» latitude|number|false|none||none| +|»»» user_name|string|false|none||none| +|»»» user_real_name|string|false|none||none| +|»»» species_name|string|false|none||none| +|»»» species_latin_name|string|false|none||none| +|»»» reviewer_name|string|false|none||none| +|»»» reviewer_real_name|string|false|none||none| +|»» pagination|object|true|none||none| +|»»» total|integer|true|none||none| +|»»» page|integer|true|none||none| +|»»» page_size|integer|true|none||none| +|»»» total_pages|integer|true|none||none| + +## PUT 更新观察记录 + +PUT /api/admin/observations/2 + +> Body 请求参数 + +```json +{ + "title": "黑脸琵鹭群体活动观察", + "content": "更新:经过仔细观察,发现是一个3只黑脸琵鹭的小群体", + "image_urls": [ + "http://example.com/images/bird_group1.jpg", + "http://example.com/images/bird_group2.jpg" + ], + "weather": "多云", + "temperature": 25.8 +} +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string" +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| + +## DELETE 删除观察记录 + +DELETE /api/admin/observations/2 + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +## PUT 更新观察记录状态 + +PUT /api/admin/observations/2/status + +> Body 请求参数 + +```json +"// 通过审核\r\n{\r\n \"status\": 1\r\n}\r\n\r\n// // 拒绝审核\r\n// {\r\n// \"status\": 2,\r\n// \"review_comment\": \"图片不够清晰,请重新上传更清晰的照片,并补充观察时间段信息。\"\r\n// }" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string" +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| + +# 物种信息 + +## POST 创建物种信息 + +POST /api/admin/species + +> Body 请求参数 + +```json +"{\r\n \"species_code\": \"BIRD001\", // 物种编号,必填,最大50字符\r\n \"chinese_name\": \"东方白鹳\", // 中文名称,必填,最大100字符\r\n \"latin_name\": \"Ciconia boyciana\", // 拉丁名称,选填,最大100字符\r\n \"category\": \"bird\", // 物种类别,必填,可选值:bird(鸟类)、mammal(哺乳类)、fish(鱼类)、amphibian(两栖类)、reptile(爬行类)、insect(昆虫类)、plant(植物)\r\n \"protection_level\": \"national_first\", // 保护等级,选填,默认normal,可选值:national_first(国家一级)、national_second(国家二级)、provincial(省级)、normal(普通)\r\n \"characteristics\": \"体型大,羽毛白色,喙长而直...\", // 特征描述,选填\r\n \"habits\": \"常在沼泽、湿地等水域觅食...\", // 生活习性,选填\r\n \"distribution\": \"主要分布在东北地区...\", // 分布区域,选填\r\n \"image_urls\": [\"http://example.com/image1.jpg\"], // 图片URL数组,选填\r\n \"status\": 1 // 状态,选填,默认1,可选值:0(禁用)、1(启用)\r\n}" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0 + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| + +## GET 获取物种列表 + +GET /api/admin/species + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "list": [ + { + "id": 0, + "species_code": "string", + "chinese_name": "string", + "latin_name": "string", + "category": "string", + "protection_level": "string", + "characteristics": "string", + "habits": "string", + "distribution": "string", + "image_urls": [ + "string" + ], + "status": 0, + "created_at": "string", + "updated_at": "string", + "created_by": 0, + "updated_by": 0 + } + ], + "pagination": { + "total": 0, + "page": 0, + "page_size": 0, + "total_pages": 0 + } + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» list|[object]|true|none||none| +|»»» id|integer|false|none||none| +|»»» species_code|string|false|none||none| +|»»» chinese_name|string|false|none||none| +|»»» latin_name|string|false|none||none| +|»»» category|string|false|none||none| +|»»» protection_level|string|false|none||none| +|»»» characteristics|string|false|none||none| +|»»» habits|string|false|none||none| +|»»» distribution|string|false|none||none| +|»»» image_urls|[string]|false|none||none| +|»»» status|integer|false|none||none| +|»»» created_at|string|false|none||none| +|»»» updated_at|string|false|none||none| +|»»» created_by|integer|false|none||none| +|»»» updated_by|integer|false|none||none| +|»» pagination|object|true|none||none| +|»»» total|integer|true|none||none| +|»»» page|integer|true|none||none| +|»»» page_size|integer|true|none||none| +|»»» total_pages|integer|true|none||none| + +## GET 获取物种详情 + +GET /api/admin/species/1 + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0, + "species_code": "string", + "chinese_name": "string", + "latin_name": "string", + "category": "string", + "protection_level": "string", + "characteristics": "string", + "habits": "string", + "distribution": "string", + "image_urls": [ + "string" + ], + "status": 0, + "created_at": "string", + "updated_at": "string", + "created_by": 0, + "updated_by": 0 + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| +|»» species_code|string|true|none||none| +|»» chinese_name|string|true|none||none| +|»» latin_name|string|true|none||none| +|»» category|string|true|none||none| +|»» protection_level|string|true|none||none| +|»» characteristics|string|true|none||none| +|»» habits|string|true|none||none| +|»» distribution|string|true|none||none| +|»» image_urls|[string]|true|none||none| +|»» status|integer|true|none||none| +|»» created_at|string|true|none||none| +|»» updated_at|string|true|none||none| +|»» created_by|integer|true|none||none| +|»» updated_by|integer|true|none||none| + +## PUT 更新物种信息 + +PUT /api/admin/species/1 + +> Body 请求参数 + +```json +"{\r\n \"species_code\": \"BIRD001\", // 物种编号,必填,最大50字符\r\n \"chinese_name\": \"东方白鹳1\", // 中文名称,必填,最大100字符\r\n \"latin_name\": \"Ciconia boyciana\", // 拉丁名称,选填,最大100字符\r\n \"category\": \"bird\", // 物种类别,必填,可选值:bird(鸟类)、mammal(哺乳类)、fish(鱼类)、amphibian(两栖类)、reptile(爬行类)、insect(昆虫类)、plant(植物)\r\n \"protection_level\": \"national_first\", // 保护等级,选填,默认normal,可选值:national_first(国家一级)、national_second(国家二级)、provincial(省级)、normal(普通)\r\n \"characteristics\": \"体型大,羽毛白色,喙长而直...\", // 特征描述,选填\r\n \"habits\": \"常在沼泽、湿地等水域觅食...\", // 生活习性,选填\r\n \"distribution\": \"主要分布在东北地区...\", // 分布区域,选填\r\n \"image_urls\": [\"http://example.com/image1.jpg\"], // 图片URL数组,选填\r\n \"status\": 1 // 状态,选填,默认1,可选值:0(禁用)、1(启用)\r\n}" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string" +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| + +## GET 获取物种统计信息 + +GET /api/admin/species/statistics/overview + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "categories": { + "bird": { + "total_count": 0, + "enabled_count": "string" + } + }, + "protection_levels": { + "national_first": 0 + } + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» categories|object|true|none||none| +|»»» bird|object|true|none||none| +|»»»» total_count|integer|true|none||none| +|»»»» enabled_count|string|true|none||none| +|»» protection_levels|object|true|none||none| +|»»» national_first|integer|true|none||none| + +## PUT 更新物种状态 + +PUT /api/admin/species/1/status + +> Body 请求参数 + +```json +"{\r\n \"status\": 0 // 必填,状态值:0(禁用)或1(启用)\r\n}" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string" +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| + +# 满意度调查/管理端 + +## POST 创建调查 + +POST /api/admin/surveys + +> Body 请求参数 + +```json +{ + "title": "2024春季湿地公园环境满意度调查", + "description": "为了持续改善湿地公园环境,提升游客体验,特开展此次调查", + "questions": [ + { + "question_id": 1, + "question_type": "single", + "question_content": "您对湿地公园的整体环境是否满意?", + "options": [ + "非常满意", + "比较满意", + "一般", + "不太满意", + "非常不满意" + ], + "required": true + }, + { + "question_id": 2, + "question_type": "multiple", + "question_content": "您觉得以下哪些设施需要改进?", + "options": [ + "步道", + "座椅", + "标识牌", + "垃圾桶", + "卫生间", + "停车场" + ], + "required": true + }, + { + "question_id": 3, + "question_type": "text", + "question_content": "您对湿地公园环境改善有什么建议?", + "options": [], + "required": false + } + ], + "start_time": "2024-03-20 00:00:00", + "end_time": "2024-04-20 23:59:59", + "status": 1 +} +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0 + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| + +## GET 获取调查列表 + +GET /api/admin/surveys + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "list": [ + { + "id": 0, + "title": "string", + "description": "string", + "questions": [ + "string" + ], + "start_time": "string", + "end_time": "string", + "status": 0, + "created_at": "string", + "updated_at": "string", + "created_by": 0, + "updated_by": 0 + } + ], + "pagination": { + "total": 0, + "page": 0, + "page_size": 0 + } + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» list|[object]|true|none||none| +|»»» id|integer|false|none||none| +|»»» title|string|false|none||none| +|»»» description|string|false|none||none| +|»»» questions|[string]|false|none||none| +|»»» start_time|string|false|none||none| +|»»» end_time|string|false|none||none| +|»»» status|integer|false|none||none| +|»»» created_at|string|false|none||none| +|»»» updated_at|string|false|none||none| +|»»» created_by|integer|false|none||none| +|»»» updated_by|integer|false|none||none| +|»» pagination|object|true|none||none| +|»»» total|integer|true|none||none| +|»»» page|integer|true|none||none| +|»»» page_size|integer|true|none||none| + +## PUT 更新调查 + +PUT /api/admin/surveys/1 + +> Body 请求参数 + +```json +{ + "title": "2024春季湿地公园环境满意度调查", + "description": "为了持续改善湿地公园环境,提升游客体验,特开展此次调查", + "questions": [ + { + "question_id": 1, + "question_type": "single", + "question_content": "您对湿地公园的整体环境是否满意?", + "options": [ + "非常满意", + "比较满意", + "一般", + "不太满意", + "非常不满意" + ], + "required": true + }, + { + "question_id": 2, + "question_type": "multiple", + "question_content": "您觉得以下哪些设施需要改进?", + "options": [ + "步道", + "座椅", + "标识牌", + "垃圾桶", + "卫生间", + "停车场" + ], + "required": true + }, + { + "question_id": 3, + "question_type": "text", + "question_content": "您对湿地公园环境改善有什么建议?", + "options": [], + "required": false + } + ], + "start_time": "2024-03-20 00:00:00", + "end_time": "2025-05-20 23:59:59", + "status": 1 +} +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string" +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| + +## DELETE 删除调查 + +DELETE /api/admin/surveys/1 + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +## GET 获取调查详情 + +GET /api/admin/surveys/1 + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0, + "title": "string", + "description": "string", + "questions": [ + { + "options": [ + "string" + ], + "required": true, + "question_id": 0, + "question_type": "string", + "question_content": "string" + } + ], + "start_time": "string", + "end_time": "string", + "status": 0, + "created_at": "string", + "updated_at": "string", + "created_by": 0, + "updated_by": 0 + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| +|»» title|string|true|none||none| +|»» description|string|true|none||none| +|»» questions|[object]|true|none||none| +|»»» options|[string]|true|none||none| +|»»» required|boolean|true|none||none| +|»»» question_id|integer|true|none||none| +|»»» question_type|string|true|none||none| +|»»» question_content|string|true|none||none| +|»» start_time|string|true|none||none| +|»» end_time|string|true|none||none| +|»» status|integer|true|none||none| +|»» created_at|string|true|none||none| +|»» updated_at|string|true|none||none| +|»» created_by|integer|true|none||none| +|»» updated_by|integer|true|none||none| + +# 满意度调查/客户端 + +## GET 获取进行中的调查列表 + +GET /api/client/surveys + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "list": [ + "string" + ], + "pagination": { + "total": 0, + "page": 0, + "page_size": 0 + } + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» list|[string]|true|none||none| +|»» pagination|object|true|none||none| +|»»» total|integer|true|none||none| +|»»» page|integer|true|none||none| +|»»» page_size|integer|true|none||none| + +## GET 获取调查详情 + +GET /api/client/surveys/1 + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0, + "title": "string", + "description": "string", + "questions": [ + { + "options": [ + "string" + ], + "required": true, + "question_id": 0, + "question_type": "string", + "question_content": "string" + } + ], + "start_time": "string", + "end_time": "string", + "status": 0, + "created_at": "string", + "updated_at": "string", + "created_by": 0, + "updated_by": 0 + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| +|»» title|string|true|none||none| +|»» description|string|true|none||none| +|»» questions|[object]|true|none||none| +|»»» options|[string]|true|none||none| +|»»» required|boolean|true|none||none| +|»»» question_id|integer|true|none||none| +|»»» question_type|string|true|none||none| +|»»» question_content|string|true|none||none| +|»» start_time|string|true|none||none| +|»» end_time|string|true|none||none| +|»» status|integer|true|none||none| +|»» created_at|string|true|none||none| +|»» updated_at|string|true|none||none| +|»» created_by|integer|true|none||none| +|»» updated_by|integer|true|none||none| + +## POST 提交调查答案 + +POST /api/client/surveys/1/submit + +> Body 请求参数 + +```json +{ + "answers": [ + { + "question_id": 1, + "answer": "非常满意" + }, + { + "question_id": 2, + "answer": [ + "步道", + "座椅", + "标识牌" + ] + }, + { + "question_id": 3, + "answer": "建议增加更多遮阳设施,并在主要景点增加饮水点。步道的指示牌可以做得更清晰一些。" + } + ] +} +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0 + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| + +# 巡护计划 + +## POST 创建计划 + +POST /api/admin/patrol/plans + +> Body 请求参数 + +```json +"{\r\n \"plan_name\": \"2025年春季湿地巡护计划\", // 必填,最大100字符\r\n \"plan_type\": \"daily\", // 必填,枚举值:daily(日常巡护)、special(专项巡护)\r\n \"start_date\": \"2024-02-20\", // 必填,ISO日期格式\r\n \"end_date\": \"2024-03-20\", // 必填,必须大于start_date\r\n \"area_scope\": [ // 必填,至少3个点位\r\n {\r\n \"latitude\": 30.5866,\r\n \"longitude\": 114.2995\r\n },\r\n {\r\n \"latitude\": 30.5868,\r\n \"longitude\": 114.3001\r\n },\r\n {\r\n \"latitude\": 30.5862,\r\n \"longitude\": 114.2999\r\n }\r\n ],\r\n \"task_frequency\": \"daily\", // 可选,枚举值:daily、weekly、monthly\r\n \"description\": \"春季重点关注候鸟迁徙情况\", // 可选,最大1000字符\r\n \"status\": 1 // 可选,默认1,0:禁用 1:启用\r\n}" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0 + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| + +## GET 获取计划列表 + +GET /api/admin/patrol/plans + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "list": [ + { + "id": 0, + "plan_name": "string", + "plan_type": "string", + "start_date": "string", + "end_date": "string", + "area_scope": [ + { + "latitude": null, + "longitude": null + } + ], + "task_frequency": "string", + "description": "string", + "status": 0, + "created_at": "string", + "updated_at": "string", + "created_by": 0, + "updated_by": 0 + } + ], + "pagination": { + "total": 0, + "page": 0, + "page_size": 0, + "total_pages": 0 + } + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» list|[object]|true|none||none| +|»»» id|integer|true|none||none| +|»»» plan_name|string|true|none||none| +|»»» plan_type|string|true|none||none| +|»»» start_date|string|true|none||none| +|»»» end_date|string|true|none||none| +|»»» area_scope|[object]|true|none||none| +|»»»» latitude|number|true|none||none| +|»»»» longitude|number|true|none||none| +|»»» task_frequency|string|true|none||none| +|»»» description|string|true|none||none| +|»»» status|integer|true|none||none| +|»»» created_at|string|true|none||none| +|»»» updated_at|string|true|none||none| +|»»» created_by|integer|true|none||none| +|»»» updated_by|integer|true|none||none| +|»» pagination|object|true|none||none| +|»»» total|integer|true|none||none| +|»»» page|integer|true|none||none| +|»»» page_size|integer|true|none||none| +|»»» total_pages|integer|true|none||none| + +## GET 查询巡护计划详情 + +GET /api/admin/patrol/plans/1 + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0, + "plan_name": "string", + "plan_type": "string", + "start_date": "string", + "end_date": "string", + "area_scope": [ + { + "latitude": 0, + "longitude": 0 + } + ], + "task_frequency": "string", + "description": "string", + "status": 0, + "created_at": "string", + "updated_at": "string", + "created_by": 0, + "updated_by": 0 + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| +|»» plan_name|string|true|none||none| +|»» plan_type|string|true|none||none| +|»» start_date|string|true|none||none| +|»» end_date|string|true|none||none| +|»» area_scope|[object]|true|none||none| +|»»» latitude|number|true|none||none| +|»»» longitude|number|true|none||none| +|»» task_frequency|string|true|none||none| +|»» description|string|true|none||none| +|»» status|integer|true|none||none| +|»» created_at|string|true|none||none| +|»» updated_at|string|true|none||none| +|»» created_by|integer|true|none||none| +|»» updated_by|integer|true|none||none| + +## PUT 更新计划状态 + +PUT /api/admin/patrol/plans/1/status + +> Body 请求参数 + +```json +"{\r\n \"status\": 1 // 必填,0:禁用 1:启用\r\n}" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string" +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| + +## POST 检查计划名称 + +POST /api/admin/patrol/plans/check-name + +> Body 请求参数 + +```json +"{\r\n \"plan_name\": \"2025年春季湿地巡护计划\" // 必填\r\n}" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "exists": true + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» exists|boolean|true|none||none| + +# 巡护任务 + +## PUT 更新任务 + +PUT /api/admin/patrol/plans/1 + +> Body 请求参数 + +```json +"{\r\n \"plan_name\": \"巡护计划名称\", // 字符串,最大100个字符\r\n \"plan_type\": \"daily\", // 字符串,可选值: \"daily\"(日常巡护) 或 \"special\"(专项巡护)\r\n \"start_date\": \"2024-02-20\", // 日期,格式:YYYY-MM-DD\r\n \"end_date\": \"2024-03-20\", // 日期,格式:YYYY-MM-DD,必须大于开始日期\r\n \"area_scope\": [ // 数组,至少需要3个坐标点\r\n {\r\n \"latitude\": 30.5, // 纬度,范围:-90到90\r\n \"longitude\": 114.3 // 经度,范围:-180到180\r\n },\r\n // ... 更多坐标点\r\n ],\r\n \"task_frequency\": \"daily\", // 字符串,可选值: \"daily\"(每日)、\"weekly\"(每周)、\"monthly\"(每月)\r\n \"description\": \"巡护计划描述\", // 字符串,最大1000个字符\r\n \"status\": 1 // 数字,可选值: 0(禁用)、1(启用)\r\n}" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string" +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| + +## POST 创建任务 + +POST /api/admin/patrol/tasks + +> Body 请求参数 + +```json +"{\r\n \"plan_id\": 1, // 必填,关联的巡护计划ID\r\n \"task_name\": \"早班巡护任务\", // 必填,最大100字符\r\n \"task_type\": \"regular\", // 必填,枚举值:regular(常规)、emergency(紧急)\r\n \"patrol_date\": \"2024-02-20\", // 必填,巡护日期\r\n \"start_time\": \"08:00\", // 必填,格式HH:mm\r\n \"end_time\": \"10:00\", // 必填,格式HH:mm\r\n \"route_points\": [ // 可选,巡护路线点位\r\n {\r\n \"latitude\": 30.5866,\r\n \"longitude\": 114.2995\r\n }\r\n ],\r\n \"executor_ids\": [1, 2], // 必填,执行人ID列表,至少1人\r\n \"description\": \"重点检查湖区水质情况\", // 可选,最大1000字符\r\n \"priority\": 2, // 可选,默认2,1:高 2:中 3:低\r\n \"status\": 0 // 可选,默认0,0:待执行 1:执行中 2:已完成 3:已取消\r\n}" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0 + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| + +## GET 获取任务列表 + +GET /api/admin/patrol/tasks + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "list": [ + { + "id": 0, + "plan_id": 0, + "task_name": "string", + "task_type": "string", + "patrol_date": "string", + "start_time": "string", + "end_time": "string", + "route_points": [ + { + "latitude": null, + "longitude": null + } + ], + "executor_ids": [ + 0 + ], + "description": "string", + "priority": 0, + "status": 0, + "created_at": "string", + "updated_at": "string", + "created_by": 0, + "updated_by": 0, + "plan_name": "string", + "plan_type": "string" + } + ], + "pagination": { + "total": 0, + "page": 0, + "page_size": 0, + "total_pages": 0 + } + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» list|[object]|true|none||none| +|»»» id|integer|false|none||none| +|»»» plan_id|integer|false|none||none| +|»»» task_name|string|false|none||none| +|»»» task_type|string|false|none||none| +|»»» patrol_date|string|false|none||none| +|»»» start_time|string|false|none||none| +|»»» end_time|string|false|none||none| +|»»» route_points|[object]|false|none||none| +|»»»» latitude|number|false|none||none| +|»»»» longitude|number|false|none||none| +|»»» executor_ids|[integer]|false|none||none| +|»»» description|string|false|none||none| +|»»» priority|integer|false|none||none| +|»»» status|integer|false|none||none| +|»»» created_at|string|false|none||none| +|»»» updated_at|string|false|none||none| +|»»» created_by|integer|false|none||none| +|»»» updated_by|integer|false|none||none| +|»»» plan_name|string|false|none||none| +|»»» plan_type|string|false|none||none| +|»» pagination|object|true|none||none| +|»»» total|integer|true|none||none| +|»»» page|integer|true|none||none| +|»»» page_size|integer|true|none||none| +|»»» total_pages|integer|true|none||none| + +## GET 获取计划详情 + +GET /api/admin/patrol/tasks/1 + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string", + "data": { + "id": 0, + "plan_id": 0, + "task_name": "string", + "task_type": "string", + "patrol_date": "string", + "start_time": "string", + "end_time": "string", + "route_points": [ + { + "latitude": 0, + "longitude": 0 + } + ], + "executor_ids": [ + 0 + ], + "description": "string", + "priority": 0, + "status": 0, + "created_at": "string", + "updated_at": "string", + "created_by": 0, + "updated_by": 0, + "plan_name": "string", + "plan_type": "string" + } +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|integer|true|none||none| +|»» plan_id|integer|true|none||none| +|»» task_name|string|true|none||none| +|»» task_type|string|true|none||none| +|»» patrol_date|string|true|none||none| +|»» start_time|string|true|none||none| +|»» end_time|string|true|none||none| +|»» route_points|[object]|true|none||none| +|»»» latitude|number|false|none||none| +|»»» longitude|number|false|none||none| +|»» executor_ids|[integer]|true|none||none| +|»» description|string|true|none||none| +|»» priority|integer|true|none||none| +|»» status|integer|true|none||none| +|»» created_at|string|true|none||none| +|»» updated_at|string|true|none||none| +|»» created_by|integer|true|none||none| +|»» updated_by|integer|true|none||none| +|»» plan_name|string|true|none||none| +|»» plan_type|string|true|none||none| + +## GET 获取执行人任务 + +GET /api/admin/patrol/tasks/executor/{executorId} + +> Body 请求参数 + +```json +"// 如果要查询ID为15的用户被分配的任务:\r\n// GET /api/admin/patrol/tasks/executor/15" +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|executorId|path|string| 是 |none| +|Authorization|header|string| 否 |none| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +## POST 取消任务 + +POST /api/admin/patrol/tasks/1/cancel + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|Authorization|header|string| 否 |none| + +> 返回示例 + +> 200 Response + +```json +{ + "success": true, + "message": "string" +} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|none|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» success|boolean|true|none||none| +|» message|string|true|none||none| + # 数据模型 diff --git a/src/assets/images/login/bg.jpg b/src/assets/images/login/bg.jpg new file mode 100644 index 0000000..b17c93e Binary files /dev/null and b/src/assets/images/login/bg.jpg differ diff --git a/src/router/index.ts b/src/router/index.ts index 6572ddc..3ab16a5 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,5 +1,6 @@ import { createRouter, createWebHistory } from 'vue-router' import AdminLayout from '../layout/AdminLayout.vue' +import { useUserStore } from '../stores/user' const router = createRouter({ history: createWebHistory(), @@ -13,6 +14,7 @@ const router = createRouter({ path: '/', component: AdminLayout, redirect: '/dashboard', + meta: { requiresAuth: true }, children: [ { path: 'dashboard', @@ -101,9 +103,20 @@ const router = createRouter({ // 路由守卫 router.beforeEach((to, from, next) => { - const token = localStorage.getItem('token') - if (to.path !== '/login' && !token) { - next('/login') + const userStore = useUserStore() + + // 如果访问登录页且已登录,重定向到首页 + if (to.path === '/login' && userStore.isLoggedIn) { + next('/') + return + } + + // 如果需要认证但未登录,重定向到登录页 + if (to.matched.some(record => record.meta.requiresAuth) && !userStore.isLoggedIn) { + next({ + path: '/login', + query: { redirect: to.fullPath } // 保存原目标路径 + }) } else { next() } diff --git a/src/views/login/index.vue b/src/views/login/index.vue index 9813993..9b4fad6 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -2,12 +2,15 @@ import { ref, reactive } from "vue"; import { useRouter } from "vue-router"; import { ElMessage } from "element-plus"; -import { useUserStore } from '../../stores/user'; -import { useSystemLogStore } from '../../stores/systemLog'; +import { useUserStore } from "../../stores/user"; +import { useSystemLogStore } from "../../stores/systemLog"; +import { useRoute } from "vue-router"; +import { User, Lock } from "@element-plus/icons-vue"; const router = useRouter(); const userStore = useUserStore(); const systemLogStore = useSystemLogStore(); +const route = useRoute(); const loginForm = reactive({ username: "", @@ -16,28 +19,27 @@ const loginForm = reactive({ // 表单验证规则 const rules = { - username: [ - { required: true, message: "请输入用户名", trigger: "blur" } - ], - password: [ - { required: true, message: "请输入密码", trigger: "blur" } - ] + username: [{ required: true, message: "请输入用户名", trigger: "blur" }], + password: [{ required: true, message: "请输入密码", trigger: "blur" }], }; const loading = ref(false); const formRef = ref(); +const shake = ref(false); // 获取当前时间 const getCurrentTime = () => { - return new Date().toLocaleString('zh-CN', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false - }).replace(/\//g, '-'); + return new Date() + .toLocaleString("zh-CN", { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hour12: false, + }) + .replace(/\//g, "-"); }; const handleLogin = async (formEl: any) => { @@ -45,7 +47,7 @@ const handleLogin = async (formEl: any) => { loading.value = true; try { await formEl.validate(); - + const success = await userStore.login(loginForm.username, loginForm.password); if (success) { // 记录登录日志 @@ -55,9 +57,12 @@ const handleLogin = async (formEl: any) => { action: "登录系统", ip: "192.168.1.100", status: "成功", - detail: "用户登录成功" + detail: "用户登录成功", }); - router.push("/dashboard"); + + // 获取重定向地址 + const redirect = route.query.redirect as string; + router.push(redirect || "/dashboard"); } else { // 记录失败日志 systemLogStore.addLog({ @@ -66,56 +71,86 @@ const handleLogin = async (formEl: any) => { action: "登录系统", ip: "192.168.1.100", status: "失败", - detail: "用户名或密码错误" + detail: "用户名或密码错误", }); ElMessage.error("用户名或密码错误"); } } catch (error) { - console.error(error); + shake.value = true; + setTimeout(() => { + shake.value = false; + }, 500); } finally { loading.value = false; } }; + +// 添加输入框获得焦点时的处理函数 +const handleFocus = (prop: "username" | "password") => { + if (formRef.value) { + // 清除对应字段的验证错误 + formRef.value.clearValidate(prop); + } +}; @@ -124,35 +159,210 @@ const handleLogin = async (formEl: any) => { .login-container { height: 100vh; + position: relative; + overflow: hidden; +} + +.login-bg { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: url("@/assets/images/login/bg.jpg"); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + + .bg-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.3) 100%); + backdrop-filter: blur(3px); + } +} + +.login-wrapper { + position: relative; + z-index: 1; + height: 100%; display: flex; + flex-direction: column; justify-content: center; +} + +.login-content { + display: flex; + flex-direction: column; align-items: center; - background-color: #f5f7fa; - background-image: linear-gradient(135deg, #f5f7fa 0%, #ecf5ff 100%); + padding: 20px; + margin-bottom: 60px; +} + +.login-header { + text-align: center; + margin-bottom: 40px; + color: white; + + h1 { + font-size: 36px; + font-weight: 600; + margin: 0 0 20px; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + letter-spacing: 2px; + } + + .subtitle { + font-size: 16px; + opacity: 0.9; + margin: 0; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); + letter-spacing: 1px; + } } .login-card { width: 400px; + padding: 30px; border: none; - border-radius: 4px; - box-shadow: $box-shadow; - background: #ffffff; + border-radius: 16px; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(10px); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); + transition: box-shadow 0.3s; + + &:hover { + box-shadow: 0 12px 48px rgba(0, 0, 0, 0.15); + } h2 { text-align: center; margin-bottom: 30px; color: $text-primary; font-weight: 500; + font-size: 24px; } - .el-input { - margin-bottom: 20px; + :deep(.form-item) { + margin-bottom: 24px; + + .el-form-item__error { + padding-top: 4px; + font-size: 12px; + opacity: 0; + transition: opacity 0.3s; + } + + &.is-error .el-form-item__error { + opacity: 1; + } + + &:last-child { + margin-bottom: 0; + } + + .el-input__wrapper { + padding: 8px 16px; + border-radius: 8px 8px 4px 4px; + background: #f0f2f5; + box-shadow: none !important; + border: 2px solid transparent; + border-bottom: 2px solid #a3d0ff; + transition: all 0.3s; + + &:hover { + background: #e8f1ff; + border-bottom-color: #409eff; + } + + &.is-focus { + background: #ffffff; + border-bottom-color: #409eff; + box-shadow: 0 2px 4px rgba(64, 158, 255, 0.15) !important; + } + + .el-input__prefix { + margin-right: 8px; + color: #606266; + } + + .el-input__inner { + color: #303133; + &::placeholder { + color: #606266; + } + } + } } .login-button { width: 100%; - height: 40px; + height: 44px; font-size: 16px; + border-radius: 8px; + font-weight: 500; + letter-spacing: 1px; + transition: all 0.3s; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3); + } + } + + &.shake { + animation: shake 0.5s ease-in-out; + } +} + +.login-footer { + position: absolute; + left: 0; + right: 0; + bottom: 35px; + padding: 12px; + text-align: center; + color: rgba(255, 255, 255, 0.8); + font-size: 13px; +} + +// 响应式设计 +@media (max-width: 768px) { + .login-header { + h1 { + font-size: 28px; + } + .subtitle { + font-size: 14px; + } + } + + .login-card { + width: 90%; + max-width: 400px; + } +} + +@keyframes shake { + 0%, + 100% { + transform: translateX(0); + } + 10%, + 30%, + 50%, + 70%, + 90% { + transform: translateX(-4px); + } + 20%, + 40%, + 60%, + 80% { + transform: translateX(4px); } }