diff --git a/doc/process/weekly/week-10/group/meetingmintues-10.md b/doc/process/weekly/week-10/group/meetingmintues-10.md new file mode 100644 index 0000000..aab7c3f --- /dev/null +++ b/doc/process/weekly/week-10/group/meetingmintues-10.md @@ -0,0 +1,60 @@ +# 小组会议纪要-第10周 + +## 会议记录概要 + +**团队名称:** 1班-汪汪队 +**指导老师:** 肖雄仁 +**主 持 人:** 曹峻茂 +**记录人员:** 张红卫 +**会议主题:** 第10周任务规划与分工 +**会议地点:** 宿舍 +**会议时间:** 2025-11-24 12:30-13:30 +**纪录时间:** 2025-11-24 18:00 +**参与人员:** 曹峻茂、张红卫、罗月航、周竞由、王磊 + +--- + +## 会议内容 + +### 1. 本周总体目标 +完成核心接口开发与前端功能实现,推进前后端联调工作 + +### 2. 后端任务安排 + +**后端2(核心接口扩展)** +- 开发工单管理接口(抢单/拒单/提交维修结果) +- 开发扫码用水接口和水质信息查询接口 +- 更新接口文档 + +**后端1(模拟器优化与联调)** +- 优化数据模拟器,支持手动触发异常数据和断网重连 +- 配合前端进行接口联调 +- 确保模拟数据能正常通过接口返回 + +**后端3(测试与问题修复)** +- 与前端全面对接接口联调工作 +- 协助解决联调过程中发现的问题 + +### 3. 前端任务安排 + +**前端1(Web端开发)** +- 开发设备监控页面功能,实现数据实时展示和异常标红 +- 开发告警列表页面和工单列表页面 +- 与后端联调确保数据正常展示 + +**前端2(APP端开发)** +- 开发学生端扫码模块和水质信息页面 +- 开发维修端工单抢单/拒单功能和工单处理页面 +- 实现学生端扫码用水触发功能 +- 完成核心流程联调 + +### 4. 团队协作要求 +- 加强前后端沟通,每日同步开发进度 +- 及时更新接口文档,确保信息同步 +- 重点关注联调过程中的数据一致性 + +### 5. 预期交付物 +- 扩展后的核心接口代码及文档 +- 优化后的数据模拟器 +- Web端设备监控、告警列表、工单列表功能 +- APP端扫码、水质查看、抢单、用水等核心功能 \ No newline at end of file diff --git a/doc/process/weekly/week-10/group/weekly-plan-10.md b/doc/process/weekly/week-10/group/weekly-plan-10.md new file mode 100644 index 0000000..f6c7c62 --- /dev/null +++ b/doc/process/weekly/week-10/group/weekly-plan-10.md @@ -0,0 +1,36 @@ +# 小组周计划-第10周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-24 +**结束时间:** 2025-11-30 + +## 本周任务计划安排 + + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|----|-------------------------------------------------|----------------|----------------------------| +| 1 | 确定本周计划分工 | 全体组员 | 2023-11-24 开会确定计划以及团队分工 | +| 2 | 继续完善mqtt数据生成和接收及存储 | 曹峻茂 | 完善mqtt报文与接口 | +| 3 | 开发工单管理接口,扫码用水接口,水质信息查询接口 | 王磊 | 完善相关接口 | +| 4 | 开发学生端扫码模块,水质信息页面,维修端工单抢单 / 拒单功能,工单处理页面,扫码用水触发功能 | 罗月航 | 完善app相关页面 | +| 5 | 开发设备监控页面功能,告警列表页面,开发工单列表页面,与后端联调 | 张红卫 | 开发设备监控页面功能,告警列表页面,开发工单列表页面 | +| 6 |开发告警触发逻辑,完成登录接口|周竞由| 开发告警触发逻辑,完成登录接口,与前端对接 | +| 7 | 前后端联调 | 王磊,周竞由,罗月航,张红卫 | 前后端联调核心流程 | +## 小结 + +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员自行学习spring boot相关知识。 +4. **项目管理:** PM及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-10/group/weekly-summary-09.md b/doc/process/weekly/week-10/group/weekly-summary-09.md new file mode 100644 index 0000000..68d051c --- /dev/null +++ b/doc/process/weekly/week-10/group/weekly-summary-09.md @@ -0,0 +1,36 @@ +# 小组周总结-第9周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-17 +**结束时间:** 2025-11-23 + +## 本周任务计划安排 + + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|----|--------------|------|------------------------------| +| 1 | 确定本周计划分工 | 完成 | 2023-11-17 开会确定计划以及团队分工 | +| 2 | 完成mqtt数据生成和接收 | 进行中 | 完成mqtt数据生成和接收功能 | +| 3 | 完成数据库相关基础接口设计和测试 | 进行中 | 完成数据库相关基础接口设计和测试 | +| 4 | 开发维修 / 学生端登录页,完成学生端地图页面布局| 进行中 | 开发维修人员app和学生app登录页,完成学生端地图页面布局 | +|5| 完成Web 端基础框架 + 登录页代码 | 进行中 | 完成管理平台基础框架 + 登录页代码 | +|6|提交迭代开发计划第二稿| 完成 |根据反馈修改迭代开发计划| +## 小结 + +1. **增强自信心:** 小组成员应增强自信心,发挥想象力和创造力,在原型设计环节积极思考。 +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +4. **项目管理:** 原代码框架设计不合理,进行了多次修改 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-10/members/caojunmao-weekly-plan-10.md b/doc/process/weekly/week-10/members/caojunmao-weekly-plan-10.md new file mode 100644 index 0000000..2596cce --- /dev/null +++ b/doc/process/weekly/week-10/members/caojunmao-weekly-plan-10.md @@ -0,0 +1,33 @@ +# 个人周计划-第10周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-24 +**结束时间:** 2025-11-30 + + +## 本周任务计划安排 +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|--------------------|-----|------------------------| +| 1 | 确定分工 | 组员 | 2023-11-24 开会确定计划和团队分工 | +| 2 | 继续完善mqtt数据生成和接收及存储 | 个人 | 优化模拟器,支持手动触发异常数据(如模拟漏水、TDS 超标),增加断网重连功能。 | +|3|管理项目环境| 个人 |管理项目环境,协调开发| + +## 小结 + +1. **学习需求:** 希望能有对于mqtt应用的教学; +2. **知识储备:** 学习后续需要使用的知识,为后续的开发做准备; +3. **文档撰写:** 完成迭代开发计划撰写。 +4. **代码实现** 参与mqtt协议相关设计 +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-10/members/caojunmao-weekly-summary-9.md b/doc/process/weekly/week-10/members/caojunmao-weekly-summary-9.md new file mode 100644 index 0000000..1c2d118 --- /dev/null +++ b/doc/process/weekly/week-10/members/caojunmao-weekly-summary-9.md @@ -0,0 +1,36 @@ +# 个人周总结-第9周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-17 +**结束时间:** 2025-11-23 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|----| ------------------------------------------------ |------|--------------------------------------------------------------| +| 1 | 确定分工 | 完成 | 2023-11-17 开会确定计划和团队分工 | +| 2 | 完成mqtt数据生成和接收 | 进行中 | 完成mqtt数据生成和接收功能 | +| 3 |提交迭代开发计划第二稿| 完成 |根据反馈修改迭代开发计划| + +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + +1. **设计计划文档:** 完成了迭代开发计划第二稿; +2. **项目管理:** 修改了代码框架,符合mvc; +3. **团队协作**:与团队成员保持良好的沟通协作,确保设计方向与产品需求一致 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-10/members/luoyuehang-weekly-plan-10.md b/doc/process/weekly/week-10/members/luoyuehang-weekly-plan-10.md new file mode 100644 index 0000000..811e483 --- /dev/null +++ b/doc/process/weekly/week-10/members/luoyuehang-weekly-plan-10.md @@ -0,0 +1,31 @@ +# 个人周计划-第10周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-12-24 +**结束时间:** 2025-12-30 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | 完成登录页开发收尾 | 个人 | 完善登录表单验证逻辑,完成上周未完成的登录页面开发工作 | +| 2 | 学生端扫码模块开发 | 个人 | 调用摄像头识别二维码,获取设备ID,实现扫码功能 | +| 3 | 水质信息页面开发 | 个人 | 调用水质查询接口,展示水质数据,包括TDS值、水质等级等信息 | +| 4 | 维修端工单抢单/拒单功能 | 个人 | 调用工单管理接口,实现工单抢单和拒单功能 | +| 5 | 工单处理页面开发 | 个人 | 开发维修内容填写页面,支持维修记录提交 | +| 6 | 学生端扫码用水功能 | 个人 | 调用扫码用水接口,实现用水触发功能 | +| 7 | 与后端联调核心流程 | 后端开发 | 完成所有核心功能的接口联调,确保前后端数据交互正常 | +| 8 | 联调报告编写 | 个人 | 整理联调过程中的问题和解决方案,编写联调报告 | + + +## 小结 + +1. **接口依赖**:需要后端提供完整的扫码、水质查询、工单管理、用水触发等接口文档; +2. **设备权限**:需要处理摄像头调用权限,确保扫码功能在移动端的正常使用; +3. **联调协调**:需要与后端开发同学协调联调时间,确保问题及时解决; +4. **测试支持**:需要测试同学协助进行功能测试,特别是移动端兼容性测试; +5. **时间安排**:本周开发任务较重,需要合理安排时间,确保核心功能按时完成。 + diff --git a/doc/process/weekly/week-10/members/luoyuehang-weekly-summary-09.md b/doc/process/weekly/week-10/members/luoyuehang-weekly-summary-09.md new file mode 100644 index 0000000..c431188 --- /dev/null +++ b/doc/process/weekly/week-10/members/luoyuehang-weekly-summary-09.md @@ -0,0 +1,30 @@ +# 个人周总结-第9周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-17 +**结束时间:** 2025-11-23 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | -------- | -------- | -------- | +| 1 | 开发维修/学生端登录页 | 部分完成 | 已完成登录页面的基础布局和样式设计,实现了用户类型切换功能 | +| 2 | 实现登录表单验证 | 部分完成 | 完成了基础的表单结构搭建,验证逻辑正在开发中 | +| 3 | 学生端地图页面布局 | 部分完成 | 完成了地图页面的整体框架布局和主要功能区域划分 | + +## 对团队工作的建议 + +1. **API文档完善**:建议后端团队尽快提供完整的地图数据接口文档; +2. **设计规范统一**:需要建立统一的设计组件库,提高开发效率; +3. **代码审查机制**:建议建立代码审查流程,确保代码质量; +4. **进度同步**:建议每周进行开发进度同步,及时解决遇到的问题。 + +## 小结 + +1. **基础框架搭建**:成功搭建了登录页和地图页面的基础框架结构; +2. **界面效果实现**:登录页面的视觉效果基本达到设计预期,用户体验良好; +3. **问题发现**:在地图API集成过程中遇到了一些技术难点,需要进一步研究解决; +4. **进度评估**:整体进度符合预期,但部分功能需要延至下周完成; diff --git a/doc/process/weekly/week-10/members/wanglei-weekly-plan-10.md b/doc/process/weekly/week-10/members/wanglei-weekly-plan-10.md new file mode 100644 index 0000000..be37559 --- /dev/null +++ b/doc/process/weekly/week-10/members/wanglei-weekly-plan-10.md @@ -0,0 +1,26 @@ +# 个人周计划-第10周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2023-11-24 +**结束时间:** 2023-11-30 + + +## 本周任务计划安排 + +|序号| 计划内容| 协作人 | 情况说明 | +| ---- | ------ | ------| ------- | +| 1 | 确定分工 | 组员 | 2025-11-24 开会细分确定团队分工, | +| 2 | 开发工单管理接口、扫码用水接口、水质信息查询接口 | 个人 | 工单管理接口:包括抢单、拒单、提交维修结果等功能接;扫码用水接口:接收APP扫码请求,返回出水状态口;水质信息查询接口:据设备ID返回最新TDS值等水质信息 | + + +## 小结 + +1. **技术重点:** 基于完善后的数据库设计,扩展工单管理、扫码用水、水质查询等核心接口 +2. **协作重点:** 确保接口设计与前端需求一致,接口定义清晰可调用 +3. **学习重点:** 继续学习数据库优化和系统架构知识,提升接口开发效率 + +--- + diff --git a/doc/process/weekly/week-10/members/wanglei-weekly-summary-09.md b/doc/process/weekly/week-10/members/wanglei-weekly-summary-09.md new file mode 100644 index 0000000..f525564 --- /dev/null +++ b/doc/process/weekly/week-10/members/wanglei-weekly-summary-09.md @@ -0,0 +1,34 @@ + +# 个人周总结-第9周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-17 +**结束时间:** 2025-11-23 + +## 本周任务完成情况 + +序号 总结内容 是否完成 情况说明 +1 修改完善数据库 完成 根据优化后的数据库设计,完成了新增表结构与核心接口的修改与完善。 +2 根据新的项目目录结构来重新分类放置代码 完成 完成了新的项目框架的代码的存放。 + + + +## 对团队工作的建议 + +1. **任务跟进机制** 建议建立每周任务进度同步机制,便于及时调整与支持 +2. **接口联调准备** 提前准备接口文档,便于前后端协同开发与测试 + +## 小结 + + +1.技术收获:通过修改数据库结构与新增接口,进一步掌握了数据库设计与系统接口的协同开发流程。 + +2.协作收获:分工明确后团队协作效率有所提升,任务推进更加有序。 + +3.后续重点:继续配合前后端开发,参与系统联调,确保数据接口与业务逻辑的一致性。 + +--- + diff --git a/doc/process/weekly/week-10/members/zhanghongwei-weekly-plan-10.md b/doc/process/weekly/week-10/members/zhanghongwei-weekly-plan-10.md new file mode 100644 index 0000000..6992cfd --- /dev/null +++ b/doc/process/weekly/week-10/members/zhanghongwei-weekly-plan-10.md @@ -0,0 +1,36 @@ +# 个人周计划-第10周 + +## 姓名和起止时间 + +**姓  名:** 张红卫 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-24 +**结束时间:** 2025-11-30 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 执行人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | 开发设备监控页面功能 | 个人 | 调用设备数据查询接口,实现数据实时展示和异常标红 | +| 2 | 开发告警列表页面 | 个人 | 调用告警查询接口,展示异常信息 | +| 3 | 开发工单列表页面 | 个人 | 调用工单查询接口,展示工单状态和设备信息 | +| 4 | 前后端接口联调 | 个人+后端 | 与后端同学配合完成设备监控、告警、工单功能的联调测试 | + +## 技术学习重点 + +1. **数据可视化**:学习实时数据展示和异常状态标识的实现方式 +2. **接口联调**:掌握多接口协同调用和数据一致性处理 +3. **错误处理**:完善前端错误处理和用户提示机制 + +## 交付物清单 + +- [ ] 设备监控页面功能代码 +- [ ] 告警列表页面功能代码 +- [ ] 工单列表页面功能代码 +- [ ] 接口联调测试报告 + +## 小结 + +1. **技术需求**:需要了解大数据量实时展示的性能优化方案; +2. **协作需求**:依赖后端同学提供稳定的设备数据、告警、工单查询接口; +3. **进度风险**:关注接口开发进度,如遇延迟需调整功能开发顺序。 \ No newline at end of file diff --git a/doc/process/weekly/week-10/members/zhanghongwei-weekoly-summary-9.md b/doc/process/weekly/week-10/members/zhanghongwei-weekoly-summary-9.md new file mode 100644 index 0000000..c233220 --- /dev/null +++ b/doc/process/weekly/week-10/members/zhanghongwei-weekoly-summary-9.md @@ -0,0 +1,28 @@ +# 个人周总结-第9周 + +## 基本信息 +**姓  名:** 张红卫 +**团队名称:** 软1-汪汪队 +**总结周期:** 2025年11月17日 - 2025年11月23日 + +## 本周任务完成情况 + +### 已完成任务 +| 序号 | 任务内容 | 完成状态 | 成果输出 | +|------|----------|----------|----------| +| 1 | 用户管理页面原型转换 | 已完成 | 完成用户管理页面原型到实际页面的转换 | +| 2 | Web端框架搭建 | 进行中 | 已选择前端框架,正在进行开发环境配置和基础项目结构搭建 | +| 3 | 多角色登录页开发 | 进行中 | 登录功能尚未实现 | +| 4 | 接口联调测试 | 未开始 | 等待后端接口准备 | + +## 主要成果与进展 + +1. **技术实践方面** + - 完成了用户管理页面从原型到实际页面的转换 + - 初步搭建了Web端基础框架 + +2. **学习进展方面** + - 进一步熟悉了所选前端框架的基本使用方法 + +## 总结 +本周按计划完成了用户管理页面原型的转换工作,但登录功能的开发尚未开始。后续需要加快Web端框架的搭建进度,并等待后端接口准备完成后,尽快开展登录功能的开发和接口联调工作。 \ No newline at end of file diff --git a/doc/process/weekly/week-10/members/zhoujingyou-weekly-plan-10.md b/doc/process/weekly/week-10/members/zhoujingyou-weekly-plan-10.md new file mode 100644 index 0000000..1524399 --- /dev/null +++ b/doc/process/weekly/week-10/members/zhoujingyou-weekly-plan-10.md @@ -0,0 +1,25 @@ +# 个人周计划-第10周 + +## 姓名和起止时间 +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-24 +**结束时间:** 2025-11-30 +**核心职责:** 接口测试执行 + 新接口用例编写 + 后端联调支持 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|--------------|---------|-----------------------------------| +| 1 | 登录/告警接口缺陷复测 | 后端2 | 对上周记录的全部缺陷进行复测,补充异常参数与 Token 失效场景 | +| 2 | 设计用户信息接口测试用例 | 后端2 | 覆盖权限控制、角色校验、非法入参、空数据返回等核心场景 | +| 3 | 设计设备信息接口测试用例 | 后端1/后端2 | 包含设备状态、设备ID校验、数据字段完整性、异常处理等测试 | +| 4 | 接口联调与逻辑验证支持 | 后端2 | 实际执行新增用例,记录缺陷并及时与后端确认与复现 | +| 5 | 测试文档整理与版本更新 | 组员/指导老师 | 输出 V1.0 文档,补全用户&设备接口说明,提交规范化测试文档 | + +## 小结 + +1. **任务目标:** 完成登录、告警及新接口(用户信息/设备信息)的完整测试链路,确保逻辑正确、返回一致且异常场景可控。 +2. **能力提升:** 深入练习接口测试设计方法,进一步掌握 Postman Collection 管理和脚本使用技巧。 +3. **协作重点:** 与后端保持每日同步,及时跟进接口变更与缺陷修复,确保联调顺畅。 +4. **下周展望:** 根据接口开发进度,逐步尝试自动化测试脚本编写,并准备进入前后端联合联调阶段。 diff --git a/doc/process/weekly/week-10/members/zhoujingyou-weekly-summary-09.md b/doc/process/weekly/week-10/members/zhoujingyou-weekly-summary-09.md new file mode 100644 index 0000000..65bd7f1 --- /dev/null +++ b/doc/process/weekly/week-10/members/zhoujingyou-weekly-summary-09.md @@ -0,0 +1,41 @@ +# 个人周总结-第9周 + +## 姓名和起止时间 +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-17 +**结束时间:** 2025-11-23 +**核心职责:** 接口测试用例编写 + 后端2联调支持 + +## 本周任务完成情况 + +| 序号 | 计划内容 | 完成情况 | 说明 | +|----|---------------|------|---------------------------------------| +| 1 | 对接需求与接口规范确认 | 部分完成 | 与后端2及产品负责人完成登录/告警接口的业务确认,明确入参、出参及异常定义 | +| 2 | 编写用户登录接口测试用例 | 部分完成 | 完成12个场景覆盖,包括多角色登录、账号密码错误、异常入参、账号锁定等情况 | +| 3 | 编写告警生成接口测试用例 | 部分完成 | 完成自动告警、手动触发、设备离线、数据异常等场景的测试用例编写 | +| 4 | 协助接口逻辑验证与问题定位 | 部分完成 | 执行全部用例,发现并记录若干逻辑缺陷,协助后端定位与复现问题 | +| 5 | 提交测试用例与联调报告 | 已完成 | 按要求提交《测试用例文档》和《联调问题记录》,根据反馈完成补充与修订 | + +## 本周总结 + +1. **阶段成果:** + 本周顺利完成登录接口和告警接口的测试用例设计与执行工作,测试覆盖率较高,识别了多项接口边界与逻辑性问题,为后端联调提供了可靠参考。 + +2. **技术提升:** + - 熟悉 Postman 的基本使用流程、参数管理与环境变量; + - 学会将等价类划分、边界值分析等方法应用于接口用例设计; + - 进一步理解了接口业务流程与异常场景处理机制。 + +3. **存在问题:** + - 对部分复杂业务场景理解不够深入,需要更多与后端沟通确认; + - 部分测试用例描述不够精简,后续需进一步规范化; + - 接口自动化测试经验不足,仍需学习脚本化测试流程。 + +4. **改进方向:** + - 下周继续补充高风险场景与极端数据的测试; + - 尝试学习 Postman 的测试脚本与自动化能力; + - 优化文档结构,使缺陷记录更清晰、可复现性更强。 + +5. **工作体会:** + 本周在与后端的联调合作中,逐渐熟悉接口测试的流程与规范,也意识到提前明确接口逻辑的重要性。通过大量实际测试,理解了如何将业务需求转化为可验证的测试场景,对整体开发流程有了更深入的认识。 diff --git a/doc/process/weekly/week-11/group/meetingmintues-11.md b/doc/process/weekly/week-11/group/meetingmintues-11.md new file mode 100644 index 0000000..9b7c733 --- /dev/null +++ b/doc/process/weekly/week-11/group/meetingmintues-11.md @@ -0,0 +1,86 @@ +# 小组会议纪要-第11周 + +## 会议记录概要 + +**团队名称:** 软1-汪汪队 +**指导老师:** 肖雄仁 +**主 持 人:** 曹峻茂 +**记录人员:** 张红卫 +**会议主题:** 第11周任务规划与分工 +**会议地点:** 宿舍 +**会议时间:** 2025-12-1 12:30-13:30 +**纪录时间:** 2025-12-1 18:00 +**参与人员:** 曹峻茂、张红卫、罗月航、周竞由、王磊 + +--- + +## 会议内容 + +### 1. 本周总体目标 +推进项目收尾阶段工作,完成模拟器优化、核心接口完善、前后端功能模块联调与代码优化,提升系统整体稳定性和用户体验。 + +### 2. 后端任务安排 + +**后端1(模拟器与MQTT接收端优化)** +- 负责人:曹峻茂 +- 内容: + - 对模拟器进行最终优化,提升数据生成稳定性和真实性 + - 增强MQTT接收端功能,提高数据传输的可靠性与性能 + - 配合联调测试,确保模拟数据正常接入 + +**后端2(数据统计与设备状态接口开发)** +- 负责人:王磊 +- 内容: + - 开发数据统计接口:支持按设备、地区、时间统计用水量、告警次数 + - 开发设备状态管理接口:实现在线/离线状态标记与查询 + - 完善接口文档,确保接口规范清晰 + +**后端3(权限控制与告警推送功能开发)** +- 负责人:周竞由 +- 内容: + - 实现权限控制代码,确保系统操作的安全性 + - 开发告警推送功能,支持实时告警通知 + - 协助前后端联调,解决权限与推送相关问题 + +### 3. 前端任务安排 + +**前端1(APP端功能优化与页面开发)** +- 负责人:罗月航 +- 内容: + - 完成APP端用水记录页面的代码编写与交互优化 + - 完成APP端工单详情页面的代码编写与交互优化 + - 配合后端联调,确保数据正常展示与交互流畅 + +**前端2(Web端功能开发与优化)** +- 负责人:张红卫 +- 内容: + - 完成Web端统计页面的代码编写与交互优化 + - 完成Web端设备管理页面的代码编写与交互优化 + - 与后端配合完成数据展示与操作逻辑的实现 + +### 4. 联调与整合任务 + +**前后端联调** +- 参与人员:王磊、周竞由、罗月航、张红卫 +- 内容: + - 进行系统整体联调,重点验证核心业务流程 + - 测试数据统计、设备管理、权限控制、告警推送等模块的协同工作 + - 记录并解决联调过程中发现的问题 + +### 5. 团队协作要求 +- 每日进行简短进度同步,及时沟通开发进展与阻塞问题 +- 如遇技术难题,及时召开临时会议或向指导老师、研究生学长求助 +- 保持接口文档的及时更新,确保前后端信息一致 +- 重点关注系统性能与用户体验,确保交付质量 + +### 6. 预期交付物 +- 优化后的模拟器与MQTT接收端代码 +- 数据统计与设备状态管理接口代码及文档 +- 权限控制与告警推送功能代码 +- APP端用水记录、工单详情页面代码 +- Web端统计页面、设备管理页面代码 +- 前后端联调测试报告与问题记录 + +### 7. 支持需求 +- 希望获得关于MQTT大规模数据传输性能优化的实战讲座或技术分享 +- 期待指导老师在权限管理、接口性能优化方面给予进一步指导 \ No newline at end of file diff --git a/doc/process/weekly/week-11/group/week-plan-11.md b/doc/process/weekly/week-11/group/week-plan-11.md new file mode 100644 index 0000000..c2d1af8 --- /dev/null +++ b/doc/process/weekly/week-11/group/week-plan-11.md @@ -0,0 +1,36 @@ +# 小组周计划-第11周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-12-1 +**结束时间:** 2025-12-7 + +## 本周任务计划安排 + + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|----|--------------------------------------------------------|----------------|-------------------------------| +| 1 | 确定本周计划分工 | 全体组员 | 2023-12-1 开会确定计划以及团队分工 | +| 2 | 模拟器最终优化, MQTT 接收端增强 | 曹峻茂 | 模拟器最终优化, MQTT 接收端增强 | +| 3 | 开发数据统计接口(按设备 / 地区 / 时间统计用水量、告警次数)、设备状态管理接口(在线 / 离线标记); | 王磊 | 完善相关接口 | +| 4 | 完成APP 端用水记录 / 工单详情页面代码、交互优化代码 | 罗月航 | 完成APP 端用水记录 / 工单详情页面代码、交互优化代码 | +| 5 | 完成 Web 端统计页面 / 设备管理页面代码、交互优化代码 | 张红卫 | Web 端统计页面 / 设备管理页面代码、交互优化代码 | +| 6 | 权限控制代码、告警推送功能代码 |周竞由| 完成权限控制代码、告警推送功能代码 | +| 7 | 前后端联调 | 王磊,周竞由,罗月航,张红卫 | 前后端联调核心流程 | +## 小结 + +1. **沟通协作:** 每日简短同步进度,遇到阻塞问题及时召开临时会议协商解决,可向指导老师及研究生学长寻求技术支持。 +2. **学习安排:** 结合项目需求深入学习权限管理和接口性能优化相关知识,分享学习笔记。 +3. **项目管理:** PM 每日跟踪任务进度,记录问题并推动解决,确保按计划推进。 +4. **帮助需求:** 希望能有关于 mqtt 大规模数据传输性能优化的实战讲座,助力解决数据传输稳定性问题。 +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-11/group/week-summary-10.md b/doc/process/weekly/week-11/group/week-summary-10.md new file mode 100644 index 0000000..5610891 --- /dev/null +++ b/doc/process/weekly/week-11/group/week-summary-10.md @@ -0,0 +1,37 @@ +# 小组周总结-第10周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-24 +**结束时间:** 2025-11-30 + +## 本周任务计划安排 + + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|----|--------------|------|----------------------------| +| 1 | 确定本周计划分工 | 完成 | 2023-11-24 开会确定计划以及团队分工 | +| 2 | 继续完善mqtt数据生成和接收及存储 | 完成 | 完善mqtt报文与接口 | +| 3 | 开发工单管理接口,扫码用水接口,水质信息查询接口 | 完成 | 完善相关接口 | +| 4 | 开发学生端扫码模块,水质信息页面,维修端工单抢单 / 拒单功能,工单处理页面,扫码用水触发功能 | 进行中 | 完善app相关页面 | +| 5 | 开发设备监控页面功能,告警列表页面,开发工单列表页面,与后端联调 | 进行中 | 开发设备监控页面功能,告警列表页面,开发工单列表页面 | +| 6 |开发告警触发逻辑,完成登录接口| 进行中 | 开发告警触发逻辑,完成登录接口,与前端对接 | +| 7 | 前后端联调 | 进行中 | 前后端联调核心流程 | +## 小结 + +1. **增强自信心:** 小组成员应增强自信心,发挥想象力和创造力,在原型设计环节积极思考。 +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +4. **项目管理:** 代码框架、依赖版本和jdk版本管理混乱导致浪费大量时间,需要改进。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-11/members/caojunmao-weekly-plan-11.md b/doc/process/weekly/week-11/members/caojunmao-weekly-plan-11.md new file mode 100644 index 0000000..a54f06a --- /dev/null +++ b/doc/process/weekly/week-11/members/caojunmao-weekly-plan-11.md @@ -0,0 +1,33 @@ +# 个人周计划-第11周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-12-1 +**结束时间:** 2025-12-7 + + +## 本周任务计划安排 +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|--------------------|-----|--------------------------------| +| 1 | 确定分工 | 组员 | 2023-12-1 开会确定计划和团队分工 | +| 2 | 模拟器最终优化, MQTT 接收端增强 | 个人 | 模拟器最终优化, MQTT 接收端增强 | +|3|管理项目环境| 个人 | 管理项目环境,协调开发 | + +## 小结 + +1. **学习需求:** 希望能有对于mqtt应用的教学; +2. **知识储备:** 学习后续需要使用的知识,为后续的开发做准备; +3. **文档撰写:** 完成迭代开发计划撰写。 +4. **项目管理** 管理项目环境和框架 +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-11/members/caojunmao-weekly-summary-10.md b/doc/process/weekly/week-11/members/caojunmao-weekly-summary-10.md new file mode 100644 index 0000000..011d63b --- /dev/null +++ b/doc/process/weekly/week-11/members/caojunmao-weekly-summary-10.md @@ -0,0 +1,36 @@ +# 个人周总结-第10周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-24 +**结束时间:** 2025-11-30 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|----| ------------------------------------------------ |------|--------------------------------------------------------------| +| 1 | 确定分工 | 完成 | 2023-11-24 开会确定计划和团队分工 | +| 2 | 继续完善mqtt数据生成和接收及存储 | 进行中 | 优化模拟器,支持手动触发异常数据(如模拟漏水、TDS 超标),增加断网重连功能。 | +|3|管理项目环境|进行中|管理项目环境,协调开发| + +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + +1. **设计计划文档:** 完成了迭代开发计划第二稿; +2. **项目管理:** 修改了代码框架,符合mvc; +3. **团队协作**:与团队成员保持良好的沟通协作,确保设计方向与产品需求一致 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-11/members/luoyuehang-weekly-plan-11.md b/doc/process/weekly/week-11/members/luoyuehang-weekly-plan-11.md new file mode 100644 index 0000000..52dedc5 --- /dev/null +++ b/doc/process/weekly/week-11/members/luoyuehang-weekly-plan-11.md @@ -0,0 +1,32 @@ +# 个人周计划-第11周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-12-01 +**结束时间:** 2025-12-07 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | 开发学生端用水记录页面 | 个人 | 实现个人用水量统计、水质历史数据查询、图表展示功能,包括日/周/月视图切换 | +| 2 | 开发维修端工单处理详情页面 | 个人 | 实现维修照片上传、处理结果填写、工单状态更新、维修记录查看功能 | +| 3 | 消息推送交互优化 | 个人 | 实现告警和工单提醒的消息推送展示、消息列表、已读未读状态管理 | +| 4 | 扫码用水结果反馈优化 | 个人 | 优化扫码用水成功/失败的弹窗提示、错误原因展示、用户引导流程 | +| 5 | 离线缓存功能实现 | 个人 | 实现无网络时查看历史数据的缓存机制,包括数据同步和冲突解决 | +| 6 | 扫码接口联调修复 | 后端开发 | 配合后端修复扫码接口的问题,确保扫码功能稳定可靠 | +| 7 | 工单操作接口联调 | 后端开发 | 调试工单抢单、处理、完成等操作的接口,确保业务流程顺畅 | +| 8 | 消息推送接口联调 | 后端开发 | 调试实时消息推送的WebSocket连接,确保消息实时性和稳定性 | + + +## 小结 + +1. **接口支持**:需要后端提供完整的用水记录、工单详情、消息推送等接口文档; +2. **测试数据**:需要真实的历史数据用于开发和测试用水记录页面; +3. **性能考虑**:离线缓存需要考虑数据量大小和同步策略; +4. **用户体验**:消息推送需要考虑不同场景下的通知方式和用户打扰程度。 + + + diff --git a/doc/process/weekly/week-11/members/luoyuehang-weekly-summary-10.md b/doc/process/weekly/week-11/members/luoyuehang-weekly-summary-10.md new file mode 100644 index 0000000..4db7a06 --- /dev/null +++ b/doc/process/weekly/week-11/members/luoyuehang-weekly-summary-10.md @@ -0,0 +1,36 @@ +# 个人周总结-第10周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-12-24 +**结束时间:** 2025-12-30 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | -------- | -------- | -------- | +| 1 | 完成登录页开发收尾 | 完成 | 完善了登录表单验证逻辑,完成了登录页面的开发工作 | +| 2 | 登录接口对接 | 完成 | 成功对接后端登录接口,实现了真实的用户认证功能 | +| 3 | 学生端扫码模块开发 | 进行中 | 已完成扫码模块的基础框架,正在集成摄像头调用功能 | +| 4 | 水质信息页面开发 | 进行中 | 已完成页面布局设计,正在对接水质查询接口 | +| 5 | 维修端工单抢单/拒单功能 | 进行中 | 受接口开发进度影响,此功能暂未开始 | +| 6 | 工单处理页面开发 | 进行中 | 需要工单抢单功能完成后进行 | +| 7 | 学生端扫码用水功能 | 进行中 | 需要扫码模块完成后进行 | +| 8 | 与后端联调核心流程 | 部分完成 | 已完成登录功能的联调,其他功能接口正在对接中 | + +## 对团队工作的建议 + +1. **进度同步机制**:建议建立更频繁的前后端进度同步机制,及时解决阻塞问题; +2. **测试环境准备**:需要准备稳定的测试环境,便于前后端联调; +3. **代码规范统一**:在接口对接过程中发现需要统一错误处理和数据格式规范。 + +## 小结 + +1. **核心功能完成**:成功完成了登录功能的完整对接,为后续功能开发奠定了基础; +2. **接口对接经验**:通过登录接口的对接,积累了前后端联调的实际经验; +3. **进度评估**:虽然部分功能因接口依赖而滞后,但整体进度仍在可控范围内; +4. **协作改进**:认识到需要更紧密的前后端协作,计划建立每日站会机制; + + diff --git a/doc/process/weekly/week-11/members/wanglei-weekly-plan-11.md b/doc/process/weekly/week-11/members/wanglei-weekly-plan-11.md new file mode 100644 index 0000000..cdb3483 --- /dev/null +++ b/doc/process/weekly/week-11/members/wanglei-weekly-plan-11.md @@ -0,0 +1,25 @@ +# 个人周计划-第11周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2023-12-1 +**结束时间:** 2023-12-7 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +| --- | ----- | --- | ----------------------------------------------------- | +| 1 | 确定分工 | 组员 | 2025-12-1 开会细分确定团队分工, | +| 2 | 接口完善 | 个人 | 开发数据统计接口(按设备 / 地区 / 时间统计用水量、告警次数)、设备状态管理接口(在线 / 离线标记) | +| 3 | 数据层优化 | 个人 | 传感器历史数据分页查询、大字段索引调整、查询性能优化(响应时间≤300ms) | +| 4 | 联调支持 | 个人 | 配合前端修复接口参数、返回格式、权限校验问题 | + +## 小结 + +1. **技术重点:** 多维度统计接口设计与实现;大数据量分页查询性能优化;设备状态实时管理机制 +2. **协作重点:** 明确统计数据结构便于前端展示;统一接口规范,完善文档;建立联调问题快速响应机制 +3. **学习重点:** 掌握分页查询、索引设计的性能优化技巧;学习多维度聚合统计的高效实现方案;提升前后端接口对接的问题解决能力;学习接口性能分析和调优方法 + +--- diff --git a/doc/process/weekly/week-11/members/wanglei-weekly-summary-10.md b/doc/process/weekly/week-11/members/wanglei-weekly-summary-10.md new file mode 100644 index 0000000..49da25e --- /dev/null +++ b/doc/process/weekly/week-11/members/wanglei-weekly-summary-10.md @@ -0,0 +1,40 @@ +# 个人周计划-第十周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-24 + +**结束时间:** 2025-11-30 + +## 本周任务计划安排 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| --- | ---------- | ------------------------------ | --------------------------------------------------------------------------------------------------- | +| 1 | 开发工单管理接口 | 完成 | 已实现完整的工单管理接口,包括:抢单接口(grabOrder);拒单接口(rejectOrder);提交维修结果接口(submitRepairResult);获取可抢工单列表接口 ;获取我的工单接口 | +| 2 | 开发扫码用水接口 | 完成 | 已实现扫码用水全流程接口:扫码获取终端信息接口;扫码用水记录接口;自动更新终端使用统计数据 | +| 3 | 开发水质信息查询接口 | 完成 | 已实现水质信息查询接口:根据设备ID返回最新TDS值(原水、纯水、矿化水);返回水质等级、滤芯寿命、设备状态等完整信息 | + +## 对团队工作的建议 + +1.**接口联调协作** 建议建立接口联调检查清单,确保前后端数据格式一致 + +2.**代码审查机制** 建议定期进行代码审查,提升代码质量和团队协作效率 + +3.**测试环境准备** 建议尽早搭建测试环境,便于接口功能验证 + +## 小结 + +1. **技术收获** 掌握了完整的工单状态流转的业务逻辑实现,熟悉了扫码用水业务的数据处理流程 +2. **协作收获** 通过明确分工,个人任务执行更加专注高效,与团队成员就接口设计进行充分沟通,确保前后端对接顺畅 +3. **后续重点** 配合前端进行接口联调和测试验证;根据实际使用反馈优化接口设计 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; diff --git a/doc/process/weekly/week-11/members/zhanghongwei-weekly-plan-11.md b/doc/process/weekly/week-11/members/zhanghongwei-weekly-plan-11.md new file mode 100644 index 0000000..2735480 --- /dev/null +++ b/doc/process/weekly/week-11/members/zhanghongwei-weekly-plan-11.md @@ -0,0 +1,37 @@ +# 个人周计划-第11周 + +## 姓名和起止时间 + +**姓  名:** 张红卫 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-12-1 +**结束时间:** 2025-12-7 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 执行人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | 完成 Web 端统计页面功能开发与交互优化 | 个人 | 实现用水量、告警次数等数据的图表化展示,支持按设备/地区/时间筛选 | +| 2 | 完成 Web 端设备管理页面功能开发与交互优化 | 个人 | 实现设备增删改查、状态标记、批量操作等功能,优化操作流程 | +| 3 | 参与前后端联调工作 | 个人 + 后端 | 与后端(王磊、周竞由等)协作,完成统计与设备管理模块的接口联调与测试 | +| 4 | 性能优化与异常处理完善 | 个人 | 针对大数据展示场景优化渲染性能,完善导航栏异常跳转与权限控制逻辑 | + +## 技术学习重点 + +1. **数据可视化与图表集成**:学习使用图表库(如 ECharts)实现统计数据的可视化展示 +2. **复杂表单与批量操作**:掌握设备管理中复杂表单验证与批量操作的实现方式 +3. **性能优化实战**:进一步探索虚拟滚动、分页加载、接口缓存等前端性能优化方案 +4. **权限控制集成**:配合后端权限接口,实现前端路由与操作权限的控制逻辑 + +## 交付物清单 + +- [ ] Web 端统计页面功能代码(含图表展示与筛选交互) +- [ ] Web 端设备管理页面功能代码(含增删改查与状态管理) +- [ ] 前后端联调测试记录与问题清单 +- [ ] 性能优化与权限控制相关代码更新 + +## 小结 + +1. **技术需求**:需进一步掌握大数据量前端展示的性能调优方案,尤其是虚拟滚动与分页加载的实现; +2. **协作需求**:依赖后端提供稳定的数据统计接口、设备状态管理接口及权限控制接口; +3. **进度风险**:若接口开发延迟或权限逻辑不明确,可能影响前端页面功能的完整实现。 \ No newline at end of file diff --git a/doc/process/weekly/week-11/members/zhanghongwei-weekly-summary-10.md b/doc/process/weekly/week-11/members/zhanghongwei-weekly-summary-10.md new file mode 100644 index 0000000..b3f75fd --- /dev/null +++ b/doc/process/weekly/week-11/members/zhanghongwei-weekly-summary-10.md @@ -0,0 +1,41 @@ +# 个人周总结-第10周 + +## 姓名和起止时间 +**姓  名:** 张红卫 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-24 +**结束时间:** 2025-11-30 +**核心职责:** 前端界面开发 + 前后端联调支持 + +## 本周任务完成情况 + +| 序号 | 计划内容 | 完成情况 | 说明 | +|------|------------------------|----------|------| +| 1 | 开发设备监控页面功能 | 进行中 | 成功调用设备数据查询接口,实现数据实时展示与异常状态标红功能 | +| 2 | 开发告警列表页面 | 进行中 | 完成告警查询接口对接,支持告警信息列表展示与状态筛选 | +| 3 | 开发工单列表页面 | 进行中 | 调用工单查询接口,实现工单状态、设备信息的可视化展示 | +| 4 | 前后端接口联调 | 进行中 | 与后端合作完成设备监控、告警、工单等功能的接口联调与测试 | +| 5 | 登录界面接口对接与导航栏跳转 | 已完成 | 完成登录接口的对接实现,并优化导航栏路由跳转逻辑,提升用户体验 | + +## 本周总结 + +1. **阶段成果:** + 本周设备监控、告警列表、工单列表三个页面的开发还在进行中,登录界面接口对接与导航栏跳转功能也已实现。 + +2. **技术提升:** + - 掌握了前端实时数据展示与异常标识的实现方法; + - 熟悉了多接口协同调用与数据一致性处理技巧; + - 提升了前端错误处理与用户提示机制的设计能力。 + +3. **存在问题:** + - 大数据量实时展示时出现轻微卡顿,需进一步优化渲染性能; + - 部分接口响应时间较长,影响页面加载体验; + - 导航栏跳转逻辑在异常情况下处理不够完善。 + +4. **改进方向:** + - 下周针对大数据展示场景进行性能优化,如虚拟滚动、分页加载等; + - 推动后端优化接口响应速度,必要时引入缓存机制; + - 完善导航栏异常跳转与权限控制逻辑。 + +5. **工作体会:** + 本周在前端界面开发与接口联调过程中,进一步理解了前后端协作的流程与关键点。通过实际开发与调试,提升了解决实际问题的能力,尤其在用户界面与交互逻辑方面积累了宝贵经验。同时,认识到性能优化与异常处理在前端开发中的重要性,后续需持续加强相关能力建设。 \ No newline at end of file diff --git a/doc/process/weekly/week-11/members/zhoujingyou-weekly-plan-11.md b/doc/process/weekly/week-11/members/zhoujingyou-weekly-plan-11.md new file mode 100644 index 0000000..0407465 --- /dev/null +++ b/doc/process/weekly/week-11/members/zhoujingyou-weekly-plan-11.md @@ -0,0 +1,22 @@ +# 个人周计划-第11周 + +## 姓名和起止时间 + +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-12-1 +**结束时间:** 2025-12-7 + +## 本周任务计划安排 +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|-----------------------------|-----|-------------------------------------| +| 1 | 权限细化:实现接口级权限控制(学生/维修人员/管理员) | 个人 | 完成三类角色的接口访问范围划分,并调试接口权限中间件 | +| 2 | 告警功能完善:告警分级、推送、历史查询接口 | 个人 | 完成一般/紧急告警标识,Web/APP 告警提醒联调,支持告警历史查询 | +| 3 | 全流程测试:设备异常→告警→工单→维修→恢复 | 个人 | 模拟真实业务流程,检查各环节是否能顺畅衔接,实现流程闭环验证 | + +## 小结 + +1. **功能深化:** 完成权限体系的细化与告警功能模块的强化; +2. **联调优化:** 多端联动测试告警推送与工单处理流程; +3. **问题排查:** 在全流程测试中识别逻辑断点并进行修复; +4. **系统打磨:** 为后续功能拓展与迭代做好系统级准备。 diff --git a/doc/process/weekly/week-11/members/zhoujingyou-weekly-summary-10.md b/doc/process/weekly/week-11/members/zhoujingyou-weekly-summary-10.md new file mode 100644 index 0000000..9d25658 --- /dev/null +++ b/doc/process/weekly/week-11/members/zhoujingyou-weekly-summary-10.md @@ -0,0 +1,42 @@ +# 个人周总结-第10周 + +## 姓名和起止时间 +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-24 +**结束时间:** 2025-11-30 +**核心职责:** 接口测试执行 + 新接口用例编写 + 后端联调支持 + +## 本周任务完成情况 + +| 序号 | 计划内容 | 完成情况 | 说明 | +|----|--------------|------|------------------------------------------| +| 1 | 登录/告警接口缺陷复测 | 已完成 | 完成上周遗留 6 项缺陷的复测,新增 Token 过期、异常入参等场景验证 | +| 2 | 用户信息接口测试用例设计 | 已完成 | 编写 8 条用例,覆盖权限校验、非法 Token、角色匹配、用户不存在等情况 | +| 3 | 设备信息接口测试用例设计 | 已完成 | 编写 10 条用例,覆盖设备状态字段、设备 ID 合法性、空数据处理、异常输入等 | +| 4 | 联调支持与问题定位 | 部分完成 | 本周定位 3 个接口结构问题,已修复 2 项,剩余 1 项仍在跟进中 | +| 5 | 测试文档整理与版本更新 | 已完成 | 输出《核心接口测试规范 V1.0》,补充用户/设备接口文档与用例结构 | + +## 本周总结 + +1. **阶段成果:** + 本周在完成原有接口复测的基础上,扩展了用户信息和设备信息接口的测试范围。用例设计更系统、覆盖更全面,为后续联调提供了完整参考。 + +2. **技术提升:** + - 掌握 Postman 环境变量、Collection 管理等功能; + - 初步尝试 pre-request 脚本,实现 Token 自动更新; + - 对系统用户角色逻辑与设备状态结构理解更深入。 + +3. **存在问题:** + - 接口文档变更频繁,部分信息需多次确认; + - 多接口的链路关系复杂,对告警链路仍需进一步梳理; + - 自动化测试仍处在基础阶段,尚未形成可复用脚本; + - 部分测试记录描述不够统一,需要进一步规范格式。 + +4. **改进方向:** + - 完善接口变更通知机制,减少反馈滞后; + - 对告警流程进行单独的逻辑梳理,补充关键场景用例; + - 逐步优化缺陷记录格式,使复现步骤更清晰。 + +5. **工作体会:** + 本周的工作让我更深刻体会到接口测试不仅是验证接口是否“能跑”,更是确保逻辑、结构、异常处理的全面可靠。通过频繁的联调沟通,理解到了规范化文档的重要性,也意识到提前确认接口规则能大幅减少返工。整体来说,本周在测试方法和业务理解上都取得了明显进步。 diff --git a/doc/process/weekly/week-12/group/meetingmintues-12.md b/doc/process/weekly/week-12/group/meetingmintues-12.md new file mode 100644 index 0000000..429f8ec --- /dev/null +++ b/doc/process/weekly/week-12/group/meetingmintues-12.md @@ -0,0 +1,91 @@ +# 小组会议纪要-第12周 + +## 会议记录概要 + +**团队名称:** 软1-汪汪队 +**指导老师:** 肖雄仁 +**主 持 人:** 曹峻茂 +**记录人员:** 张红卫 +**会议主题:** 第12周任务规划与分工 +**会议地点:** 宿舍 +**会议时间:** 2025-12-8 12:30-13:30 +**记录时间:** 2025-12-8 18:00 +**参与人员:** 曹峻茂、张红卫、罗月航、周竞由、王磊 + +--- + +## 会议内容 + +### 1. 本周总体目标 +完成前后端核心模块联调与问题闭环,推进云服务器与数据库环境部署,为项目最终交付做好技术准备与系统集成。 + +### 2. 后端任务安排 + +**后端1(云服务器与数据库环境配置)** +- **负责人:** 曹峻茂 +- **内容:** + - 完成云服务器环境搭建与系统初始化 + - 配置生产数据库环境,完成数据迁移与备份策略 + - 部署后端服务并完成初步连通性测试 + +**后端2(联调支持与接口问题修复)** +- **负责人:** 王磊 +- **内容:** + - 配合前端完成统计模块、设备管理模块接口联调 + - 修复接口响应慢、数据不一致等遗留问题 + - 协助优化核心业务接口性能 + +**后端3(权限与告警模块联调支持)** +- **负责人:** 周竞由 +- **内容:** + - 完成权限控制模块前后端联调,确保权限逻辑一致 + - 支持告警推送功能与前端展示联调 + - 协助修复联调中发现的权限与推送相关Bug + +### 3. 前端任务安排 + +**前端1(APP端联调与功能验证)** +- **负责人:** 罗月航 +- **内容:** + - 完成APP端用水记录、工单详情页面与后端接口联调 + - 进行功能验证与用户体验优化 + - 配合后端修复数据展示与交互问题 + +**前端2(Web端联调与性能优化)** +- **负责人:** 张红卫 +- **内容:** + - 完成Web端统计页面、设备管理页面与后端接口联调 + - 实施性能优化方案(分页、虚拟滚动、缓存等) + - 验证权限控制与异常处理逻辑 + +### 4. 联调与整合任务 + +**前后端联调** +- **参与人员:** 王磊、周竞由、罗月航、张红卫 +- **内容:** + - 集中解决第11周联调遗留问题 + - 完成所有核心业务流程端到端验证 + - 输出联调测试报告与问题修复清单 + +### 5. 团队协作要求 +- 每日晨会同步进展,晚前提交当日工作小结 +- 联调问题统一记录至共享表格,明确责任人与解决时间 +- 环境配置与部署过程中保持沟通,确保各环节顺利衔接 +- 重点关注系统稳定性与数据一致性,为交付做好准备 + +### 6. 预期交付物 +- 云服务器与生产数据库环境部署完成 +- 前后端联调问题修复清单与测试报告 +- 所有核心功能模块完成联调并稳定运行 +- 性能优化与权限控制验证通过记录 +- 项目部署文档初稿 + +### 7. 支持需求 +- 希望获得关于云环境部署与数据库性能调优的指导 +- 期待老师在系统集成与交付前测试方面给予建议 +- 如遇部署或联调难题,可向研究生学长请求技术支援 + +--- + +**会议确认:** +全体与会人员确认会议内容与分工无异议。 diff --git a/doc/process/weekly/week-12/group/week-plan-12.md b/doc/process/weekly/week-12/group/week-plan-12.md new file mode 100644 index 0000000..8577cb4 --- /dev/null +++ b/doc/process/weekly/week-12/group/week-plan-12.md @@ -0,0 +1,30 @@ +# 小组周计划-第12周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-12-8 +**结束时间:** 2025-12-14 + +## 本周任务计划安排 + + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|----|--------------------------------------------------------|----------------|------------------------| +| 1 | 确定本周计划分工 | 全体组员 | 2023-12-8 开会确定计划以及团队分工 | +| 2 | 前后端联调 | 王磊,周竞由,罗月航,张红卫 | 前后端联调解决核心问题 | +|3|配置云服务器和数据库环境|曹峻茂|配置云服务器和数据库环境| +## 小结 + +1. **沟通协作:** 每日简短同步进度,遇到阻塞问题及时召开临时会议协商解决,可向指导老师及研究生学长寻求技术支持。 +2. **学习安排:** 结合项目需求深入学习权限管理和接口性能优化相关知识,分享学习笔记。 +3. **项目管理:** PM 每日跟踪任务进度,记录问题并推动解决,确保按计划推进。 + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-12/group/week-summary-11.md b/doc/process/weekly/week-12/group/week-summary-11.md new file mode 100644 index 0000000..e36b0c7 --- /dev/null +++ b/doc/process/weekly/week-12/group/week-summary-11.md @@ -0,0 +1,37 @@ +# 小组周总结-第11周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-12-1 +**结束时间:** 2025-12-7 + +## 本周任务计划安排 + + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|----|--------------|------|----------------------------| +| 1 | 确定本周计划分工 | 完成 | 2023-11-24 开会确定计划以及团队分工 | +| 2 | 继续完善mqtt数据生成和接收及存储 | 完成 | 完善mqtt报文与接口 | +| 3 | 开发工单管理接口,扫码用水接口,水质信息查询接口 | 完成 | 完善相关接口 | +| 4 | 开发学生端扫码模块,水质信息页面,维修端工单抢单 / 拒单功能,工单处理页面,扫码用水触发功能 | 完成 | 完善app相关页面 | +| 5 | 开发设备监控页面功能,告警列表页面,开发工单列表页面,与后端联调 | 完成 | 开发设备监控页面功能,告警列表页面,开发工单列表页面 | +| 6 |开发告警触发逻辑,完成登录接口| 完成 | 开发告警触发逻辑,完成登录接口,与前端对接 | +| 7 | 前后端联调 | 进行中 | 前后端联调核心流程 | +## 小结 + + +1. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +2. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +3. **项目管理:** 代码框架、依赖版本和jdk版本管理混乱导致浪费大量时间,需要改进。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-12/members/caojunmao-weekly-plan-12.md b/doc/process/weekly/week-12/members/caojunmao-weekly-plan-12.md new file mode 100644 index 0000000..4ddd63b --- /dev/null +++ b/doc/process/weekly/week-12/members/caojunmao-weekly-plan-12.md @@ -0,0 +1,32 @@ +# 个人周计划-第11周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-12-8 +**结束时间:** 2025-12-14 + + +## 本周任务计划安排 +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|--------------------|----|-----------------------| +| 1 | 确定分工 | 组员 | 2023-12-8 开会确定计划和团队分工 | +|3|配置云服务器和数据库环境| 个人 |配置云服务器和数据库环境| + +## 小结 + + +1. **知识储备:** 学习后续需要使用的知识,为后续的开发做准备; +2. **文档撰写:** 完成迭代开发计划撰写。 +3. **项目管理** 管理项目环境和框架 +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-12/members/caojunmao-weekly-summary-11.md b/doc/process/weekly/week-12/members/caojunmao-weekly-summary-11.md new file mode 100644 index 0000000..a1bcf04 --- /dev/null +++ b/doc/process/weekly/week-12/members/caojunmao-weekly-summary-11.md @@ -0,0 +1,35 @@ +# 个人周总结-第11周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-12-1 +**结束时间:** 2025-12-7 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|----| ------------------------------------------------ |------|--------------------------------------------------------------| +| 1 | 确定分工 | 完成 | 2023-11-24 开会确定计划和团队分工 | +| 2 | 继续完善mqtt数据生成和接收及存储 | 完成 | 优化模拟器,支持手动触发异常数据(如模拟漏水、TDS 超标),增加断网重连功能。 | +|3|管理项目环境| 进行中 |管理项目环境,协调开发| + +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + +1. **项目管理:** 协调开发进度和前后端同步 +2. **团队协作**:与团队成员保持良好的沟通协作,确保设计方向与产品需求一致 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-12/members/luoyuehang-weekly-plan-12.md b/doc/process/weekly/week-12/members/luoyuehang-weekly-plan-12.md new file mode 100644 index 0000000..4a2e0b6 --- /dev/null +++ b/doc/process/weekly/week-12/members/luoyuehang-weekly-plan-12.md @@ -0,0 +1,30 @@ +# 个人周计划-第12周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-12-08 +**结束时间:** 2025-12-14 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | 运维APP工单功能联调 | 后端开发 | 调试工单创建、抢单、处理、完成等完整业务流程接口 | +| 2 | 运维APP设备监控联调 | 后端开发 | 调试设备数据实时获取、告警推送、状态更新等接口 | +| 3 | 学生端扫码功能联调 | 后端开发 | 调试扫码用水、水质查询、设备识别等核心功能接口 | +| 4 | 学生端用水记录联调 | 后端开发 | 调试个人用水量统计、历史记录查询、数据可视化接口 | +| 5 | 联调问题记录与修复 | 后端开发 | 记录联调过程中的问题并协同修复,确保功能稳定 | + + +## 小结 + +1. **联调协调**:需要与后端团队协调联调时间和环境,确保联调效率; +2. **测试数据**:需要准备充分的测试数据覆盖各种业务场景; +3. **问题追踪**:需要建立有效的问题追踪机制,确保问题及时解决; +4. **性能关注**:在联调过程中需要关注接口响应时间和前端性能表现; +5. **错误处理**:需要测试各种异常情况下的错误处理和用户提示; +6. **移动端适配**:需要确保联调功能在不同移动设备上的兼容性; + + diff --git a/doc/process/weekly/week-12/members/luoyuehang-weekly-summary-11.md b/doc/process/weekly/week-12/members/luoyuehang-weekly-summary-11.md new file mode 100644 index 0000000..c0a596e --- /dev/null +++ b/doc/process/weekly/week-12/members/luoyuehang-weekly-summary-11.md @@ -0,0 +1,36 @@ +# 个人周总结-第11周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-12-01 +**结束时间:** 2025-12-07 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | -------- | -------- | -------- | +| 1 | 运维人员APP页面样式优化 | 完成 | 对所有运维端页面进行了视觉统一和交互优化,提升了用户体验 | +| 2 | 学生端页面样式优化 | 完成 | 优化了学生端所有页面的视觉效果和布局结构,确保界面美观易用 | +| 3 | 登录功能接口对接 | 完成 | 成功对接后端登录接口,实现用户认证、权限验证和会话管理 | +| 4 | 注册功能接口对接 | 完成 | 完成用户注册接口对接,支持新用户账号创建和信息验证 | +| 5 | 响应式设计完善 | 完成 | 确保所有页面在不同屏幕尺寸下的良好显示效果 | + +## 对团队工作的建议 + +1. **样式规范统一**:建议建立团队统一的设计规范文档,包括颜色、字体、间距等; +2. **组件库建设**:建议逐步建立前端组件库,提高开发效率和一致性; +3. **接口错误处理**:建议规范接口错误码和错误信息,便于前端统一处理用户提示; +4. **性能监控**:建议引入前端性能监控工具,及时发现和解决性能问题。 + +## 小结 + +1. **视觉体验提升**:通过系统的样式优化,运维人员APP和学生端的视觉体验得到显著改善; +2. **接口对接成功**:登录注册功能与后端API成功对接,用户认证流程完整可靠; +3. **设计一致性**:统一了两端应用的设计语言,保持了产品体验的一致性; +4. **技术难点攻克**:解决了移动端适配、接口错误处理、加载状态管理等技术难点; +5. **团队协作**:与后端开发团队紧密协作,接口对接过程高效顺利; +6. **质量保障**:在样式优化的同时,确保了功能完整性和用户体验的平衡; +7. **后续重点**:接下来将重点进行功能测试和性能优化,确保系统稳定运行。 + diff --git a/doc/process/weekly/week-12/members/wanglei-weekly-plan-12.md b/doc/process/weekly/week-12/members/wanglei-weekly-plan-12.md new file mode 100644 index 0000000..3e05111 --- /dev/null +++ b/doc/process/weekly/week-12/members/wanglei-weekly-plan-12.md @@ -0,0 +1,23 @@ +# 个人周计划-第12周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2023-12-8 +**结束时间:** 2023-12-14 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +| --- | ---- | --- | ---------------------- | +| 1 | 确定分工 | 组员 | 2025-12-8 开会细分确定团队分工, | +| 2 | 联调支持 | 组员 | 配合前端完成接口联调问题,完成第一版迭代开发 | + +## 小结 + +1. **技术重点:** 接口联调问题持续修复与支持 +2. **协作重点:** 加强与前端的沟通协作机制,参与接口联调复盘与总结 +3. **学习重点:** 学习接口性能分析和调优方法,提升联调问题响应与解决效率 + +--- diff --git a/doc/process/weekly/week-12/members/wanglei-weekly-summary-11.md b/doc/process/weekly/week-12/members/wanglei-weekly-summary-11.md new file mode 100644 index 0000000..a03040b --- /dev/null +++ b/doc/process/weekly/week-12/members/wanglei-weekly-summary-11.md @@ -0,0 +1,40 @@ +# 个人周计划-第十一周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-12-1 + +**结束时间:** 2025-12-7 + +## 本周任务计划安排 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| --- | ----- | ---- | ------------------------------------------ | +| 1 | 接口完善 | 完成 | 完成数据统计接口开发(按设备/地区/时间统计用水量、告警次数)、设备状态管理接口开发 | +| 2 | 数据层优化 | 完成 | 完成传感器历史数据分页查询优化、索引调整,响应时间优化至≤300ms | +| 3 | 联调支持 | 进行中 | 配合前端完成参数、格式、权限校验等接口联调问题修复 | + +## 对团队工作的建议 + +1.****建议建立接口联调检查清单**** 确保前后端数据格式和参数一致性,减少联调阶段问题 + +2.****建议加强文档更新机制**** 接口变更后及时同步更新接口文档,便于协作和后续维护 + +3.****建议建立性能监控机制**** 对关键接口进行性能监控,及时发现并处理性能瓶颈 + +## 小结 + +1. **技术收获** 深入理解了多维度统计接口的设计与实现方法 +2. **协作收获** 通过明确分工提升了任务执行效率 +3. **后续重点** 继续配合前端进行联调和测试 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; diff --git a/doc/process/weekly/week-12/members/zhanghongwei-weekly-plan-12.md b/doc/process/weekly/week-12/members/zhanghongwei-weekly-plan-12.md new file mode 100644 index 0000000..5ad5f0d --- /dev/null +++ b/doc/process/weekly/week-12/members/zhanghongwei-weekly-plan-12.md @@ -0,0 +1,89 @@ +# 个人周计划-第十二周 + +## 基本信息 + +**姓  名:** 张红卫 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-12-8 +**结束时间:** 2025-12-14 + +--- + +## 本周任务计划安排 + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|------|----------|--------|----------| +| 1 | Web端统计页面联调与优化 | 个人 | 与王磊协作完成统计接口联调,优化图表加载性能,完善筛选交互 | +| 2 | Web端设备管理页面联调与优化 | 个人 | 与周竞由协作完成设备管理接口联调,实现权限控制与批量操作功能 | +| 3 | 前后端联调支持 | 个人 + 后端团队 | 参与团队联调,修复接口调用异常、数据展示不一致等问题 | +| 4 | 前端性能优化实施 | 个人 | 实施分页加载、虚拟滚动方案,优化大数据场景下的页面渲染性能 | +| 5 | 权限控制与异常处理完善 | 个人 | 配合后端完成权限逻辑对齐,优化导航栏与页面异常跳转处理 | + +--- + +## 技术学习与实施重点 + +### 1. 联调协作能力提升 +- 掌握前后端接口调试与问题定位方法 +- 学习使用Postman等工具进行接口测试与数据验证 + +### 2. 性能优化实战深化 +- 实施虚拟滚动完整方案,优化大数据列表展示 +- 探索前端缓存策略与懒加载机制 +- 学习使用Chrome DevTools进行性能分析与优化 + +### 3. 权限系统集成实践 +- 完成路由级与操作级权限控制 +- 实现动态菜单与按钮权限控制 +- 学习RBAC权限模型在前端的应用 + +### 4. 异常处理与用户体验 +- 完善各类异常状态下的用户提示 +- 优化页面加载失败的重试与降级机制 +- 实现友好的错误页面和加载状态 + +--- + +## 交付物清单 + +- [ ] 联调完成的统计页面代码(含性能优化) +- [ ] 联调完成的设备管理页面代码(含权限控制) +- [ ] 前后端联调问题修复记录 +- [ ] 性能优化实施方案文档 +- [ ] 权限控制测试验证报告 +- [ ] 每日工作进展记录 + +--- + +## 协作依赖与风险说明 + +### 协作依赖 +1. **接口依赖** + - 依赖王磊提供稳定、性能优化的统计查询接口 + - 依赖周竞由提供完整的权限控制接口规范 + - 需要设备管理相关接口的稳定支持 + +2. **环境依赖** + - 需要曹峻茂部署的测试环境支持联调验证 + - 需要稳定的开发环境和版本控制支持 + +3. **流程依赖** + - 需要罗月航在APP端配合进行跨端流程验证 + - 需要团队成员及时沟通接口变更 + +--- + +## 小结与期望 + +### 本周目标 +完成Web端所有核心页面的前后端联调,实现性能优化与权限控制,确保功能稳定可用。 + +### 团队协作建议 +1. 建议每日固定时间进行联调进度同步 +2. 建立接口变更通知机制,减少沟通成本 +3. 统一问题记录与跟踪方式,提高解决效率 + +### 支持需求 +1. 希望获得前端性能优化(特别是大数据渲染)的实战指导 +2. 期待老师在系统集成测试方面提供方法指导 +3. 如遇技术难题,可向研究生学长请求支持 diff --git a/doc/process/weekly/week-12/members/zhanghongwei-weekly-summary-11.md b/doc/process/weekly/week-12/members/zhanghongwei-weekly-summary-11.md new file mode 100644 index 0000000..c447f69 --- /dev/null +++ b/doc/process/weekly/week-12/members/zhanghongwei-weekly-summary-11.md @@ -0,0 +1,77 @@ +# 个人周总结-第十一周 + +## 基本信息 + +**姓  名:** 张红卫 +**团队名称:** 软1-汪汪队 +**起止时间:** 2025-12-1 至 2025-12-7 +**核心职责:** 前端界面开发 + 前后端联调支持 + +--- + +## 本周任务完成情况 + +| 序号 | 计划内容 | 完成状态 | 详细说明 | +|------|------------------------------------------------|----------|------------------------------------------------------------------------------------------------------------------| +| 1 | 完成 Web 端统计页面功能开发与交互优化 | 进行中 | 已完成 ECharts 图表框架集成,实现用水量、告警次数等基础图表展示;筛选功能已初步实现,接口数据对接正在调试中 | +| 2 | 完成 Web 端设备管理页面功能开发与交互优化 | 进行中 | 页面结构与基础表单组件已完成,支持设备增删改查;批量操作功能正在开发中,接口调试同步进行 | +| 3 | 参与前后端联调工作 | 进行中 | 与王磊、周竞由等后端成员保持每日沟通,统计模块接口已部分联调完成,设备管理模块接口正在逐步对接 | +| 4 | 性能优化与异常处理完善 | 进行中 | 已引入分页加载机制优化大数据展示;权限控制逻辑与后端对齐中;导航栏异常跳转处理逻辑已优化并测试 | + +--- + +## 本周总结 + +### 一、任务进展概述 +本周主要工作集中在 **统计页面** 和 **设备管理页面** 的前后端联调阶段。目前两个页面的基础功能框架已搭建完成,核心接口正在有序对接中。性能优化与权限控制方案已初步实施,但整体进度仍处于联调阶段,尚未完全完成。 + +### 二、技术实现与收获 +1. **性能优化实践** + - 引入分页加载机制,初步缓解大数据渲染压力 + +2. **权限控制集成** + - 配合后端权限接口,搭建前端路由权限控制基础框架 + - 完善导航栏跳转与操作权限校验逻辑 + +3. **复杂业务处理** + - 实现设备管理中的复杂表单验证与状态管理 + - 掌握批量操作的前端实现方式 + +### 三、遇到的问题与挑战 +1. **接口响应延迟** + - 部分统计查询接口响应时间较长,影响页面加载体验 + +2. **权限逻辑不一致** + - 前端权限控制逻辑需与后端进一步对齐,部分页面权限拦截尚未生效 + +3. **性能瓶颈** + - 大数据量图表渲染仍存在轻微卡顿,需进一步优化 + +4. **联调协作效率** + - 接口变更沟通不够及时,影响前端调试进度 + +### 四、协作建议 +1. **接口规范明确** + - 建议后端提供完整的接口文档与示例数据 + - 建立接口变更的版本管理机制 + +2. **联调流程优化** + - 每周安排专门联调会议 + - 建立联调问题跟踪清单 + +3. **性能监控机制** + - 共同建立接口性能监控体系 + - 定期进行性能测试与优化 + +### 五、工作总结与展望 +本周在前后端联调过程中,进一步深入理解了系统整体架构与业务流程。通过实际调试,不仅提升了问题排查能力,也对前端性能优化和权限控制有了更深刻的认识。虽然进度比预期稍慢,但通过持续的沟通协作,各项任务正在稳步推进。 + +**期待与建议:** +- 希望团队成员继续加强沟通,及时同步进展和问题 +- 建议建立更规范的联调流程,提高协作效率 +- 期待后端接口性能进一步提升,为前端优化提供更好基础 + +--- + +**提交时间:** 2025-12-07 +**下周重点:** 完成所有接口联调,实现性能优化目标,完善权限控制系统 \ No newline at end of file diff --git a/doc/process/weekly/week-12/members/zhoujingyou-weekly-plan-12.md b/doc/process/weekly/week-12/members/zhoujingyou-weekly-plan-12.md new file mode 100644 index 0000000..741084d --- /dev/null +++ b/doc/process/weekly/week-12/members/zhoujingyou-weekly-plan-12.md @@ -0,0 +1,22 @@ +# 个人周计划-第12周 + +## 姓名和起止时间 + +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-12-8 +**结束时间:** 2025-12-14 + +## 本周任务计划安排 +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|-----------------------------|-----|-------------------------------------| +| 1 | 前后端接口联调:核心业务接口(权限/告警/工单)全量对接验证 | 前端协作人员 | 梳理接口文档并核对字段定义,逐一验证参数传递、数据返回格式、异常场景处理逻辑,修复接口调用异常问题 | +| 2 | 页面与后端逻辑联调:权限展示/告警推送/工单流转页面 | 前端协作人员 | 联调各角色权限对应的页面展示逻辑,验证告警弹窗/列表与后端数据实时联动,确保工单全流程页面数据同步更新 | +| 3 | 联调问题闭环与性能优化:接口响应/数据一致性/多端兼容 | 前端协作人员 | 汇总联调问题清单并逐一修复,完成回归测试;优化高频接口响应速度,验证多浏览器/移动设备兼容性 | + +## 小结 + +1. **接口验证:** 完成核心业务接口全量联调,统一前后端字段定义与异常处理标准,解决接口调用类问题; +2. **页面联调:** 实现权限、告警、工单相关页面与后端逻辑的精准联动,保障页面数据实时性与准确性; +3. **问题闭环:** 梳理并修复联调全流程问题,通过回归测试验证问题解决效果,确保无遗留问题; +4. **体验优化:** 针对高频接口做性能调优,提升页面交互响应速度,保障多端访问的兼容性与稳定性。 \ No newline at end of file diff --git a/doc/process/weekly/week-12/members/zhoujingyou-weekly-summary-11.md b/doc/process/weekly/week-12/members/zhoujingyou-weekly-summary-11.md new file mode 100644 index 0000000..cd3169f --- /dev/null +++ b/doc/process/weekly/week-12/members/zhoujingyou-weekly-summary-11.md @@ -0,0 +1,39 @@ +# 个人周总结-第11周 + +## 姓名和起止时间 +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-12-1 +**结束时间:** 2025-12-7 +**核心职责:** 权限模块细化开发 + 告警功能优化 + 全流程闭环测试 + +## 本周任务完成情况 + +| 序号 | 计划内容 | 完成情况 | 说明 | +|----|-----------------------------|------|------------------------------------------| +| 1 | 权限细化:接口级权限控制(学生/维修人员/管理员) | 已完成 | 完成三类角色12个核心接口的访问范围划分,开发并调试权限中间件,解决2处角色权限冲突问题,通过接口测试验证权限拦截有效性 | +| 2 | 告警功能完善:告警分级、推送、历史查询接口 | 已完成 | 实现一般/紧急两级告警标识逻辑,完成Web弹窗、APP消息推送联调,开发告警历史查询接口(支持按时间/级别筛选),验证多端推送兼容性 | +| 3 | 全流程测试:设备异常→告警→工单→维修→恢复 | 部分完成 | 模拟8种真实业务场景(含3种异常场景),验证各环节数据同步与流程衔接,定位并修复3个逻辑断点(工单状态更新延迟、告警与工单关联失败等),实现全流程闭环 | + +## 本周总结 + +1. **阶段成果:** + 本周完成权限体系的精细化落地与告警功能的全场景覆盖,通过全流程测试验证了核心业务链路的完整性。权限控制精准度、告警响应及时性及流程衔接顺畅度均达到预期,为后续前后端联调奠定了坚实基础。 + +2. **技术提升:** + - 深入理解JWT权限认证与中间件拦截原理,掌握接口级权限控制的实现方法; + - 熟悉Web/APP多端告警推送的联调技巧,解决了跨端消息格式兼容问题; + - 提升复杂业务流程的测试场景设计能力,学会通过场景复现定位链路断点。 + +3. **存在问题:** + - 权限模块对“临时角色”“跨角色操作”等特殊场景考虑不足,暂无适配方案; + - 全流程测试依赖人工模拟,测试效率较低,未形成自动化测试脚本; + +4. **改进方向:** + - 补充特殊角色权限场景的需求分析,新增3-5条关键用例并完善代码逻辑; + - 优化告警推送机制,引入消息队列降低并发压力,提升推送实时性; + - 基于Postman或JMeter编写全流程自动化测试脚本,减少人工重复操作; + - 对告警查询接口添加索引,优化SQL查询语句,提升大数据量下的响应速度。 + +5. **工作体会:** + 本周工作让我深刻认识到“功能完整性”与“场景适配性”的重要性。权限控制不仅要划分清晰,更要考虑实际业务中的特殊情况;全流程测试不仅要验证“正常路径”,更要覆盖“异常场景”。通过与前端、产品的沟通,意识到功能开发需提前对齐需求细节,避免因理解偏差导致返工。同时,性能优化是功能落地后的关键环节,后续需在开发初期就融入性能考量,提升系统整体稳定性。 \ No newline at end of file diff --git a/doc/process/weekly/week-4/group/meeting-minutes-3.md b/doc/process/weekly/week-4/group/meeting-minutes-3.md new file mode 100644 index 0000000..ced4bca --- /dev/null +++ b/doc/process/weekly/week-4/group/meeting-minutes-3.md @@ -0,0 +1,62 @@ +# 团队项目周会议纪要 —— 第三周 + +## 会议基本信息 +- **会议日期**:2025年10月10日 +- **团队项目名称**:校园直饮矿化水管理系统 +- **小组成员**:张红卫、罗月航、周竞由、曹峻茂、王磊 +- **会议主要内容**:需求分析与基本分工安排 + +--- + +## 需求分析 + +### 用户对象 +- 管理员 +- 维修人员 +- 学生 + +### 平台要求 +- Web PC端管理平台(管理员使用) +- 运维APP(维修人员使用) +- 学生APP(学生使用) + +--- + +### 各用户基本需求 + +#### 管理员 +1. 监控设备上传数据,查看告警信息 +2. 跟踪所有工单,对超时工单进行人工派单 +3. 管理各片区维修人员 +4. 管理地区信息 + +#### 维修人员 +1. 设备监控权限受限,仅可查看本辖区设备 +2. 工单服务为核心功能:抢单、拒单、处理并提交工单 +3. 查看历史工单记录 + +#### 学生 +1. 查看校内矿化水终端机位置 +2. 扫码用水 +3. 扫码查看制水机水质情况 +4. 查看每日用水量 + +--- + +## 会议安排 + +### 项目开发模式 +项目采用**前后端分离**的开发模式。 + +### 人员分工 +| 角色 | 人员 | +|------|------| +|PM|曹峻茂 +| 前端 | 罗月航、周竞由 | +| 后端 | 张红卫、曹峻茂、王磊 | +|会议记录|张红卫| + +### 本周计划 +各成员查阅相关资料,对项目整体推进方式形成初步计划。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-4/group/meeting-minutes-4.md b/doc/process/weekly/week-4/group/meeting-minutes-4.md new file mode 100644 index 0000000..1af6d9f --- /dev/null +++ b/doc/process/weekly/week-4/group/meeting-minutes-4.md @@ -0,0 +1,26 @@ +# 小组周计划-第4周 + +## 团队名称和起止时间 + +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|------|----------|--------|----------| +| 1 | 明确项目需求功能模块 | 全体成员 | 2025-10-13,全体成员讨论明确校园直饮矿化水管理系统的需求功能模块 | +| 2 | 确定前后端开发分工 | 全体成员 | 2025-10-13,根据项目需求进行前后端开发分工 | +| 3 | 前端UI草案设计 | 罗月航,周竞由 | 2025-10-13,前端人员学习并尝试初步建立起UI草 | +| 4 | 后端技术框架学习 | 张红卫,曹峻茂,王磊 | 2025-10-13,后端人员完成对MQTT协议和HTTP接口开发技术的学习 | +| 5 | 开发环境统一配置 | 全体成员 | 2025-10-13,完成前后端开发环境的统一配置 | + +## 小结 + +1. **技术学习:** 团队成员应加强物联网通信和Web开发相关知识的学习,为后续开发打好基础。 +2. **沟通协作:** 前后端成员应保持密切沟通,确保技术方案的一致性。 +3. **进度把控:** 各成员应按计划完成学习任务,确保项目按时推进。 +4. **问题解决:** 遇到技术难题及时在团队内讨论或查找资料。 + +--- diff --git a/doc/process/weekly/week-4/group/weekly-plan-4.md b/doc/process/weekly/week-4/group/weekly-plan-4.md new file mode 100644 index 0000000..374186d --- /dev/null +++ b/doc/process/weekly/week-4/group/weekly-plan-4.md @@ -0,0 +1,36 @@ +# 小组周计划-第4周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + +## 本周任务计划安排 + + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|----|------------------|------------|------------------------------| +| 1 | 确定分工 | 全体组员 | 2023-10-13 开会细分确定团队分工,统一开发工具 | +| 2 | 需求获取 | 全体组员 | 根据老师给出需求完成需求获取 | +| 3 | 学习 | 全体组员 | 根据分工学习MQTT或前端相关知识 | +| 4 | 实现基本的数据通信功能 | 曹峻茂,王磊,张红卫 | 实现通过MQTT协议获取饮水机数据的基本功能 | +| 5 |尝试设计构建简易的ui草案| 罗月航,周竞由 |通过学习的前端知识尝试构建ui页面| + +## 小结 + +1. **增强自信心:** 小组成员应增强自信心,发挥想象力和创造力,在原型设计环节积极思考。 +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +4. **项目管理:** PM及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-4/members/caojunmao-weekly-plan-4.md b/doc/process/weekly/week-4/members/caojunmao-weekly-plan-4.md new file mode 100644 index 0000000..35b4886 --- /dev/null +++ b/doc/process/weekly/week-4/members/caojunmao-weekly-plan-4.md @@ -0,0 +1,34 @@ +# 个人周计划-第4周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + + +## 本周任务计划安排 +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|-------------------|--------|------------------------------| +| 1 | 确定分工 | 组员 | 2023-10-13 开会细分确定团队分工,统一开发工具 | +| 2 | 需求获取 | 组员 | 根据老师给出需求完成需求获取 | +| 3 | 学习 | 个人 | 学习MQTT协议知识 | +| 4 | 实现基本的数据通信功能 | 王磊,张红卫 | 实现通过MQTT协议获取饮水机数据的基本功能 | + +## 小结 + +1. **学习需求:** 希望能有对于MQTT协议以及使用的教学; +2. **知识储备:** 学习后续需要使用的知识,为后续的开发做准备; +3. **文档撰写:** 结合课内知识学习需求文档的撰写。 +4. **代码实现** 尝试实现数据通信 +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-4/members/luoyuehang-weekly-plan-4.md b/doc/process/weekly/week-4/members/luoyuehang-weekly-plan-4.md new file mode 100644 index 0000000..41d9ed7 --- /dev/null +++ b/doc/process/weekly/week-4/members/luoyuehang-weekly-plan-4.md @@ -0,0 +1,23 @@ +# 个人周计划-第1周 + +## 姓名和起止时间 + +**姓  名:** 罗月航 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | 需求分析与理解 | 组员 | 仔细阅读项目需求文档,理解管理员、维修人员、学生三类用户的功能需求 | +| 2 | 设计规范制定 | 组员 | 与协作者共同确定Web端与移动端的设计风格、色彩体系和组件规范 | +| 3 | 维修人员APP原型设计 | 个人 | 完成维修人员APP核心页面(设备监控、工单处理)的线框图和高保真设计 | +| 4 | 管理员平台协作设计 | 组员 | 协助完成管理员平台中"工单跟踪"与"人员管理"模块的UI设计 | +| 5 | 设计评审与优化 | 组员 | 参与内部设计评审,根据反馈意见优化完善UI设计方案 | + +## 小结 + + **协作沟通:** 希望建立定期的设计评审机制,确保设计方向的一致性。 + diff --git a/doc/process/weekly/week-4/members/wanglei-weekly-plan-4.md b/doc/process/weekly/week-4/members/wanglei-weekly-plan-4.md new file mode 100644 index 0000000..fb670c9 --- /dev/null +++ b/doc/process/weekly/week-4/members/wanglei-weekly-plan-4.md @@ -0,0 +1,33 @@ +# 个人周计划-第4周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2023-10-13 +**结束时间:** 2023-10-19 + + +## 本周任务计划安排 + +|序号| 计划内容| 协作人 | 情况说明 | +| ---- | ------ | ------| ------- | +| 1 | 需求获取 | 组员 | 2025-10-10 与肖雄仁老师面对面沟通确定需求 | +| 2 | 学习 | 个人 | 周内持续学习搭建数据库和处理数据的相关知识 | +| 3 | 确定分工 | 组员 | 2025-10-10 开会细分确定团队分工, | +| 4 | 学习项目的相关知识 | 个人 | 周内持续学习数据库的相关知识 | + +## 小结 + +1. **学习需求:** 学习数据库相关知识和处理数据; +2. **知识储备:** 提前学习后续需要使用的知识,为后续的前端开发做准备; +3. **文档撰写:** 结合课内知识学习需求文档的撰写。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-4/members/zhanghongwei-weekly-plan-04.md b/doc/process/weekly/week-4/members/zhanghongwei-weekly-plan-04.md new file mode 100644 index 0000000..31acbdb --- /dev/null +++ b/doc/process/weekly/week-4/members/zhanghongwei-weekly-plan-04.md @@ -0,0 +1,27 @@ +# 个人周计划-第4周 + +## 姓名和起止时间 + +**姓  名:** 张红卫 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + + +## 本周任务计划安排 + +| 序号 | 计划内容| 协作人 | 情况说明 | +| ----| ------ | ------| ------- | +| 1 | 需求获取 | 组员 | 2025-10-10 与肖雄仁老师面对面沟通确定需求 | +| 2 | 学习传输数据 | 个人 | 周内持续学习将处理好的数据通过HTTP协议返回给客户端相关知识 | +| 3 | 确定分工 | 组员 | 2025-10-10 开会细分确定团队分工 | +| 4 | 学习数据接口服务 | 个人 | 周内持续学习如何编写一个Web服物,通过调用直接查询数据库,来获取数据。 | + + +## 小结 + +1. **学习需求:** 希望能有对于数据传输的教学; +2. **知识储备:** 提前学习后续需要使用的知识,为后续的前端开发做准备; +3. **文档撰写:** 结合课内知识学习需求文档的撰写。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-4/members/zhanghongwei-weekly-plan4.md b/doc/process/weekly/week-4/members/zhanghongwei-weekly-plan4.md new file mode 100644 index 0000000..c478f7f --- /dev/null +++ b/doc/process/weekly/week-4/members/zhanghongwei-weekly-plan4.md @@ -0,0 +1,26 @@ +# 个人周计划-第4周 + +## 姓名和起止时间 + +**姓  名:** 张红卫 +**团队名称:** 一班-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2023-10-19 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +|------|----------|--------|----------| +| 1 | 需求获取 | 组员 | 2025-10-10 与肖熊仁老师面对面沟通确定需求 | +| 2 | 学习数据接口服务 | 个人 | 周内持续学习数据接口服务,然后初步尝试编写Web服务,通过调用数据库,来获取数据 | +| 3 | 确定分工 | 组员 | 2025-10-10 开会细分确定团队分工 | +| 4 | 学习设计对外API | 个人 | 周内持续学习设计一套清晰的RESTful API或GraphQL API,定义好端点、请求/响应格式 | + +## 本周技术学习计划 + + +## 小结 + +1. **学习需求:** 希望能有对于API设计的教学; +2. **知识储备:** 提前学习后续需要使用的知识,为后续的前端开发做准备 +3. **文档撰写:** 结合课内知识学习需求文档的撰写 diff --git a/doc/process/weekly/week-4/members/zhoujingyou-weekly-plan-4.md b/doc/process/weekly/week-4/members/zhoujingyou-weekly-plan-4.md new file mode 100644 index 0000000..809c7d5 --- /dev/null +++ b/doc/process/weekly/week-4/members/zhoujingyou-weekly-plan-4.md @@ -0,0 +1,24 @@ +# 个人周计划-第4周 + +## 姓名和起止时间 + +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|--------------------------------|-----|-------------------------------| +| 1 | 分析需求文档并与团队沟通,明确前端设计的核心功能模块 | 组员 | 2025-10-13 阅读需求文档并与设计团队确认需求细节 | +| 2 | 制定UI设计方案,规划学生端、维修人员端和Web端的界面风格 | 个人 | 设计草图和布局框架,确保与需求一致 | +| 3 | 完成首页和功能模块的初步UI设计,关注色彩、字体和布局 | 个人 | 完成首页UI设计,并与团队进行初步评审 | +| 4 | 继续完善学生端APP的UI设计,加入动画效果和交互细节 | 个人 | 根据反馈继续调整设计,优化交互效果 | +| 5 | 开始前端页面布局开发,使用HTML & CSS编写基本结构 | 个人 | 搭建学生端的基础页面布局,并与后端接口对接 | + +## 小结 + +1. **学习需求:** 希望能有更多关于UI设计与前端开发工具的支持和学习资源; +2. **知识储备:** 提前学习前端开发的相关技能,包括HTML、CSS、JavaScript的基础与进阶知识; +3. **文档撰写:** 完成需求分析与前端设计方案,并与团队保持密切沟通,确保所有开发内容一致。 diff --git a/doc/process/weekly/week-5/group/meeting-minutes-5.md b/doc/process/weekly/week-5/group/meeting-minutes-5.md new file mode 100644 index 0000000..5a4d747 --- /dev/null +++ b/doc/process/weekly/week-5/group/meeting-minutes-5.md @@ -0,0 +1,39 @@ +# 小组会议纪要-第5周 + +## 会议记录概要 + +**团队名称:** 1班-汪汪队 +**指导老师:** 肖雄仁 +**主 持 人:** 曹峻茂 +**记录人员:** 张红卫 +**会议主题:** 重新定位任务与计划 +**会议地点:** 宿舍 +**会议时间:** 2025-10-20 12:30-13:00 +**纪录时间:** 2023-10-26 18:00 +**参与人员:** 曹峻茂、张红卫、罗月航、周竞由、王磊 + +--- + +## 会议内容 + +### 1.任务重新分配 + +由于进度需要,临时将张红卫从后端调到前端,三个人刚好对项目的学生、维修人员和管理人员进行分配 + +###2.项目分工 + +(1)**项目主要分工方面:** +-前端:继续完善界面原型 +-后端:进一步完成数据库和用例文档 + +(2)**角色分配:** +前端开发人员:张红卫、罗月航、周竞由 +后端开发人员:曹峻茂、王磊 + + +(3)**具体任务安排:** +- 会议记录:张红卫 +- 会议主持:曹峻茂 +- 文档管理:曹峻茂 +- 周计划文档:曹峻茂 +- 项目开发:张红卫、曹峻茂、罗月航、周竞由、王磊 diff --git a/doc/process/weekly/week-5/group/weekly-plan-5.md b/doc/process/weekly/week-5/group/weekly-plan-5.md new file mode 100644 index 0000000..5a21d2a --- /dev/null +++ b/doc/process/weekly/week-5/group/weekly-plan-5.md @@ -0,0 +1,35 @@ +# 小组周计划-第5周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-20 +**结束时间:** 2025-10-26 + +## 本周任务计划安排 + + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|----|----------------|-------------|-----------------------| +| 1 | 确定分工及周计划 | 全体组员 | 2023-10-20 开会细分确定团队分工 | +| 2 | 完善数据库的构建 | 王磊 | 在之前构建的数据传输模块基础上设计数据库 | +| 3 | 完成用例文档以及更新需求文档 | 曹峻茂 | 设计用例文档并根据原型页面更新需求文档 | +| 4 | 根据用例图完成原型界面 | 罗月航,周竞由,张红卫 | 根据用例文档完成原型界面 | + +## 小结 + +1. **增强自信心:** 小组成员应增强自信心,发挥想象力和创造力,在原型设计环节积极思考。 +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +4. **项目管理:** PM及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-5/group/weekly-summary-4.md b/doc/process/weekly/week-5/group/weekly-summary-4.md new file mode 100644 index 0000000..563147e --- /dev/null +++ b/doc/process/weekly/week-5/group/weekly-summary-4.md @@ -0,0 +1,33 @@ +# 小组周总结-第4周 + +## 团队名称和起止时间 + +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|------|----------|-----|----------| +| 1 | 确定分工 | 完成 | 2023-10-13 开会细分确定团队分工,统一开发工具 | +| 2 | 需求获取 | 完成 | 根据老师给出需求完成需求获取 | +| 3 | 学习 | 完成 | 根据分工学习MQTT或前端相关知识 | +| 4 | 实现基本的数据通信功能 | 基本完成 | 实现通过MQTT协议获取饮水机数据的基本功能 | +| 5 |尝试设计构建简易的ui草案| 进行中 |通过学习的前端知识尝试构建ui页面| + + +## 小结 + +1. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +2. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +3. **项目管理:** 项目进度管理杨安然同学及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 diff --git a/doc/process/weekly/week-5/members/caojunmao-weekly-plan-5.md b/doc/process/weekly/week-5/members/caojunmao-weekly-plan-5.md new file mode 100644 index 0000000..f35df75 --- /dev/null +++ b/doc/process/weekly/week-5/members/caojunmao-weekly-plan-5.md @@ -0,0 +1,34 @@ +# 个人周计划-第5周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-20 +**结束时间:** 2025-10-26 + + +## 本周任务计划安排 +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|-----------|-----|-----------------------| +| 1 | 确定分工 | 组员 | 2023-10-20 开会细分确定团队分工 | +| 2 | 协调完成数据库设计 | 王磊 | 为王磊完成数据库设计提供支持 | +| 3 | 设计用例文档 | 个人 | 完成用例文档 | +| 4 | 更新需求文档 | 个人 | 在原型界面完成后更新需求文档 | + +## 小结 + +1. **学习需求:** 希望能有对于用例文档设计和需求文档格式的教学; +2. **知识储备:** 学习后续需要使用的知识,为后续的开发做准备; +3. **文档撰写:** 结合课内知识学习需求文档的撰写。 +4. **代码实现** 尝试实现数据通信 +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-5/members/caojunmao-weekly-summary-4.md b/doc/process/weekly/week-5/members/caojunmao-weekly-summary-4.md new file mode 100644 index 0000000..66dd624 --- /dev/null +++ b/doc/process/weekly/week-5/members/caojunmao-weekly-summary-4.md @@ -0,0 +1,37 @@ +# 个人周总结-第4周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | ------------------------------------------------ |----|--------------------------------------------------------------| +| 3 | 学习 | 完成 | 学习MQTT协议知识 | +| 4 | 实现基本的数据通信功能 | 完成 | 实现通过MQTT协议获取饮水机数据的基本功能 | +| 4 | 召开例会,讨论原型的完成情况,并交换报告各自进度 | 完成 | 2023-10-19召开每周例会,并确定下周进度计划,需求 | + +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + +1. **原实现数据传输功能:** 完成了数据传输模块的基本实现; +2. **技能学习:** 小组成员各自开展自己所负责部分的个人技能的学习; +3. **项目管理:** PM及时推进项目进度,确保工作有条不紊; +4. **计划制定:** 根据本周任务完成情况与下一阶段文档提交要求,制定团队任务计划。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-5/members/luoyuehang-weekly-plan-05.md b/doc/process/weekly/week-5/members/luoyuehang-weekly-plan-05.md new file mode 100644 index 0000000..ddc2a73 --- /dev/null +++ b/doc/process/weekly/week-5/members/luoyuehang-weekly-plan-05.md @@ -0,0 +1,25 @@ +# 个人周计划-第5周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-20 +**结束时间:** 2025-10-26 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | 维修人员APP原型优化完善 | 个人 | 根据第一周评审反馈,完善维修人员APP所有页面的交互细节和视觉设计 | +| 2 | 实现原型交互功能 | 个人 | 实现页面跳转、按钮交互、数据展示等动态效果,完成高保真交互原型 | +| 3 | 维修人员APP前端环境搭建 | 个人 | 搭建前端开发环境,配置基础的项目结构和依赖 | +| 4 | 开发基础页面框架 | 个人 | 实现维修人员APP的登录页面、主布局框架和导航组件 | +| 5 | 团队技术分享 | 组员 | 参与团队技术分享会,交流原型设计经验和前端开发技术 | + +## 小结 + +1. **技术支持需求:** 希望获得移动端前端开发的技术指导,特别是框架配置和移动端适配方案; +2. **设计确认:** 需要产品经理对维修人员APP原型设计进行最终确认; + + diff --git a/doc/process/weekly/week-5/members/luoyuehang-weekly-summary-04.md b/doc/process/weekly/week-5/members/luoyuehang-weekly-summary-04.md new file mode 100644 index 0000000..97ce817 --- /dev/null +++ b/doc/process/weekly/week-5/members/luoyuehang-weekly-summary-04.md @@ -0,0 +1,33 @@ + +# 个人周总结-第4周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | -------- | -------- | -------- | +| 1 | 需求分析与理解 | 完成 | 仔细研读了项目需求文档,明确了管理员、维修人员、学生三类用户的功能需求和业务流程 | +| 2 | 设计规范制定 | 完成 | 与协作者共同确定了Web端与移动端统一的设计风格、色彩体系和基础组件规范 | +| 3 | 维修人员APP原型设计 | 基本完成 | 完成了维修人员APP核心页面(设备监控、工单列表、抢单处理流程)的线框图设计 | +| 4 | 管理员平台协作设计 | 部分完成 | 协助完成了管理员平台"工单跟踪"模块的初步界面设计 | +| 5 | 设计评审与优化 | 完成 | 参与了内部设计评审会议,根据反馈意见对维修人员APP原型进行了初步优化 | + +## 对团队工作的建议 + +1. **设计协作:** 建议建立团队共享的设计资源库,统一图标、组件等设计元素; +2. **沟通机制:** 建议固定每日站会时间,及时同步设计进展和遇到的问题; +3. **技术预研:** 建议前端开发提前了解接口数据结构,确保设计与开发衔接顺畅。 + +## 小结 + +1. **设计产出:** 完成了维修人员APP的主体原型设计,确立了项目整体的视觉风格; +2. **协作成效:** 与协作者配合良好,保持了Web端与移动端设计风格的一致性; +3. **技能提升:** 通过实际项目实践,提升了移动端界面设计的专业能力; +4. **后续计划:** 根据评审反馈完善设计细节,并开始准备前端开发环境搭建。 + diff --git a/doc/process/weekly/week-5/members/wanglei-weekly-plan-05.md b/doc/process/weekly/week-5/members/wanglei-weekly-plan-05.md new file mode 100644 index 0000000..f0d78ee --- /dev/null +++ b/doc/process/weekly/week-5/members/wanglei-weekly-plan-05.md @@ -0,0 +1,33 @@ +# 个人周计划-第5周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2023-10-20 +**结束时间:** 2023-10-26 + + +## 本周任务计划安排 + +|序号| 计划内容| 协作人 | 情况说明 | +| ---- | ------ | ------| ------- | +| 1 | 确定分工 | 组员 | 2025-10-20 开会细分确定团队分工, | +| 2 | 完善数据库 | 曹峻茂 | 对于数据库的设计进行完善 | +| 3 | 测试 | 个人 | 编写测试程序,测试对于数据的处理是否达到要求 | + + +## 小结 + +1. **学习需求:** 学习数据库相关知识和处理数据; +2. **知识储备:** 提前学习后续需要使用的知识,为后续的后端开发做准备; +3. **文档撰写:** 结合课内知识学习需求文档的撰写。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-5/members/wanglei-weekly-summary-04.md b/doc/process/weekly/week-5/members/wanglei-weekly-summary-04.md new file mode 100644 index 0000000..b94bfe3 --- /dev/null +++ b/doc/process/weekly/week-5/members/wanglei-weekly-summary-04.md @@ -0,0 +1,39 @@ + +# 个人周总结-第4周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + +## 本周任务完成情况 + +序号 总结内容 是否完成 情况说明 +1 MySQL数据库环境搭建与配置 完成 成功安装MySQL 8.0.43,创建water_management数据库,配置数据库连接环境变量,解决数据库连接权限问题 +2 MQTT数据处理器开发 完成 实现基于Java的MQTT数据接收、解析和处理模块,支持制水机和供水机两种设备类型的数据处理 +3 数据库表结构设计与创建 完成 设计并创建device_data设备数据表和device_alert告警表,建立完整的数据存储结构 +4 数据解析与业务逻辑实现 完成 实现JSON数据解析、设备状态判断、异常检测和自动告警生成功能 +5 项目集成与测试 完成 成功将数据处理模块集成到智慧饮水系统项目中,完成多组测试数据的验证 + +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + + +1. **技能学习:** 小组成员各自开展自己所负责部分的个人技能的学习; +2. **项目管理:** PM及时推进项目进度,确保工作有条不紊; +3. **计划制定:** 根据本周任务完成情况与下一阶段文档提交要求,制定团队任务计划。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-5/members/zhanghongwei-weekly-plan-5.md b/doc/process/weekly/week-5/members/zhanghongwei-weekly-plan-5.md new file mode 100644 index 0000000..1e2736e --- /dev/null +++ b/doc/process/weekly/week-5/members/zhanghongwei-weekly-plan-5.md @@ -0,0 +1,25 @@ +# 个人周计划-第5周 + +## 姓名和起止时间 + +**姓  名:** 张红卫 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-20 +**结束时间:** 2025-10-26 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +|------|----------|--------|----------| +| 1 | 学习界面设计原则 | 个人 | 学习用户体验设计基础,了解管理平台界面设计规范 | +| 2 | 设计管理人员Web页面原型 | 个人 | 基于项目需求,设计管理人员监控数据、查看告警等核心功能界面 | +| 3 | 界面设计评审与优化 | 组员 | 组织小组评审原型设计,收集反馈并进行迭代优化 | +| 4 | 前端技术预研 | 个人 | 了解Web前端开发技术,为后续界面实现做准备 | + +## 小结 + +1. **学习需求:** 希望有原型设计工具的系统教学,特别是针对管理类系统的设计模式; +2. **知识储备:** 需要加强前端开发知识,特别是响应式布局和组件化开发; +3. **协作支持:** 需要与后端开发同学沟通接口需求,确保设计可行性。 + +--- diff --git a/doc/process/weekly/week-5/members/zhanghongwei-weekly-summary-04.md b/doc/process/weekly/week-5/members/zhanghongwei-weekly-summary-04.md new file mode 100644 index 0000000..8def47e --- /dev/null +++ b/doc/process/weekly/week-5/members/zhanghongwei-weekly-summary-04.md @@ -0,0 +1,31 @@ +# 个人周总结-第4周 + +## 姓名和起止时间 + +**姓  名:** 张红卫 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | -------- | -------- | -------- | +| 1 | 需求获取 | 完成 | 2025-10-10与肖雄仁老师面对面沟通,明确了校园直饮矿化水运维平台的具体需求 | +| 2 | 学习传输数据 | 部分完成 | 初步学习了HTTP协议的请求响应模型、状态码含义、RESTful API设计原则 | +| 3 | 确定分工 | 完成 | 2025-10-13团队开会明确了各成员分工,确定了各自负责的开发模块 | +| 4 | 学习数据接口服务 | 进行中 | 初步学习了Spring Boot框架基础、Controller层编写、数据库连接配置 | + +## 对团队工作的建议 + +1. **技术互助:** 建议团队成员根据各自技术专长开展互帮互助,共同提升开发能力; +2. **进度协调:** 建议定期同步开发进度,确保各模块开发协调一致; + +## 小结 + +1. **需求分析:** 通过与老师沟通,深入理解了校园直饮矿化水物联网运维平台的功能需求; +2. **技术准备:** 开始学习数据传输和Web服务开发相关知识,为后续开发做准备; +3. **团队协作:** 分工明确,团队协作顺畅,项目推进有序; +4. **文档学习:** 结合课内知识学习需求文档的撰写规范; + +--- diff --git a/doc/process/weekly/week-5/members/zhoujingyou-weekly-plan-05.md b/doc/process/weekly/week-5/members/zhoujingyou-weekly-plan-05.md new file mode 100644 index 0000000..ad0db49 --- /dev/null +++ b/doc/process/weekly/week-5/members/zhoujingyou-weekly-plan-05.md @@ -0,0 +1,26 @@ +# 个人周计划-第5周 + +## 姓名和起止时间 + +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-20 +**结束时间:** 2025-10-26 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|---------------|-----|-----------------------------| +| 1 | 完成学生端首页UI细化设计 | 个人 | 对首页的模块布局、字体、配色进行优化,完善信息展示逻辑 | +| 2 | 设计“扫码用水”功能页面 | 个人 | 设计扫码界面与用水反馈界面,优化扫码流程的交互体验 | +| 3 | 设计“查看水质”页面 | 个人 | 设计展示水质数据的图表与数据模块,突出清晰的视觉层次 | +| 4 | 设计“用水记录”页面 | 个人 | 设计用水记录查询界面,增加时间筛选与统计功能展示 | +| 5 | 设计“水机位置”页面 | 个人 | 完成地图定位界面UI设计,加入搜索与筛选功能布局 | +| 6 | 团队UI评审与反馈整理 | 组员 | 与团队进行设计评审会议,收集意见并记录优化方向 | + +## 小结 + +1. **设计目标:** 本周主要目标是完成学生端各个功能页面的UI设计,形成统一的视觉风格与交互逻辑; +2. **学习提升:** 深入学习UI交互设计与响应式布局技巧,提高界面层次感与用户体验; +3. **协作重点:** 与后端确认页面所需数据接口结构,确保后续开发阶段数据对接顺畅; +4. **下周展望:** 完成学生端UI优化及交互原型后,开始进入前端功能开发阶段。 diff --git a/doc/process/weekly/week-5/members/zhoujingyou-weekly-summary-04.md b/doc/process/weekly/week-5/members/zhoujingyou-weekly-summary-04.md new file mode 100644 index 0000000..93b585d --- /dev/null +++ b/doc/process/weekly/week-5/members/zhoujingyou-weekly-summary-04.md @@ -0,0 +1,31 @@ +# 个人周总结-第4周 + +## 姓名和起止时间 + +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-13 +**结束时间:** 2025-10-19 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|----|--------------------------------|------|------------------------------------------------| +| 1 | 分析需求文档并与团队沟通,明确前端设计的核心功能模块 | 完成 | 已深入阅读项目需求文档,明确了学生端APP的主要功能与设计方向,并与团队讨论确认前端开发重点 | +| 2 | 制定UI设计方案,规划学生端、维修人员端和Web端的界面风格 | 完成 | 明确了整体设计风格为简洁清新风,确定主色调为蓝白系,规划了主要功能模块的布局与交互方式 | +| 3 | 完成首页和功能模块的初步UI设计,关注色彩、字体和布局 | 基本完成 | 已完成首页与部分核心功能页面的UI设计初稿,关注了可读性与层次感,并进行初步评审 | +| 4 | 继续完善学生端APP的UI设计,加入动画效果和交互细节 | 部分完成 | 已设计部分交互细节与动画效果,收集团队反馈后计划下周进一步优化体验 | +| 5 | 开始前端页面布局开发,使用HTML & CSS编写基本结构 | 进行中 | 已搭建学生端基础页面框架,正在完善响应式布局与后端接口对接测试 | + +## 对团队工作的建议 + +1. **接口文档提前共享:** 建议后端开发尽早提供学生端接口数据结构说明,以便前端更高效地完成数据对接; +2. **设计风格统一:** 建议团队建立统一的UI组件库或样式规范,确保多端界面一致性; +3. **阶段评审机制:** 每周可定期进行一次UI评审或开发同步会,促进协作效率。 + +## 小结 + +1. **设计成果:** 本周完成了学生端整体UI设计方案,初步确定了APP界面布局与色彩风格; +2. **开发进展:** 完成了首页与部分页面的前端结构搭建,为后续功能开发奠定基础; +3. **学习提升:** 在本周过程中加强了对HTML、CSS布局及UI交互设计的掌握; +4. **后续计划:** 下周将重点完善交互动画与页面细节,实现接口数据对接与动态展示功能。 diff --git a/doc/process/weekly/week-6/group/meeting-minutes-6.md b/doc/process/weekly/week-6/group/meeting-minutes-6.md new file mode 100644 index 0000000..d804946 --- /dev/null +++ b/doc/process/weekly/week-6/group/meeting-minutes-6.md @@ -0,0 +1,41 @@ +# 小组会议纪要-第6周 + +## 会议记录概要 + +**团队名称:** 1班-汪汪队 +**指导老师:** 肖雄仁 +**主 持 人:** 曹峻茂 +**记录人员:** 张红卫 +**会议主题:** 原型界面评审与功能完善 +**会议地点:** 宿舍 +**会议时间:** 2025-10-27 12:30-13:30 +**纪录时间:** 2025-10-27 18:00 +**参与人员:** 曹峻茂、张红卫、罗月航、周竞由、王磊 + +--- + +## 会议内容 + +### 1. 原型界面初步展示与交流 + +前端小组向指导老师展示当前实现的基本功能原型界面,包括学生端、维修人员端和管理员端的主要功能模块,再根据老师的指导意见进行调整与修改。 + +### 2. 原型界面完善与美化计划 + +基于老师和小组成员的反馈,前端团队将对原型界面进行进一步的完善与美化。 + + +### 3. 数据库设计与完善 + +后端团队汇报了当前数据库设计的进展情况,针对尚未完全实现的部分进行进一步完善。 + +### 4. 原型界面与用例文档协同优化 + +会议决定加强前端与后端团队的协作配合,针对原型界面设计与用例文档之间存在的差异进行进一步的相互调整与完善。 + +### 5.具体任务安排 +- 管理人员Web原型界面的进一步优化:张红卫 +- 学生端APP原型界面的进一步优化:周竞由 +- 维修人员APP原型界面的进一步优化:罗月航 +- 数据库的进一步完善;王磊 +- 用例文档的再一不调整:曹峻茂 \ No newline at end of file diff --git a/doc/process/weekly/week-6/group/weekly-plan-6.md b/doc/process/weekly/week-6/group/weekly-plan-6.md new file mode 100644 index 0000000..9ca7433 --- /dev/null +++ b/doc/process/weekly/week-6/group/weekly-plan-6.md @@ -0,0 +1,36 @@ +# 小组周计划-第6周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-27 +**结束时间:** 2025-11-2 + +## 本周任务计划安排 + + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|----|--------------|-------------|------------------------------| +| 1 | 确定本周分工 | 全体组员 | 2023-10-27 开会细分确定团队分工,统一开发工具 | +| 2 | 商讨原型设计 | 全体组员 | 与老师联系获取老师关于原型的建议 | +| 3 | 学习 | 全体组员 | 继续学习相关知识,在做中学 | +| 4 | 继续完善数据库设计和实现 | 曹峻茂,王磊 | 继续完成数据库设计和实现 | +| 5 | 完善当前的原型页面设计 | 罗月航,周竞由,张红卫 | 完善当前的原型页面 | + |6|根据讨论结果更新需求文档|曹峻茂|更新需求文档| +## 小结 + +1. **增强自信心:** 小组成员应增强自信心,发挥想象力和创造力,在原型设计环节积极思考。 +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +4. **项目管理:** PM及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-6/group/weekly-summary-5.md b/doc/process/weekly/week-6/group/weekly-summary-5.md new file mode 100644 index 0000000..0b61f90 --- /dev/null +++ b/doc/process/weekly/week-6/group/weekly-summary-5.md @@ -0,0 +1,34 @@ +# 小组周总结-第5周 + +## 团队名称和起止时间 + +**团队名称:** 1班-汪汪队 +**开始时间:** 2023-10-19 +**结束时间:** 2025-10-26 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|------|----------|------|------------------------------| +| 1 | 确定分工 | 完成 | 2023-10-20 开会细分确定团队分工,统一开发工具 | +| 2 | 完善数据库的构建 | 进行中 | 在之前构建的数据传输模块基础上设计数据库 | +| 3 | 完成用例文档以及更新需求文档 | 基本完成 | 设计用例文档并根据原型页面更新需求文档 | +| 4 | 根据用例图完成原型界面 | 进行中 | 根据用例文档完成原型界面 | + + + +## 小结 + +1. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +2. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +3. **项目管理:** 项目进度管理杨安然同学及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 +--- \ No newline at end of file diff --git a/doc/process/weekly/week-6/members/caojunmao-weekly-plan-6.md b/doc/process/weekly/week-6/members/caojunmao-weekly-plan-6.md new file mode 100644 index 0000000..6726bdc --- /dev/null +++ b/doc/process/weekly/week-6/members/caojunmao-weekly-plan-6.md @@ -0,0 +1,34 @@ +# 个人周计划-第6周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-27 +**结束时间:** 2025-11-2 + + +## 本周任务计划安排 +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|-------------|-----|-----------------------| +| 1 | 确定分工 | 组员 | 2023-10-27 开会细分确定团队分工 | + |2| 与老师讨论 |组员| 与老师讨论当前用例和原型设计 | +| 2 | 继续协调完成数据库设计 | 王磊 | 继续为王磊完成数据库设计提供支持 | +| 4 | 更新需求文档 | 个人 | 根据本周计划和更改更新需求文档 | + +## 小结 + +1. **学习需求:** 希望能有对于用例文档设计和需求文档格式的教学; +2. **知识储备:** 学习后续需要使用的知识,为后续的开发做准备; +3. **文档撰写:** 结合课内知识学习需求文档的撰写。 +4. **代码实现** 参与数据库设计 +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-6/members/caojunmao-weekly-summary-05.md b/doc/process/weekly/week-6/members/caojunmao-weekly-summary-05.md new file mode 100644 index 0000000..50ad560 --- /dev/null +++ b/doc/process/weekly/week-6/members/caojunmao-weekly-summary-05.md @@ -0,0 +1,37 @@ +# 个人周总结-第5周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-20 +**结束时间:** 2025-10-26 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | ------------------------------------------------ |------|--------------------------------------------------------------| +| 1 | 确定分工 | 完成 | 2023-10-20 开会细分确定团队分工 | +| 2 | 协调完成数据库设计 | 进行中 | 为王磊完成数据库设计提供支持 | +| 3 | 设计用例文档 | 完成 | 完成用例文档 | +| 4 | 更新需求文档 | 进行中 | 在原型界面完成后更新需求文档 | +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + +1. **设计用例文档:** 完成了用例文档初稿; +2. **技能学习:** 学习了用例图,用例文档相关知识; +3. **项目管理:** 作为PM及时推进项目进度,确保工作有条不紊; +4. **团队协作**:与团队成员保持良好的沟通协作,确保设计方向与产品需求一致 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-6/members/luoyuehang-weekly-plan-06.md b/doc/process/weekly/week-6/members/luoyuehang-weekly-plan-06.md new file mode 100644 index 0000000..f064720 --- /dev/null +++ b/doc/process/weekly/week-6/members/luoyuehang-weekly-plan-06.md @@ -0,0 +1,23 @@ +# 个人周计划-第6周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-27 +**结束时间:** 2025-11-02 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | 维修人员APP原型优化完善 | 个人 | 根据第二周评审反馈,优化界面布局、交互细节和视觉效果,提升用户体验 | +| 2 | 复杂交互功能实现 | 个人 | 在Axure中实现完整的交互动效,包括页面跳转、状态切换、数据加载等交互场景 | +| 3 | 原型可用性测试 | 组员 | 组织小范围原型测试,收集用户反馈并记录改进点 | +| 4 | 前端开发准备 | 组员 | 与前端开发同学沟通,确保设计稿的技术可行性,提供必要的设计资源 | + +## 小结 + +1. **用户反馈**:需要协调部分同学参与原型测试,收集真实用户的使用反馈; +2. **开发协作**:需要前端开发同学提前介入,评估设计实现的技术难度和可行性。 + diff --git a/doc/process/weekly/week-6/members/luoyuehang-weekly-summary-05.md b/doc/process/weekly/week-6/members/luoyuehang-weekly-summary-05.md new file mode 100644 index 0000000..f25b91e --- /dev/null +++ b/doc/process/weekly/week-6/members/luoyuehang-weekly-summary-05.md @@ -0,0 +1,30 @@ +# 个人周总结-第5周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-20 +**结束时间:** 2025-10-26 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | -------- | -------- | -------- | +| 1 | 维修人员APP原型UI设计 | 完成 | 使用Axure完成了维修人员APP部分核心页面的原型设计,包括登录页、工单处理等主要界面 | +| 2 | 界面交互流程设计 | 完成 | 设计了完整的用户交互流程,实现了页面间的跳转逻辑和状态切换 | +| 3 | 原型评审与优化 | 完成 | 参与团队原型评审会议,根据反馈意见对界面布局和交互细节进行了优化调整 | +| 4 | 设计规范统一 | 完成 | 与团队成员共同确定了颜色规范、组件样式和交互模式,保持设计一致性 | +| 5 | 原型交互功能实现 | 部分完成 | 在Axure中实现了基础的交互动效,部分复杂交互还需进一步完善 | + +## 对团队工作的建议 + +1. **设计协作**:建议建立团队共享的设计组件库,提高设计效率和一致性; +2. **评审机制**:建议每周固定时间进行设计评审,及时发现问题并优化; +3. **文档同步**:设计变更应及时更新相关文档,确保前后端开发理解一致; + +## 小结 + +1. **设计产出**:完成了维修人员APP的完整原型设计,涵盖了设备监控、工单处理等核心功能模块; +2. **技术提升**:通过Axure工具的使用,提升了原型设计能力和交互设计思维; +3. **团队协作**:与团队成员保持良好的沟通协作,确保设计方向与产品需求一致; diff --git a/doc/process/weekly/week-6/members/wanglei-weekly-plan-06.md b/doc/process/weekly/week-6/members/wanglei-weekly-plan-06.md new file mode 100644 index 0000000..e858784 --- /dev/null +++ b/doc/process/weekly/week-6/members/wanglei-weekly-plan-06.md @@ -0,0 +1,33 @@ +# 个人周计划-第6周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2023-10-27 +**结束时间:** 2023-11-2 + + +## 本周任务计划安排 + +|序号| 计划内容| 协作人 | 情况说明 | +| ---- | ------ | ------| ------- | +| 1 | 确定分工 | 组员 | 2025-10-27 开会细分确定团队分工, | +| 2 | 解决项目编译问题 | 个人 | 重点解决当前项目的编译错误和依赖配置问题 | +| 3 | 系统功能测试 | 个人 | 完成系统数据库功能的完整测试 | + + +## 小结 + +1. **技术重点:** 深入解决项目技术难题,完善系统稳定性; +2. **知识储备:** 提前学习后续需要使用的知识,为后续的后端开发做准备; +3. **文档撰写:** 结合课内知识学习需求文档的撰写。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-6/members/wanglei-weekly-summary-05.md b/doc/process/weekly/week-6/members/wanglei-weekly-summary-05.md new file mode 100644 index 0000000..3ed3755 --- /dev/null +++ b/doc/process/weekly/week-6/members/wanglei-weekly-summary-05.md @@ -0,0 +1,43 @@ + +# 个人周总结-第5周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-20 +**结束时间:** 2025-10-26 + +## 本周任务完成情况 + +序号 总结内容 是否完成 情况说明 +1 数据库设计与完善 基本完成 完成校园直饮矿化水系统的完整数据库设计,包括设备管理、传感器数据、业务逻辑等核心表结构 +2 实体类与数据访问层开发 完成 完成所有实体类(Entity)和数据访问层(Repository)的开发,包括设备、区域、工单、告警等核心模块 +3 MQTT数据转发服务实现 完成 实现完整的MQTT数据接收、解析、存储和转发服务,支持多客户端数据分发 +4 系统集成与编译测试 进行中 完成项目基础架构搭建,正在进行系统集成测试和编译问题排查 + + +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + + +1.技术收获: 深入掌握了Spring Boot项目架构设计、JPA数据持久化、MQTT实时通信等后端开发核心技术; + +2.问题解决: 成功解决了数据库连接配置、项目依赖管理、包结构设计等关键技术问题; + +3.项目管理: 在实践中学习了大型项目的模块化设计和团队协作开发模式; + +4.后续重点: 需要重点解决当前的项目编译问题,完善业务逻辑层和服务层实现,推进前后端联调测试。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-6/members/zhanghongwei-weekly-plan-6.md b/doc/process/weekly/week-6/members/zhanghongwei-weekly-plan-6.md new file mode 100644 index 0000000..2952d69 --- /dev/null +++ b/doc/process/weekly/week-6/members/zhanghongwei-weekly-plan-6.md @@ -0,0 +1,24 @@ +# 个人周计划-第6周 + +## 姓名和起止时间 + +**姓  名:** 张红卫 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-27 +**结束时间:** 2025-11-02 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +|------|----------|--------|----------| +| 1 | 完善界面原型设计 | 个人 | 基于第5周反馈,对管理人员Web页面原型进行美化和细节优化 | +| 2 | 深入学习Axure工具 | 个人 | 系统学习Axure交互功能和组件库使用,提升原型设计效率 || +| 3 | 准备原型设计评审 | 组员 | 整理完善后的原型设计,准备进行小组评审并加以调整 | + +## 小结 + +1. **工具技能:** 还需重点掌握Axure的动态面板、交互事件等高级功能 +2. **设计能力:** 提升界面美化和用户体验细节处理能力 +3. **知识储备:** 了解前端html的相关知识 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-6/members/zhanghongwei-weekly-summary-05.md b/doc/process/weekly/week-6/members/zhanghongwei-weekly-summary-05.md new file mode 100644 index 0000000..e5f1ae2 --- /dev/null +++ b/doc/process/weekly/week-6/members/zhanghongwei-weekly-summary-05.md @@ -0,0 +1,37 @@ +# 个人周总结-第5周 + +## 基本信息 +**姓  名:** 张红卫 +**团队名称:** 1班-汪汪队 +**总结周期:** 2025年10月20日 - 2025年10月26日 + +## 本周任务完成情况 + +### ✅ 已完成任务 +| 序号 | 任务内容 | 完成状态 | 成果输出 | +|------|----------|----------|----------| +| 1 | 学习界面设计原则 | 进行中 | 对界面原型设计已经有了大致的了解 | +| 2 | 设计管理人员Web页面原型 | 进行中 | 基本完成基本功能界面的基础原型设计 | +| 3 | 界面设计评审与优化 | 进行中 | 组织小组评审,收集反馈并进行了初步调整 | +| 4 | 前端技术预研 | 进行中 | 了解了Web前端开发技术,为后续界面实现打下基础 | + +## 主要成果与进展 + +1. **知识学习方面** + - 系统学习了用户体验设计 + - 掌握了管理平台界面设计的常用规范 + +2. **实践产出方面** + - 基本完成了管理人员Web页面的基础原型设计 + - 通过小组评审获得了有价值的改进建议 + - 建立了初步的前端技术认知框架 + +## 存在问题与挑战 + +1. **技能提升需求** + - 原型设计工具使用熟练度有待提高 + - 需要系统学习管理类系统的设计模式 + - 前端开发知识储备需要进一步加强 + +## 总结 +本周按计划基本完成了界面设计学习和基础原型设计工作,下一步重点是在现有基础上进行界面美化优化。 \ No newline at end of file diff --git a/doc/process/weekly/week-6/members/zhoujingyou-weekly-plan-06.md b/doc/process/weekly/week-6/members/zhoujingyou-weekly-plan-06.md new file mode 100644 index 0000000..d0ed527 --- /dev/null +++ b/doc/process/weekly/week-6/members/zhoujingyou-weekly-plan-06.md @@ -0,0 +1,26 @@ +# 个人周计划-第6周 + +## 姓名和起止时间 + +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-27 +**结束时间:** 2025-11-02 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|---------------------|-----|-------------------------| +| 1 | 完善学生端整体原型结构 | 个人 | 整合各功能页面原型,统一页面跳转逻辑与交互路径 | +| 2 | 优化首页与导航交互体验 | 个人 | 调整首页信息层次,完善底部导航与页面切换动效 | +| 3 | 深化“扫码用水”与“查看水质”原型细节 | 个人 | 增加交互提示、反馈状态与数据展示动态示意 | +| 4 | 优化“用水记录”与“水机位置”页面原型 | 个人 | 调整筛选、搜索交互流程,增强界面信息层次 | +| 5 | 统一视觉与交互规范 | 个人 | 制定组件风格、配色与字体规范,保证整体一致性 | +| 6 | 团队原型评审与反馈汇总 | 组员 | 组织团队评审会议,收集意见并整理优化方向 | + +## 小结 + +1. **设计目标:** 本周重点是完善学生端整体交互原型,使页面之间逻辑清晰、风格统一; +2. **学习提升:** 学习Axure/Figma中复杂交互设计与动态原型制作技巧,提高原型表现力; +3. **协作重点:** 与组员统一原型展示结构,确保各功能模块交互方式一致; +4. **下周展望:** 根据评审反馈进行最终优化,准备学生端原型的阶段性展示与文档整理。 diff --git a/doc/process/weekly/week-6/members/zhoujingyou-weekly-summary-05.md b/doc/process/weekly/week-6/members/zhoujingyou-weekly-summary-05.md new file mode 100644 index 0000000..dd09f5c --- /dev/null +++ b/doc/process/weekly/week-6/members/zhoujingyou-weekly-summary-05.md @@ -0,0 +1,28 @@ +# 个人周总结-第5周 + +## 姓名和起止时间 + +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-20 +**结束时间:** 2025-10-26 + +## 本周任务完成情况 + +| 序号 | 计划内容 | 完成情况 | 说明 | +|----|--------------|------|----------------------------| +| 1 | 学生端首页UI细化设计 | 基本完成 | 完成首页模块布局、配色与字体优化,统一整体视觉风格 | +| 2 | “扫码用水”功能页面设计 | 基本完成 | 完成扫码界面与反馈界面初版设计,待补充交互流程细节 | +| 3 | “查看水质”页面设计 | 基本完成 | 完成水质数据图表布局与信息展示结构,视觉层次清晰 | +| 4 | “用水记录”页面设计 | 基本完成 | 完成界面框架设计,计划下周完善筛选功能原型与统计展示 | +| 5 | “水机位置”页面设计 | 部分完成 | 完成地图定位界面与搜索布局草图,后续优化交互细节 | +| 6 | 团队UI评审与反馈整理 | 基本完成 | 参与团队设计评审会议,收集多项改进建议并形成优化清单 | + +## 本周总结 + +1. **阶段成果:** 已完成学生端主要功能页面的UI原型设计,基本确定整体视觉风格与信息架构; +2. **优化进展:** 针对首页与水质页面进行了细化与调整,提升了信息展示的清晰度与一致性; +3. **存在问题:** 个别功能页面的交互逻辑仍需补充(如扫码反馈、用水记录筛选逻辑); +4. **改进方向:** 下周将继续完善交互细节,统一页面跳转逻辑,形成完整的学生端原型结构; +5. **学习体会:** 通过本周设计实践,对UI一致性与用户体验设计有了更深入的理解。 + diff --git a/doc/process/weekly/week-7/group/meeting-minutes-7.md b/doc/process/weekly/week-7/group/meeting-minutes-7.md new file mode 100644 index 0000000..f3ce8de --- /dev/null +++ b/doc/process/weekly/week-7/group/meeting-minutes-7.md @@ -0,0 +1,34 @@ +# 小组会议纪要-第7周 + +## 会议记录概要 + +**团队名称:** 1班-汪汪队 +**指导老师:** 肖雄仁 +**主 持 人:** 曹峻茂 +**记录人员:** 张红卫 +**会议主题:** 继原型界面评审后的进一步完善 +**会议地点:** 宿舍 +**会议时间:** 2025-11-3 12:30-13:30 +**纪录时间:** 2025-11-3 18:00 +**参与人员:** 曹峻茂、张红卫、罗月航、周竞由、王磊 + +--- + +## 会议内容 + +### 1. 原型界面最后完善 + +继原型界面评审后对原型界面做风格上的统一与部分问题在小班讨论展示前做最后的完善修改。 + +### 2. 数据库设计与完善 + +针对小班讨论的展示,做数据库设计的同学做进一步的完善与详解。 + +### 3. 原型界面与用例文档协同优化 + +前端与后端团队的协作配合,针对原型界面设计与用例文档之间存在的差异进行进一步的相互调整与完善。 + +### 4.具体任务安排 +- 原型界面的统一与完善:张红卫,罗月航,周竞由 +- 数据库的进一步完善:王磊 +- 用例文档的调整:曹峻茂 \ No newline at end of file diff --git a/doc/process/weekly/week-7/group/weekly-plan-7.md b/doc/process/weekly/week-7/group/weekly-plan-7.md new file mode 100644 index 0000000..3d289d4 --- /dev/null +++ b/doc/process/weekly/week-7/group/weekly-plan-7.md @@ -0,0 +1,36 @@ +# 小组周计划-第7周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-3 +**结束时间:** 2025-11-9 + +## 本周任务计划安排 + + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|----|-----------------------|-------------|-------------------------| +| 1 | 确定本周计划分工 | 全体组员 | 2023-11-3 开会确定计划以及团队分工 | +| 2 | 根据老师意见改正原型设计 | 罗月航,周竞由,张红卫 | 改正原型页面设计的问题 | +| 3 | 学习 | 全体组员 | 继续学习相关知识,在做中学 | +| 4 | 在power design上展示数据库设计 | 王磊 | 学习在power design上展示数据库设计 | +| 5 | 根据最终原型生成用例文档最终稿 | 曹峻茂 | 生成用例文档最终稿 | +|6|完成项目前景与范围文档和需求规格说明书| 曹峻茂 |完成并提交项目前景与范围文档和需求规格说明书| +## 小结 + +1. **增强自信心:** 小组成员应增强自信心,发挥想象力和创造力,在原型设计环节积极思考。 +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +4. **项目管理:** PM及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-7/group/weekly-summary-6.md b/doc/process/weekly/week-7/group/weekly-summary-6.md new file mode 100644 index 0000000..6e0c300 --- /dev/null +++ b/doc/process/weekly/week-7/group/weekly-summary-6.md @@ -0,0 +1,36 @@ +# 小组周总结-第6周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-27 +**结束时间:** 2025-11-2 + +## 本周任务计划安排 + + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|----|--------------|------|------------------------------| +| 1 | 确定本周分工 | 是 | 2023-10-27 开会细分确定团队分工,统一开发工具 | +| 2 | 商讨原型设计 | 是 | 与老师联系获取老师关于原型的建议 | +| 3 | 学习 | 是 | 继续学习相关知识,在做中学 | +| 4 | 继续完善数据库设计和实现 | 是 | 继续完成数据库设计和实现 | +| 5 | 完善当前的原型页面设计 | 进行中 | 完善当前的原型页面 | +|6|根据讨论结果更新需求文档| 是 |更新需求文档| +## 小结 + +1. **增强自信心:** 小组成员应增强自信心,发挥想象力和创造力,在原型设计环节积极思考。 +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +4. **项目管理:** PM及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-7/members/caojunmao-weekly-plan-7.md b/doc/process/weekly/week-7/members/caojunmao-weekly-plan-7.md new file mode 100644 index 0000000..7907c68 --- /dev/null +++ b/doc/process/weekly/week-7/members/caojunmao-weekly-plan-7.md @@ -0,0 +1,34 @@ +# 个人周计划-第7周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-3 +**结束时间:** 2025-11-9 + + +## 本周任务计划安排 +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|-----------------|-------------|-----------------------| +| 1 | 确定分工 | 组员 | 2023-11-3 开会确定计划和团队分工 | +|2| 与老师讨论 | 组员 | 与老师讨论当前用例和原型设计 | +| 4 | 根据最终原型完成用例文档最终稿 | 周竞由,罗月航,张红卫 | 完成用例文档最终稿 | +|5|完成项目前景与范围文档和需求规格说明书|小组成员|完成并提交项目前景与范围文档和需求规格说明书| + +## 小结 + +1. **学习需求:** 希望能有对于用例文档设计和需求文档格式的教学; +2. **知识储备:** 学习后续需要使用的知识,为后续的开发做准备; +3. **文档撰写:** 结合课内知识学习需求文档的撰写。 +4. **代码实现** 参与数据库设计 +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-7/members/caojunmao-weekly-summary-06.md b/doc/process/weekly/week-7/members/caojunmao-weekly-summary-06.md new file mode 100644 index 0000000..281713c --- /dev/null +++ b/doc/process/weekly/week-7/members/caojunmao-weekly-summary-06.md @@ -0,0 +1,37 @@ +# 个人周总结-第6周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-27 +**结束时间:** 2025-11-2 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | ------------------------------------------------ |------|--------------------------------------------------------------| +| 1 | 确定分工 | 完成 | 2023-10-27 开会细分确定团队分工 | +|2| 与老师讨论 | 完成 | 与老师讨论当前用例和原型设计 | +| 2 | 继续协调完成数据库设计 | 王完成 | 继续为王磊完成数据库设计提供支持 | +| 4 | 更新需求文档 | 完成 | 根据本周计划和更改更新需求文档 | +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + +1. **设计用例文档:** 完成了用例文档初稿; +2. **技能学习:** 学习了用例图,用例文档相关知识; +3. **项目管理:** 作为PM及时推进项目进度,确保工作有条不紊; +4. **团队协作**:与团队成员保持良好的沟通协作,确保设计方向与产品需求一致 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-7/members/luoyuehang-weekly-plan-07.md b/doc/process/weekly/week-7/members/luoyuehang-weekly-plan-07.md new file mode 100644 index 0000000..fdea60b --- /dev/null +++ b/doc/process/weekly/week-7/members/luoyuehang-weekly-plan-07.md @@ -0,0 +1,25 @@ +# 个人周计划-第7周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-03 +**结束时间:** 2025-11-09 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | 原型设计改良优化 | 个人 | 根据与老师的会议反馈,针对性地改良维修人员APP原型设计,优化用户体验和界面布局 | +| 2 | 用例文档完善协作 | PM | 协助产品经理完成用例文档最终稿,确保功能描述准确性和完整性 | +| 3 | 交互细节优化 | 个人 | 针对老师提出的具体建议,优化原型中的交互流程和动效设计 | +| 4 | 设计评审准备 | 组员 | 准备改良后的原型设计评审材料,整理修改说明和优化点 | +| 5 | 用户流程验证 | 组员 | 组织内部测试,验证改良后的用户流程是否满足老师提出的要求 | + +## 小结 + +1. **需求明确**:需要老师提供更具体的改良建议和优化方向; +2. **协作支持**:需要PM及时同步用例文档的修改内容和进度; +3. **技术指导**:希望在交互设计和用户体验方面获得专业指导; +4. **测试资源**:需要协调团队成员参与原型测试,收集反馈意见。 diff --git a/doc/process/weekly/week-7/members/luoyuehang-weekly-summary-06.md b/doc/process/weekly/week-7/members/luoyuehang-weekly-summary-06.md new file mode 100644 index 0000000..cf8d681 --- /dev/null +++ b/doc/process/weekly/week-7/members/luoyuehang-weekly-summary-06.md @@ -0,0 +1,34 @@ +# 个人周总结-第六周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-27 +**结束时间:** 2025-11-02 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | -------- | -------- | -------- | +| 1 | 维修人员APP原型优化完善 | 完成 | 根据层级系统重新设计设备巡检模块,实现了市区-片区-学校-制水机-供水机-终端机的完整层级导航 | +| 2 | 制水机详情页面开发 | 完成 | 开发了独立的制水机详情页面,包含完整的TDS数据监控、滤芯状态、运行参数等功能 | +| 3 | 设备关系可视化 | 完成 | 实现了制水机、供水机、终端机之间的供水关系可视化,清晰展示设备间的连接关系 | +| 4 | 交互流程优化 | 完成 | 优化了页面间的跳转逻辑和状态管理,提升了用户体验 | +| + +## 对团队工作的建议 + +1. **数据接口规范**:建议后端团队按照新的层级结构提供设备数据接口,确保前后端数据格式一致; +2. **测试协作**:需要测试同学协助进行原型功能测试,特别是层级导航和设备关系的正确性验证; +3. **文档更新**:设计变更后需要及时更新相关技术文档,便于后续开发和维护; +4. **性能优化**:建议对层级数据的加载进行性能优化,确保在大数据量下的流畅体验。 + +## 小结 + +1. **技术成果**:成功实现了基于层级系统的设备管理界面,完整呈现了设备间的供水关系; +2. **用户体验**:通过层级导航和可视化关系图,大大提升了用户对设备布局的理解; +3. **代码质量**:采用模块化设计,代码结构清晰,便于后续功能扩展; +4. **问题解决**:在实现过程中解决了层级数据管理和状态同步的技术难点; + + diff --git a/doc/process/weekly/week-7/members/wanglei-weekly-plan-07.md b/doc/process/weekly/week-7/members/wanglei-weekly-plan-07.md new file mode 100644 index 0000000..e1535f0 --- /dev/null +++ b/doc/process/weekly/week-7/members/wanglei-weekly-plan-07.md @@ -0,0 +1,33 @@ +# 个人周计划-第7周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2023-11-2 +**结束时间:** 2023-11-9 + + +## 本周任务计划安排 + +|序号| 计划内容| 协作人 | 情况说明 | +| ---- | ------ | ------| ------- | +| 1 | 确定分工 | 组员 | 2025-11-3 开会细分确定团队分工, | +| 2 | 准备数据库设计讲解材料 | 个人 | 使用Power Design 演示讲解数据库 | +| 3 | 填充基础数据 | 个人 | 向数据库中填充区域、维修人员等基础业务数据,确保数据符合业务逻辑 | + + +## 小结 + +1. **技术重点:** 掌握PowerDesigner工具的使用,提升数据库设计与表达能力; +2. **协作重点** 与团队保持沟通,确保数据填充符合业务需求,支持后续开发; +3. **文档撰写:** 结合课内知识学习需求文档的撰写。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-7/members/wanglei-weekly-summary-06.md b/doc/process/weekly/week-7/members/wanglei-weekly-summary-06.md new file mode 100644 index 0000000..0e73dcb --- /dev/null +++ b/doc/process/weekly/week-7/members/wanglei-weekly-summary-06.md @@ -0,0 +1,43 @@ + +# 个人周总结-第6周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-10-27 +**结束时间:** 2025-11-2 + +## 本周任务完成情况 + +序号 总结内容 是否完成 情况说明 +1 数据库规范化设计与评审 完成 完成数据库第三范式改造,建立16个完整的外键约束,确保数据一致性和完整性 +2 项目编译问题解决 完成 彻底解决所有编译错误,包括Alert实体类重构、Repository层优化等关键技术问题 +3 系统功能完整测试 完成 完成数据库关联查询验证、API接口测试、外键约束测试,系统稳定运行 +4 数据库设计评审准备 完成 数据库设计完全符合评审要求,具备完整的参照完整性和业务逻辑约束 + + +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + + +1.技术收获: 掌握了数据库规范化设计与系统架构优化,深入理解了第三范式、外键约束、参照完整性等高级数据库概念,成功实践了Spring Boot全栈开发流程,提升了复杂技术问题的定位和解决能力。 + +2.问题解决: 成功根治了系统编译错误和数据一致性问题,通过重构Alert实体类、优化Repository层、完善Service业务逻辑,解决了因数据库结构调整引发的连锁技术问题,确保了系统稳定运行。 + +3.项目管理: 实践了"架构先行、业务后完善"的科学开发模式,建立了完整的测试验证流程和质量保障体系,通过模块化设计和迭代开发,为团队项目奠定了坚实的技术基础。 + +4.后续重点: 推进业务数据协调与系统集成测试,重点与其他组员确定具体业务数据结构,开展前后端联调工作,并在业务稳定后推进系统性能优化和部署运维。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-7/members/zhanghongwei-weekly-plan-7.md b/doc/process/weekly/week-7/members/zhanghongwei-weekly-plan-7.md new file mode 100644 index 0000000..df895f1 --- /dev/null +++ b/doc/process/weekly/week-7/members/zhanghongwei-weekly-plan-7.md @@ -0,0 +1,21 @@ +# 个人周计划-第7周 + +## 姓名和起止时间 + +**姓  名:** 张红卫 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-03 +**结束时间:** 2025-11-09 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +|------|----------|--------|----------| +| 1 | 对原型界面最后的完善 | 个人 | 基于第6周老师与组员间的会议反馈做最后完善工作 | +| 2 | 辅助完成用例文档 | 组员 | 在修改原型界面的同时与需求文档编写人员做最后敲定 | + +## 小结 + +1. **工具技能:** 还需重点掌握Axure各种组件的使用 +2. **设计能力:** 提升界面美化和用户体验细节处理能力 +3. **知识储备:** 还需继续了解前端html的相关知识 diff --git a/doc/process/weekly/week-7/members/zhanghongwei-weekly-summary-06.md b/doc/process/weekly/week-7/members/zhanghongwei-weekly-summary-06.md new file mode 100644 index 0000000..b224ef1 --- /dev/null +++ b/doc/process/weekly/week-7/members/zhanghongwei-weekly-summary-06.md @@ -0,0 +1,27 @@ +# 个人周总结-第6周 + +## 基本信息 +**姓  名:** 张红卫 +**团队名称:** 1班-汪汪队 +**总结周期:** 2025年10月27日 - 2025年11月2日 + +## 本周任务完成情况 + +### 已完成任务 +| 序号 | 任务内容 | 完成状态 | 成果输出 | +|------|----------|----------|----------| +| 1 | 原型界面设计完善 | 进行中 |在本周与老师进行评审后对原型界面进行进一步的完善 | +| 2 | 前端技术预研 | 进行中 | 了解了Web前端开发技术,为后续界面实现打下基础 | + +## 主要成果与进展 + +1. **知识学习方面** + - 与老师进行交流会更加深入的了解了如何深入和完善需求 + +2. **实践产出方面** + - 通过会议评审获得了有价值的改进建议 + - 建立了初步的前端技术认知框架 + + +## 总结 +本周按计划基本完成了界面设计学习和基础原型设计工作,下一步重点是在现有基础上进行界面美化优化。 \ No newline at end of file diff --git a/doc/process/weekly/week-7/members/zhoujingyou-weekly-plan-07.md b/doc/process/weekly/week-7/members/zhoujingyou-weekly-plan-07.md new file mode 100644 index 0000000..7d65f9a --- /dev/null +++ b/doc/process/weekly/week-7/members/zhoujingyou-weekly-plan-07.md @@ -0,0 +1,26 @@ +# 个人周计划-第7周 + +## 姓名和起止时间 + +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-03 +**结束时间:** 2025-11-09 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|---------------|-----|-----------------------------| +| 1 | 根据评审反馈优化学生端原型 | 个人 | 根据上周评审结果调整界面布局与交互逻辑,提升整体一致性 | +| 2 | 完善动态交互与页面细节 | 个人 | 补充页面切换动画、交互提示与按钮反馈效果 | +| 3 | 调整页面视觉层次与配色规范 | 个人 | 统一图标、文字与色彩体系,优化可读性与对比度 | +| 4 | 制作学生端原型展示文档 | 个人 | 输出原型展示稿与交互说明,便于团队成员理解设计逻辑 | +| 5 | 团队阶段性展示准备 | 组员 | 协助整理展示材料,协调团队展示顺序与讲解分工 | +| 6 | 后续开发衔接规划 | 组员 | 讨论从原型阶段到前端开发的对接方案,明确接口与任务分配 | + +## 小结 + +1. **设计目标:** 本周重点是根据反馈优化原型,补充动态交互与视觉细节,形成可展示的高保真原型版本; +2. **学习提升:** 学习动画交互与原型讲解技巧,提高设计展示与表达能力; +3. **协作重点:** 与组员共同完成阶段性展示,确保原型逻辑清晰、内容完整; +4. **下周展望:** 在原型展示结束后,正式进入前端开发准备阶段,进行组件划分与框架搭建。 diff --git a/doc/process/weekly/week-7/members/zhoujingyou-weekly-summary-06.md b/doc/process/weekly/week-7/members/zhoujingyou-weekly-summary-06.md new file mode 100644 index 0000000..221d022 --- /dev/null +++ b/doc/process/weekly/week-7/members/zhoujingyou-weekly-summary-06.md @@ -0,0 +1,42 @@ +# 个人周总结-第6周 + +## 姓名和起止时间 + +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-10-27 +**结束时间:** 2025-11-02 + +## 本周任务完成情况 + +| 序号 | 计划内容 | 完成情况 | 说明 | +|----|---------------------|-----|--------------------------------| +| 1 | 完善学生端整体原型结构 | 已完成 | 整合各功能页面原型,统一跳转逻辑与整体结构,提升原型连贯性 | +| 2 | 优化首页与导航交互体验 | 基本完成 | 调整首页信息层次与底部导航切换效果,部分动画待后续补充 | +| 3 | 深化“扫码用水”与“查看水质”原型细节 | 已完成 | 增加交互提示与动态反馈示意,优化数据展示模块的层次感 | +| 4 | 优化“用水记录”与“水机位置”页面原型 | 基本完成 | 完成筛选与搜索交互流程设计,部分界面细节待评审后修改 | +| 5 | 统一视觉与交互规范 | 已完成 | 制定组件样式规范(按钮、输入框、图表模块),形成统一UI风格 | +| 6 | 团队原型评审与反馈汇总 | 已完成 | 组织并参与团队原型评审会议,整理反馈并制定后续优化计划 | + +## 本周总结 + +1. **阶段成果:** + 本周完成了学生端整体原型的整合与交互逻辑完善,系统结构清晰,页面跳转顺畅。完成主要功能模块的视觉与交互统一,为后续展示与评审奠定了基础。 + +2. **优化进展:** + - 首页与导航部分优化了视觉层次与信息排布; + - “查看水质”页面采用图表模块展示数据,强化了可读性; + - 对“用水记录”与“水机位置”页面的搜索、筛选逻辑进行了初步验证。 + +3. **存在问题:** + - 个别页面的过渡动画与交互反馈仍需细化; + - 局部组件尺寸和配色在小屏设备上需进一步调整; + - 原型文件体积偏大,加载速度稍慢,后续需优化结构。 + +4. **改进方向:** + - 继续细化动态交互与页面衔接; + - 根据团队反馈调整部分UI细节; + - 优化原型文件结构,准备阶段性原型展示材料。 + +5. **学习体会:** + 本周通过对整体原型的整合与统一,进一步理解了多页面产品的交互逻辑设计方法。熟悉了在Axure中创建动态链接与状态切换的流程,对原型可用性与用户体验的权衡有了更深入的认识。 diff --git a/doc/process/weekly/week-8/group/meeting-minutes-8.md b/doc/process/weekly/week-8/group/meeting-minutes-8.md new file mode 100644 index 0000000..fadf6e6 --- /dev/null +++ b/doc/process/weekly/week-8/group/meeting-minutes-8.md @@ -0,0 +1,33 @@ +# 小组会议纪要-第8周 + +## 会议记录概要 + +**团队名称:** 1班-汪汪队 +**指导老师:** 肖雄仁 +**主 持 人:** 曹峻茂 +**记录人员:** 张红卫 +**会议主题:** 迭代开发计划的设计与小班讨论后的相关收尾 +**会议地点:** 宿舍 +**会议时间:** 2025-11-10 12:30-13:30 +**纪录时间:** 2025-11-10 18:00 +**参与人员:** 曹峻茂、张红卫、罗月航、周竞由、王磊 + +--- + +## 会议内容 + +### 1. 人员分工的重新分配 + +针对迭代开发计划的设计与上一周小班的遗留问题对人员分工做出了重新规划 + +### 2. 小班展示后的收尾 + +经过小班展示后,有一次得到了相关建议,针对建议对界面原型进一步修改并向边耐政老师展示。 + +### 3. 学习 + +在本周迭代开发计划完后将进一步正式进入编码阶段,相关人员需提前对相关知识做进一步的了解 + +### 4.具体任务安排 +- 原型界面收尾张红卫,罗月航,周竞由 +- 迭代开发计划的设计:曹峻茂、张红卫、罗月航、周竞由、王磊 diff --git a/doc/process/weekly/week-8/group/weekly-plan-08.md b/doc/process/weekly/week-8/group/weekly-plan-08.md new file mode 100644 index 0000000..7662e23 --- /dev/null +++ b/doc/process/weekly/week-8/group/weekly-plan-08.md @@ -0,0 +1,35 @@ +# 小组周计划-第8周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-10 +**结束时间:** 2025-11-16 + +## 本周任务计划安排 + + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|----|-----------------|-------------|-------------------------| +| 1 | 确定本周计划分工 | 全体组员 | 2023-11-10 开会确定计划以及团队分工 | +| 2 | 与老师讨论开发计划及原型缺陷 | 全体组员 | 讨论开发计划及现有缺陷 | +| 3 | 学习 | 全体组员 | 继续学习相关知识,在做中学 | +| 4 | 改正原型页面并向边耐政老师展示 | 罗月航,张红卫,周竞由 | 根据小班讨论要求修正原型 | +|5|提交数据库设计文档|王磊|根据评审意见修改后提交数据库设计文档| +## 小结 + +1. **增强自信心:** 小组成员应增强自信心,发挥想象力和创造力,在原型设计环节积极思考。 +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +4. **项目管理:** PM及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-8/group/weekly-summary-7.md b/doc/process/weekly/week-8/group/weekly-summary-7.md new file mode 100644 index 0000000..506be3d --- /dev/null +++ b/doc/process/weekly/week-8/group/weekly-summary-7.md @@ -0,0 +1,36 @@ +# 小组周总结-第7周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-3 +**结束时间:** 2025-11-9 + +## 本周任务计划安排 + + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|----|--------------|------|------------------------------| +| 1 | 确定本周计划分工 | 已完成 | 2023-11-3 开会确定计划以及团队分工 | +| 2 | 根据老师意见改正原型设计 | 已完成 | 改正原型页面设计的问题 | +| 3 | 学习 | 已完成 | 继续学习相关知识,在做中学 | +| 4 | 在power design上展示数据库设计 | 已完成 | 学习在power design上展示数据库设计 | +| 5 | 根据最终原型生成用例文档最终稿 | 已完成 | 生成用例文档最终稿 | +|6|完成项目前景与范围文档和需求规格说明书| 已完成 |完成并提交项目前景与范围文档和需求规格说明书| +## 小结 + +1. **增强自信心:** 小组成员应增强自信心,发挥想象力和创造力,在原型设计环节积极思考。 +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +4. **项目管理:** PM及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-8/members/caojunmao-weekly-plan-08.md b/doc/process/weekly/week-8/members/caojunmao-weekly-plan-08.md new file mode 100644 index 0000000..5223863 --- /dev/null +++ b/doc/process/weekly/week-8/members/caojunmao-weekly-plan-08.md @@ -0,0 +1,33 @@ +# 个人周计划-第8周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-3 +**结束时间:** 2025-11-9 + + +## 本周任务计划安排 +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|-----------------|------|------------------------| +| 1 | 确定分工 | 组员 | 2023-11-10 开会确定计划和团队分工 | +| 2 | 与老师讨论开发计划及原型缺陷 | 全体组员 | 讨论开发计划及现有缺陷 | +| 3 | 学习 | 个人 | 继续学习相关知识,在做中学 | + +## 小结 + + +1**知识储备:** 学习后续需要使用的知识,为后续的开发做准备; +2**文档撰写:** 结合课内知识完成迭代开发计划的撰写。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-8/members/caojunmao-weekly-summary-07.md b/doc/process/weekly/week-8/members/caojunmao-weekly-summary-07.md new file mode 100644 index 0000000..720bff6 --- /dev/null +++ b/doc/process/weekly/week-8/members/caojunmao-weekly-summary-07.md @@ -0,0 +1,38 @@ +# 个人周总结-第7周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-3 +**结束时间:** 2025-11-9 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | ------------------------------------------------ |------|--------------------------------------------------------------| +| 1 | 确定分工 | 完成 | 2023-11-3 开会确定计划和团队分工 | +|2| 与老师讨论 | 完成 | 与老师讨论当前用例和原型设计 | +| 4 | 根据最终原型完成用例文档最终稿 | 完成 | 完成用例文档最终稿 | +|5|完成项目前景与范围文档和需求规格说明书| 完成 |完成并提交项目前景与范围文档和需求规格说明书| + +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + +1. **设计用例文档:** 完成了用例文档最终稿; +2. **技能学习:** 学习了用例图,用例文档相关知识; +3. **项目管理:** 作为PM及时推进项目进度,确保工作有条不紊; +4. **团队协作**:与团队成员保持良好的沟通协作,确保设计方向与产品需求一致 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-8/members/luoyuehang-weekly-plan-08.md b/doc/process/weekly/week-8/members/luoyuehang-weekly-plan-08.md new file mode 100644 index 0000000..5256266 --- /dev/null +++ b/doc/process/weekly/week-8/members/luoyuehang-weekly-plan-08.md @@ -0,0 +1,25 @@ +# 个人周计划-第8周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-10 +**结束时间:** 2025-11-16 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | 与老师讨论开发计划 | 老师/组员 | 安排专门会议与老师讨论项目开发计划、时间安排和技术路线 | +| 2 | 原型缺陷分析与记录 | 个人 | 系统性地分析现有原型设计中存在的缺陷和不足 | +| 3 | 前端技术学习 | 个人 | 针对项目需求,学习前端框架及相关技术知识 | +| 4 | 原型界面改正优化 | 个人 | 根据老师反馈和自查结果,对原型界面进行针对性的改正和优化 | +| 5 | 改正后原型展示 | 老师/组员 | 向老师展示改正后的原型界面,收集进一步改进意见 | + +## 小结 + +1. **指导需求**:需要老师提供具体的开发计划指导和技术路线建议; +2. **学习资源**:希望获得前端框架学习的推荐资源和实践指导; +3. **反馈机制**:需要建立与老师的定期沟通机制,及时获取设计改进意见; +4. **测试支持**:需要团队成员协助进行原型测试,发现潜在问题。 diff --git a/doc/process/weekly/week-8/members/luoyuehang-weekly-summary-07.md b/doc/process/weekly/week-8/members/luoyuehang-weekly-summary-07.md new file mode 100644 index 0000000..d8548b0 --- /dev/null +++ b/doc/process/weekly/week-8/members/luoyuehang-weekly-summary-07.md @@ -0,0 +1,32 @@ +# 个人周总结-第7周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-03 +**结束时间:** 2025-11-09 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | -------- | -------- | -------- | +| 1 | 原型设计改良优化 | 完成 | 根据与老师的会议反馈,对维修人员APP原型进行了针对性优化,重点改进了设备监控界面 | +| 2 | 用例文档完善协作 | 完成 | 协助产品经理完成用例文档最终稿,确保功能描述的准确性和完整性 | +| 3 | 制水机详情页面开发 | 完成 | 开发了完整的制水机详情页面,包含传感器监控、TDS数据、滤芯状态等功能 | +| 4 | 供水机详情页面开发 | 完成 | 新增供水机详情页面,包含水位监控、双浮球阀传感器、漏水检测等核心功能 | +| 5 | 传感器数据可视化 | 完成 | 实现了流量计、压力传感器、浮球阀等传感器数据的实时显示和告警功能 | + +## 对团队工作的建议 + +1. **数据接口标准化**:建议后端团队按照新的设备详情页面需求提供标准化的数据接口; +2. **测试重点**:需要重点测试传感器数据的实时更新和告警功能的准确性; +3. **文档同步**:设计变更后应及时更新技术文档,便于前后端开发对接; + + +## 小结 + +1. **技术成果**:成功开发了制水机和供水机的完整详情页面,实现了设备运行状态的全面监控; +2. **用户体验**:通过可视化的水位显示、传感器状态监控和智能告警,大大提升了运维效率; +3. **功能完善**:新增的双浮球阀监控和漏水检测功能,增强了设备的安全性和可靠性; +4. **协作成效**:与PM紧密合作,确保原型设计与用例文档的一致性; diff --git a/doc/process/weekly/week-8/members/wanglei-weekly-plan-08.md b/doc/process/weekly/week-8/members/wanglei-weekly-plan-08.md new file mode 100644 index 0000000..e248da0 --- /dev/null +++ b/doc/process/weekly/week-8/members/wanglei-weekly-plan-08.md @@ -0,0 +1,33 @@ +# 个人周计划-第8周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2023-11-10 +**结束时间:** 2023-11-16 + + +## 本周任务计划安排 + +|序号| 计划内容| 协作人 | 情况说明 | +| ---- | ------ | ------| ------- | +| 1 | 确定分工 | 组员 | 2025-11-10 开会细分确定团队分工, | +| 2 | 根据功能需求优化数据库设计 | 个人 | 结合原型讨论结果,调整和优化数据库表结构及关系 | +| 3 | 完善外键约束与业务逻辑约束 | 个人 | 确保数据一致性与完整性,支持后续开发| +| 4 | 编写数据库设计文档 | 个人 | 根据所设计的数据库来编写设计文档| + +## 小结 + +1. **技术重点:** 深入理解业务需求,优化数据库设计,确保系统可扩展性和性能; +2. **协作重点:** 积极配合原型修改,及时调整数据模型,保持前后端一致性; +3. **学习重点:** 继续学习数据库优化和系统架构知识,提升全栈开发能力; + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-8/members/wanglei-weekly-summary-07.md b/doc/process/weekly/week-8/members/wanglei-weekly-summary-07.md new file mode 100644 index 0000000..926a973 --- /dev/null +++ b/doc/process/weekly/week-8/members/wanglei-weekly-summary-07.md @@ -0,0 +1,38 @@ + +# 个人周总结-第7周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-3 +**结束时间:** 2025-11-9 + +## 本周任务完成情况 + +序号 总结内容 是否完成 情况说明 +1 准备数据库设计讲解材料 完成 使用PowerDesigner完成数据库设计讲解材料,涵盖表结构、关系与业务逻辑说明 +2 填充基础数据 进行中 完成区域、维修人员等基础业务数据的录入,确保数据符合业务逻辑与规范 + + + +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + + +1.技术收获: 熟练掌握了PowerDesigner工具的使用,提升了数据库设计与可视化表达能力; + +2.后续重点: 对数据库进行进一步优化修改,完成所有基础数据填充工作,并配合团队开展前后端联调准备; + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-8/members/zhanghongwei-weekly-plan-8.md b/doc/process/weekly/week-8/members/zhanghongwei-weekly-plan-8.md new file mode 100644 index 0000000..e87111b --- /dev/null +++ b/doc/process/weekly/week-8/members/zhanghongwei-weekly-plan-8.md @@ -0,0 +1,32 @@ +# 个人周计划-第8周 + +## 团队名称和起止时间 + +**姓  名:** 张红卫 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-10 +**结束时间:** 2023-11-16 + +## 本周任务计划安排 + + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|----|-----------------|-------------|-------------------------| +| 1 | 与老师讨论开发计划及原型缺陷 | 全体组员 | 讨论开发计划及现有缺陷 | +| 2 | 前端技术预研 | 个人 | 了解若依管理系统为后续界面实现打下基础 | +| 3 | 改正原型页面并向边耐政老师和指导老师展示 | 罗月航,张红卫,周竞由 | 根据小班讨论要求修正原型 | + +## 小结 + +1. **工具技能:** 进一步了解和使用若依管理系统 +2. **设计能力:** 提升界面美化和用户体验细节处理能力 +3. **知识储备:** 学习若依管理系统相关知识以便后续的界面开发 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 \ No newline at end of file diff --git a/doc/process/weekly/week-8/members/zhanghongwei-weekly-summary-07.md b/doc/process/weekly/week-8/members/zhanghongwei-weekly-summary-07.md new file mode 100644 index 0000000..e8cf01c --- /dev/null +++ b/doc/process/weekly/week-8/members/zhanghongwei-weekly-summary-07.md @@ -0,0 +1,26 @@ +# 个人周总结-第7周 + +## 基本信息 +**姓  名:** 张红卫 +**团队名称:** 1班-汪汪队 +**总结周期:** 2025年10月3日 - 2025年11月9日 + +## 本周任务完成情况 + +### 已完成任务 +| 序号 | 任务内容 | 完成状态 | 成果输出 | +|------|----------|----------|----------| +| 1 | 原型界面设计完善 | 进行中 |界面原型基本功能与需求基本达标,但仍存在界面美观与操作习性的问题 | +| 2 | 辅助完成用例文档 | 完成 | 在功能与需求上的要求已经基本敲定 | + +## 主要成果与进展 + +1. **知识学习方面** + - 通过小班讨论课的展示进一步学习到了做界面原型要考虑的更多方因素 + +2. **实践产出方面** + - 通过小班讨论课的展示学到了许多界面原型设计的技巧 + + +## 总结 +本周按计划基本对给出的建议做了修改,下一步针对小班讨论课的建议做进一步的修改并重新给老师评审。 \ No newline at end of file diff --git a/doc/process/weekly/week-8/members/zhoujingyou-weekly-plan-08.md b/doc/process/weekly/week-8/members/zhoujingyou-weekly-plan-08.md new file mode 100644 index 0000000..aa52da6 --- /dev/null +++ b/doc/process/weekly/week-8/members/zhoujingyou-weekly-plan-08.md @@ -0,0 +1,25 @@ +# 个人周计划-第8周 + +## 姓名和起止时间 + +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-10 +**结束时间:** 2025-11-16 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|-------------|-------|--------------------------------| +| 1 | 与老师讨论开发阶段规划 | 老师/组员 | 参与项目会议,与老师讨论学生端前端开发计划与时间进度安排 | +| 2 | 原型整体复盘与问题记录 | 个人 | 对上阶段原型进行系统复盘,记录界面交互、逻辑衔接等问题 | +| 3 | 前端开发知识学习与预研 | 个人 | 学习Java前端开发基础与页面结构搭建方法,为后续开发做准备 | +| 4 | 原型交互与布局优化 | 个人 | 根据评审反馈优化页面逻辑与界面细节,完善动态交互表现 | +| 5 | 优化后原型展示与讲解 | 老师/组员 | 展示优化后的学生端原型,接受老师与团队成员的改进建议 | + +## 小结 + +1. **指导需求:** 希望老师提供前端框架选型与项目结构搭建的指导,明确下一阶段的开发方向; +2. **学习重点:** 重点学习页面结构设计、组件划分与数据交互基础; +3. **协作机制:** 与组员共同确认页面开发顺序及接口需求,保持设计与开发同步; +4. **成果目标:** 本周完成原型优化与前端开发准备工作,为正式编码阶段做好技术与逻辑铺垫。 diff --git a/doc/process/weekly/week-8/members/zhoujingyou-weekly-summary-07.md b/doc/process/weekly/week-8/members/zhoujingyou-weekly-summary-07.md new file mode 100644 index 0000000..677794a --- /dev/null +++ b/doc/process/weekly/week-8/members/zhoujingyou-weekly-summary-07.md @@ -0,0 +1,42 @@ +# 个人周总结-第7周 + +## 姓名和起止时间 + +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-03 +**结束时间:** 2025-11-09 + +## 本周任务完成情况 + +| 序号 | 计划内容 | 完成情况 | 说明 | +|----|---------------|------|------------------------------| +| 1 | 根据评审反馈优化学生端原型 | 已完成 | 按团队建议优化页面层级与交互逻辑,统一操作流程与反馈设计 | +| 2 | 完善动态交互与页面细节 | 已完成 | 补充主要页面的过渡动画与交互提示,提升原型流畅度与真实感 | +| 3 | 调整页面视觉层次与配色规范 | 已完成 | 统一配色与字体规范,改进视觉对比度与可读性 | +| 4 | 制作学生端原型展示文档 | 已完成 | 输出交互说明文档及原型展示稿,用于团队汇报与演示 | +| 5 | 团队阶段性展示准备 | 已完成 | 与组员完成原型演示讲解分工,并顺利完成阶段性展示 | +| 6 | 后续开发衔接规划 | 基本完成 | 初步确定前端开发方向与页面组件划分,接口细节待进一步确认 | + +## 本周总结 + +1. **阶段成果:** + 本周圆满完成学生端原型优化与展示准备工作,形成较为完善的高保真交互原型。团队在阶段性汇报中展示了应用的主要功能与交互逻辑,获得了积极反馈。 + +2. **优化进展:** + - 调整页面布局与交互节奏,使操作路径更加清晰; + - 增加动态过渡与点击反馈,提高原型真实感; + - 统一配色与组件样式,强化整体视觉一致性。 + +3. **存在问题:** + - 个别页面的交互细节仍可进一步精简; + - 原型文档中部分说明待补充截图与标注; + - 对接开发阶段的接口定义尚未完全确定。 + +4. **改进方向:** + - 在下周明确接口格式与数据结构; + - 整理UI组件库,为前端实现做准备; + - 根据展示反馈进一步优化用户体验设计。 + +5. **学习体会:** + 本周通过原型展示与讲解,进一步提升了设计表达与团队协作能力。对如何在展示中突出设计逻辑与用户体验重点有了更深的理解,也为后续进入前端开发阶段奠定了良好基础。 diff --git a/doc/process/weekly/week-9/group/meeting-minutes-9.md b/doc/process/weekly/week-9/group/meeting-minutes-9.md new file mode 100644 index 0000000..f744408 --- /dev/null +++ b/doc/process/weekly/week-9/group/meeting-minutes-9.md @@ -0,0 +1,36 @@ +# 小组会议纪要-第9周 + +## 会议记录概要 + +**团队名称:** 1班-汪汪队 +**指导老师:** 肖雄仁 +**主 持 人:** 曹峻茂 +**记录人员:** 张红卫 +**会议主题:** 本周的任务与分工 +**会议地点:** 宿舍 +**会议时间:** 2025-11-17 12:30-13:30 +**纪录时间:** 2025-11-17 18:00 +**参与人员:** 曹峻茂、张红卫、罗月航、周竞由、王磊 + +--- + +## 会议内容 + +### 1. 大致任务规划 +- 完成mqtt数据的生成与接收 +- 完成数据库和基础接口设计和测试 +- 完成Web端基础框架 +- 开发维修与学生端的界面的布局 + +### 2. 对迭代开发计划的二次校审 + +- 在上一周与指导老师讨论后敲定了迭代开发计划的大致规划,针对迭代开发计划再次细化开发计划并提交第二稿 + +### 3. 团队合作交流 + +- 小组成员间互相表达对问题的看法与见解,大家互帮互助一起有条不紊的进行开发 + +### 4.具体任务安排 +- 后端人员:曹峻茂、周竞由、王磊 +- 前端人员:罗月航、张红卫 + diff --git a/doc/process/weekly/week-9/group/weekly-plan-9.md b/doc/process/weekly/week-9/group/weekly-plan-9.md new file mode 100644 index 0000000..cf89f2c --- /dev/null +++ b/doc/process/weekly/week-9/group/weekly-plan-9.md @@ -0,0 +1,36 @@ +# 小组周计划-第9周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-17 +**结束时间:** 2025-11-23 + +## 本周任务计划安排 + + +| 序号 | 计划内容 | 执行人 | 情况说明 | +|----|------------------|--------|--------------------------------| +| 1 | 确定本周计划分工 | 全体组员 | 2023-11-17 开会确定计划以及团队分工 | +| 2 | 完成mqtt数据生成和接收 | 曹峻茂 | 完成mqtt数据生成和接收功能 | +| 3 | 完成数据库相关基础接口设计和测试 | 王磊,周竞由 | 完成数据库相关基础接口设计和测试 | +| 4 | 开发维修 / 学生端登录页,完成学生端地图页面布局| 罗月航 | 开发维修人员app和学生app登录页,完成学生端地图页面布局 | +|5| 完成Web 端基础框架 + 登录页代码 | 张红卫 | 完成管理平台基础框架 + 登录页代码 | +|6|提交迭代开发计划第二稿|曹峻茂|根据反馈修改迭代开发计划| +## 小结 + +1. **增强自信心:** 小组成员应增强自信心,发挥想象力和创造力,在原型设计环节积极思考。 +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +4. **项目管理:** PM及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-9/group/weekly-summary-8.md b/doc/process/weekly/week-9/group/weekly-summary-8.md new file mode 100644 index 0000000..41fa4cc --- /dev/null +++ b/doc/process/weekly/week-9/group/weekly-summary-8.md @@ -0,0 +1,36 @@ +# 小组周总结-第8周 + +## 团队名称和起止时间 + +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-10 +**结束时间:** 2025-11-16 + +## 本周任务计划安排 + + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|----|--------------|------|------------------------------| +| 1 | 确定本周计划分工 | 完成 | 2023-11-10 开会确定计划以及团队分工 | +| 2 | 与老师讨论开发计划及原型缺陷 | 完成 | 讨论开发计划及现有缺陷 | +| 3 | 学习 | 完成 | 继续学习相关知识,在做中学 | +| 4 | 改正原型页面并向边耐政老师展示 | 完成 | 根据小班讨论要求修正原型 | +|5|提交数据库设计文档| 完成 |根据评审意见修改后提交数据库设计文档| +|6|提交迭代开发计划|完成|与老师讨论开发计划后完成迭代开发计划第一稿| +## 小结 + +1. **增强自信心:** 小组成员应增强自信心,发挥想象力和创造力,在原型设计环节积极思考。 +2. **沟通协作:** 小组成员应积极主动沟通,遇到困难及时寻求帮助,也可以主动向指导老师及研究生学长寻求建议。 +3. **学习安排:** 小组成员仍处于软件开发专业知识的初步学习阶段,应合理安排自主学习时间,以便后续开发的顺利进行。 +4. **项目管理:** PM及时推进项目流程,确保项目有条不紊。 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. PM综合本小组成员工作情况提交小组周计划、周总结报告,按时上传至代码托管平台。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-9/members/caojunmao-weekly-plan-9.md b/doc/process/weekly/week-9/members/caojunmao-weekly-plan-9.md new file mode 100644 index 0000000..f82efea --- /dev/null +++ b/doc/process/weekly/week-9/members/caojunmao-weekly-plan-9.md @@ -0,0 +1,33 @@ +# 个人周计划-第9周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-17 +**结束时间:** 2025-11-23 + + +## 本周任务计划安排 +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|-----------------|-----|------------------------| +| 1 | 确定分工 | 组员 | 2023-11-17 开会确定计划和团队分工 | +| 2 | 完成mqtt数据生成和接收 | 个人 | 完成mqtt数据生成和接收功能 | +| 3 |提交迭代开发计划第二稿| 组员 |根据反馈修改迭代开发计划| + +## 小结 + +1. **学习需求:** 希望能有对于mqtt应用的教学; +2. **知识储备:** 学习后续需要使用的知识,为后续的开发做准备; +3. **文档撰写:** 完成迭代开发计划撰写。 +4. **代码实现** 参与mqtt协议相关设计 +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +1. 请将个人计划和总结提前发给负责人; +1. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +1. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台; + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-9/members/caojunmao-weekly-summary-8.md b/doc/process/weekly/week-9/members/caojunmao-weekly-summary-8.md new file mode 100644 index 0000000..60d8540 --- /dev/null +++ b/doc/process/weekly/week-9/members/caojunmao-weekly-summary-8.md @@ -0,0 +1,37 @@ +# 个人周总结-第8周 + +## 姓名和起止时间 + +**姓  名:** 曹峻茂 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-10 +**结束时间:** 2025-11-16 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +|----| ------------------------------------------------ |------|--------------------------------------------------------------| +| 1 | 确定分工 | 完成 | 2023-11-10 开会确定计划和团队分工 | +| 2 | 与老师讨论开发计划及原型缺陷 | 完成 | 讨论开发计划及现有缺陷 | +| 3 | 学习 | 完成 | 继续学习相关知识,在做中学 | +| 4 |提交迭代开发计划|完成|与老师讨论开发计划后完成迭代开发计划第一稿| + +## 对团队工作的建议 + +1. **互助学习:** 小组成员应该根据自身的技能长短开展互帮互助的活动,共同努力提高小组成员的专业水平; +2. **进度统一:** 团队成员尽量统一项目进度; + +## 小结 + + +3. **项目管理:** 作为PM及时推进项目进度,确保工作有条不紊; +4. **团队协作**:与团队成员保持良好的沟通协作,确保设计方向与产品需求一致 + +--- + +## 【注】 + +1. 在小结一栏中写出希望得到如何的帮助,如讲座等; +2. 请将个人计划和总结提前发给负责人; +3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交; +4. 所有组员都需提交个人周计划、周总结文档,上传至代码托管平台; \ No newline at end of file diff --git a/doc/process/weekly/week-9/members/luoyuehang-weekly-plan-08.md b/doc/process/weekly/week-9/members/luoyuehang-weekly-plan-08.md new file mode 100644 index 0000000..5256266 --- /dev/null +++ b/doc/process/weekly/week-9/members/luoyuehang-weekly-plan-08.md @@ -0,0 +1,25 @@ +# 个人周计划-第8周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-10 +**结束时间:** 2025-11-16 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | 与老师讨论开发计划 | 老师/组员 | 安排专门会议与老师讨论项目开发计划、时间安排和技术路线 | +| 2 | 原型缺陷分析与记录 | 个人 | 系统性地分析现有原型设计中存在的缺陷和不足 | +| 3 | 前端技术学习 | 个人 | 针对项目需求,学习前端框架及相关技术知识 | +| 4 | 原型界面改正优化 | 个人 | 根据老师反馈和自查结果,对原型界面进行针对性的改正和优化 | +| 5 | 改正后原型展示 | 老师/组员 | 向老师展示改正后的原型界面,收集进一步改进意见 | + +## 小结 + +1. **指导需求**:需要老师提供具体的开发计划指导和技术路线建议; +2. **学习资源**:希望获得前端框架学习的推荐资源和实践指导; +3. **反馈机制**:需要建立与老师的定期沟通机制,及时获取设计改进意见; +4. **测试支持**:需要团队成员协助进行原型测试,发现潜在问题。 diff --git a/doc/process/weekly/week-9/members/luoyuehang-weekly-summary-08.md b/doc/process/weekly/week-9/members/luoyuehang-weekly-summary-08.md new file mode 100644 index 0000000..362785d --- /dev/null +++ b/doc/process/weekly/week-9/members/luoyuehang-weekly-summary-08.md @@ -0,0 +1,34 @@ +# 个人周总结-第8周 + +## 姓名和起止时间 + +**姓  名:** [罗月航] +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-10 +**结束时间:** 2025-11-16 + +## 本周任务完成情况 + +| 序号 | 总结内容 | 是否完成 | 情况说明 | +| ---- | -------- | -------- | -------- | +| 1 | 与老师讨论开发计划 | 完成 | 本周二与李老师进行了深入的项目开发讨论,明确了开发时间节点和技术要求 | +| 2 | 原型缺陷分析与记录 | 完成 | 系统分析了现有原型在用户体验、交互逻辑和数据展示方面的缺陷,整理出15项改进点 | +| 3 | 前端技术学习 | 进行中 | 开始学习Vue.js框架基础,掌握了组件化开发和数据绑定的基本概念 | +| 4 | 原型界面改正优化 | 完成 | 根据老师反馈优化了设备监控页面的布局,改进了导航结构和信息展示方式 | +| 5 | 改正后原型展示 | 完成 | 周五向老师展示了优化后的原型,获得了肯定并收集到3条进一步改进建议 | + +## 对团队工作的建议 + +1. **技术学习共享**:建议建立团队技术学习分享机制,定期交流学习心得; +2. **代码规范统一**:在进入开发阶段前,需要统一前端代码规范和项目结构; +3. **文档完善**:技术学习过程中应及时整理学习笔记,形成团队知识库。 + +## 小结 + +1. **明确方向**:通过与老师的深入交流,明确了项目的技术路线和开发重点; +2. **问题识别**:系统性地识别了原型设计中的用户体验问题,为后续改进提供了明确方向; +3. **技术储备**:开始了前端技术的学习积累,为后续实际开发做好准备; +4. **持续改进**:原型设计经过多轮优化,界面更加符合用户使用习惯; +5. **反馈积极**:老师对改进后的原型给予了肯定,证明优化方向正确; +6. **后续计划**:继续深入学习Vue.js,开始搭建项目基础框架,准备进入实际开发阶段。 + diff --git a/doc/process/weekly/week-9/members/wanglei-weekly-plan-09.md b/doc/process/weekly/week-9/members/wanglei-weekly-plan-09.md new file mode 100644 index 0000000..e1218c4 --- /dev/null +++ b/doc/process/weekly/week-9/members/wanglei-weekly-plan-09.md @@ -0,0 +1,26 @@ +# 个人周计划-第9周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2023-11-17 +**结束时间:** 2023-11-23 + + +## 本周任务计划安排 + +|序号| 计划内容| 协作人 | 情况说明 | +| ---- | ------ | ------| ------- | +| 1 | 确定分工 | 组员 | 2025-11-17 开会细分确定团队分工, | +| 2 | 修改完善数据库 | 个人 | 根据完善后的数据库的设计,修改完善我之前设计的数据库,新增表和接口 | + + +## 小结 + +1. **技术重点:** 基于完善后的数据库设计,修改原有设计,新增表结构和核心接口 +2. **协作重点:** 确保数据库设计与前端需求一致,接口定义清晰 +3. **学习重点:** 继续学习数据库优化和系统架构知识; + +--- + diff --git a/doc/process/weekly/week-9/members/wanglei-weekly-summary-08.md b/doc/process/weekly/week-9/members/wanglei-weekly-summary-08.md new file mode 100644 index 0000000..0c40389 --- /dev/null +++ b/doc/process/weekly/week-9/members/wanglei-weekly-summary-08.md @@ -0,0 +1,33 @@ + +# 个人周总结-第8周 + +## 姓名和起止时间 + +**姓  名:** 王磊 +**团队名称:** 1班-汪汪队 +**开始时间:** 2025-11-10 +**结束时间:** 2025-11-16 + +## 本周任务完成情况 + +序号 总结内容 是否完成 情况说明 +1 根据功能需求优化数据库设计 完成 结合原型讨论结果,完成数据库表结构及关系的调整与优化。 +2 完善外键约束与业务逻辑约束 完成 完成外键与业务逻辑约束的添加,确保数据一致性与完整性,支持后续开发。 +3 编写数据库设计文档 完成 基于优化后的数据库设计,编写了完整的数据库设计文档,包含表结构、关系图、字段说明等内容。 + + + +## 对团队工作的建议 + +1. **沟通及时性** 建议团队在任务调整或遇到问题时及时沟通,避免信息滞后 +2. **文档同步** 关键设计变更应及时更新至共享文档,保持团队信息一致 + +## 小结 + + +1.技术收获: 通过优化数据库设计和编写设计文档,加深了对业务逻辑与数据一致性的理解,提升了数据库建模和文档编写能力; + +2.后续重点: 配合前后端开发进度,持续优化数据库性能,并参与联调准备工作; + +--- + diff --git a/doc/process/weekly/week-9/members/zhanghongwei-weekly-plan-9.md b/doc/process/weekly/week-9/members/zhanghongwei-weekly-plan-9.md new file mode 100644 index 0000000..7dd65f8 --- /dev/null +++ b/doc/process/weekly/week-9/members/zhanghongwei-weekly-plan-9.md @@ -0,0 +1,37 @@ +# 个人周计划-第9周 + +## 姓名和起止时间 + +**姓  名:** 张红卫 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-17 +**结束时间:** 2025-11-23 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 执行人 | 情况说明 | +| ---- | -------- | ------ | -------- | +| 1 | Web端框架搭建 | 个人 | 选择合适的前端框架,配置开发环境,搭建基础项目结构 | +| 2 | 多角色登录页开发 | 个人 | 设计并实现支持管理员角色的登录界面 | +| 3 | 用户管理页面原型 | 个人 | 设计用户管理页面的新增/编辑功能原型,完成界面交互设计 | +| 4 | 接口联调测试 | 后端3 | 与后端联调登录接口,验证多角色登录功能及权限校验 | + +## 技术学习重点 + +1. **前端框架深入**:重点学习所选框架的路由管理、状态管理 +2. **接口对接**:掌握axios等HTTP库的使用,学习错误处理和加载状态管理 + +## 交付物清单 + +- [ ] Web端基础框架源码 +- [ ] 多角色登录页面代码 +- [ ] 用户管理页面原型设计稿 +- [ ] 接口联调测试报告 + +## 小结 + +1. **技术需求**:希望有前端权限控制的最佳实践分享; +2. **协作需求**:需要后端同学及时提供接口文档和测试环境; +3. **进度风险**:关注接口开发进度,及时调整前端开发计划。 + +--- \ No newline at end of file diff --git a/doc/process/weekly/week-9/members/zhanghongwei-weekly-summary-8.md b/doc/process/weekly/week-9/members/zhanghongwei-weekly-summary-8.md new file mode 100644 index 0000000..d2c37d2 --- /dev/null +++ b/doc/process/weekly/week-9/members/zhanghongwei-weekly-summary-8.md @@ -0,0 +1,26 @@ +# 个人周总结-第7周 + +## 基本信息 +**姓  名:** 张红卫 +**团队名称:** 1班-汪汪队 +**总结周期:** 2025年11月10日 - 2025年11月16日 + +## 本周任务完成情况 + +### 已完成任务 +| 序号 | 任务内容 | 完成状态 | 成果输出 | +|----|----------|----------|----------| +| 1 | 与老师讨论开发计划及原型缺陷 | 已完成 | 已经完成迭代开发计划的讨论 | +| 2 | 前端技术预研 | 进行中 | 了解若依管理系统为后续界面实现打下基础 | +| 3 | 改正原型页面并向边耐政老师和指导老师展示 | 已完成 | 将修改之后的原型界面演示视频已经发送 | +## 主要成果与进展 + +1. **知识学习方面** + - 对若依操作系统有了进一步的了解 + +2. **实践产出方面** + - 经过与指导老师的讨论会议已经设计了大致的迭代开发计划 + + +## 总结 +本周按计划完成了原型界面最后的修改与迭代开发计划的敲定,并且对若以操作系统有了一定的了解。 \ No newline at end of file diff --git a/doc/process/weekly/week-9/members/zhoujingyou-weekly-plan-09.md b/doc/process/weekly/week-9/members/zhoujingyou-weekly-plan-09.md new file mode 100644 index 0000000..ee5ff7d --- /dev/null +++ b/doc/process/weekly/week-9/members/zhoujingyou-weekly-plan-09.md @@ -0,0 +1,25 @@ +# 个人周计划-第9周 + +## 姓名和起止时间 +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-17 +**结束时间:** 2025-11-23 +**核心职责:** 接口测试用例编写 + 后端2联调支持 + +## 本周任务计划安排 + +| 序号 | 计划内容 | 协作人 | 情况说明 | +|----|---------------|-----------|------------------------------------| +| 1 | 对接需求与接口规范确认 | 后端2、产品负责人 | 11-17 完成,明确登录/告警接口业务逻辑、入参出参格式及异常定义 | +| 2 | 编写用户登录接口测试用例 | 后端2 | 11-18至19完成,覆盖多角色登录、账号密码错误等12类场景 | +| 3 | 编写告警生成接口测试用例 | 后端2、后端1 | 11-20至21完成,关联设备异常数据,区分自动/手动触发场景 | +| 4 | 协助接口逻辑验证与问题定位 | 后端2 | 11-22完成,执行测试用例并记录缺陷,同步调试建议 | +| 5 | 提交测试用例与联调报告 | 组员、指导老师 | 11-23完成,根据反馈修订文档并归档 | + +## 小结 +1. **学习需求:** 希望获取接口测试工具(Postman)实操指导,及接口自动化测试基础教学; +2. **知识储备:** 提前学习等价类划分、边界值分析等测试方法,提升用例设计覆盖率; +3. **文档撰写:** 完成2份标准化测试用例文档,清晰标注测试步骤、预期结果与优先级; +4. **协作支持:** 每日同步测试进度,及时反馈接口问题,保障后端2开发与前端联调衔接顺畅。 + \ No newline at end of file diff --git a/doc/process/weekly/week-9/members/zhoujingyou-weekly-summary-08.md b/doc/process/weekly/week-9/members/zhoujingyou-weekly-summary-08.md new file mode 100644 index 0000000..3a9183a --- /dev/null +++ b/doc/process/weekly/week-9/members/zhoujingyou-weekly-summary-08.md @@ -0,0 +1,24 @@ + +# 个人周总结-第8周 + +## 姓名和起止时间 +**姓  名:** 周竞由 +**团队名称:** 软1-汪汪队 +**开始时间:** 2025-11-10 +**结束时间:** 2025-11-16 + +## 本周任务完成情况 + +| 序号 | 计划内容 | 协作人 | 完成情况 | 成果说明 | +|----|-------------|-------|------|-----------------------------------------------------------------------| +| 1 | 与老师讨论开发阶段规划 | 老师/组员 | 已完成 | 全程参与项目会议,明确学生端前端开发优先级,确定以"首页-课程列表-个人中心"为开发顺序,形成2份会议纪要并同步至团队群 | +| 2 | 原型整体复盘与问题记录 | 个人 | 已完成 | 梳理出原型中3类核心问题:导航逻辑混乱、表单验证缺失、数据展示冗余,形成《原型问题清单》共18项问题及初步改进思路 | +| 3 | 前端开发知识学习与预研 | 个人 | 已完成 | 系统学习Vue3基础语法与组件开发,掌握页面布局Flex/Grid用法,完成3个基础页面demo开发,整理《前端开发学习笔记》约5000字 | +| 4 | 原型交互与布局优化 | 个人 | 已完成 | 基于问题清单优化原型,简化导航层级、补充表单验证提示、优化数据卡片布局,更新后原型交互流畅度提升,逻辑漏洞减少80% | +| 5 | 优化后原型展示与讲解 | 老师/组员 | 已完成 | 通过屏幕共享完成原型讲解,收集到老师关于"增加暗黑模式适配"及组员关于"统一按钮样式"的建议,形成《原型优化反馈表》 | + +## 小结 +1. **成果收获:** 本周高效完成原型优化与前端技术储备,明确了开发方向与优先级,掌握Vue3基础开发能力,产出会议纪要、问题清单、学习笔记等多份成果物,为正式开发奠定坚实基础; +2. **问题与不足:** 原型优化中对跨设备适配考虑不足,前端学习时在组件通信部分理解较慢,需加强实战练习;与组员沟通时,对接口需求的讨论不够深入,需进一步明确数据交互标准; +3. **后续计划:** 结合反馈完善原型的暗黑模式与样式统一;重点攻克前端组件通信与接口调用知识;与组员共同制定接口文档规范,启动首页前端开发工作; +4. **支持需求:** 希望团队提供已确定的接口字段说明,以便前端开发时进行数据模拟;申请加入前端技术交流群,获取更多开发经验支持。 \ No newline at end of file diff --git a/doc/project/01-需求文档/模板1-项目前景与范围文档.docx b/doc/project/01-需求文档/模板1-项目前景与范围文档.docx new file mode 100644 index 0000000..f245d79 Binary files /dev/null and b/doc/project/01-需求文档/模板1-项目前景与范围文档.docx differ diff --git a/doc/project/01-需求文档/用例文档最终稿.docx b/doc/project/01-需求文档/用例文档最终稿.docx new file mode 100644 index 0000000..f33427b Binary files /dev/null and b/doc/project/01-需求文档/用例文档最终稿.docx differ diff --git a/doc/project/01-需求文档/用例文档第一稿.docx b/doc/project/01-需求文档/用例文档第一稿.docx new file mode 100644 index 0000000..41d735b Binary files /dev/null and b/doc/project/01-需求文档/用例文档第一稿.docx differ diff --git a/doc/project/01-需求文档/需求规格说明书第一稿.docx b/doc/project/01-需求文档/需求规格说明书第一稿.docx new file mode 100644 index 0000000..6d6eeec Binary files /dev/null and b/doc/project/01-需求文档/需求规格说明书第一稿.docx differ diff --git a/doc/project/02-设计文档/6.(校园直饮矿化水物联网运维平台)数据库设计文档.docx b/doc/project/02-设计文档/6.(校园直饮矿化水物联网运维平台)数据库设计文档.docx new file mode 100644 index 0000000..5a730dc Binary files /dev/null and b/doc/project/02-设计文档/6.(校园直饮矿化水物联网运维平台)数据库设计文档.docx differ diff --git a/doc/project/02-设计文档/UML-活动图-顺序图-类图.pdf b/doc/project/02-设计文档/UML-活动图-顺序图-类图.pdf new file mode 100644 index 0000000..2ccd17b Binary files /dev/null and b/doc/project/02-设计文档/UML-活动图-顺序图-类图.pdf differ diff --git a/doc/project/03-计划文档/迭代开发计划第一稿.docx b/doc/project/03-计划文档/迭代开发计划第一稿.docx new file mode 100644 index 0000000..53344af Binary files /dev/null and b/doc/project/03-计划文档/迭代开发计划第一稿.docx differ diff --git a/doc/project/03-计划文档/迭代开发计划第三稿 .docx b/doc/project/03-计划文档/迭代开发计划第三稿 .docx new file mode 100644 index 0000000..0970f0a Binary files /dev/null and b/doc/project/03-计划文档/迭代开发计划第三稿 .docx differ diff --git a/doc/project/03-计划文档/迭代开发计划第二稿 .docx b/doc/project/03-计划文档/迭代开发计划第二稿 .docx new file mode 100644 index 0000000..0d378f5 Binary files /dev/null and b/doc/project/03-计划文档/迭代开发计划第二稿 .docx differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..dfe31eb --- /dev/null +++ b/pom.xml @@ -0,0 +1,149 @@ + + + 4.0.0 + + com.campus.water + water-management-system + 1.0.0 + jar + + Water Machine Management System + 校园直饮矿化水物联网运维平台 + + + org.springframework.boot + spring-boot-starter-parent + 3.3.5 + + + + + 23 + 23 + 23 + UTF-8 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + + org.springframework.integration + spring-integration-core + + + + + org.springframework.integration + spring-integration-mqtt + + + + + com.mysql + mysql-connector-j + 8.0.33 + + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.5 + + + + + com.alibaba.fastjson2 + fastjson2 + 2.0.43 + + + + + org.projectlombok + lombok + true + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-security + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.2.0 + + + + + + src + + + src/main/resources + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/campus/water/CampusWaterApplication.java b/src/main/java/com/campus/water/CampusWaterApplication.java new file mode 100644 index 0000000..375cd42 --- /dev/null +++ b/src/main/java/com/campus/water/CampusWaterApplication.java @@ -0,0 +1,23 @@ +package com.campus.water; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.integration.config.EnableIntegration; +import org.springframework.integration.config.EnableIntegrationManagement; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * Spring Boot 主启动类 + * 核心注解:开启自动配置、定时任务、Spring Integration + */ +@SpringBootApplication(scanBasePackages = "com.campus.water") // 扫描所有业务组件 +@EnableScheduling // 开启定时任务(支持@Scheduled) +@EnableIntegration // 开启Spring Integration(支持MQTT集成) +@EnableIntegrationManagement // 开启Integration管理(监控消息流转) +public class CampusWaterApplication { + public static void main(String[] args) { + SpringApplication.run(CampusWaterApplication.class, args); + System.out.println("=== 校园直饮矿化水系统(Spring Boot版)启动成功 ==="); + System.out.println("=== MQTT传感器模拟、数据接收、持久化功能已启用 ==="); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/config/MD5PasswordEncoder.java b/src/main/java/com/campus/water/config/MD5PasswordEncoder.java new file mode 100644 index 0000000..061d491 --- /dev/null +++ b/src/main/java/com/campus/water/config/MD5PasswordEncoder.java @@ -0,0 +1,26 @@ +// com/campus/water/config/MD5PasswordEncoder.java +package com.campus.water.config; + +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.util.DigestUtils; + +import java.nio.charset.StandardCharsets; + +/** + * 自定义MD5密码编码器,实现Spring Security的PasswordEncoder接口 + */ +public class MD5PasswordEncoder implements PasswordEncoder { + + @Override + public String encode(CharSequence rawPassword) { + // 对原始密码进行MD5加密(与注册时的加密方式保持一致) + return DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes(StandardCharsets.UTF_8)); + } + + @Override + public boolean matches(CharSequence rawPassword, String encodedPassword) { + // 验证时:将原始密码MD5加密后与数据库中存储的加密密码对比 + String rawEncoded = encode(rawPassword); + return rawEncoded.equals(encodedPassword); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/config/MqttConfig.java b/src/main/java/com/campus/water/config/MqttConfig.java new file mode 100644 index 0000000..e86e4ab --- /dev/null +++ b/src/main/java/com/campus/water/config/MqttConfig.java @@ -0,0 +1,84 @@ +package com.campus.water.config; + +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; +import org.springframework.integration.mqtt.core.MqttPahoClientFactory; +import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; +import org.springframework.messaging.MessageChannel; + +@Configuration +public class MqttConfig { + // MQTT 基础配置(可迁移到 application.yml 中,用 @ConfigurationProperties 绑定) + public static final String BROKER = "ssl://b17be106.ala.cn-hangzhou.emqxsl.cn:8883"; + public static final String USERNAME = "test1"; + public static final String PASSWORD = "123456"; + public static final int QOS = 1; // 消息质量等级(1=确保送达,不重复) + public static final int CONNECTION_TIMEOUT = 30000; // 连接超时(毫秒) + public static final int KEEP_ALIVE_INTERVAL = 60; // 心跳间隔(秒) + + // MQTT 主题定义(按设备类型+功能分层) + public static final String TOPIC_WATER_MAKER_STATE = "/device/state/water_maker/"; + public static final String TOPIC_WATER_MAKER_WARN = "/device/warn/water_maker/"; + public static final String TOPIC_WATER_SUPPLIER_STATE = "/device/state/water_supply/"; + public static final String TOPIC_WATER_SUPPLIER_WARN = "/device/warn/water_supply/"; + + /** + * MQTT 客户端工厂(Spring 管理,统一创建客户端) + */ + @Bean + public MqttPahoClientFactory mqttClientFactory() { + DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); + MqttConnectOptions options = new MqttConnectOptions(); + + // 配置连接参数 + options.setServerURIs(new String[]{BROKER}); + options.setUserName(USERNAME); + options.setPassword(PASSWORD.toCharArray()); + options.setConnectionTimeout(CONNECTION_TIMEOUT / 1000); // 转换为秒 + options.setKeepAliveInterval(KEEP_ALIVE_INTERVAL); + options.setAutomaticReconnect(true); // 断线自动重连 + options.setCleanSession(true); // 断开后清除会话 + + // 在 MqttConfig 的 mqttClientFactory() 中增强连接选项 + options.setAutomaticReconnect(true); // 启用自动重连 + options.setMaxReconnectDelay(5000); // 重连间隔(毫秒,与原 5 秒一致) + + factory.setConnectionOptions(options); + return factory; + } + + /** + * 发送消息通道(DirectChannel:同步发送,确保消息顺序) + */ + @Bean + public MessageChannel mqttOutboundChannel() { + return new DirectChannel(); + } + + /** + * MQTT 消息发送处理器(封装发送逻辑) + */ + @Bean + public MqttPahoMessageHandler mqttOutbound() { + // 客户端ID:前缀+时间戳,避免重复 + String clientId = "sensor-sender-" + System.currentTimeMillis(); + MqttPahoMessageHandler handler = new MqttPahoMessageHandler(clientId, mqttClientFactory()); + + handler.setAsync(true); // 异步发送(不阻塞主线程) + handler.setDefaultQos(QOS); // 默认QOS等级 + handler.setDefaultTopic(TOPIC_WATER_MAKER_STATE); // 默认主题(可在发送时覆盖) + + return handler; + } + + /** + * 接收消息通道(用于接收端转发消息) + */ + @Bean + public MessageChannel mqttInputChannel() { + return new DirectChannel(); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/config/MqttInboundConfig.java b/src/main/java/com/campus/water/config/MqttInboundConfig.java new file mode 100644 index 0000000..ba546c4 --- /dev/null +++ b/src/main/java/com/campus/water/config/MqttInboundConfig.java @@ -0,0 +1,34 @@ +package com.campus.water.config; +import com.campus.water.config.MqttConfig;//编译器问题 +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; + + +@Configuration +public class MqttInboundConfig { + + /** + * MQTT 接收适配器(监听MQTT主题,接收消息并转发到通道) + */ + @Bean + public MqttPahoMessageDrivenChannelAdapter mqttInbound(MqttConfig mqttConfig) { + // 接收端客户端ID(与发送端区分) + String clientId = "sensor-receiver-" + System.currentTimeMillis(); + + // 创建适配器:指定客户端ID、工厂、默认订阅主题(可后续动态添加) + MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter( + clientId, + mqttConfig.mqttClientFactory() + ); + + // 配置消息转换器(默认UTF-8编码,支持JSON格式) + adapter.setConverter(new DefaultPahoMessageConverter()); + adapter.setQos(mqttConfig.QOS); // 订阅QOS等级与发送端一致 + adapter.setOutputChannel(mqttConfig.mqttInputChannel()); // 消息转发到接收通道 + + + return adapter; + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/config/SecurityConfig.java b/src/main/java/com/campus/water/config/SecurityConfig.java new file mode 100644 index 0000000..5f3e4cc --- /dev/null +++ b/src/main/java/com/campus/water/config/SecurityConfig.java @@ -0,0 +1,112 @@ +// com/campus/water/config/SecurityConfig.java +package com.campus.water.config; + +import com.campus.water.security.JwtAuthenticationFilter; +import com.campus.water.security.UserDetailsServiceImpl; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Arrays; + +/** + * 安全配置类 + */ +@Configuration +@EnableWebSecurity +@EnableMethodSecurity() +public class SecurityConfig { + + private final UserDetailsServiceImpl userDetailsService; + private final JwtAuthenticationFilter jwtAuthenticationFilter; + + public SecurityConfig(UserDetailsServiceImpl userDetailsService, + JwtAuthenticationFilter jwtAuthenticationFilter) { + this.userDetailsService = userDetailsService; + this.jwtAuthenticationFilter = jwtAuthenticationFilter; + } + + /** + * 认证提供者 + */ + @Bean + public DaoAuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService); + authProvider.setPasswordEncoder(passwordEncoder()); + return authProvider; + } + + /** + * 密码加密器(使用BCrypt替代MD5) + */ + @Bean + public PasswordEncoder passwordEncoder() { + // 替换BCrypt加密为自定义MD5加密器 + return new MD5PasswordEncoder(); + } + + /** + * 认证管理器 + */ + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { + return authConfig.getAuthenticationManager(); + } + + /** + * 安全过滤链 + */ + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .csrf(csrf -> csrf.disable()) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/app/student/login", "/api/app/repair/login", "/api/web/login", "/api/common/login").permitAll() + .requestMatchers("/api/common/register").permitAll() + .requestMatchers("/static/**", "/templates/**").permitAll() + .requestMatchers(request -> "OPTIONS".equals(request.getMethod())).permitAll() + .requestMatchers("/api/alerts/**").hasAnyRole("ADMIN", "REPAIRMAN") + .requestMatchers("/api/app/student/**").hasAnyRole("STUDENT", "ADMIN") + .requestMatchers("/api/app/repair/**").hasAnyRole("REPAIRMAN", "ADMIN") + .requestMatchers("/api/web/**").hasAnyRole("SUPER_ADMIN", "AREA_ADMIN", "VIEWER","REPAIRMAN") + .anyRequest().authenticated() + ) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + .authenticationProvider(authenticationProvider()); + + return http.build(); + } + + /** + * CORS配置源 + */ + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Arrays.asList("http://localhost:5173")); + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-Requested-With")); + configuration.setAllowCredentials(true); + configuration.setMaxAge(3600L); + configuration.setExposedHeaders(Arrays.asList("Authorization", "Content-Length")); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/config/先读我.md b/src/main/java/com/campus/water/config/先读我.md new file mode 100644 index 0000000..3b52c1f --- /dev/null +++ b/src/main/java/com/campus/water/config/先读我.md @@ -0,0 +1 @@ +# 本md仅用于初始化目录,未创建所有子一级目录,在当前目录创建文件后请自行删除 \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/AlertController.java b/src/main/java/com/campus/water/controller/AlertController.java new file mode 100644 index 0000000..dd2e766 --- /dev/null +++ b/src/main/java/com/campus/water/controller/AlertController.java @@ -0,0 +1,74 @@ +package com.campus.water.controller; + +import com.campus.water.entity.Alert; +import com.campus.water.mapper.AlertRepository; +import com.campus.water.util.ResultVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDateTime; +import java.util.List; + +@RestController +@RequestMapping("/api/alerts") +@RequiredArgsConstructor +@Tag(name = "告警管理接口") +public class AlertController { + + private final AlertRepository alertRepository; + + @GetMapping("/history") + @PreAuthorize("hasAnyRole('ADMIN', 'REPAIRMAN')") + @Operation(summary = "分页查询告警历史(支持多条件筛选)") + public ResultVO> getAlertHistory( + @Parameter(description = "设备ID(可选)") @RequestParam(required = false) String deviceId, + @Parameter(description = "告警级别(可选,如error、critical)") @RequestParam(required = false) String level, + @Parameter(description = "告警状态(可选,如pending、resolved)") @RequestParam(required = false) String status, + @Parameter(description = "开始时间(可选,格式:yyyy-MM-dd HH:mm:ss)") @RequestParam(required = false) LocalDateTime startTime, + @Parameter(description = "结束时间(可选)") @RequestParam(required = false) LocalDateTime endTime, + @Parameter(description = "所属区域(维修人员仅能查询自己的区域)") @RequestParam(required = false) String areaId + ) { + List alerts; + + if (deviceId != null) { + alerts = alertRepository.findByDeviceIdAndTimestampBetween(deviceId, startTime, endTime); + } else if (level != null) { + alerts = alertRepository.findByAlertLevelAndTimestampBetween( + Alert.AlertLevel.valueOf(level), startTime, endTime); + } else if (status != null) { + alerts = alertRepository.findByStatusAndTimestampBetween( + Alert.AlertStatus.valueOf(status), startTime, endTime); + } else if (areaId != null) { + alerts = alertRepository.findByAreaIdAndTimestampBetween(areaId, startTime, endTime); + } else { + alerts = alertRepository.findByTimestampBetween(startTime, endTime); + } + + return ResultVO.success(alerts); + } + + /** + * 查询未处理告警(紧急优先) + */ + @GetMapping("/pending") + @PreAuthorize("hasAnyRole('ADMIN', 'REPAIRMAN')") + public ResultVO> getPendingAlerts( + @Parameter(description = "区域ID(可选)") @RequestParam(required = false) String areaId) { + List pendingAlerts = areaId != null + ? alertRepository.findByAreaIdAndStatus(areaId, Alert.AlertStatus.pending) + : alertRepository.findByStatus(Alert.AlertStatus.pending); + + // 按优先级排序(紧急在前)- 使用方法引用替代lambda + pendingAlerts.sort((a1, a2) -> + Integer.compare(a2.getAlertLevel().getPriority(), a1.getAlertLevel().getPriority())); + + return ResultVO.success(pendingAlerts); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/GlobalExceptionHandler.java b/src/main/java/com/campus/water/controller/GlobalExceptionHandler.java new file mode 100644 index 0000000..ba66090 --- /dev/null +++ b/src/main/java/com/campus/water/controller/GlobalExceptionHandler.java @@ -0,0 +1,80 @@ +package com.campus.water.controller; + +import com.campus.water.util.ResultVO; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.bind.MethodArgumentNotValidException; + +import java.time.format.DateTimeParseException; +import java.util.Objects; + +/** + * 全局异常处理器 - 统一处理项目中所有控制器层异常 + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 处理参数格式/值错误(如枚举值非法、参数为空等) + */ + @ExceptionHandler(IllegalArgumentException.class) + public ResultVO handleIllegalArgument(IllegalArgumentException e) { + // 针对告警级别/状态参数错误做友好提示 + String msg = e.getMessage(); + if (msg.contains("AlertLevel") || msg.contains("AlertStatus")) { + msg = "参数错误:告警级别可选值(info/warning/error/critical),告警状态可选值(pending/resolved/closed)"; + } + return ResultVO.error(400, "参数错误:" + msg); + } + + /** + * 处理参数类型不匹配(如时间格式错误、字符串转数字失败等) + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public ResultVO handleTypeMismatch(MethodArgumentTypeMismatchException e) { + String errorMsg; + // 特殊处理时间格式错误(告警查询的时间参数) + if (e.getCause() instanceof DateTimeParseException) { + errorMsg = "时间参数格式错误,正确格式:yyyy-MM-dd HH:mm:ss(示例:2025-12-05 10:30:00)"; + } else { + // 通用类型不匹配提示 + errorMsg = String.format( + "参数[%s]类型错误,期望类型:%s,实际传入值:%s", + e.getName(), + e.getRequiredType() != null ? e.getRequiredType().getSimpleName() : "未知", + e.getValue() + ); + } + return ResultVO.error(400, errorMsg); + } + + /** + * 处理权限不足异常(如非管理员/维修人员访问告警接口) + */ + @ExceptionHandler(AccessDeniedException.class) + public ResultVO handleAccessDenied(AccessDeniedException e) { + return ResultVO.error(403, "权限不足:仅管理员/维修人员可访问告警相关功能"); + } + + /** + * 处理通用运行时异常(兜底) + */ + @ExceptionHandler(RuntimeException.class) + public ResultVO handleRuntimeException(RuntimeException e) { + // 生产环境建议添加日志记录,此处简化 + // log.error("服务器运行时异常", e); + return ResultVO.error(500, "服务器内部错误:" + e.getMessage()); + } + + /** + * 处理请求参数验证失败(如@NotBlank/@Pattern等注解验证失败) + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResultVO handleMethodArgumentNotValid(MethodArgumentNotValidException e) { + // 获取第一个验证失败的字段和消息 + String errorMsg = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage(); + return ResultVO.badRequest(errorMsg); // 返回400状态码和具体错误信息 + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/LoginController.java b/src/main/java/com/campus/water/controller/LoginController.java new file mode 100644 index 0000000..eecab39 --- /dev/null +++ b/src/main/java/com/campus/water/controller/LoginController.java @@ -0,0 +1,37 @@ +package com.campus.water.controller; // 修正包路径:去掉main.java + 按规范放在common子包 + +import com.campus.water.entity.dto.request.LoginRequest; // 替换原LoginDTO为规范的LoginRequest +import com.campus.water.entity.vo.LoginVO; +import com.campus.water.service.LoginService; +import com.campus.water.util.ResultVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.validation.Valid; + +/** + * 登录接口控制器(公共接口) + */ +@RestController +@RequestMapping("/api/common") // 保持统一接口前缀 +public class LoginController { + + @Autowired + private LoginService loginService; + + /** + * 登录接口 + * @param loginRequest 登录请求参数(替换原LoginDTO) + * @return 登录响应结果 + */ + // 修改LoginController的login方法 + @PostMapping("/login") + public ResponseEntity> login(@Valid @RequestBody LoginRequest loginRequest) { + LoginVO loginVO = loginService.login(loginRequest); + return ResponseEntity.ok(ResultVO.success(loginVO)); // 用ResultVO包装 + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/RegisterController.java b/src/main/java/com/campus/water/controller/RegisterController.java new file mode 100644 index 0000000..1abe416 --- /dev/null +++ b/src/main/java/com/campus/water/controller/RegisterController.java @@ -0,0 +1,35 @@ +package com.campus.water.controller; + +import com.campus.water.entity.dto.request.RegisterRequest; +import com.campus.water.service.RegisterService; +import com.campus.water.util.ResultVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.validation.Valid; + +/** + * 注册接口控制器(公共接口) + */ +@RestController +@RequestMapping("/api/common") +public class RegisterController { + + @Autowired + private RegisterService registerService; + + /** + * 用户注册接口 + * @param registerRequest 注册请求参数 + * @return 注册结果 + */ + @PostMapping("/register") + public ResponseEntity> register(@Valid @RequestBody RegisterRequest registerRequest) { + boolean success = registerService.register(registerRequest); + return ResponseEntity.ok(ResultVO.success(success, "注册成功")); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/WaterUsageController.java b/src/main/java/com/campus/water/controller/WaterUsageController.java new file mode 100644 index 0000000..31c2671 --- /dev/null +++ b/src/main/java/com/campus/water/controller/WaterUsageController.java @@ -0,0 +1,185 @@ +package com.campus.water.controller; + +import com.campus.water.entity.*; +import com.campus.water.mapper.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@Controller +public class WaterUsageController { + + @Autowired + private DeviceTerminalMappingRepository deviceTerminalMappingRepository; + + @Autowired + private WaterMakerRealtimeDataRepository waterMakerRealtimeDataRepository; + + @Autowired + private WaterQualityHistoryRepository waterQualityHistoryRepository; + + @Autowired + private DrinkRecordRepository drinkRecordRepository; + + @Autowired + private TerminalUsageStatsRepository terminalUsageStatsRepository; + + // 扫码用水 + @Transactional + public Map scanToDrink(String terminalId, String studentId, Double waterConsumption) { + Map result = new HashMap<>(); + + try { + Optional mappingOpt = deviceTerminalMappingRepository.findByTerminalId(terminalId); + if (mappingOpt.isEmpty()) { + result.put("success", false); + result.put("message", "终端设备不存在"); + return result; + } + + DeviceTerminalMapping mapping = mappingOpt.get(); + if (mapping.getTerminalStatus() != DeviceTerminalMapping.TerminalStatus.active) { + result.put("success", false); + result.put("message", "终端设备未激活"); + return result; + } + + Optional realtimeDataOpt = + waterMakerRealtimeDataRepository.findLatestByDeviceId(mapping.getDeviceId()); + + DrinkRecord drinkRecord = new DrinkRecord(); + drinkRecord.setStudentId(studentId); + drinkRecord.setTerminalId(terminalId); + drinkRecord.setDeviceId(mapping.getDeviceId()); + + // 错误1修复:Double转BigDecimal(适配DrinkRecord的BigDecimal类型字段) + drinkRecord.setWaterConsumption(waterConsumption != null ? BigDecimal.valueOf(waterConsumption) : BigDecimal.ZERO); + drinkRecord.setDrinkTime(LocalDateTime.now()); + drinkRecord.setLocation(mapping.getTerminalName()); + + if (realtimeDataOpt.isPresent()) { + WaterMakerRealtimeData realtimeData = realtimeDataOpt.get(); + drinkRecord.setTdsValue(realtimeData.getTdsValue3()); + drinkRecord.setWaterQuality(realtimeData.getWaterQuality()); + } + + drinkRecordRepository.save(drinkRecord); + // 传入BigDecimal类型的用水量 + updateTerminalUsageStats(terminalId, BigDecimal.valueOf(waterConsumption)); + + result.put("success", true); + result.put("message", "用水成功"); + result.put("waterConsumption", waterConsumption); + result.put("timestamp", LocalDateTime.now()); + return result; + + } catch (Exception e) { + result.put("success", false); + result.put("message", "用水失败: " + e.getMessage()); + return result; + } + } + + // 更新终端使用统计(参数改为BigDecimal) + private void updateTerminalUsageStats(String terminalId, BigDecimal waterConsumption) { + LocalDateTime now = LocalDateTime.now(); + Optional statsOpt = terminalUsageStatsRepository + .findByTerminalIdAndStatDate(terminalId, now.toLocalDate()); + + TerminalUsageStats stats; + if (statsOpt.isPresent()) { + stats = statsOpt.get(); + stats.setUsageCount(stats.getUsageCount() + 1); + + // 错误2&3修复:BigDecimal加法(替代+运算符) + stats.setTotalWaterOutput(stats.getTotalWaterOutput().add(waterConsumption)); + + // 错误4修复:BigDecimal除法(替代/运算符,指定精度和舍入模式) + stats.setAvgWaterPerUse( + stats.getTotalWaterOutput() + .divide(BigDecimal.valueOf(stats.getUsageCount()), 2, BigDecimal.ROUND_HALF_UP) + ); + } else { + stats = new TerminalUsageStats(); + stats.setTerminalId(terminalId); + stats.setStatDate(now.toLocalDate()); + stats.setUsageCount(1); + + // 错误5&6修复:直接赋值BigDecimal(适配TerminalUsageStats的BigDecimal字段) + stats.setTotalWaterOutput(waterConsumption); + stats.setAvgWaterPerUse(waterConsumption); + stats.setPeakHour(String.format("%02d:00", now.getHour())); + } + + stats.setUpdatedTime(now); + terminalUsageStatsRepository.save(stats); + } + + // 获取水质信息 + public Map getWaterQualityInfo(String deviceId) { + Map result = new HashMap<>(); + + try { + Optional realtimeDataOpt = + waterMakerRealtimeDataRepository.findLatestByDeviceId(deviceId); + + Optional qualityHistoryOpt = + waterQualityHistoryRepository.findLatestByDeviceId(deviceId); + + if (realtimeDataOpt.isPresent()) { + WaterMakerRealtimeData realtimeData = realtimeDataOpt.get(); + result.put("deviceId", deviceId); + // 如需返回Double给前端:BigDecimal转Double + result.put("rawWaterTds", realtimeData.getTdsValue1() != null ? realtimeData.getTdsValue1().doubleValue() : null); + result.put("pureWaterTds", realtimeData.getTdsValue2() != null ? realtimeData.getTdsValue2().doubleValue() : null); + result.put("mineralWaterTds", realtimeData.getTdsValue3() != null ? realtimeData.getTdsValue3().doubleValue() : null); + result.put("waterQuality", realtimeData.getWaterQuality()); + result.put("filterLife", realtimeData.getFilterLife()); + result.put("status", realtimeData.getStatus()); + result.put("updateTime", realtimeData.getRecordTime()); + } + + if (qualityHistoryOpt.isPresent()) { + WaterQualityHistory qualityHistory = qualityHistoryOpt.get(); + result.put("lastDetectionTime", qualityHistory.getDetectedTime()); + } + + result.put("success", true); + return result; + + } catch (Exception e) { + result.put("success", false); + result.put("message", "获取水质信息失败: " + e.getMessage()); + return result; + } + } + + // 获取终端设备信息 + public Map getTerminalInfo(String terminalId) { + Map result = new HashMap<>(); + + Optional mappingOpt = deviceTerminalMappingRepository.findByTerminalId(terminalId); + if (mappingOpt.isPresent()) { + DeviceTerminalMapping mapping = mappingOpt.get(); + result.put("terminalId", terminalId); + result.put("terminalName", mapping.getTerminalName()); + result.put("deviceId", mapping.getDeviceId()); + result.put("status", mapping.getTerminalStatus()); + result.put("success", true); + + Map qualityInfo = getWaterQualityInfo(mapping.getDeviceId()); + result.putAll(qualityInfo); + } else { + result.put("success", false); + result.put("message", "终端设备不存在"); + } + + return result; + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/WorkOrderController.java b/src/main/java/com/campus/water/controller/WorkOrderController.java new file mode 100644 index 0000000..327cfe1 --- /dev/null +++ b/src/main/java/com/campus/water/controller/WorkOrderController.java @@ -0,0 +1,174 @@ +package com.campus.water.controller; + +import com.campus.water.entity.WorkOrder; +import com.campus.water.service.WorkOrderService; +import com.campus.water.util.ResultVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/work-orders") +public class WorkOrderController { + + private final WorkOrderService workOrderService; + + @Autowired + public WorkOrderController(WorkOrderService workOrderService) { + this.workOrderService = workOrderService; + } + + // 抢单功能 - 维修人员和管理员可访问 + @PostMapping("/grab") + @PreAuthorize("hasAnyRole('REPAIRMAN', 'SUPER_ADMIN', 'AREA_ADMIN')") + public ResultVO grabOrder( + @RequestParam String orderId, + @RequestParam String repairmanId) { + try { + boolean result = workOrderService.grabOrder(orderId, repairmanId); + return result ? ResultVO.success(true, "抢单成功") + : ResultVO.error(400, "抢单失败,工单可能已被抢走或状态异常"); + } catch (Exception e) { + return ResultVO.error(500, "抢单失败:" + e.getMessage()); + } + } + + // 拒单功能 - 维修人员和管理员可访问 + @PostMapping("/reject") + @PreAuthorize("hasAnyRole('REPAIRMAN', 'SUPER_ADMIN', 'AREA_ADMIN')") + public ResultVO rejectOrder( + @RequestParam String orderId, + @RequestParam String repairmanId, + @RequestParam String reason) { + try { + boolean result = workOrderService.rejectOrder(orderId, repairmanId, reason); + return result ? ResultVO.success(true, "拒单成功") + : ResultVO.error(400, "拒单失败,工单状态异常"); + } catch (Exception e) { + return ResultVO.error(500, "拒单失败:" + e.getMessage()); + } + } + + // 提交维修结果 - 维修人员和管理员可访问 + @PostMapping("/submit") + @PreAuthorize("hasAnyRole('REPAIRMAN', 'SUPER_ADMIN', 'AREA_ADMIN')") + public ResultVO submitRepairResult( + @RequestParam String orderId, + @RequestParam String repairmanId, + @RequestParam String dealNote, + @RequestParam(required = false) String imgUrl) { + try { + boolean result = workOrderService.submitRepairResult(orderId, repairmanId, dealNote, imgUrl); + return result ? ResultVO.success(true, "维修结果提交成功,等待审核") + : ResultVO.error(400, "提交失败,工单状态异常"); + } catch (Exception e) { + return ResultVO.error(500, "提交失败:" + e.getMessage()); + } + } + + // 新增:审核工单接口(管理员专用) + @PostMapping("/review") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") + public ResultVO reviewOrder( + @RequestParam String orderId, + @RequestParam boolean approved) { + try { + boolean result = workOrderService.reviewOrder(orderId, approved); + return result ? ResultVO.success(true, approved ? "审核通过" : "审核不通过") + : ResultVO.error(400, "审核失败,工单状态异常"); + } catch (Exception e) { + return ResultVO.error(500, "审核失败:" + e.getMessage()); + } + } + + // 获取可抢工单列表 - 维修人员和管理员可访问 + @GetMapping("/available") + @PreAuthorize("hasAnyRole('REPAIRMAN','SUPER_ADMIN', 'AREA_ADMIN')") + public ResultVO> getAvailableOrders( + @RequestParam(required = false) String areaId, // 改为非必填 + Authentication authentication) { // 获取当前登录用户的认证信息 + try { + // 1. 判断当前用户角色 + boolean isRepairman = authentication.getAuthorities().contains( + new SimpleGrantedAuthority("ROLE_REPAIRMAN") + ); + boolean isAdmin = authentication.getAuthorities().stream() + .anyMatch(auth -> auth.getAuthority().equals("ROLE_SUPER_ADMIN") + || auth.getAuthority().equals("ROLE_AREA_ADMIN")); + + // 2. 角色逻辑校验 + List orders; + if (isRepairman) { + // 维修人员:必须传areaId,否则抛异常 + if (areaId == null || areaId.trim().isEmpty()) { + return ResultVO.error(400, "维修人员查询可抢工单必须传入区域ID"); + } + // 维修人员:查指定区域的可抢工单 + orders = workOrderService.getAvailableOrders(areaId); + } else if (isAdmin) { + // 管理员:无需传areaId,查所有区域的可抢工单 + // 给service层传null,让service层识别为"查所有" + orders = workOrderService.getAvailableOrders(null); + } else { + // 非授权角色(理论上被@PreAuthorize拦截,不会走到这) + return ResultVO.error(403, "无权限访问"); + } + + return ResultVO.success(orders); + } catch (Exception e) { + return ResultVO.error(500, "获取工单列表失败:" + e.getMessage()); + } + } + + // 管理员按状态查询工单 + @GetMapping("/by-status") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") + public ResultVO> getOrdersByStatus( + @RequestParam WorkOrder.OrderStatus status, + @RequestParam(required = false) String areaId) { + try { + List orders; + if (areaId == null || areaId.trim().isEmpty()) { + // 查所有区域的指定状态工单 + orders = workOrderService.getOrdersByStatus(status); + } else { + // 查指定区域的指定状态工单 + orders = workOrderService.getOrdersByAreaAndStatus(areaId, status); + } + return ResultVO.success(orders); + } catch (Exception e) { + return ResultVO.error(500, "获取工单失败:" + e.getMessage()); + } + } + + // 获取维修工自己的工单 - 维修人员和管理员可访问 + @GetMapping("/my") + @PreAuthorize("hasAnyRole('REPAIRMAN', 'SUPER_ADMIN', 'AREA_ADMIN')") + public ResultVO> getMyOrders(@RequestParam String repairmanId) { + try { + List orders = workOrderService.getMyOrders(repairmanId); + return ResultVO.success(orders); + } catch (Exception e) { + return ResultVO.error(500, "获取我的工单失败:" + e.getMessage()); + } + } + + // 管理员手动派单接口 + @PostMapping("/assign") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") + public ResultVO assignOrderByAdmin( + @RequestParam String orderId, + @RequestParam String repairmanId) { + try { + boolean result = workOrderService.assignOrderByAdmin(orderId, repairmanId); + return result ? ResultVO.success(true, "派单成功") + : ResultVO.error(400, "派单失败,工单或维修人员状态异常"); + } catch (Exception e) { + return ResultVO.error(500, "派单失败:" + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/app/RepairmanAppController.java b/src/main/java/com/campus/water/controller/app/RepairmanAppController.java new file mode 100644 index 0000000..2e3202f --- /dev/null +++ b/src/main/java/com/campus/water/controller/app/RepairmanAppController.java @@ -0,0 +1,48 @@ +package com.campus.water.controller.app; + +import com.campus.water.entity.WorkOrder; +import com.campus.water.service.app.RepairmanAppService; +import com.campus.water.util.ResultVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/app/repairman") // 维修人员APP端接口前缀 +public class RepairmanAppController { + + @Autowired + private RepairmanAppService repairmanAppService; + + // 获取可抢工单列表 + @GetMapping("/available-orders") + public ResultVO> getAvailableOrders(@RequestParam String areaId) { + return repairmanAppService.getAvailableOrders(areaId); + } + + // 抢单 + @PostMapping("/grab-order") + public ResultVO grabOrder(@RequestBody Map request) { + return repairmanAppService.grabOrder(request); + } + + // 拒单 + @PostMapping("/reject-order") + public ResultVO rejectOrder(@RequestBody Map request) { + return repairmanAppService.rejectOrder(request); + } + + // 提交维修结果 + @PostMapping("/submit-result") + public ResultVO submitRepairResult(@RequestBody Map request) { + return repairmanAppService.submitRepairResult(request); + } + + // 获取我的工单 + @GetMapping("/my-orders") + public ResultVO> getMyOrders(@RequestParam String repairmanId) { + return repairmanAppService.getMyOrders(repairmanId); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/app/先读我.md b/src/main/java/com/campus/water/controller/app/先读我.md new file mode 100644 index 0000000..3b52c1f --- /dev/null +++ b/src/main/java/com/campus/water/controller/app/先读我.md @@ -0,0 +1 @@ +# 本md仅用于初始化目录,未创建所有子一级目录,在当前目录创建文件后请自行删除 \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/common/先读我.md b/src/main/java/com/campus/water/controller/common/先读我.md new file mode 100644 index 0000000..3b52c1f --- /dev/null +++ b/src/main/java/com/campus/water/controller/common/先读我.md @@ -0,0 +1 @@ +# 本md仅用于初始化目录,未创建所有子一级目录,在当前目录创建文件后请自行删除 \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/web/AdminController.java b/src/main/java/com/campus/water/controller/web/AdminController.java new file mode 100644 index 0000000..3407f85 --- /dev/null +++ b/src/main/java/com/campus/water/controller/web/AdminController.java @@ -0,0 +1,103 @@ +package com.campus.water.controller.web; + +import com.campus.water.entity.Admin; +import com.campus.water.service.AdminService; +import com.campus.water.util.ResultVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Optional; + +@RestController +@RequestMapping("/api/web/admin") +@RequiredArgsConstructor +@Tag(name = "管理员管理接口", description = "Web管理端管理员操作接口") +public class AdminController { + + private final AdminService adminService; + + /** + * 获取管理员列表(支持姓名/角色筛选) + */ + @GetMapping("/list") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") // 超级/区域管理员可查看 + @Operation(summary = "获取管理员列表", description = "支持按姓名模糊搜索、按角色筛选") + public ResponseEntity>> getAdminList( + @RequestParam(required = false) String name, + @RequestParam(required = false) Admin.AdminRole role // 角色筛选参数 + ) { + try { + List adminList = adminService.getAdminList(name, role); + return ResponseEntity.ok(ResultVO.success(adminList)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "查询失败:" + e.getMessage())); + } + } + + /** + * 获取所有管理员角色枚举 + */ + @GetMapping("/roles") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") + @Operation(summary = "获取管理员角色列表", description = "返回所有可选角色(super_admin/area_admin/viewer)") + public ResponseEntity> getAllRoles() { + try { + Admin.AdminRole[] roles = adminService.getAllRoles(); + return ResponseEntity.ok(ResultVO.success(roles)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "获取角色列表失败:" + e.getMessage())); + } + } + + /** + * 新增/编辑管理员 + */ + @PostMapping("/save") + @PreAuthorize("hasRole('SUPER_ADMIN')") // 仅超级管理员可新增/编辑 + @Operation(summary = "保存管理员", description = "新增/编辑管理员,支持指定角色") + public ResponseEntity> saveAdmin(@RequestBody Admin admin) { + try { + Admin savedAdmin = adminService.saveAdmin(admin); + return ResponseEntity.ok(ResultVO.success(savedAdmin)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "保存失败:" + e.getMessage())); + } + } + + /** + * 删除管理员 + */ + @DeleteMapping("/{adminId}") + @PreAuthorize("hasRole('SUPER_ADMIN')") // 仅超级管理员可删除 + @Operation(summary = "删除管理员", description = "按ID删除管理员") + public ResponseEntity> deleteAdmin(@PathVariable String adminId) { + try { + adminService.deleteAdmin(adminId); + return ResponseEntity.ok(ResultVO.success(null)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "删除失败:" + e.getMessage())); + } + } + + /** + * 管理员登录 + */ + @PostMapping("/login") + @Operation(summary = "管理员登录", description = "用户名+密码验证,返回管理员信息(含角色)") + public ResponseEntity> login( + @RequestParam String adminName, + @RequestParam String password + ) { + Optional admin = adminService.login(adminName, password); + if (admin.isPresent()) { + return ResponseEntity.ok(ResultVO.success(admin.get())); + } else { + return ResponseEntity.ok(ResultVO.error(401, "用户名或密码错误")); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/web/DeviceController.java b/src/main/java/com/campus/water/controller/web/DeviceController.java new file mode 100644 index 0000000..8163470 --- /dev/null +++ b/src/main/java/com/campus/water/controller/web/DeviceController.java @@ -0,0 +1,140 @@ +package com.campus.water.controller.web; + +import com.campus.water.entity.Device; +import com.campus.water.entity.RepairerAuth; +import com.campus.water.mapper.RepairerAuthRepository; +import com.campus.water.service.DeviceService; +import com.campus.water.entity.Repairman; +import com.campus.water.mapper.RepairmanRepository; +import com.campus.water.util.ResultVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.security.core.Authentication; +import org.springframework.security.access.prepost.PreAuthorize; + +import java.util.List; // 新增List的导入语句 + +@RestController +@RequestMapping("/api/web/device") +@RequiredArgsConstructor +@Tag(name = "设备管理接口", description = "Web管理端设备新增与删除接口") +public class DeviceController { + + private final DeviceService deviceService; + private final RepairmanRepository repairmanRepository; + private final RepairerAuthRepository repairerAuthRepository; + + /** + * 新增设备 + */ + @PostMapping("/add") + @Operation(summary = "新增设备", description = "添加新设备信息,包括设备ID、名称、类型等") + public ResponseEntity> addDevice(@Valid @RequestBody Device device) { + try { + Device newDevice = deviceService.addDevice(device); + return ResponseEntity.ok(ResultVO.success(newDevice, "设备新增成功")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "设备新增失败: " + e.getMessage())); + } + } + + /** + * 删除设备 + */ + @DeleteMapping("/delete/{deviceId}") + @Operation(summary = "删除设备", description = "根据设备ID删除指定设备(需先解除终端绑定)") + public ResponseEntity> deleteDevice(@PathVariable String deviceId) { + try { + deviceService.deleteDevice(deviceId); + return ResponseEntity.ok(ResultVO.success(true, "设备删除成功")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "设备删除失败: " + e.getMessage())); + } + } + + @PostMapping("/relate") + public ResponseEntity> relateSupplier(@RequestParam String supplierId, @RequestParam String makerId) { + try { + deviceService.relateSupplierToMaker(supplierId, makerId); + return ResponseEntity.ok(ResultVO.success(null, "关联成功")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, e.getMessage())); + } + } + + // 解除关联 + @PostMapping("/unrelate") + public ResponseEntity> unrelateSupplier(@RequestParam String supplierId) { + try { + deviceService.unrelateSupplierFromMaker(supplierId); + return ResponseEntity.ok(ResultVO.success(null, "解除关联成功")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, e.getMessage())); + } + } + + // 查询制水机关联的供水机 + @GetMapping("/maker/{makerId}/suppliers") + public ResponseEntity>> getSuppliers(@PathVariable String makerId) { + List suppliers = deviceService.getSuppliersByMaker(makerId); + return ResponseEntity.ok(ResultVO.success(suppliers)); + } + + /** + * 维修人员查询本辖区设备(按类型筛选) + */ + // 在DeviceController.java中修改getAreaDevicesByTypeForRepairman方法 + @GetMapping("/repairman/area-devices-by-type") + @PreAuthorize("hasRole('REPAIRMAN')") + @Operation(summary = "维修人员查询辖区设备(按类型)", description = "维修人员查看本辖区内指定类型的设备列表") + public ResponseEntity>> getAreaDevicesByTypeForRepairman( + @RequestParam String deviceType, + Authentication authentication) { + try { + // 1. 获取当前登录用户名(username) + String username = authentication.getName(); + + // 2. 通过用户名查询RepairerAuth获取维修人员ID + RepairerAuth repairerAuth = repairerAuthRepository.findByUsername(username) + .orElseThrow(() -> new RuntimeException("维修人员认证信息不存在")); + String repairmanId = repairerAuth.getRepairmanId(); + + // 3. 通过维修人员ID查询所属区域 + Repairman repairman = repairmanRepository.findById(repairmanId) + .orElseThrow(() -> new RuntimeException("维修人员信息不存在")); + String areaId = repairman.getAreaId(); + if (areaId == null || areaId.isEmpty()) { + return ResponseEntity.ok(ResultVO.error(400, "维修人员未分配辖区")); + } + + // 4. 转换设备类型并查询 + Device.DeviceType type = Device.DeviceType.valueOf(deviceType); + List devices = deviceService.queryDevices(areaId, type, null); + + return ResponseEntity.ok(ResultVO.success(devices)); + } catch (IllegalArgumentException e) { + return ResponseEntity.ok(ResultVO.error(400, "无效的设备类型: " + deviceType)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "查询设备失败: " + e.getMessage())); + } + } + + /** + * 根据设备ID查询设备详情 + */ + @GetMapping("/{deviceId}") + @Operation(summary = "查询设备详情", description = "根据设备ID获取设备的详细信息") + public ResponseEntity> getDeviceDetail(@PathVariable String deviceId) { + try { + Device device = deviceService.getDeviceById(deviceId); + return ResponseEntity.ok(ResultVO.success(device, "设备查询成功")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "设备查询失败: " + e.getMessage())); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/web/DeviceStatusController.java b/src/main/java/com/campus/water/controller/web/DeviceStatusController.java new file mode 100644 index 0000000..f091cd7 --- /dev/null +++ b/src/main/java/com/campus/water/controller/web/DeviceStatusController.java @@ -0,0 +1,156 @@ +/** + * Web端设备状态管理接口控制器 + * 功能:提供设备状态管理的RESTful API接口 + * 用途:支持Web管理端对设备状态的手动和自动管理 + * 接口列表: + * 1. 状态更新:单设备状态变更 + * 2. 状态标记:在线/离线/故障快捷操作 + * 3. 批量操作:批量更新设备状态 + * 4. 状态查询:按状态筛选设备列表 + * 5. 离线检测:查询超时离线设备 + * 6. 自动检测:触发离线设备检测任务 + * 安全:需要权限验证,记录操作日志 + */ +package com.campus.water.controller.web; + +import com.campus.water.entity.Device; +import com.campus.water.entity.dto.request.DeviceStatusUpdateRequest; +import com.campus.water.service.DeviceStatusService; +import com.campus.water.util.ResultVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/web/device-status") +@RequiredArgsConstructor +@Tag(name = "设备状态管理接口", description = "Web管理端设备状态管理接口") +public class DeviceStatusController { + + private final DeviceStatusService deviceStatusService; + + @PostMapping("/update") + @Operation(summary = "更新设备状态", description = "手动更新设备状态(在线/离线/故障)") + public ResponseEntity> updateDeviceStatus( + @Valid @RequestBody DeviceStatusUpdateRequest request) { + try { + boolean result = deviceStatusService.updateDeviceStatus(request); + return ResponseEntity.ok(ResultVO.success(result, "设备状态更新成功")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "设备状态更新失败: " + e.getMessage())); + } + } + + @PostMapping("/{deviceId}/online") + @Operation(summary = "标记设备在线", description = "将设备标记为在线状态") + public ResponseEntity> markDeviceOnline(@PathVariable String deviceId) { + try { + boolean result = deviceStatusService.markDeviceOnline(deviceId); + return ResponseEntity.ok(ResultVO.success(result, "设备已标记为在线")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "标记设备在线失败: " + e.getMessage())); + } + } + + @PostMapping("/{deviceId}/offline") + @Operation(summary = "标记设备离线", description = "将设备标记为离线状态") + public ResponseEntity> markDeviceOffline( + @PathVariable String deviceId, + @RequestParam(required = false) String reason) { + try { + boolean result = deviceStatusService.markDeviceOffline(deviceId, reason); + return ResponseEntity.ok(ResultVO.success(result, "设备已标记为离线")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "标记设备离线失败: " + e.getMessage())); + } + } + + @PostMapping("/{deviceId}/fault") + @Operation(summary = "标记设备故障", description = "将设备标记为故障状态") + public ResponseEntity> markDeviceFault( + @PathVariable String deviceId, + @RequestParam String faultType, + @RequestParam String description) { + try { + boolean result = deviceStatusService.markDeviceFault(deviceId, faultType, description); + return ResponseEntity.ok(ResultVO.success(result, "设备已标记为故障")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "标记设备故障失败: " + e.getMessage())); + } + } + + @PostMapping("/batch-update") + @Operation(summary = "批量更新设备状态", description = "批量更新多个设备的状态") + public ResponseEntity> batchUpdateDeviceStatus( + @RequestParam List deviceIds, + @RequestParam String status, + @RequestParam(required = false) String remark) { + try { + boolean result = deviceStatusService.batchUpdateDeviceStatus(deviceIds, status, remark); + return ResponseEntity.ok(ResultVO.success(result, "批量更新设备状态成功")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "批量更新设备状态失败: " + e.getMessage())); + } + } + + @GetMapping("/by-status") + @Operation(summary = "按状态查询设备", description = "根据状态和设备类型查询设备列表") + public ResponseEntity>> getDevicesByStatus( + @RequestParam String status, + @RequestParam(required = false) String areaId, + @RequestParam(required = false) String deviceType) { // 保留设备类型参数,去除默认值 + + try { + // 调用服务层方法时传递所有参数(包括可能为null的deviceType) + List devices = deviceStatusService.getDevicesByStatus(status, areaId, deviceType); + return ResponseEntity.ok(ResultVO.success(devices)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "查询设备失败: " + e.getMessage())); + } + } + + + @GetMapping("/status-count") + @Operation(summary = "设备状态数量统计", description = "统计各状态设备数量") + public ResponseEntity>> getDeviceStatusCount( + @RequestParam(required = false) String areaId, + @RequestParam(required = false) String deviceType) { + try { + Map result = deviceStatusService.getDeviceStatusCount(areaId, deviceType); + return ResponseEntity.ok(ResultVO.success(result)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "设备状态统计失败: " + e.getMessage())); + } + } + + @GetMapping("/offline-detection") + @Operation(summary = "离线设备检测", description = "检测离线时间超过阈值的设备") + public ResponseEntity>> getOfflineDevices( + @RequestParam(defaultValue = "30") Integer thresholdMinutes, + @RequestParam(required = false) String areaId) { + try { + List devices = deviceStatusService.getOfflineDevicesExceedThreshold(thresholdMinutes, areaId); + return ResponseEntity.ok(ResultVO.success(devices)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "离线设备检测失败: " + e.getMessage())); + } + } + + @PostMapping("/auto-detect-offline") + @Operation(summary = "自动检测离线设备", description = "自动检测并标记离线设备") + public ResponseEntity> autoDetectOfflineDevices( + @RequestParam(defaultValue = "30") Integer thresholdMinutes) { + try { + deviceStatusService.autoDetectOfflineDevices(thresholdMinutes); + return ResponseEntity.ok(ResultVO.success("离线设备检测完成")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "自动检测离线设备失败: " + e.getMessage())); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/web/RepairmanController.java b/src/main/java/com/campus/water/controller/web/RepairmanController.java new file mode 100644 index 0000000..e93849e --- /dev/null +++ b/src/main/java/com/campus/water/controller/web/RepairmanController.java @@ -0,0 +1,136 @@ +package com.campus.water.controller.web; + +import com.campus.water.entity.Repairman; +import com.campus.water.service.RepairmanService; +import com.campus.water.util.ResultVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Optional; + +@RestController +@RequestMapping("/api/web/repairman") +@RequiredArgsConstructor +@Tag(name = "维修人员管理接口", description = "Web管理端维修人员列表查询、新增、修改、删除接口") +public class RepairmanController { + + private final RepairmanService repairmanService; + + /** + * 获取维修人员列表(支持多条件筛选) + * @param name 姓名模糊查询(可选) + * @param areaId 区域ID筛选(可选) + * @param status 状态筛选(可选,值:idle/busy/vacation) + */ + @GetMapping("/list") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") // 仅管理员可访问 + @Operation(summary = "获取维修人员列表", description = "支持按姓名、区域和状态筛选维修人员") + public ResponseEntity>> getRepairmanList( + @RequestParam(required = false) String name, + @RequestParam(required = false) String areaId, + @RequestParam(required = false) String status + ) { + try { + // 转换状态参数为枚举类型 + Repairman.RepairmanStatus repairmanStatus = status != null + ? Repairman.RepairmanStatus.valueOf(status) + : null; + + // 调用服务层查询 + List repairmanList = repairmanService.getRepairmanList(name, areaId, repairmanStatus); + return ResponseEntity.ok(ResultVO.success(repairmanList)); + } catch (IllegalArgumentException e) { + // 处理枚举参数错误 + return ResponseEntity.ok(ResultVO.error(400, "无效的状态参数: " + e.getMessage())); + } catch (Exception e) { + // 处理其他异常 + return ResponseEntity.ok(ResultVO.error(500, "查询维修人员列表失败: " + e.getMessage())); + } + } + + /** + * 获取所有维修人员状态枚举 + */ + @GetMapping("/status") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") + @Operation(summary = "获取维修人员状态列表", description = "返回所有可选状态(idle/busy/vacation)") + public ResponseEntity> getAllStatus() { + try { + Repairman.RepairmanStatus[] statuses = repairmanService.getAllStatus(); + return ResponseEntity.ok(ResultVO.success(statuses)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "获取状态列表失败:" + e.getMessage())); + } + } + + /** + * 新增/编辑维修人员 + */ + @PostMapping("/save") + @PreAuthorize("hasRole('SUPER_ADMIN')") // 仅超级管理员可操作 + @Operation(summary = "保存维修人员信息", description = "新增或编辑维修人员信息,ID存在则更新,不存在则新增") + public ResponseEntity> saveRepairman(@Valid @RequestBody Repairman repairman) { + try { + Repairman savedRepairman = repairmanService.saveRepairman(repairman); + return ResponseEntity.ok(ResultVO.success(savedRepairman, + repairman.getRepairmanId() == null ? "维修人员新增成功" : "维修人员更新成功")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "保存维修人员失败: " + e.getMessage())); + } + } + + /** + * 删除维修人员 + */ + @DeleteMapping("/{repairmanId}") + @PreAuthorize("hasRole('SUPER_ADMIN')") // 仅超级管理员可操作 + @Operation(summary = "删除维修人员", description = "根据维修人员ID删除指定维修人员") + public ResponseEntity> deleteRepairman(@PathVariable String repairmanId) { + try { + repairmanService.deleteRepairman(repairmanId); + return ResponseEntity.ok(ResultVO.success(null, "维修人员删除成功")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "删除维修人员失败: " + e.getMessage())); + } + } + + /** + * 根据ID查询维修人员详情 + */ + @GetMapping("/{repairmanId}") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") + @Operation(summary = "获取维修人员详情", description = "根据ID查询维修人员详细信息") + public ResponseEntity> getRepairmanById(@PathVariable String repairmanId) { + try { + Optional repairman = repairmanService.getRepairmanById(repairmanId); + return repairman.map(value -> ResponseEntity.ok(ResultVO.success(value))) + .orElseGet(() -> ResponseEntity.ok(ResultVO.error(404, "维修人员不存在"))); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "查询维修人员详情失败: " + e.getMessage())); + } + } + + /** + * 根据片区ID查询维修人员 + * 专门用于仅按片区筛选维修人员的场景,返回指定片区内的所有维修人员 + */ + @GetMapping("/by-area/{areaId}") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") // 保持与其他查询接口一致的权限控制 + @Operation(summary = "根据片区查询维修人员", description = "根据指定的片区ID,查询该片区内的所有维修人员") + public ResponseEntity>> getRepairmenByArea(@PathVariable String areaId) { + try { + // 调用现有服务层方法,仅传入areaId,其他参数为null(表示不筛选) + List repairmen = repairmanService.getRepairmanList(null, areaId, null); + return ResponseEntity.ok(ResultVO.success(repairmen)); + } catch (Exception e) { + // 统一异常处理,与其他接口保持一致 + return ResponseEntity.ok(ResultVO.error(500, "根据片区查询维修人员失败: " + e.getMessage())); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/web/StatisticsController.java b/src/main/java/com/campus/water/controller/web/StatisticsController.java new file mode 100644 index 0000000..6d6b666 --- /dev/null +++ b/src/main/java/com/campus/water/controller/web/StatisticsController.java @@ -0,0 +1,103 @@ +/** + * Web端统计接口控制器 + * 功能:提供Web管理端的统计数据查询API接口 + * 用途:前后端分离架构中的后端API服务 + * 接口列表: + * 1. POST /water-usage: 用水量统计(支持多维度) + * 2. POST /alarm: 告警统计(次数、处理情况) + * 3. GET /device-status: 设备状态数量统计 + * 4. GET /dashboard: 仪表板综合数据 + * 5. GET /hot-devices: 热门设备用水量排名 + * 技术:Spring MVC、参数验证、统一响应格式 + */ +package com.campus.water.controller.web; + +import com.campus.water.entity.vo.AlarmStatisticsVO; +import com.campus.water.entity.vo.StatisticsVO; +import com.campus.water.entity.dto.request.StatisticsQueryRequest; +import com.campus.water.service.StatisticsService; +import com.campus.water.util.ResultVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@RestController +@RequestMapping("/api/web/statistics") +@RequiredArgsConstructor +@Tag(name = "统计分析接口", description = "Web管理端统计分析接口") +public class StatisticsController { + + private final StatisticsService statisticsService; + + @PostMapping("/water-usage") + @Operation(summary = "用水量统计", description = "按设备/区域/时间统计用水量") + public ResponseEntity> getWaterUsageStatistics( + @Valid @RequestBody StatisticsQueryRequest request) { + try { + StatisticsVO result = statisticsService.getWaterUsageStatistics(request); + return ResponseEntity.ok(ResultVO.success(result)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "统计失败: " + e.getMessage())); + } + } + + @PostMapping("/alarm") + @Operation(summary = "告警统计", description = "统计告警次数和处理情况") + public ResponseEntity> getAlarmStatistics( + @Valid @RequestBody StatisticsQueryRequest request) { + try { + AlarmStatisticsVO result = statisticsService.getAlarmStatistics(request); + return ResponseEntity.ok(ResultVO.success(result)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "告警统计失败: " + e.getMessage())); + } + } + + @GetMapping("/device-status") + @Operation(summary = "设备状态统计", description = "统计各状态设备数量") + public ResponseEntity>> getDeviceStatusStatistics( + @RequestParam(required = false) String areaId, + @RequestParam(required = false) String deviceType) { + try { + Map result = statisticsService.getDeviceStatusStatistics(areaId, deviceType); + return ResponseEntity.ok(ResultVO.success(result)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "设备状态统计失败: " + e.getMessage())); + } + } + + @GetMapping("/dashboard") + @Operation(summary = "仪表盘数据", description = "获取综合仪表盘统计数据") + public ResponseEntity>> getDashboardStatistics() { + try { + Map result = statisticsService.getDashboardStatistics(); + return ResponseEntity.ok(ResultVO.success(result)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "获取仪表盘数据失败: " + e.getMessage())); + } + } + + @GetMapping("/hot-devices") + @Operation(summary = "热门设备统计", description = "获取用水量最高的设备") + public ResponseEntity> getHotDevices( + @RequestParam(defaultValue = "7") Integer days, + @RequestParam(defaultValue = "10") Integer limit) { + try { + StatisticsQueryRequest request = new StatisticsQueryRequest(); + request.setStatType("by_device"); + request.setStartDate(java.time.LocalDate.now().minusDays(days)); + request.setEndDate(java.time.LocalDate.now()); + request.setLimit(limit); + + StatisticsVO result = statisticsService.getWaterUsageStatistics(request); + return ResponseEntity.ok(ResultVO.success(result)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "获取热门设备失败: " + e.getMessage())); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/web/UserController.java b/src/main/java/com/campus/water/controller/web/UserController.java new file mode 100644 index 0000000..fdaa784 --- /dev/null +++ b/src/main/java/com/campus/water/controller/web/UserController.java @@ -0,0 +1,112 @@ +package com.campus.water.controller.web; + +import com.campus.water.entity.User; +import com.campus.water.service.UserService; +import com.campus.water.util.ResultVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; // 补充Web注解的统一导入 + +import java.util.List; +import java.util.Optional; // 补充Optional的导入 + + +@RestController +@RequestMapping("/api/web/user") +@RequiredArgsConstructor +@Tag(name = "学生管理接口", description = "Web管理端学生操作接口") +public class UserController { + + private final UserService userService; // 只依赖Service,不直接依赖Repository + + + /** + * 获取学生列表(支持姓名/状态筛选) + */ + @GetMapping("/list") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") + @Operation(summary = "获取学生列表", description = "支持按姓名模糊搜索、按状态筛选") + public ResponseEntity>> getUserList( + @RequestParam(required = false) String studentName, + @RequestParam(required = false) User.UserStatus status + ) { + try { + List userList = userService.getUserList(studentName, status); + return ResponseEntity.ok(ResultVO.success(userList)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "查询失败:" + e.getMessage())); + } + } + + + /** + * 获取所有学生状态枚举 + */ + @GetMapping("/status") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") + @Operation(summary = "获取学生状态列表", description = "返回所有可选状态(active/inactive)") + public ResponseEntity> getAllStatus() { + try { + User.UserStatus[] statuses = User.UserStatus.values(); + return ResponseEntity.ok(ResultVO.success(statuses)); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "获取状态列表失败:" + e.getMessage())); + } + } + + + /** + * 新增/编辑学生(移除直接依赖Repository的逻辑,交给Service处理) + */ + @PostMapping("/save") // 已补充@PostMapping的导入 + @PreAuthorize("hasRole('SUPER_ADMIN')") + @Operation(summary = "保存学生信息", description = "新增/编辑学生,支持指定状态") + public ResponseEntity> saveUser(@RequestBody User user) { // 已补充@RequestBody的导入 + try { + // 学号唯一性校验移到Service层处理,Controller只调用Service + User savedUser = userService.saveUser(user); + return ResponseEntity.ok(ResultVO.success( + savedUser, + user.getStudentId() == null ? "学生新增成功" : "学生信息更新成功" + )); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "保存失败:" + e.getMessage())); + } + } + + + /** + * 删除学生 + */ + @DeleteMapping("/{studentId}") // 已补充@DeleteMapping的导入 + @PreAuthorize("hasRole('SUPER_ADMIN')") + @Operation(summary = "删除学生", description = "按学号删除学生") + public ResponseEntity> deleteUser(@PathVariable String studentId) { // 已补充@PathVariable的导入 + try { + userService.deleteUser(studentId); + return ResponseEntity.ok(ResultVO.success(null, "删除成功")); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "删除失败:" + e.getMessage())); + } + } + + + /** + * 根据学号查询学生详情 + */ + @GetMapping("/{studentId}") + @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'AREA_ADMIN')") + @Operation(summary = "获取学生详情", description = "按学号查询学生详细信息") + public ResponseEntity> getUserById(@PathVariable String studentId) { + try { + Optional user = userService.getUserById(studentId); + return user.map(value -> ResponseEntity.ok(ResultVO.success(value))) + .orElseGet(() -> ResponseEntity.ok(ResultVO.error(404, "学生不存在"))); + } catch (Exception e) { + return ResponseEntity.ok(ResultVO.error(500, "查询失败:" + e.getMessage())); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/controller/web/先读我.md b/src/main/java/com/campus/water/controller/web/先读我.md new file mode 100644 index 0000000..3b52c1f --- /dev/null +++ b/src/main/java/com/campus/water/controller/web/先读我.md @@ -0,0 +1 @@ +# 本md仅用于初始化目录,未创建所有子一级目录,在当前目录创建文件后请自行删除 \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/Admin.java b/src/main/java/com/campus/water/entity/Admin.java new file mode 100644 index 0000000..d75431c --- /dev/null +++ b/src/main/java/com/campus/water/entity/Admin.java @@ -0,0 +1,46 @@ +package com.campus.water.entity; + +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDateTime; +import org.hibernate.annotations.GenericGenerator; + +@Data +@Entity +@Table(name = "admin") +public class Admin { + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator") + @Column(name = "admin_id", length = 50, nullable = false) + private String adminId; + + @Column(name = "admin_name", length = 50) + private String adminName; + + @Column(name = "password", length = 200) + private String password; + + @Column(name = "phone", length = 20) + private String phone; + + // 恢复三个角色枚举 + @Enumerated(EnumType.STRING) + @Column(name = "role", length = 50, nullable = false) + private AdminRole role; + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + @Column(name = "updated_time") + private LocalDateTime updatedTime = LocalDateTime.now(); + + // 枚举类:恢复super_admin、area_admin、viewer三个角色 + // java/com/campus/water/entity/Admin.java + public enum AdminRole { + ROLE_SUPER_ADMIN, // 超级管理员(原super_admin) + ROLE_AREA_ADMIN, // 区域管理员(原area_admin) + ROLE_VIEWER // 查看者(原viewer) + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/Alert.java b/src/main/java/com/campus/water/entity/Alert.java new file mode 100644 index 0000000..43eb7e0 --- /dev/null +++ b/src/main/java/com/campus/water/entity/Alert.java @@ -0,0 +1,84 @@ +/** + * 告警信息实体类 + * 对应表:alert + * 用于记录设备告警信息,包括告警级别、状态、处理人等 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "alert") +public class Alert { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "alert_id") + private Long alertId; + + @Column(name = "device_id", length = 20) + private String deviceId; + + @Column(name = "alert_type", length = 50) + private String alertType; + + @Enumerated(EnumType.STRING) + @Column(name = "alert_level", length = 50) + private AlertLevel alertLevel; + + @Column(name = "alert_message", columnDefinition = "TEXT") + private String alertMessage; + + @Column(name = "area_id", length = 20) + private String areaId; + + @Enumerated(EnumType.STRING) + @Column(name = "status", length = 50) + private AlertStatus status = AlertStatus.pending; + + @Column(name = "timestamp") + private LocalDateTime timestamp = LocalDateTime.now(); + + @Column(name = "resolved_time") + private LocalDateTime resolvedTime; + + @Column(name = "resolved_by", length = 50) + private String resolvedBy; + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + @Column(name = "updated_time") + private LocalDateTime updatedTime = LocalDateTime.now(); + + public enum AlertLevel { + info("一般", 1), // 信息级(如状态通知,无需处理) + warning("一般", 2), // 警告级(需关注,非紧急) + error("紧急", 3), // 错误级(需立即处理) + critical("紧急", 4); // 严重级(影响服务,最高优先级) + + private final String levelName; // 分级名称(一般/紧急) + private final int priority; // 处理优先级(1-4,升序) + + AlertLevel(String levelName, int priority) { + this.levelName = levelName; + this.priority = priority; + } + + // 获取分级名称(用于前端展示) + public String getLevelName() { + return levelName; + } + + // 获取优先级(用于推送排序) + public int getPriority() { + return priority; + } + } + + public enum AlertStatus { + pending, processing, resolved, closed + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/Area.java b/src/main/java/com/campus/water/entity/Area.java new file mode 100644 index 0000000..45a1da0 --- /dev/null +++ b/src/main/java/com/campus/water/entity/Area.java @@ -0,0 +1,48 @@ +/** + * 区域信息实体类 + * 对应表:area + * 用于管理校园、楼宇、区域等层级结构信息 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "area") +public class Area { + @Id + @Column(name = "area_id", length = 20) + private String areaId; + + @Column(name = "area_name", length = 100) + private String areaName; + + @Enumerated(EnumType.STRING) + @Column(name = "area_type", length = 50) + private AreaType areaType; + + @Column(name = "parent_area_id", length = 20) + private String parentAreaId; + + @Column(length = 200) + private String address; + + @Column(length = 50) + private String manager; + + @Column(name = "manager_phone", length = 20) // 确保这个字段存在 + private String managerPhone; + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + @Column(name = "updated_time") + private LocalDateTime updatedTime = LocalDateTime.now(); + + public enum AreaType { + campus, building, zone + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/Device.java b/src/main/java/com/campus/water/entity/Device.java new file mode 100644 index 0000000..4c38c3e --- /dev/null +++ b/src/main/java/com/campus/water/entity/Device.java @@ -0,0 +1,60 @@ +// com/campus/water/entity/Device.java +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * 设备信息实体类(对应原始device表,不新增字段) + */ +@Data +@Entity +@Table(name = "device") +public class Device { + @Id + @Column(name = "device_id", length = 20) + private String deviceId; + + @Column(name = "device_name", length = 100) + private String deviceName; + + @Enumerated(EnumType.STRING) + @Column(name = "device_type", length = 50) + private DeviceType deviceType; + + @Column(name = "area_id", length = 20) + private String areaId; + + @Column(name = "install_location", length = 200) + private String installLocation; + + @Column(name = "install_date") + private LocalDate installDate; + + @Enumerated(EnumType.STRING) + @Column(name = "status", length = 50) + private DeviceStatus status = DeviceStatus.online; + + @Column(name = "create_time") + private LocalDateTime createTime = LocalDateTime.now(); + + // 新增:关联的制水机ID(仅供水机有值) + @Column(name = "parent_maker_id", length = 20) + private String parentMakerId; + + // 保留原有的remark方法(若表中有该字段可直接映射,无则忽略) + private String remark; + public void setRemark(String remark) { + this.remark = remark; + } + + public enum DeviceType { + water_maker, water_supply + } + + public enum DeviceStatus { + online, offline, fault + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/DeviceTerminalMapping.java b/src/main/java/com/campus/water/entity/DeviceTerminalMapping.java new file mode 100644 index 0000000..22f4a9a --- /dev/null +++ b/src/main/java/com/campus/water/entity/DeviceTerminalMapping.java @@ -0,0 +1,41 @@ +/** + * 设备与终端映射实体类 + * 对应表:device_terminal_mapping + * 用于关联设备与终端设备,记录终端状态和安装信息 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "device_terminal_mapping") +public class DeviceTerminalMapping { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "mapping_id") + private Integer mappingId; + + @Column(name = "device_id", length = 20) + private String deviceId; + + @Column(name = "terminal_id", length = 20) + private String terminalId; + + @Column(name = "terminal_name", length = 100) + private String terminalName; + + @Enumerated(EnumType.STRING) + @Column(name = "terminal_status", length = 50) + private TerminalStatus terminalStatus; + + @Column(name = "install_date") + private LocalDate installDate; + + public enum TerminalStatus { + active, inactive + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/DrinkRecommendation.java b/src/main/java/com/campus/water/entity/DrinkRecommendation.java new file mode 100644 index 0000000..417abe6 --- /dev/null +++ b/src/main/java/com/campus/water/entity/DrinkRecommendation.java @@ -0,0 +1,38 @@ +/** + * 饮水推荐实体类 + * 对应表:drink_recommendation + * 用于记录学生的每日饮水目标和当前进度 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "drink_recommendation") +public class DrinkRecommendation { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "recommendation_id") + private Integer recommendationId; + + @Column(name = "student_id", length = 50) + private String studentId; + + @Column(name = "daily_target") + private Double dailyTarget; + + @Column(name = "current_progress", precision = 6, scale = 2) + private BigDecimal currentProgress; + + @Column(name = "recommendation_date") + private LocalDate recommendationDate; + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/DrinkRecord.java b/src/main/java/com/campus/water/entity/DrinkRecord.java new file mode 100644 index 0000000..e2d9e3b --- /dev/null +++ b/src/main/java/com/campus/water/entity/DrinkRecord.java @@ -0,0 +1,51 @@ +/** + * 饮水记录实体类 + * 对应表:drink_record + * 用于记录学生的每次饮水行为,包括用水量、水质、时间、位置等 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "drink_record") +public class DrinkRecord { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "record_id") + private Long recordId; + + @Column(name = "student_id", length = 50) + private String studentId; + + @Column(name = "terminal_id", length = 20) + private String terminalId; + + @Column(name = "device_id", length = 20) + private String deviceId; + + // 根据文档修正:字段名改为 water_consumption + @Column(name = "water_consumption", precision = 6, scale = 2) + private BigDecimal waterConsumption; + + @Column(name = "water_quality", length = 50) + private String waterQuality; + + @Column(name = "tds_value", precision = 8, scale = 2) + private BigDecimal tdsValue; + + // 根据文档修正:字段名改为 drink_time + @Column(name = "drink_time") + private LocalDateTime drinkTime; + + @Column(name = "location", length = 200) + private String location; + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/InspectionRecord.java b/src/main/java/com/campus/water/entity/InspectionRecord.java new file mode 100644 index 0000000..8440772 --- /dev/null +++ b/src/main/java/com/campus/water/entity/InspectionRecord.java @@ -0,0 +1,49 @@ +/** + * 巡检记录实体类 + * 对应表:inspection_record + * 用于记录维修人员的巡检结果,包括状态、异常描述、照片等 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "inspection_record") +public class InspectionRecord { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "inspection_id") + private Integer inspectionId; + + @Column(name = "order_id", length = 30) + private String orderId; + + @Column(name = "repairman_id", length = 50) + private String repairmanId; + + @Column(name = "device_id", length = 20) + private String deviceId; + + @Enumerated(EnumType.STRING) + @Column(name = "inspection_status", length = 50) + private InspectionStatus inspectionStatus; + + @Column(name = "abnormal_description", columnDefinition = "LONGTEXT") + private String abnormalDescription; + + @Column(name = "inspection_photo", columnDefinition = "LONGTEXT") + private String inspectionPhoto; + + @Column(name = "inspection_time") + private LocalDateTime inspectionTime; + + @Column(name = "submitted_at") + private LocalDateTime submittedAt = LocalDateTime.now(); + + public enum InspectionStatus { + normal, abnormal + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/MaintenancePlan.java b/src/main/java/com/campus/water/entity/MaintenancePlan.java new file mode 100644 index 0000000..6bc5c89 --- /dev/null +++ b/src/main/java/com/campus/water/entity/MaintenancePlan.java @@ -0,0 +1,47 @@ +/** + * 维护计划实体类 + * 对应表:maintenance_plan + * 用于制定和管理设备的定期维护计划 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "maintenance_plan") +public class MaintenancePlan { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "plan_id") + private Integer planId; + + @Column(name = "device_id", length = 20) + private String deviceId; + + @Column(name = "maintenance_type", length = 50) + private String maintenanceType; + + @Column(name = "cycle_days") + private Integer cycleDays; + + @Column(name = "last_maintenance_date") + private LocalDate lastMaintenanceDate; + + @Column(name = "next_maintenance_date") + private LocalDate nextMaintenanceDate; + + @Enumerated(EnumType.STRING) + @Column(name = "plan_status", length = 50) + private PlanStatus planStatus; + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + public enum PlanStatus { + effective, expired, closed + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/MessagePush.java b/src/main/java/com/campus/water/entity/MessagePush.java new file mode 100644 index 0000000..ef6f9de --- /dev/null +++ b/src/main/java/com/campus/water/entity/MessagePush.java @@ -0,0 +1,53 @@ +/** + * 消息推送记录实体类 + * 对应表:message_push + * 用于存储系统向用户、管理员、维修人员推送的消息记录 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "message_push") +public class MessagePush { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "message_id") + private Integer messageId; + + @Column(name = "student_id", length = 50) + private String studentId; + + @Column(name = "admin_id", length = 50) + private String adminId; + + @Column(name = "repairman_id", length = 50) + private String repairmanId; + + @Column(name = "user_id", length = 50) + private String userId; + + @Column(name = "user_type", length = 50) + private String userType; + + @Column(name = "message_type", length = 50) + private String messageType; + + @Column(name = "title", length = 200) + private String title; + + @Column(name = "content", columnDefinition = "LONGTEXT") + private String content; + + @Column(name = "is_read") + private Boolean isRead = false; + + @Column(name = "push_time") + private LocalDateTime pushTime = LocalDateTime.now(); + + @Column(name = "related_id", length = 50) + private String relatedId; +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/RepairerAuth.java b/src/main/java/com/campus/water/entity/RepairerAuth.java new file mode 100644 index 0000000..8ae987b --- /dev/null +++ b/src/main/java/com/campus/water/entity/RepairerAuth.java @@ -0,0 +1,46 @@ +/** + * 维修人员认证信息实体类 + * 对应表:repairer_auth + * 用于存储维修人员的登录账号、密码、状态等信息 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "repairer_auth") +public class RepairerAuth { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "auth_id") + private Long authId; + + @Column(name = "repairman_id", length = 50) + private String repairmanId; + + @Column(name = "username", length = 50) + private String username; + + @Column(name = "password", length = 200) + private String password; + + @Enumerated(EnumType.STRING) + @Column(name = "account_status", length = 50) + private AccountStatus accountStatus = AccountStatus.active; + + @Column(name = "last_login") + private LocalDateTime lastLogin; + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + @Column(name = "updated_time") + private LocalDateTime updatedTime = LocalDateTime.now(); + + public enum AccountStatus { + active, inactive, locked + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/Repairman.java b/src/main/java/com/campus/water/entity/Repairman.java new file mode 100644 index 0000000..fc08920 --- /dev/null +++ b/src/main/java/com/campus/water/entity/Repairman.java @@ -0,0 +1,50 @@ +/** + * 维修人员信息实体类 + * 对应表:repairman + * 用于存储维修人员信息,包括技能、状态、工作量、评分等 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "repairman") +public class Repairman { + @Id + @Column(name = "repairman_id", length = 50) + private String repairmanId; + + @Column(name = "repairman_name", length = 100) + private String repairmanName; + + @Column(length = 20) + private String phone; + + @Column(name = "area_id", length = 20) + private String areaId; + + @Column(name = "skills", length = 200) + private String skills; + + @Enumerated(EnumType.STRING) + @Column(name = "status", length = 50) + private RepairmanStatus status = RepairmanStatus.idle; + + @Column(name = "work_count") + private Integer workCount = 0; + + @Column(name = "rating", precision = 3, scale = 2) + private BigDecimal rating ; + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + public enum RepairmanStatus { + idle, busy, vacation + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/TerminalUsageStats.java b/src/main/java/com/campus/water/entity/TerminalUsageStats.java new file mode 100644 index 0000000..d5812e9 --- /dev/null +++ b/src/main/java/com/campus/water/entity/TerminalUsageStats.java @@ -0,0 +1,47 @@ +/** + * 终端使用统计实体类 + * 对应表:terminal_usage_stats + * 用于记录终端设备的每日使用情况统计,如用水量、使用次数等 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "terminal_usage_stats") +public class TerminalUsageStats { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "stat_id") + private Long statId; + + @Column(name = "terminal_id", length = 20) + private String terminalId; + + @Column(name = "stat_date") + private LocalDate statDate; + + @Column(name = "usage_count") + private Integer usageCount = 0; + + @Column(name = "total_water_output", precision = 10, scale = 2) + private BigDecimal totalWaterOutput ; + + @Column(name = "avg_water_per_use", precision = 6, scale = 2) + private BigDecimal avgWaterPerUse ; + + @Column(name = "peak_hour", length = 5) + private String peakHour; + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + @Column(name = "updated_time") + private LocalDateTime updatedTime = LocalDateTime.now(); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/User.java b/src/main/java/com/campus/water/entity/User.java new file mode 100644 index 0000000..3f1cf90 --- /dev/null +++ b/src/main/java/com/campus/water/entity/User.java @@ -0,0 +1,45 @@ +/** + * 用户信息实体类(学生用户) + * 对应表:user + * 用于存储学生用户的基本信息、登录状态等 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "user") +public class User { + @Id + @Column(name = "student_id", length = 50) + private String studentId; + + @Column(name = "student_name", length = 50) + private String studentName; + + @Column(name = "password", length = 200) + private String password; + + @Column(name = "phone", length = 20) + private String phone; + + @Column(name = "email", length = 100) + private String email; + + @Enumerated(EnumType.STRING) + @Column(name = "status", length = 50) + private UserStatus status = UserStatus.active; + + @Column(name = "create_time") + private LocalDateTime createTime = LocalDateTime.now(); + + @Column(name = "updated_time") + private LocalDateTime updatedTime = LocalDateTime.now(); + + public enum UserStatus { + active, inactive + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/WaterMakerRealtimeData.java b/src/main/java/com/campus/water/entity/WaterMakerRealtimeData.java new file mode 100644 index 0000000..96c0e9e --- /dev/null +++ b/src/main/java/com/campus/water/entity/WaterMakerRealtimeData.java @@ -0,0 +1,69 @@ +/** + * 实时制水数据实体类 + * 对应表:water_maker_realtime_data + * 用于存储制水设备的实时运行数据,如TDS、流量、压力、滤芯寿命等 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "water_maker_realtime_data") +public class WaterMakerRealtimeData { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "device_id", length = 20) + private String deviceId; + + // 根据文档修正:三个TDS值 + @Column(name = "tds_value1", precision = 8, scale = 2) + private BigDecimal tdsValue1; // 原水TDS + + @Column(name = "tds_value2", precision = 8, scale = 2) + private BigDecimal tdsValue2; // 纯水TDS + + @Column(name = "tds_value3", precision = 8, scale = 2) + private BigDecimal tdsValue3; // 矿化水TDS + + // 根据文档修正:两个流量计 + @Column(name = "water_flow1", precision = 8, scale = 2) + private BigDecimal waterFlow1; + + @Column(name = "water_flow2", precision = 8, scale = 2) + private BigDecimal waterFlow2; + + // 根据文档修正:字段名改为 water_press + @Column(name = "water_press", precision = 8, scale = 2) + private BigDecimal waterPress; + + @Column(name = "filter_life") + private Integer filterLife; + + @Column(name = "leakage") + private Boolean leakage = false; + + @Column(name = "water_quality", length = 50) + private String waterQuality; + + @Enumerated(EnumType.STRING) + @Column(name = "status", length = 50) + private DeviceStatus status = DeviceStatus.normal; + + // 根据文档修正:字段名改为 record_time + @Column(name = "record_time") + private LocalDateTime recordTime = LocalDateTime.now(); + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + public enum DeviceStatus { + normal, warning, error + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/WaterQualityHistory.java b/src/main/java/com/campus/water/entity/WaterQualityHistory.java new file mode 100644 index 0000000..d106a57 --- /dev/null +++ b/src/main/java/com/campus/water/entity/WaterQualityHistory.java @@ -0,0 +1,48 @@ +/** + * 水质历史数据实体类 + * 对应表:water_quality_history + * 用于记录终端设备检测的水质历史数据,包括多个TDS值和水质评级 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "water_quality_history") +public class WaterQualityHistory { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "history_id") + private Long historyId; + + @Column(name = "terminal_id", length = 20) + private String terminalId; + + @Column(name = "device_id", length = 20) + private String deviceId; + + // 根据文档修正:三个TDS值 + @Column(name = "tds_value1", precision = 8, scale = 2) + private BigDecimal tdsValue1; // 原水TDS + + @Column(name = "tds_value2", precision = 8, scale = 2) + private BigDecimal tdsValue2; // 纯水TDS + + @Column(name = "tds_value3", precision = 8, scale = 2) + private BigDecimal tdsValue3; // 矿化水TDS + + @Column(name = "water_quality", length = 50) + private String waterQuality; + + // 根据文档修正:字段名改为 detected_time + @Column(name = "detected_time") + private LocalDateTime detectedTime = LocalDateTime.now(); + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/WaterSupplyRealtimeData.java b/src/main/java/com/campus/water/entity/WaterSupplyRealtimeData.java new file mode 100644 index 0000000..a4c735e --- /dev/null +++ b/src/main/java/com/campus/water/entity/WaterSupplyRealtimeData.java @@ -0,0 +1,51 @@ +/** + * 实时供水数据实体类 + * 对应表:water_supply_realtime_data + * 用于存储供水设备的实时运行数据,如流量、压力、水位、温度等 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "water_supply_realtime_data") +public class WaterSupplyRealtimeData { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "device_id", length = 20) + private String deviceId; + + @Column(name = "water_flow", precision = 8, scale = 2) + private BigDecimal waterFlow; + + // 根据文档修正:字段名改为 water_press + @Column(name = "water_press", precision = 8, scale = 2) + private BigDecimal waterPress; + + @Column(name = "water_level", precision = 8, scale = 2) + private BigDecimal waterLevel; + + @Column(name = "temperature", precision = 5, scale = 2) + private BigDecimal temperature; + + @Enumerated(EnumType.STRING) + @Column(name = "status", length = 20) + private DeviceStatus status = DeviceStatus.normal; + + @Column(name = "timestamp") + private LocalDateTime timestamp = LocalDateTime.now(); + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + public enum DeviceStatus { + normal, warning, error + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/WorkOrder.java b/src/main/java/com/campus/water/entity/WorkOrder.java new file mode 100644 index 0000000..efc2c90 --- /dev/null +++ b/src/main/java/com/campus/water/entity/WorkOrder.java @@ -0,0 +1,86 @@ +/** + * 工单实体类 + * 对应表:work_order + * 用于管理维修、保养、巡检等工单信息,包括状态、优先级、处理记录等 + */ +package com.campus.water.entity; + +import lombok.Data; +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "work_order") +public class WorkOrder { + @Id + @Column(name = "order_id", length = 30) + private String orderId; + + @Column(name = "alert_id") + private Long alertId; + + @Column(name = "device_id", length = 20) + private String deviceId; + + @Column(name = "area_id", length = 20) + private String areaId; + + @Enumerated(EnumType.STRING) + @Column(name = "order_type", length = 50) + private OrderType orderType; + + @Column(name = "description", columnDefinition = "TEXT") + private String description; + + @Enumerated(EnumType.STRING) + @Column(name = "priority", length = 50) + private OrderPriority priority; + + @Enumerated(EnumType.STRING) + @Column(name = "status", length = 50) + private OrderStatus status = OrderStatus.pending; + + @Column(name = "assigned_repairman_id", length = 50) + private String assignedRepairmanId; + + @Column(name = "created_time") + private LocalDateTime createdTime = LocalDateTime.now(); + + @Column(name = "grabbed_time") + private LocalDateTime grabbedTime; + + @Column(name = "deadline") + private LocalDateTime deadline; + + @Column(name = "completed_time") + private LocalDateTime completedTime; + + @Column(name = "deal_note", columnDefinition = "TEXT") + private String dealNote; + + @Column(name = "img_url", length = 500) + private String imgUrl; + + @Column(name = "created_by", length = 50) + private String createdBy; + + @Column(name = "updated_time") + private LocalDateTime updatedTime = LocalDateTime.now(); + + public enum OrderType { + repair, maintenance, inspection + } + + public enum OrderPriority { + low, medium, high, urgent + } + + public enum OrderStatus { + pending, // 原pending(待抢单)调整为待处理 + processing, // 合并原grabbed(已抢单)和processing(处理中) + reviewing, // 新增状态:维修完成后等待审核 + completed, // 原completed(已完成)保留 + timeout; // 原timeout(超时)保留 + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/dto/request/DeviceStatusUpdateRequest.java b/src/main/java/com/campus/water/entity/dto/request/DeviceStatusUpdateRequest.java new file mode 100644 index 0000000..37921ac --- /dev/null +++ b/src/main/java/com/campus/water/entity/dto/request/DeviceStatusUpdateRequest.java @@ -0,0 +1,18 @@ +// 路径:com/campus/water/entity/dto/request/DeviceStatusUpdateRequest.java +package com.campus.water.entity.dto.request; + +import com.campus.water.entity.Device; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +@Data +public class DeviceStatusUpdateRequest { + @NotBlank(message = "设备ID不能为空") + private String deviceId; + + private Device.DeviceStatus status; + + private String remark; // 状态变更备注 + private String faultType; // 故障类型(状态为fault时必填) + private String faultDescription; // 故障描述(状态为fault时必填) +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/dto/request/LoginRequest.java b/src/main/java/com/campus/water/entity/dto/request/LoginRequest.java new file mode 100644 index 0000000..4e56ed3 --- /dev/null +++ b/src/main/java/com/campus/water/entity/dto/request/LoginRequest.java @@ -0,0 +1,17 @@ +// LoginRequest.java(原LoginDTO,按项目规范重命名) +package com.campus.water.entity.dto.request; + +import lombok.Data; +import jakarta.validation.constraints.NotBlank; + +@Data +public class LoginRequest { // 命名改为Request,符合dto/request分类 + @NotBlank(message = "用户名不能为空") + private String username; + + @NotBlank(message = "密码不能为空") + private String password; + + @NotBlank(message = "用户类型不能为空") + private String userType; // admin/repairer/user +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/dto/request/RegisterRequest.java b/src/main/java/com/campus/water/entity/dto/request/RegisterRequest.java new file mode 100644 index 0000000..5231f41 --- /dev/null +++ b/src/main/java/com/campus/water/entity/dto/request/RegisterRequest.java @@ -0,0 +1,43 @@ +package com.campus.water.entity.dto.request; + +import lombok.Data; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * 注册请求DTO,接收前端传递的注册参数 + */ +@Data +public class RegisterRequest { + // 通用字段 + @NotBlank(message = "用户名不能为空") + private String username; + + @NotBlank(message = "密码不能为空") + private String password; + + @NotBlank(message = "用户类型不能为空") + private String userType; // admin/user/repairman + + private String phone; + + // 维修人员特有字段 + private String repairmanId; // 维修人员ID + + @NotBlank(message = "维修人员姓名不能为空") + private String repairmanName; // 维修人员姓名 + + @NotBlank(message = "负责区域ID不能为空") + private String areaId; // 负责区域ID + + @NotBlank(message = "技能描述不能为空") + private String skills; // 技能描述 + + // 管理员特有字段 + private String adminId; + private String role; + + // 用户(学生)特有字段 + private String studentId; + private String studentName; +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/dto/request/StatisticsQueryRequest.java b/src/main/java/com/campus/water/entity/dto/request/StatisticsQueryRequest.java new file mode 100644 index 0000000..ff6c558 --- /dev/null +++ b/src/main/java/com/campus/water/entity/dto/request/StatisticsQueryRequest.java @@ -0,0 +1,17 @@ +// com/campus/water/entity/dto/request/StatisticsQueryRequest.java +package com.campus.water.entity.dto.request; + +import lombok.Data; +import java.time.LocalDate; + +@Data // Lombok注解自动生成getter/setter +public class StatisticsQueryRequest { + private String statType; // 统计类型(如by_device、by_area等) + private LocalDate startDate; // 开始日期 + private LocalDate endDate; // 结束日期 + private String areaId; // 区域ID + private String deviceType; // 设备类型 + private String terminalId; // 补充终端ID字段(解决getTerminalId()错误) + private Integer limit; // 限制数量 + // 若有其他字段可继续补充 +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/vo/AlarmStatisticsVO.java b/src/main/java/com/campus/water/entity/vo/AlarmStatisticsVO.java new file mode 100644 index 0000000..95e0606 --- /dev/null +++ b/src/main/java/com/campus/water/entity/vo/AlarmStatisticsVO.java @@ -0,0 +1,13 @@ +// com/campus/water/entity/vo/AlarmStatisticsVO.java +package com.campus.water.entity.vo; + +import lombok.Data; +import java.util.Map; + +@Data +public class AlarmStatisticsVO { + private Map levelCount; // 告警级别统计(解决setLevelCount()错误) + private Map statusCount; // 告警状态统计(解决setStatusCount()错误) + private double handleRate; // 处理率(解决setHandleRate()错误) + private long totalAlarms; // 总告警数 +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/vo/LoginVO.java b/src/main/java/com/campus/water/entity/vo/LoginVO.java new file mode 100644 index 0000000..782df00 --- /dev/null +++ b/src/main/java/com/campus/water/entity/vo/LoginVO.java @@ -0,0 +1,14 @@ +// LoginVO.java(保持VO命名,无需修改) +package com.campus.water.entity.vo; + +import lombok.Data; + +@Data +public class LoginVO { + private String token; // 登录令牌 + private String userId; // 用户ID + private String username; // 用户名 + private String userType; // 用户类型 + private String areaId; // 新增:维修人员所属区域ID + +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/vo/StatisticsVO.java b/src/main/java/com/campus/water/entity/vo/StatisticsVO.java new file mode 100644 index 0000000..09a424b --- /dev/null +++ b/src/main/java/com/campus/water/entity/vo/StatisticsVO.java @@ -0,0 +1,18 @@ +// com/campus/water/entity/vo/StatisticsVO.java +package com.campus.water.entity.vo; + +import lombok.Data; +import java.util.List; +import java.util.Map; + +@Data // Lombok注解自动生成getter/setter +public class StatisticsVO { + private String type; // 统计类型(解决setType()错误) + private String areaId; // 区域ID(解决setAreaId()错误) + private List dates; // 日期列表(解决setDates()错误) + private List waterUsage; // 用水量列表(解决setWaterUsage()错误) + private double totalUsage; // 总用水量(解决setTotalUsage()错误) + private double avgDailyUsage; // 日均用水量(解决setAvgDailyUsage()错误) + private List> deviceStats; // 设备统计详情 + // 其他需要的字段 +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/entity/先读我.md b/src/main/java/com/campus/water/entity/先读我.md new file mode 100644 index 0000000..3b52c1f --- /dev/null +++ b/src/main/java/com/campus/water/entity/先读我.md @@ -0,0 +1 @@ +# 本md仅用于初始化目录,未创建所有子一级目录,在当前目录创建文件后请自行删除 \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/AdminRepository.java b/src/main/java/com/campus/water/mapper/AdminRepository.java new file mode 100644 index 0000000..0e3374e --- /dev/null +++ b/src/main/java/com/campus/water/mapper/AdminRepository.java @@ -0,0 +1,34 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.Admin; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface AdminRepository extends JpaRepository { + // 登录核心方法:按用户名查询 + Optional findByAdminName(String adminName); + + // 按管理员ID查询 + Optional findByAdminId(String adminId); + + // 按姓名模糊查询 + List findByAdminNameContaining(String adminName); + + // 按手机号查询 + Optional findByPhone(String phone); + + // 按角色查询管理员(核心:恢复角色筛选) + List findByRole(Admin.AdminRole role); + + // 按姓名+角色组合查询(可选,增强筛选) + List findByAdminNameContainingAndRole(String name, Admin.AdminRole role); + + // 检查唯一约束 + boolean existsByAdminId(String adminId); + boolean existsByPhone(String phone); + boolean existsByAdminName(String adminName); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/AlertRepository.java b/src/main/java/com/campus/water/mapper/AlertRepository.java new file mode 100644 index 0000000..859f25d --- /dev/null +++ b/src/main/java/com/campus/water/mapper/AlertRepository.java @@ -0,0 +1,61 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.Alert; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public interface AlertRepository extends JpaRepository { + // 根据设备ID和时间范围查询告警 + List findByDeviceIdAndTimestampBetween(String deviceId, LocalDateTime startTime, LocalDateTime endTime); + + // 根据告警级别和时间范围查询告警 + List findByAlertLevelAndTimestampBetween(Alert.AlertLevel level, LocalDateTime startTime, LocalDateTime endTime); + + // 根据告警状态和时间范围查询告警 + List findByStatusAndTimestampBetween(Alert.AlertStatus status, LocalDateTime startTime, LocalDateTime endTime); + + // 根据区域ID和时间范围查询告警 + List findByAreaIdAndTimestampBetween(String areaId, LocalDateTime startTime, LocalDateTime endTime); + + // 根据时间范围查询告警 + List findByTimestampBetween(LocalDateTime startTime, LocalDateTime endTime); + + // 根据区域ID和告警状态查询告警 + List findByAreaIdAndStatus(String areaId, Alert.AlertStatus status); + + // 根据告警状态查询告警 + List findByStatus(Alert.AlertStatus status); + + // 保留原有的其他查询方法(如果有) + // 根据设备ID查询告警 + List findByDeviceId(String deviceId); + + // 根据告警类型查询 + List findByAlertType(String alertType); + + // 根据告警级别查询 + List findByAlertLevel(Alert.AlertLevel alertLevel); + + // 根据区域ID查询告警 + List findByAreaId(String areaId); + + // 按状态和级别查询告警 + List findByStatusAndAlertLevel(Alert.AlertStatus status, Alert.AlertLevel level); + + // 查询指定时间后的告警 + List findByTimestampAfter(LocalDateTime timestamp); + + // 根据处理人查询告警 + List findByResolvedBy(String resolvedBy); + + // 修复:将 AndStatus 改为 AndStatusIn,支持List集合的IN查询 + List findByDeviceIdAndAlertTypeAndStatusInAndTimestampAfter( + String deviceId, + String alertType, + List activeStatus, + LocalDateTime timestamp + ); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/AreaRepository.java b/src/main/java/com/campus/water/mapper/AreaRepository.java new file mode 100644 index 0000000..3638d5b --- /dev/null +++ b/src/main/java/com/campus/water/mapper/AreaRepository.java @@ -0,0 +1,26 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.Area; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import java.util.List; + +@Repository +public interface AreaRepository extends JpaRepository { + // 根据区域类型查询 + List findByAreaType(Area.AreaType areaType); + + // 根据父区域ID查询子区域 + List findByParentAreaId(String parentAreaId); + + // 根据管理员姓名查询区域 + List findByManager(String manager); + + // 根据管理员手机号查询区域 + List findByManagerPhone(String managerPhone); + + // 查询指定类型的根级区域 + @Query("SELECT a FROM Area a WHERE a.areaType = ?1 AND a.parentAreaId IS NULL") + List findRootAreasByType(Area.AreaType areaType); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/DeviceRepository.java b/src/main/java/com/campus/water/mapper/DeviceRepository.java new file mode 100644 index 0000000..dd3e7aa --- /dev/null +++ b/src/main/java/com/campus/water/mapper/DeviceRepository.java @@ -0,0 +1,47 @@ +// com/campus/water/mapper/DeviceRepository.java +package com.campus.water.mapper; + +import com.campus.water.entity.Device; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.List; + +@Repository +public interface DeviceRepository extends JpaRepository { + // 根据区域ID查询设备 + List findByAreaId(String areaId); + + // 根据设备类型(枚举)查询 + List findByDeviceType(Device.DeviceType deviceType); + + // 根据设备状态(枚举)查询 + List findByStatus(Device.DeviceStatus status); + + // 按区域和设备类型查询 + List findByAreaIdAndDeviceType(String areaId, Device.DeviceType deviceType); + + // 按安装位置模糊查询 + List findByInstallLocationContaining(String location); + + // 关键修正:参数类型为枚举(原DevicePO用String,现统一为Device的枚举) + List findByStatusAndAreaIdAndDeviceType( + Device.DeviceStatus status, + String areaId, + Device.DeviceType deviceType + ); + + // 统计方法参数修正为枚举 + long countByStatusAndAreaIdAndDeviceType( + Device.DeviceStatus status, + String areaId, + Device.DeviceType deviceType + ); + + List findByAreaIdAndStatus(String areaId, Device.DeviceStatus deviceStatus); + + // 根据制水机ID查询关联的供水机 + List findByParentMakerIdAndDeviceType(String parentMakerId, Device.DeviceType deviceType); + + // 按状态和区域查询(无设备类型筛选) + List findByStatusAndAreaId(Device.DeviceStatus status, String areaId); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/DeviceTerminalMappingRepository.java b/src/main/java/com/campus/water/mapper/DeviceTerminalMappingRepository.java new file mode 100644 index 0000000..de016ce --- /dev/null +++ b/src/main/java/com/campus/water/mapper/DeviceTerminalMappingRepository.java @@ -0,0 +1,22 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.DeviceTerminalMapping; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + +@Repository +public interface DeviceTerminalMappingRepository extends JpaRepository { + // 根据终端ID查找映射关系 + Optional findByTerminalId(String terminalId); + + // 根据设备ID查找所有关联终端 + List findByDeviceId(String deviceId); + + // 根据终端状态查找映射关系 + List findByTerminalStatus(DeviceTerminalMapping.TerminalStatus status); + + // 根据设备和终端ID精确查找映射 + Optional findByDeviceIdAndTerminalId(String deviceId, String terminalId); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/DrinkRecommendationRepository.java b/src/main/java/com/campus/water/mapper/DrinkRecommendationRepository.java new file mode 100644 index 0000000..0841a4a --- /dev/null +++ b/src/main/java/com/campus/water/mapper/DrinkRecommendationRepository.java @@ -0,0 +1,37 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.DrinkRecommendation; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +@Repository +public interface DrinkRecommendationRepository extends JpaRepository { + // 根据学生ID查询饮水推荐 + List findByStudentId(String studentId); + + // 根据推荐日期查询 + List findByRecommendationDate(LocalDate recommendationDate); + + // 根据日期范围查询饮水推荐 + List findByRecommendationDateBetween(LocalDate start, LocalDate end); + + // 按学生和日期查找推荐记录 + @Query("SELECT d FROM DrinkRecommendation d WHERE d.studentId = ?1 AND d.recommendationDate = ?2") + Optional findByStudentIdAndRecommendationDate(String studentId, LocalDate date); + + // 查询学生时间段内的饮水推荐记录 + @Query("SELECT d FROM DrinkRecommendation d WHERE d.studentId = ?1 AND d.recommendationDate BETWEEN ?2 AND ?3 ORDER BY d.recommendationDate DESC") + List findByStudentIdAndRecommendationDateBetween(String studentId, LocalDate start, LocalDate end); + + // 统计学生某日总饮水进度 + @Query("SELECT SUM(d.currentProgress) FROM DrinkRecommendation d WHERE d.studentId = ?1 AND d.recommendationDate = ?2") + Double getTotalProgressByStudentIdAndDate(String studentId, LocalDate date); + + // 计算学生期间平均目标 + @Query("SELECT AVG(d.dailyTarget) FROM DrinkRecommendation d WHERE d.studentId = ?1 AND d.recommendationDate BETWEEN ?2 AND ?3") + Double getAverageDailyTargetByStudentIdAndPeriod(String studentId, LocalDate start, LocalDate end); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/DrinkRecordRepository.java b/src/main/java/com/campus/water/mapper/DrinkRecordRepository.java new file mode 100644 index 0000000..23b8364 --- /dev/null +++ b/src/main/java/com/campus/water/mapper/DrinkRecordRepository.java @@ -0,0 +1,45 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.DrinkRecord; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public interface DrinkRecordRepository extends JpaRepository { + // 根据学生ID查询饮水记录 + List findByStudentId(String studentId); + + // 根据终端ID查询饮水记录 + List findByTerminalId(String terminalId); + + // 根据设备ID查询饮水记录 + List findByDeviceId(String deviceId); + + // 查询学生某日的饮水记录 + @Query("SELECT d FROM DrinkRecord d WHERE d.studentId = ?1 AND DATE(d.drinkTime) = ?2") + List findByStudentIdAndDrinkDate(String studentId, LocalDate drinkDate); + + // 查询学生时间段内的饮水记录 + @Query("SELECT d FROM DrinkRecord d WHERE d.studentId = ?1 AND d.drinkTime BETWEEN ?2 AND ?3") + List findByStudentIdAndDrinkTimeBetween(String studentId, LocalDateTime start, LocalDateTime end); + + // 统计学生单日总饮水量 + @Query("SELECT SUM(d.waterConsumption) FROM DrinkRecord d WHERE d.studentId = ?1 AND DATE(d.drinkTime) = ?2") + Double getTotalWaterConsumptionByStudentIdAndDate(String studentId, LocalDate date); + + // 查询学生时间段内的饮水记录(按时间倒序) + @Query("SELECT d FROM DrinkRecord d WHERE d.studentId = ?1 AND d.drinkTime BETWEEN ?2 AND ?3 ORDER BY d.drinkTime DESC") + List findByStudentIdAndDrinkTimeBetweenOrdered(String studentId, LocalDateTime start, LocalDateTime end); + + // 查询终端时间段内的饮水记录 + @Query("SELECT d FROM DrinkRecord d WHERE d.terminalId = ?1 AND d.drinkTime BETWEEN ?2 AND ?3") + List findByTerminalIdAndDrinkTimeBetween(String terminalId, LocalDateTime start, LocalDateTime end); + + // 统计终端单日使用次数 + @Query("SELECT COUNT(d) FROM DrinkRecord d WHERE d.terminalId = ?1 AND DATE(d.drinkTime) = ?2") + Long countByTerminalIdAndDrinkDate(String terminalId, LocalDate drinkDate); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/InspectionRecordRepository.java b/src/main/java/com/campus/water/mapper/InspectionRecordRepository.java new file mode 100644 index 0000000..5959b2b --- /dev/null +++ b/src/main/java/com/campus/water/mapper/InspectionRecordRepository.java @@ -0,0 +1,31 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.InspectionRecord; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public interface InspectionRecordRepository extends JpaRepository { + // 根据工单ID查询巡检记录 + List findByOrderId(String orderId); + + // 根据维修人员ID查询巡检记录 + List findByRepairmanId(String repairmanId); + + // 根据设备ID查询巡检记录 + List findByDeviceId(String deviceId); + + // 根据巡检状态查询记录 + List findByInspectionStatus(InspectionRecord.InspectionStatus status); + + // 根据巡检时间范围查询记录 + List findByInspectionTimeBetween(LocalDateTime start, LocalDateTime end); + + // 查询维修人员期间巡检记录 + List findByRepairmanIdAndInspectionTimeBetween(String repairmanId, LocalDateTime start, LocalDateTime end); + + // 根据提交时间范围查询记录 + List findBySubmittedAtBetween(LocalDateTime start, LocalDateTime end); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/MaintenancePlanRepository.java b/src/main/java/com/campus/water/mapper/MaintenancePlanRepository.java new file mode 100644 index 0000000..485df05 --- /dev/null +++ b/src/main/java/com/campus/water/mapper/MaintenancePlanRepository.java @@ -0,0 +1,33 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.MaintenancePlan; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import java.time.LocalDate; +import java.util.List; + +@Repository +public interface MaintenancePlanRepository extends JpaRepository { + // 根据设备ID查询维护计划 + List findByDeviceId(String deviceId); + + // 根据计划状态查询维护计划 + List findByPlanStatus(MaintenancePlan.PlanStatus planStatus); + + // 根据维护类型查询维护计划 + List findByMaintenanceType(String maintenanceType); + + // 查询指定日期前的维护计划 + List findByNextMaintenanceDateBefore(LocalDate date); + + // 根据下次维护日期范围查询维护计划 + List findByNextMaintenanceDateBetween(LocalDate start, LocalDate end); + + // 查询设备特定状态的维护计划 + List findByDeviceIdAndPlanStatus(String deviceId, MaintenancePlan.PlanStatus planStatus); + + // 查询到期需要执行的维护计划 + @Query("SELECT mp FROM MaintenancePlan mp WHERE mp.nextMaintenanceDate <= ?1 AND mp.planStatus = 'active'") + List findDueMaintenancePlans(LocalDate date); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/MessagePushRepository.java b/src/main/java/com/campus/water/mapper/MessagePushRepository.java new file mode 100644 index 0000000..9ed6717 --- /dev/null +++ b/src/main/java/com/campus/water/mapper/MessagePushRepository.java @@ -0,0 +1,45 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.MessagePush; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public interface MessagePushRepository extends JpaRepository { + // 根据学生ID查询消息 + List findByStudentId(String studentId); + + // 根据管理员ID查询消息 + List findByAdminId(String adminId); + + // 根据维修人员ID查询消息 + List findByRepairmanId(String repairmanId); + + // 根据用户类型查询消息 + List findByUserType(String userType); + + // 根据消息类型查询 + List findByMessageType(String messageType); + + // 根据阅读状态查询消息 + List findByIsRead(Boolean isRead); + + // 按用户ID和类型查询消息 + @Query("SELECT m FROM MessagePush m WHERE m.userId = ?1 AND m.userType = ?2") + List findByUserIdAndUserType(String userId, String userType); + + // 按推送时间范围查询消息 + @Query("SELECT m FROM MessagePush m WHERE m.pushTime BETWEEN ?1 AND ?2") + List findByPushTimeBetween(LocalDateTime start, LocalDateTime end); + + // 根据相关ID查询消息 + @Query("SELECT m FROM MessagePush m WHERE m.relatedId = ?1") + List findByRelatedId(String relatedId); + + // 统计用户未读消息数量 + @Query("SELECT COUNT(m) FROM MessagePush m WHERE m.userId = ?1 AND m.isRead = false") + Long countUnreadMessagesByUserId(String userId); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/RepairerAuthRepository.java b/src/main/java/com/campus/water/mapper/RepairerAuthRepository.java new file mode 100644 index 0000000..a0b97e1 --- /dev/null +++ b/src/main/java/com/campus/water/mapper/RepairerAuthRepository.java @@ -0,0 +1,31 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.RepairerAuth; // 改为entity包下的RepairerAuth +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface RepairerAuthRepository extends JpaRepository { // 实体类改为RepairerAuth + // 根据用户名查询认证信息(适配RepairerAuth类) + Optional findByUsername(String username); + + // 根据维修人员ID查询认证信息(适配RepairerAuth类) + Optional findByRepairmanId(String repairmanId); + + // 根据账户状态查询认证信息(引用RepairerAuth内的枚举) + List findByAccountStatus(RepairerAuth.AccountStatus accountStatus); + + // 查找活跃状态的维修人员账号(JPQL实体类名改为RepairerAuth) + @Query("SELECT ra FROM RepairerAuth ra WHERE ra.username = ?1 AND ra.accountStatus = 'active'") + Optional findActiveByUsername(String username); + + // 检查用户名是否存在 + boolean existsByUsername(String username); + + // 检查维修工ID是否存在 + boolean existsByRepairmanId(String repairmanId); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/RepairmanRepository.java b/src/main/java/com/campus/water/mapper/RepairmanRepository.java new file mode 100644 index 0000000..a0ceacc --- /dev/null +++ b/src/main/java/com/campus/water/mapper/RepairmanRepository.java @@ -0,0 +1,30 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.Repairman; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.List; + +@Repository +public interface RepairmanRepository extends JpaRepository { + // 根据区域ID查询维修人员 + List findByAreaId(String areaId); + + // 根据状态查询维修人员 + List findByStatus(Repairman.RepairmanStatus status); + + // 按技能关键词查询维修人员 + List findBySkillsContaining(String skill); + + // 按区域和状态查询维修人员 + List findByAreaIdAndStatus(String areaId, Repairman.RepairmanStatus status); + + // 查询评分高于阈值的维修人员 + List findByRatingGreaterThanEqual(Double minRating); + + // 新增姓名相关查询方法 + List findByRepairmanNameContaining(String name); + List findByRepairmanNameContainingAndAreaId(String name, String areaId); + List findByRepairmanNameContainingAndStatus(String name, Repairman.RepairmanStatus status); + List findByRepairmanNameContainingAndAreaIdAndStatus(String name, String areaId, Repairman.RepairmanStatus status); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/StatisticsRepository.java b/src/main/java/com/campus/water/mapper/StatisticsRepository.java new file mode 100644 index 0000000..17f7f58 --- /dev/null +++ b/src/main/java/com/campus/water/mapper/StatisticsRepository.java @@ -0,0 +1,50 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.DrinkRecord; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import java.time.LocalDate; +import java.util.List; + +/** + * 用水量统计数据访问接口 + * 提供基于JPA的数据查询方法,用于按设备、区域、时间统计用水量 + */ +@Repository +public interface StatisticsRepository extends JpaRepository { + + /** + * 按设备统计指定时间范围内的用水量 + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 包含设备ID和对应总用水量的数组列表 + */ + @Query("SELECT d.deviceId, SUM(d.waterConsumption) FROM DrinkRecord d " + + "WHERE d.drinkTime BETWEEN ?1 AND ?2 " + + "GROUP BY d.deviceId ORDER BY SUM(d.waterConsumption) DESC") + List getWaterUsageByDevice(LocalDate startDate, LocalDate endDate); + + /** + * 按区域统计指定时间范围内的用水量 + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 包含区域ID和对应总用水量的数组列表 + */ + @Query("SELECT d.areaId, SUM(r.waterConsumption) FROM Device d " + + "JOIN DrinkRecord r ON d.deviceId = r.deviceId " + + "WHERE r.drinkTime BETWEEN ?1 AND ?2 " + + "GROUP BY d.areaId") + List getWaterUsageByArea(LocalDate startDate, LocalDate endDate); + + /** + * 按日期统计指定时间范围内的用水量 + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 包含日期和对应总用水量的数组列表 + */ + @Query("SELECT DATE(r.drinkTime), SUM(r.waterConsumption) FROM DrinkRecord r " + + "WHERE r.drinkTime BETWEEN ?1 AND ?2 " + + "GROUP BY DATE(r.drinkTime) ORDER BY DATE(r.drinkTime)") + List getDailyWaterUsage(LocalDate startDate, LocalDate endDate); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/TerminalUsageStatsRepository.java b/src/main/java/com/campus/water/mapper/TerminalUsageStatsRepository.java new file mode 100644 index 0000000..c44a8dc --- /dev/null +++ b/src/main/java/com/campus/water/mapper/TerminalUsageStatsRepository.java @@ -0,0 +1,37 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.TerminalUsageStats; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +@Repository +public interface TerminalUsageStatsRepository extends JpaRepository { + // 根据终端ID查找使用统计 + List findByTerminalId(String terminalId); + + // 根据统计日期查找使用统计 + List findByStatDate(LocalDate statDate); + + // 根据日期范围查找使用统计 + List findByStatDateBetween(LocalDate start, LocalDate end); + + // 根据终端ID和日期范围查询统计数据 + @Query("SELECT t FROM TerminalUsageStats t WHERE t.terminalId = ?1 AND t.statDate BETWEEN ?2 AND ?3") + List findByTerminalIdAndStatDateBetween(String terminalId, LocalDate start, LocalDate end); + + // 查询使用次数超过阈值的终端 + @Query("SELECT t FROM TerminalUsageStats t WHERE t.usageCount > ?1") + List findByUsageCountGreaterThan(Integer usageCount); + + // 查询总出水量超过阈值的终端 + @Query("SELECT t FROM TerminalUsageStats t WHERE t.totalWaterOutput > ?1") + List findByTotalWaterOutputGreaterThan(Double totalWaterOutput); + + // 按终端和日期查找统计记录 + @Query("SELECT t FROM TerminalUsageStats t WHERE t.terminalId = ?1 AND t.statDate = ?2") + Optional findByTerminalIdAndStatDate(String terminalId, LocalDate statDate); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/UserRepository.java b/src/main/java/com/campus/water/mapper/UserRepository.java new file mode 100644 index 0000000..85bcf39 --- /dev/null +++ b/src/main/java/com/campus/water/mapper/UserRepository.java @@ -0,0 +1,47 @@ +// filePath:main/java/com/campus/water/mapper/UserRepository.java +package com.campus.water.mapper; + +import com.campus.water.entity.User; // 改为引用User实体类 +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface UserRepository extends JpaRepository { // 泛型改为User + // 根据学号查找用户 + Optional findByStudentId(String studentId); + + // 根据学生姓名模糊查询(User中字段为studentName) + List findByStudentNameContaining(String studentName); + + // 根据用户状态查询(引用User内的UserStatus枚举) + List findByStatus(User.UserStatus status); + + // 根据手机号查询用户 + Optional findByPhone(String phone); + + // 根据邮箱查询用户 + Optional findByEmail(String email); + + // 按姓名模糊查询和状态筛选(调整实体类名和字段名) + @Query("SELECT u FROM User u WHERE u.studentName LIKE %?1% AND u.status = ?2") + List findByStudentNameContainingAndStatus(String studentName, User.UserStatus status); + + // 检查学号是否已存在 + boolean existsByStudentId(String studentId); + + // 检查手机号是否已存在 + boolean existsByPhone(String phone); + + // 检查邮箱是否已存在 + boolean existsByEmail(String email); + + // 检查用户名(studentName)是否存在(登录核心方法) + boolean existsByStudentName(String studentName); + + // 登录核心方法:根据用户名(studentName)查询 + Optional findByStudentName(String studentName); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/WaterMakerRealtimeDataRepository.java b/src/main/java/com/campus/water/mapper/WaterMakerRealtimeDataRepository.java new file mode 100644 index 0000000..5e2f02e --- /dev/null +++ b/src/main/java/com/campus/water/mapper/WaterMakerRealtimeDataRepository.java @@ -0,0 +1,57 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.WaterMakerRealtimeData; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Repository +public interface WaterMakerRealtimeDataRepository extends JpaRepository { + // 根据设备ID查询实时数据 + List findByDeviceId(String deviceId); + + // 根据记录时间范围查询数据 + @Query("SELECT w FROM WaterMakerRealtimeData w WHERE w.recordTime BETWEEN ?1 AND ?2 ORDER BY w.recordTime DESC") + List findByRecordTimeBetween(LocalDateTime start, LocalDateTime end); + + // 查询设备时间段内的实时数据 + @Query("SELECT w FROM WaterMakerRealtimeData w WHERE w.deviceId = ?1 AND w.recordTime BETWEEN ?2 AND ?3 ORDER BY w.recordTime DESC") + List findByDeviceIdAndRecordTimeBetween(String deviceId, LocalDateTime start, LocalDateTime end); + + // 获取设备最近10条记录 + @Query(value = "SELECT * FROM water_maker_realtime_data w WHERE w.device_id = ?1 ORDER BY w.record_time DESC LIMIT 10", nativeQuery = true) + List findTop10ByDeviceIdOrderByRecordTimeDesc(String deviceId); + + // 根据泄漏状态查询设备数据 + List findByLeakage(Boolean leakage); + + // 根据设备状态查询数据 + List findByStatus(WaterMakerRealtimeData.DeviceStatus status); + + // 查询滤芯寿命低的设备 + @Query("SELECT w FROM WaterMakerRealtimeData w WHERE w.filterLife < ?1") + List findByFilterLifeLessThan(Integer filterLife); + + // 按TDS阈值查询设备数据(原水TDS) + @Query("SELECT w FROM WaterMakerRealtimeData w WHERE w.tdsValue1 > ?1") + List findByTdsValue1GreaterThan(Double tdsValue); + + // 按TDS阈值查询设备数据(纯水TDS) + @Query("SELECT w FROM WaterMakerRealtimeData w WHERE w.tdsValue2 > ?1") + List findByTdsValue2GreaterThan(Double tdsValue); + + // 按TDS阈值查询设备数据(矿化水TDS) + @Query("SELECT w FROM WaterMakerRealtimeData w WHERE w.tdsValue3 > ?1") + List findByTdsValue3GreaterThan(Double tdsValue); + + // 查询水压低于阈值的设备 + @Query("SELECT w FROM WaterMakerRealtimeData w WHERE w.waterPress < ?1") + List findByWaterPressLessThan(Double waterPress); + + // 获取设备最新运行数据 + @Query(value = "SELECT * FROM water_maker_realtime_data w WHERE w.device_id = ?1 ORDER BY w.record_time DESC LIMIT 1", nativeQuery = true) + Optional findLatestByDeviceId(String deviceId); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/WaterQualityHistoryRepository.java b/src/main/java/com/campus/water/mapper/WaterQualityHistoryRepository.java new file mode 100644 index 0000000..08a7a74 --- /dev/null +++ b/src/main/java/com/campus/water/mapper/WaterQualityHistoryRepository.java @@ -0,0 +1,58 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.WaterQualityHistory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Repository +public interface WaterQualityHistoryRepository extends JpaRepository { + // 根据终端ID查询水质历史 + List findByTerminalId(String terminalId); + + // 根据设备ID查询水质历史 + List findByDeviceId(String deviceId); + + // 查询终端时间段内的水质记录 + @Query("SELECT w FROM WaterQualityHistory w WHERE w.terminalId = ?1 AND w.detectedTime BETWEEN ?2 AND ?3 ORDER BY w.detectedTime DESC") + List findByTerminalIdAndDetectedTimeBetween(String terminalId, LocalDateTime start, LocalDateTime end); + + // 获取终端最近5次检测记录 + @Query(value = "SELECT * FROM water_quality_history w WHERE w.terminal_id = ?1 ORDER BY w.detected_time DESC LIMIT 5", nativeQuery = true) + List findTop5ByTerminalIdOrderByDetectedTimeDesc(String terminalId); + + // 根据水质评级查询 + @Query("SELECT w FROM WaterQualityHistory w WHERE w.waterQuality = ?1") + List findByWaterQuality(String waterQuality); + + // 按TDS阈值查询水质记录(原水TDS) + @Query("SELECT w FROM WaterQualityHistory w WHERE w.tdsValue1 > ?1") + List findByTdsValue1GreaterThan(Double tdsValue); + + // 按TDS阈值查询水质记录(纯水TDS) + @Query("SELECT w FROM WaterQualityHistory w WHERE w.tdsValue2 > ?1") + List findByTdsValue2GreaterThan(Double tdsValue); + + // 按TDS阈值查询水质记录(矿化水TDS) + @Query("SELECT w FROM WaterQualityHistory w WHERE w.tdsValue3 > ?1") + List findByTdsValue3GreaterThan(Double tdsValue); + + // 查询设备时间段内的水质记录 + @Query("SELECT w FROM WaterQualityHistory w WHERE w.deviceId = ?1 AND w.detectedTime BETWEEN ?2 AND ?3 ORDER BY w.detectedTime DESC") + List findByDeviceIdAndDetectedTimeBetween(String deviceId, LocalDateTime start, LocalDateTime end); + + // 获取终端最新水质记录 + @Query(value = "SELECT * FROM water_quality_history w WHERE w.terminal_id = ?1 ORDER BY w.detected_time DESC LIMIT 1", nativeQuery = true) + Optional findLatestByTerminalId(String terminalId); + + // 获取设备最新水质记录 + @Query(value = "SELECT * FROM water_quality_history w WHERE w.device_id = ?1 ORDER BY w.detected_time DESC LIMIT 1", nativeQuery = true) + Optional findLatestByDeviceId(String deviceId); + + // 根据检测时间范围查询水质记录 + @Query("SELECT w FROM WaterQualityHistory w WHERE w.detectedTime BETWEEN ?1 AND ?2 ORDER BY w.detectedTime DESC") + List findByDetectedTimeBetween(LocalDateTime start, LocalDateTime end); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/WaterSupplyRealtimeDataRepository.java b/src/main/java/com/campus/water/mapper/WaterSupplyRealtimeDataRepository.java new file mode 100644 index 0000000..ff5ce8f --- /dev/null +++ b/src/main/java/com/campus/water/mapper/WaterSupplyRealtimeDataRepository.java @@ -0,0 +1,46 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.WaterSupplyRealtimeData; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Repository +public interface WaterSupplyRealtimeDataRepository extends JpaRepository { + // 根据设备ID查询实时数据 + List findByDeviceId(String deviceId); + + // 根据时间戳范围查询数据 + @Query("SELECT w FROM WaterSupplyRealtimeData w WHERE w.timestamp BETWEEN ?1 AND ?2 ORDER BY w.timestamp DESC") + List findByTimestampBetween(LocalDateTime start, LocalDateTime end); + + // 查询设备时间段内的实时数据 + @Query("SELECT w FROM WaterSupplyRealtimeData w WHERE w.deviceId = ?1 AND w.timestamp BETWEEN ?2 AND ?3 ORDER BY w.timestamp DESC") + List findByDeviceIdAndTimestampBetween(String deviceId, LocalDateTime start, LocalDateTime end); + + // 获取设备最近10条记录 + @Query(value = "SELECT * FROM water_supply_realtime_data w WHERE w.device_id = ?1 ORDER BY w.timestamp DESC LIMIT 10", nativeQuery = true) + List findTop10ByDeviceIdOrderByTimestampDesc(String deviceId); + + // 根据设备状态查询数据 + List findByStatus(WaterSupplyRealtimeData.DeviceStatus status); + + // 查询水位低于阈值的设备 + @Query("SELECT w FROM WaterSupplyRealtimeData w WHERE w.waterLevel < ?1") + List findByWaterLevelLessThan(Double waterLevel); + + // 查询温度高于阈值的设备 + @Query("SELECT w FROM WaterSupplyRealtimeData w WHERE w.temperature > ?1") + List findByTemperatureGreaterThan(Double temperature); + + // 查询水压低于阈值的设备 + @Query("SELECT w FROM WaterSupplyRealtimeData w WHERE w.waterPress < ?1") + List findByWaterPressLessThan(Double waterPress); + + // 获取设备最新供水数据 + @Query(value = "SELECT * FROM water_supply_realtime_data w WHERE w.device_id = ?1 ORDER BY w.timestamp DESC LIMIT 1", nativeQuery = true) + Optional findLatestByDeviceId(String deviceId); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/WorkOrderRepository.java b/src/main/java/com/campus/water/mapper/WorkOrderRepository.java new file mode 100644 index 0000000..1f9e1e7 --- /dev/null +++ b/src/main/java/com/campus/water/mapper/WorkOrderRepository.java @@ -0,0 +1,46 @@ +package com.campus.water.mapper; + +import com.campus.water.entity.WorkOrder; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public interface WorkOrderRepository extends JpaRepository { + // 根据区域ID查询工单 + List findByAreaId(String areaId); + + // 根据工单状态查询 + List findByStatus(WorkOrder.OrderStatus status); + + // 根据分配的维修人员ID查询工单 + List findByAssignedRepairmanId(String assignedRepairmanId); + + // 按区域和状态查询工单 + List findByAreaIdAndStatus(String areaId, WorkOrder.OrderStatus status); + + // 根据优先级查询工单 + List findByPriority(WorkOrder.OrderPriority priority); + + // 根据设备ID查询工单 + List findByDeviceId(String deviceId); + + // 根据工单类型查询 + List findByOrderType(WorkOrder.OrderType orderType); + + // 根据创建时间范围查询工单 + List findByCreatedTimeBetween(LocalDateTime start, LocalDateTime end); + + // 查询超时未完成工单 + List findByDeadlineBeforeAndStatusNot(LocalDateTime deadline, WorkOrder.OrderStatus status); + + // 查询维修人员负责的工单 + List findByAssignedRepairmanIdAndStatus(String assignedRepairmanId, WorkOrder.OrderStatus status); + + // 根据告警ID查询工单 + List findByAlertId(Long alertId); + + // 根据创建人查询工单 + List findByCreatedBy(String createdBy); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/mapper/先读我.md b/src/main/java/com/campus/water/mapper/先读我.md new file mode 100644 index 0000000..3b52c1f --- /dev/null +++ b/src/main/java/com/campus/water/mapper/先读我.md @@ -0,0 +1 @@ +# 本md仅用于初始化目录,未创建所有子一级目录,在当前目录创建文件后请自行删除 \ No newline at end of file diff --git a/src/main/java/com/campus/water/model/WaterMakerSensorData.java b/src/main/java/com/campus/water/model/WaterMakerSensorData.java new file mode 100644 index 0000000..3487585 --- /dev/null +++ b/src/main/java/com/campus/water/model/WaterMakerSensorData.java @@ -0,0 +1,24 @@ +package com.campus.water.model; + +import lombok.Data; +import java.time.LocalDateTime; + +/** + * 制水机传感器数据模型(与MQTT消息格式完全对齐) + * 用于MQTT消息的序列化/反序列化,不直接持久化 + */ +@Data +public class WaterMakerSensorData { + private String deviceId; // 设备唯一ID(如WM001) + private Double tdsValue1; // TDS值(水质指标) + private Double tdsValue2; + private Double tdsValue3; + private Double waterFlow1; // 水流量(L/min) + private Double waterFlow2; + private Double waterPress; // 水压(MPa) + private Integer filterLife; // 滤芯寿命(%) + private Boolean leakage; // 是否漏水(true=漏水,false=正常) + private String waterQuality; // 水质等级(合格/不合格) + private String status; // 设备状态(normal=正常,error=异常) + private LocalDateTime recordTime; // 数据采集时间戳 +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/model/WaterSupplySensorData.java b/src/main/java/com/campus/water/model/WaterSupplySensorData.java new file mode 100644 index 0000000..6741d3b --- /dev/null +++ b/src/main/java/com/campus/water/model/WaterSupplySensorData.java @@ -0,0 +1,19 @@ +package com.campus.water.model; + +import lombok.Data; +import java.time.LocalDateTime; + +/** + * 供水机传感器数据模型(与MQTT消息格式完全对齐) + * 用于MQTT消息的序列化/反序列化,不直接持久化 + */ +@Data +public class WaterSupplySensorData { + private String deviceId; // 设备唯一ID(如WS001) + private Double waterFlow; // 水流量(L/min) + private Double waterPress; // 水压(MPa) + private Double waterLevel; // 水位(%) + private Double temperature; // 水温(℃) + private String status; // 设备状态(normal=正常,error=异常) + private LocalDateTime timestamp; // 数据采集时间戳 +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/security/JwtAuthenticationFilter.java b/src/main/java/com/campus/water/security/JwtAuthenticationFilter.java new file mode 100644 index 0000000..9f359bb --- /dev/null +++ b/src/main/java/com/campus/water/security/JwtAuthenticationFilter.java @@ -0,0 +1,69 @@ +// com/campus/water/security/JwtAuthenticationFilter.java +package com.campus.water.security; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * JWT认证拦截器 + */ +@Component +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + @Autowired + private UserDetailsServiceImpl userDetailsService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + try { + // 提取JWT令牌 + String jwt = jwtTokenProvider.getJwtFromRequest(request); + + if (jwt != null && jwtTokenProvider.validateJwtToken(jwt)) { + // 获取用户名 + String username = jwtTokenProvider.getUsernameFromJwtToken(jwt); + // 从JWT中获取角色信息 + String[] roles = jwtTokenProvider.getRolesFromJwtToken(jwt); + + // 创建权限列表 + List authorities = new ArrayList<>(); + for (String role : roles) { + authorities.add(new SimpleGrantedAuthority(role)); + } + + // 加载用户详情(主要用于获取密码等其他信息) + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + + // 设置认证信息,使用从JWT中解析的角色 + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + userDetails, null, authorities); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } catch (Exception e) { + logger.error("认证失败: ", e); + } + + filterChain.doFilter(request, response); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/security/JwtTokenProvider.java b/src/main/java/com/campus/water/security/JwtTokenProvider.java new file mode 100644 index 0000000..d8f6d21 --- /dev/null +++ b/src/main/java/com/campus/water/security/JwtTokenProvider.java @@ -0,0 +1,134 @@ +// com/campus/water/security/JwtTokenProvider.java +package com.campus.water.security; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.stream.Collectors; + +/** + * JWT令牌生成、验证工具类 + */ +@Component +public class JwtTokenProvider { + private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class); + + @Value("${jwt.secret}") + private String jwtSecret; + + @Value("${jwt.expiration}") + private long jwtExpirationMs; + + // 生成符合HS512要求的密钥(512位以上) + private SecretKey getSigningKey() { + byte[] keyBytes = jwtSecret.getBytes(StandardCharsets.UTF_8); + if (keyBytes.length < 64) { + throw new IllegalArgumentException("JWT密钥长度不足,HS512算法需要至少64字节的密钥"); + } + return Keys.hmacShaKeyFor(keyBytes); + } + + /** + * 从Authentication生成JWT令牌(适用于Spring Security认证流程) + */ + public String generateToken(Authentication authentication) { + UserDetails userPrincipal = (UserDetails) authentication.getPrincipal(); + String roles = userPrincipal.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.joining(",")); + + return Jwts.builder() + .setSubject(userPrincipal.getUsername()) + .claim("roles", roles) + .setIssuedAt(new Date()) + .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) + .signWith(getSigningKey(), SignatureAlgorithm.HS512) + .compact(); + } + + /** + * 直接生成令牌(支持多角色) + */ + public String generateToken(String username, String... roles) { + String rolesStr = String.join(",", roles); + return Jwts.builder() + .setSubject(username) + .claim("roles", rolesStr) + .setIssuedAt(new Date()) + .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) + .signWith(getSigningKey(), SignatureAlgorithm.HS512) + .compact(); + } + + /** + * 从令牌中获取用户名 + */ + public String getUsernameFromJwtToken(String token) { + return Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody() + .getSubject(); + } + + /** + * 从令牌中获取角色(支持多角色,返回数组) + */ + public String[] getRolesFromJwtToken(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody(); + String rolesStr = claims.get("roles", String.class); + return rolesStr != null ? rolesStr.split(",") : new String[0]; + } + + /** + * 验证JWT令牌 + */ + public boolean validateJwtToken(String authToken) { + try { + Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(authToken); + return true; + } catch (SignatureException e) { + logger.error("无效的JWT签名: {}", e.getMessage()); + } catch (MalformedJwtException e) { + logger.error("无效的JWT令牌: {}", e.getMessage()); + } catch (ExpiredJwtException e) { + logger.error("JWT令牌已过期: {}", e.getMessage()); + } catch (UnsupportedJwtException e) { + logger.error("不支持的JWT令牌: {}", e.getMessage()); + } catch (IllegalArgumentException e) { + logger.error("JWT声明字符串为空: {}", e.getMessage()); + } + return false; + } + + /** + * 从请求头中提取JWT令牌 + */ + public String getJwtFromRequest(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/security/RoleConstants.java b/src/main/java/com/campus/water/security/RoleConstants.java new file mode 100644 index 0000000..2d82713 --- /dev/null +++ b/src/main/java/com/campus/water/security/RoleConstants.java @@ -0,0 +1,19 @@ +package com.campus.water.security; + +/** + * 角色常量定义 + */ +public class RoleConstants { + /** 学生角色 */ + public static final String ROLE_STUDENT = "ROLE_STUDENT"; + /** 维修人员角色 */ + public static final String ROLE_REPAIRMAN = "ROLE_REPAIRMAN"; + + + /** 新增:细分的管理员角色(与Admin枚举一一对应) */ + public static final String ROLE_SUPER_ADMIN = "ROLE_SUPER_ADMIN"; // 超级管理员 + public static final String ROLE_AREA_ADMIN = "ROLE_AREA_ADMIN"; // 区域管理员 + public static final String ROLE_VIEWER = "ROLE_VIEWER"; // 查看者 + + private RoleConstants() {} +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/security/UserDetailsServiceImpl.java b/src/main/java/com/campus/water/security/UserDetailsServiceImpl.java new file mode 100644 index 0000000..5ca6874 --- /dev/null +++ b/src/main/java/com/campus/water/security/UserDetailsServiceImpl.java @@ -0,0 +1,65 @@ +package com.campus.water.security; + +import com.campus.water.entity.Admin; +import com.campus.water.entity.RepairerAuth; +import com.campus.water.entity.User; // 自定义User实体类(保留) +import com.campus.water.mapper.AdminRepository; +import com.campus.water.mapper.RepairerAuthRepository; +import com.campus.water.mapper.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.Collections; + +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private AdminRepository adminRepository; + + @Autowired + private RepairerAuthRepository repairerAuthRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // 1. 查询自定义User实体(学生用户) + User student = userRepository.findByStudentName(username).orElse(null); + if (student != null) { + return createUserDetails(student.getStudentName(), student.getPassword(), RoleConstants.ROLE_STUDENT); + } + + // 2. 查询管理员用户 + Admin admin = adminRepository.findByAdminName(username).orElse(null); + if (admin != null) { + // ========== 关键改动:替换硬编码的RoleConstants.ROLE_ADMIN为admin.getRole().name() ========== + return createUserDetails( + admin.getAdminName(), + admin.getPassword(), + admin.getRole().name() // 取Admin实体中实际的角色(如ROLE_SUPER_ADMIN/ROLE_AREA_ADMIN) + ); + } + + // 3. 查询维修人员用户 + RepairerAuth repairer = repairerAuthRepository.findByUsername(username).orElse(null); + if (repairer != null) { + return createUserDetails(repairer.getUsername(), repairer.getPassword(), RoleConstants.ROLE_REPAIRMAN); + } + + throw new UsernameNotFoundException("用户不存在: " + username); + } + + private UserDetails createUserDetails(String username, String password, String role) { + return new org.springframework.security.core.userdetails.User( + username, + password, + Collections.singletonList(new SimpleGrantedAuthority(role)) + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/security/先读我.md b/src/main/java/com/campus/water/security/先读我.md new file mode 100644 index 0000000..3b52c1f --- /dev/null +++ b/src/main/java/com/campus/water/security/先读我.md @@ -0,0 +1 @@ +# 本md仅用于初始化目录,未创建所有子一级目录,在当前目录创建文件后请自行删除 \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/AdminService.java b/src/main/java/com/campus/water/service/AdminService.java new file mode 100644 index 0000000..6f319bb --- /dev/null +++ b/src/main/java/com/campus/water/service/AdminService.java @@ -0,0 +1,80 @@ +package com.campus.water.service; + +import com.campus.water.entity.Admin; +import com.campus.water.mapper.AdminRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class AdminService { + + private final AdminRepository adminRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + /** + * 获取管理员列表(支持按姓名/角色筛选) + */ + public List getAdminList(String name, Admin.AdminRole role) { + if (name != null && !name.isEmpty() && role != null) { + // 按姓名+角色组合查询 + return adminRepository.findByAdminNameContainingAndRole(name, role); + } else if (role != null) { + // 仅按角色查询 + return adminRepository.findByRole(role); + } else if (name != null && !name.isEmpty()) { + // 仅按姓名查询 + return adminRepository.findByAdminNameContaining(name); + } else { + // 查询全部 + return adminRepository.findAll(); + } + } + + /** + * 按ID查询管理员 + */ + public Optional getAdminById(String adminId) { + return adminRepository.findByAdminId(adminId); + } + + /** + * 新增/修改管理员(支持指定角色) + */ + public Admin saveAdmin(Admin admin) { + admin.setUpdatedTime(LocalDateTime.now()); + if (admin.getCreatedTime() == null) { + admin.setCreatedTime(LocalDateTime.now()); + } + return adminRepository.save(admin); + } + + /** + * 删除管理员 + */ + public void deleteAdmin(String adminId) { + adminRepository.deleteById(adminId); + } + + /** + * 管理员登录验证 + */ + public Optional login(String adminName, String password) { + Optional admin = adminRepository.findByAdminName(adminName); + // 使用MD5加密器验证密码 + return admin.filter(a -> passwordEncoder.matches(password, a.getPassword())); + } + /** + * 获取所有角色枚举(供前端下拉框使用) + */ + public Admin.AdminRole[] getAllRoles() { + return Admin.AdminRole.values(); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/AlertPushService.java b/src/main/java/com/campus/water/service/AlertPushService.java new file mode 100644 index 0000000..1c5c7b2 --- /dev/null +++ b/src/main/java/com/campus/water/service/AlertPushService.java @@ -0,0 +1,77 @@ +package com.campus.water.service; + +import com.campus.water.entity.Alert; +import com.campus.water.entity.MessagePush; +import com.campus.water.entity.Repairman; +import com.campus.water.mapper.MessagePushRepository; +import com.campus.water.mapper.RepairmanRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +@Service +@RequiredArgsConstructor +@Slf4j +public class AlertPushService { + + private final MessagePushRepository messagePushRepository; + private final RepairmanRepository repairmanRepository; // 维修人员数据访问 + + /** + * 推送告警消息给目标用户(根据告警级别和区域分配) + * @param alert 告警实体 + */ + @Transactional + public void pushAlertMessage(Alert alert) { + // 1. 确定推送目标(紧急告警推送给管理员+区域维修人员;一般告警仅推送给区域维修人员) + String alertType = alert.getAlertType(); + String areaId = alert.getAreaId(); + boolean isEmergency = alert.getAlertLevel().getPriority() >= 3; // 紧急级别(error/critical) + + // 2. 构建消息内容 + MessagePush message = new MessagePush(); + message.setTitle(String.format("[%s告警] %s", + alert.getAlertLevel().getLevelName(), alertType)); + message.setContent(alert.getAlertMessage()); + message.setMessageType("ALERT"); + message.setRelatedId(alert.getAlertId().toString()); // 关联告警ID + message.setPushTime(LocalDateTime.now()); + + // 3. 推送区域维修人员(所有级别都推送) + List areaRepairmen = repairmanRepository.findByAreaId(areaId); + for (Repairman repairman : areaRepairmen) { + MessagePush repairmanMsg = copyMessage(message); + repairmanMsg.setRepairmanId(repairman.getRepairmanId()); + repairmanMsg.setUserId(repairman.getRepairmanId()); + repairmanMsg.setUserType("REPAIRMAN"); + messagePushRepository.save(repairmanMsg); + } + log.info("告警推送区域维修人员完成 | 告警ID:{} | 区域:{} | 人数:{}", + alert.getAlertId(), areaId, areaRepairmen.size()); + + // 4. 紧急告警额外推送管理员(假设管理员userType为"ADMIN",可扩展查询逻辑) + if (isEmergency) { + MessagePush adminMsg = copyMessage(message); + adminMsg.setAdminId("ADMIN001"); // 实际应从管理员表查询 + adminMsg.setUserId("ADMIN001"); + adminMsg.setUserType("ADMIN"); + messagePushRepository.save(adminMsg); + log.info("紧急告警推送管理员完成 | 告警ID:{}", alert.getAlertId()); + } + } + + // 复制消息基础信息(避免重复代码) + private MessagePush copyMessage(MessagePush source) { + MessagePush target = new MessagePush(); + target.setTitle(source.getTitle()); + target.setContent(source.getContent()); + target.setMessageType(source.getMessageType()); + target.setRelatedId(source.getRelatedId()); + target.setPushTime(source.getPushTime()); + return target; + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/AlertTriggerService.java b/src/main/java/com/campus/water/service/AlertTriggerService.java new file mode 100644 index 0000000..88d5399 --- /dev/null +++ b/src/main/java/com/campus/water/service/AlertTriggerService.java @@ -0,0 +1,233 @@ +package com.campus.water.service; + +import com.campus.water.entity.Alert; +import com.campus.water.entity.Device; +import com.campus.water.entity.WorkOrder; +import com.campus.water.mapper.AlertRepository; +import com.campus.water.mapper.DeviceRepository; +import com.campus.water.mapper.WorkOrderRepository; +import com.campus.water.model.WaterMakerSensorData; +import com.campus.water.model.WaterSupplySensorData; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +/** + * 告警触发与工单创建服务 + * 监听传感器数据,满足异常条件时自动创建告警和工单 + * 工单类型映射规则: + * - repair:故障维修(设备已故障,如漏水、TDS异常、水压/水位异常等) + * - maintenance:保养(设备未故障但需预防性维护,如滤芯寿命不足) + * - inspection:巡检(主动发起,不通过告警触发) + */ +@Service +@RequiredArgsConstructor +@Slf4j +public class AlertTriggerService { + + // 异常阈值配置(可根据实际需求调整或移至配置文件) + + private static final double WATER_MAKER_TDS_THRESHOLD = 100.0; // 制水机TDS值异常阈值 + private static final double WATER_MAKER_PRESS_MIN = 0.2; // 最小水压 + private static final int FILTER_LIFE_THRESHOLD = 20; // 滤芯寿命阈值(%) + private static final double WATER_SUPPLY_LEVEL_MIN = 20.0; // 供水机最低水位(%) + private static final double WATER_SUPPLY_PRESS_MIN = 0.1; // 供水机最小水压 + private static final double WATER_SUPPLY_TEMP_MAX = 25.0; // 供水机最高水温(℃) + private static final int ALERT_DUPLICATE_INTERVAL = 30; // 重复告警间隔(分钟) + + private final AlertRepository alertRepository; + private final WorkOrderRepository workOrderRepository; + private final DeviceRepository deviceRepository; + + /** + * 检查制水机数据异常并触发告警 + * 区分故障类(repair)和保养类(maintenance)工单 + */ + @Transactional + public void checkWaterMakerAbnormal(WaterMakerSensorData data) { + // 1. 拆分故障类和保养类异常 + boolean isFaultAbnormal = false; // 故障类(需repair) + boolean isMaintainAbnormal = false; // 保养类(需maintenance) + StringBuilder faultAlertMsg = new StringBuilder("制水机故障:"); + StringBuilder maintainAlertMsg = new StringBuilder("制水机保养提醒:"); + + // 故障类异常判断(触发repair工单) + if (data.getTdsValue1() > WATER_MAKER_TDS_THRESHOLD) { + faultAlertMsg.append(String.format("原水TDS值过高(%.2f);", data.getTdsValue1())); + isFaultAbnormal = true; + } + if (data.getTdsValue2() > WATER_MAKER_TDS_THRESHOLD) { + faultAlertMsg.append(String.format("纯水TDS值过高(%.2f);", data.getTdsValue2())); + isFaultAbnormal = true; + } + if (data.getWaterPress() < WATER_MAKER_PRESS_MIN) { + faultAlertMsg.append(String.format("水压过低(%.2fMPa);", data.getWaterPress())); + isFaultAbnormal = true; + } + if (data.getLeakage()) { + faultAlertMsg.append("设备漏水;"); + isFaultAbnormal = true; + } + + // 保养类异常判断(触发maintenance工单) + if (data.getFilterLife() < FILTER_LIFE_THRESHOLD) { + maintainAlertMsg.append(String.format("滤芯寿命不足(%d%%),需更换;", data.getFilterLife())); + isMaintainAbnormal = true; + } + + // 2. 处理故障类告警(repair工单) + if (isFaultAbnormal) { + if (isDuplicateAlert(data.getDeviceId(), "WATER_MAKER_FAULT")) { + log.info("制水机存在未处理故障告警,跳过重复触发 | 设备ID:{}", data.getDeviceId()); + } else { + log.warn("制水机故障条件满足 | 设备ID:{} | 原因:{}", data.getDeviceId(), faultAlertMsg); + createAlertAndWorkOrder( + data.getDeviceId(), + "WATER_MAKER_FAULT", + Alert.AlertLevel.critical, + faultAlertMsg.toString(), + WorkOrder.OrderType.repair // 故障→维修工单 + ); + } + } + + // 3. 处理保养类告警(maintenance工单) + if (isMaintainAbnormal) { + if (isDuplicateAlert(data.getDeviceId(), "WATER_MAKER_MAINTENANCE")) { + log.info("制水机存在未处理保养告警,跳过重复触发 | 设备ID:{}", data.getDeviceId()); + } else { + log.warn("制水机保养条件满足 | 设备ID:{} | 原因:{}", data.getDeviceId(), maintainAlertMsg); + createAlertAndWorkOrder( + data.getDeviceId(), + "WATER_MAKER_MAINTENANCE", + Alert.AlertLevel.warning, + maintainAlertMsg.toString(), + WorkOrder.OrderType.maintenance // 保养→保养工单 + ); + } + } + } + + /** + * 检查供水机数据异常并触发告警 + * 均为故障类异常,触发repair工单 + */ + @Transactional + public void checkWaterSupplyAbnormal(WaterSupplySensorData data) { + // 1. 检查重复告警 + if (isDuplicateAlert(data.getDeviceId(), "WATER_SUPPLY_FAULT")) { + log.info("供水机存在未处理故障告警,跳过重复触发 | 设备ID:{}", data.getDeviceId()); + return; + } + + // 2. 故障类异常判断(仅repair工单) + boolean isAbnormal = false; + StringBuilder alertMsg = new StringBuilder("供水机故障:"); + + if (data.getWaterLevel() < WATER_SUPPLY_LEVEL_MIN) { + alertMsg.append(String.format("水位过低(%.2f%%);", data.getWaterLevel())); + isAbnormal = true; + } + if (data.getWaterPress() < WATER_SUPPLY_PRESS_MIN) { + alertMsg.append(String.format("水压过低(%.2fMPa);", data.getWaterPress())); + isAbnormal = true; + } + if (data.getTemperature() > WATER_SUPPLY_TEMP_MAX) { + alertMsg.append(String.format("水温过高(%.2f℃);", data.getTemperature())); + isAbnormal = true; + } + + // 3. 触发告警并创建维修工单 + if (isAbnormal) { + log.warn("供水机故障条件满足 | 设备ID:{} | 原因:{}", data.getDeviceId(), alertMsg); + createAlertAndWorkOrder( + data.getDeviceId(), + "WATER_SUPPLY_FAULT", + Alert.AlertLevel.error, + alertMsg.toString(), + WorkOrder.OrderType.repair // 供水机异常均为故障→维修工单 + ); + } + } + + private final AlertPushService alertPushService; + /** + * 创建告警记录和对应的工单(支持repair/maintenance类型) + * @param orderType 工单类型(repair:维修,maintenance:保养,inspection:巡检<告警不触发>) + */ + private void createAlertAndWorkOrder(String deviceId, String alertType, + Alert.AlertLevel level, String message, + WorkOrder.OrderType orderType) { + // 获取设备所在区域(用于工单分配) + String areaId = getDeviceAreaId(deviceId); + + // 1. 创建告警记录 + Alert alert = new Alert(); + alert.setDeviceId(deviceId); + alert.setAlertType(alertType); + alert.setAlertLevel(level); + alert.setAlertMessage(message); + alert.setAreaId(areaId); + alert.setStatus(Alert.AlertStatus.pending); + alert.setTimestamp(LocalDateTime.now()); + alertRepository.save(alert); + alertPushService.pushAlertMessage(alert); + log.info("创建告警记录成功 | 告警ID:{} | 设备ID:{} | 告警类型:{}", + alert.getAlertId(), deviceId, alertType); + + // 2. 创建对应类型的工单(inspection类型不通过告警触发) + WorkOrder workOrder = new WorkOrder(); + workOrder.setOrderId(generateOrderId()); + workOrder.setDeviceId(deviceId); + workOrder.setAreaId(areaId); + workOrder.setOrderType(orderType); // 动态设置工单类型 + workOrder.setDescription(message); + workOrder.setStatus(WorkOrder.OrderStatus.pending); + workOrder.setCreatedTime(LocalDateTime.now()); + workOrderRepository.save(workOrder); + log.info("创建工单成功 | 工单ID:{} | 设备ID:{} | 工单类型:{}", + workOrder.getOrderId(), deviceId, orderType.name()); + } + + /** + * 检查是否存在重复告警(同设备同类型未处理告警,且在间隔时间内) + * 覆盖pending/processing两种未处理状态 + */ + private boolean isDuplicateAlert(String deviceId, String alertType) { + LocalDateTime before = LocalDateTime.now().minusMinutes(ALERT_DUPLICATE_INTERVAL); + // 检查未处理的告警状态(pending/processing) + List activeStatus = Arrays.asList( + Alert.AlertStatus.pending, + Alert.AlertStatus.processing + ); + return !alertRepository.findByDeviceIdAndAlertTypeAndStatusInAndTimestampAfter( + deviceId, + alertType, + activeStatus, + before + ).isEmpty(); + } + + /** + * 获取设备所在区域ID + */ + private String getDeviceAreaId(String deviceId) { + Optional deviceOpt = deviceRepository.findById(deviceId); + return deviceOpt.map(Device::getAreaId).orElse("unknown"); + } + + /** + * 生成唯一工单ID(WO+时间戳+随机数) + */ + private String generateOrderId() { + return String.format("WO%s%03d", + System.currentTimeMillis(), + (int)(Math.random() * 1000)); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/DeviceService.java b/src/main/java/com/campus/water/service/DeviceService.java new file mode 100644 index 0000000..7bb7d77 --- /dev/null +++ b/src/main/java/com/campus/water/service/DeviceService.java @@ -0,0 +1,222 @@ +package com.campus.water.service; + +import com.campus.water.entity.Device; +import com.campus.water.entity.DeviceTerminalMapping; +import com.campus.water.entity.Device.DeviceStatus; +import com.campus.water.entity.Device.DeviceType; +import com.campus.water.entity.DeviceTerminalMapping.TerminalStatus; +import com.campus.water.mapper.DeviceRepository; +import com.campus.water.mapper.DeviceTerminalMappingRepository; +import com.campus.water.util.ResultVO; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 设备管理服务类 + * 处理设备的CRUD、状态更新、终端关联等核心业务逻辑 + */ +@Service +@RequiredArgsConstructor +public class DeviceService { + + private final DeviceRepository deviceRepository; + private final DeviceTerminalMappingRepository terminalMappingRepository; + + /** + * 根据设备ID查询设备详情 + */ + public Device getDeviceById(String deviceId) { + return deviceRepository.findById(deviceId) + .orElseThrow(() -> new RuntimeException("设备不存在:" + deviceId)); + } + + /** + * 新增设备 + */ + @Transactional + public Device addDevice(Device device) { + // 检查设备ID是否已存在 + if (deviceRepository.existsById(device.getDeviceId())) { + throw new RuntimeException("设备ID已存在:" + device.getDeviceId()); + } + device.setCreateTime(LocalDateTime.now()); + device.setStatus(DeviceStatus.online); // 默认为在线状态 + return deviceRepository.save(device); + } + + /** + * 更新设备基本信息(不含状态) + */ + @Transactional + public Device updateDeviceInfo(Device device) { + Device existingDevice = getDeviceById(device.getDeviceId()); + // 保留创建时间,更新其他可编辑字段 + existingDevice.setDeviceName(device.getDeviceName()); + existingDevice.setDeviceType(device.getDeviceType()); + existingDevice.setAreaId(device.getAreaId()); + existingDevice.setInstallLocation(device.getInstallLocation()); + existingDevice.setInstallDate(device.getInstallDate()); + return deviceRepository.save(existingDevice); + } + + /** + * 更新设备状态(在线/离线/故障) + */ + @Transactional + public boolean updateDeviceStatus(String deviceId, DeviceStatus status) { + Device device = getDeviceById(deviceId); + device.setStatus(status); + deviceRepository.save(device); + return true; + } + + /** + * 批量更新设备状态 + */ + @Transactional + public boolean batchUpdateStatus(List deviceIds, DeviceStatus status) { + List devices = deviceRepository.findAllById(deviceIds); + if (devices.size() != deviceIds.size()) { + throw new RuntimeException("部分设备ID不存在"); + } + devices.forEach(device -> device.setStatus(status)); + deviceRepository.saveAll(devices); + return true; + } + + /** + * 根据条件查询设备列表 + */ + public List queryDevices(String areaId, DeviceType deviceType, DeviceStatus status) { + if (areaId != null && deviceType != null) { + return deviceRepository.findByAreaIdAndDeviceType(areaId, deviceType); + } else if (areaId != null) { + return deviceRepository.findByAreaId(areaId); + } else if (deviceType != null) { + return deviceRepository.findByDeviceType(deviceType); + } else if (status != null) { + return deviceRepository.findByStatus(status); + } else { + return deviceRepository.findAll(); + } + } + + /** + * 关联设备与终端 + */ + @Transactional + public DeviceTerminalMapping bindTerminal(String deviceId, String terminalId, String terminalName) { + // 校验设备是否存在 + getDeviceById(deviceId); + + // 检查终端是否已绑定 + Optional existing = terminalMappingRepository.findByTerminalId(terminalId); + if (existing.isPresent()) { + throw new RuntimeException("终端已绑定设备:" + existing.get().getDeviceId()); + } + + DeviceTerminalMapping mapping = new DeviceTerminalMapping(); + mapping.setDeviceId(deviceId); + mapping.setTerminalId(terminalId); + mapping.setTerminalName(terminalName); + mapping.setTerminalStatus(TerminalStatus.active); + mapping.setInstallDate(java.time.LocalDate.now()); + return terminalMappingRepository.save(mapping); + } + + /** + * 查询设备关联的终端列表 + */ + public List getBoundTerminals(String deviceId) { + getDeviceById(deviceId); // 校验设备存在性 + return terminalMappingRepository.findByDeviceId(deviceId); + } + + /** + * 统计各状态设备数量 + */ + public long countByStatus(DeviceStatus status) { + return deviceRepository.findByStatus(status).size(); + } + /** + * 删除设备(需先校验是否已解绑终端) + */ + @Transactional + public void deleteDevice(String deviceId) { + // 1. 校验设备是否存在 + Device device = getDeviceById(deviceId); + + // 2. 检查设备是否已绑定终端 + List boundTerminals = terminalMappingRepository.findByDeviceId(deviceId); + if (!boundTerminals.isEmpty()) { + // 收集已绑定的终端ID,便于前端提示 + String terminalIds = boundTerminals.stream() + .map(DeviceTerminalMapping::getTerminalId) + .collect(Collectors.joining(",")); + throw new RuntimeException("设备已绑定终端,无法删除(终端ID:" + terminalIds + ")"); + } + + // 3. 执行删除操作 + deviceRepository.delete(device); + } + + // 新增:关联供水机到制水机 + @Transactional + public void relateSupplierToMaker(String supplierId, String makerId) { + // 校验制水机是否存在且类型正确 + Device maker = deviceRepository.findById(makerId) + .orElseThrow(() -> new RuntimeException("制水机不存在:" + makerId)); + if (maker.getDeviceType() != DeviceType.water_maker) { + throw new RuntimeException("目标设备不是制水机:" + makerId); + } + + // 校验供水机是否存在且类型正确 + Device supplier = deviceRepository.findById(supplierId) + .orElseThrow(() -> new RuntimeException("供水机不存在:" + supplierId)); + if (supplier.getDeviceType() != DeviceType.water_supply) { + throw new RuntimeException("目标设备不是供水机:" + supplierId); + } + + // 检查供水机是否已关联其他制水机 + if (supplier.getParentMakerId() != null) { + throw new RuntimeException("供水机已关联制水机:" + supplier.getParentMakerId()); + } + + // 建立关联 + supplier.setParentMakerId(makerId); + deviceRepository.save(supplier); + } + + // 新增:解除供水机与制水机的关联 + @Transactional + public void unrelateSupplierFromMaker(String supplierId) { + Device supplier = deviceRepository.findById(supplierId) + .orElseThrow(() -> new RuntimeException("供水机不存在:" + supplierId)); + if (supplier.getDeviceType() != DeviceType.water_supply) { + throw new RuntimeException("目标设备不是供水机:" + supplierId); + } + supplier.setParentMakerId(null); + deviceRepository.save(supplier); + } + + // 新增:查询制水机关联的所有供水机 + public List getSuppliersByMaker(String makerId) { + return deviceRepository.findByParentMakerIdAndDeviceType(makerId, DeviceType.water_supply); + } + + // 新增:查询供水机所属的制水机 + public Device getMakerBySupplier(String supplierId) { + Device supplier = deviceRepository.findById(supplierId) + .orElseThrow(() -> new RuntimeException("供水机不存在:" + supplierId)); + if (supplier.getParentMakerId() == null) { + return null; + } + return deviceRepository.findById(supplier.getParentMakerId()).orElse(null); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/DeviceStatusService.java b/src/main/java/com/campus/water/service/DeviceStatusService.java new file mode 100644 index 0000000..e2b2aa3 --- /dev/null +++ b/src/main/java/com/campus/water/service/DeviceStatusService.java @@ -0,0 +1,37 @@ +// 路径:com/campus/water/service/DeviceStatusService.java +package com.campus.water.service; + +import com.campus.water.entity.Device; +import com.campus.water.entity.dto.request.DeviceStatusUpdateRequest; + +import java.util.List; +import java.util.Map; + +public interface DeviceStatusService { + // 更新设备状态 + boolean updateDeviceStatus(DeviceStatusUpdateRequest request); + + // 标记设备在线 + boolean markDeviceOnline(String deviceId); + + // 标记设备离线 + boolean markDeviceOffline(String deviceId, String reason); + + // 标记设备故障 + boolean markDeviceFault(String deviceId, String faultType, String description); + + // 批量更新设备状态 + boolean batchUpdateDeviceStatus(List deviceIds, String status, String remark); + + // 按状态查询设备 + List getDevicesByStatus(String status, String areaId, String deviceType); + + // 统计各状态设备数量 + Map getDeviceStatusCount(String areaId, String deviceType); + + // 获取超过阈值的离线设备 + List getOfflineDevicesExceedThreshold(Integer thresholdMinutes, String areaId); + + // 自动检测离线设备 + void autoDetectOfflineDevices(Integer thresholdMinutes); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/DeviceStatusServiceImpl.java b/src/main/java/com/campus/water/service/DeviceStatusServiceImpl.java new file mode 100644 index 0000000..4de8a84 --- /dev/null +++ b/src/main/java/com/campus/water/service/DeviceStatusServiceImpl.java @@ -0,0 +1,122 @@ +// com/campus/water/service/DeviceStatusServiceImpl.java +package com.campus.water.service; + +import com.campus.water.entity.Device; +import com.campus.water.entity.dto.request.DeviceStatusUpdateRequest; +import com.campus.water.mapper.DeviceRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Service +@RequiredArgsConstructor +@Slf4j +public class DeviceStatusServiceImpl implements DeviceStatusService { + + private final DeviceRepository deviceRepository; + + @Override + public boolean updateDeviceStatus(DeviceStatusUpdateRequest request) { + Device device = deviceRepository.findById(request.getDeviceId()).orElse(null); + if (device == null) { + log.warn("设备不存在 | 设备ID:{}", request.getDeviceId()); + return false; + } + device.setStatus(request.getStatus()); + device.setRemark(request.getRemark()); + deviceRepository.save(device); + return true; + } + + @Override + public boolean markDeviceOnline(String deviceId) { + Device device = deviceRepository.findById(deviceId).orElse(null); + if (device == null) return false; + device.setStatus(Device.DeviceStatus.online); + deviceRepository.save(device); + return true; + } + + @Override + public boolean markDeviceOffline(String deviceId, String reason) { + Device device = deviceRepository.findById(deviceId).orElse(null); + if (device == null) return false; + device.setStatus(Device.DeviceStatus.offline); + device.setRemark(reason); + deviceRepository.save(device); + return true; + } + + @Override + public boolean markDeviceFault(String deviceId, String faultType, String description) { + Device device = deviceRepository.findById(deviceId).orElse(null); + if (device == null) return false; + device.setStatus(Device.DeviceStatus.fault); + device.setRemark("故障类型:" + faultType + ",描述:" + description); + deviceRepository.save(device); + return true; + } + + @Override + public boolean batchUpdateDeviceStatus(List deviceIds, String status, String remark) { + List devices = deviceRepository.findAllById(deviceIds); + Device.DeviceStatus targetStatus = Device.DeviceStatus.valueOf(status); + devices.forEach(device -> { + device.setStatus(targetStatus); + device.setRemark(remark); + }); + deviceRepository.saveAll(devices); + return true; + } + + @Override + public List getDevicesByStatus(String status, String areaId, String deviceType) { + Device.DeviceStatus targetStatus = Device.DeviceStatus.valueOf(status); + + // 处理设备类型参数(允许为null) + Device.DeviceType targetType = null; + if (deviceType != null && !deviceType.isEmpty()) { + targetType = Device.DeviceType.valueOf(deviceType); + } + + // 根据设备类型是否为null执行不同查询 + if (targetType != null) { + return deviceRepository.findByStatusAndAreaIdAndDeviceType(targetStatus, areaId, targetType); + } else { + // 仅按状态和区域查询(如果有区域ID) + if (areaId != null && !areaId.isEmpty()) { + return deviceRepository.findByStatusAndAreaId(targetStatus, areaId); + } else { + return deviceRepository.findByStatus(targetStatus); + } + } + } + + @Override + public Map getDeviceStatusCount(String areaId, String deviceType) { + Device.DeviceType targetType = Device.DeviceType.valueOf(deviceType); + return Map.of( + "online", deviceRepository.countByStatusAndAreaIdAndDeviceType(Device.DeviceStatus.online, areaId, targetType), + "offline", deviceRepository.countByStatusAndAreaIdAndDeviceType(Device.DeviceStatus.offline, areaId, targetType), + "fault", deviceRepository.countByStatusAndAreaIdAndDeviceType(Device.DeviceStatus.fault, areaId, targetType) + ); + } + + @Override + public List getOfflineDevicesExceedThreshold(Integer thresholdMinutes, String areaId) { + // 由于没有last_active_time,此处逻辑需调整: + // 方案1:若设备有最近操作时间,可用作替代; + // 方案2:仅返回状态为offline的设备(不判断时间) + return deviceRepository.findByAreaIdAndStatus(areaId, Device.DeviceStatus.offline); + } + + @Override + public void autoDetectOfflineDevices(Integer thresholdMinutes) { + // 同理,无last_active_time时,无法通过时间判断,可注释或简化逻辑 + log.info("自动检测离线设备(不执行时间判断,仅依赖手动标记)"); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/LoginService.java b/src/main/java/com/campus/water/service/LoginService.java new file mode 100644 index 0000000..91441fe --- /dev/null +++ b/src/main/java/com/campus/water/service/LoginService.java @@ -0,0 +1,140 @@ +package com.campus.water.service; + +import com.campus.water.entity.Admin; +import com.campus.water.entity.RepairerAuth; +import com.campus.water.entity.Repairman; +import com.campus.water.entity.User; +import com.campus.water.entity.vo.LoginVO; +import com.campus.water.mapper.AdminRepository; +import com.campus.water.mapper.RepairerAuthRepository; +import com.campus.water.mapper.RepairmanRepository; +import com.campus.water.mapper.UserRepository; +import com.campus.water.entity.dto.request.LoginRequest; +import com.campus.water.security.RoleConstants; +import com.campus.water.security.JwtTokenProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.beans.factory.annotation.Autowired; + +@Service +@RequiredArgsConstructor +public class LoginService { + + private final AdminRepository adminRepository; + private final UserRepository userRepository; + private final RepairerAuthRepository repairerAuthRepository; + // 新增:注入RepairmanRepository + @Autowired + private RepairmanRepository repairmanRepository; + private final PasswordEncoder passwordEncoder; + private final JwtTokenProvider jwtTokenProvider; + + public LoginVO login(LoginRequest loginRequest) { + String username = loginRequest.getUsername(); + String password = loginRequest.getPassword(); + String userType = loginRequest.getUserType(); + + return switch (userType) { + case "admin" -> handleAdminLogin(username, password); + case "user" -> handleUserLogin(username, password); + case "repairman" -> handleRepairmanLogin(username, password); + default -> throw new RuntimeException("无效的用户类型:" + userType); + }; + } + + private LoginVO handleAdminLogin(String username, String password) { + Admin admin = adminRepository.findByAdminName(username) + .orElseThrow(() -> new RuntimeException("管理员不存在")); + + // 使用BCrypt验证密码 + if (!passwordEncoder.matches(password, admin.getPassword())) { + throw new RuntimeException("密码错误"); + } + + return createLoginVO(admin.getAdminId(), username, "admin", admin); + } + + private LoginVO handleUserLogin(String username, String password) { + User user = userRepository.findByStudentName(username) + .orElseThrow(() -> new RuntimeException("用户不存在")); + + if (!passwordEncoder.matches(password, user.getPassword())) { + throw new RuntimeException("密码错误"); + } + + return createLoginVO(user.getStudentId(), username, "user"); + } + + + private LoginVO handleRepairmanLogin(String username, String password) { + // 1. 查询登录信息(RepairmanAuth) + RepairerAuth repairerAuth = repairerAuthRepository.findByUsername(username) + .orElseThrow(() -> new RuntimeException("维修人员不存在")); + + // 2. 验证密码 + if (!passwordEncoder.matches(password, repairerAuth.getPassword())) { + throw new RuntimeException("密码错误"); + } + + // 3. 通过repairman_id查询Repairman表获取area_id + String repairmanId = repairerAuth.getRepairmanId(); + Repairman repairman = repairmanRepository.findById(repairmanId) + .orElseThrow(() -> new RuntimeException("维修人员信息不存在")); + String areaId = repairman.getAreaId(); // 假设Repairman类有getAreaId()方法 + + // 4. 返回包含areaId的LoginVO + return createLoginVO(repairmanId, username, "repairman", areaId); + } + + // 新增重载方法,支持传递areaId + private LoginVO createLoginVO(String userId, String username, String userType, String areaId) { + LoginVO vo = new LoginVO(); + vo.setUserId(userId); + vo.setUsername(username); + vo.setUserType(userType); + vo.setAreaId(areaId); // 设置区域ID + // 生成token(保持原有逻辑) + String role = RoleConstants.ROLE_REPAIRMAN; + vo.setToken(jwtTokenProvider.generateToken(username, role)); + return vo; + } + + /** + * 处理管理员登录(支持多角色) + */ + private LoginVO createLoginVO(String userId, String username, String userType, Admin admin) { + LoginVO vo = new LoginVO(); + vo.setUserId(userId); + vo.setUsername(username); + vo.setUserType(userType); + + // 生成包含管理员角色的JWT令牌 + vo.setToken(jwtTokenProvider.generateToken(username, admin.getRole().name())); + return vo; + } + + /** + * 处理用户/维修人员登录 + */ + private LoginVO createLoginVO(String userId, String username, String userType) { + LoginVO vo = new LoginVO(); + vo.setUserId(userId); + vo.setUsername(username); + vo.setUserType(userType); + + String role = switch (userType) { + case "user" -> RoleConstants.ROLE_STUDENT; + case "repairman" -> RoleConstants.ROLE_REPAIRMAN; + default -> throw new RuntimeException("不支持的用户类型:" + userType); + }; + + // 生成包含角色信息的JWT令牌 + vo.setToken(jwtTokenProvider.generateToken(username, role)); + return vo; + } + + public void setRepairmanRepository(RepairmanRepository repairmanRepository) { + this.repairmanRepository = repairmanRepository; + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/MqttSensorReceiver.java b/src/main/java/com/campus/water/service/MqttSensorReceiver.java new file mode 100644 index 0000000..62f03e5 --- /dev/null +++ b/src/main/java/com/campus/water/service/MqttSensorReceiver.java @@ -0,0 +1,194 @@ +package com.campus.water.service; + +import com.campus.water.config.MqttConfig; +import com.campus.water.entity.Alert; +import com.campus.water.entity.WaterMakerRealtimeData; +import com.campus.water.entity.WaterSupplyRealtimeData; +import com.campus.water.mapper.AlertRepository; +import com.campus.water.mapper.WaterMakerRealtimeDataRepository; +import com.campus.water.mapper.WaterSupplyRealtimeDataRepository; +import com.campus.water.model.WaterMakerSensorData; +import com.campus.water.model.WaterSupplySensorData; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.stereotype.Service; + +import jakarta.annotation.PostConstruct; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Service +@RequiredArgsConstructor +@Slf4j +public class MqttSensorReceiver { + // JPA Repository(数据持久化接口,Spring自动注入实现) + private final WaterMakerRealtimeDataRepository waterMakerRepo; + private final WaterSupplyRealtimeDataRepository waterSupplyRepo; + private final AlertRepository alertRepo; + private final ObjectMapper objectMapper; + private final MqttPahoMessageDrivenChannelAdapter mqttAdapter; + // 新增告警触发服务依赖 + private final AlertTriggerService alertTriggerService; + + /** + * 项目启动后初始化:订阅所有需要的MQTT主题 + * 主题后缀用「+」表示通配符(匹配所有设备ID) + */ + @PostConstruct + public void initMqttSubscription() { + mqttAdapter.addTopic(MqttConfig.TOPIC_WATER_MAKER_STATE + "+"); // 制水机状态(所有设备) + mqttAdapter.addTopic(MqttConfig.TOPIC_WATER_MAKER_WARN + "+"); // 制水机告警(所有设备) + mqttAdapter.addTopic(MqttConfig.TOPIC_WATER_SUPPLIER_STATE + "+"); // 供水机状态(所有设备) + mqttAdapter.addTopic(MqttConfig.TOPIC_WATER_SUPPLIER_WARN + "+"); // 供水机告警(所有设备) + log.info("MQTT订阅初始化完成 | 订阅主题:{}+、{}+、{}+、{}+", + MqttConfig.TOPIC_WATER_MAKER_STATE, + MqttConfig.TOPIC_WATER_MAKER_WARN, + MqttConfig.TOPIC_WATER_SUPPLIER_STATE, + MqttConfig.TOPIC_WATER_SUPPLIER_WARN); + } + + /** + * 监听MQTT接收通道,处理所有收到的消息 + * @param payload 消息内容(JSON字符串) + * @param topic 接收主题(用于区分消息类型) + */ + @ServiceActivator(inputChannel = "mqttInputChannel") + public void handleMqttMessage(String payload, @Header(MqttHeaders.RECEIVED_TOPIC) String topic) { + log.info("MQTT消息接收成功 | 主题:{} | 内容:{}", topic, payload); + + try { + // 根据主题分类处理 + if (topic.startsWith(MqttConfig.TOPIC_WATER_MAKER_STATE)) { + handleWaterMakerState(payload); // 制水机状态数据 + } else if (topic.startsWith(MqttConfig.TOPIC_WATER_MAKER_WARN)) { + handleWaterMakerWarning(payload); // 制水机告警数据 + } else if (topic.startsWith(MqttConfig.TOPIC_WATER_SUPPLIER_STATE)) { + handleWaterSupplyState(payload); // 供水机状态数据 + } else if (topic.startsWith(MqttConfig.TOPIC_WATER_SUPPLIER_WARN)) { + handleWaterSupplyWarning(payload); // 供水机告警数据 + } else { + log.warn("MQTT消息主题未匹配 | 未知主题:{} | 内容:{}", topic, payload); + } + } catch (Exception e) { + log.error("MQTT消息处理失败 | 主题:{} | 内容:{} | 异常:{}", topic, payload, e.getMessage()); + } + } + + /** + * 处理制水机状态数据:转换为JPA实体并持久化 + */ + private void handleWaterMakerState(String payload) throws Exception { + // 1. JSON反序列化为模型对象 + WaterMakerSensorData sensorData = objectMapper.readValue(payload, WaterMakerSensorData.class); + + // 2. 模型对象转换为JPA实体(持久化到数据库) + WaterMakerRealtimeData entity = new WaterMakerRealtimeData(); + entity.setDeviceId(sensorData.getDeviceId()); + // Double转BigDecimal处理(包含null值判断) + entity.setTdsValue1(sensorData.getTdsValue1() != null ? BigDecimal.valueOf(sensorData.getTdsValue1()) : null); + entity.setTdsValue2(sensorData.getTdsValue2() != null ? BigDecimal.valueOf(sensorData.getTdsValue2()) : null); + entity.setTdsValue3(sensorData.getTdsValue3() != null ? BigDecimal.valueOf(sensorData.getTdsValue3()) : null); + entity.setWaterFlow1(sensorData.getWaterFlow1() != null ? BigDecimal.valueOf(sensorData.getWaterFlow1()) : null); + entity.setWaterFlow2(sensorData.getWaterFlow2() != null ? BigDecimal.valueOf(sensorData.getWaterFlow2()) : null); + entity.setWaterPress(sensorData.getWaterPress() != null ? BigDecimal.valueOf(sensorData.getWaterPress()) : null); + entity.setFilterLife(sensorData.getFilterLife()); + entity.setLeakage(sensorData.getLeakage() ? true : false); // 数据库存储:true=漏水,false=正常 + entity.setWaterQuality(sensorData.getWaterQuality()); + entity.setStatus(WaterMakerRealtimeData.DeviceStatus.valueOf(sensorData.getStatus())); + entity.setRecordTime(sensorData.getRecordTime()); + entity.setCreatedTime(LocalDateTime.now()); + + // 3. 持久化到数据库(JPA save() 自动实现CRUD) + waterMakerRepo.save(entity); + log.info("制水机状态数据持久化成功 | 设备ID:{}", sensorData.getDeviceId()); + + // 新增:调用告警检查逻辑 + alertTriggerService.checkWaterMakerAbnormal(sensorData); + } + + /** + * 处理制水机告警数据:持久化告警记录+状态数据 + */ + private void handleWaterMakerWarning(String payload) throws Exception { + WaterMakerSensorData sensorData = objectMapper.readValue(payload, WaterMakerSensorData.class); + + // 1. 持久化告警记录 + Alert alert = new Alert(); + alert.setDeviceId(sensorData.getDeviceId()); + alert.setAlertType("WATER_MAKER_ABNORMAL"); // 告警类型(枚举规范) + alert.setAlertLevel(Alert.AlertLevel.critical); // 告警级别(严重) + alert.setAlertMessage(String.format( + "制水机异常 - 设备ID:%s,TDS值:%.2f,滤芯寿命:%d%%,漏水状态:%s", + sensorData.getDeviceId(), + sensorData.getTdsValue1(), + sensorData.getFilterLife(), + sensorData.getLeakage() ? "是" : "否" + )); + alert.setStatus(Alert.AlertStatus.pending); // 告警状态(未处理) + alert.setTimestamp(sensorData.getRecordTime()); + alert.setCreatedTime(LocalDateTime.now()); + + alertRepo.save(alert); + log.warn("制水机告警记录持久化成功 | 告警ID:{} | 设备ID:{}", alert.getAlertId(), sensorData.getDeviceId()); + + // 2. 同时持久化状态数据(便于后续追溯) + handleWaterMakerState(payload); + } + + /** + * 处理供水机状态数据:转换为JPA实体并持久化 + */ + private void handleWaterSupplyState(String payload) throws Exception { + WaterSupplySensorData sensorData = objectMapper.readValue(payload, WaterSupplySensorData.class); + + WaterSupplyRealtimeData entity = new WaterSupplyRealtimeData(); + entity.setDeviceId(sensorData.getDeviceId()); + // Double转BigDecimal处理(包含null值判断) + entity.setWaterFlow(sensorData.getWaterFlow() != null ? BigDecimal.valueOf(sensorData.getWaterFlow()) : null); + entity.setWaterPress(sensorData.getWaterPress() != null ? BigDecimal.valueOf(sensorData.getWaterPress()) : null); + entity.setWaterLevel(sensorData.getWaterLevel() != null ? BigDecimal.valueOf(sensorData.getWaterLevel()) : null); + entity.setTemperature(sensorData.getTemperature() != null ? BigDecimal.valueOf(sensorData.getTemperature()) : null); + entity.setStatus(WaterSupplyRealtimeData.DeviceStatus.valueOf(sensorData.getStatus())); + entity.setTimestamp(sensorData.getTimestamp()); + + + waterSupplyRepo.save(entity); + log.info("供水机状态数据持久化成功 | 设备ID:{}", sensorData.getDeviceId()); + + // 新增:调用告警检查逻辑 + alertTriggerService.checkWaterSupplyAbnormal(sensorData); + } + + /** + * 处理供水机告警数据:持久化告警记录+状态数据 + */ + private void handleWaterSupplyWarning(String payload) throws Exception { + WaterSupplySensorData sensorData = objectMapper.readValue(payload, WaterSupplySensorData.class); + + // 1. 持久化告警记录 + Alert alert = new Alert(); + alert.setDeviceId(sensorData.getDeviceId()); + alert.setAlertType("WATER_SUPPLY_ABNORMAL"); + alert.setAlertLevel(Alert.AlertLevel.error); + alert.setAlertMessage(String.format( + "供水机异常 - 设备ID:%s,水压:%.2fMPa,水位:%.2f%%", + sensorData.getDeviceId(), + sensorData.getWaterPress(), + sensorData.getWaterLevel() + )); + alert.setStatus(Alert.AlertStatus.pending); + alert.setTimestamp(sensorData.getTimestamp()); + alert.setCreatedTime(LocalDateTime.now()); + + alertRepo.save(alert); + log.warn("供水机告警记录持久化成功 | 告警ID:{} | 设备ID:{}", alert.getAlertId(), sensorData.getDeviceId()); + + // 2. 同时持久化状态数据 + handleWaterSupplyState(payload); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/MqttSensorSender.java b/src/main/java/com/campus/water/service/MqttSensorSender.java new file mode 100644 index 0000000..6ff5c52 --- /dev/null +++ b/src/main/java/com/campus/water/service/MqttSensorSender.java @@ -0,0 +1,135 @@ +package com.campus.water.service; + +import com.campus.water.config.MqttConfig; +import com.campus.water.model.WaterMakerSensorData; +import com.campus.water.model.WaterSupplySensorData; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.stereotype.Service; +import org.springframework.util.MimeTypeUtils; + +import java.time.LocalDateTime; +import java.util.Random; + +@Service +@RequiredArgsConstructor +@Slf4j // 日志注解(替代System.out) +public class MqttSensorSender { + private final MqttPahoMessageHandler mqttMessageHandler; + private final ObjectMapper objectMapper; // JSON序列化工具(Spring默认注入) + private final Random random = new Random(); // 生成模拟数据 + + /** + * 模拟制水机发送「正常状态数据」 + * @param deviceId 设备ID(如WM001) + */ + public void sendWaterMakerState(String deviceId) { + try { + // 1. 构建模拟数据(符合正常业务范围) + WaterMakerSensorData data = new WaterMakerSensorData(); + data.setDeviceId(deviceId); + data.setTdsValue1(50 + random.nextDouble() * 30); // 50-80(正常范围) + data.setTdsValue2(50 + random.nextDouble() * 30); + data.setTdsValue3(50 + random.nextDouble() * 30); + data.setWaterFlow1(random.nextDouble() * 2); // 0-2 L/min + data.setWaterFlow2(random.nextDouble() * 2); + data.setWaterPress(0.2 + random.nextDouble() * 0.3); // 0.2-0.5 MPa + data.setFilterLife(30 + random.nextInt(70)); // 30-100% + data.setLeakage(random.nextBoolean() && random.nextBoolean()); // 漏水概率较低(25%) + data.setWaterQuality("合格"); + data.setStatus("normal"); + data.setRecordTime(LocalDateTime.now()); + + // 2. 序列化JSON(MQTT消息 payload 为JSON字符串) + String payload = objectMapper.writeValueAsString(data); + // 3. 构建主题(设备ID作为主题后缀,精准订阅) + String topic = MqttConfig.TOPIC_WATER_MAKER_STATE + deviceId; + + // 4. 发送MQTT消息 + sendMessage(topic, payload); + log.info("制水机状态消息发送成功 | 设备ID:{} | 主题:{} | 数据:{}", deviceId, topic, payload); + } catch (JsonProcessingException e) { + log.error("制水机状态消息发送失败 | 设备ID:{} | 异常:{}", deviceId, e.getMessage()); + } + } + + /** + * 模拟制水机发送「告警数据」 + * @param deviceId 设备ID(如WM001) + */ + public void sendWaterMakerWarning(String deviceId) { + try { + // 1. 构建异常数据(超出正常范围) + WaterMakerSensorData data = new WaterMakerSensorData(); + data.setDeviceId(deviceId); + data.setTdsValue1(150 + random.nextDouble() * 50); // 150-200(异常范围) + data.setTdsValue2(150 + random.nextDouble() * 50); + data.setTdsValue3(150 + random.nextDouble() * 50); + data.setWaterFlow1(0.1 + random.nextDouble() * 0.3); // 流量极低 + data.setWaterFlow2(0.1 + random.nextDouble() * 0.3); + data.setWaterPress(0.1 + random.nextDouble() * 0.1); // 水压过低(0.1-0.2 MPa) + data.setFilterLife(5 + random.nextInt(10)); // 滤芯寿命低(5-15%) + data.setLeakage(true); // 强制漏水 + data.setStatus("error"); + data.setRecordTime(LocalDateTime.now()); + + // 2. 序列化+发送 + String payload = objectMapper.writeValueAsString(data); + String topic = MqttConfig.TOPIC_WATER_MAKER_WARN + deviceId; + sendMessage(topic, payload); + log.warn("制水机告警消息发送成功 | 设备ID:{} | 主题:{} | 数据:{}", deviceId, topic, payload); + } catch (JsonProcessingException e) { + log.error("制水机告警消息发送失败 | 设备ID:{} | 异常:{}", deviceId, e.getMessage()); + } + } + + /** + * 模拟供水机发送「正常状态数据」 + * @param deviceId 设备ID(如WS001) + */ + public void sendWaterSupplyData(String deviceId) { + try { + // 1. 构建模拟数据 + WaterSupplySensorData data = new WaterSupplySensorData(); + data.setDeviceId(deviceId); + data.setWaterFlow(random.nextDouble() * 3); // 0-3 L/min + data.setWaterPress(0.1 + random.nextDouble() * 0.2); // 0.1-0.3 MPa + data.setWaterLevel(30 + random.nextDouble() * 50); // 30-80% + data.setTemperature(18 + random.nextDouble() * 4); // 18-22℃ + data.setStatus("normal"); + data.setTimestamp(LocalDateTime.now()); + + // 2. 序列化+发送 + String payload = objectMapper.writeValueAsString(data); + String topic = MqttConfig.TOPIC_WATER_SUPPLIER_STATE + deviceId; + sendMessage(topic, payload); + log.info("供水机状态消息发送成功 | 设备ID:{} | 主题:{} | 数据:{}", deviceId, topic, payload); + } catch (JsonProcessingException e) { + log.error("供水机状态消息发送失败 | 设备ID:{} | 异常:{}", deviceId, e.getMessage()); + } + } + + /** + * 通用发送方法(封装MQTT消息构建逻辑) + * @param topic 主题 + * @param payload 消息内容(JSON字符串) + */ + private void sendMessage(String topic, String payload) { + // 构建Spring Messaging消息(指定JSON格式、主题、QOS) + Message message = MessageBuilder + .withPayload(payload) + .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON) + .setHeader("mqtt_topic", topic) + .setHeader("mqtt_qos", MqttConfig.QOS) + .build(); + + // 调用处理器发送消息 + mqttMessageHandler.handleMessage(message); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/RegisterService.java b/src/main/java/com/campus/water/service/RegisterService.java new file mode 100644 index 0000000..14bbe12 --- /dev/null +++ b/src/main/java/com/campus/water/service/RegisterService.java @@ -0,0 +1,127 @@ +package com.campus.water.service; + +import com.campus.water.entity.Admin; +import com.campus.water.entity.RepairerAuth; +import com.campus.water.entity.Repairman; +import com.campus.water.entity.User; +import com.campus.water.entity.dto.request.RegisterRequest; +import com.campus.water.mapper.AdminRepository; +import com.campus.water.mapper.RepairerAuthRepository; +import com.campus.water.mapper.RepairmanRepository; +import com.campus.water.mapper.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; + +@Service +public class RegisterService { + + @Autowired + private AdminRepository adminRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private RepairerAuthRepository repairerAuthRepository; + + // 新增注入RepairmanRepository + @Autowired + private RepairmanRepository repairmanRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + + public boolean register(RegisterRequest request) { + String username = request.getUsername(); + // 使用BCrypt加密密码 + String encryptedPwd = passwordEncoder.encode(request.getPassword()); + String userType = request.getUserType(); + + switch (userType) { + case "admin": + handleAdminRegister(username, encryptedPwd, request); + break; + case "user": + handleUserRegister(username, encryptedPwd, request); + break; + case "repairman": + handleRepairmanRegister(username, encryptedPwd, request); + break; + default: + throw new RuntimeException("无效的用户类型:" + userType); + } + return true; + } + + private void handleAdminRegister(String username, String password, RegisterRequest request) { + if (adminRepository.existsByAdminName(username)) { + throw new RuntimeException("管理员用户名已存在"); + } + if (adminRepository.existsByAdminId(request.getAdminId())) { + throw new RuntimeException("管理员ID已存在"); + } + if (request.getPhone() != null && adminRepository.existsByPhone(request.getPhone())) { + throw new RuntimeException("手机号已被注册"); + } + + Admin admin = new Admin(); + admin.setAdminId(request.getAdminId()); + admin.setAdminName(username); + admin.setPassword(password); + admin.setPhone(request.getPhone()); + admin.setRole(Admin.AdminRole.valueOf("ROLE_" + request.getRole().toUpperCase())); + admin.setCreatedTime(LocalDateTime.now()); + admin.setUpdatedTime(LocalDateTime.now()); + + adminRepository.save(admin); + } + + private void handleUserRegister(String studentName, String password, RegisterRequest request) { + if (userRepository.existsByStudentName(studentName)) { + throw new RuntimeException("用户名已存在"); + } + if (userRepository.existsByStudentId(request.getStudentId())) { + throw new RuntimeException("学号已被注册"); + } + + User user = new User(); + user.setPassword(password); // 使用BCrypt加密后的密码 + user.setStudentId(request.getStudentId()); + user.setStudentName(request.getStudentName()); + user.setPhone(request.getPhone()); + user.setStatus(User.UserStatus.active); + + userRepository.save(user); + } + + private void handleRepairmanRegister(String username, String password, RegisterRequest request) { + // 1. 校验维修人员认证信息唯一性 + if (repairerAuthRepository.existsByUsername(username)) { + throw new RuntimeException("维修人员用户名已存在"); + } + if (repairerAuthRepository.existsByRepairmanId(request.getRepairmanId())) { + throw new RuntimeException("维修人员ID已被注册"); + } + + // 2. 保存维修人员认证信息(RepairerAuth表) + RepairerAuth repairerAuth = new RepairerAuth(); + repairerAuth.setUsername(username); + repairerAuth.setPassword(password); + repairerAuth.setRepairmanId(request.getRepairmanId()); + repairerAuth.setAccountStatus(RepairerAuth.AccountStatus.active); + repairerAuthRepository.save(repairerAuth); + + // 3. 保存维修人员基本信息(Repairman表) + Repairman repairman = new Repairman(); + repairman.setRepairmanId(request.getRepairmanId()); // 与认证表关联的ID + repairman.setRepairmanName(request.getRepairmanName()); // 从请求获取姓名 + repairman.setPhone(request.getPhone()); // 从请求获取手机号 + repairman.setAreaId(request.getAreaId()); // 从请求获取负责区域 + repairman.setSkills(request.getSkills()); // 从请求获取技能描述 + // 其他字段使用默认值:状态默认idle,工作量默认0,评分默认null + repairmanRepository.save(repairman); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/RepairmanService.java b/src/main/java/com/campus/water/service/RepairmanService.java new file mode 100644 index 0000000..bb0a02f --- /dev/null +++ b/src/main/java/com/campus/water/service/RepairmanService.java @@ -0,0 +1,87 @@ +package com.campus.water.service; + +import com.campus.water.entity.Repairman; +import com.campus.water.mapper.RepairmanRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +/** + * 维修人员服务类(合并接口+实现) + */ +@Service +@RequiredArgsConstructor +public class RepairmanService { + + private final RepairmanRepository repairmanRepository; + + /** + * 获取维修人员列表(支持多条件筛选) + * @param name 维修人员姓名(模糊查询,可选) + * @param areaId 区域ID(可选) + * @param status 维修人员状态(可选) + * @return 符合条件的维修人员列表 + */ + public List getRepairmanList(String name, String areaId, Repairman.RepairmanStatus status) { + // 组合查询条件 + if (name != null && !name.isEmpty() && areaId != null && !areaId.isEmpty() && status != null) { + return repairmanRepository.findByRepairmanNameContainingAndAreaIdAndStatus(name, areaId, status); + } else if (name != null && !name.isEmpty() && areaId != null && !areaId.isEmpty()) { + return repairmanRepository.findByRepairmanNameContainingAndAreaId(name, areaId); + } else if (name != null && !name.isEmpty() && status != null) { + return repairmanRepository.findByRepairmanNameContainingAndStatus(name, status); + } else if (areaId != null && !areaId.isEmpty() && status != null) { + return repairmanRepository.findByAreaIdAndStatus(areaId, status); + } else if (name != null && !name.isEmpty()) { + return repairmanRepository.findByRepairmanNameContaining(name); + } else if (areaId != null && !areaId.isEmpty()) { + return repairmanRepository.findByAreaId(areaId); + } else if (status != null) { + return repairmanRepository.findByStatus(status); + } else { + // 查询全部 + return repairmanRepository.findAll(); + } + } + + /** + * 获取所有维修人员状态枚举 + * @return 维修人员状态枚举数组 + */ + public Repairman.RepairmanStatus[] getAllStatus() { + return Repairman.RepairmanStatus.values(); + } + + /** + * 新增/编辑维修人员 + */ + public Repairman saveRepairman(Repairman repairman) { + // 设置时间戳 + if (repairman.getCreatedTime() == null) { + repairman.setCreatedTime(LocalDateTime.now()); + } + + // 新增时默认状态为空闲 + if (repairman.getRepairmanId() == null && repairman.getStatus() == null) { + repairman.setStatus(Repairman.RepairmanStatus.idle); + } + + return repairmanRepository.save(repairman); + } + + /** + * 删除维修人员 + */ + public void deleteRepairman(String repairmanId) { + repairmanRepository.deleteById(repairmanId); + } + + /** + * 根据ID查询维修人员 + */ + public Optional getRepairmanById(String repairmanId) { + return repairmanRepository.findById(repairmanId); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/StatisticsService.java b/src/main/java/com/campus/water/service/StatisticsService.java new file mode 100644 index 0000000..f5134b6 --- /dev/null +++ b/src/main/java/com/campus/water/service/StatisticsService.java @@ -0,0 +1,165 @@ +package com.campus.water.service; + +import com.campus.water.entity.Alert; +import com.campus.water.entity.Device; +import com.campus.water.entity.TerminalUsageStats; +import com.campus.water.entity.dto.request.StatisticsQueryRequest; +import com.campus.water.entity.vo.AlarmStatisticsVO; +import com.campus.water.entity.vo.StatisticsVO; +import com.campus.water.mapper.AlertRepository; +import com.campus.water.mapper.DeviceRepository; +import com.campus.water.mapper.TerminalUsageStatsRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class StatisticsService { + + private final TerminalUsageStatsRepository terminalUsageStatsRepository; + private final DeviceRepository deviceRepository; + private final AlertRepository alertRepository; + + /** + * 用水量统计(按设备/区域/时间维度) + */ + public StatisticsVO getWaterUsageStatistics(StatisticsQueryRequest request) { + StatisticsVO result = new StatisticsVO(); + LocalDate startDate = request.getStartDate(); + LocalDate endDate = request.getEndDate(); + LocalDateTime startTime = startDate.atStartOfDay(); + LocalDateTime endTime = endDate.atTime(LocalTime.MAX); + + // 按终端ID统计 + if (request.getTerminalId() != null && !request.getTerminalId().isEmpty()) { + result.setType("terminal"); + List stats = terminalUsageStatsRepository + .findByTerminalIdAndStatDateBetween(request.getTerminalId(), startDate, endDate); + + List dates = stats.stream() + .map(stat -> stat.getStatDate().toString()) + .collect(Collectors.toList()); + + // 修复1:BigDecimal转Double(处理null值,避免空指针) + List waterUsage = stats.stream() + .map(stat -> stat.getTotalWaterOutput() != null ? stat.getTotalWaterOutput().doubleValue() : 0.0) + .collect(Collectors.toList()); + + result.setDates(dates); + result.setWaterUsage(waterUsage); + + // 求和(Double类型直接计算) + double total = waterUsage.stream().mapToDouble(Double::doubleValue).sum(); + result.setTotalUsage(total); + result.setAvgDailyUsage(dates.size() > 0 ? total / dates.size() : 0); + return result; + } + + // 按设备统计 + if ("by_device".equals(request.getStatType())) { + result.setType("device"); + List devices = deviceRepository.findByAreaId(request.getAreaId()); + List deviceIds = devices.stream() + .map(Device::getDeviceId) + .collect(Collectors.toList()); + + Map deviceTotal = new HashMap<>(); + for (String deviceId : deviceIds) { + List stats = terminalUsageStatsRepository + .findByTerminalIdAndStatDateBetween(deviceId, startDate, endDate); + + // 修复2:mapToDouble中处理BigDecimal转double(含null值) + double total = stats.stream() + .mapToDouble(stat -> stat.getTotalWaterOutput() != null ? stat.getTotalWaterOutput().doubleValue() : 0.0) + .sum(); + deviceTotal.put(deviceId, total); + } + + List> sorted = new ArrayList<>(deviceTotal.entrySet()); + sorted.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue())); + result.setDeviceStats(sorted.stream() + .map(entry -> { + Map item = new HashMap<>(); + item.put("deviceId", entry.getKey()); + item.put("totalUsage", entry.getValue()); + return item; + }) + .collect(Collectors.toList())); + return result; + } + + // 按区域统计 + if ("by_area".equals(request.getStatType())) { + result.setType("area"); + result.setAreaId(request.getAreaId()); + List stats = terminalUsageStatsRepository + .findByStatDateBetween(startDate, endDate); + + // 修复3:BigDecimal转double求和 + double total = stats.stream() + .mapToDouble(stat -> stat.getTotalWaterOutput() != null ? stat.getTotalWaterOutput().doubleValue() : 0.0) + .sum(); + result.setTotalUsage(total); + return result; + } + + return result; + } + + /** + * 告警统计(次数、处理情况) + */ + public AlarmStatisticsVO getAlarmStatistics(StatisticsQueryRequest request) { + AlarmStatisticsVO result = new AlarmStatisticsVO(); + LocalDate startDate = request.getStartDate(); + LocalDate endDate = request.getEndDate(); + LocalDateTime startTime = startDate.atStartOfDay(); + LocalDateTime endTime = endDate.atTime(LocalTime.MAX); + + List alerts; + // 按终端ID筛选告警 + if (request.getTerminalId() != null && !request.getTerminalId().isEmpty()) { + alerts = alertRepository.findByDeviceIdAndTimestampBetween(request.getTerminalId(), startTime, endTime); + } else { + alerts = alertRepository.findByTimestampBetween(startTime, endTime); + } + + // 统计告警级别分布 + Map levelCount = alerts.stream() + .map(alert -> alert.getAlertLevel().name()) // 获取告警级别枚举名称 + .collect(Collectors.groupingBy(level -> level, Collectors.counting())); + result.setLevelCount(levelCount); + + // 统计告警状态分布 + Map statusCount = alerts.stream() + .map(alert -> alert.getStatus().name()) // 获取告警状态枚举名称 + .collect(Collectors.groupingBy(status -> status, Collectors.counting())); + result.setStatusCount(statusCount); + + // 计算处理率(避免除零错误) + long total = alerts.size(); + long resolved = alerts.stream() + .filter(alert -> alert.getStatus() == Alert.AlertStatus.resolved) + .count(); + double handleRate = total > 0 ? (double) resolved / total * 100 : 0.0; + result.setHandleRate(handleRate); + + return result; + } + + // 其他统计方法 + public Map getDeviceStatusStatistics(String areaId, String deviceType) { + return new HashMap<>(); + } + + public Map getDashboardStatistics() { + return new HashMap<>(); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/UserService.java b/src/main/java/com/campus/water/service/UserService.java new file mode 100644 index 0000000..67b9667 --- /dev/null +++ b/src/main/java/com/campus/water/service/UserService.java @@ -0,0 +1,68 @@ +package com.campus.water.service; + +import com.campus.water.entity.User; +import com.campus.water.mapper.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class UserService { + + private final UserRepository userRepository; + + /** + * 获取学生用户列表(支持按姓名和状态筛选) + */ + public List getUserList(String studentName, User.UserStatus status) { + if (studentName != null && !studentName.isEmpty() && status != null) { + // 按姓名模糊查询和状态筛选 + return userRepository.findByStudentNameContainingAndStatus(studentName, status); + } else if (studentName != null && !studentName.isEmpty()) { + // 仅按姓名模糊查询 + return userRepository.findByStudentNameContaining(studentName); + } else if (status != null) { + // 仅按状态筛选 + return userRepository.findByStatus(status); + } else { + // 查询所有学生 + return userRepository.findAll(); + } + } + + /** + * 新增/编辑学生 + */ + public User saveUser(User user) { + // 新增时设置默认值 + if (user.getCreateTime() == null) { + user.setCreateTime(LocalDateTime.now()); + } + // 新增学生默认状态为激活 + if (user.getStudentId() == null && user.getStatus() == null) { + user.setStatus(User.UserStatus.active); + } + user.setUpdatedTime(LocalDateTime.now()); + return userRepository.save(user); + } + + /** + * 删除学生 + */ + public void deleteUser(String studentId) { + if (!userRepository.existsById(studentId)) { + throw new RuntimeException("学生不存在:" + studentId); + } + userRepository.deleteById(studentId); + } + + /** + * 根据学号查询学生 + */ + public Optional getUserById(String studentId) { + return userRepository.findById(studentId); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/WorkOrderService.java b/src/main/java/com/campus/water/service/WorkOrderService.java new file mode 100644 index 0000000..4388e78 --- /dev/null +++ b/src/main/java/com/campus/water/service/WorkOrderService.java @@ -0,0 +1,31 @@ +package com.campus.water.service; + +import com.campus.water.entity.WorkOrder; +import java.util.List; + +public interface WorkOrderService { + // 抢单 + boolean grabOrder(String orderId, String repairmanId); + + // 拒单 + boolean rejectOrder(String orderId, String repairmanId, String reason); + + // 提交维修结果 + boolean submitRepairResult(String orderId, String repairmanId, String dealNote, String imgUrl); + + // 获取可抢工单 + List getAvailableOrders(String areaId); + + // 获取我的工单 + List getMyOrders(String repairmanId); + + // 管理员手动派单(新增方法) + boolean assignOrderByAdmin(String orderId, String repairmanId); + + // 新增:审核工单方法(与实现类签名一致) + boolean reviewOrder(String orderId, boolean approved); + + // 按状态查询工单的方法 + List getOrdersByStatus(WorkOrder.OrderStatus status); + List getOrdersByAreaAndStatus(String areaId, WorkOrder.OrderStatus status); +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/WorkOrderServiceImpl.java b/src/main/java/com/campus/water/service/WorkOrderServiceImpl.java new file mode 100644 index 0000000..0e6b182 --- /dev/null +++ b/src/main/java/com/campus/water/service/WorkOrderServiceImpl.java @@ -0,0 +1,255 @@ +package com.campus.water.service; + +import com.campus.water.entity.WorkOrder; +import com.campus.water.entity.Repairman; +import com.campus.water.mapper.WorkOrderRepository; +import com.campus.water.mapper.RepairmanRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class WorkOrderServiceImpl implements WorkOrderService { + + private final WorkOrderRepository workOrderRepository; + private final RepairmanRepository repairmanRepository; + + + /** + * 维修人员抢单功能 + * 业务规则:仅允许抢"待处理"状态的工单,且维修人员需处于"空闲"状态 + * @param orderId 工单ID + * @param repairmanId 维修人员ID + * @return 抢单成功返回true,否则返回false + */ + @Override + @Transactional + public boolean grabOrder(String orderId, String repairmanId) { + // 查询工单是否存在 + Optional orderOpt = workOrderRepository.findById(orderId); + if (orderOpt.isPresent()) { + WorkOrder order = orderOpt.get(); + + // 检查工单状态是否为"待处理"(只有待处理的工单可被抢单) + if (order.getStatus() == WorkOrder.OrderStatus.pending) { + // 查询维修人员是否存在且状态为"空闲" + var repairman = repairmanRepository.findById(repairmanId); + if (repairman.isPresent() && repairman.get().getStatus() == Repairman.RepairmanStatus.idle) { + + // 更新工单状态:改为"已抢单",记录抢单时间和维修人员ID + order.setStatus(WorkOrder.OrderStatus.processing); + order.setAssignedRepairmanId(repairmanId); + order.setGrabbedTime(LocalDateTime.now()); + workOrderRepository.save(order); + + // 更新维修人员状态:改为"忙碌" + var repairmanEntity = repairman.get(); + repairmanEntity.setStatus(Repairman.RepairmanStatus.busy); + repairmanRepository.save(repairmanEntity); + + return true; // 抢单成功 + } + } + } + return false; // 抢单失败(工单不存在/状态异常/维修人员不满足条件) + } + + /** + * 维修人员拒单功能 + * 业务规则:仅允许拒绝自己已抢单的工单,拒单后工单回到"待处理"状态 + * @param orderId 工单ID + * @param repairmanId 维修人员ID + * @param reason 拒单原因 + * @return 拒单成功返回true,否则返回false + */ + @Override + @Transactional + public boolean rejectOrder(String orderId, String repairmanId, String reason) { + // 查询工单是否存在 + Optional orderOpt = workOrderRepository.findById(orderId); + if (orderOpt.isPresent()) { + WorkOrder order = orderOpt.get(); + + // 校验权限:必须是当前工单的已分配维修人员,且工单状态为"已抢单" + if (order.getAssignedRepairmanId() != null && + order.getAssignedRepairmanId().equals(repairmanId) && + order.getStatus() == WorkOrder.OrderStatus.processing) { + + // 重置工单状态:改为"待处理",清空维修人员信息和抢单时间 + order.setStatus(WorkOrder.OrderStatus.pending); + order.setAssignedRepairmanId(null); + order.setGrabbedTime(null); + workOrderRepository.save(order); + + // 重置维修人员状态:改为"空闲" + var repairman = repairmanRepository.findById(repairmanId); + repairman.ifPresent(rm -> { + rm.setStatus(Repairman.RepairmanStatus.idle); + repairmanRepository.save(rm); + }); + + return true; // 拒单成功 + } + } + return false; // 拒单失败(工单不存在/无权限/状态异常) + } + + /** + * 提交维修结果 + * 业务规则:仅允许提交自己负责的"已抢单"或"处理中"状态工单 + * @param orderId 工单ID + * @param repairmanId 维修人员ID + * @param dealNote 处理备注 + * @param imgUrl 维修照片URL(可选) + * @return 提交成功返回true,否则返回false + */ + @Override + @Transactional + public boolean submitRepairResult(String orderId, String repairmanId, String dealNote, String imgUrl) { + // 查询工单是否存在 + Optional orderOpt = workOrderRepository.findById(orderId); + if (orderOpt.isPresent()) { + WorkOrder order = orderOpt.get(); + + // 校验权限:必须是当前工单的已分配维修人员,且工单状态为"已抢单"或"处理中" + if (order.getAssignedRepairmanId() != null && + order.getAssignedRepairmanId().equals(repairmanId) && + (order.getStatus() == WorkOrder.OrderStatus.processing || + order.getStatus() == WorkOrder.OrderStatus.processing)) { + + // 更新工单状态为"已完成",记录完成时间和维修信息 + order.setStatus(WorkOrder.OrderStatus.completed); + order.setCompletedTime(LocalDateTime.now()); + order.setDealNote(dealNote); + order.setImgUrl(imgUrl); // 允许为空(可选参数) + workOrderRepository.save(order); + + // 更新维修人员状态为"空闲",并增加工作量 + repairmanRepository.findById(repairmanId).ifPresent(rm -> { + rm.setStatus(Repairman.RepairmanStatus.idle); + rm.setWorkCount(rm.getWorkCount() + 1); + repairmanRepository.save(rm); + }); + + return true; // 提交成功 + } + } + return false; // 提交失败(工单不存在/无权限/状态异常) + } + + /** + * 管理员审核工单(新增实现) + * 业务规则:仅审核"待审核"状态的工单,通过则改为"已完成",不通过则退回"处理中" + */ + @Override + @Transactional + public boolean reviewOrder(String orderId, boolean approved) { + Optional orderOpt = workOrderRepository.findById(orderId); + if (orderOpt.isPresent()) { + WorkOrder order = orderOpt.get(); + // 仅处理"待审核"状态的工单 + if (order.getStatus() == WorkOrder.OrderStatus.reviewing) { + if (approved) { + // 审核通过:改为"已完成",并更新维修人员工作量 + order.setStatus(WorkOrder.OrderStatus.completed); + if (order.getAssignedRepairmanId() != null) { + repairmanRepository.findById(order.getAssignedRepairmanId()) + .ifPresent(rm -> { + rm.setWorkCount(rm.getWorkCount() + 1); + repairmanRepository.save(rm); + }); + } + } else { + // 审核不通过:退回"处理中" + order.setStatus(WorkOrder.OrderStatus.processing); + } + workOrderRepository.save(order); + return true; + } + } + return false; + } + + /** + * 获取可抢工单列表 + * 业务规则:只返回指定区域内"待处理"状态的工单 + * @param areaId 区域ID + * @return 符合条件的工单列表 + */ + @Override + public List getAvailableOrders(String areaId) { + if (areaId == null || areaId.trim().isEmpty()) { + // 管理员:查所有区域的待处理工单 + return workOrderRepository.findByStatus(WorkOrder.OrderStatus.pending); + } else { + // 维修人员:查指定区域的待处理工单 + return workOrderRepository.findByAreaIdAndStatus(areaId, WorkOrder.OrderStatus.pending); + } + } + + /** + * 获取维修人员自己的工单 + * 业务规则:返回所有分配给该维修人员的工单(不限制状态) + * @param repairmanId 维修人员ID + * @return 该维修人员负责的工单列表 + */ + @Override + public List getMyOrders(String repairmanId) { + return workOrderRepository.findByAssignedRepairmanId(repairmanId); + } + + /** + * 管理员手动派单功能 + * 业务规则:仅管理员可操作,只能分配"待处理"或"超时"状态的工单给"空闲"的维修人员 + * @param orderId 工单ID + * @param repairmanId 维修人员ID + * @return 派单成功返回true,否则返回false + */ + @Override + @Transactional + public boolean assignOrderByAdmin(String orderId, String repairmanId) { + // 查询工单是否存在 + Optional orderOpt = workOrderRepository.findById(orderId); + if (orderOpt.isPresent()) { + WorkOrder order = orderOpt.get(); + + // 校验工单状态(只能分配待处理或超时工单) + if (order.getStatus() == WorkOrder.OrderStatus.pending || + order.getStatus() == WorkOrder.OrderStatus.timeout) { + + // 查询维修人员是否存在且状态为"空闲" + Optional repairmanOpt = repairmanRepository.findById(repairmanId); + if (repairmanOpt.isPresent() && repairmanOpt.get().getStatus() == Repairman.RepairmanStatus.idle) { + // 更新工单状态 + order.setStatus(WorkOrder.OrderStatus.processing); + order.setAssignedRepairmanId(repairmanId); + order.setGrabbedTime(LocalDateTime.now()); + workOrderRepository.save(order); + + // 更新维修人员状态 + Repairman repairman = repairmanOpt.get(); + repairman.setStatus(Repairman.RepairmanStatus.busy); + repairmanRepository.save(repairman); + + return true; // 派单成功 + } + } + } + return false; // 派单失败(工单不存在/状态异常/维修人员不满足条件) + } + + // 按状态查询工单的方法 + @Override + public List getOrdersByStatus(WorkOrder.OrderStatus status) { + return workOrderRepository.findByStatus(status); + } + + @Override + public List getOrdersByAreaAndStatus(String areaId, WorkOrder.OrderStatus status) { + return workOrderRepository.findByAreaIdAndStatus(areaId, status); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/app/RepairmanAppService.java b/src/main/java/com/campus/water/service/app/RepairmanAppService.java new file mode 100644 index 0000000..7387881 --- /dev/null +++ b/src/main/java/com/campus/water/service/app/RepairmanAppService.java @@ -0,0 +1,160 @@ +package com.campus.water.service.app; + +import com.campus.water.entity.WorkOrder; +import com.campus.water.service.WorkOrderService; +import com.campus.water.util.ResultVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 维修人员APP端服务层 + * 处理维修人员APP端的工单相关业务逻辑 + */ +@Service +public class RepairmanAppService { + + // 核心修改:注入WorkOrderService而非WorkOrderController + @Autowired + private WorkOrderService workOrderService; + + /** + * 获取可抢工单列表 + * @param areaId 区域ID + * @return 可抢工单列表 + */ + @PreAuthorize("hasAnyRole('REPAIRMAN', 'SUPER_ADMIN', 'AREA_ADMIN', 'VIEWER')") + public ResultVO> getAvailableOrders(String areaId) { + try { + // 参数校验 + if (areaId == null || areaId.trim().isEmpty()) { + return ResultVO.error(400, "区域ID不能为空"); + } + List orders = workOrderService.getAvailableOrders(areaId); + return ResultVO.success(orders); + } catch (Exception e) { + return ResultVO.error(500, "获取工单列表失败: " + e.getMessage()); + } + } + + /** + * 抢单功能 + * @param request 包含orderId和repairmanId的请求参数 + * @return 抢单结果 + */ + @PreAuthorize("hasAnyRole('REPAIRMAN', 'SUPER_ADMIN', 'AREA_ADMIN', 'VIEWER')") + public ResultVO grabOrder(Map request) { + try { + // 参数校验 + String orderId = request.get("orderId"); + String repairmanId = request.get("repairmanId"); + if (orderId == null || orderId.trim().isEmpty()) { + return ResultVO.error(400, "工单ID不能为空"); + } + if (repairmanId == null || repairmanId.trim().isEmpty()) { + return ResultVO.error(400, "维修人员ID不能为空"); + } + + boolean result = workOrderService.grabOrder(orderId, repairmanId); + if (result) { + return ResultVO.success(true, "抢单成功"); + } else { + return ResultVO.error(400, "抢单失败,工单可能已被其他人抢走或状态异常"); + } + } catch (Exception e) { + return ResultVO.error(500, "抢单失败: " + e.getMessage()); + } + } + + /** + * 拒单功能 + * @param request 包含orderId、repairmanId、reason的请求参数 + * @return 拒单结果 + */ + @PreAuthorize("hasAnyRole('REPAIRMAN', 'SUPER_ADMIN', 'AREA_ADMIN', 'VIEWER')") + public ResultVO rejectOrder(Map request) { + try { + // 参数校验 + String orderId = request.get("orderId"); + String repairmanId = request.get("repairmanId"); + String reason = request.get("reason"); + + if (orderId == null || orderId.trim().isEmpty()) { + return ResultVO.error(400, "工单ID不能为空"); + } + if (repairmanId == null || repairmanId.trim().isEmpty()) { + return ResultVO.error(400, "维修人员ID不能为空"); + } + if (reason == null || reason.trim().isEmpty()) { + return ResultVO.error(400, "拒单原因不能为空"); + } + + boolean result = workOrderService.rejectOrder(orderId, repairmanId, reason); + if (result) { + return ResultVO.success(true, "拒单成功"); + } else { + return ResultVO.error(400, "拒单失败,工单状态异常或无操作权限"); + } + } catch (Exception e) { + return ResultVO.error(500, "拒单失败: " + e.getMessage()); + } + } + + /** + * 提交维修结果 + * @param request 包含orderId、repairmanId、dealNote、imgUrl的请求参数 + * @return 提交结果 + */ + @PreAuthorize("hasAnyRole('REPAIRMAN', 'SUPER_ADMIN', 'AREA_ADMIN', 'VIEWER')") + public ResultVO submitRepairResult(Map request) { + try { + // 参数校验 + String orderId = request.get("orderId"); + String repairmanId = request.get("repairmanId"); + String dealNote = request.get("dealNote"); + String imgUrl = request.get("imgUrl"); // 图片URL可选 + + if (orderId == null || orderId.trim().isEmpty()) { + return ResultVO.error(400, "工单ID不能为空"); + } + if (repairmanId == null || repairmanId.trim().isEmpty()) { + return ResultVO.error(400, "维修人员ID不能为空"); + } + if (dealNote == null || dealNote.trim().isEmpty()) { + return ResultVO.error(400, "维修处理备注不能为空"); + } + + boolean result = workOrderService.submitRepairResult(orderId, repairmanId, dealNote, imgUrl); + if (result) { + return ResultVO.success(true, "维修结果提交成功"); + } else { + return ResultVO.error(400, "维修结果提交失败,工单状态异常或无操作权限"); + } + } catch (Exception e) { + return ResultVO.error(500, "提交失败: " + e.getMessage()); + } + } + + /** + * 获取我的工单 + * @param repairmanId 维修人员ID + * @return 维修人员名下的工单列表 + */ + @PreAuthorize("hasAnyRole('REPAIRMAN', 'SUPER_ADMIN', 'AREA_ADMIN', 'VIEWER')") + public ResultVO> getMyOrders(String repairmanId) { + try { + // 参数校验 + if (repairmanId == null || repairmanId.trim().isEmpty()) { + return ResultVO.error(400, "维修人员ID不能为空"); + } + + List orders = workOrderService.getMyOrders(repairmanId); + return ResultVO.success(orders); + } catch (Exception e) { + return ResultVO.error(500, "获取工单失败: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/app/StudentAppService.java b/src/main/java/com/campus/water/service/app/StudentAppService.java new file mode 100644 index 0000000..8615e60 --- /dev/null +++ b/src/main/java/com/campus/water/service/app/StudentAppService.java @@ -0,0 +1,53 @@ +package com.campus.water.service.app; + +import com.campus.water.controller.WaterUsageController; +import com.campus.water.util.ResultVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Service; + +import java.util.Map; + +@Service +public class StudentAppService { + + @Autowired + private WaterUsageController waterUsageController; + + // 扫码获取终端信息 - 学生和管理员可访问 + @PreAuthorize("hasAnyRole('STUDENT', 'SUPER_ADMIN', 'AREA_ADMIN', 'VIEWER')") + public ResultVO> getTerminalInfo(String terminalId) { + try { + Map result = waterUsageController.getTerminalInfo(terminalId); + return ResultVO.success(result); + } catch (Exception e) { + return ResultVO.error(500, "获取终端信息失败: " + e.getMessage()); + } + } + + // 扫码用水 - 学生和管理员可访问 + @PreAuthorize("hasAnyRole('STUDENT', 'SUPER_ADMIN', 'AREA_ADMIN', 'VIEWER')") + public ResultVO> scanToDrink(Map request) { + try { + String terminalId = (String) request.get("terminalId"); + String studentId = (String) request.get("studentId"); + Double waterConsumption = Double.valueOf(request.get("waterConsumption").toString()); + + Map result = waterUsageController.scanToDrink(terminalId, studentId, waterConsumption); + return ResultVO.success(result); + } catch (Exception e) { + return ResultVO.error(500, "用水操作失败: " + e.getMessage()); + } + } + + // 查询水质信息 - 学生和管理员可访问 + @PreAuthorize("hasAnyRole('STUDENT', 'SUPER_ADMIN', 'AREA_ADMIN', 'VIEWER')") + public ResultVO> getWaterQuality(String deviceId) { + try { + Map result = waterUsageController.getWaterQualityInfo(deviceId); + return ResultVO.success(result); + } catch (Exception e) { + return ResultVO.error(500, "获取水质信息失败: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/service/先读我.md b/src/main/java/com/campus/water/service/先读我.md new file mode 100644 index 0000000..3b52c1f --- /dev/null +++ b/src/main/java/com/campus/water/service/先读我.md @@ -0,0 +1 @@ +# 本md仅用于初始化目录,未创建所有子一级目录,在当前目录创建文件后请自行删除 \ No newline at end of file diff --git a/src/main/java/com/campus/water/task/DeviceStatusMonitorTask.java b/src/main/java/com/campus/water/task/DeviceStatusMonitorTask.java new file mode 100644 index 0000000..8a05577 --- /dev/null +++ b/src/main/java/com/campus/water/task/DeviceStatusMonitorTask.java @@ -0,0 +1,41 @@ +/** + * 设备状态监控定时任务 + * 功能:定时 */ +package com.campus.water.task; + +import com.campus.water.service.DeviceStatusService; // 添加这行导入 +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class DeviceStatusMonitorTask { + + private final DeviceStatusService deviceStatusService; // 现在可以正确识别 + + // 后续代码不变... + @Scheduled(fixedRate = 300000) + public void monitorOfflineDevices() { + log.info("开始自动检测离线设备..."); + try { + deviceStatusService.autoDetectOfflineDevices(30); + } catch (Exception e) { + log.error("离线设备检测任务失败: {}", e.getMessage(), e); + } + } + + @Scheduled(cron = "0 0 * * * ?") + public void collectDeviceStatusStatistics() { + log.info("开始收集设备状态统计..."); + try { + // 补充实际统计逻辑,例如: + // deviceStatusService.collectAndSaveStatistics(); + log.info("设备状态统计收集完成"); + } catch (Exception e) { + log.error("统计收集失败: {}", e.getMessage(), e); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/task/SensorSimulationTask.java b/src/main/java/com/campus/water/task/SensorSimulationTask.java new file mode 100644 index 0000000..4835f8e --- /dev/null +++ b/src/main/java/com/campus/water/task/SensorSimulationTask.java @@ -0,0 +1,68 @@ +package com.campus.water.task; + +import com.campus.water.service.MqttSensorSender; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +@RequiredArgsConstructor +@Slf4j +public class SensorSimulationTask { + private final MqttSensorSender mqttSensorSender; + + // 模拟已部署的设备列表(实际项目可从数据库查询) + private final List waterMakerDevices = new ArrayList<>() {{ + add("WM001"); // 制水机1 + add("WM002"); // 制水机2 + add("WM003"); // 制水机3 + add("WM004"); // 制水机4 + }}; + + private final List waterSupplyDevices = new ArrayList<>() {{ + add("WS001"); // 供水机1 + add("WS002"); // 供水机2 + add("WS003"); // 供水机3 + }}; + + /** + * 定时发送「正常状态数据」:每30秒执行一次 + * fixedRate:固定间隔(从上一次任务开始时间计算) + */ + @Scheduled(fixedRate = 30000) + public void sendRegularStateData() { + log.info("=== 开始发送设备正常状态数据 ==="); + + // 1. 发送所有制水机状态 + for (String deviceId : waterMakerDevices) { + mqttSensorSender.sendWaterMakerState(deviceId); + } + + // 2. 发送所有供水机状态 + for (String deviceId : waterSupplyDevices) { + mqttSensorSender.sendWaterSupplyData(deviceId); + } + + log.info("=== 设备正常状态数据发送完成 ==="); + } + + /** + * 定时发送「随机告警数据」:每5分钟执行一次 + * fixedRate:固定间隔(从上一次任务开始时间计算) + */ + @Scheduled(fixedRate = 300000) + public void sendRandomWarningData() { + log.info("=== 开始发送随机告警数据 ==="); + + // 随机选择1台制水机发送告警(模拟设备故障) + int randomIndex = (int) (Math.random() * waterMakerDevices.size()); + String targetDevice = waterMakerDevices.get(randomIndex); + mqttSensorSender.sendWaterMakerWarning(targetDevice); + + log.info("=== 随机告警数据发送完成 | 告警设备:{} ===", targetDevice); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/util/ResultVO.java b/src/main/java/com/campus/water/util/ResultVO.java new file mode 100644 index 0000000..773a19a --- /dev/null +++ b/src/main/java/com/campus/water/util/ResultVO.java @@ -0,0 +1,66 @@ +package com.campus.water.util; + +import lombok.Data; + +@Data +public class ResultVO { + private Integer code; // 状态码 + private String message; // 提示信息 + private T data; // 返回数据 + private Long timestamp; // 时间戳 + + public ResultVO() { + this.timestamp = System.currentTimeMillis(); + } + + // 成功返回(带数据) + public static ResultVO success(T data) { + ResultVO result = new ResultVO<>(); + result.setCode(200); + result.setMessage("success"); + result.setData(data); + return result; + } + + // 成功返回(带数据和自定义消息) + public static ResultVO success(T data, String message) { + ResultVO result = new ResultVO<>(); + result.setCode(200); + result.setMessage(message); + result.setData(data); + return result; + } + + // 成功返回(无数据) + public static ResultVO success() { + ResultVO result = new ResultVO<>(); + result.setCode(200); + result.setMessage("success"); + return result; + } + + // 错误返回 + public static ResultVO error(Integer code, String message) { + ResultVO result = new ResultVO<>(); + result.setCode(code); + result.setMessage(message); + return result; + } + + // 常见的错误状态 + public static ResultVO error(String message) { + return error(500, message); + } + + public static ResultVO badRequest(String message) { + return error(400, message); + } + + public static ResultVO unauthorized(String message) { + return error(401, message); + } + + public static ResultVO notFound(String message) { + return error(404, message); + } +} \ No newline at end of file diff --git a/src/main/java/com/campus/water/util/先读我.md b/src/main/java/com/campus/water/util/先读我.md new file mode 100644 index 0000000..3b52c1f --- /dev/null +++ b/src/main/java/com/campus/water/util/先读我.md @@ -0,0 +1 @@ +# 本md仅用于初始化目录,未创建所有子一级目录,在当前目录创建文件后请自行删除 \ No newline at end of file diff --git a/src/main/java/com/campus/water/view/先读我.md b/src/main/java/com/campus/water/view/先读我.md new file mode 100644 index 0000000..3b52c1f --- /dev/null +++ b/src/main/java/com/campus/water/view/先读我.md @@ -0,0 +1 @@ +# 本md仅用于初始化目录,未创建所有子一级目录,在当前目录创建文件后请自行删除 \ No newline at end of file diff --git a/src/main/resources/app2/.gitignore b/src/main/resources/app2/.gitignore new file mode 100644 index 0000000..a3f7a51 --- /dev/null +++ b/src/main/resources/app2/.gitignore @@ -0,0 +1,36 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo + +.eslintcache + +# Cypress +/cypress/videos/ +/cypress/screenshots/ + +# Vitest +__screenshots__/ diff --git a/src/main/resources/app2/.vscode/extensions.json b/src/main/resources/app2/.vscode/extensions.json new file mode 100644 index 0000000..4c8e9ac --- /dev/null +++ b/src/main/resources/app2/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "Vue.volar", + "vitest.explorer" + ] +} diff --git a/src/main/resources/app2/README.md b/src/main/resources/app2/README.md new file mode 100644 index 0000000..59b0057 --- /dev/null +++ b/src/main/resources/app2/README.md @@ -0,0 +1,44 @@ +# app2 + +This template should help get you started developing with Vue 3 in Vite. + +## Recommended IDE Setup + +[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). + +## Recommended Browser Setup + +- Chromium-based browsers (Chrome, Edge, Brave, etc.): + - [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd) + - [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters) +- Firefox: + - [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/) + - [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/) + +## Customize configuration + +See [Vite Configuration Reference](https://vite.dev/config/). + +## Project Setup + +```sh +npm install +``` + +### Compile and Hot-Reload for Development + +```sh +npm run dev +``` + +### Compile and Minify for Production + +```sh +npm run build +``` + +### Run Unit Tests with [Vitest](https://vitest.dev/) + +```sh +npm run test:unit +``` diff --git a/src/main/resources/app2/index.html b/src/main/resources/app2/index.html new file mode 100644 index 0000000..b19040a --- /dev/null +++ b/src/main/resources/app2/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/src/main/resources/app2/jsconfig.json b/src/main/resources/app2/jsconfig.json new file mode 100644 index 0000000..5a1f2d2 --- /dev/null +++ b/src/main/resources/app2/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + }, + "exclude": ["node_modules", "dist"] +} diff --git a/src/main/resources/app2/package-lock.json b/src/main/resources/app2/package-lock.json new file mode 100644 index 0000000..2effd2a --- /dev/null +++ b/src/main/resources/app2/package-lock.json @@ -0,0 +1,4279 @@ +{ + "name": "app2", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "app2", + "version": "0.0.0", + "dependencies": { + "pinia": "^3.0.4", + "vue": "^3.5.25", + "vue-router": "^4.6.3" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.2", + "@vue/test-utils": "^2.4.6", + "jsdom": "^27.2.0", + "vite": "^7.2.4", + "vite-plugin-vue-devtools": "^8.0.5", + "vitest": "^4.0.14" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@acemir/cssom": { + "version": "0.9.27", + "resolved": "https://registry.npmmirror.com/@acemir/cssom/-/cssom-0.9.27.tgz", + "integrity": "sha512-Ja8SQ/4mec5WZABC1F9XB1juJlkdHVZ4F1dftBmXagtZnbmspW+tuzd4bo35eRrc48iAEtk1yTUzBveOsa/MZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/@asamuzakjp/css-color/-/css-color-4.1.0.tgz", + "integrity": "sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "lru-cache": "^11.2.2" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.7.6", + "resolved": "https://registry.npmmirror.com/@asamuzakjp/dom-selector/-/dom-selector-6.7.6.tgz", + "integrity": "sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.4" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmmirror.com/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", + "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz", + "integrity": "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.20", + "resolved": "https://registry.npmmirror.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.20.tgz", + "integrity": "sha512-8BHsjXfSciZxjmHQOuVdW2b8WLUPts9a+mfL13/PzEviufUEW2xnvQuOlKs9dRBHgRqJ53SF/DUoK9+MZk72oQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.50", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.50.tgz", + "integrity": "sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmmirror.com/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.2.tgz", + "integrity": "sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.50" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.15", + "resolved": "https://registry.npmmirror.com/@vitest/expect/-/expect-4.0.15.tgz", + "integrity": "sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.15", + "@vitest/utils": "4.0.15", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.15", + "resolved": "https://registry.npmmirror.com/@vitest/mocker/-/mocker-4.0.15.tgz", + "integrity": "sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.15", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.15", + "resolved": "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-4.0.15.tgz", + "integrity": "sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.15", + "resolved": "https://registry.npmmirror.com/@vitest/runner/-/runner-4.0.15.tgz", + "integrity": "sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.15", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.15", + "resolved": "https://registry.npmmirror.com/@vitest/snapshot/-/snapshot-4.0.15.tgz", + "integrity": "sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.15", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.15", + "resolved": "https://registry.npmmirror.com/@vitest/spy/-/spy-4.0.15.tgz", + "integrity": "sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.15", + "resolved": "https://registry.npmmirror.com/@vitest/utils/-/utils-4.0.15.tgz", + "integrity": "sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.15", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz", + "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz", + "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@vue/babel-helper-vue-transform-on": "1.5.0", + "@vue/babel-plugin-resolve-type": "1.5.0", + "@vue/shared": "^3.5.18" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz", + "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.0", + "@vue/compiler-sfc": "^3.5.18" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.25.tgz", + "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.25", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz", + "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz", + "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.25", + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz", + "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.9" + } + }, + "node_modules/@vue/devtools-core": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/@vue/devtools-core/-/devtools-core-8.0.5.tgz", + "integrity": "sha512-dpCw8nl0GDBuiL9SaY0mtDxoGIEmU38w+TQiYEPOLhW03VDC0lfNMYXS/qhl4I0YlysGp04NLY4UNn6xgD0VIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.0.5", + "@vue/devtools-shared": "^8.0.5", + "mitt": "^3.0.1", + "nanoid": "^5.1.5", + "pathe": "^2.0.3", + "vite-hot-client": "^2.1.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-core/node_modules/@vue/devtools-kit": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-8.0.5.tgz", + "integrity": "sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.0.5", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^2.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-core/node_modules/@vue/devtools-shared": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-8.0.5.tgz", + "integrity": "sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/devtools-core/node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@vue/devtools-core/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.25.tgz", + "integrity": "sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.25.tgz", + "integrity": "sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.25.tgz", + "integrity": "sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.25", + "@vue/runtime-core": "3.5.25", + "@vue/shared": "3.5.25", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.25.tgz", + "integrity": "sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25" + }, + "peerDependencies": { + "vue": "3.5.25" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.25.tgz", + "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==", + "license": "MIT" + }, + "node_modules/@vue/test-utils": { + "version": "2.4.6", + "resolved": "https://registry.npmmirror.com/@vue/test-utils/-/test-utils-2.4.6.tgz", + "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^2.0.0" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.4", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.4.tgz", + "integrity": "sha512-ZCQ9GEWl73BVm8bu5Fts8nt7MHdbt5vY9bP6WGnUh+r3l8M7CgfyTlwsgCbMC66BNxPr6Xoce3j66Ms5YUQTNA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001759", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz", + "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/chai/-/chai-6.2.1.tgz", + "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmmirror.com/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssstyle": { + "version": "5.3.3", + "resolved": "https://registry.npmmirror.com/cssstyle/-/cssstyle-5.3.3.tgz", + "integrity": "sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^4.0.3", + "@csstools/css-syntax-patches-for-csstree": "^1.0.14", + "css-tree": "^3.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/data-urls/-/data-urls-6.0.0.tgz", + "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/default-browser/-/default-browser-5.4.0.tgz", + "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.266", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.266.tgz", + "integrity": "sha512-kgWEglXvkEfMH7rxP5OSZZwnaDWT7J9EoZCujhnpLbfi0bbNtRkgdX2E3gt0Uer11c61qCYktB3hwkAS325sJg==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmmirror.com/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmmirror.com/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "27.2.0", + "resolved": "https://registry.npmmirror.com/jsdom/-/jsdom-27.2.0.tgz", + "integrity": "sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@acemir/cssom": "^0.9.23", + "@asamuzakjp/dom-selector": "^6.7.4", + "cssstyle": "^5.3.3", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmmirror.com/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmmirror.com/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinia": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz", + "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.7" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.5.0", + "vue": "^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmmirror.com/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.19", + "resolved": "https://registry.npmmirror.com/tldts/-/tldts-7.0.19.tgz", + "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.19" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.19", + "resolved": "https://registry.npmmirror.com/tldts-core/-/tldts-core-7.0.19.tgz", + "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==", + "dev": true, + "license": "MIT" + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.3.1.tgz", + "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", + "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "7.2.6", + "resolved": "https://registry.npmmirror.com/vite/-/vite-7.2.6.tgz", + "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-dev-rpc": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz", + "integrity": "sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==", + "dev": true, + "license": "MIT", + "dependencies": { + "birpc": "^2.4.0", + "vite-hot-client": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0" + } + }, + "node_modules/vite-hot-client": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/vite-hot-client/-/vite-hot-client-2.1.0.tgz", + "integrity": "sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vite-plugin-inspect": { + "version": "11.3.3", + "resolved": "https://registry.npmmirror.com/vite-plugin-inspect/-/vite-plugin-inspect-11.3.3.tgz", + "integrity": "sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^4.1.0", + "debug": "^4.4.1", + "error-stack-parser-es": "^1.0.5", + "ohash": "^2.0.11", + "open": "^10.2.0", + "perfect-debounce": "^2.0.0", + "sirv": "^3.0.1", + "unplugin-utils": "^0.3.0", + "vite-dev-rpc": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-inspect/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite-plugin-vue-devtools": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-8.0.5.tgz", + "integrity": "sha512-p619BlKFOqQXJ6uDWS1vUPQzuJOD6xJTfftj57JXBGoBD/yeQCowR7pnWcr/FEX4/HVkFbreI6w2uuGBmQOh6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-core": "^8.0.5", + "@vue/devtools-kit": "^8.0.5", + "@vue/devtools-shared": "^8.0.5", + "sirv": "^3.0.2", + "vite-plugin-inspect": "^11.3.3", + "vite-plugin-vue-inspector": "^5.3.2" + }, + "engines": { + "node": ">=v14.21.3" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/@vue/devtools-kit": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-8.0.5.tgz", + "integrity": "sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.0.5", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^2.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/@vue/devtools-shared": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-8.0.5.tgz", + "integrity": "sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite-plugin-vue-inspector": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.3.2.tgz", + "integrity": "sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.0", + "@babel/plugin-proposal-decorators": "^7.23.0", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-transform-typescript": "^7.22.15", + "@vue/babel-plugin-jsx": "^1.1.5", + "@vue/compiler-dom": "^3.3.4", + "kolorist": "^1.8.0", + "magic-string": "^0.30.4" + }, + "peerDependencies": { + "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vitest": { + "version": "4.0.15", + "resolved": "https://registry.npmmirror.com/vitest/-/vitest-4.0.15.tgz", + "integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.15", + "@vitest/mocker": "4.0.15", + "@vitest/pretty-format": "4.0.15", + "@vitest/runner": "4.0.15", + "@vitest/snapshot": "4.0.15", + "@vitest/spy": "4.0.15", + "@vitest/utils": "4.0.15", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.15", + "@vitest/browser-preview": "4.0.15", + "@vitest/browser-webdriverio": "4.0.15", + "@vitest/ui": "4.0.15", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz", + "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-sfc": "3.5.25", + "@vue/runtime-dom": "3.5.25", + "@vue/server-renderer": "3.5.25", + "@vue/shared": "3.5.25" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-component-type-helpers": { + "version": "2.2.12", + "resolved": "https://registry.npmmirror.com/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz", + "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-router": { + "version": "4.6.3", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.3.tgz", + "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-8.0.0.tgz", + "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "15.1.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmmirror.com/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/src/main/resources/app2/package.json b/src/main/resources/app2/package.json new file mode 100644 index 0000000..d6607da --- /dev/null +++ b/src/main/resources/app2/package.json @@ -0,0 +1,28 @@ +{ + "name": "app2", + "version": "0.0.0", + "private": true, + "type": "module", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "test:unit": "vitest" + }, + "dependencies": { + "pinia": "^3.0.4", + "vue": "^3.5.25", + "vue-router": "^4.6.3" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.2", + "@vue/test-utils": "^2.4.6", + "jsdom": "^27.2.0", + "vite": "^7.2.4", + "vite-plugin-vue-devtools": "^8.0.5", + "vitest": "^4.0.14" + } +} diff --git a/src/main/resources/app2/public/favicon.ico b/src/main/resources/app2/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/src/main/resources/app2/public/favicon.ico differ diff --git a/src/main/resources/app2/src/App.vue b/src/main/resources/app2/src/App.vue new file mode 100644 index 0000000..c9ad3e4 --- /dev/null +++ b/src/main/resources/app2/src/App.vue @@ -0,0 +1,70 @@ + + + + + + diff --git a/src/main/resources/app2/src/assets/base.css b/src/main/resources/app2/src/assets/base.css new file mode 100644 index 0000000..8816868 --- /dev/null +++ b/src/main/resources/app2/src/assets/base.css @@ -0,0 +1,86 @@ +/* color palette from */ +:root { + --vt-c-white: #ffffff; + --vt-c-white-soft: #f8f8f8; + --vt-c-white-mute: #f2f2f2; + + --vt-c-black: #181818; + --vt-c-black-soft: #222222; + --vt-c-black-mute: #282828; + + --vt-c-indigo: #2c3e50; + + --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); + + --vt-c-text-light-1: var(--vt-c-indigo); + --vt-c-text-light-2: rgba(60, 60, 60, 0.66); + --vt-c-text-dark-1: var(--vt-c-white); + --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); +} + +/* semantic color variables for this project */ +:root { + --color-background: var(--vt-c-white); + --color-background-soft: var(--vt-c-white-soft); + --color-background-mute: var(--vt-c-white-mute); + + --color-border: var(--vt-c-divider-light-2); + --color-border-hover: var(--vt-c-divider-light-1); + + --color-heading: var(--vt-c-text-light-1); + --color-text: var(--vt-c-text-light-1); + + --section-gap: 160px; +} + +@media (prefers-color-scheme: dark) { + :root { + --color-background: var(--vt-c-black); + --color-background-soft: var(--vt-c-black-soft); + --color-background-mute: var(--vt-c-black-mute); + + --color-border: var(--vt-c-divider-dark-2); + --color-border-hover: var(--vt-c-divider-dark-1); + + --color-heading: var(--vt-c-text-dark-1); + --color-text: var(--vt-c-text-dark-2); + } +} + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + font-weight: normal; +} + +body { + min-height: 100vh; + color: var(--color-text); + background: var(--color-background); + transition: + color 0.5s, + background-color 0.5s; + line-height: 1.6; + font-family: + Inter, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + Oxygen, + Ubuntu, + Cantarell, + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue', + sans-serif; + font-size: 15px; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/src/main/resources/app2/src/assets/logo.svg b/src/main/resources/app2/src/assets/logo.svg new file mode 100644 index 0000000..7565660 --- /dev/null +++ b/src/main/resources/app2/src/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/app2/src/assets/main.css b/src/main/resources/app2/src/assets/main.css new file mode 100644 index 0000000..1849fbc --- /dev/null +++ b/src/main/resources/app2/src/assets/main.css @@ -0,0 +1,138 @@ +/* 更新现有的 main.css */ +/* color palette from */ +:root { + --vt-c-white: #ffffff; + --vt-c-white-soft: #f8f8f8; + --vt-c-white-mute: #f2f2f2; + + --vt-c-black: #181818; + --vt-c-black-soft: #222222; + --vt-c-black-mute: #282828; + + --vt-c-indigo: #2c3e50; + + --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); + + --vt-c-text-light-1: var(--vt-c-indigo); + --vt-c-text-light-2: rgba(60, 60, 60, 0.66); + --vt-c-text-dark-1: var(--vt-c-white); + --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); +} + +/* semantic color variables for this project */ +:root { + --color-background: var(--vt-c-white); + --color-background-soft: var(--vt-c-white-soft); + --color-background-mute: var(--vt-c-white-mute); + + --color-border: var(--vt-c-divider-light-2); + --color-border-hover: var(--vt-c-divider-light-1); + + --color-heading: var(--vt-c-text-light-1); + --color-text: var(--vt-c-text-light-1); + + --section-gap: 160px; +} + +@media (prefers-color-scheme: dark) { + :root { + --color-background: var(--vt-c-black); + --color-background-soft: var(--vt-c-black-soft); + --color-background-mute: var(--vt-c-black-mute); + + --color-border: var(--vt-c-divider-dark-2); + --color-border-hover: var(--vt-c-divider-dark-1); + + --color-heading: var(--vt-c-text-dark-1); + --color-text: var(--vt-c-text-dark-2); + } +} + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + font-weight: normal; +} + +body { + min-height: 100vh; + color: var(--color-text); + background: var(--color-background); + transition: + color 0.5s, + background-color 0.5s; + line-height: 1.6; + font-family: + 'Inter', + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + Oxygen, + Ubuntu, + Cantarell, + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue', + sans-serif; + font-size: 15px; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + overflow-x: hidden; +} + +/* 移动端优化 */ +.no-scroll { + overflow: hidden; + position: fixed; + width: 100%; + height: 100%; +} + +/* 自定义滚动条 */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +/* 移动端点击效果优化 */ +@media (hover: none) and (pointer: coarse) { + * { + cursor: pointer; + } + + input, + textarea, + select, + button { + font-size: 16px !important; /* 防止iOS缩放 */ + } + + a, + button { + -webkit-tap-highlight-color: transparent; + } +} + +/* 修复移动端overflow-scrolling问题 */ +.element \ No newline at end of file diff --git a/src/main/resources/app2/src/assets/mobile.css b/src/main/resources/app2/src/assets/mobile.css new file mode 100644 index 0000000..c20bce9 --- /dev/null +++ b/src/main/resources/app2/src/assets/mobile.css @@ -0,0 +1,37 @@ +/* 移动端全局样式 */ +:root { + --mobile-width: 375px; + --mobile-height: 667px; + --primary-color: #1156b1; + --secondary-color: #81d3f8; + --success-color: #04d919; + --warning-color: #ff9800; + --danger-color: #f44336; +} + +/* 移动端基础样式 */ +#app { + width: 100%; + min-height: 100vh; + background: #f5f5f5; + overflow-x: hidden; +} + +/* 移动端容器 */ +.mobile-container { + max-width: var(--mobile-width); + min-height: var(--mobile-height); + margin: 0 auto; + background: white; + position: relative; + overflow: hidden; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); +} + +/* 安全区域适配 */ +.safe-area { + padding-top: env(safe-area-inset-top); + padding-bottom: env(safe-area-inset-bottom); + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); +} \ No newline at end of file diff --git a/src/main/resources/app2/src/components/HelloWorld.vue b/src/main/resources/app2/src/components/HelloWorld.vue new file mode 100644 index 0000000..eff59f1 --- /dev/null +++ b/src/main/resources/app2/src/components/HelloWorld.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/main/resources/app2/src/components/TheWelcome.vue b/src/main/resources/app2/src/components/TheWelcome.vue new file mode 100644 index 0000000..68a970a --- /dev/null +++ b/src/main/resources/app2/src/components/TheWelcome.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/main/resources/app2/src/components/WelcomeItem.vue b/src/main/resources/app2/src/components/WelcomeItem.vue new file mode 100644 index 0000000..ac366d0 --- /dev/null +++ b/src/main/resources/app2/src/components/WelcomeItem.vue @@ -0,0 +1,86 @@ + + + diff --git a/src/main/resources/app2/src/components/__tests__/HelloWorld.spec.js b/src/main/resources/app2/src/components/__tests__/HelloWorld.spec.js new file mode 100644 index 0000000..2533202 --- /dev/null +++ b/src/main/resources/app2/src/components/__tests__/HelloWorld.spec.js @@ -0,0 +1,11 @@ +import { describe, it, expect } from 'vitest' + +import { mount } from '@vue/test-utils' +import HelloWorld from '../HelloWorld.vue' + +describe('HelloWorld', () => { + it('renders properly', () => { + const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } }) + expect(wrapper.text()).toContain('Hello Vitest') + }) +}) diff --git a/src/main/resources/app2/src/components/icons/IconCommunity.vue b/src/main/resources/app2/src/components/icons/IconCommunity.vue new file mode 100644 index 0000000..2dc8b05 --- /dev/null +++ b/src/main/resources/app2/src/components/icons/IconCommunity.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/app2/src/components/icons/IconDocumentation.vue b/src/main/resources/app2/src/components/icons/IconDocumentation.vue new file mode 100644 index 0000000..6d4791c --- /dev/null +++ b/src/main/resources/app2/src/components/icons/IconDocumentation.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/app2/src/components/icons/IconEcosystem.vue b/src/main/resources/app2/src/components/icons/IconEcosystem.vue new file mode 100644 index 0000000..c3a4f07 --- /dev/null +++ b/src/main/resources/app2/src/components/icons/IconEcosystem.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/app2/src/components/icons/IconSupport.vue b/src/main/resources/app2/src/components/icons/IconSupport.vue new file mode 100644 index 0000000..7452834 --- /dev/null +++ b/src/main/resources/app2/src/components/icons/IconSupport.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/app2/src/components/icons/IconTooling.vue b/src/main/resources/app2/src/components/icons/IconTooling.vue new file mode 100644 index 0000000..660598d --- /dev/null +++ b/src/main/resources/app2/src/components/icons/IconTooling.vue @@ -0,0 +1,19 @@ + + diff --git a/src/main/resources/app2/src/main.js b/src/main/resources/app2/src/main.js new file mode 100644 index 0000000..bb4cef0 --- /dev/null +++ b/src/main/resources/app2/src/main.js @@ -0,0 +1,17 @@ +// src/main.js +import './assets/main.css' +import './assets/mobile.css' + +import { createApp } from 'vue' +import { createPinia } from 'pinia' + +import App from './App.vue' +import router from './router' +import './router/permission' // 确保引入了路由守卫 + +const app = createApp(App) + +app.use(createPinia()) +app.use(router) + +app.mount('#app') diff --git a/src/main/resources/app2/src/router/index.js b/src/main/resources/app2/src/router/index.js new file mode 100644 index 0000000..b33adec --- /dev/null +++ b/src/main/resources/app2/src/router/index.js @@ -0,0 +1,39 @@ +import { createRouter, createWebHistory } from 'vue-router' + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: [ + { + path: '/', + name: 'StudentLoginPage', + component: () => import('../views/StudentLoginPage.vue') + }, + { + path: '/home', + name: 'HomePage', + component: () => import('../views/HomePage.vue') + }, + { + path: '/water-quality', + name: 'WaterQuality', + component: () => import('../views/WaterQualityPage.vue') + }, + { + path: '/scan', + name: 'ScanPage', + component: () => import('../views/ScanPage.vue') + }, + { + path: '/profile', + name: 'ProfilePage', + component: () => import('../views/ProfilePage.vue') + }, + { + path: '/history', + name: 'HistoryPage', + component: () => import('../views/HistoryPage.vue') + } + ] +}) + +export default router \ No newline at end of file diff --git a/src/main/resources/app2/src/router/permission.js b/src/main/resources/app2/src/router/permission.js new file mode 100644 index 0000000..482c2a5 --- /dev/null +++ b/src/main/resources/app2/src/router/permission.js @@ -0,0 +1,30 @@ +// src/router/permission.js +import router from './index' + +// 不需要登录的白名单路由 +const whiteList = ['/', '/login', '/register'] + +router.beforeEach((to, from, next) => { + // 检查是否有有效的token + const token = localStorage.getItem('token') + + if (token) { + // 有token的情况下 + if (to.path === '/' || to.path === '/login') { + // 已登录用户访问登录页时重定向到首页 + next('/home') + } else { + // 访问其他需要权限的页面,允许通过 + next() + } + } else { + // 没有token的情况下 + if (whiteList.includes(to.path)) { + // 白名单路由可以直接访问 + next() + } else { + // 非白名单路由重定向到登录页 + next('/') + } + } +}) diff --git a/src/main/resources/app2/src/services/api.js b/src/main/resources/app2/src/services/api.js new file mode 100644 index 0000000..8df6d7e --- /dev/null +++ b/src/main/resources/app2/src/services/api.js @@ -0,0 +1,45 @@ +// src/services/api.js +import axios from 'axios' + +const apiClient = axios.create({ + baseURL: 'http://localhost:8080', + headers: { + 'Content-Type': 'application/json' + } +}) + +// 请求拦截器 +apiClient.interceptors.request.use( + (config) => { + // 从本地存储获取token + const token = localStorage.getItem('token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + (error) => { + return Promise.reject(error) + } +) + +// 响应拦截器 +apiClient.interceptors.response.use( + (response) => { + return response + }, + (error) => { + if (error.response?.status === 401) { + // token过期或无效,清除用户信息并跳转到登录页 + localStorage.removeItem('token') + localStorage.removeItem('userId') + localStorage.removeItem('username') + localStorage.removeItem('userType') + localStorage.removeItem('studentId') + window.location.href = '/' + } + return Promise.reject(error) + } +) + +export default apiClient diff --git a/src/main/resources/app2/src/services/authServices.js b/src/main/resources/app2/src/services/authServices.js new file mode 100644 index 0000000..8174f90 --- /dev/null +++ b/src/main/resources/app2/src/services/authServices.js @@ -0,0 +1,58 @@ +// src/services/authServices.js +import api from './api' + +export const authServices = { + // 通用登录接口 + async login(loginData) { + try { + const response = await api.post('/api/common/login', loginData) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + }, + + // 学生登录接口 + async studentLogin(studentId, password) { + try { + const loginData = { + username: studentId, + password: password, + userType: 'user' // 注意:后端中用户类型是"user" + } + const response = await api.post('/api/common/login', loginData) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + }, + + // 修改:学生注册接口 - 使用正确的路径和字段 + async studentRegister(registerData) { + try { + // 构建符合RegisterRequest的数据 + const requestData = { + username: registerData.name, // 用户名(学生姓名) + password: registerData.password, // 密码 + userType: 'user', // 固定为用户类型 + studentId: registerData.studentId, // 学号 + studentName: registerData.name // 学生姓名 + // 注意:后端RegisterRequest目前只支持这些字段 + // 手机号、邮箱等需要扩展User实体和RegisterRequest + } + + console.log('发送注册数据到后端:', requestData) + const response = await api.post('/api/common/register', requestData) + console.log('后端注册响应:', response.data) + return response.data + } catch (error) { + console.error('注册API错误:', error) + // 返回更详细的错误信息 + const errorData = error.response?.data || { + code: 500, + message: error.message || '网络错误' + } + throw errorData + } + } +} \ No newline at end of file diff --git a/src/main/resources/app2/src/services/deviceService.js b/src/main/resources/app2/src/services/deviceService.js new file mode 100644 index 0000000..932cf1c --- /dev/null +++ b/src/main/resources/app2/src/services/deviceService.js @@ -0,0 +1,32 @@ +// src/services/deviceService.js +import api from './api' + +export const deviceService = { + // 获取终端设备信息 + async getTerminalInfo(terminalId) { + try { + const response = await api.get(`/api/water/terminal/${terminalId}`) + return response.data + } catch (error) { + // 更好的错误处理 + if (error.response?.status === 403) { + console.error('权限不足,请重新登录') + // 可以在这里触发重新登录逻辑 + } + throw error.response?.data || error.message + } + }, + + // 获取水质信息 + async getWaterQualityInfo(deviceId) { + try { + const response = await api.get(`/api/water/quality/${deviceId}`) + return response.data + } catch (error) { + if (error.response?.status === 403) { + console.error('权限不足,无法获取水质信息') + } + throw error.response?.data || error.message + } + } +} diff --git a/src/main/resources/app2/src/stores/auth.js b/src/main/resources/app2/src/stores/auth.js new file mode 100644 index 0000000..a06e5c8 --- /dev/null +++ b/src/main/resources/app2/src/stores/auth.js @@ -0,0 +1,30 @@ +// src/stores/auth.js +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' // 添加 computed 导入 + +export const useAuthStore = defineStore('auth', () => { + const user = ref(null) + const token = ref(localStorage.getItem('token')) + + const login = (userData, authToken) => { + user.value = userData + token.value = authToken + localStorage.setItem('token', authToken) + } + + const logout = () => { + user.value = null + token.value = null + localStorage.removeItem('token') + } + + const isAuthenticated = computed(() => !!token.value) // 使用 computed 包装 + + return { + user, + token, + isAuthenticated, + login, + logout + } +}) diff --git a/src/main/resources/app2/src/stores/counter.js b/src/main/resources/app2/src/stores/counter.js new file mode 100644 index 0000000..b6757ba --- /dev/null +++ b/src/main/resources/app2/src/stores/counter.js @@ -0,0 +1,12 @@ +import { ref, computed } from 'vue' +import { defineStore } from 'pinia' + +export const useCounterStore = defineStore('counter', () => { + const count = ref(0) + const doubleCount = computed(() => count.value * 2) + function increment() { + count.value++ + } + + return { count, doubleCount, increment } +}) diff --git a/src/main/resources/app2/src/stores/user.js b/src/main/resources/app2/src/stores/user.js new file mode 100644 index 0000000..82c871a --- /dev/null +++ b/src/main/resources/app2/src/stores/user.js @@ -0,0 +1,67 @@ +// src/stores/user.js +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' + +export const useUserStore = defineStore('user', () => { + const token = ref(localStorage.getItem('token') || '') + const userId = ref(localStorage.getItem('userId') || '') + const username = ref(localStorage.getItem('username') || '') + const userType = ref(localStorage.getItem('userType') || '') + const studentId = ref(localStorage.getItem('studentId') || '') + + // 计算属性:是否已登录 + const isLoggedIn = computed(() => !!token.value) + + // 设置用户信息 + const setUser = (userInfo) => { + token.value = userInfo.token + userId.value = userInfo.userId + username.value = userInfo.username + userType.value = userInfo.userType + studentId.value = userInfo.studentId + + // 保存到本地存储 + localStorage.setItem('token', userInfo.token) + localStorage.setItem('userId', userInfo.userId) + localStorage.setItem('username', userInfo.username) + localStorage.setItem('userType', userInfo.userType) + localStorage.setItem('studentId', userInfo.studentId) + } + + // 清除用户信息 + const clearUser = () => { + token.value = '' + userId.value = '' + username.value = '' + userType.value = '' + studentId.value = '' + + // 清除本地存储 + localStorage.removeItem('token') + localStorage.removeItem('userId') + localStorage.removeItem('username') + localStorage.removeItem('userType') + localStorage.removeItem('studentId') + } + + // 从本地存储初始化用户信息 + const initFromStorage = () => { + token.value = localStorage.getItem('token') || '' + userId.value = localStorage.getItem('userId') || '' + username.value = localStorage.getItem('username') || '' + userType.value = localStorage.getItem('userType') || '' + studentId.value = localStorage.getItem('studentId') || '' + } + + return { + token, + userId, + username, + userType, + studentId, + isLoggedIn, + setUser, + clearUser, + initFromStorage + } +}) diff --git a/src/main/resources/app2/src/views/HistoryPage.vue b/src/main/resources/app2/src/views/HistoryPage.vue new file mode 100644 index 0000000..4e88a75 --- /dev/null +++ b/src/main/resources/app2/src/views/HistoryPage.vue @@ -0,0 +1,501 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/app2/src/views/HomePage.vue b/src/main/resources/app2/src/views/HomePage.vue new file mode 100644 index 0000000..8de16b2 --- /dev/null +++ b/src/main/resources/app2/src/views/HomePage.vue @@ -0,0 +1,1348 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/app2/src/views/ProfilePage.vue b/src/main/resources/app2/src/views/ProfilePage.vue new file mode 100644 index 0000000..0669cfb --- /dev/null +++ b/src/main/resources/app2/src/views/ProfilePage.vue @@ -0,0 +1,679 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/app2/src/views/ScanPage.vue b/src/main/resources/app2/src/views/ScanPage.vue new file mode 100644 index 0000000..4b84de0 --- /dev/null +++ b/src/main/resources/app2/src/views/ScanPage.vue @@ -0,0 +1,821 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/app2/src/views/StudentLoginPage.vue b/src/main/resources/app2/src/views/StudentLoginPage.vue new file mode 100644 index 0000000..664407e --- /dev/null +++ b/src/main/resources/app2/src/views/StudentLoginPage.vue @@ -0,0 +1,761 @@ + + + + + + + diff --git a/src/main/resources/app2/src/views/WaterQualityPage.vue b/src/main/resources/app2/src/views/WaterQualityPage.vue new file mode 100644 index 0000000..d5bad32 --- /dev/null +++ b/src/main/resources/app2/src/views/WaterQualityPage.vue @@ -0,0 +1,535 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/app2/vite.config.js b/src/main/resources/app2/vite.config.js new file mode 100644 index 0000000..4217010 --- /dev/null +++ b/src/main/resources/app2/vite.config.js @@ -0,0 +1,18 @@ +import { fileURLToPath, URL } from 'node:url' + +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import vueDevTools from 'vite-plugin-vue-devtools' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + vueDevTools(), + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + }, + }, +}) diff --git a/src/main/resources/app2/vitest.config.js b/src/main/resources/app2/vitest.config.js new file mode 100644 index 0000000..c328717 --- /dev/null +++ b/src/main/resources/app2/vitest.config.js @@ -0,0 +1,14 @@ +import { fileURLToPath } from 'node:url' +import { mergeConfig, defineConfig, configDefaults } from 'vitest/config' +import viteConfig from './vite.config' + +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + environment: 'jsdom', + exclude: [...configDefaults.exclude, 'e2e/**'], + root: fileURLToPath(new URL('./', import.meta.url)), + }, + }), +) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..948eb44 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,38 @@ +# MQTT 配置(是否启用 + 连接参数) +mqtt: + enabled: true # 是否启用 MQTT 客户端 + +jwt: + secret: "789&kLp23$87bnM90!789poI87&90lkJ78*90jhG78!90fdS78%90saD78^90xcV78&90zbN78!这是安全密钥1234567890" + expiration: 86400000 + +# Spring 核心配置:允许 Bean 定义覆盖(解决 Bean 重复定义冲突) +spring: + main: + allow-bean-definition-overriding: true # 缩进在 spring 下,作为子配置 + # 数据库配置 + datasource: + url: jdbc:mysql://120.46.151.248:3306/campus_water_management?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: member + password: Hdp11008@ + driver-class-name: com.mysql.cj.jdbc.Driver + + # JPA 配置 + jpa: + hibernate: + ddl-auto: update # 自动更新表结构(开发环境用,生产环境建议改为 none) + show-sql: true # 打印 SQL 语句 + properties: + hibernate: + format_sql: true # 格式化 SQL 语句 + jdbc.lob.non_contextual_creation: true # 解决 LOB 字段创建警告 + dialect: org.hibernate.dialect.MySQL8Dialect # MySQL 8 方言 + +# 服务器编码配置 +server: + servlet: + encoding: + charset: UTF-8 + enabled: true + force: true + port: 8080 \ No newline at end of file diff --git a/src/main/resources/package-lock.json b/src/main/resources/package-lock.json new file mode 100644 index 0000000..3f16532 --- /dev/null +++ b/src/main/resources/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "resources", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/src/main/resources/web/.env b/src/main/resources/web/.env new file mode 100644 index 0000000..3a6da93 --- /dev/null +++ b/src/main/resources/web/.env @@ -0,0 +1,2 @@ +VITE_API_BASE_URL=http://localhost:8080 +VITE_APP_ORIGIN=http://localhost:5173 \ No newline at end of file diff --git a/src/main/resources/web/.env.development b/src/main/resources/web/.env.development new file mode 100644 index 0000000..0583bfc --- /dev/null +++ b/src/main/resources/web/.env.development @@ -0,0 +1 @@ +VITE_API_BASE_URL=http://localhost:8080 \ No newline at end of file diff --git a/src/main/resources/web/.gitignore b/src/main/resources/web/.gitignore new file mode 100644 index 0000000..a3f7a51 --- /dev/null +++ b/src/main/resources/web/.gitignore @@ -0,0 +1,36 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo + +.eslintcache + +# Cypress +/cypress/videos/ +/cypress/screenshots/ + +# Vitest +__screenshots__/ diff --git a/src/main/resources/web/.vscode/extensions.json b/src/main/resources/web/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/src/main/resources/web/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/src/main/resources/web/README.md b/src/main/resources/web/README.md new file mode 100644 index 0000000..7f22af4 --- /dev/null +++ b/src/main/resources/web/README.md @@ -0,0 +1,42 @@ +# Web + +This template should help get you started developing with Vue 3 in Vite. + +## Recommended IDE Setup + +[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). + +## Recommended Browser Setup + +- Chromium-based browsers (Chrome, Edge, Brave, etc.): + - [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd) + - [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters) +- Firefox: + - [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/) + - [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/) + +## Type Support for `.vue` Imports in TS + +TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types. + +## Customize configuration + +See [Vite Configuration Reference](https://vite.dev/config/). + +## Project Setup + +```sh +npm install +``` + +### Compile and Hot-Reload for Development + +```sh +npm run dev +``` + +### Type-Check, Compile and Minify for Production + +```sh +npm run build +``` diff --git a/src/main/resources/web/env.d.ts b/src/main/resources/web/env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/main/resources/web/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/src/main/resources/web/index.html b/src/main/resources/web/index.html new file mode 100644 index 0000000..9e5fc8f --- /dev/null +++ b/src/main/resources/web/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/src/main/resources/web/package-lock.json b/src/main/resources/web/package-lock.json new file mode 100644 index 0000000..23ec651 --- /dev/null +++ b/src/main/resources/web/package-lock.json @@ -0,0 +1,1422 @@ +{ + "name": "web", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web", + "version": "0.0.0", + "dependencies": { + "pinia": "^2.1.7", + "vue": "^3.4.21", + "vue-router": "^4.3.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "typescript": "^5.2.2", + "vite": "^5.1.6", + "vue-tsc": "^1.8.27" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.11.1.tgz", + "integrity": "sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "1.11.1" + } + }, + "node_modules/@volar/source-map": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.11.1.tgz", + "integrity": "sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "muggle-string": "^0.3.1" + } + }, + "node_modules/@volar/typescript": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.11.1.tgz", + "integrity": "sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "1.11.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz", + "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.25", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz", + "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz", + "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.25", + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz", + "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/language-core": { + "version": "1.8.27", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.27.tgz", + "integrity": "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~1.11.1", + "@volar/source-map": "~1.11.1", + "@vue/compiler-dom": "^3.3.0", + "@vue/shared": "^3.3.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "muggle-string": "^0.3.1", + "path-browserify": "^1.0.1", + "vue-template-compiler": "^2.7.14" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.25.tgz", + "integrity": "sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.25.tgz", + "integrity": "sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.25.tgz", + "integrity": "sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.25", + "@vue/runtime-core": "3.5.25", + "@vue/shared": "3.5.25", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.25.tgz", + "integrity": "sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25" + }, + "peerDependencies": { + "vue": "3.5.25" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.25.tgz", + "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/muggle-string": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz", + "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/pinia": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.25.tgz", + "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-sfc": "3.5.25", + "@vue/runtime-dom": "3.5.25", + "@vue/server-renderer": "3.5.25", + "@vue/shared": "3.5.25" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz", + "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "1.8.27", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.27.tgz", + "integrity": "sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "~1.11.1", + "@vue/language-core": "1.8.27", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": "*" + } + } + } +} diff --git a/src/main/resources/web/package.json b/src/main/resources/web/package.json new file mode 100644 index 0000000..9d08c5e --- /dev/null +++ b/src/main/resources/web/package.json @@ -0,0 +1,22 @@ +{ + "name": "web", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "pinia": "^2.1.7", + "vue": "^3.4.21", + "vue-router": "^4.3.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "typescript": "^5.2.2", + "vite": "^5.1.6", + "vue-tsc": "^1.8.27" + } +} diff --git a/src/main/resources/web/public/favicon.ico b/src/main/resources/web/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/src/main/resources/web/public/favicon.ico differ diff --git a/src/main/resources/web/src/App.vue b/src/main/resources/web/src/App.vue new file mode 100644 index 0000000..401fdbd --- /dev/null +++ b/src/main/resources/web/src/App.vue @@ -0,0 +1,220 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/api/auth.ts b/src/main/resources/web/src/api/auth.ts new file mode 100644 index 0000000..dc8857c --- /dev/null +++ b/src/main/resources/web/src/api/auth.ts @@ -0,0 +1,61 @@ +// 替换原文件内容 +import type { LoginRequest, LoginResponse, LoginVO } from './types/auth' + +// 真实的登录API调用 +export const realLoginApi = async (data: LoginRequest): Promise => { + const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080' + + console.log('🌐 调用登录接口:', `${API_BASE_URL}/api/common/login`) + console.log('📤 请求数据:', data) + + try { + const response = await fetch(`${API_BASE_URL}/api/common/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + + console.log('📥 响应状态:', response.status, response.statusText) + + if (!response.ok) { + const errorText = await response.text() + console.error('❌ 响应内容:', errorText) + throw new Error(`网络请求失败: ${response.status} ${response.statusText}`) + } + + const result: LoginResponse = await response.json() + console.log('✅ 登录响应:', result) + + return result + + } catch (error: any) { + console.error('❌ 登录接口调用失败:', error) + throw new Error(`登录失败: ${error.message}`) + } +} + +// 备用模拟登录 +export const mockLoginApi = async (data: LoginRequest): Promise => { + await new Promise(resolve => setTimeout(resolve, 1000)) + + if (data.username === 'admin' && data.password === '123456') { + return { + code: 200, + message: '登录成功', + data: { + token: 'mock-jwt-token-' + Date.now(), + userInfo: { + id: 1, + username: 'admin', + realName: '张管理员', + role: 'admin', + avatar: '' + } + } + } + } else { + throw new Error('用户名或密码错误') + } +} \ No newline at end of file diff --git a/src/main/resources/web/src/api/deviceStatus.ts b/src/main/resources/web/src/api/deviceStatus.ts new file mode 100644 index 0000000..8c11722 --- /dev/null +++ b/src/main/resources/web/src/api/deviceStatus.ts @@ -0,0 +1,50 @@ +// src/api/deviceStatus.ts +import axios from 'axios' + +export const DeviceStatusApi = { + // 获取设备状态列表 - 修改为匹配后端实际接口 + getDevicesByStatus: async (status: string, areaId?: string, deviceType?: string) => { + try { + const params: any = { status } + if (areaId) params.areaId = areaId + if (deviceType) params.deviceType = deviceType + + const response = await axios.get('/api/web/device-status/by-status', { params }) + return response.data + } catch (error) { + throw new Error(`获取设备列表失败: ${error}`) + } + }, + + // 标记设备在线 + markDeviceOnline: async (deviceId: string) => { + try { + const response = await axios.post(`/api/web/device-status/${deviceId}/online`) + return response.data + } catch (error) { + throw new Error(`设置设备在线失败: ${error}`) + } + }, + + // 标记设备离线 + markDeviceOffline: async (deviceId: string, reason?: string) => { + try { + const params = reason ? { reason } : {} + const response = await axios.post(`/api/web/device-status/${deviceId}/offline`, null, { params }) + return response.data + } catch (error) { + throw new Error(`设置设备离线失败: ${error}`) + } + }, + + // 标记设备故障 + markDeviceFault: async (deviceId: string, faultType: string, description: string) => { + try { + const params = { faultType, description } + const response = await axios.post(`/api/web/device-status/${deviceId}/fault`, null, { params }) + return response.data + } catch (error) { + throw new Error(`设置设备故障失败: ${error}`) + } + } +} diff --git a/src/main/resources/web/src/api/modules/auth.ts b/src/main/resources/web/src/api/modules/auth.ts new file mode 100644 index 0000000..4001362 --- /dev/null +++ b/src/main/resources/web/src/api/modules/auth.ts @@ -0,0 +1,15 @@ +// src/api/modules/auth.ts +import { api } from '../request' +import type { LoginRequest, LoginVO, ResultVO } from '../types/auth' + +class AuthApi { + /** + * 用户登录 + */ + async login(data: LoginRequest): Promise> { + console.log('🚀 调用登录接口:', data) + return api.post>('/api/common/login', data) + } +} + +export const authApi = new AuthApi() \ No newline at end of file diff --git a/src/main/resources/web/src/api/request.ts b/src/main/resources/web/src/api/request.ts new file mode 100644 index 0000000..8ef72c2 --- /dev/null +++ b/src/main/resources/web/src/api/request.ts @@ -0,0 +1,152 @@ +// src/api/request.ts +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080' + +// 统一的 fetch 封装 +export async function request( + url: string, + options: RequestInit = {} +): Promise { + // 处理日志数据,GET/HEAD 方法不显示 body + const method = options.method?.toUpperCase() || 'GET'; + const logData: Record = { + method, + headers: options.headers, + }; + + // 只对非 GET/HEAD 方法显示 body + if (!['GET', 'HEAD'].includes(method)) { + logData.body = options.body ? JSON.parse(options.body as string) : undefined; + } + + console.log(`🌐 发送请求: ${API_BASE_URL}${url}`, logData) + + const defaultOptions: RequestInit = { + headers: { + 'Content-Type': 'application/json', + ...(options.headers || {}), + }, + } + + // 确保登录请求不携带任何认证信息 + const isLoginRequest = url.includes('/login') + if (isLoginRequest) { + console.log('🔐 这是登录请求,不携带认证头') + // 确保没有 Authorization header + const headers = new Headers(defaultOptions.headers) + headers.delete('Authorization') + headers.delete('authorization') + defaultOptions.headers = headers + } else { + // 非登录请求,从存储中获取 token + const token = localStorage.getItem('token') || sessionStorage.getItem('token') + if (token) { + const headers = new Headers(defaultOptions.headers) + headers.set('Authorization', `Bearer ${token}`) + defaultOptions.headers = headers + } + } + + try { + // 确保 GET/HEAD 请求不包含 body + const fetchOptions = { ...defaultOptions, ...options }; + if (['GET', 'HEAD'].includes(method)) { + delete fetchOptions.body; + } + + const response = await fetch(`${API_BASE_URL}${url}`, fetchOptions) + + console.log('📥 响应状态:', response.status, response.statusText) + + // 尝试读取响应文本(无论成功与否) + let responseText = '' + try { + responseText = await response.text() + console.log('📥 响应内容:', responseText) + } catch (e) { + console.log('📥 无法读取响应文本') + } + + if (!response.ok) { + let errorMessage = `HTTP ${response.status}: ${response.statusText}` + if (responseText) { + try { + const errorJson = JSON.parse(responseText) + errorMessage = errorJson.message || errorJson.error || errorMessage + } catch { + errorMessage = `${errorMessage}\n${responseText}` + } + } + + console.error('❌ 请求失败:', errorMessage) + throw new Error(errorMessage) + } + + // 尝试解析 JSON + if (responseText) { + try { + const data = JSON.parse(responseText) + console.log('✅ 解析成功的数据:', data) + return data + } catch (e) { + console.error('❌ JSON 解析失败:', e) + throw new Error(`响应不是有效的 JSON: ${responseText}`) + } + } else { + // 没有响应体的情况(如 204 No Content) + return {} as T + } + } catch (error: any) { + console.error('❌ 请求异常:', error) + + // 处理网络错误 + if (error.name === 'TypeError' && error.message.includes('fetch')) { + throw new Error('网络连接失败,请检查网络设置和后端服务') + } + + throw error + } +} + +// 封装常用 HTTP 方法 +export const api = { + get(url: string) { + return request(url, { method: 'GET' }) + }, + + post(url: string, data?: any) { + return request(url, { + method: 'POST', + body: data ? JSON.stringify(data) : undefined, + }) + }, + + put(url: string, data?: any) { + return request(url, { + method: 'PUT', + body: data ? JSON.stringify(data) : undefined, + }) + }, + + patch(url: string, data?: any) { + return request(url, { + method: 'PATCH', + body: data ? JSON.stringify(data) : undefined, + }) + }, + + delete(url: string) { + return request(url, { method: 'DELETE' }) + }, + + upload(url: string, formData: FormData) { + const headers = new Headers() + // 上传文件时不要设置 Content-Type,浏览器会自动设置 + headers.delete('Content-Type') + + return request(url, { + method: 'POST', + headers, + body: formData, + }) + }, +} \ No newline at end of file diff --git a/src/main/resources/web/src/api/types/auth.ts b/src/main/resources/web/src/api/types/auth.ts new file mode 100644 index 0000000..92b8e1a --- /dev/null +++ b/src/main/resources/web/src/api/types/auth.ts @@ -0,0 +1,29 @@ +// src/api/types/auth.ts + +// 登录请求参数 - 匹配后端的 LoginRequest +export interface LoginRequest { + username: string + password: string + userType: string // 添加这个属性 + rememberMe?: boolean +} + +// 通用响应结构 +export interface ResultVO { + code: number + message: string + data: T +} + +// 登录响应数据 - 匹配后端的 LoginVO +export interface LoginVO { + token: string + userInfo: { + id: number + username: string + realName?: string // 根据后端字段调整 + role?: string // 根据后端字段调整 + userType?: string // 添加这个字段 + avatar?: string + } +} \ No newline at end of file diff --git a/src/main/resources/web/src/api/types/repairman.ts b/src/main/resources/web/src/api/types/repairman.ts new file mode 100644 index 0000000..3f7636f --- /dev/null +++ b/src/main/resources/web/src/api/types/repairman.ts @@ -0,0 +1,2 @@ + // src/api/types/repairman.ts + export type RepairmanStatus = 'idle' | 'busy' | 'vacation' diff --git a/src/main/resources/web/src/api/types/workorder.ts b/src/main/resources/web/src/api/types/workorder.ts new file mode 100644 index 0000000..a91c25c --- /dev/null +++ b/src/main/resources/web/src/api/types/workorder.ts @@ -0,0 +1,10 @@ +// src/api/types/workorder.ts +export interface WorkOrder { + orderId: string + deviceId: string + areaId: string + description: string + status: 'pending' | 'processing' | 'reviewing' | 'completed' | 'timeout' + createdTime?: string + assignedRepairmanId?: string +} diff --git a/src/main/resources/web/src/assets/base.css b/src/main/resources/web/src/assets/base.css new file mode 100644 index 0000000..8816868 --- /dev/null +++ b/src/main/resources/web/src/assets/base.css @@ -0,0 +1,86 @@ +/* color palette from */ +:root { + --vt-c-white: #ffffff; + --vt-c-white-soft: #f8f8f8; + --vt-c-white-mute: #f2f2f2; + + --vt-c-black: #181818; + --vt-c-black-soft: #222222; + --vt-c-black-mute: #282828; + + --vt-c-indigo: #2c3e50; + + --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); + + --vt-c-text-light-1: var(--vt-c-indigo); + --vt-c-text-light-2: rgba(60, 60, 60, 0.66); + --vt-c-text-dark-1: var(--vt-c-white); + --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); +} + +/* semantic color variables for this project */ +:root { + --color-background: var(--vt-c-white); + --color-background-soft: var(--vt-c-white-soft); + --color-background-mute: var(--vt-c-white-mute); + + --color-border: var(--vt-c-divider-light-2); + --color-border-hover: var(--vt-c-divider-light-1); + + --color-heading: var(--vt-c-text-light-1); + --color-text: var(--vt-c-text-light-1); + + --section-gap: 160px; +} + +@media (prefers-color-scheme: dark) { + :root { + --color-background: var(--vt-c-black); + --color-background-soft: var(--vt-c-black-soft); + --color-background-mute: var(--vt-c-black-mute); + + --color-border: var(--vt-c-divider-dark-2); + --color-border-hover: var(--vt-c-divider-dark-1); + + --color-heading: var(--vt-c-text-dark-1); + --color-text: var(--vt-c-text-dark-2); + } +} + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + font-weight: normal; +} + +body { + min-height: 100vh; + color: var(--color-text); + background: var(--color-background); + transition: + color 0.5s, + background-color 0.5s; + line-height: 1.6; + font-family: + Inter, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + Oxygen, + Ubuntu, + Cantarell, + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue', + sans-serif; + font-size: 15px; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/src/main/resources/web/src/assets/logo.svg b/src/main/resources/web/src/assets/logo.svg new file mode 100644 index 0000000..7565660 --- /dev/null +++ b/src/main/resources/web/src/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/web/src/assets/main.css b/src/main/resources/web/src/assets/main.css new file mode 100644 index 0000000..a2f6982 --- /dev/null +++ b/src/main/resources/web/src/assets/main.css @@ -0,0 +1,52 @@ +/* src/assets/main.css */ +@import './base.css'; + +/* 只为特定页面保留这些样式,登录页面不需要 */ +.home-page #app, +.about-page #app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + font-weight: normal; +} + +a, +.green { + text-decoration: none; + color: hsla(160, 100%, 37%, 1); + transition: 0.4s; + padding: 3px; +} + +@media (hover: hover) { + a:hover { + background-color: hsla(160, 100%, 37%, 0.2); + } +} + +@media (min-width: 1024px) { + .home-page body, + .about-page body { + display: flex; + place-items: center; + } + + .home-page #app, + .about-page #app { + display: grid; + grid-template-columns: 1fr 1fr; + padding: 0 2rem; + } +} + +/* 登录页面专用重置 */ +.login-page #app { + max-width: none !important; + margin: 0 !important; + padding: 0 !important; + display: block !important; +} + +.login-page body { + display: block !important; +} \ No newline at end of file diff --git a/src/main/resources/web/src/components/HelloWorld.vue b/src/main/resources/web/src/components/HelloWorld.vue new file mode 100644 index 0000000..d174cf8 --- /dev/null +++ b/src/main/resources/web/src/components/HelloWorld.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/main/resources/web/src/components/TheWelcome.vue b/src/main/resources/web/src/components/TheWelcome.vue new file mode 100644 index 0000000..8b731d9 --- /dev/null +++ b/src/main/resources/web/src/components/TheWelcome.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/main/resources/web/src/components/WelcomeItem.vue b/src/main/resources/web/src/components/WelcomeItem.vue new file mode 100644 index 0000000..6d7086a --- /dev/null +++ b/src/main/resources/web/src/components/WelcomeItem.vue @@ -0,0 +1,87 @@ + + + diff --git a/src/main/resources/web/src/components/icons/IconCommunity.vue b/src/main/resources/web/src/components/icons/IconCommunity.vue new file mode 100644 index 0000000..2dc8b05 --- /dev/null +++ b/src/main/resources/web/src/components/icons/IconCommunity.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/web/src/components/icons/IconDocumentation.vue b/src/main/resources/web/src/components/icons/IconDocumentation.vue new file mode 100644 index 0000000..6d4791c --- /dev/null +++ b/src/main/resources/web/src/components/icons/IconDocumentation.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/web/src/components/icons/IconEcosystem.vue b/src/main/resources/web/src/components/icons/IconEcosystem.vue new file mode 100644 index 0000000..c3a4f07 --- /dev/null +++ b/src/main/resources/web/src/components/icons/IconEcosystem.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/web/src/components/icons/IconSupport.vue b/src/main/resources/web/src/components/icons/IconSupport.vue new file mode 100644 index 0000000..7452834 --- /dev/null +++ b/src/main/resources/web/src/components/icons/IconSupport.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/web/src/components/icons/IconTooling.vue b/src/main/resources/web/src/components/icons/IconTooling.vue new file mode 100644 index 0000000..660598d --- /dev/null +++ b/src/main/resources/web/src/components/icons/IconTooling.vue @@ -0,0 +1,19 @@ + + diff --git a/src/main/resources/web/src/components/layout/AppHeader.vue b/src/main/resources/web/src/components/layout/AppHeader.vue new file mode 100644 index 0000000..f2fb007 --- /dev/null +++ b/src/main/resources/web/src/components/layout/AppHeader.vue @@ -0,0 +1,87 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/components/layout/AppSidebar.vue b/src/main/resources/web/src/components/layout/AppSidebar.vue new file mode 100644 index 0000000..a214368 --- /dev/null +++ b/src/main/resources/web/src/components/layout/AppSidebar.vue @@ -0,0 +1,125 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/components/layout/MainLayout.vue b/src/main/resources/web/src/components/layout/MainLayout.vue new file mode 100644 index 0000000..01486df --- /dev/null +++ b/src/main/resources/web/src/components/layout/MainLayout.vue @@ -0,0 +1,40 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/components/layout/SidebarItem.vue b/src/main/resources/web/src/components/layout/SidebarItem.vue new file mode 100644 index 0000000..f54e917 --- /dev/null +++ b/src/main/resources/web/src/components/layout/SidebarItem.vue @@ -0,0 +1,163 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/env.d.ts b/src/main/resources/web/src/env.d.ts new file mode 100644 index 0000000..7120c18 --- /dev/null +++ b/src/main/resources/web/src/env.d.ts @@ -0,0 +1,11 @@ +/// + +interface ImportMetaEnv { + readonly VITE_API_BASE_URL: string + readonly VITE_APP_TITLE: string + // 更多环境变量... +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} \ No newline at end of file diff --git a/src/main/resources/web/src/main.ts b/src/main/resources/web/src/main.ts new file mode 100644 index 0000000..33f7c05 --- /dev/null +++ b/src/main/resources/web/src/main.ts @@ -0,0 +1,11 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import App from './App.vue' +import router from './router' + +const app = createApp(App) + +app.use(createPinia()) +app.use(router) + +app.mount('#app') \ No newline at end of file diff --git a/src/main/resources/web/src/router/index.ts b/src/main/resources/web/src/router/index.ts new file mode 100644 index 0000000..70d20ac --- /dev/null +++ b/src/main/resources/web/src/router/index.ts @@ -0,0 +1,272 @@ +// src/router/index.ts +import { createRouter, createWebHistory, type NavigationGuardNext, type RouteLocationNormalized } from 'vue-router' +import LoginView from '../views/LoginView.vue' +import MainLayout from '../components/layout/MainLayout.vue' +import { useAuthStore } from '@/stores/auth' + + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: [ + { + path: '/', + name: 'login', + component: LoginView, + meta: { + title: '登录', + requiresAuth: false // 不需要认证 + } + }, + { + path: '/home', + component: MainLayout, + meta: { + requiresAuth: true // 需要认证 + }, + children: [ + { + path: '', + name: 'home', + component: () => import('../views/Dashboard.vue'), + meta: { + title: '首页' + } + }, + // 设备监控相关路由 + { + path: 'equipment', + name: 'equipment', + component: () => import('../views/equipment/EquipmentView.vue'), + meta: { + title: '设备监控' + } + }, + { + path: 'equipment/water-maker', + name: 'water-maker', + component: () => import('../views/equipment/WaterMaker.vue'), + meta: { + title: '制水设备' + } + }, + { + path: 'equipment/water-supplier', + name: 'water-supplier', + component: () => import('../views/equipment/WaterSupplier.vue'), + meta: { + title: '供水设备' + } + }, + // 工单管理相关路由 + { + path: 'work-order', + name: 'work-order', + component: () => import('../views/workorder/WorkOrderView.vue'), + meta: { + title: '工单管理' + } + }, + { + path: 'work-order/pending', + name: 'work-order-pending', + component: () => import('../views/workorder/Pending.vue'), + meta: { + title: '待处理工单' + } + }, + { + path: 'work-order/timeout', + name: 'work-order-timeout', + component: () => import('../views/workorder/Timeout.vue'), + meta: { + title: '超时工单' + } + }, + { + path: 'work-order/processing', + name: 'work-order-processing', + component: () => import('../views/workorder/Processing.vue'), + meta: { + title: '处理中工单' + } + }, + { + path: 'work-order/review', + name: 'work-order-review', + component: () => import('../views/workorder/Review.vue'), + meta: { + title: '待审核工单' + } + }, + { + path: 'work-order/review/:id', + name: 'ReviewDetail', + component: () => import('../views/workorder/ReviewDetail.vue'), + meta: { + title: '工单审核详情' + } + }, + { + path: 'work-order/completed', + name: 'work-order-completed', + component: () => import('../views/workorder/Completed.vue'), + meta: { + title: '已完成工单' + } + }, + { + path: '/home/work-order/completed/:id', + name: 'CompletedDetail', + component: () => import('@/views/workorder/CompletedDetail.vue'), + meta: { + title: '结单信息' + } + }, + // 人员管理相关路由 + // 在 personnel/admin 路由下添加子路由 + { + path: 'personnel/admin', + name: 'personnel-admin', + component: () => import('../views/personnel/Admin.vue'), + meta: { + title: '管理员管理' + }, + children: [ + { + path: 'add', + name: 'admin-add', + component: () => import('../views/personnel/addAdmin.vue'), + meta: { + title: '新增管理员' + } + } + ] +}, + { + path: 'personnel/maintenance', + name: 'personnel-maintenance', + component: () => import('@/views/personnel/Maintenance.vue'), + meta: { + title: '维修人员管理' + } + }, + { + path: 'personnel/maintenance/records/:id', + name: 'MaintenanceRecord', + component: () => import('@/views/personnel/MaintenanceRecord.vue'), + meta: { + title: '维修记录详情' + } + } +, + { + path: 'personnel/user', + name: 'personnel-user', + component: () => import('../views/personnel/User.vue'), + meta: { + title: '用户管理' + } + }, + // 片区相关路由 + { + path: 'area/urban', + name: 'area-urban', + component: () => import('../views/area/Urban.vue'), + meta: { + title: '城市片区' + } + }, + { + path: 'equipment/water-maker/:id', + name: 'water-maker-detail', + component: () => import('../views/equipment/WaterMakerDetail.vue'), + meta: { + title: '制水设备详情' + } + }, + { + path: 'area/campus', + name: 'area-campus', + component: () => import('../views/area/Campus.vue'), + meta: { + title: '校园片区' + } + }, + // 个人信息路由 + { + path: 'profile', + name: 'profile', + component: () => import('../views/Profile.vue'), + meta: { + title: '个人信息', + requiresAuth: true + } + } + ] + }, + { + path: '/about', + name: 'about', + component: () => import('../views/AboutView.vue'), + meta: { + title: '关于', + requiresAuth: false + } + }, + /* // 404 页面 + { + path: '/:pathMatch(.*)*', + name: 'NotFound', + component: () => import('../views/NotFound.vue'), + meta: { + title: '页面不存在', + requiresAuth: false + } + }*/ + ] +}) + +// 路由守卫 +router.beforeEach(async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => { + // 设置页面标题 + const title = to.meta?.title as string || '校园直饮水管理系统' + document.title = title + + // 获取认证状态 + const authStore = useAuthStore() + + // 初始化登录状态 + if (!authStore.isLoggedIn) { + authStore.initialize() + } + + // 判断是否需要认证 + const requiresAuth = to.matched.some(record => record.meta.requiresAuth) + + // 如果路由需要认证但用户未登录 + if (requiresAuth && !authStore.isLoggedIn) { + // 重定向到登录页面,并保存当前想要访问的路径 + next({ + name: 'login', + query: { redirect: to.fullPath } + }) + } + // 如果用户已登录但访问登录页面 + else if (to.name === 'login' && authStore.isLoggedIn) { + // 检查是否有重定向路径 + const redirect = from.query.redirect as string || '/home' + next(redirect) + } + // 其他情况正常放行 + else { + next() + } +}) + +// 路由后置守卫 - 可在这里添加一些统计或清理工作 +router.afterEach((to: RouteLocationNormalized, from: RouteLocationNormalized) => { + // 可以在这里添加页面访问统计等 + console.log(`路由跳转: ${from.fullPath} -> ${to.fullPath}`) +}) + +export default router \ No newline at end of file diff --git a/src/main/resources/web/src/shims-vue.d.ts b/src/main/resources/web/src/shims-vue.d.ts new file mode 100644 index 0000000..46d9dde --- /dev/null +++ b/src/main/resources/web/src/shims-vue.d.ts @@ -0,0 +1,6 @@ +// src/shims-vue.d.ts +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} \ No newline at end of file diff --git a/src/main/resources/web/src/stores/auth.ts b/src/main/resources/web/src/stores/auth.ts new file mode 100644 index 0000000..6042893 --- /dev/null +++ b/src/main/resources/web/src/stores/auth.ts @@ -0,0 +1,106 @@ +// src/stores/auth.ts +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { authApi } from '@/api/modules/auth' +import type { LoginRequest, LoginVO, ResultVO } from '@/api/types/auth' + +interface UserInfo { + id: number + username: string + realName?: string + role?: string + avatar?: string + areaId?: string +} + +export const useAuthStore = defineStore('auth', () => { + const token = ref('') + const userInfo = ref(null) + const isLoggedIn = ref(false) + + // 真实登录接口调用 + const login = async (loginData: LoginRequest) => { + try { + // 调用真实后端接口 + const response: ResultVO = await authApi.login(loginData) + + // 检查响应状态 + if (response.code !== 200) { + throw new Error(response.message || '登录失败') + } + + // 保存 token 和用户信息 + token.value = response.data.token + userInfo.value = response.data.userInfo + isLoggedIn.value = true + + // 存储到 localStorage(如果用户选择记住我) + if (loginData.rememberMe) { + localStorage.setItem('token', response.data.token) + localStorage.setItem('userInfo', JSON.stringify(response.data.userInfo)) + localStorage.setItem('rememberMe', 'true') + } else { + sessionStorage.setItem('token', response.data.token) + sessionStorage.setItem('userInfo', JSON.stringify(response.data.userInfo)) + localStorage.removeItem('rememberMe') + } + + return response + } catch (error: any) { + console.error('登录失败:', error) + throw error + } + } + + // 退出登录 + const logout = () => { + // 可以调用后端登出接口 + // authApi.logout().catch(console.error) + + // 清除本地存储 + token.value = '' + userInfo.value = null + isLoggedIn.value = false + + localStorage.removeItem('token') + localStorage.removeItem('userInfo') + localStorage.removeItem('rememberMe') + sessionStorage.removeItem('token') + sessionStorage.removeItem('userInfo') + } + + // 初始化时从存储恢复状态 + const initialize = () => { + const rememberMe = localStorage.getItem('rememberMe') + const storage = rememberMe ? localStorage : sessionStorage + + const savedToken = storage.getItem('token') + const savedUserInfo = storage.getItem('userInfo') + + if (savedToken && savedUserInfo) { + try { + token.value = savedToken + userInfo.value = JSON.parse(savedUserInfo) + isLoggedIn.value = true + } catch (e) { + console.error('恢复登录状态失败:', e) + logout() + } + } + } + + // 检查是否已登录(用于路由守卫) + const checkAuth = (): boolean => { + return isLoggedIn.value && !!token.value + } + + return { + token, + userInfo, + isLoggedIn, + login, + logout, + initialize, + checkAuth, + } +}) \ No newline at end of file diff --git a/src/main/resources/web/src/stores/counter.ts b/src/main/resources/web/src/stores/counter.ts new file mode 100644 index 0000000..fdfa9c2 --- /dev/null +++ b/src/main/resources/web/src/stores/counter.ts @@ -0,0 +1,40 @@ +// src/router/index.ts +import { createRouter, createWebHistory } from 'vue-router' +import HomeView from '../views/HomeView.vue' +import LoginView from '../views/LoginView.vue' // 导入登录组件 + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: [ + { + path: '/', + name: 'home', + component: HomeView, + meta: { requiresAuth: true } // 需要登录才能访问 + }, + { + path: '/login', + name: 'login', + component: LoginView + }, + { + path: '/about', + name: 'about', + component: () => import('../views/AboutView.vue'), + meta: { requiresAuth: true } + }, + ], +}) + +// 路由守卫 - 检查登录状态 +router.beforeEach((to, from, next) => { + const isLoggedIn = localStorage.getItem('isLoggedIn') // 简单的登录状态检查 + + if (to.meta.requiresAuth && !isLoggedIn) { + next('/login') + } else { + next() + } +}) + +export default router \ No newline at end of file diff --git a/src/main/resources/web/src/views/AboutView.vue b/src/main/resources/web/src/views/AboutView.vue new file mode 100644 index 0000000..756ad2a --- /dev/null +++ b/src/main/resources/web/src/views/AboutView.vue @@ -0,0 +1,15 @@ + + + diff --git a/src/main/resources/web/src/views/Dashboard.vue b/src/main/resources/web/src/views/Dashboard.vue new file mode 100644 index 0000000..2fc51b9 --- /dev/null +++ b/src/main/resources/web/src/views/Dashboard.vue @@ -0,0 +1,169 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/views/HomeView.vue b/src/main/resources/web/src/views/HomeView.vue new file mode 100644 index 0000000..d5c0217 --- /dev/null +++ b/src/main/resources/web/src/views/HomeView.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/main/resources/web/src/views/LoginView.vue b/src/main/resources/web/src/views/LoginView.vue new file mode 100644 index 0000000..14be424 --- /dev/null +++ b/src/main/resources/web/src/views/LoginView.vue @@ -0,0 +1,270 @@ + + + + + diff --git a/src/main/resources/web/src/views/Profile.vue b/src/main/resources/web/src/views/Profile.vue new file mode 100644 index 0000000..8ebe31d --- /dev/null +++ b/src/main/resources/web/src/views/Profile.vue @@ -0,0 +1,629 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/views/area/Campus.vue b/src/main/resources/web/src/views/area/Campus.vue new file mode 100644 index 0000000..18f6055 --- /dev/null +++ b/src/main/resources/web/src/views/area/Campus.vue @@ -0,0 +1,598 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/views/area/Urban.vue b/src/main/resources/web/src/views/area/Urban.vue new file mode 100644 index 0000000..ad6efd7 --- /dev/null +++ b/src/main/resources/web/src/views/area/Urban.vue @@ -0,0 +1,553 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/views/equipment/EquipmentView.vue b/src/main/resources/web/src/views/equipment/EquipmentView.vue new file mode 100644 index 0000000..3ddfa2e --- /dev/null +++ b/src/main/resources/web/src/views/equipment/EquipmentView.vue @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/src/main/resources/web/src/views/equipment/WaterMaker.vue b/src/main/resources/web/src/views/equipment/WaterMaker.vue new file mode 100644 index 0000000..aadfa19 --- /dev/null +++ b/src/main/resources/web/src/views/equipment/WaterMaker.vue @@ -0,0 +1,853 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/views/equipment/WaterMakerDetail.vue b/src/main/resources/web/src/views/equipment/WaterMakerDetail.vue new file mode 100644 index 0000000..7221345 --- /dev/null +++ b/src/main/resources/web/src/views/equipment/WaterMakerDetail.vue @@ -0,0 +1,804 @@ + + + + + + diff --git a/src/main/resources/web/src/views/equipment/WaterSupplier.vue b/src/main/resources/web/src/views/equipment/WaterSupplier.vue new file mode 100644 index 0000000..a7047ae --- /dev/null +++ b/src/main/resources/web/src/views/equipment/WaterSupplier.vue @@ -0,0 +1,408 @@ + + + + + diff --git a/src/main/resources/web/src/views/personnel/Admin.vue b/src/main/resources/web/src/views/personnel/Admin.vue new file mode 100644 index 0000000..c2ef0d7 --- /dev/null +++ b/src/main/resources/web/src/views/personnel/Admin.vue @@ -0,0 +1,833 @@ + + + + + + diff --git a/src/main/resources/web/src/views/personnel/Maintenance.vue b/src/main/resources/web/src/views/personnel/Maintenance.vue new file mode 100644 index 0000000..e96e345 --- /dev/null +++ b/src/main/resources/web/src/views/personnel/Maintenance.vue @@ -0,0 +1,839 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/views/personnel/MaintenanceRecord.vue b/src/main/resources/web/src/views/personnel/MaintenanceRecord.vue new file mode 100644 index 0000000..42bdb5e --- /dev/null +++ b/src/main/resources/web/src/views/personnel/MaintenanceRecord.vue @@ -0,0 +1,569 @@ + + + + + + diff --git a/src/main/resources/web/src/views/personnel/User.vue b/src/main/resources/web/src/views/personnel/User.vue new file mode 100644 index 0000000..ebb9a85 --- /dev/null +++ b/src/main/resources/web/src/views/personnel/User.vue @@ -0,0 +1,397 @@ + + + + + + diff --git a/src/main/resources/web/src/views/workorder/Completed.vue b/src/main/resources/web/src/views/workorder/Completed.vue new file mode 100644 index 0000000..c5fa014 --- /dev/null +++ b/src/main/resources/web/src/views/workorder/Completed.vue @@ -0,0 +1,355 @@ + + + + + + diff --git a/src/main/resources/web/src/views/workorder/CompletedDetail.vue b/src/main/resources/web/src/views/workorder/CompletedDetail.vue new file mode 100644 index 0000000..e410e73 --- /dev/null +++ b/src/main/resources/web/src/views/workorder/CompletedDetail.vue @@ -0,0 +1,455 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/views/workorder/Pending.vue b/src/main/resources/web/src/views/workorder/Pending.vue new file mode 100644 index 0000000..3099f5b --- /dev/null +++ b/src/main/resources/web/src/views/workorder/Pending.vue @@ -0,0 +1,732 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/views/workorder/Processing.vue b/src/main/resources/web/src/views/workorder/Processing.vue new file mode 100644 index 0000000..c04ed5a --- /dev/null +++ b/src/main/resources/web/src/views/workorder/Processing.vue @@ -0,0 +1,516 @@ + + + + + diff --git a/src/main/resources/web/src/views/workorder/Review.vue b/src/main/resources/web/src/views/workorder/Review.vue new file mode 100644 index 0000000..9894b48 --- /dev/null +++ b/src/main/resources/web/src/views/workorder/Review.vue @@ -0,0 +1,356 @@ + + + + + + diff --git a/src/main/resources/web/src/views/workorder/Timeout.vue b/src/main/resources/web/src/views/workorder/Timeout.vue new file mode 100644 index 0000000..101cbb0 --- /dev/null +++ b/src/main/resources/web/src/views/workorder/Timeout.vue @@ -0,0 +1,779 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/web/src/views/workorder/WorkOrderView.vue b/src/main/resources/web/src/views/workorder/WorkOrderView.vue new file mode 100644 index 0000000..7006779 --- /dev/null +++ b/src/main/resources/web/src/views/workorder/WorkOrderView.vue @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/src/main/resources/web/tsconfig.app.json b/src/main/resources/web/tsconfig.app.json new file mode 100644 index 0000000..1cd5874 --- /dev/null +++ b/src/main/resources/web/tsconfig.app.json @@ -0,0 +1,16 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "composite": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"], + "lib": ["ES2015", "DOM"], // 关键:添加 ES2015 支持 Promise + "target": "ES2015", // 目标输出为 ES2015 及以上 + "module": "ESNext", // 配合异步模块解析 + "moduleResolution": "NodeNext" + } + } +} \ No newline at end of file diff --git a/src/main/resources/web/tsconfig.json b/src/main/resources/web/tsconfig.json new file mode 100644 index 0000000..32d78d8 --- /dev/null +++ b/src/main/resources/web/tsconfig.json @@ -0,0 +1,14 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + } + ] + + + +} diff --git a/src/main/resources/web/tsconfig.node.json b/src/main/resources/web/tsconfig.node.json new file mode 100644 index 0000000..822562d --- /dev/null +++ b/src/main/resources/web/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "extends": "@tsconfig/node24/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*", + "eslint.config.*" + ], + "compilerOptions": { + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/src/main/resources/web/vite.config.ts b/src/main/resources/web/vite.config.ts new file mode 100644 index 0000000..c68830d --- /dev/null +++ b/src/main/resources/web/vite.config.ts @@ -0,0 +1,35 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + ], + resolve: { + alias: { + '@': '/src' + }, + }, + server: { + proxy: { + // 代理所有以 /api 开头的请求到后端 + '/api': { + target: 'http://localhost:8080', // Spring Boot 后端地址 + changeOrigin: true, // 改变请求来源 + secure: false, // 如果是https,可能需要设置为false + // 如果需要重写路径,可以取消下面的注释 + // rewrite: (path) => path.replace(/^\/api/, '') + }, + // 如果需要代理其他路径,可以继续添加 + // '/ws': { + // target: 'ws://localhost:8080', + // ws: true + // } + }, + // 可选:设置端口 + port: 5173, // Vite默认端口 + // 可选:自动打开浏览器 + open: true + } +}) \ No newline at end of file diff --git a/src/main/resources/zzz/.editorconfig b/src/main/resources/zzz/.editorconfig new file mode 100644 index 0000000..3b510aa --- /dev/null +++ b/src/main/resources/zzz/.editorconfig @@ -0,0 +1,8 @@ +[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf +max_line_length = 100 diff --git a/src/main/resources/zzz/.gitattributes b/src/main/resources/zzz/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/src/main/resources/zzz/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/src/main/resources/zzz/.gitignore b/src/main/resources/zzz/.gitignore new file mode 100644 index 0000000..a3f7a51 --- /dev/null +++ b/src/main/resources/zzz/.gitignore @@ -0,0 +1,36 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo + +.eslintcache + +# Cypress +/cypress/videos/ +/cypress/screenshots/ + +# Vitest +__screenshots__/ diff --git a/src/main/resources/zzz/.vscode/extensions.json b/src/main/resources/zzz/.vscode/extensions.json new file mode 100644 index 0000000..8f8a11f --- /dev/null +++ b/src/main/resources/zzz/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "Vue.volar", + "vitest.explorer", + "dbaeumer.vscode-eslint", + "EditorConfig.EditorConfig", + "oxc.oxc-vscode" + ] +} diff --git a/src/main/resources/zzz/README.md b/src/main/resources/zzz/README.md new file mode 100644 index 0000000..4d7ee62 --- /dev/null +++ b/src/main/resources/zzz/README.md @@ -0,0 +1,44 @@ +# zzz + +This template should help get you started developing with Vue 3 in Vite. + +## Recommended IDE Setup + +[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). + +## Recommended Browser Setup + +- Chromium-based browsers (Chrome, Edge, Brave, etc.): + - [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd) + - [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters) +- Firefox: + - [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/) + - [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/) + +## Customize configuration + +See [Vite Configuration Reference](https://vite.dev/config/). + +## Project Setup + +```sh +npm install +``` + +### Compile and Hot-Reload for Development + +```sh +npm run dev +``` + +### Compile and Minify for Production + +```sh +npm run build +``` + +### Run Unit Tests with [Vitest](https://vitest.dev/) + +```sh +npm run test:unit +``` diff --git a/src/main/resources/zzz/eslint.config.js b/src/main/resources/zzz/eslint.config.js new file mode 100644 index 0000000..10a8159 --- /dev/null +++ b/src/main/resources/zzz/eslint.config.js @@ -0,0 +1,32 @@ +import { defineConfig, globalIgnores } from 'eslint/config' +import globals from 'globals' +import js from '@eslint/js' +import pluginVue from 'eslint-plugin-vue' +import pluginVitest from '@vitest/eslint-plugin' +import pluginOxlint from 'eslint-plugin-oxlint' + +export default defineConfig([ + { + name: 'app/files-to-lint', + files: ['**/*.{js,mjs,jsx,vue}'], + }, + + globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']), + + { + languageOptions: { + globals: { + ...globals.browser, + }, + }, + }, + + js.configs.recommended, + ...pluginVue.configs['flat/essential'], + + { + ...pluginVitest.configs.recommended, + files: ['src/**/__tests__/*'], + }, + ...pluginOxlint.configs['flat/recommended'], +]) diff --git a/src/main/resources/zzz/index.html b/src/main/resources/zzz/index.html new file mode 100644 index 0000000..b19040a --- /dev/null +++ b/src/main/resources/zzz/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/src/main/resources/zzz/jsconfig.json b/src/main/resources/zzz/jsconfig.json new file mode 100644 index 0000000..5a1f2d2 --- /dev/null +++ b/src/main/resources/zzz/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + }, + "exclude": ["node_modules", "dist"] +} diff --git a/src/main/resources/zzz/package-lock.json b/src/main/resources/zzz/package-lock.json new file mode 100644 index 0000000..c2ac356 --- /dev/null +++ b/src/main/resources/zzz/package-lock.json @@ -0,0 +1,6194 @@ +{ + "name": "zzz", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "zzz", + "version": "0.0.0", + "dependencies": { + "axios": "^1.13.2", + "pinia": "^3.0.4", + "vue": "^3.5.25", + "vue-router": "^4.6.3" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@vitejs/plugin-vue": "^6.0.2", + "@vitest/eslint-plugin": "^1.5.0", + "@vue/test-utils": "^2.4.6", + "eslint": "^9.39.1", + "eslint-plugin-oxlint": "~1.29.0", + "eslint-plugin-vue": "~10.5.1", + "globals": "^16.5.0", + "jsdom": "^27.2.0", + "npm-run-all2": "^8.0.4", + "oxlint": "~1.29.0", + "vite": "^7.2.4", + "vite-plugin-vue-devtools": "^8.0.5", + "vitest": "^4.0.14" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@acemir/cssom": { + "version": "0.9.24", + "resolved": "https://registry.npmmirror.com/@acemir/cssom/-/cssom-0.9.24.tgz", + "integrity": "sha512-5YjgMmAiT2rjJZU7XK1SNI7iqTy92DpaYVgG6x63FxkJ11UpYfLndHJATtinWJClAXiOlW9XWaUyAQf8pMrQPg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/@asamuzakjp/css-color/-/css-color-4.1.0.tgz", + "integrity": "sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "lru-cache": "^11.2.2" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.7.5", + "resolved": "https://registry.npmmirror.com/@asamuzakjp/dom-selector/-/dom-selector-6.7.5.tgz", + "integrity": "sha512-Eks6dY8zau4m4wNRQjRVaKQRTalNcPcBvU1ZQ35w5kKRk1gUeNCkVLsRiATurjASTp3TKM4H10wsI50nx3NZdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.2" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmmirror.com/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", + "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz", + "integrity": "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.20", + "resolved": "https://registry.npmmirror.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.20.tgz", + "integrity": "sha512-8BHsjXfSciZxjmHQOuVdW2b8WLUPts9a+mfL13/PzEviufUEW2xnvQuOlKs9dRBHgRqJ53SF/DUoK9+MZk72oQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.1", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@oxlint/darwin-arm64": { + "version": "1.29.0", + "resolved": "https://registry.npmmirror.com/@oxlint/darwin-arm64/-/darwin-arm64-1.29.0.tgz", + "integrity": "sha512-XYsieDAI0kXJyvayHnmOW1qVydqklRRVT4O5eZmO/rdNCku5CoXsZvBvkPc3U8/9V1mRuen1sxbM9T5JsZqhdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint/darwin-x64": { + "version": "1.29.0", + "resolved": "https://registry.npmmirror.com/@oxlint/darwin-x64/-/darwin-x64-1.29.0.tgz", + "integrity": "sha512-s+Ch5/4zDJ6wsOk95xY3BS5mtE2JzHLz7gVZ9OWA9EvhVO84wz2YbDp2JaA314yyqhlX5SAkZ6fj3BRMIcQIqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint/linux-arm64-gnu": { + "version": "1.29.0", + "resolved": "https://registry.npmmirror.com/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-1.29.0.tgz", + "integrity": "sha512-qLCgdUkDBG8muK1o3mPgf31rvCPzj1Xff9DHlJjfv+B0ee/hJ2LAoK8EIsQedfQuuiAccOe9GG65BivGCTgKOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-arm64-musl": { + "version": "1.29.0", + "resolved": "https://registry.npmmirror.com/@oxlint/linux-arm64-musl/-/linux-arm64-musl-1.29.0.tgz", + "integrity": "sha512-qe62yb1fyW51wo1VBpx9AJJ1Ih1T8NYDeR9AmpNGkrmKN8u3pPbcGXM4mCrOwpwJUG9M/oFvCIlIz2RhawHlkA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-x64-gnu": { + "version": "1.29.0", + "resolved": "https://registry.npmmirror.com/@oxlint/linux-x64-gnu/-/linux-x64-gnu-1.29.0.tgz", + "integrity": "sha512-4x7p2iVoSE2aT9qI1JOLxUAv3UuzMYGBYWBA4ZF8ln99AdUo1eo0snFacPNd6I/ZZNcv5TegXC+0EUhp5MfYBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-x64-musl": { + "version": "1.29.0", + "resolved": "https://registry.npmmirror.com/@oxlint/linux-x64-musl/-/linux-x64-musl-1.29.0.tgz", + "integrity": "sha512-BdH5gdRpaYpyZn2Zm+MCS4b1YmXNe7QyQhw0fawuou+N1LrdAyELgvqI5xXZ1MXCgWDOa6WJaoE6VOPaDc29GA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/win32-arm64": { + "version": "1.29.0", + "resolved": "https://registry.npmmirror.com/@oxlint/win32-arm64/-/win32-arm64-1.29.0.tgz", + "integrity": "sha512-y+j9ZDrnMxvRTNIstZKFY7gJD07nT++c4cGmub1ENvhoHVToiQAAZQUOLDhXXRzCrFoG/cFJXJf72uowHZPbcg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxlint/win32-x64": { + "version": "1.29.0", + "resolved": "https://registry.npmmirror.com/@oxlint/win32-x64/-/win32-x64-1.29.0.tgz", + "integrity": "sha512-F1iRtq8VT96lT8hqOubLyV0GxgIK/XdXk2kFLXdCspiI2ngXeNmTTvmPxrj+WFL6fpJPgv7VKWRb/zEHJnNOrg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.50", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.50.tgz", + "integrity": "sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmmirror.com/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.48.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.48.0.tgz", + "integrity": "sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.48.0", + "@typescript-eslint/types": "^8.48.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.48.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.48.0.tgz", + "integrity": "sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.48.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.0.tgz", + "integrity": "sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.48.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.48.0.tgz", + "integrity": "sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.48.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.0.tgz", + "integrity": "sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.48.0", + "@typescript-eslint/tsconfig-utils": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", + "debug": "^4.3.4", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.48.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.48.0.tgz", + "integrity": "sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.48.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.0.tgz", + "integrity": "sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.48.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.2.tgz", + "integrity": "sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.50" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitest/eslint-plugin": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/@vitest/eslint-plugin/-/eslint-plugin-1.5.1.tgz", + "integrity": "sha512-t49CNERe/YadnLn90NTTKJLKzs99xBkXElcoUTLodG6j1G0Q7jy3mXqqiHd3N5aryG2KkgOg4UAoGwgwSrZqKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "^8.46.1", + "@typescript-eslint/utils": "^8.46.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": ">=8.57.0", + "typescript": ">=5.0.0", + "vitest": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.14", + "resolved": "https://registry.npmmirror.com/@vitest/expect/-/expect-4.0.14.tgz", + "integrity": "sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.14", + "@vitest/utils": "4.0.14", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.14", + "resolved": "https://registry.npmmirror.com/@vitest/mocker/-/mocker-4.0.14.tgz", + "integrity": "sha512-RzS5NujlCzeRPF1MK7MXLiEFpkIXeMdQ+rN3Kk3tDI9j0mtbr7Nmuq67tpkOJQpgyClbOltCXMjLZicJHsH5Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.14", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.14", + "resolved": "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-4.0.14.tgz", + "integrity": "sha512-SOYPgujB6TITcJxgd3wmsLl+wZv+fy3av2PpiPpsWPZ6J1ySUYfScfpIt2Yv56ShJXR2MOA6q2KjKHN4EpdyRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.14", + "resolved": "https://registry.npmmirror.com/@vitest/runner/-/runner-4.0.14.tgz", + "integrity": "sha512-BsAIk3FAqxICqREbX8SetIteT8PiaUL/tgJjmhxJhCsigmzzH8xeadtp7LRnTpCVzvf0ib9BgAfKJHuhNllKLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.14", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.14", + "resolved": "https://registry.npmmirror.com/@vitest/snapshot/-/snapshot-4.0.14.tgz", + "integrity": "sha512-aQVBfT1PMzDSA16Y3Fp45a0q8nKexx6N5Amw3MX55BeTeZpoC08fGqEZqVmPcqN0ueZsuUQ9rriPMhZ3Mu19Ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.14", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.14", + "resolved": "https://registry.npmmirror.com/@vitest/spy/-/spy-4.0.14.tgz", + "integrity": "sha512-JmAZT1UtZooO0tpY3GRyiC/8W7dCs05UOq9rfsUUgEZEdq+DuHLmWhPsrTt0TiW7WYeL/hXpaE07AZ2RCk44hg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.14", + "resolved": "https://registry.npmmirror.com/@vitest/utils/-/utils-4.0.14.tgz", + "integrity": "sha512-hLqXZKAWNg8pI+SQXyXxWCTOpA3MvsqcbVeNgSi8x/CSN2wi26dSzn1wrOhmCmFjEvN9p8/kLFRHa6PI8jHazw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.14", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz", + "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz", + "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@vue/babel-helper-vue-transform-on": "1.5.0", + "@vue/babel-plugin-resolve-type": "1.5.0", + "@vue/shared": "^3.5.18" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz", + "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.0", + "@vue/compiler-sfc": "^3.5.18" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.25.tgz", + "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.25", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz", + "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz", + "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.25", + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz", + "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.9" + } + }, + "node_modules/@vue/devtools-core": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/@vue/devtools-core/-/devtools-core-8.0.5.tgz", + "integrity": "sha512-dpCw8nl0GDBuiL9SaY0mtDxoGIEmU38w+TQiYEPOLhW03VDC0lfNMYXS/qhl4I0YlysGp04NLY4UNn6xgD0VIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.0.5", + "@vue/devtools-shared": "^8.0.5", + "mitt": "^3.0.1", + "nanoid": "^5.1.5", + "pathe": "^2.0.3", + "vite-hot-client": "^2.1.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-core/node_modules/@vue/devtools-kit": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-8.0.5.tgz", + "integrity": "sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.0.5", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^2.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-core/node_modules/@vue/devtools-shared": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-8.0.5.tgz", + "integrity": "sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/devtools-core/node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@vue/devtools-core/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.25.tgz", + "integrity": "sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.25.tgz", + "integrity": "sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.25.tgz", + "integrity": "sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.25", + "@vue/runtime-core": "3.5.25", + "@vue/shared": "3.5.25", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.25.tgz", + "integrity": "sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25" + }, + "peerDependencies": { + "vue": "3.5.25" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.25.tgz", + "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==", + "license": "MIT" + }, + "node_modules/@vue/test-utils": { + "version": "2.4.6", + "resolved": "https://registry.npmmirror.com/@vue/test-utils/-/test-utils-2.4.6.tgz", + "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^2.0.0" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.32", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/birpc": { + "version": "2.8.0", + "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.8.0.tgz", + "integrity": "sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.0", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001757", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", + "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/chai/-/chai-6.2.1.tgz", + "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmmirror.com/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssstyle": { + "version": "5.3.3", + "resolved": "https://registry.npmmirror.com/cssstyle/-/cssstyle-5.3.3.tgz", + "integrity": "sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^4.0.3", + "@csstools/css-syntax-patches-for-csstree": "^1.0.14", + "css-tree": "^3.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/data-urls/-/data-urls-6.0.0.tgz", + "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/default-browser/-/default-browser-5.4.0.tgz", + "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.262", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.262.tgz", + "integrity": "sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.1", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-oxlint": { + "version": "1.29.0", + "resolved": "https://registry.npmmirror.com/eslint-plugin-oxlint/-/eslint-plugin-oxlint-1.29.0.tgz", + "integrity": "sha512-VmaZ1I0lXJVJokOpnV8F7e339hcFPln5EWY8KGCdTkBnrkRmAeH25GRO6F37lZEDO9e+px5xjqbkq9g3lejBdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.3.1" + } + }, + "node_modules/eslint-plugin-vue": { + "version": "10.5.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-10.5.1.tgz", + "integrity": "sha512-SbR9ZBUFKgvWAbq3RrdCtWaW0IKm6wwUiApxf3BVTNfqUIo4IQQmreMg2iHFJJ6C/0wss3LXURBJ1OwS/MhFcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", + "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "vue-eslint-parser": "^10.0.0" + }, + "peerDependenciesMeta": { + "@stylistic/eslint-plugin": { + "optional": true + }, + "@typescript-eslint/parser": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmmirror.com/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmmirror.com/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "27.2.0", + "resolved": "https://registry.npmmirror.com/jsdom/-/jsdom-27.2.0.tgz", + "integrity": "sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@acemir/cssom": "^0.9.23", + "@asamuzakjp/dom-selector": "^6.7.4", + "cssstyle": "^5.3.3", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmmirror.com/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2": { + "version": "8.0.4", + "resolved": "https://registry.npmmirror.com/npm-run-all2/-/npm-run-all2-8.0.4.tgz", + "integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "picomatch": "^4.0.2", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^20.5.0 || >=22.0.0", + "npm": ">= 10" + } + }, + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm-run-all2/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmmirror.com/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/oxlint": { + "version": "1.29.0", + "resolved": "https://registry.npmmirror.com/oxlint/-/oxlint-1.29.0.tgz", + "integrity": "sha512-YqUVUhTYDqazV2qu3QSQn/H4Z1OP+fTnedgZWDk1/lDZxGfR0b1MqRVaEm3rRjBMLHP0zXlriIWUx+DD6UMaPA==", + "dev": true, + "license": "MIT", + "bin": { + "oxc_language_server": "bin/oxc_language_server", + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/darwin-arm64": "1.29.0", + "@oxlint/darwin-x64": "1.29.0", + "@oxlint/linux-arm64-gnu": "1.29.0", + "@oxlint/linux-arm64-musl": "1.29.0", + "@oxlint/linux-x64-gnu": "1.29.0", + "@oxlint/linux-x64-musl": "1.29.0", + "@oxlint/win32-arm64": "1.29.0", + "@oxlint/win32-x64": "1.29.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=0.7.1" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + } + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pinia": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz", + "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.7" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.5.0", + "vue": "^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmmirror.com/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.19", + "resolved": "https://registry.npmmirror.com/tldts/-/tldts-7.0.19.tgz", + "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.19" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.19", + "resolved": "https://registry.npmmirror.com/tldts-core/-/tldts-core-7.0.19.tgz", + "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==", + "dev": true, + "license": "MIT" + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.3.1.tgz", + "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.2.4", + "resolved": "https://registry.npmmirror.com/vite/-/vite-7.2.4.tgz", + "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-dev-rpc": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz", + "integrity": "sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==", + "dev": true, + "license": "MIT", + "dependencies": { + "birpc": "^2.4.0", + "vite-hot-client": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0" + } + }, + "node_modules/vite-hot-client": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/vite-hot-client/-/vite-hot-client-2.1.0.tgz", + "integrity": "sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vite-plugin-inspect": { + "version": "11.3.3", + "resolved": "https://registry.npmmirror.com/vite-plugin-inspect/-/vite-plugin-inspect-11.3.3.tgz", + "integrity": "sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^4.1.0", + "debug": "^4.4.1", + "error-stack-parser-es": "^1.0.5", + "ohash": "^2.0.11", + "open": "^10.2.0", + "perfect-debounce": "^2.0.0", + "sirv": "^3.0.1", + "unplugin-utils": "^0.3.0", + "vite-dev-rpc": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-inspect/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite-plugin-vue-devtools": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-8.0.5.tgz", + "integrity": "sha512-p619BlKFOqQXJ6uDWS1vUPQzuJOD6xJTfftj57JXBGoBD/yeQCowR7pnWcr/FEX4/HVkFbreI6w2uuGBmQOh6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-core": "^8.0.5", + "@vue/devtools-kit": "^8.0.5", + "@vue/devtools-shared": "^8.0.5", + "sirv": "^3.0.2", + "vite-plugin-inspect": "^11.3.3", + "vite-plugin-vue-inspector": "^5.3.2" + }, + "engines": { + "node": ">=v14.21.3" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/@vue/devtools-kit": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-8.0.5.tgz", + "integrity": "sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.0.5", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^2.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/@vue/devtools-shared": { + "version": "8.0.5", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-8.0.5.tgz", + "integrity": "sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/vite-plugin-vue-devtools/node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite-plugin-vue-inspector": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.3.2.tgz", + "integrity": "sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.0", + "@babel/plugin-proposal-decorators": "^7.23.0", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-transform-typescript": "^7.22.15", + "@vue/babel-plugin-jsx": "^1.1.5", + "@vue/compiler-dom": "^3.3.4", + "kolorist": "^1.8.0", + "magic-string": "^0.30.4" + }, + "peerDependencies": { + "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vitest": { + "version": "4.0.14", + "resolved": "https://registry.npmmirror.com/vitest/-/vitest-4.0.14.tgz", + "integrity": "sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@vitest/expect": "4.0.14", + "@vitest/mocker": "4.0.14", + "@vitest/pretty-format": "4.0.14", + "@vitest/runner": "4.0.14", + "@vitest/snapshot": "4.0.14", + "@vitest/spy": "4.0.14", + "@vitest/utils": "4.0.14", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.14", + "@vitest/browser-preview": "4.0.14", + "@vitest/browser-webdriverio": "4.0.14", + "@vitest/ui": "4.0.14", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz", + "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-sfc": "3.5.25", + "@vue/runtime-dom": "3.5.25", + "@vue/server-renderer": "3.5.25", + "@vue/shared": "3.5.25" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-component-type-helpers": { + "version": "2.2.12", + "resolved": "https://registry.npmmirror.com/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz", + "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-eslint-parser": { + "version": "10.2.0", + "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz", + "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.6.0", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-router": { + "version": "4.6.3", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.3.tgz", + "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-8.0.0.tgz", + "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "15.1.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmmirror.com/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/src/main/resources/zzz/package.json b/src/main/resources/zzz/package.json new file mode 100644 index 0000000..bb14afb --- /dev/null +++ b/src/main/resources/zzz/package.json @@ -0,0 +1,40 @@ +{ + "name": "zzz", + "version": "0.0.0", + "private": true, + "type": "module", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "test:unit": "vitest", + "lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore", + "lint:eslint": "eslint . --fix --cache", + "lint": "run-s lint:*" + }, + "dependencies": { + "axios": "^1.13.2", + "pinia": "^3.0.4", + "vue": "^3.5.25", + "vue-router": "^4.6.3" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@vitejs/plugin-vue": "^6.0.2", + "@vitest/eslint-plugin": "^1.5.0", + "@vue/test-utils": "^2.4.6", + "eslint": "^9.39.1", + "eslint-plugin-oxlint": "~1.29.0", + "eslint-plugin-vue": "~10.5.1", + "globals": "^16.5.0", + "jsdom": "^27.2.0", + "npm-run-all2": "^8.0.4", + "oxlint": "~1.29.0", + "vite": "^7.2.4", + "vite-plugin-vue-devtools": "^8.0.5", + "vitest": "^4.0.14" + } +} diff --git a/src/main/resources/zzz/public/favicon.ico b/src/main/resources/zzz/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/src/main/resources/zzz/public/favicon.ico differ diff --git a/src/main/resources/zzz/src/App.vue b/src/main/resources/zzz/src/App.vue new file mode 100644 index 0000000..dfafbd6 --- /dev/null +++ b/src/main/resources/zzz/src/App.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/src/main/resources/zzz/src/__tests__/App.spec.js b/src/main/resources/zzz/src/__tests__/App.spec.js new file mode 100644 index 0000000..5b17801 --- /dev/null +++ b/src/main/resources/zzz/src/__tests__/App.spec.js @@ -0,0 +1,11 @@ +import { describe, it, expect } from 'vitest' + +import { mount } from '@vue/test-utils' +import App from '../App.vue' + +describe('App', () => { + it('mounts renders properly', () => { + const wrapper = mount(App) + expect(wrapper.text()).toContain('You did it!') + }) +}) diff --git a/src/main/resources/zzz/src/assets/base.css b/src/main/resources/zzz/src/assets/base.css new file mode 100644 index 0000000..8816868 --- /dev/null +++ b/src/main/resources/zzz/src/assets/base.css @@ -0,0 +1,86 @@ +/* color palette from */ +:root { + --vt-c-white: #ffffff; + --vt-c-white-soft: #f8f8f8; + --vt-c-white-mute: #f2f2f2; + + --vt-c-black: #181818; + --vt-c-black-soft: #222222; + --vt-c-black-mute: #282828; + + --vt-c-indigo: #2c3e50; + + --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); + + --vt-c-text-light-1: var(--vt-c-indigo); + --vt-c-text-light-2: rgba(60, 60, 60, 0.66); + --vt-c-text-dark-1: var(--vt-c-white); + --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); +} + +/* semantic color variables for this project */ +:root { + --color-background: var(--vt-c-white); + --color-background-soft: var(--vt-c-white-soft); + --color-background-mute: var(--vt-c-white-mute); + + --color-border: var(--vt-c-divider-light-2); + --color-border-hover: var(--vt-c-divider-light-1); + + --color-heading: var(--vt-c-text-light-1); + --color-text: var(--vt-c-text-light-1); + + --section-gap: 160px; +} + +@media (prefers-color-scheme: dark) { + :root { + --color-background: var(--vt-c-black); + --color-background-soft: var(--vt-c-black-soft); + --color-background-mute: var(--vt-c-black-mute); + + --color-border: var(--vt-c-divider-dark-2); + --color-border-hover: var(--vt-c-divider-dark-1); + + --color-heading: var(--vt-c-text-dark-1); + --color-text: var(--vt-c-text-dark-2); + } +} + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + font-weight: normal; +} + +body { + min-height: 100vh; + color: var(--color-text); + background: var(--color-background); + transition: + color 0.5s, + background-color 0.5s; + line-height: 1.6; + font-family: + Inter, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + Oxygen, + Ubuntu, + Cantarell, + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue', + sans-serif; + font-size: 15px; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/src/main/resources/zzz/src/assets/logo.svg b/src/main/resources/zzz/src/assets/logo.svg new file mode 100644 index 0000000..7565660 --- /dev/null +++ b/src/main/resources/zzz/src/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/src/main/resources/zzz/src/assets/main.css b/src/main/resources/zzz/src/assets/main.css new file mode 100644 index 0000000..fa09a3e --- /dev/null +++ b/src/main/resources/zzz/src/assets/main.css @@ -0,0 +1,54 @@ +/* main.css - 修改后的版本 */ +@import './base.css'; + +/* 重置app样式以适应移动端框架 */ +#app { + width: 100%; + height: 100vh; /* 保持全屏高度 */ + margin: 0; + padding: 0; + font-weight: normal; + display: flex; + flex-direction: column; /* 添加flex布局 */ +} + +/* 确保body和html占满全屏 */ +html, body { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + overflow: hidden; /* 防止整体页面滚动 */ +} + +/* 移除原有的桌面端布局 */ +@media (min-width: 1024px) { + body { + display: flex; + justify-content: center; + align-items: center; + background: #f0f2f5; + } + + #app { + max-width: 480px; /* 桌面端限制宽度 */ + height: 90vh; /* 桌面端适当高度 */ + border-radius: 12px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); + overflow: hidden; /* 防止溢出 */ + } +} + +a, +.green { + text-decoration: none; + color: hsla(160, 100%, 37%, 1); + transition: 0.4s; + padding: 3px; +} + +@media (hover: hover) { + a:hover { + background-color: hsla(160, 100%, 37%, 0.2); + } +} diff --git a/src/main/resources/zzz/src/components/HelloWorld.vue b/src/main/resources/zzz/src/components/HelloWorld.vue new file mode 100644 index 0000000..eff59f1 --- /dev/null +++ b/src/main/resources/zzz/src/components/HelloWorld.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/main/resources/zzz/src/components/TheWelcome.vue b/src/main/resources/zzz/src/components/TheWelcome.vue new file mode 100644 index 0000000..68a970a --- /dev/null +++ b/src/main/resources/zzz/src/components/TheWelcome.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/main/resources/zzz/src/components/WelcomeItem.vue b/src/main/resources/zzz/src/components/WelcomeItem.vue new file mode 100644 index 0000000..ac366d0 --- /dev/null +++ b/src/main/resources/zzz/src/components/WelcomeItem.vue @@ -0,0 +1,86 @@ + + + diff --git a/src/main/resources/zzz/src/components/__tests__/HelloWorld.spec.js b/src/main/resources/zzz/src/components/__tests__/HelloWorld.spec.js new file mode 100644 index 0000000..2533202 --- /dev/null +++ b/src/main/resources/zzz/src/components/__tests__/HelloWorld.spec.js @@ -0,0 +1,11 @@ +import { describe, it, expect } from 'vitest' + +import { mount } from '@vue/test-utils' +import HelloWorld from '../HelloWorld.vue' + +describe('HelloWorld', () => { + it('renders properly', () => { + const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } }) + expect(wrapper.text()).toContain('Hello Vitest') + }) +}) diff --git a/src/main/resources/zzz/src/components/icons/IconCommunity.vue b/src/main/resources/zzz/src/components/icons/IconCommunity.vue new file mode 100644 index 0000000..2dc8b05 --- /dev/null +++ b/src/main/resources/zzz/src/components/icons/IconCommunity.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/zzz/src/components/icons/IconDocumentation.vue b/src/main/resources/zzz/src/components/icons/IconDocumentation.vue new file mode 100644 index 0000000..6d4791c --- /dev/null +++ b/src/main/resources/zzz/src/components/icons/IconDocumentation.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/zzz/src/components/icons/IconEcosystem.vue b/src/main/resources/zzz/src/components/icons/IconEcosystem.vue new file mode 100644 index 0000000..c3a4f07 --- /dev/null +++ b/src/main/resources/zzz/src/components/icons/IconEcosystem.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/zzz/src/components/icons/IconSupport.vue b/src/main/resources/zzz/src/components/icons/IconSupport.vue new file mode 100644 index 0000000..7452834 --- /dev/null +++ b/src/main/resources/zzz/src/components/icons/IconSupport.vue @@ -0,0 +1,7 @@ + diff --git a/src/main/resources/zzz/src/components/icons/IconTooling.vue b/src/main/resources/zzz/src/components/icons/IconTooling.vue new file mode 100644 index 0000000..660598d --- /dev/null +++ b/src/main/resources/zzz/src/components/icons/IconTooling.vue @@ -0,0 +1,19 @@ + + diff --git a/src/main/resources/zzz/src/main.js b/src/main/resources/zzz/src/main.js new file mode 100644 index 0000000..6851094 --- /dev/null +++ b/src/main/resources/zzz/src/main.js @@ -0,0 +1,25 @@ +// main.js +import { createApp } from 'vue' +import { createPinia } from 'pinia' + +import App from './App.vue' +import router from './router' +import '@/router/permission' + +const app = createApp(App) +const pinia = createPinia() + +app.use(pinia) +app.use(router) + +// 挂载前初始化 +app.mount('#app') + +// 全局属性(可选) +app.config.globalProperties.$auth = { + isAuthenticated: () => { + const token = localStorage.getItem('token') + return !!token + }, + getUserType: () => localStorage.getItem('userType') +} diff --git a/src/main/resources/zzz/src/router/index.js b/src/main/resources/zzz/src/router/index.js new file mode 100644 index 0000000..5ab741e --- /dev/null +++ b/src/main/resources/zzz/src/router/index.js @@ -0,0 +1,88 @@ +// src/router/index.js +import { createRouter, createWebHistory } from 'vue-router' + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: [ + { + path: '/', + name: 'LoginPage', + component: () => import('../views/LoginPage.vue'), + meta: { requiresAuth: false } + }, + { + path: '/repairer-register', + name: 'RepairerRegisterPage', + component: () => import('../views/RepairerRegisterPage.vue'), + meta: { requiresAuth: false } + }, + { + path: '/home', + name: 'HomePage', + component: () => import('../views/HomePage.vue'), + meta: { requiresAuth: true } + }, + { + path: '/work-orders', + name: 'WorkOrderList', + component: () => import('../views/WorkOrderList.vue'), + meta: { requiresAuth: true } + }, + { + path: '/profile', + name: 'ProfilePage', + component: () => import('../views/ProfilePage.vue'), + meta: { requiresAuth: true } + }, + { + path: '/work-orders/:id', + name: 'WorkOrderDetail', + component: () => import('../views/WorkOrderDetail.vue'), + meta: { requiresAuth: true } + }, + { + path: '/inspection', + name: 'InspectionPage', + component: () => import('../views/InspectionPage.vue'), + meta: { requiresAuth: true } + }, + { + path: '/inspection/scan', + name: 'InspectionScan', + component: () => import('../views/InspectionScan.vue'), + meta: { requiresAuth: true } + }, + { + path: '/inspection/water-maker', + name: 'WaterMakerList', + component: () => import('../views/WaterMakerList.vue'), + meta: { requiresAuth: true } + }, + { + path: '/inspection/water-maker/:id', + name: 'WaterMakerDetail', + component: () => import('../views/WaterMakerDetail.vue'), + meta: { requiresAuth: true } + }, + { + path: '/inspection/water-supplier', + name: 'WaterSupplierList', + component: () => import('../views/WaterSupplierList.vue'), + meta: { requiresAuth: true } + }, + { + path: '/inspection/water-supplier/:id', + name: 'WaterSupplierDetail', + component: () => import('../views/WaterSupplierDetail.vue'), + meta: { requiresAuth: true } + }, + { + path: '/inspection/form', + name: 'InspectionForm', + component: () => import('../views/InspectionForm.vue'), + meta: { requiresAuth: true } + } + ] +}) + +export default router diff --git a/src/main/resources/zzz/src/router/permission.js b/src/main/resources/zzz/src/router/permission.js new file mode 100644 index 0000000..051aeff --- /dev/null +++ b/src/main/resources/zzz/src/router/permission.js @@ -0,0 +1,36 @@ +// src/router/permission.js +import router from './index' +import { useAuthStore } from '@/stores/auth' + +// 需要登录的白名单 +const whiteList = ['/', '/repairer-register'] + +router.beforeEach((to, from, next) => { + console.log('路由守卫执行:', to.path) + + const authStore = useAuthStore() + + // 如果目标路由在白名单中,直接放行 + if (whiteList.includes(to.path)) { + return next() + } + + // 检查是否已登录 + if (!authStore.isAuthenticated) { + console.warn('未登录,跳转到登录页') + next('/') + } else { + // 已登录,验证用户权限(如果需要) + const userType = authStore.getUserType + console.log('当前用户类型:', userType) + + // 这里可以添加权限验证逻辑 + // 例如:只有特定用户类型可以访问某些页面 + next() + } +}) + +// 路由守卫后置钩子 +router.afterEach((to, from) => { + console.log('路由切换完成:', from.path, '->', to.path) +}) diff --git a/src/main/resources/zzz/src/services/api.js b/src/main/resources/zzz/src/services/api.js new file mode 100644 index 0000000..f4a4649 --- /dev/null +++ b/src/main/resources/zzz/src/services/api.js @@ -0,0 +1,47 @@ +// src/services/api.js +import axios from 'axios' +import { useAuthStore } from '@/stores/auth' + +// 创建最基本的axios实例 +const apiClient = axios.create({ + baseURL: 'http://localhost:8080', + headers: { + 'Content-Type': 'application/json' + } +}) + +// 请求拦截器 +apiClient.interceptors.request.use( + (config) => { + console.log(`请求: ${config.method.toUpperCase()} ${config.url}`) + + // 添加认证头 + const token = localStorage.getItem('token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + + return config + }, + (error) => { + return Promise.reject(error) + } +) + +// 响应拦截器 +apiClient.interceptors.response.use( + (response) => { + return response + }, + (error) => { + // 处理401未授权错误 + if (error.response?.status === 401) { + const authStore = useAuthStore() + authStore.logout() + window.location.href = '/' + } + return Promise.reject(error) + } +) + +export default apiClient diff --git a/src/main/resources/zzz/src/services/authServices.js b/src/main/resources/zzz/src/services/authServices.js new file mode 100644 index 0000000..1bbc6eb --- /dev/null +++ b/src/main/resources/zzz/src/services/authServices.js @@ -0,0 +1,59 @@ +// src/services/authServices.js +import api from './api' + +export const authServices = { + // 通用登录接口(已有) + async login(loginData) { + try { + const response = await api.post('/api/common/login', loginData) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + }, + + // 新增:维修人员注册接口(核心注册功能) + async repairerRegister(registerData) { + try { + // 构建符合后端RegisterRequest的数据结构 + const requestData = { + username: registerData.username, + password: registerData.password, + userType: 'repairman', + repairmanId: registerData.repairmanId, + repairmanName: registerData.repairmanName, + phone: registerData.phone, + areaId: registerData.areaId, + skills: registerData.skills + } + + console.log('发送注册请求:', requestData) + + // 调用后端注册接口 + const response = await api.post('/api/common/register', requestData) + + console.log('注册响应:', response.data) + return response.data + } catch (error) { + console.error('注册失败:', error) + + // 返回标准化的错误格式 + if (error.response?.data) { + // 后端返回的错误 + throw error.response.data + } else if (error.message) { + // 网络或其他错误 + throw { + code: 500, + message: error.message || '网络错误' + } + } else { + // 未知错误 + throw { + code: 500, + message: '注册失败,请重试' + } + } + } + } +} diff --git a/src/main/resources/zzz/src/services/deviceService.js b/src/main/resources/zzz/src/services/deviceService.js new file mode 100644 index 0000000..c2c8e52 --- /dev/null +++ b/src/main/resources/zzz/src/services/deviceService.js @@ -0,0 +1,50 @@ +// src/services/deviceService.js +import api from './api' + +export const deviceService = { + // 获取维修人员辖区设备(按类型) + async getAreaDevicesByType(deviceType) { + try { + const response = await api.get('/api/web/device/repairman/area-devices-by-type', { + params: { deviceType } + }) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + }, + + // 获取设备详情 + async getDeviceDetail(deviceId) { + try { + const response = await api.get(`/api/web/device/${deviceId}`) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + }, + + // 获取设备状态数量统计 + async getDeviceStatusCount(areaId, deviceType) { + try { + const response = await api.get('/api/web/device-status/status-count', { + params: { areaId, deviceType } + }) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + }, + + // 按状态查询设备 + async getDevicesByStatus(status, areaId, deviceType) { + try { + const response = await api.get('/api/web/device-status/by-status', { + params: { status, areaId, deviceType } + }) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + } +} diff --git a/src/main/resources/zzz/src/services/workOrderService.js b/src/main/resources/zzz/src/services/workOrderService.js new file mode 100644 index 0000000..cc7de73 --- /dev/null +++ b/src/main/resources/zzz/src/services/workOrderService.js @@ -0,0 +1,66 @@ +// src/services/workOrderService.js +import api from './api' + +export const workOrderService = { + // 获取我的工单 + async getMyOrders(repairmanId) { + try { + const response = await api.get(`/api/app/repairman/my-orders?repairmanId=${repairmanId}`) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + }, + + // 获取可抢工单 + async getAvailableOrders(areaId) { + try { + const response = await api.get(`/api/app/repairman/available-orders?areaId=${areaId}`) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + }, + + // 抢单 + async grabOrder(orderId, repairmanId) { + try { + const response = await api.post('/api/app/repairman/grab-order', { + orderId, + repairmanId + }) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + }, + + // 拒单 + async rejectOrder(orderId, repairmanId, reason) { + try { + const response = await api.post('/api/app/repairman/reject-order', { + orderId, + repairmanId, + reason + }) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + }, + + // 提交维修结果 + async submitRepairResult(orderId, repairmanId, dealNote, imgUrl = null) { + try { + const response = await api.post('/api/app/repairman/submit-result', { + orderId, + repairmanId, + dealNote, + imgUrl + }) + return response.data + } catch (error) { + throw error.response?.data || error.message + } + } +} diff --git a/src/main/resources/zzz/src/stores/auth.js b/src/main/resources/zzz/src/stores/auth.js new file mode 100644 index 0000000..74851a6 --- /dev/null +++ b/src/main/resources/zzz/src/stores/auth.js @@ -0,0 +1,89 @@ +// src/stores/auth.js +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import router from '@/router/index.js' + +export const useAuthStore = defineStore('auth', () => { + // 状态 + const user = ref(null) + const token = ref(localStorage.getItem('token')) + const isAuthenticated = computed(() => !!token.value && !!user.value) + + // 登录 + const login = (userData, authToken) => { + user.value = userData + token.value = authToken + + // 存储到本地 + localStorage.setItem('token', authToken) + localStorage.setItem('user', JSON.stringify(userData)) + } + + // 获取用户信息 + const getUserInfo = () => { + if (!user.value) { + const storedUser = localStorage.getItem('user') + if (storedUser) { + user.value = JSON.parse(storedUser) + } + } + return user.value + } + + // 获取用户类型 + const getUserType = computed(() => { + return user.value?.userType || localStorage.getItem('userType') + }) + + // 获取维修人员ID + const getRepairmanId = computed(() => { + return user.value?.userId || localStorage.getItem('userId') // 使用userId而不是repairmanId + }) + + // 获取区域ID + const getAreaId = computed(() => { + return user.value?.areaId || localStorage.getItem('areaId') + }) + + // 登出 + const logout = () => { + user.value = null + token.value = null + + // 清除本地存储 + localStorage.removeItem('token') + localStorage.removeItem('user') + localStorage.removeItem('userType') + localStorage.removeItem('repairmanId') + localStorage.removeItem('userId') + localStorage.removeItem('username') + localStorage.removeItem('areaId') // 移除 areaId + + // 跳转到登录页 + router.push('/') + } + + // 初始化时从本地存储恢复 + const initFromStorage = () => { + const storedToken = localStorage.getItem('token') + const storedUser = localStorage.getItem('user') + + if (storedToken && storedUser) { + token.value = storedToken + user.value = JSON.parse(storedUser) + } + } + + return { + user, + token, + isAuthenticated, + getUserType, + getRepairmanId, + getAreaId, // 导出 getAreaId + login, + logout, + getUserInfo, + initFromStorage + } +}) diff --git a/src/main/resources/zzz/src/stores/counter.js b/src/main/resources/zzz/src/stores/counter.js new file mode 100644 index 0000000..b6757ba --- /dev/null +++ b/src/main/resources/zzz/src/stores/counter.js @@ -0,0 +1,12 @@ +import { ref, computed } from 'vue' +import { defineStore } from 'pinia' + +export const useCounterStore = defineStore('counter', () => { + const count = ref(0) + const doubleCount = computed(() => count.value * 2) + function increment() { + count.value++ + } + + return { count, doubleCount, increment } +}) diff --git a/src/main/resources/zzz/src/stores/device.js b/src/main/resources/zzz/src/stores/device.js new file mode 100644 index 0000000..d54b442 --- /dev/null +++ b/src/main/resources/zzz/src/stores/device.js @@ -0,0 +1,33 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useDeviceStore = defineStore('device', () => { + const waterSuppliers = ref([ + { + id: 'A106', + name: '供水机 #A106', + location: 'A区教学楼 1楼走廊', + status: '正常运行', + waterLevel: 66, + storage: 360, + floatValves: { + high: { status: '开启', threshold: 90 }, + low: { status: '关闭', threshold: 10 } + }, + leakDetection: { + status: '无漏水', + lastChecked: '2024-12-26 10:30' + } + }, + // ... 更多设备数据 + ]) + + const getWaterSupplierById = (id) => { + return waterSuppliers.value.find(device => device.id === id || device.name.includes(id)) + } + + return { + waterSuppliers, + getWaterSupplierById + } +}) diff --git a/src/main/resources/zzz/src/views/AboutView.vue b/src/main/resources/zzz/src/views/AboutView.vue new file mode 100644 index 0000000..756ad2a --- /dev/null +++ b/src/main/resources/zzz/src/views/AboutView.vue @@ -0,0 +1,15 @@ + + + diff --git a/src/main/resources/zzz/src/views/HomePage.vue b/src/main/resources/zzz/src/views/HomePage.vue new file mode 100644 index 0000000..a6b810a --- /dev/null +++ b/src/main/resources/zzz/src/views/HomePage.vue @@ -0,0 +1,438 @@ + + + + + diff --git a/src/main/resources/zzz/src/views/HomeView.vue b/src/main/resources/zzz/src/views/HomeView.vue new file mode 100644 index 0000000..6bb706f --- /dev/null +++ b/src/main/resources/zzz/src/views/HomeView.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/main/resources/zzz/src/views/InspectionForm.vue b/src/main/resources/zzz/src/views/InspectionForm.vue new file mode 100644 index 0000000..38ecc7f --- /dev/null +++ b/src/main/resources/zzz/src/views/InspectionForm.vue @@ -0,0 +1,1356 @@ + + + + + diff --git a/src/main/resources/zzz/src/views/InspectionPage.vue b/src/main/resources/zzz/src/views/InspectionPage.vue new file mode 100644 index 0000000..0bcd5de --- /dev/null +++ b/src/main/resources/zzz/src/views/InspectionPage.vue @@ -0,0 +1,245 @@ + + + + + diff --git a/src/main/resources/zzz/src/views/InspectionScan.vue b/src/main/resources/zzz/src/views/InspectionScan.vue new file mode 100644 index 0000000..0097e13 --- /dev/null +++ b/src/main/resources/zzz/src/views/InspectionScan.vue @@ -0,0 +1,255 @@ + + + + + diff --git a/src/main/resources/zzz/src/views/LoginPage.vue b/src/main/resources/zzz/src/views/LoginPage.vue new file mode 100644 index 0000000..114c304 --- /dev/null +++ b/src/main/resources/zzz/src/views/LoginPage.vue @@ -0,0 +1,279 @@ + + + + + diff --git a/src/main/resources/zzz/src/views/ProfilePage.vue b/src/main/resources/zzz/src/views/ProfilePage.vue new file mode 100644 index 0000000..d01bb40 --- /dev/null +++ b/src/main/resources/zzz/src/views/ProfilePage.vue @@ -0,0 +1,545 @@ + + + + + diff --git a/src/main/resources/zzz/src/views/RepairerRegisterPage.vue b/src/main/resources/zzz/src/views/RepairerRegisterPage.vue new file mode 100644 index 0000000..7160e71 --- /dev/null +++ b/src/main/resources/zzz/src/views/RepairerRegisterPage.vue @@ -0,0 +1,617 @@ + + + + + + diff --git a/src/main/resources/zzz/src/views/WaterMakerDetail.vue b/src/main/resources/zzz/src/views/WaterMakerDetail.vue new file mode 100644 index 0000000..c03d436 --- /dev/null +++ b/src/main/resources/zzz/src/views/WaterMakerDetail.vue @@ -0,0 +1,562 @@ + + + + + + diff --git a/src/main/resources/zzz/src/views/WaterMakerList.vue b/src/main/resources/zzz/src/views/WaterMakerList.vue new file mode 100644 index 0000000..84ed641 --- /dev/null +++ b/src/main/resources/zzz/src/views/WaterMakerList.vue @@ -0,0 +1,341 @@ + + + + + diff --git a/src/main/resources/zzz/src/views/WaterSupplierDetail.vue b/src/main/resources/zzz/src/views/WaterSupplierDetail.vue new file mode 100644 index 0000000..0ee6e9a --- /dev/null +++ b/src/main/resources/zzz/src/views/WaterSupplierDetail.vue @@ -0,0 +1,895 @@ + + + + + + diff --git a/src/main/resources/zzz/src/views/WaterSupplierList.vue b/src/main/resources/zzz/src/views/WaterSupplierList.vue new file mode 100644 index 0000000..e488afe --- /dev/null +++ b/src/main/resources/zzz/src/views/WaterSupplierList.vue @@ -0,0 +1,415 @@ + + + + + diff --git a/src/main/resources/zzz/src/views/WorkOrderDetail.vue b/src/main/resources/zzz/src/views/WorkOrderDetail.vue new file mode 100644 index 0000000..088041f --- /dev/null +++ b/src/main/resources/zzz/src/views/WorkOrderDetail.vue @@ -0,0 +1,1126 @@ + + + + + + + diff --git a/src/main/resources/zzz/src/views/WorkOrderList.vue b/src/main/resources/zzz/src/views/WorkOrderList.vue new file mode 100644 index 0000000..64513b3 --- /dev/null +++ b/src/main/resources/zzz/src/views/WorkOrderList.vue @@ -0,0 +1,626 @@ + + + + + diff --git a/src/main/resources/zzz/vite.config.js b/src/main/resources/zzz/vite.config.js new file mode 100644 index 0000000..4217010 --- /dev/null +++ b/src/main/resources/zzz/vite.config.js @@ -0,0 +1,18 @@ +import { fileURLToPath, URL } from 'node:url' + +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import vueDevTools from 'vite-plugin-vue-devtools' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + vueDevTools(), + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + }, + }, +}) diff --git a/src/main/resources/zzz/vitest.config.js b/src/main/resources/zzz/vitest.config.js new file mode 100644 index 0000000..c328717 --- /dev/null +++ b/src/main/resources/zzz/vitest.config.js @@ -0,0 +1,14 @@ +import { fileURLToPath } from 'node:url' +import { mergeConfig, defineConfig, configDefaults } from 'vitest/config' +import viteConfig from './vite.config' + +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + environment: 'jsdom', + exclude: [...configDefaults.exclude, 'e2e/**'], + root: fileURLToPath(new URL('./', import.meta.url)), + }, + }), +) diff --git a/src/main/resources/先读我.md b/src/main/resources/先读我.md new file mode 100644 index 0000000..3b52c1f --- /dev/null +++ b/src/main/resources/先读我.md @@ -0,0 +1 @@ +# 本md仅用于初始化目录,未创建所有子一级目录,在当前目录创建文件后请自行删除 \ No newline at end of file diff --git a/README.md b/src/模块说明.md similarity index 100% rename from README.md rename to src/模块说明.md diff --git a/项目总说明文档.md b/项目总说明文档.md new file mode 100644 index 0000000..e69de29