diff --git a/docs/变更管理/code-adjust-record-2025-07-01-wuzilong.md b/docs/变更管理/code-adjust-record-2025-07-01-wuzilong.md deleted file mode 100644 index 979e41fd..00000000 --- a/docs/变更管理/code-adjust-record-2025-07-01-wuzilong.md +++ /dev/null @@ -1,4811 +0,0 @@ -# 代码调整记录 - 2025-07-01 - wuzilong - -## 2025-07-02 19:30:00 - 实现全局路由守卫功能 - -### 🎯 变更目标 - -- 实现用户登录失效或未登录时,访问任何需要权限的页面都自动跳转到登录页 -- 建立完整的路由权限控制体系 -- 提供多层次的权限检查机制 -- 增强用户体验和系统安全性 - -### 🔧 修改内容 - -**1. 新增全局路由守卫模块** - -文件:`mobile/src/utils/route-guard.js` - -```javascript -// 核心功能 -- checkPageAccess() // 全局页面权限检查 -- guardedNavigate() // 带权限检查的页面跳转 -- guardedSwitchTab() // 带权限检查的Tab切换 -- routeGuardMixin // 页面混入,自动权限检查 -- initRouteGuard() // 初始化全局路由守卫 - -// 配置管理 -- PUBLIC_PAGES: ['/pages/login/index', '/pages/index/index'] // 公开页面列表 -- LOGIN_PAGE: '/pages/login/index' // 登录页路径 -``` - -**功能特性**: -- ✅ 自动检测当前页面路径 -- ✅ 区分公开页面和受保护页面 -- ✅ 多重登录状态验证(isLoggedIn + Token存在 + Token未过期) -- ✅ 优雅的错误提示和跳转 -- ✅ 支持页面加载和显示时的权限检查 -- ✅ 提供页面混入和手动调用两种使用方式 - -**2. 修改应用初始化** - -文件:`mobile/src/App.vue` - -```javascript -import { initRouteGuard } from '@/utils/route-guard'; - -export default { - onLaunch: function () { - console.log('App Launch'); - // 初始化全局路由守卫 - initRouteGuard(); - } -} -``` - -**3. 更新页面权限检查** - -文件:`mobile/src/pages/profile/index.vue` - -```javascript -import { checkPageAccess } from '@/utils/route-guard'; - -// 页面生命周期权限检查 -onLoad() { - if (!checkPageAccess()) return; - // 其他初始化逻辑... -}, -onShow() { - if (!checkPageAccess()) return; - // 刷新用户信息... -} -``` - -文件:`mobile/src/pages/workbench/index.vue` - -```javascript -import { checkPageAccess } from '@/utils/route-guard'; - -// 同样的权限检查模式 -onLoad() { - if (!checkPageAccess()) return; - this.checkLoginStatus(); -}, -onShow() { - if (!checkPageAccess()) return; -} -``` - -**4. 优化首页跳转逻辑** - -文件:`mobile/src/pages/index/index.vue` - -```javascript -import { guardedSwitchTab } from '@/utils/route-guard'; - -// 使用带权限检查的Tab切换 -navigateToService() { - guardedSwitchTab('/pages/workbench/index'); -} -``` - -**5. 增强HTTP请求拦截器** - -文件:`mobile/src/utils/request.js` - -```javascript -// 401响应自动处理 -function handleUnauthorized(message = '登录已失效,请重新登录') { - // 清除登录信息 - uni.removeStorageSync('access_token'); - uni.removeStorageSync('userInfo'); - uni.removeStorageSync('isLoggedIn'); - - // 显示提示并跳转 - uni.showToast({ title: message, icon: 'none' }); - setTimeout(() => { - uni.reLaunch({ url: '/pages/login/index' }); - }, 500); -} - -// HTTP响应拦截 -success: (res) => { - if (res.data.code === 401 || res.statusCode === 401) { - handleUnauthorized(res.data.msg || '登录已失效,请重新登录'); - reject(res.data); - } - // 其他处理... -} -``` - -**6. 新增测试页面** - -文件:`mobile/src/pages/test-route/index.vue` - -- 🧪 路由守卫功能测试页面 -- 📊 实时显示登录状态、Token状态、用户信息 -- 🔧 提供测试按钮:清除Token、带守卫跳转等 -- 📋 详细的测试日志显示 - -### 📝 技术细节 - -**权限检查流程**: - -1. **页面路径检查** → 判断是否为公开页面 -2. **登录状态检查** → userStorage.isLoggedIn() -3. **Token存在检查** → userStorage.getToken() -4. **Token过期检查** → userState.checkTokenExpiration() - -**多层次拦截机制**: - -1. **页面级拦截** → onLoad/onShow 中调用 checkPageAccess() -2. **跳转级拦截** → guardedNavigate/guardedSwitchTab 预检查 -3. **HTTP级拦截** → 401响应自动处理 -4. **应用级拦截** → App进入前台时状态检查 - -**用户体验优化**: - -- ✅ 友好的错误提示信息 -- ✅ 延迟跳转确保Toast显示 -- ✅ 使用reLaunch清除页面栈 -- ✅ 失败时自动尝试备用跳转方式 - -### ✅ 验证结果 - -**权限控制测试**: -- ✅ 未登录访问受保护页面 → 自动跳转登录页 -- ✅ Token过期访问受保护页面 → 清除信息并跳转 -- ✅ 登录后正常访问所有页面 -- ✅ 公开页面无论何时都可访问 - -**跳转功能测试**: -- ✅ guardedNavigate 正确拦截未授权跳转 -- ✅ guardedSwitchTab 正确处理Tab切换 -- ✅ HTTP 401响应自动清理和跳转 - -**兼容性测试**: -- ✅ 不影响现有登录流程 -- ✅ 与现有用户状态管理兼容 -- ✅ 页面加载性能无影响 - -### 🎯 影响评估 - -**安全性提升**: -- 🔒 完全防止未授权页面访问 -- 🔒 自动处理Token失效情况 -- 🔒 多层次权限检查防止绕过 - -**用户体验改善**: -- 🎨 无感知的权限检查 -- 🎨 友好的错误提示 -- 🎨 自动跳转减少用户操作 - -**开发效率提升**: -- 🚀 统一的权限检查工具 -- 🚀 简单的页面集成方式 -- 🚀 完善的测试工具 - -### 💡 最佳实践建议 - -**页面开发规范**: -```javascript -// 在需要权限的页面中 -import { checkPageAccess } from '@/utils/route-guard'; - -onLoad() { - if (!checkPageAccess()) return; - // 页面初始化逻辑 -}, -onShow() { - if (!checkPageAccess()) return; - // 页面显示逻辑 -} -``` - -**页面跳转规范**: -```javascript -// 使用带权限检查的跳转 -import { guardedNavigate, guardedSwitchTab } from '@/utils/route-guard'; - -// 普通页面跳转 -guardedNavigate('/pages/some-page/index'); - -// Tab页面切换 -guardedSwitchTab('/pages/workbench/index'); -``` - -**公开页面配置**: -```javascript -// 在 route-guard.js 中配置公开页面 -const PUBLIC_PAGES = [ - '/pages/login/index', // 登录页 - '/pages/index/index' // 首页(功能受限) -]; -``` - ---- - -## 2025-07-02 18:00:00 - 解决移动端 Buffer 兼容性问题 - -### 🐛 问题描述 - -- **问题现象**: 移动端报错 "ReferenceError: Buffer is not defined" -- **影响范围**: node-rsa 库在浏览器环境中无法使用 -- **技术栈**: uni-app + Vite + crypto-js + node-rsa - -### 🔧 解决方案 - -**1. 尝试 polyfill 方案** - -文件:`mobile/vite.config.js` - -```javascript -import { nodePolyfills } from 'vite-plugin-node-polyfills'; - -plugins: [ - uni(), - nodePolyfills({ - include: ['buffer', 'crypto', 'stream', 'util'], - globals: { - Buffer: true, - global: true, - process: true, - }, - }), -], -define: { - global: 'globalThis', -} -``` - -**2. 最终采用 JSEncrypt 替代方案** - -文件:`mobile/src/utils/encrypt-jsencrypt.js` - -- ✅ 使用 JSEncrypt 库替代 node-rsa -- ✅ 完全浏览器兼容,无需 polyfill -- ✅ 与 custom-login-script.js 保持一致的加密逻辑 -- ✅ 支持相同的 RSA 公钥和 Base64 编码格式 - -**3. 依赖包更新** - -```bash -npm install jsencrypt buffer vite-plugin-node-polyfills -``` - -### 📝 技术细节 - -**问题根因**: - -- node-rsa 是 Node.js 环境的库,依赖 Buffer 等 Node.js 全局对象 -- 浏览器环境缺少这些对象,导致运行时错误 -- uni-app 在 H5 端运行在浏览器环境中 - -**解决思路**: - -1. **Polyfill 方案**: 使用 vite-plugin-node-polyfills 提供 Buffer 等 polyfill -2. **库替换方案**: 使用 JSEncrypt 等浏览器原生加密库 - -**最终选择 JSEncrypt 的原因**: - -- 专为浏览器设计,兼容性更好 -- 体积更小,性能更优 -- 与后端 RSA 加密完全兼容 -- 无需复杂的 polyfill 配置 - -### ✅ 验证结果 - -**兼容性测试**: - -- ✅ 移动端 H5 环境正常运行 -- ✅ 无 Buffer 相关错误 -- ✅ RSA 加密功能正常 -- ✅ AES 加密数据格式正确 - -**加密测试**: - -- ✅ AES 密钥生成正常 -- ✅ AES 数据加密成功 -- ✅ RSA 密钥加密成功 -- ✅ 请求头格式符合要求 - -### 🎯 影响评估 - -**稳定性提升**: - -- 消除了 Node.js 依赖相关的兼容性问题 -- 提供了更稳定的浏览器运行环境 -- 减少了 polyfill 的复杂性和潜在问题 - -**性能优化**: - -- JSEncrypt 库体积更小 -- 减少了 polyfill 加载时间 -- 原生浏览器支持,性能更好 - -**开发体验**: - -- 减少了配置复杂度 -- 错误更容易调试 -- 更符合前端开发最佳实践 - -### 💡 经验总结 - -1. **库选择原则**: 前端项目优先选择浏览器原生支持的库 -2. **兼容性考虑**: Node.js 库在浏览器中使用需要额外注意 -3. **polyfill 权衡**: polyfill 会增加打包体积和复杂度 -4. **测试覆盖**: 跨环境兼容性需要充分测试 - -### 📄 相关文档 - -- **JSEncrypt 官网**: https://github.com/travist/jsencrypt -- **Vite Node Polyfills**: https://github.com/davidmyersdev/vite-plugin-node-polyfills - ---- - -## 2025-07-02 17:30:00 - 实现移动端 AES+RSA 双重加密登录 - -### 🎯 变更目标 - -- 根据 custom-login-script.js 的加密逻辑,实现移动端的 AES+RSA 双重加密登录 -- 修复登录接口入参格式问题(从 JSON 对象改为加密字符串) -- 保证与后端登录接口的完全兼容 -- 正确保存和使用 Token 进行后续 API 调用 - -### 🔧 修改内容 - -**1. 新增加密工具模块** - -文件:`mobile/src/utils/encrypt.js` - -```javascript -// 核心加密功能 -- generateRandomAESKey() // 生成32位随机AES密钥 -- encryptWithAES() // AES-ECB-PKCS7加密 -- encryptAESKeyWithRSA() // RSA公钥加密AES密钥 -- encryptLoginData() // 登录数据完整加密处理 - -// 配置信息 -- RSA_PUBLIC_KEY: 与后端保持一致的RSA公钥 -- CLIENT_ID: e5cd7e4891bf95d1d19206ce24a7b32e -- TENANT_ID: 000000 -``` - -**2. 重写登录 API 方法** - -文件:`mobile/src/utils/request.js` - -- ✅ 添加 crypto-js 和 node-rsa 依赖支持 -- ✅ 登录方法改为 async/await 模式 -- ✅ 使用 encryptLoginData 加密登录数据 -- ✅ 发送加密字符串而非 JSON 对象 -- ✅ 按照测试脚本要求设置请求头: - ```javascript - headers: { - 'Content-Type': 'application/json;charset=utf-8', - clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e', - 'encrypt-key': encryptedAESKey, - 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', - isToken: 'false', - isEncrypt: 'true', - repeatSubmit: 'false', - referer: baseURL - } - ``` - -**3. 优化 Token 处理** - -- ✅ 正确保存 access_token 到本地存储 -- ✅ 后续 API 调用使用 Bearer Token 格式 -- ✅ 优化 getUserInfo 方法的请求头设置 -- ✅ 添加详细的加密和 Token 使用日志 - -**4. 安装必要依赖** - -```bash -npm install crypto-js node-rsa -``` - -### 📝 技术细节 - -**加密流程**: - -1. 生成 32 位随机 AES 密钥 -2. 使用 AES-ECB-PKCS7 模式加密登录数据 JSON -3. 使用 RSA 公钥加密 AES 密钥 -4. 在请求头中传递加密后的 AES 密钥 -5. 请求体为 AES 加密后的字符串 - -**请求格式对比**: - -- ❌ 修改前:发送 JSON 对象 `{username: "admin", password: "admin123", ...}` -- ✅ 修改后:发送加密字符串 `"encrypted_base64_string"` - -**Token 处理**: - -- 登录成功后保存 `access_token` -- 后续 API 使用 `Authorization: Bearer ${token}` 格式 -- 包含必需的 clientid 和 referer 头信息 - -### ✅ 验证结果 - -**加密功能**: - -- ✅ AES 密钥生成正常(32 位随机字符串) -- ✅ AES 数据加密成功 -- ✅ RSA 密钥加密成功 -- ✅ 请求头格式符合后端要求 - -**登录流程**: - -- ✅ 登录数据正确加密并发送 -- ✅ 能够成功获取 Token -- ✅ Token 正确保存到本地存储 -- ✅ 后续 getUserInfo 调用成功 - -**兼容性**: - -- ✅ 与 custom-login-script.js 完全一致的加密逻辑 -- ✅ 与后端登录接口完全兼容 -- ✅ 支持现有的用户信息管理机制 - -### 🎯 影响评估 - -**安全性提升**: - -- 登录密码通过 AES+RSA 双重加密传输 -- AES 密钥每次随机生成,保证安全性 -- 完全符合后端安全要求 - -**功能完善**: - -- 移动端登录功能与 web 端保持一致 -- 支持完整的 Token 认证流程 -- 为后续 API 开发提供标准模板 - -**开发体验**: - -- 详细的加密过程日志,便于调试 -- 模块化的加密工具,可复用 -- 错误处理完善,用户体验良好 - -### 💡 技术要点 - -1. **依赖包选择**: 使用 crypto-js 和 node-rsa,与测试脚本保持一致 -2. **加密参数**: AES-ECB-PKCS7 模式,RSA-PKCS1 加密方案 -3. **请求格式**: Content-Type 为 application/json,但 body 为加密字符串 -4. **Token 格式**: Bearer Token,包含必需的请求头信息 - -### 📄 相关文档 - -- **参考脚本**: `test/custom-login-script.js` -- **API 规范**: `docs/开发文档/API/用户认证登录API.md` -- **移动端规范**: `.cursor/cursorrules-mobile.md` - ---- - -## 2025-07-02 17:00:00 - 解决移动端登录 CORS 跨域问题 - -### 🐛 问题描述 - -- **问题现象**: 移动端登录时报错 "Access to XMLHttpRequest at 'http://localhost:8080/auth/login' from origin 'http://localhost:5173' has been blocked by CORS policy" -- **影响范围**: 移动端开发环境 API 调用 -- **技术栈**: uni-app + Vite 开发服务器 - -### 🔧 解决方案 - -**1. 配置 Vite 代理** - -文件:`mobile/vite.config.js` - -```javascript -// 开发服务器配置 -server: { - port: 5173, - host: '0.0.0.0', - open: true, - // 代理配置解决CORS问题 - proxy: { - '/api': { - target: 'http://localhost:8080', - changeOrigin: true, - rewrite: (path) => path.replace(/^\/api/, '') - } - } -} -``` - -**2. 更新 API 请求地址** - -文件:`mobile/src/utils/request.js` - -```javascript -// API请求配置 -const baseURL = - process.env.NODE_ENV === 'development' - ? '/api' // 开发环境使用代理 - : 'https://your-api-domain.com'; -``` - -### 📝 技术细节 - -- **CORS 问题**: 浏览器同源策略阻止 localhost:5173 访问 localhost:8080 -- **代理原理**: Vite 开发服务器将/api/\*请求转发到后端服务器 -- **路径重写**: 去除/api 前缀,保持与后端 API 路径一致 -- **changeOrigin**: 修改请求头中的 Origin 字段,避免 CORS 限制 - -### ✅ 验证结果 - -- ✅ 移动端开发环境可以正常调用后端 API -- ✅ 登录功能不再报 CORS 错误 -- ✅ 其他 API 接口也通过代理正常访问 -- ✅ 生产环境配置保持不变 - -### 🎯 影响评估 - -**开发体验**: - -- 彻底解决开发环境跨域问题 -- 无需后端配置 CORS 策略 -- 开发环境与生产环境行为一致 - -**配置管理**: - -- 开发环境使用代理,生产环境使用真实域名 -- 配置清晰,易于维护 - -### 💡 最佳实践 - -1. **代理配置**: 开发环境统一使用代理解决跨域 -2. **环境区分**: 开发/生产环境使用不同的 baseURL -3. **路径规范**: API 路径保持与后端一致,避免混淆 - ---- - -## 2025-07-02 16:30:00 - 完善移动端登录功能和用户信息管理 - -### 🎯 变更目标 - -- 根据 web 端登录逻辑完善移动端登录功能 -- 实现完整的用户信息存储和管理 -- 添加权限检查和状态管理 -- 优化用户体验和错误处理 - -### 🔧 修改内容 - -**1. 完善登录页面功能** - -文件:`mobile/src/pages/login/index.vue` - -- ✅ 集成真实的登录 API 调用 -- ✅ 添加租户列表获取和处理 -- ✅ 实现完整的登录流程(登录 → 获取用户信息 → 保存 → 跳转) -- ✅ 添加登录状态显示和加载动画 -- ✅ 优化错误处理和用户提示 -- ✅ 支持密码框回车确认登录 - -**2. 新增用户管理工具** - -文件:`mobile/src/utils/user.js` - -```javascript -// 用户信息管理 -export const userManager = { - getCurrentUser(), // 获取当前用户信息 - hasPermission(), // 权限检查 - hasRole(), // 角色检查 - isAdmin(), // 管理员检查 - getDisplayName(), // 获取显示名称 - getAvatarUrl(), // 获取头像URL - updateUserInfo(), // 更新用户信息 - formatUserForDisplay() // 格式化用户信息 -}; - -// 权限控制工具 -export const permissionUtils = { - checkPagePermission(), // 页面权限检查 - navigateWithPermission() // 权限控制的页面跳转 -}; - -// 用户状态管理 -export const userState = { - checkTokenExpiration(), // Token过期检查 - refreshToken(), // Token刷新 - autoLoginCheck() // 自动登录检查 -}; -``` - -**3. 优化请求工具** - -文件:`mobile/src/utils/request.js` - -- ✅ 增强`saveLoginInfo`方法,记录登录时间和 Token 过期时间 -- ✅ 支持 refresh_token 存储 -- ✅ 改进登录信息的存储结构 - -**4. 更新页面集成** - -文件:`mobile/src/pages/index/index.vue`、`mobile/src/pages/profile/index.vue` - -- ✅ 集成新的用户管理工具 -- ✅ 使用`userState.autoLoginCheck()`进行登录状态检查 -- ✅ 使用`userManager.formatUserForDisplay()`获取用户显示信息 -- ✅ 优化用户信息加载逻辑 - -### ✅ 验证结果 - -**登录功能**: - -- ✅ 支持 admin/admin123 测试账号登录 -- ✅ 登录成功后正确跳转到首页 -- ✅ 登录失败时显示具体错误信息 -- ✅ 登录过程中显示加载状态 - -**用户信息**: - -- ✅ 首页和个人中心正确显示用户名称 -- ✅ 用户信息格式化和显示正常 -- ✅ 退出登录功能正常工作 - -**状态管理**: - -- ✅ 页面刷新后保持登录状态 -- ✅ 未登录时自动跳转到登录页 -- ✅ Token 过期检查机制工作正常 - -### 🎯 影响评估 - -**功能完善**: - -- 移动端登录从演示功能升级为完整业务功能 -- 统一的用户信息管理,便于后续功能扩展 -- 完善的权限控制机制 - -**开发效率**: - -- 提供标准化的用户管理工具 -- 简化页面中的用户信息获取和权限检查 -- 便于后续功能模块集成 - -**用户体验**: - -- 完整的登录反馈和状态提示 -- 自动登录状态检查,减少重复登录 -- 优雅的错误处理和用户提示 - -### 📄 相关文档 - -- **API 规范**: `docs/开发文档/API/用户认证登录API.md` -- **移动端规范**: `.cursor/cursorrules-mobile.md` - ---- - -## 2025-07-02 11:00:00 - 修复移动端 H5 启动 JavaScript 异常问题 - -### 🐛 问题描述 - -- **问题现象**: 移动端 http://localhost:8081/访问提示"很抱歉,校园管理移动端需要启用 JavaScript 才能正常运行。" -- **影响范围**: uniapp 移动端 H5 开发模式 -- **技术栈**: uni-app + uView UI + Vite 4.4.6 - -### 🔧 解决方案 - -1. **重启开发服务器**: - - ```bash - # 停止旧进程 - pkill -f "npm run dev:h5" - - # 重新启动H5开发服务器 - npm run dev:h5 - ``` - -2. **验证项目配置**: - - 检查`vite.config.js`端口配置(8081) - - 确认 uniapp 插件正确加载 - - 验证项目结构完整性 - -### 📝 技术细节 - -- **Vite 配置**: 端口 8081,host 0.0.0.0,cors 启用 -- **启动时间**: 编译时间 644ms -- **访问地址**: - - 本地: http://localhost:8081/ - - 网络: http://172.27.137.251:8081/ - - 网络: http://10.241.40.29:8081/ - -### ✅ 验证结果 - -- ✅ 移动端 H5 正常启动并运行 -- ✅ 页面标题正确显示"校园管理移动端" -- ✅ JavaScript 正常加载,无 noscript 提示 -- ✅ 浏览器可正常访问 http://localhost:8081/ -- ✅ Vite 开发服务器稳定运行 - -### 📄 项目信息 - -- **项目名称**: 校园管理移动端 (xgxt-mobile) -- **技术栈**: uniapp (Vue 3) + uView UI 2.x -- **构建工具**: Vite 4.4.6 -- **开发模式**: H5 端开发调试 - -### 🎯 影响评估 - -- **正向影响**: 移动端开发环境恢复正常,支持实时调试 -- **开发效率**: 支持热重载,代码修改即时生效 -- **兼容性**: 支持多端发布(H5、微信小程序、APP) - -### 💡 经验总结 - -- **问题根因**: 开发服务器进程异常或构建不完整 -- **解决思路**: 完全重启开发环境,确保构建过程完整 -- **预防措施**: 定期检查开发服务器状态,及时重启异常进程 - ---- - -## 2025-07-01 17:40:00 - 修复 Dialog 关闭按钮 hover 灰色背景问题 - -### 🐛 问题描述 - -- **问题现象**: 场地管理新增场地弹窗中,鼠标移动到左上角叉号时出现灰色背景 -- **影响范围**: 所有 Element Plus Dialog 弹窗的关闭按钮 hover 效果 -- **用户体验**: 灰色背景影响视觉美观,用户希望去除这个背景 - -### 🔧 解决方案 - -**修改文件**: `web/src/main.ts` - -1. **在 hover 状态中添加透明背景**: - ```css - .el-dialog__headerbtn:hover { - color: #409eff !important; - background: transparent !important; /* 新增:强制背景透明 */ - } - ``` - -### 📝 技术细节 - -- **原因分析**: Element Plus 默认的 hover 状态可能包含背景色样式 -- **解决思路**: 在现有 hover 样式中明确设置`background: transparent` -- **影响评估**: 只影响视觉效果,不影响功能 - -### ✅ 验证结果 - -- ✅ Dialog 关闭按钮 hover 时保持透明背景 -- ✅ 颜色变化效果保持正常(文字颜色变为蓝色) -- ✅ 所有弹窗关闭按钮表现一致 -- ✅ 视觉体验更加清洁美观 - -### 📄 相关文件 - -- `web/src/main.ts` - 主要修复文件 - -### 🎯 影响评估 - -- **正向影响**: 提升 Dialog 弹窗关闭按钮的视觉体验 -- **无副作用**: 保持原有 hover 交互效果,只去除背景色 -- **构建状态**: 成功构建,无编译错误 - ---- - -## 📅 2025-07-02 00:15:00 - 彻底修复场地预约我的预约接口 SQL 语法错误 - -### 🎯 变更目标 - -- 彻底修复 `/venue/reservation/my` 接口的 SQL 语法错误 -- 解决 MyBatis-Plus 拼接 WHERE 子句时缺少 AND 关键字的问题 -- 完善 `${ew.sqlSegment}` 的条件拼接逻辑 - -### 🔧 问题描述 - -**SQL 语法错误**: - -``` -Failed to process, Error SQL: -WHERE deleted = 0 - (applicant_id = ?) ORDER BY create_time DESC -order by create_time desc -``` - -**错误分析**: - -1. **WHERE 子句语法错误**:`${ew.sqlSegment}`前面多了`and`关键字 -2. **重复 ORDER BY 子句**:XML 中写了`order by create_time desc`,Service 中`buildQueryWrapper`也设置了`.orderByDesc()` - -**根本原因**: - -- Mapper.xml 中的`selectReservationPage`方法 SQL 拼接逻辑不正确 -- `${ew.sqlSegment}`内容不包含 AND 前缀,直接拼接导致语法错误 -- 需要在 `deleted = 0` 和 `${ew.sqlSegment}` 之间显式添加 AND - -### 📁 修复内容 - -**修复 HqVenueReservationMapper.xml**: - -```xml - - - - - -``` - -**修复要点**: - -1. **显式添加 AND**:在 `deleted = 0` 和 `${ew.sqlSegment}` 之间添加 AND -2. **条件判断**:使用 `` 标签判断 `${ew.sqlSegment}` 是否存在 -3. **安全拼接**:确保 SQL 语法正确 - -### 🛠️ 修复过程 - -1. **问题定位**:通过错误日志分析 SQL 拼接问题 -2. **检查代码**:定位到 Controller→Service→Mapper 的调用链 -3. **修复 Mapper.xml**:调整 SQL 拼接逻辑 -4. **重新构建 venue 模块**: - ```bash - cd ruoyi-modules/ruoyi-venue - mvn clean install -U -Dmaven.test.skip=true -q - ``` -5. **重新构建主应用**: - ```bash - cd ruoyi-admin - mvn clean compile -U -Dmaven.test.skip=true -q - ``` -6. **触发热重启**:修改`.reloadtrigger`文件内容 - -### 🎯 修复效果 - -**功能恢复**: - -- ✅ `/venue/reservation/my` 接口正常响应 -- ✅ SQL 语法错误完全解决 -- ✅ 我的预约列表正常加载 -- ✅ 分页和排序功能正常 - -**技术改进**: - -- ✅ Mapper.xml 中 SQL 拼接逻辑更加合理 -- ✅ 消除了重复的 ORDER BY 子句 -- ✅ 提升了 MyBatis-Plus 与自定义 SQL 的兼容性 - -### 📊 技术细节 - -**SQL 拼接原理**: - -```sql --- ${ew.sqlSegment} 实际生成的片段 -(applicant_id = ?) ORDER BY create_time DESC - --- 错误的拼接结果(第一次修复后) -WHERE deleted = 0 (applicant_id = ?) ORDER BY create_time DESC - --- 正确的拼接结果(最终修复版) -WHERE deleted = 0 AND (applicant_id = ?) ORDER BY create_time DESC -``` - -**关键发现**: - -- `${ew.sqlSegment}` 的内容**不包含前置 AND** -- 必须在固定条件和动态条件之间**显式添加 AND** -- 使用 `` 标签进行安全的条件拼接 - -**MyBatis-Plus 拼接规则**: - -- `${ew.sqlSegment}`包含完整的条件和排序片段 -- XML 中的``标签会自动处理第一个条件的`AND` -- 不应该在`${ew.sqlSegment}`前手动添加`AND` - -### 💡 最佳实践 - -**Mapper.xml 自定义 SQL 规范**: - -1. **使用``标签**:自动处理条件拼接 -2. **正确使用`${ew.sqlSegment}`**: - - `${ew.sqlSegment}` 内容不包含前置 AND - - 固定条件和动态条件之间需要显式添加 AND - - 使用 `` 标签进行安全拼接 -3. **避免重复 ORDER BY**:要么在 XML 中写,要么在 Java 代码中设置 -4. **统一排序策略**:建议在 Java 代码中使用 LambdaQueryWrapper 设置排序 - -**推荐的安全拼接模式**: - -```xml - - fixed_condition = value - - AND ${ew.sqlSegment} - - -``` - -**SQL 拼接检查清单**: - -- [ ] `${ew.sqlSegment}`前是否误加了`AND`? -- [ ] 是否存在重复的 ORDER BY 子句? -- [ ] ``标签是否正确使用? -- [ ] 分页查询是否正常工作? - -### 🔍 相关知识点 - -**MyBatis-Plus 动态 SQL**: - -- `${ew.sqlSegment}`:完整的 WHERE 条件和 ORDER BY 片段 -- ``标签:自动添加 WHERE 关键字,处理第一个 AND -- LambdaQueryWrapper:类型安全的条件构造器 - -**问题预防**: - -- 编写自定义 SQL 时要理解 MyBatis-Plus 的拼接机制 -- 测试时要验证生成的完整 SQL 语句 -- 使用 MyBatis 日志输出完整 SQL 进行调试 - ---- - -## 📅 2025-07-01 23:45:00 - 制定 VO 类基础字段统一规范 - -### 🎯 变更目标 - -- 制定项目级别的 VO 类基础字段统一规范 -- 解决 MyBatis 映射错误的根本性问题 -- 确保所有 VO 类具有一致的字段结构 -- 为代码生成器和开发流程提供标准模板 - -### 🔧 问题背景 - -**实际开发中发现的问题**: - -1. **查询方式差异**: - - - MyBatis-Plus 自动查询:不包含继承字段,无需基础字段 - - 自定义 SQL 查询:明确包含字段,必须有对应字段 - -2. **开发不一致**: - - - `HqSatCategoryVo`:使用自动查询,无基础字段 - - `HqVenueReservationVo`:使用自定义 SQL,缺少基础字段导致错误 - -3. **维护困难**: - - 开发者无法预知将来会使用哪种查询方式 - - 字段缺失导致运行时错误,影响开发效率 - -### 📁 规范内容 - -**统一 VO 类基础字段标准**: - -```java -// 所有VO类必须包含的四个基础字段 -private Long createBy; // 创建者 -private Date createTime; // 创建时间(含JSON格式化) -private Long updateBy; // 更新者 -private Date updateTime; // 更新时间(含JSON格式化) -``` - -**字段要求**: - -- **类型统一**:createBy/updateBy 为 Long,时间字段为 Date -- **注解规范**:时间字段使用`@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")` -- **Excel 支持**:createTime 可选导出,updateTime 通常不导出 - -### 🛠️ 规范实施 - -**1. 更新后端开发规范**: - -- 在`.cursor/cursorrules-backend.md`中添加"VO 类基础字段规范(强制性)"章节 -- 提供标准 VO 类模板 -- 建立检查清单和修复流程 - -**2. 规范内容包括**: - -- 📋 标准 VO 类基础字段模板 -- 🛠️ 基础字段说明表格 -- 📝 字段使用注解规范 -- 🚨 强制性要求列表 -- 🔍 VO 类检查清单 -- 💡 最佳实践指导 -- 🎯 修复现有问题的标准流程 -- 📊 实际修复案例总结 - -**3. 开发流程规范**: - -``` -新增VO类 → 使用标准模板 → 包含基础字段 → 编译测试 -修复VO类 → 补全缺失字段 → 重新构建 → 热重启验证 -``` - -### 🎯 规范效果 - -**解决的问题**: - -- ✅ 彻底解决 MyBatis 映射错误 -- ✅ 消除不同查询方式的兼容性问题 -- ✅ 统一项目中所有 VO 类的字段结构 -- ✅ 提供清晰的开发和修复指导 - -**预防的问题**: - -- ✅ 避免运行时字段缺失错误 -- ✅ 减少开发过程中的调试时间 -- ✅ 保证代码的一致性和可维护性 -- ✅ 支持未来查询方式的变更 - -**技术价值**: - -- ✅ 兼容性:支持所有查询方式 -- ✅ 一致性:统一的 VO 类结构 -- ✅ 可维护性:减少运行时错误 -- ✅ 可扩展性:支持未来功能扩展 - -### 📊 规范应用 - -**立即适用**: - -- 新开发的 VO 类必须遵循新规范 -- 发现字段缺失错误时按规范修复 -- 代码评审中检查 VO 类基础字段完整性 - -**长期价值**: - -- 为代码生成器提供标准模板 -- 建立项目级别的开发标准 -- 积累最佳实践经验 -- 提升整体代码质量 - -### 🚨 实施要求 - -**强制性规范**: - -1. **所有新增 VO 类必须包含四个基础字段** -2. **修复现有 VO 类时必须补全缺失字段** -3. **字段类型和注解必须符合规范** -4. **开发过程中必须进行 VO 类检查** - -**违规检查**: - -- 开发阶段:新增 VO 类字段检查 -- 测试阶段:MyBatis 映射错误检查 -- 生产阶段:接口数据完整性检查 - -**规范维护**: - -- 定期更新和完善规范内容 -- 收集实际应用中的问题和经验 -- 持续优化开发流程和工具 - ---- - -## 📅 2025-07-01 23:40:00 - 修复场地预约列表 MyBatis 映射错误 - -### 🎯 变更目标 - -- 修复场地预约列表展示时的 MyBatis 映射错误 -- 解决 HqVenueReservationVo 类中缺少基础字段导致的属性设置失败问题 -- 确保场地预约界面能正常加载数据 - -### 🔧 问题描述 - -**MyBatis 映射错误**: - -``` -org.mybatis.spring.MyBatisSystemException: -Could not set property 'createBy' of 'class org.dromara.venue.domain.vo.HqVenueReservationVo' with value '1' -There is no setter for property named 'createBy' in 'class org.dromara.venue.domain.vo.HqVenueReservationVo' -``` - -**根本原因**: - -- HqVenueReservationVo 类中缺少 createBy、updateBy、updateTime 等基础字段 -- SQL 查询包含了这些字段,但 VO 类没有对应的属性和 setter 方法 -- MyBatis 无法完成结果集到 VO 对象的映射 - -### 📁 修复内容 - -**在 HqVenueReservationVo 类中添加缺失字段**: - -```java -/** - * 创建者 - */ -private Long createBy; - -/** - * 更新者 - */ -private Long updateBy; - -/** - * 更新时间 - */ -@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") -private Date updateTime; -``` - -### 🛠️ 修复过程 - -1. **问题定位**:通过错误日志确认是 VO 类字段缺失问题 -2. **修复 VO 类**:添加缺失的 createBy、updateBy、updateTime 字段 -3. **重新构建 venue 模块**: - ```bash - cd ruoyi-modules/ruoyi-venue - mvn clean install -U -Dmaven.test.skip=true -q - ``` -4. **重新构建主应用**: - ```bash - cd ruoyi-admin - mvn clean compile -U -Dmaven.test.skip=true -q - ``` -5. **触发热重启**:修改 `.reloadtrigger` 文件内容 - -### 🎯 修复效果 - -**功能恢复**: - -- ✅ 场地预约界面能正常打开 -- ✅ 数据列表正常加载和显示 -- ✅ MyBatis 映射错误解决 -- ✅ 基础字段信息正常显示 - -**技术改进**: - -- ✅ VO 类字段完整性提升 -- ✅ 与 Entity 类字段保持一致 -- ✅ 支持创建人和更新信息的显示 - ---- - -## 📅 2025-07-01 23:35:00 - 完全修复场地管理界面 MyBatis 映射错误 - -### 🎯 变更目标 - -- 彻底修复场地管理界面列表检索数据时的 MyBatis 映射错误 -- 解决 HqVenueInfoVo 类修改后系统仍使用旧版本 jar 包的问题 -- 通过重新构建和热重启确保修改生效 -- 确保场地管理界面能正常加载数据 - -### 🔧 问题描述 - -**MyBatis 映射错误**: - -``` -org.mybatis.spring.MyBatisSystemException: -Could not set property 'createBy' of 'class org.dromara.venue.domain.vo.HqVenueInfoVo' with value '1' -Cause: There is no setter for property named 'createBy' in 'class org.dromara.venue.domain.vo.HqVenueInfoVo' -``` - -**根本原因**: - -- HqVenueInfoVo 类中缺少 createBy、updateBy、updateTime 等基础字段 -- SQL 查询包含了这些字段,但 VO 类没有对应的属性和 setter 方法 -- MyBatis 无法完成结果集到 VO 对象的映射 - -### 📁 修复内容 - -**在 HqVenueInfoVo 类中添加缺失字段**: - -```java -/** - * 创建者 - */ -private Long createBy; - -/** - * 更新者 - */ -private Long updateBy; - -/** - * 更新时间 - */ -@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") -private Date updateTime; -``` - -### 🛠️ 修复过程 - -**第一阶段:问题分析和字段修复** - -1. **问题定位**:通过错误日志确认是 VO 类字段缺失问题 -2. **检查 Entity 类**:确认 HqVenueInfo 继承 TenantEntity,包含基础字段 -3. **检查 Mapper.xml**:确认字段映射配置正确 -4. **修复 VO 类**:添加缺失的 createBy、updateBy、updateTime 字段 - -**第二阶段:jar 包版本问题解决** 5. **发现问题**:错误日志显示系统仍使用旧版本 jar 包 - -``` -jar:file:/Users/wuzilong/work/repository/org/dromara/ruoyi-venue/5.4.0/ruoyi-venue-5.4.0.jar -``` - -6. **强制重建 venue 模块**: - ```bash - cd ruoyi-modules/ruoyi-venue - mvn clean install -U -Dmaven.test.skip=true - ``` -7. **重新构建主应用**: - ```bash - cd ruoyi-admin - mvn clean compile -U -Dmaven.test.skip=true - ``` -8. **触发热重启**:修改 `.reloadtrigger` 文件内容 - -### 🎯 修复效果 - -**功能恢复**: - -- ✅ 场地管理界面能正常打开 -- ✅ 数据列表正常加载和显示 -- ✅ MyBatis 映射错误解决 -- ✅ 基础字段信息正常显示 - -**技术改进**: - -- ✅ VO 类字段完整性提升 -- ✅ 与 Entity 类字段保持一致 -- ✅ 支持创建人和更新信息的显示 -- ✅ 解决了 jar 包版本不一致问题 -- ✅ 热重启机制正常工作 -- ✅ 本地仓库 jar 包成功更新 - -### 📊 技术细节 - -**字段映射关系**: - -```xml - - - - -``` - -**SQL 查询字段**: - -```sql -SELECT id, venue_name, venue_code, venue_location, venue_capacity, venue_type, venue_status, - venue_description, venue_images, venue_manager_id, venue_manager_name, - create_by, create_time, update_by, update_time -FROM hq_venue_info -``` - -### 💡 经验总结 - -**问题预防**: - -- VO 类字段应与 Entity 类保持一致 -- 特别注意继承的基础字段 -- SQL 查询的字段必须在 VO 类中有对应属性 - -**最佳实践**: - -- 使用代码生成器确保字段一致性 -- 定期检查 VO 类与 Entity 类的字段对应关系 -- MyBatis 映射错误要及时检查字段定义 -- **jar 包版本管理**: - - 修改源码后必须重新构建和安装模块 - - 检查错误日志中的 jar 包路径,确认使用的版本 - - 使用 `-U` 参数强制更新依赖 - - 通过热重启触发文件确保系统重新加载 - -**修复标准**: - -- Entity 继承基础类的字段,VO 也应包含 -- 字段命名遵循驼峰命名规范 -- 日期字段添加 JSON 格式化注解 -- **版本同步标准**: - - 本地源码修改后立即重新构建 - - 确认本地仓库 jar 包版本更新 - - 验证系统加载的是新版本类文件 - ---- - -## 📅 2025-07-01 23:25:00 - 再次修复场地菜单图标兼容性 - -### 🎯 变更目标 - -- 修复场地管理和使用统计图标在前端系统中的兼容性问题 -- 使用更通用和兼容的图标名称,确保在前端正常显示 -- 解决部分图标名称在 ruoyi 系统中不兼容的问题 - -### 🔧 问题描述 - -**图标兼容性问题**: - -- 之前使用的 `office-building` 和 `data-analysis` 图标可能在前端系统中不兼容 -- 部分图标名称在 Element Plus 或 ruoyi 系统中无法正确显示 -- 需要使用更通用和稳定的图标名称 - -### 📁 修复内容 - -**图标兼容性更新**:使用更通用的图标名称,确保前端兼容性 - -**最终修复结果**: - -``` -+---------+--------------+----------+-----------+-----------+---------------------+ -| menu_id | menu_name | icon | parent_id | menu_type | update_time | -+---------+--------------+----------+-----------+-----------+---------------------+ -| 2210 | 场地管理 | building | 2140 | C | 2025-07-01 23:17:34 | -| 2230 | 使用统计 | monitor | 2140 | C | 2025-07-01 23:17:36 | -+---------+--------------+----------+-----------+-----------+---------------------+ -``` - -### 🎨 图标选择说明 - -**新图标的优势**: - -1. **场地管理** (`building`): - - - 建筑图标,直观表示场地建筑 - - 通用性强,在大部分 UI 框架中都有支持 - - 语义明确,与场地管理功能匹配 - -2. **使用统计** (`monitor`): - - 监控图标,表示数据监控和统计 - - 兼容性好,是常见的系统图标 - - 与统计监控功能高度契合 - -### 🛠️ 修复执行 - -**SQL 修复命令**: - -```sql --- 场地管理 - 使用通用的building图标 -UPDATE sys_menu SET icon = 'building', update_time = NOW(), update_by = 1 -WHERE menu_id = 2210 AND menu_name = '场地管理'; - --- 使用统计 - 使用monitor图标表示监控统计 -UPDATE sys_menu SET icon = 'monitor', update_time = NOW(), update_by = 1 -WHERE menu_id = 2230 AND menu_name = '使用统计'; -``` - -### 🎯 修复效果 - -**兼容性提升**: - -- ✅ 使用 ruoyi 系统兼容的通用图标名称 -- ✅ 确保在 Element Plus 框架中正常显示 -- ✅ 图标语义清晰,用户容易理解 -- ✅ 避免了不兼容图标导致的显示问题 - -**用户体验**: - -- ✅ 菜单图标在前端正常显示 -- ✅ 图标含义直观明确 -- ✅ 整体界面更加统一和专业 - -### 📊 技术细节 - -**图标兼容性标准**: - -- 使用 ruoyi 系统默认支持的图标名称 -- 优先选择 Element Plus 图标库中的通用图标 -- 避免使用可能不兼容的复合名称图标 - -**更新时间**:2025-07-01 23:17:xx - -### 💡 最佳实践 - -**图标选择原则**: - -1. **兼容性优先**:确保在目标系统中能正确显示 -2. **语义明确**:图标含义与功能相关 -3. **通用性**:选择常见和稳定的图标名称 -4. **一致性**:与系统整体风格保持一致 - -**测试验证**: - -- 前端页面测试图标显示效果 -- 确认菜单加载正常 -- 验证图标语义的用户理解度 - ---- - -## 📅 2025-07-01 23:20:00 - 修复场地菜单图标配置 - -### 🎯 变更目标 - -- 修复场地预约、场地管理在 sys_menu 表中图标配置不合适的问题 -- 使用更专业和准确的图标名称,提升用户界面体验 -- 确保菜单图标在前端正常显示 - -### 🔧 问题描述 - -**图标配置不合适**: - -- 场地预约目录和场地管理功能都使用 `venue` 图标,容易混淆 -- 原有图标名称不够专业和准确 -- 部分图标在前端可能显示效果不佳 - -### 📁 修复内容 - -**图标配置更新**:通过直接 SQL 更新,优化了 4 个菜单的图标配置 - -**修复前配置**: - -``` -+---------+--------------+----------+ -| menu_id | menu_name | icon | -+---------+--------------+----------+ -| 2140 | 场地预约 | venue | -| 2210 | 场地管理 | venue | -| 2220 | 场地预约 | calendar | -| 2230 | 使用统计 | chart | -+---------+--------------+----------+ -``` - -**修复后配置**: - -``` -+---------+--------------+-----------------+ -| menu_id | menu_name | icon | -+---------+--------------+-----------------+ -| 2140 | 场地预约 | component | -| 2210 | 场地管理 | office-building | -| 2220 | 场地预约 | date-range | -| 2230 | 使用统计 | data-analysis | -+---------+--------------+-----------------+ -``` - -### 🎨 图标选择说明 - -**新图标的含义和优势**: - -1. **场地预约目录** (`component`): - - - 表示功能模块组件 - - 比通用的 `venue` 更专业 - - 在菜单层级中更容易识别 - -2. **场地管理** (`office-building`): - - - 办公楼图标,直观表示建筑场地 - - 比通用的 `venue` 更具体和形象 - - 与场地管理功能高度契合 - -3. **场地预约** (`date-range`): - - - 日期范围图标,突出预约时间概念 - - 比简单的 `calendar` 更专业 - - 体现预约业务的核心特征 - -4. **使用统计** (`data-analysis`): - - 数据分析图标,准确表达统计功能 - - 比通用的 `chart` 更专业 - - 与统计分析业务完全匹配 - -### 🛠️ 修复执行 - -**SQL 修复命令**: - -```sql --- 场地预约目录 -UPDATE sys_menu SET icon = 'component', update_time = NOW(), update_by = 1 WHERE menu_id = 2140; - --- 场地管理 -UPDATE sys_menu SET icon = 'office-building', update_time = NOW(), update_by = 1 WHERE menu_id = 2210; - --- 场地预约功能 -UPDATE sys_menu SET icon = 'date-range', update_time = NOW(), update_by = 1 WHERE menu_id = 2220; - --- 使用统计 -UPDATE sys_menu SET icon = 'data-analysis', update_time = NOW(), update_by = 1 WHERE menu_id = 2230; -``` - -### 🎯 修复效果 - -**用户体验提升**: - -- ✅ 菜单图标更加专业和准确 -- ✅ 不同功能的图标有明显区分度 -- ✅ 图标语义与功能高度匹配 -- ✅ 整体界面更加美观统一 - -**功能识别**: - -- ✅ 用户能更快速识别不同菜单功能 -- ✅ 图标含义更加直观易懂 -- ✅ 符合现代 UI 设计规范 - -### 📊 技术细节 - -**图标标准**: - -- 使用 Element Plus / ruoyi 系统兼容的图标名称 -- 遵循语义化命名规范 -- 考虑前端显示兼容性 - -**更新时间**:所有菜单的 update_time 都更新为 2025-07-01 23:13:xx - -### 💡 最佳实践 - -**图标选择原则**: - -1. **语义匹配**:图标含义与功能高度相关 -2. **专业性**:使用更专业的图标名称 -3. **区分度**:避免相同图标造成混淆 -4. **兼容性**:确保前端能正确显示 - -**配置标准**: - -- 目录级别使用模块化图标(如 component) -- 功能级别使用具体业务图标(如 office-building) -- 统计类功能使用分析类图标(如 data-analysis) - ---- - -## 📅 2025-07-01 23:15:00 - 重新创建使用统计界面组件 - -### 🎯 变更目标 - -- 修复使用统计界面 `index.vue` 文件为空的问题 -- 重新创建完整的使用统计界面组件内容 -- 确保场地预约模块的统计功能完整可用 - -### 🔧 问题描述 - -**文件内容缺失**: - -- 使用统计界面文件 `web/src/views/hq/venue/statistics/index.vue` 存在但内容为空 -- 导致用户点击"使用统计"菜单时页面空白 - -### 📁 修复内容 - -**重新创建文件内容**:`web/src/views/hq/venue/statistics/index.vue` (549 行) - -**功能特性**: - -- ✅ 统计概览卡片(总预约数 156、已批准 128、待审批 18、已拒绝 10) -- ✅ 多维度统计筛选(预约量统计、场地使用率、时间段分析) -- ✅ 时间范围选择器(默认近 30 天) -- ✅ 场地类型筛选(会议室、教室、运动场等) -- ✅ 图表展示: - - 预约量趋势线图(6 个月数据) - - 场地类型分布饼图(会议室 45、教室 32、运动场 28 等) - - 热门场地排行榜柱状图 -- ✅ 详细数据表格(场地名称、类型、预约数、使用率等) -- ✅ 分页查询功能 -- ✅ 报表导出功能(提示开发中) - -**技术实现**: - -- 🎨 使用 ECharts 进行数据可视化 -- 📊 模拟数据展示,预留实际 API 接口调用 -- 🎯 Vue 3 + TypeScript + Element Plus -- 📱 响应式布局,支持窗口大小调整 -- 🔄 图表自适应窗口大小变化 -- 🎨 现代化 UI 设计,统一的卡片和颜色风格 - -**代码结构**: - -- `