chore: 清理冗余配置、优化代码结构

- 移除 SSE 独立代理(VITE_APP_SSE_URL)及相关配置
- 删除 CHANGELOG.md,清理 .env 多余注释
- 重构 permission.ts 路由守卫,用 return 替代 next() 调用
- 优化 useSse.ts:合并重复逻辑、增加中文注释、调整重连参数
- 简化 vite.config.ts 代理和注释
This commit is contained in:
Ray.Hao
2026-03-27 09:03:26 +08:00
parent 8f15098042
commit 764e6582f2
11 changed files with 668 additions and 2000 deletions

View File

@@ -7,13 +7,10 @@ VITE_APP_BASE_API=/dev-api
# 接口地址
VITE_APP_API_URL=https://api.youlai.tech # 线上
# VITE_APP_API_URL=https://api.youlai.tech/v2 # 线上(多租户)
# VITE_APP_API_URL=http://localhost:8000 # 本地
# 启用 Mock 服务(true:开启 false:关闭)
VITE_MOCK_DEV_SERVER=false
# 多租户开关(true:开启 false:关闭)
VITE_APP_TENANT_ENABLED=false

View File

@@ -1,16 +1,8 @@
# ============================================
# 🌐 网络配置
# ============================================
# 代理前缀
VITE_APP_BASE_API = '/prod-api'
# 项目名称
VITE_APP_TITLE=vue3-element-admin
# WebSocket 端点(可选)
#VITE_APP_WS_ENDPOINT=wss://api.youlai.tech/ws
# ============================================
# 🎛️ 功能开关
# ============================================
# 多租户(需与后端 app.tenant.enabled 保持一致)
# 多租户开关(true/false)
VITE_APP_TENANT_ENABLED=true

View File

@@ -1 +1 @@
npm run lint:lint-staged -- --no-stash
npm run lint:lint-staged

View File

@@ -1,386 +0,0 @@
# 2.11.5 (2024/6/18)
## ✨ feat
- 支持后端文件导入([#142](https://github.com/youlaitech/vue3-element-admin/pull/142)) [@cshaptx4869](https://github.com/cshaptx4869)
## 🐛 fix
- vue-dev-tools 插件导致菜单路由切换卡死,暂时关闭 ([28349e](https://github.com/youlaitech/vue3-element-admin/commit/28349efe147afab36531ba148eaac3a448fe6c71)) [@haoxianrui](https://github.com/haoxianrui)
# 2.11.4 (2024/6/16)
## ✨ feat
- 操作栏增加render配置参数([#138](https://github.com/youlaitech/vue3-element-admin/pull/140)) [@cshaptx4869](https://github.com/cshaptx4869)
- 左侧工具栏增加type配置参数([#141](https://github.com/youlaitech/vue3-element-admin/pull/141)) [@diamont1001](https://github.com/diamont1001)
## ♻️ refactor
- 更换权限分配弹窗类型为 drawer 并添加父子联动开关([2d9193](https://github.com/youlaitech/vue3-element-admin/commit/2d9193c47fd224f01f82b9c0b2bbeb5e7cb33584)) [@haoxianrui](https://github.com/haoxianrui)
# 2.11.3 (2024/6/11)
## ✨ feat
- 支持默认工具栏的导入([#138](https://github.com/youlaitech/vue3-element-admin/pull/138)) [@cshaptx4869](https://github.com/cshaptx4869)
- 添加CURD导入示例([19e7bb](https://github.com/youlaitech/vue3-element-admin/commit/eab91effd6a01d5a3d9257249c8d06aa252b3bf8)) [@cshaptx4869](https://github.com/cshaptx4869)
## ♻️ refactor
- 修改导出全量数据选项文本([904fec](https://github.com/youlaitech/vue3-element-admin/commit/904fecad65217650482fcdbb10ffb7f3d27eb9ea)) [@cshaptx4869](https://github.com/cshaptx4869)
## 🐛 fix
- 菜单列表未适配el-icon导致图标不显示问题修复([e72b68](https://github.com/youlaitech/vue3-element-admin/commit/e72b68337562b5a7ea24ad55bbe00023e1266b40)) [@haoxianrui](https://github.com/haoxianrui)
# 2.11.2 (2024/6/8)
## ✨ feat
- 支持表格远程筛选([#131](https://github.com/youlaitech/vue3-element-admin/pull/131)) [@cshaptx4869](https://github.com/cshaptx4869)
- 支持标签输入框([#132](https://github.com/youlaitech/vue3-element-admin/pull/132)) [@cshaptx4869](https://github.com/cshaptx4869)
- 表单项支持tips配置([#133](https://github.com/youlaitech/vue3-element-admin/pull/133)) [@cshaptx4869](https://github.com/cshaptx4869)
- 前端导出支持全量数据([#134](https://github.com/youlaitech/vue3-element-admin/pull/134)) [@cshaptx4869](https://github.com/cshaptx4869)
- 支持选中数据导出([#135](https://github.com/youlaitech/vue3-element-admin/pull/135)) [@cshaptx4869](https://github.com/cshaptx4869)
- 表格默认工具栏的导出、搜索按钮增加权限点控制([883128](https://github.com/youlaitech/vue3-element-admin/commit/8831289b655f2cc086ecdababaa89f8d8a087c42)) [@cshaptx4869](https://github.com/cshaptx4869)
- 页签title支持动态设置([23876a](https://github.com/youlaitech/vue3-element-admin/commit/23876aa396143bf77cb5c86af8d6023d9ff6555a)) [@haoxianrui](https://github.com/haoxianrui)
## ♻️ refactor
- 默认工具栏支持自定义([#136](https://github.com/youlaitech/vue3-element-admin/pull/136)) [@cshaptx4869](https://github.com/cshaptx4869)
- 未配置全量导出接口时选项隐藏([eab91ef](https://github.com/youlaitech/vue3-element-admin/commit/eab91effd6a01d5a3d9257249c8d06aa252b3bf8)) [@cshaptx4869](https://github.com/cshaptx4869)
## 🐛 fix
- 修复注销登出后redirect跳转路由参数丢失([5626017](https://github.com/youlaitech/vue3-element-admin/commit/562601736731afd20bb1a5140d856f6515720159)) [@haoxianrui](https://github.com/haoxianrui)
# 2.11.1 (2024/6/6)
## ✨ feat
- 增加pagination、request、parseData配置参数([#119](https://github.com/youlaitech/vue3-element-admin/pull/119)) [@cshaptx4869](https://github.com/cshaptx4869)
- 增加返回顶部功能([#120](https://github.com/youlaitech/vue3-element-admin/pull/120)) [@cshaptx4869](https://github.com/cshaptx4869)
- 支持前端导出([#126](https://github.com/youlaitech/vue3-element-admin/pull/126)) [@cshaptx4869](https://github.com/cshaptx4869)
## ♻️ refactor
- 重构布局样式(解决页面抖动问题)([#116](https://github.com/youlaitech/vue3-element-admin/pull/116)) [@cshaptx4869](https://github.com/cshaptx4869)
- 修改CURD示例编辑弹窗尺寸([#121](https://github.com/youlaitech/vue3-element-admin/pull/121)) [@cshaptx4869](https://github.com/cshaptx4869)
- 统一注册vue插件([#122](https://github.com/youlaitech/vue3-element-admin/pull/122)) [@cshaptx4869](https://github.com/cshaptx4869)
- 默认主题跟随系统([#128](https://github.com/youlaitech/vue3-element-admin/pull/128)) [@cshaptx4869](https://github.com/cshaptx4869)
- 增加"scss.lint.unknownAtRules": "ignore"代码解决style中使用@apply提示unknow at rules@apply提示问题([Gitee#22](https://gitee.com/youlaiorg/vue3-element-admin/pulls/22)) [@zjsy521](https://gitee.com/zjsy521)
## 🐛 fix
- 修复左侧布局移动端菜单弹出样式 ([#117](https://github.com/youlaitech/vue3-element-admin/pull/117)) [@cshaptx4869](https://github.com/cshaptx4869)
- 修复编辑后未清空id再新增菜单覆盖的问题([0e78eeb](https://github.com/youlaitech/vue3-element-admin/commit/0e78eeb75008fa8e9732b1b4e7d7a1ea345c7a1b)) [@haoxianrui](https://github.com/haoxianrui)
- 修复水印层级问题([#123](https://github.com/youlaitech/vue3-element-admin/pull/123)) [@cshaptx4869](https://github.com/cshaptx4869)
- 修复混合布局样式问题([#124](https://github.com/youlaitech/vue3-element-admin/pull/124)) [@cshaptx4869](https://github.com/cshaptx4869)
- 修复关闭弹窗时没有clearValidate问题([#125](https://github.com/youlaitech/vue3-element-admin/pull/125)) [@andm31](https://github.com/andm31)
# 2.11.0 (2024/5/27)
## ✨ feat
- 菜单添加路由参数设置author by [haoxianrui](https://github.com/haoxianrui)
- 增加列表选择组件author by [cshaptx4869](https://github.com/cshaptx4869)
- 增加列表选择组件使用示例author by [cshaptx4869](https://github.com/cshaptx4869)
- 增加defaultToolbar配置参数author by [cshaptx4869](https://github.com/cshaptx4869)
- 表单弹窗支持drawer模式author by [cshaptx4869](https://github.com/cshaptx4869)
- 表单项增加computed和watchEffect配置author by [cshaptx4869](https://github.com/cshaptx4869)
- 支持switch属性修改author by [cshaptx4869](https://github.com/cshaptx4869)
- 表单项增加文本类型支持author by [cshaptx4869](https://github.com/cshaptx4869)
- 列表列增加show配置项author by [cshaptx4869](https://github.com/cshaptx4869)
- 支持搜索表单显隐控制author by [cshaptx4869](https://github.com/cshaptx4869)
- 支持input属性修改author by [cshaptx4869](https://github.com/cshaptx4869)
- search配置新增函数能力拓展author by [xiudaozhe](https://github.com/xiudaozhe)
- 表格新增列设置控制author by [haoxianrui](https://github.com/haoxianrui)
- 搜索添加展开和收缩author by [haoxianrui](https://github.com/haoxianrui)
- watch函数增加配置项参数返回author by [cshaptx4869](https://github.com/cshaptx4869)
## ♻️ refactor
- 重构图标选择组件author by [cshaptx4869](https://github.com/cshaptx4869)
- 重构列表选择组件默认样式 author by [cshaptx4869](https://github.com/cshaptx4869)
- 加强对话框表单组件和列表选择组件author by [cshaptx4869](https://github.com/cshaptx4869)
- routeMeta增加alwaysShow字段声明author by [cshaptx4869](https://github.com/cshaptx4869)
- 分页组件增加溢出滚动效果author by [cshaptx4869](https://github.com/cshaptx4869)
- 修正登录表单的Ref类型author by [cshaptx4869](https://github.com/cshaptx4869)
- 点击表格刷新按钮不重置页码author by [cshaptx4869](https://github.com/cshaptx4869)
- 筛选列超出一定高度滚动author by [cshaptx4869](https://github.com/cshaptx4869)
- 优化加强initFn函数表单项增加initFn函数author by [cshaptx4869](https://github.com/cshaptx4869)
- 重构watch、computed、watchEffect调用author by [cshaptx4869](https://github.com/cshaptx4869)
- 修改操作成功提示author by [cshaptx4869](https://github.com/cshaptx4869)
- PageSearch 改用card作为容器,样式改用unocss写法author by [cshaptx4869](https://github.com/cshaptx4869)
- 优化首页 loading 动画效果author by [haoxianrui](https://github.com/haoxianrui)
## 🐛 fix
- 路由是否始终显示不限制只有顶级目录才有的配置,开放至菜单 author by [haoxianrui](https://github.com/haoxianrui)
- sockjs-client 报错 global is not defined 导致开发环境无法打开 WebSocket 页面问题修复 author by [haoxianrui](https://github.com/haoxianrui)
- 发送用户重启密码功能最少为6位字符小于6位登陆时不允许的问题 author by [dreamnyj](https://gitee.com/dreamnyj)
- 修复系统设置面板滚动条问题author by [cshaptx4869](https://github.com/cshaptx4869)
- 修复表单插槽失效问题author by [cshaptx4869](https://github.com/cshaptx4869)
- 修改tagsview刷新丢失query问题author by [xiudaozhe](https://github.com/xiudaozhe)
## 📦️ build
- 升级 NPM 包版本至最新 author by [haoxianrui](https://github.com/haoxianrui)
## ⚙️ ci
- 规整脚本执行命令author by [cshaptx4869](https://github.com/cshaptx4869)
# 2.10.1 (2024/5/4)
## ♻️ refactor
- 抽离CURD的使用部分代码为Hooks实现author by [cshaptx4869](https://github.com/cshaptx4869)
- 修改CURD导入权限点标识名author by [cshaptx4869](https://github.com/cshaptx4869)
- cURD表单字段支持watch监听author by [cshaptx4869](https://github.com/cshaptx4869)
- cURD表单input支持number修饰author by [cshaptx4869](https://github.com/cshaptx4869)
- cURD表单组件支持checkbox多选框author by [cshaptx4869](https://github.com/cshaptx4869)
- 优化axios响应数据TS类型提示author by [cshaptx4869](https://github.com/cshaptx4869)
- 修改CURD表单组件自定义类型的attrs传值author by [cshaptx4869](https://github.com/cshaptx4869)
- 同步重置密码按钮权限标识重命名author by [haoxianrui](https://github.com/haoxianrui)
- 重构API为静态方法实现模块化管理并将types.ts重命名为model.ts用于存放接口模型定义author by [haoxianrui](https://github.com/haoxianrui)
## 🐛 fix
- sockjs-client 报错 global is not defined 导致开发环境无法打开 WebSocket 页面问题修复 author by [haoxianrui](https://github.com/haoxianrui)
- 主题颜色设置覆盖暗黑模式下el-table行激活的背景色问题修复 author by [haoxianrui](https://github.com/haoxianrui)
- 修复因API接口调整而影响的调用页面的问题 author by [haoxianrui](https://github.com/haoxianrui)
## 📦️ build
- 升级 NPM 包版本至最新 author by [haoxianrui](https://github.com/haoxianrui)
# 2.10.0 (2024/4/26)
## ✨ feat
- 封装增删改查组件author by [cshaptx4869](https://github.com/cshaptx4869)
- 集成 vite-plugin-vue-devtools 插件author by [Tricker39](https://github.com/Tricker39)
- 增加CURD配置化实现author by [cshaptx4869](https://github.com/cshaptx4869)
# 2.9.3 (2024/04/14)
## ✨ feat
- 增加vue文件代码片段author by [cshaptx4869](https://github.com/cshaptx4869)
- 菜单 hover 背景色添加值全局SCSS变量进行控制author by [haoxianrui](https://github.com/haoxianrui)
## ♻️ refactor
- 加强基础国际化author by [cshaptx4869](https://github.com/cshaptx4869)
- 增加语言和布局大小枚举类型author by [cshaptx4869](https://github.com/cshaptx4869)
- 增加侧边栏状态枚举类型author by [cshaptx4869](https://github.com/cshaptx4869)
- 使用布局枚举替换字面量author by [haoxianrui](https://github.com/haoxianrui)
- 控制台使用静态数据循环渲染author by [april](mailto:april@zen-game.cn)
- 本地缓存的 token 变量重命名author by [haoxianrui](https://github.com/haoxianrui)
- 完善 Vite 环境变量类型声明author by [haoxianrui](https://github.com/haoxianrui)
## 🐛 fix
- 修复构建时提示iconComponent.name可能为undefined的报错 author by [wangji1042](https://github.com/wangji1042)
- 修复浏览器密码自动填充时可能存在的报错 author by [cshaptx4869](https://github.com/cshaptx4869)
- 修复eslint报错author by [cshaptx4869](https://github.com/cshaptx4869)
- 移动端下点击左侧菜单节点后关闭侧边栏author by [haoxianrui](https://github.com/haoxianrui)
- 添加 size 类型断言修复类型报错author by [haoxianrui](https://github.com/haoxianrui)
## 📦️ build
- husky9.x版本适配 author by [cshaptx4869](https://github.com/cshaptx4869)
- 升级 npm 包版本至最新author by [haoxianrui](https://github.com/haoxianrui)
# 2.9.2 (2024/03/05)
## ✨ feat
- vscode开发扩展推荐author by [cshaptx4869](https://github.com/cshaptx4869)
- 完善基础增删改查Mock接口author by [haoxianrui](https://github.com/haoxianrui)
## ♻️ refactor
- 修改login密码框功能实现author by [cshaptx4869](https://github.com/cshaptx4869)
- 弱化页面进入动画效果author by [cshaptx4869](https://github.com/cshaptx4869)
- 取消推荐TypeScript Vue Plugin author by [cshaptx4869](https://github.com/cshaptx4869)
- 网站加载动画替换 author by [haoxianrui](https://github.com/haoxianrui)
- 优化主题和主题色监听,避免多个页面重复初始化 author by [haoxianrui](https://github.com/haoxianrui)
## 🐛 fix
- AppMain 高度在非固定头部不正确导致出现滚动条问题修复 author by [haoxianrui](https://github.com/haoxianrui)
- 修复混合模式开启固定Head时的样式问题 author by [cshaptx4869](https://github.com/cshaptx4869)
- 设置面板统一字体大小 author by [cshaptx4869](https://github.com/cshaptx4869)
## 📦build
- 通过env配置控制mock服务 author by [cshaptx4869](https://github.com/cshaptx4869)
- 升级依赖包至最新版本 author by [haoxianrui](https://github.com/haoxianrui)
- 定义vite全局常量替换项目标题和版本 author by [cshaptx4869](https://github.com/cshaptx4869)
# 2.9.1 (2024/02/28)
## ♻️ refactor
- 项目配置按钮移入navbarauthor by [cshaptx4869](https://github.com/cshaptx4869)
- 优化user数据定义author by [cshaptx4869](https://github.com/cshaptx4869)
- 统一设置栏的 SVG 图标风格
## 🐛 fix
- 规整一些开发依赖author by [cshaptx4869](https://github.com/cshaptx4869)
- 修复登录页主题切换问题 author by [cshaptx4869](https://github.com/cshaptx4869)
## 🚀 pref
- 压缩图片资源 author by [cshaptx4869](https://github.com/cshaptx4869)
# 2.9.0 (2024/02/25)
## ✨ feat
- 引入 animate.css 动画库
- 新增水印和配置
- 动态路由菜单支持 element plus 的图标
## ♻️ refactor
- Layout 布局重构和相关问题修复
- sass 使用 @use 替代 @import 引入外部文件指令
## 🐛 fix
- 修复管理页面部分弹窗无法打开问题
- 主题颜色设置按钮 hover 等未变化问题修复
# 2.8.1 (2024/01/10)
## ✨ feat
- 替换 Mock 解决方案 vite-plugin-mock 为 vite-plugin-mock-dev-server 适配 Vite5
# 2.8.0 (2023/12/27)
## ⬆️ chore
- 升级 Vite4 至 Vite5
# 2.7.1 (2023/12/12)
## ♻️ refactor
- 将打包后的文件进行分类 author by [ityangzhiwen](https://gitee.com/ityangzhiwen)
# 2.7.0 (2023/11/19)
## ♻️ refactor
- 代码重构优化
- 修改自动导入组件类型声明文件路径
- 完善 typescript 类型
## 🐛 fix
- 修复管理页面部分弹窗无法打开问题
# 2.7.0 (2023/11/19)
## ♻️ refactor
- 代码重构
- 修改自动导入组件类型声明文件路径
- 完善 typescript 类型
## 🐛 fix
- 修复管理页面部分弹窗无法打开问题
# 2.6.3 (2023/10/22)
## ✨ feat
- 菜单管理新增目录只有一级子路由是否始终显示(alwaysShow)和路由页面是否缓存(keepAlive)的配置
- 接口文档新增 swagger、knife4j
- 引入和支持 tsx
## ♻️ refactor
- 代码瘦身,整理并删除未使用的 svg
- 控制台样式优化
## 🐛 fix
- 菜单栏折叠和展开的图标暗黑模式显示问题修复
# 2.6.2 (2023/10/11)
## 🐛 fix
- 主题设置未持久化问题
- UnoCSS 插件无智能提示
## ♻️ refactor
- WebSocket 演示样式和代码优化
- 用户管理代码重构
# 2.6.1 (2023/9/4)
## 🐛 fix
- 导航顶部模式、混合模式样式在固定 Header 出现的样式问题修复
- 固定 Header 没有持久化问题修复
- 字典回显兼容 String 和 Number 类型
# 2.6.0 (2023/8/24)💥💥💥
## ✨ feat
- 导航顶部模式、混合模式支持author by [april-tong](https://april-tong.com/)
- 平台文档(内嵌)author by [april-tong](https://april-tong.com/)
# 2.5.0 (2023/8/8)
## ✨ feat
- 新增 Mockauthor by [ygcaicn](https://github.com/ygcaicn)
- 图标 DEMOauthor by [ygcaicn](https://github.com/ygcaicn)
## 🐛 fix
- 字典支持 Number 类型
# 2.4.1 (2023/7/20)
## ✨ feat
- 整合 vite-plugin-compression 插件打包优化(3.66MB → 1.58MB) author by [april-tong](https://april-tong.com/)
- 字典组件封装author by [haoxr](https://juejin.cn/user/4187394044331261/posts)
## 🐛 fix
- 分页组件hidden无效
- 签名无法保存至后端
- Git 提交 stylelint 校验部分机器报错
# 2.4.0 (2023/6/17)
## ✨ feat
- 新增组件标签输入框author by [april-tong](https://april-tong.com/)
- 新增组件签名author by [april-tong](https://april-tong.com/)
- 新增组件表格author by [april-tong](https://april-tong.com/)
- Echarts 图表添加下载功能 author by [april-tong](https://april-tong.com/)
## ♻️ refactor
- 限制包管理器为 pnpm 和 node 版本16+
- 自定义组件自动导入配置
- 搜索框样式写法优化
## 🐛 fix
- 用户导入的部门回显成数字问题修复
## ⬆️ chore
- element-plus 版本升级 2.3.5 → 2.3.6
# 2.3.1 (2023/5/21)
## 🔄 refactor
- 组件示例文件名称优化
# 2.2.2 (2023/5/11)
## ✨ feat
- 组件封装示例添加源码地址
- 角色、菜单、部门、字段按钮添加权限控制
# 2.3.0 (2023/5/12)
## ⬆️ chore
- vue 版本升级 3.2.45 → 3.3.1 ([CHANGELOG](https://github.com/vuejs/core/blob/main/CHANGELOG.md))
- vite 版本升级 4.3.1 → 4.3.5
## ♻️ refactor
- 使用 vue 3.3 版本新特性 `defineOptions``setup` 定义组件名称,移除重复的 `script` 标签
# 2.2.2 (2023/5/11)
## ✨ feat
- 用户新增提交添加 `vueUse``useDebounceFn` 函数实现按钮防抖节流
# 2.2.1 (2023/4/25)
## 🐛 fix
- 图标选择器组件使用 `onClickOutside` 未排除下拉弹出框元素导致无法输入搜索。

View File

@@ -4,7 +4,7 @@
<img src="https://img.shields.io/badge/Vue-3.5.30-brightgreen.svg"/>
<img src="https://img.shields.io/badge/Vite-8.0.0-green.svg"/>
<img src="https://img.shields.io/badge/Element Plus-2.13.5-blue.svg"/>
<img src="https://img.shields.io/badge/Element Plus-2.13.6-blue.svg"/>
<img src="https://img.shields.io/badge/license-MIT-green.svg"/>
<a href="https://gitee.com/youlaiorg/vue3-element-admin" target="_blank">

View File

@@ -194,7 +194,14 @@ export default [
"@typescript-eslint/no-empty-object-type": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unused-vars": "warn", // 降级为警告
"@typescript-eslint/no-unused-vars": [
"warn",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
"@typescript-eslint/no-unused-expressions": "warn", // 降级为警告
"@typescript-eslint/consistent-type-imports": "off", // 关闭强制使用type import
"@typescript-eslint/no-import-type-side-effects": "error",

View File

@@ -52,10 +52,10 @@
"@wangeditor-next/editor-for-vue": "^5.1.14",
"animate.css": "^4.1.1",
"axios": "^1.13.6",
"codemirror": "^5.65.21",
"codemirror": "^5.65.16",
"codemirror-editor-vue3": "^2.8.0",
"echarts": "^6.0.0",
"element-plus": "^2.13.5",
"element-plus": "^2.13.6",
"exceljs": "^4.4.0",
"lodash-es": "^4.17.23",
"nprogress": "^0.2.0",
@@ -82,8 +82,6 @@
"@types/path-browserify": "^1.0.3",
"@types/qs": "^6.15.0",
"@types/sortablejs": "^1.15.9",
"@typescript-eslint/eslint-plugin": "^8.57.0",
"@typescript-eslint/parser": "^8.57.0",
"@vitejs/plugin-vue": "^6.0.5",
"autoprefixer": "^10.4.27",
"commitizen": "^4.3.1",
@@ -94,6 +92,7 @@
"eslint-plugin-vue": "^10.8.0",
"globals": "^17.4.0",
"husky": "^9.1.7",
"jiti": "^2.6.1",
"lint-staged": "^16.4.0",
"postcss": "^8.5.8",
"postcss-html": "^1.8.1",
@@ -109,13 +108,12 @@
"stylelint-prettier": "^5.0.3",
"terser": "^5.46.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.57.0",
"typescript-eslint": "^8.57.2",
"unocss": "^66.6.6",
"unplugin-auto-import": "^21.0.0",
"unplugin-vue-components": "^31.0.0",
"unplugin-vue-components": "^32.0.0",
"vite": "^8.0.0",
"vite-plugin-mock-dev-server": "^2.1.0",
"vue-eslint-parser": "^10.4.0",
"vite-plugin-mock-dev-server": "^2.1.1",
"vue-tsc": "^3.2.5"
},
"engines": {

2036
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,115 +1,91 @@
import { AuthStorage } from "@/utils/auth";
export interface UseSseOptions {
/** SSE 端点 URL不传时使用环境变量拼接 */
url?: string;
/** 是否开启调试日志 */
debug?: boolean;
/** 连接超时时间,单位毫秒,默认为 10000 */
connectionTimeout?: number;
url?: string; // SSE 连接地址,默认走 VITE_APP_BASE_API 代理
debug?: boolean; // 是否在控制台打印调试日志
connectionTimeout?: number; // 连接超时时间(ms)
/** 重连间隔基数,实际间隔 = min(基数 × 2^n, 最大间隔) */
reconnectInterval?: number;
maxReconnectInterval?: number; // 重连间隔上限(ms)
maxReconnectAttempts?: number; // 最大重试次数,超过后停止重连
}
type EventHandler = (data: any) => void;
/**
* SSE 连接状态枚举
*/
export enum SseConnectionState {
DISCONNECTED = "DISCONNECTED",
CONNECTING = "CONNECTING",
CONNECTED = "CONNECTED",
DISCONNECTED = "DISCONNECTED", // 未连接
CONNECTING = "CONNECTING", // 连接中
CONNECTED = "CONNECTED", // 已连接
}
/**
* 全局 SSE 实例管理
*/
let globalInstance: ReturnType<typeof createSseConnection> | null = null;
/**
* 创建 SSE 连接(内部工厂函数)
*/
function createSseConnection(options: UseSseOptions = {}) {
const baseApi = import.meta.env.VITE_APP_BASE_API || "/dev-api";
const defaultUrl = `${baseApi}/api/v1/sse/connect`;
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const defaultUrl = `${baseUrl}/api/v1/sse/connect`;
const config = {
url: options.url ?? defaultUrl,
debug: options.debug ?? false,
connectionTimeout: options.connectionTimeout ?? 10000,
connectionTimeout: options.connectionTimeout ?? 10000, // 连接超时 10s
reconnectInterval: options.reconnectInterval ?? 5000, // 首次重连等 5s之后翻倍
maxReconnectInterval: options.maxReconnectInterval ?? 120000, // 重连间隔最大 2min
maxReconnectAttempts: options.maxReconnectAttempts ?? 10, // 最多重试 10 次
};
const connectionState = ref<SseConnectionState>(SseConnectionState.DISCONNECTED);
const isConnected = computed(() => connectionState.value === SseConnectionState.CONNECTED);
let eventSource: EventSource | null = null;
let abortController: AbortController | null = null;
let isManualDisconnect = false;
let connectionTimeoutTimer: ReturnType<typeof setTimeout> | null = null;
let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;
let isManualDisconnect = false; // 主动断开则不重连
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
let reconnectAttempts = 0;
let currentReconnectInterval = config.reconnectInterval;
const eventHandlers = new Map<string, Set<EventHandler>>();
const log = config.debug ? (...args: any[]) => console.log("[SSE]", ...args) : () => {};
const log = (...args: any[]) => console.log("[SSE]", ...args);
const logError = (...args: any[]) => console.error("[SSE]", ...args);
const clearConnectionTimeout = () => {
if (connectionTimeoutTimer) {
clearTimeout(connectionTimeoutTimer);
connectionTimeoutTimer = null;
const clearTimer = (timer: typeof connectionTimeoutTimer) => {
if (timer) {
clearTimeout(timer);
return null;
}
return timer;
};
const handleOpen = () => {
clearConnectionTimeout();
connectionState.value = SseConnectionState.CONNECTED;
log("SSE 连接已建立");
};
// 指数退避重连
const scheduleReconnect = () => {
if (isManualDisconnect) return;
if (config.maxReconnectAttempts > 0 && reconnectAttempts >= config.maxReconnectAttempts) {
log(`已达到最大重试次数 ${config.maxReconnectAttempts},停止重连`);
return;
}
const handleError = (event: Event) => {
clearConnectionTimeout();
connectionState.value = SseConnectionState.DISCONNECTED;
logError("SSE 连接错误:", event);
reconnectAttempts++;
log(`将在 ${currentReconnectInterval}ms 后重试(${reconnectAttempts}`);
if (!isManualDisconnect && eventSource) {
log("浏览器将自动重连...");
}
};
const handleMessage = (event: MessageEvent) => {
log("收到消息:", event.data);
const handlers = eventHandlers.get("message");
if (handlers) {
try {
const data = JSON.parse(event.data);
handlers.forEach((handler) => handler(data));
} catch {
handlers.forEach((handler) => handler(event.data));
}
}
};
const handleCustomEvent = (eventName: string) => (event: MessageEvent) => {
log(`收到事件[${eventName}]:`, event.data);
const handlers = eventHandlers.get(eventName);
if (handlers) {
try {
const data = JSON.parse(event.data);
handlers.forEach((handler) => handler(data));
} catch {
handlers.forEach((handler) => handler(event.data));
}
}
reconnectTimer = setTimeout(() => {
connect();
currentReconnectInterval = Math.min(
currentReconnectInterval * 2,
config.maxReconnectInterval
);
}, currentReconnectInterval);
};
const connect = () => {
isManualDisconnect = false;
if (connectionState.value === SseConnectionState.CONNECTED) {
log("SSE 已连接,跳过重复连接");
return;
}
if (connectionState.value === SseConnectionState.CONNECTING) {
log("SSE 正在连接中,跳过重复连接");
if (connectionState.value !== SseConnectionState.DISCONNECTED) {
log(
connectionState.value === SseConnectionState.CONNECTED
? "SSE 已连接,跳过重复连接"
: "SSE 正在连接中,跳过重复连接"
);
return;
}
@@ -120,10 +96,9 @@ function createSseConnection(options: UseSseOptions = {}) {
}
connectionState.value = SseConnectionState.CONNECTING;
// 使用 fetch + ReadableStream 替代 EventSource支持 Authorization header
abortController = new AbortController();
// 超时自动断开
connectionTimeoutTimer = setTimeout(() => {
if (connectionState.value === SseConnectionState.CONNECTING) {
log("SSE 连接超时");
@@ -131,6 +106,8 @@ function createSseConnection(options: UseSseOptions = {}) {
}
}, config.connectionTimeout);
log("正在建立 SSE 连接...");
fetch(config.url, {
method: "GET",
headers: {
@@ -143,8 +120,10 @@ function createSseConnection(options: UseSseOptions = {}) {
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
clearConnectionTimeout();
connectionTimeoutTimer = clearTimer(connectionTimeoutTimer);
connectionState.value = SseConnectionState.CONNECTED;
reconnectAttempts = 0;
currentReconnectInterval = config.reconnectInterval;
log("SSE 连接已建立");
return response.body?.getReader();
})
@@ -154,6 +133,7 @@ function createSseConnection(options: UseSseOptions = {}) {
const decoder = new TextDecoder();
let buffer = "";
// SSE 文本协议解析event / data / 空行分隔
const processChunk = ({
done,
value,
@@ -166,19 +146,18 @@ function createSseConnection(options: UseSseOptions = {}) {
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() || ""; // 保留不完整的行
buffer = lines.pop() || "";
let currentEvent = "message";
let currentData = "";
for (const line of lines) {
if (line.startsWith(":")) continue; // 注释行(心跳)
if (line.startsWith(":")) continue;
if (line.startsWith("event:")) {
currentEvent = line.slice(6).trim();
} else if (line.startsWith("data:")) {
currentData = line.slice(5).trim();
} else if (line === "") {
// 空行表示事件结束
if (currentData) {
const handlers = eventHandlers.get(currentEvent);
if (handlers) {
@@ -207,18 +186,17 @@ function createSseConnection(options: UseSseOptions = {}) {
} else {
logError("SSE 连接错误:", err);
connectionState.value = SseConnectionState.DISCONNECTED;
scheduleReconnect();
}
});
log("正在建立 SSE 连接...");
};
// 订阅事件,返回取消函数
const on = (eventName: string, handler: EventHandler): (() => void) => {
if (!eventHandlers.has(eventName)) {
eventHandlers.set(eventName, new Set());
}
eventHandlers.get(eventName)!.add(handler);
log(`已订阅事件: ${eventName}`);
return () => {
@@ -229,31 +207,23 @@ function createSseConnection(options: UseSseOptions = {}) {
eventHandlers.delete(eventName);
}
}
log(`已取消订阅事件: ${eventName}`);
};
};
// 主动断开,不会触发重连
const disconnect = () => {
isManualDisconnect = true;
clearConnectionTimeout();
if (reader) {
reader.cancel();
connectionTimeoutTimer = clearTimer(connectionTimeoutTimer);
reconnectTimer = clearTimer(reconnectTimer);
reader?.cancel();
reader = null;
}
if (abortController) {
abortController.abort();
abortController?.abort();
abortController = null;
}
if (eventSource) {
eventSource.close();
eventSource = null;
}
connectionState.value = SseConnectionState.DISCONNECTED;
log("SSE 连接已断开");
};
// 登出时调用,断开并释放所有资源
const cleanup = () => {
disconnect();
eventHandlers.clear();
@@ -270,9 +240,6 @@ function createSseConnection(options: UseSseOptions = {}) {
};
}
/**
* SSE 连接组合式函数(单例模式)
*/
export function useSse(options: UseSseOptions = {}) {
if (!globalInstance) {
globalInstance = createSseConnection(options);
@@ -280,16 +247,6 @@ export function useSse(options: UseSseOptions = {}) {
return globalInstance;
}
/**
* 获取或创建 SSE 实例(用于外部访问)
*/
export function getSseInstance() {
return globalInstance;
}
/**
* 清理全局 SSE 实例
*/
export function cleanupSse() {
if (globalInstance) {
globalInstance.cleanup();

View File

@@ -14,7 +14,7 @@ import { addRecentMenu } from "@/composables/useRecentMenus";
export function setupPermissionGuard() {
const whiteList = ["/login"];
router.beforeEach(async (to, _from, next) => {
router.beforeEach(async (to, _from) => {
NProgress.start();
try {
@@ -23,18 +23,15 @@ export function setupPermissionGuard() {
// 未登录处理
if (!isLoggedIn) {
if (whiteList.includes(to.path)) {
next();
} else {
next(`/login?redirect=${encodeURIComponent(to.fullPath)}`);
NProgress.done();
}
return;
}
NProgress.done();
return `/login?redirect=${encodeURIComponent(to.fullPath)}`;
}
// 已登录访问登录页,重定向到首页
if (to.path === "/login") {
next({ path: "/" });
return;
return { path: "/" };
}
const permissionStore = usePermissionStore();
@@ -54,14 +51,12 @@ export function setupPermissionGuard() {
router.addRoute(route);
});
next({ ...to, replace: true });
return;
return { ...to, replace: true };
}
// 路由 404 检查
if (to.matched.length === 0) {
next("/404");
return;
return "/404";
}
// 动态标题
@@ -69,13 +64,11 @@ export function setupPermissionGuard() {
if (title) {
to.meta.title = title;
}
next();
} catch (error) {
console.error("Route guard error:", error);
await useUserStore().resetAllState();
next("/login");
NProgress.done();
return "/login";
}
});

View File

@@ -43,12 +43,10 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
port: +env.VITE_APP_PORT,
open: true,
proxy: {
// 代理 /dev-api 的请求
[env.VITE_APP_BASE_API]: {
changeOrigin: true,
// 代理目标地址https://api.youlai.tech
target: env.VITE_APP_API_URL,
rewrite: (path: string) => path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
rewrite: (path: string) => path.replace(new RegExp(`^${env.VITE_APP_BASE_API}`), ""),
},
},
},