28 Commits

Author SHA1 Message Date
RuoYi
a8ea02bd4c 优化字典类型属性提醒说明 2026-02-05 12:49:56 +08:00
RuoYi
753b64fc62 update README.md 2026-01-28 14:27:58 +08:00
RuoYi
a723bbefc8 update README.md 2026-01-28 13:35:05 +08:00
RuoYi
05b8d4b32e 添加新群号:127358632 2026-01-28 13:15:05 +08:00
RuoYi
5db10967c8 update README.md 2026-01-28 13:13:19 +08:00
RuoYi
837ecf26bf 优化代码 2026-01-28 13:08:39 +08:00
RuoYi
5cb67df134 升级组件依赖到最新版本 2026-01-15 13:27:26 +08:00
RuoYi
ed36e23505 优化防重提交间隔时间可自定义 2026-01-08 13:39:52 +08:00
RuoYi
2e30c2cecc 优化yarn下富文本控制台警告异常 2026-01-07 15:51:59 +08:00
RuoYi
a1bee42238 暗黑模式底部版权效果优化 2026-01-05 16:32:20 +08:00
RuoYi
2a72074832 暗黑模式菜单效果优化 2026-01-05 16:21:45 +08:00
RuoYi
577376c807 copyright 2026 2026-01-05 14:32:28 +08:00
RuoYi
eee7482e04 update dicttag 2026-01-05 14:26:18 +08:00
RuoYi
c6f3d8b455 暗黑模式切换效果优化 2026-01-04 14:41:59 +08:00
RuoYi
686fd011a9 优化topbar顶部菜单样式 2025-12-18 14:40:52 +08:00
RuoYi
63cfd07f74 若依 3.9.1 2025-12-18 09:07:10 +08:00
RuoYi
d8ef2c3dde 默认固定头部 2025-12-18 09:03:24 +08:00
RuoYi
e430dae0dc 优化字典组件值宽松匹配 2025-12-16 16:41:30 +08:00
RuoYi
88ae28cf5a 优化字典组件数字类型值处理逻辑 2025-12-16 15:46:44 +08:00
RuoYi
eee40b33ba 优化index页面标题读取配置 2025-12-16 14:51:07 +08:00
RuoYi
f6db068c14 优化日志详细请求参数显示 2025-12-16 14:48:26 +08:00
RuoYi
73efecc5c2 菜单导航设置支持纯顶部 2025-12-16 11:41:45 +08:00
RuoYi
168ad6a3b6 行内表单默认设置固定宽度 2025-12-10 09:37:56 +08:00
RuoYi
d2b4332589 优化表单构建关闭页签销毁复制插件 2025-12-04 13:03:38 +08:00
RuoYi
b086e172ae 优化生成代码下载的zip文件名 2025-12-03 10:26:48 +08:00
RuoYi
e7d8010390 登录/注册页面底部版权信息修改为读取配置 2025-12-02 15:29:17 +08:00
RuoYi
b51b7cfa04 修正命名Default 2025-12-02 13:11:06 +08:00
RuoYi
199c9951f5 修复表单构建移除所有控件后切换路由回来空白问题 2025-12-02 13:07:15 +08:00
51 changed files with 710 additions and 234 deletions

View File

@@ -1,11 +1,11 @@
<p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.0</h1>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.1</h1>
<h4 align="center">基于SpringBoot+Vue3前后端分离的Java快速开发框架</h4>
<p align="center">
<a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.0-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.1-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p>
@@ -13,9 +13,23 @@
* 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。
* 配套后端代码仓库地址[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) 或 [RuoYi-Vue-fast](https://gitcode.com/yangzongzhuan/RuoYi-Vue-fast) 版本。
* 前端技术栈([Vue2](https://cn.vuejs.org) + [Element](https://github.com/ElemeFE/element) + [Vue CLI](https://cli.vuejs.org/zh)),请移步[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue/tree/master/ruoyi-ui)。
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)&nbsp;&nbsp;
# 版本对比
RuoYi-Vue 前端项目的三个主要演进版本,方便你直观对比其技术栈差异(并行开发维护)。
| 项目名称 | **RuoYi-Vue** | **RuoYi-Vue3** | **RuoYi-Vue3-TypeScript** |
| :--- | :--- | :--- | :--- |
| **前端框架** | Vue 2 | Vue 3 | Vue 3 |
| **脚本语言** | JavaScript | JavaScript | TypeScript |
| **构建工具** | Vue CLI | Vite | Vite |
| **UI 组件库** | Element UI | Element Plus | Element Plus |
| **状态管理** | Vuex | Pinia | Pinia |
| **路由管理** | Vue Router 3 | Vue Router 4 | Vue Router 4 |
| **核心特点** | 1. 技术栈经典稳定<br>2. 社区资料丰富<br>3. 当前维护重心已转移 | 1. 现代前端技术栈<br>2. 开发体验与性能更优<br>3. 官方主推的活跃版本 | 1. 类型加持,减少沟通成本<br>2. 开发时有提示,效率更高<br>3. 多人协作企业级开发项目 |
| **仓库地址** | [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) | [RuoYi-Vue3](https://gitcode.com/yangzongzhuan/RuoYi-Vue3) | [RuoYi-Vue3-TypeScript](https://gitcode.com/yangzongzhuan/RuoYi-Vue3/tree/typescript) |
## 前端运行
```bash
@@ -105,4 +119,4 @@ yarn dev
## 若依前后端分离交流群
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/已满-104748341-blue.svg)](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [![加入QQ群](https://img.shields.io/badge/已满-160110482-blue.svg)](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [![加入QQ群](https://img.shields.io/badge/已满-170801498-blue.svg)](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [![加入QQ群](https://img.shields.io/badge/已满-108482800-blue.svg)](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [![加入QQ群](https://img.shields.io/badge/已满-101046199-blue.svg)](https://jq.qq.com/?_wv=1027&k=SpyH2875) [![加入QQ群](https://img.shields.io/badge/已满-136919097-blue.svg)](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [![加入QQ群](https://img.shields.io/badge/已满-143961921-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [![加入QQ群](https://img.shields.io/badge/已满-174951577-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [![加入QQ群](https://img.shields.io/badge/已满-161281055-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [![加入QQ群](https://img.shields.io/badge/已满-138988063-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [![加入QQ群](https://img.shields.io/badge/已满-151450850-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [![加入QQ群](https://img.shields.io/badge/已满-224622315-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [![加入QQ群](https://img.shields.io/badge/已满-287842588-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [![加入QQ群](https://img.shields.io/badge/已满-187944233-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [![加入QQ群](https://img.shields.io/badge/已满-228578329-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) [![加入QQ群](https://img.shields.io/badge/已满-191164766-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GsOo-OLz53J8y_9TPoO6XXSGNRTgbFxA&authKey=R7Uy%2Feq%2BZsoKNqHvRKhiXpypW7DAogoWapOawUGHokJSBIBIre2%2FoiAZeZBSLuBc&noverify=0&group_code=191164766) [![加入QQ群](https://img.shields.io/badge/174569686-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=PmYavuzsOthVqfdAPbo4uAeIbu7Ttjgc&authKey=p52l8%2FXa4PS1JcEmS3VccKSwOPJUZ1ZfQ69MEKzbrooNUljRtlKjvsXf04bxNp3G&noverify=0&group_code=174569686) 点击按钮入群。
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/已满-104748341-blue.svg)](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [![加入QQ群](https://img.shields.io/badge/已满-160110482-blue.svg)](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [![加入QQ群](https://img.shields.io/badge/已满-170801498-blue.svg)](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [![加入QQ群](https://img.shields.io/badge/已满-108482800-blue.svg)](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [![加入QQ群](https://img.shields.io/badge/已满-101046199-blue.svg)](https://jq.qq.com/?_wv=1027&k=SpyH2875) [![加入QQ群](https://img.shields.io/badge/已满-136919097-blue.svg)](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [![加入QQ群](https://img.shields.io/badge/已满-143961921-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [![加入QQ群](https://img.shields.io/badge/已满-174951577-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [![加入QQ群](https://img.shields.io/badge/已满-161281055-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [![加入QQ群](https://img.shields.io/badge/已满-138988063-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [![加入QQ群](https://img.shields.io/badge/已满-151450850-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [![加入QQ群](https://img.shields.io/badge/已满-224622315-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [![加入QQ群](https://img.shields.io/badge/已满-287842588-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [![加入QQ群](https://img.shields.io/badge/已满-187944233-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [![加入QQ群](https://img.shields.io/badge/已满-228578329-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) [![加入QQ群](https://img.shields.io/badge/已满-191164766-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GsOo-OLz53J8y_9TPoO6XXSGNRTgbFxA&authKey=R7Uy%2Feq%2BZsoKNqHvRKhiXpypW7DAogoWapOawUGHokJSBIBIre2%2FoiAZeZBSLuBc&noverify=0&group_code=191164766) [![加入QQ群](https://img.shields.io/badge/已满-174569686-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=PmYavuzsOthVqfdAPbo4uAeIbu7Ttjgc&authKey=p52l8%2FXa4PS1JcEmS3VccKSwOPJUZ1ZfQ69MEKzbrooNUljRtlKjvsXf04bxNp3G&noverify=0&group_code=174569686) [![加入QQ群](https://img.shields.io/badge/127358632-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=M9y5NjAl44lAL_Vh2crmEehZU_PMU6KS&authKey=ZSDz8hEREWSaPuxQV3gEwqGIaGjfRNnkB4rJjf0IvXhrSUGSGwQFmBA%2Boe8HFxyl&noverify=0&group_code=127358632) 点击按钮入群。

View File

@@ -7,7 +7,7 @@
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="/favicon.ico">
<title>若依管理系统</title>
<title>%VITE_APP_TITLE%</title>
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
<style>
html,

View File

@@ -1,6 +1,6 @@
{
"name": "ruoyi",
"version": "3.9.0",
"version": "3.9.1",
"description": "若依管理系统",
"author": "若依",
"license": "MIT",
@@ -16,36 +16,39 @@
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
},
"dependencies": {
"@element-plus/icons-vue": "2.3.1",
"@element-plus/icons-vue": "2.3.2",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "13.3.0",
"axios": "1.9.0",
"@vueuse/core": "14.1.0",
"axios": "1.13.2",
"clipboard": "2.0.11",
"echarts": "5.6.0",
"element-plus": "2.10.7",
"element-plus": "2.13.1",
"file-saver": "2.0.5",
"fuse.js": "6.6.2",
"js-beautify": "1.14.11",
"fuse.js": "7.1.0",
"js-beautify": "1.15.4",
"js-cookie": "3.0.5",
"jsencrypt": "3.3.2",
"nprogress": "0.2.0",
"pinia": "3.0.2",
"pinia": "3.0.4",
"splitpanes": "4.0.4",
"vue": "3.5.16",
"vue": "3.5.26",
"vue-cropper": "1.1.1",
"vue-router": "4.5.1",
"vue-router": "4.6.4",
"vuedraggable": "4.1.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "5.2.4",
"sass-embedded": "1.89.1",
"sass-embedded": "1.97.2",
"unplugin-auto-import": "0.18.6",
"unplugin-vue-setup-extend-plus": "1.0.1",
"vite": "6.3.5",
"vite": "6.4.1",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1"
},
"overrides": {
"quill": "2.0.2"
},
"resolutions": {
"quill": "2.0.2"
}
}

View File

@@ -1 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h54.796v18.286H36.531V128H18.265V73.143H0V54.857zm127.857-36.571H91.935V128H72.456V18.286H36.534V0h91.326l-.003 18.286z"/></svg>
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path fill="#5a5e66" d="M0 54.857h54.796v18.286H36.531V128H18.265V73.143H0V54.857zm127.857-36.571H91.935V128H72.456V18.286H36.534V0h91.326l-.003 18.286z"/></svg>

Before

Width:  |  Height:  |  Size: 211 B

After

Width:  |  Height:  |  Size: 226 B

View File

@@ -60,6 +60,14 @@
color: inherit;
}
.el-form--inline {
.el-form-item {
.el-input, .el-cascader, .el-select, .el-autocomplete {
width: 200px;
}
}
}
.el-form .el-form-item__label {
font-weight: 700;
}
@@ -148,6 +156,16 @@
width: inherit;
}
/* horizontal el menu */
.el-menu--horizontal .el-menu-item .svg-icon + span,
.el-menu--horizontal .el-sub-menu__title .svg-icon + span {
margin-left: 3px;
}
.el-menu--horizontal .el-menu--popup {
min-width: 120px !important;
}
/** 表格更多操作下拉样式 */
.el-table .el-dropdown-link {
cursor: pointer;

View File

@@ -61,7 +61,7 @@
}
.svg-icon {
margin-right: 16px;
margin-right: 10px !important;
}
.el-menu {

View File

@@ -47,3 +47,34 @@
.breadcrumb-leave-active {
position: absolute;
}
/* 黑暗模式下过渡效果 */
::view-transition-new(root), ::view-transition-old(root) {
animation: none !important;
backface-visibility: hidden;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.dark::view-transition-old(root) {
z-index: 2147483646;
background: var(--bg-color-dark);
}
.dark::view-transition-new(root) {
z-index: 1;
background: var(--bg-color);
}
::view-transition-old(root) {
z-index: 1;
background: var(--bg-color);
}
::view-transition-new(root) {
z-index: 2147483646;
background: var(--bg-color-dark);
}

View File

@@ -89,6 +89,9 @@ html.dark {
--el-text-color-regular: #d0d0d0;
--el-border-color: #434343;
--el-border-color-light: #434343;
/* primary */
--primary-bg: #18212b;
/* 侧边栏 */
--sidebar-bg: #141414;
@@ -127,8 +130,8 @@ html.dark {
--splitpanes-default-bg: #141414;
/* 侧边栏菜单覆盖 */
.sidebar-container {
.el-menu-item, .menu-title {
.sidebar-container {
.el-menu-item:not(.is-active), .menu-title {
color: var(--el-text-color-regular);
}
& .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title,
@@ -137,13 +140,27 @@ html.dark {
}
}
.topmenu-container {
.el-menu-item,
.el-sub-menu .el-sub-menu__title {
color: var(--el-text-color-regular) !important;
}
}
.topbar-menu.el-menu--horizontal > .el-sub-menu .el-sub-menu__title{
color: var(--el-text-color-regular) !important;
}
/* 顶部栏栏菜单覆盖 */
.el-menu--horizontal {
.el-menu-item {
.el-menu-item, .el-sub-menu {
&:not(.is-disabled) {
&:hover,
&:focus {
background-color: var(--navbar-hover) !important;
.el-sub-menu__title {
background-color: var(--navbar-hover) !important;
}
}
}
}
@@ -173,6 +190,33 @@ html.dark {
}
}
/* 按钮样式覆盖 */
.el-button--primary.is-plain {
background-color: var(--primary-bg);
border: 1px solid var(--el-color-primary-light-2);
color: var(--el-color-primary-light-2);
&:hover {
background-color: var(--el-button-hover-bg-color);
border-color: var(--el-button-hover-border-color);
color: var(--el-button-hover-text-color);
}
&.is-disabled {
background-color: var(--link-active-bg-color);
border-color: var(--el-color-primary-light-3);
color: var(--el-color-primary-light-3);
opacity: 0.5;
}
}
/* primary tag 样式覆盖 */
.el-tag--primary {
background-color: var(--primary-bg);
border: 1px solid var(--el-border-color-light);
color: var(--el-color-primary);
}
/* 表格样式覆盖 */
.el-table {
--el-table-header-bg-color: var(--el-bg-color-overlay) !important;
@@ -217,5 +261,11 @@ html.dark {
background: var(--cron-border);
}
/* 底部版权样式覆盖 */
.copyright {
background-color: var(--el-bg-color) !important;
color: var(--el-text-color-regular) !important;
border-top: 1px solid var(--el-bg-color) !important;
}
}

View File

@@ -88,7 +88,6 @@ getBreadcrumb()
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 8px;
.no-redirect {
color: #97a8be;

View File

@@ -1,7 +1,7 @@
<template>
<div>
<template v-for="(item, index) in options">
<template v-if="values.includes(item.value)">
<template v-if="isValueMatch(item.value)">
<span
v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)"
:key="item.value"
@@ -49,6 +49,7 @@ const props = defineProps({
const values = computed(() => {
if (props.value === null || typeof props.value === 'undefined' || props.value === '') return []
if (typeof props.value === 'number' || typeof props.value === 'boolean') return [props.value]
return Array.isArray(props.value) ? props.value.map(item => '' + item) : String(props.value).split(props.separator)
})
@@ -59,7 +60,7 @@ const unmatch = computed(() => {
// 传入值为数组
let unmatch = false // 添加一个标志来判断是否有未匹配项
values.value.forEach(item => {
if (!props.options.some(v => v.value === item)) {
if (!props.options.some(v => v.value == item)) {
unmatchArray.value.push(item)
unmatch = true // 如果有未匹配项将标志设置为true
}
@@ -73,6 +74,10 @@ function handleArray(array) {
return pre + " " + cur
})
}
function isValueMatch(itemValue) {
return values.value.some(val => val == itemValue)
}
</script>
<style scoped>

View File

@@ -29,11 +29,11 @@
<!-- 文件列表 -->
<transition-group ref="uploadFileList" class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
<el-link :href="`${baseUrl}${file.url}`" underline="never" target="_blank">
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
</el-link>
<div class="ele-upload-list__item-content-action">
<el-link :underline="false" @click="handleDelete(index)" type="danger" v-if="!disabled">&nbsp;删除</el-link>
<el-link underline="never" @click="handleDelete(index)" type="danger" v-if="!disabled">&nbsp;删除</el-link>
</div>
</li>
</transition-group>

View File

@@ -7,7 +7,7 @@
<script setup>
import { useFullscreen } from '@vueuse/core'
const { isFullscreen, enter, exit, toggle } = useFullscreen()
const { isFullscreen, toggle } = useFullscreen()
</script>
<style lang='scss' scoped>

View File

@@ -20,8 +20,6 @@ import useAppStore from "@/store/modules/app"
const appStore = useAppStore()
const size = computed(() => appStore.size)
const route = useRoute()
const router = useRouter()
const { proxy } = getCurrentInstance()
const sizeOptions = ref([
{ label: "较大", value: "large" },

View File

@@ -175,7 +175,7 @@ onMounted(() => {
float: left;
height: 50px !important;
line-height: 50px !important;
color: #999093 !important;
color: #303133 !important;
padding: 0 5px !important;
margin: 0 10px !important;
}
@@ -190,7 +190,7 @@ onMounted(() => {
float: left;
height: 50px !important;
line-height: 50px !important;
color: #999093 !important;
color: #303133 !important;
padding: 0 5px !important;
margin: 0 10px !important;
}
@@ -212,6 +212,4 @@ onMounted(() => {
margin-left: 8px;
margin-top: 0px;
}
</style>

View File

@@ -71,6 +71,39 @@ function addIframe() {
min-height: 0px;
}
}
/* 移动端fixed-header优化 */
@media screen and (max-width: 991px) {
.fixed-header + .app-main {
padding-bottom: max(60px, calc(constant(safe-area-inset-bottom) + 40px));
padding-bottom: max(60px, calc(env(safe-area-inset-bottom) + 40px));
overscroll-behavior-y: none;
}
.hasTagsView .fixed-header + .app-main {
padding-bottom: max(60px, calc(constant(safe-area-inset-bottom) + 40px));
padding-bottom: max(60px, calc(env(safe-area-inset-bottom) + 40px));
overscroll-behavior-y: none;
}
}
@supports (-webkit-touch-callout: none) {
@media screen and (max-width: 991px) {
.fixed-header + .app-main {
padding-bottom: max(17px, calc(constant(safe-area-inset-bottom) + 10px));
padding-bottom: max(17px, calc(env(safe-area-inset-bottom) + 10px));
height: calc(100svh - 50px);
height: calc(100dvh - 50px);
}
.hasTagsView .fixed-header + .app-main {
padding-bottom: max(17px, calc(constant(safe-area-inset-bottom) + 10px));
padding-bottom: max(17px, calc(env(safe-area-inset-bottom) + 10px));
height: calc(100svh - 84px);
height: calc(100dvh - 84px);
}
}
}
</style>
<style lang="scss">

View File

@@ -1,8 +1,12 @@
<template>
<div class="navbar">
<div class="navbar" :class="'nav' + settingsStore.navType">
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" />
<top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" />
<breadcrumb v-if="settingsStore.navType == 1" id="breadcrumb-container" class="breadcrumb-container" />
<top-nav v-if="settingsStore.navType == 2" id="topmenu-container" class="topmenu-container" />
<template v-if="settingsStore.navType == 3">
<logo v-show="settingsStore.sidebarLogo" :collapse="false"></logo>
<top-bar id="topbar-container" class="topbar-container" />
</template>
<div class="right-menu">
<template v-if="appStore.device !== 'mobile'">
@@ -57,6 +61,8 @@
import { ElMessageBox } from 'element-plus'
import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav'
import TopBar from './TopBar'
import Logo from './Sidebar/Logo'
import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
@@ -105,26 +111,75 @@ function setLayout() {
emits('setLayout')
}
function toggleTheme() {
settingsStore.toggleTheme()
async function toggleTheme(event) {
const x = event?.clientX || window.innerWidth / 2
const y = event?.clientY || window.innerHeight / 2
const wasDark = settingsStore.isDark
const isReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches
const isSupported = document.startViewTransition && !isReducedMotion
if (!isSupported) {
settingsStore.toggleTheme()
return
}
try {
const transition = document.startViewTransition(async () => {
await new Promise((resolve) => setTimeout(resolve, 10))
settingsStore.toggleTheme()
await nextTick()
})
await transition.ready
const endRadius = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y))
const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`]
document.documentElement.animate(
{
clipPath: !wasDark ? [...clipPath].reverse() : clipPath
}, {
duration: 650,
easing: "cubic-bezier(0.4, 0, 0.2, 1)",
fill: "forwards",
pseudoElement: !wasDark ? "::view-transition-old(root)" : "::view-transition-new(root)"
}
)
await transition.finished
} catch (error) {
console.warn("View transition failed, falling back to immediate toggle:", error)
settingsStore.toggleTheme()
}
}
</script>
<style lang='scss' scoped>
.navbar.nav3 {
.hamburger-container {
display: none !important;
}
}
.navbar {
height: 50px;
overflow: hidden;
position: relative;
background: var(--navbar-bg);
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
display: flex;
align-items: center;
// padding: 0 8px;
box-sizing: border-box;
.hamburger-container {
line-height: 46px;
height: 100%;
float: left;
cursor: pointer;
transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
display: flex;
align-items: center;
flex-shrink: 0;
margin-right: 8px;
&:hover {
background: rgba(0, 0, 0, 0.025);
@@ -132,7 +187,7 @@ function toggleTheme() {
}
.breadcrumb-container {
float: left;
flex-shrink: 0;
}
.topmenu-container {
@@ -140,16 +195,26 @@ function toggleTheme() {
left: 50px;
}
.topbar-container {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
overflow: hidden;
margin-left: 8px;
}
.errLog-container {
display: inline-block;
vertical-align: top;
}
.right-menu {
float: right;
height: 100%;
line-height: 50px;
display: flex;
align-items: center;
margin-left: auto;
&:focus {
outline: none;

View File

@@ -1,5 +1,26 @@
<template>
<el-drawer v-model="showSettings" :withHeader="false" :lock-scroll="false" direction="rtl" size="300px">
<div class="setting-drawer-title">
<h3 class="drawer-title">菜单导航设置</h3>
</div>
<div class="nav-wrap">
<el-tooltip content="左侧菜单" placement="bottom">
<div class="item left" @click="handleNavType(1)" :class="{ activeItem: navType == 1 }">
<b></b><b></b>
</div>
</el-tooltip>
<el-tooltip content="混合菜单" placement="bottom">
<div class="item mix" @click="handleNavType(2)" :class="{ activeItem: navType == 2 }">
<b></b><b></b>
</div>
</el-tooltip>
<el-tooltip content="顶部菜单" placement="bottom">
<div class="item top" @click="handleNavType(3)" :class="{ activeItem: navType == 3 }">
<b></b><b></b>
</div>
</el-tooltip>
</div>
<div class="setting-drawer-title">
<h3 class="drawer-title">主题风格设置</h3>
</div>
@@ -35,13 +56,6 @@
<h3 class="drawer-title">系统布局配置</h3>
<div class="drawer-item">
<span>开启 TopNav</span>
<span class="comp-style">
<el-switch v-model="settingsStore.topNav" @change="topNavChange" class="drawer-switch" />
</span>
</div>
<div class="drawer-item">
<span>开启 Tags-Views</span>
<span class="comp-style">
@@ -103,19 +117,12 @@ const appStore = useAppStore()
const settingsStore = useSettingsStore()
const permissionStore = usePermissionStore()
const showSettings = ref(false)
const navType = ref(settingsStore.navType)
const theme = ref(settingsStore.theme)
const sideTheme = ref(settingsStore.sideTheme)
const storeSettings = computed(() => settingsStore)
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"])
/** 是否需要topnav */
function topNavChange(val) {
if (!val) {
appStore.toggleSideBarHide(false)
permissionStore.setSidebarRouters(permissionStore.defaultRoutes)
}
}
/** 是否需要dynamicTitle */
function dynamicTitleChange() {
useSettingsStore().setTitle(useSettingsStore().title)
@@ -131,10 +138,34 @@ function handleTheme(val) {
sideTheme.value = val
}
function handleNavType(val) {
settingsStore.navType = val
navType.value = val
}
/** 菜单导航设置 */
watch(() => navType, val => {
if (val.value == 1) {
appStore.sidebar.opened = true
appStore.toggleSideBarHide(false)
}
if (val.value == 2) {
appStore.sidebar.opened = true
}
if (val.value == 3) {
appStore.sidebar.opened = false
appStore.toggleSideBarHide(true)
}
if ([1, 3].includes(val.value)) {
permissionStore.setSidebarRouters(permissionStore.defaultRoutes)
}
}, { immediate: true, deep: true }
)
function saveSetting() {
proxy.$modal.loading("正在保存到本地,请稍候...")
let layoutSetting = {
"topNav": storeSettings.value.topNav,
"navType": storeSettings.value.navType,
"tagsView": storeSettings.value.tagsView,
"tagsIcon": storeSettings.value.tagsIcon,
"fixedHeader": storeSettings.value.fixedHeader,
@@ -218,4 +249,67 @@ defineExpose({
margin: -3px 8px 0px 0px;
}
}
// 导航模式
.nav-wrap {
display: flex;
justify-content: flex-start;
align-items: center;
margin-top: 10px;
margin-bottom: 20px;
.activeItem {
border: 2px solid var(--el-color-primary) !important;
}
.item {
position: relative;
margin-right: 16px;
cursor: pointer;
width: 56px;
height: 48px;
border-radius: 4px;
background: #f0f2f5;
border: 2px solid transparent;
}
.left {
b:first-child {
display: block;
height: 30%;
background: #fff;
}
b:last-child {
width: 30%;
background: #1b2a47;
position: absolute;
height: 100%;
top: 0;
border-radius: 4px 0 0 4px;
}
}
.mix {
b:first-child {
border-radius: 4px 4px 0 0;
display: block;
height: 30%;
background: #1b2a47;
}
b:last-child {
width: 30%;
background: #1b2a47;
position: absolute;
height: 70%;
border-radius: 0 0 0 4px;
}
}
.top {
b:first-child {
display: block;
height: 30%;
background: #1b2a47;
border-radius: 4px 4px 0 0;
}
}
}
</style>

View File

@@ -34,6 +34,9 @@ const getLogoBackground = computed(() => {
if (settingsStore.isDark) {
return 'var(--sidebar-bg)'
}
if (settingsStore.navType == 3) {
return variables.menuLightBg
}
return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBg
})
@@ -42,6 +45,9 @@ const getLogoTextColor = computed(() => {
if (settingsStore.isDark) {
return 'var(--sidebar-text)'
}
if (settingsStore.navType == 3) {
return variables.menuLightText
}
return sideTheme.value === 'theme-dark' ? '#fff' : variables.menuLightText
})
</script>
@@ -58,7 +64,6 @@ const getLogoTextColor = computed(() => {
.sidebar-logo-container {
position: relative;
width: 100%;
height: 50px;
line-height: 50px;
background: v-bind(getLogoBackground);

View File

@@ -0,0 +1,99 @@
<template>
<el-menu class="topbar-menu" :ellipsis="false" :default-active="activeMenu" :active-text-color="theme" mode="horizontal">
<sidebar-item :key="route.path + index" v-for="(route, index) in topMenus" :item="route" :base-path="route.path" />
<el-sub-menu index="more" class="el-sub-menu__hide-arrow" v-if="moreRoutes.length > 0">
<template #title>
<span>更多菜单</span>
</template>
<sidebar-item :key="route.path + index" v-for="(route, index) in moreRoutes" :item="route" :base-path="route.path" />
</el-sub-menu>
</el-menu>
</template>
<script setup>
import SidebarItem from '../Sidebar/SidebarItem'
import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
const route = useRoute()
const appStore = useAppStore()
const settingsStore = useSettingsStore()
const permissionStore = usePermissionStore()
const sidebarRouters = computed(() => permissionStore.sidebarRouters)
const theme = computed(() => settingsStore.theme)
const device = computed(() => appStore.device)
const activeMenu = computed(() => {
const { meta, path } = route
if (meta.activeMenu) {
return meta.activeMenu
}
return path
})
const visibleNumber = ref(5)
const topMenus = computed(() => {
return permissionStore.sidebarRouters.filter((f) => !f.hidden).slice(0, visibleNumber.value)
})
const moreRoutes = computed(() => {
return permissionStore.sidebarRouters.filter((f) => !f.hidden).slice(visibleNumber.value, sidebarRouters.value.length - visibleNumber.value)
})
function setVisibleNumber() {
const width = document.body.getBoundingClientRect().width / 3
visibleNumber.value = parseInt(width / 85)
}
onMounted(() => {
window.addEventListener('resize', setVisibleNumber)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', setVisibleNumber)
})
onMounted(() => {
setVisibleNumber()
})
</script>
<style lang="scss">
/* menu item */
.topbar-menu.el-menu--horizontal .el-submenu__title, .topbar-menu.el-menu--horizontal .el-menu-item {
padding: 0 10px !important;
}
.topbar-menu.el-menu--horizontal > .el-menu-item {
float: left;
height: 50px !important;
line-height: 50px !important;
color: #303133 !important;
padding: 0 5px !important;
margin: 0 10px !important;
}
.el-sub-menu.is-active .svg-icon, .el-menu-item.is-active .svg-icon + span, .el-sub-menu.is-active .svg-icon + span, .el-sub-menu.is-active .el-sub-menu__title span {
color: v-bind(theme);
}
/* sub-menu item */
.topbar-menu.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
float: left;
line-height: 50px !important;
color: #303133 !important;
margin: 0 15px -3px!important;
}
/* topbar more arrow */
.topbar-menu .el-sub-menu .el-sub-menu__icon-arrow {
position: static;
margin-left: 8px;
margin-top: 0px;
display: block !important;
}
/* menu__title el-menu-item */
.topbar-menu.el-menu--horizontal .el-sub-menu__title, .topbar-menu.el-menu--horizontal .el-menu-item {
height: 60px;
}
</style>

View File

@@ -22,7 +22,6 @@ import useSettingsStore from '@/store/modules/settings'
const settingsStore = useSettingsStore()
const theme = computed(() => settingsStore.theme)
const sideTheme = computed(() => settingsStore.sideTheme)
const sidebar = computed(() => useAppStore().sidebar)
const device = computed(() => useAppStore().device)
const needTagsView = computed(() => settingsStore.tagsView)

View File

@@ -15,9 +15,9 @@ export default {
showSettings: true,
/**
* 是否显示顶部导航
* 菜单导航模式 1、纯左侧 2、混合左侧+顶部) 3、纯顶部
*/
topNav: false,
navType: 1,
/**
* 是否显示 tagsView
@@ -32,7 +32,7 @@ export default {
/**
* 是否固定头部
*/
fixedHeader: false,
fixedHeader: true,
/**
* 是否显示logo
@@ -52,6 +52,6 @@ export default {
/**
* 底部版权文本内容
*/
footerContent: 'Copyright © 2018-2025 RuoYi. All Rights Reserved.'
footerContent: 'Copyright © 2018-2026 RuoYi. All Rights Reserved.'
}

View File

@@ -1,3 +1,3 @@
const store = createPinia()
export default store
export default store

View File

@@ -5,7 +5,7 @@ import { useDynamicTitle } from '@/utils/dynamicTitle'
const isDark = useDark()
const toggleDark = useToggle(isDark)
const { sideTheme, showSettings, topNav, tagsView, tagsIcon, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings
const { sideTheme, showSettings, navType, tagsView, tagsIcon, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings
const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
@@ -17,7 +17,7 @@ const useSettingsStore = defineStore(
theme: storageSetting.theme || '#409EFF',
sideTheme: storageSetting.sideTheme || sideTheme,
showSettings: showSettings,
topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
navType: storageSetting.navType === undefined ? navType : storageSetting.navType,
tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
tagsIcon: storageSetting.tagsIcon === undefined ? tagsIcon : storageSetting.tagsIcon,
fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,

View File

@@ -1,29 +0,0 @@
export default [
{
layout: 'colFormItem',
tagIcon: 'input',
label: '手机号',
vModel: 'mobile',
formId: 6,
tag: 'el-input',
placeholder: '请输入手机号',
defaultValue: '',
span: 24,
style: { width: '100%' },
clearable: true,
prepend: '',
append: '',
'prefix-icon': 'Cellphone',
'suffix-icon': '',
maxlength: 11,
'show-word-limit': true,
readonly: false,
disabled: false,
required: true,
changeTag: true,
regList: [{
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
message: '手机号格式错误'
}]
}
]

View File

@@ -0,0 +1,37 @@
export const drawingDefaultValue = []
export function initDrawingDefaultValue() {
if (drawingDefaultValue.length === 0) {
drawingDefaultValue.push({
layout: 'colFormItem',
tagIcon: 'input',
label: '手机号',
vModel: 'mobile',
formId: 6,
tag: 'el-input',
placeholder: '请输入手机号',
defaultValue: '',
span: 24,
style: {width: '100%'},
clearable: true,
prepend: '',
append: '',
'prefix-icon': 'Cellphone',
'suffix-icon': '',
maxlength: 11,
'show-word-limit': true,
readonly: false,
disabled: false,
required: true,
changeTag: true,
regList: [{
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
message: '手机号格式错误'
}]
})
}
}
export function cleanDrawingDefaultValue() {
drawingDefaultValue.splice(0, drawingDefaultValue.length)
}

View File

@@ -5,13 +5,13 @@ import { parseTime } from './ruoyi'
*/
export function formatDate(cellValue) {
if (cellValue == null || cellValue == "") return ""
var date = new Date(cellValue)
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
const date = new Date(cellValue)
const year = date.getFullYear()
const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
const hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
const minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
const seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
}
@@ -84,7 +84,7 @@ export function getQueryObject(url) {
export function byteLength(str) {
// returns the byte length of an utf8 string
let s = str.length
for (var i = str.length - 1; i >= 0; i--) {
for (let i = str.length - 1; i >= 0; i--) {
const code = str.charCodeAt(i)
if (code > 0x7f && code <= 0x7ff) s++
else if (code > 0x7ff && code <= 0xffff) s += 2

View File

@@ -26,6 +26,8 @@ service.interceptors.request.use(config => {
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
// 间隔时间(ms),小于此时间视为重复提交
const interval = (config.headers || {}).interval || 1000
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
@@ -55,7 +57,6 @@ service.interceptors.request.use(config => {
const s_url = sessionObj.url // 请求地址
const s_data = sessionObj.data // 请求数据
const s_time = sessionObj.time // 请求时间
const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交'
console.warn(`[${s_url}]: ` + message)
@@ -115,7 +116,7 @@ service.interceptors.response.use(res => {
} else if (message.includes("timeout")) {
message = "系统接口请求超时"
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常"
message = "系统接口" + message.slice(-3) + "异常"
}
ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)

View File

@@ -71,7 +71,7 @@ export function selectDictLabel(datas, value) {
if (value === undefined) {
return ""
}
var actions = []
const actions = []
Object.keys(datas).some((key) => {
if (datas[key].value == ('' + value)) {
actions.push(datas[key].label)
@@ -92,11 +92,11 @@ export function selectDictLabels(datas, value, separator) {
if (Array.isArray(value)) {
value = value.join(",")
}
var actions = []
var currentSeparator = undefined === separator ? "," : separator
var temp = value.split(currentSeparator)
const actions = []
const currentSeparator = undefined === separator ? "," : separator
const temp = value.split(currentSeparator)
Object.keys(value.split(currentSeparator)).some((val) => {
var match = false
let match = false
Object.keys(datas).some((key) => {
if (datas[key].value == ('' + temp[val])) {
actions.push(datas[key].label + currentSeparator)
@@ -112,9 +112,9 @@ export function selectDictLabels(datas, value, separator) {
// 字符串格式化(%s )
export function sprintf(str) {
var args = arguments, flag = true, i = 1
let flag = true, i = 1
str = str.replace(/%s/g, function () {
var arg = args[i++]
const arg = args[i++]
if (typeof arg === 'undefined') {
flag = false
return ''
@@ -134,7 +134,7 @@ export function parseStrEmpty(str) {
// 数据合并
export function mergeRecursive(source, target) {
for (var p in target) {
for (const p in target) {
try {
if (target[p].constructor == Object) {
source[p] = mergeRecursive(source[p], target[p])
@@ -156,25 +156,25 @@ export function mergeRecursive(source, target) {
* @param {*} children 孩子节点字段 默认 'children'
*/
export function handleTree(data, id, parentId, children) {
let config = {
const config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children'
}
var childrenListMap = {}
var tree = []
for (let d of data) {
let id = d[config.id]
const childrenListMap = {}
const tree = []
for (const d of data) {
const id = d[config.id]
childrenListMap[id] = d
if (!d[config.childrenList]) {
d[config.childrenList] = []
}
}
for (let d of data) {
let parentId = d[config.parentId]
let parentObj = childrenListMap[parentId]
for (const d of data) {
const parentId = d[config.parentId]
const parentObj = childrenListMap[parentId]
if (!parentObj) {
tree.push(d)
} else {
@@ -192,13 +192,13 @@ export function tansParams(params) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName]
var part = encodeURIComponent(propName) + "="
const part = encodeURIComponent(propName) + "="
if (value !== null && value !== "" && typeof (value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
let params = propName + '[' + key + ']'
var subPart = encodeURIComponent(params) + "="
const params = propName + '[' + key + ']'
const subPart = encodeURIComponent(params) + "="
result += subPart + encodeURIComponent(value[key]) + "&"
}
}

View File

@@ -8,7 +8,7 @@ Math.easeInOutQuad = function(t, b, c, d) {
}
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
var requestAnimFrame = (function() {
const requestAnimFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
})()
@@ -37,11 +37,11 @@ export function scrollTo(to, duration, callback) {
const increment = 20
let currentTime = 0
duration = (typeof (duration) === 'undefined') ? 500 : duration
var animateScroll = function() {
const animateScroll = function() {
// increment the time
currentTime += increment
// find the value with the quadratic in-out easing function
var val = Math.easeInOutQuad(currentTime, start, change, duration)
const val = Math.easeInOutQuad(currentTime, start, change, duration)
// move the document.body
move(val)
// do the animation unless its over

View File

@@ -1,49 +1,49 @@
// 处理主题样式
export function handleThemeStyle(theme) {
document.documentElement.style.setProperty('--el-color-primary', theme)
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, `${getLightColor(theme, i / 10)}`)
}
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, `${getDarkColor(theme, i / 10)}`)
}
document.documentElement.style.setProperty('--el-color-primary', theme)
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, `${getLightColor(theme, i / 10)}`)
}
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, `${getDarkColor(theme, i / 10)}`)
}
}
// hex颜色转rgb颜色
export function hexToRgb(str) {
str = str.replace('#', '')
let hexs = str.match(/../g)
for (let i = 0; i < 3; i++) {
hexs[i] = parseInt(hexs[i], 16)
}
return hexs
str = str.replace('#', '')
let hexs = str.match(/../g)
for (let i = 0; i < 3; i++) {
hexs[i] = parseInt(hexs[i], 16)
}
return hexs
}
// rgb颜色转Hex颜色
export function rgbToHex(r, g, b) {
let hexs = [r.toString(16), g.toString(16), b.toString(16)]
for (let i = 0; i < 3; i++) {
if (hexs[i].length == 1) {
hexs[i] = `0${hexs[i]}`
}
}
return `#${hexs.join('')}`
let hexs = [r.toString(16), g.toString(16), b.toString(16)]
for (let i = 0; i < 3; i++) {
if (hexs[i].length == 1) {
hexs[i] = `0${hexs[i]}`
}
}
return `#${hexs.join('')}`
}
// 变浅颜色值
export function getLightColor(color, level) {
let rgb = hexToRgb(color)
for (let i = 0; i < 3; i++) {
rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i])
}
return rgbToHex(rgb[0], rgb[1], rgb[2])
let rgb = hexToRgb(color)
for (let i = 0; i < 3; i++) {
rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i])
}
return rgbToHex(rgb[0], rgb[1], rgb[2])
}
// 变深颜色值
export function getDarkColor(color, level) {
let rgb = hexToRgb(color)
for (let i = 0; i < 3; i++) {
rgb[i] = Math.floor(rgb[i] * (1 - level))
}
return rgbToHex(rgb[0], rgb[1], rgb[2])
let rgb = hexToRgb(color)
for (let i = 0; i < 3; i++) {
rgb[i] = Math.floor(rgb[i] * (1 - level))
}
return rgbToHex(rgb[0], rgb[1], rgb[2])
}

View File

@@ -88,7 +88,7 @@
<s> 满101046199 </s> <s> 满136919097 </s> <s> 满143961921 </s> <s> 满174951577 </s>
<s> 满161281055 </s> <s> 满138988063 </s> <s> 满151450850 </s> <s> 满224622315 </s>
<s> 满287842588 </s> <s> 满187944233 </s> <s> 满228578329 </s> <s> 满191164766 </s>
<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=EeCBXu51I1zPWRia2uskpjDRx6VrbnFN&authKey=Xm8yDxk0%2FyYGI11oxhXaQnTn4K7UwCk7Kn2MZTh3P1JxLctollAkyeySjnaILDkb&noverify=0&group_code=174569686" target="_blank">174569686</a>
<s> 满174569686 </s> <a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=M9y5NjAl44lAL_Vh2crmEehZU_PMU6KS&authKey=ZSDz8hEREWSaPuxQV3gEwqGIaGjfRNnkB4rJjf0IvXhrSUGSGwQFmBA%2Boe8HFxyl&noverify=0&group_code=127358632" target="_blank">127358632</a>
</p>
<p>
<i class="el-icon-chat-dot-round"></i> 微信<a
@@ -114,6 +114,42 @@
</div>
</template>
<el-collapse accordion>
<el-collapse-item title="v3.9.1 - 2025-12-18">
<ol>
<li>支持防盗链功能</li>
<li>菜单导航设置支持纯顶部</li>
<li>使用yauaa代替bitwalker</li>
<li>用户头像更换后移除旧头像文件</li>
<li>支持Excel导出对象的多个子列表</li>
<li>升级oshi到最新版本6.9.1</li>
<li>升级druid到最新版本1.2.27</li>
<li>升级fastjson到最新版2.0.60</li>
<li>升级spring-security到5.7.14</li>
<li>升级tomcat到最新版本9.0.112</li>
<li>升级commons.io到最新版本2.21.0</li>
<li>用户导入添加验证提示</li>
<li>显示列信息支持对象格式</li>
<li>忽略用户密码字段的JSON序列化</li>
<li>网页标题设置新增SET_TITLE方法</li>
<li>自动识别json对象白名单配置范围缩小</li>
<li>登录/注册页面底部版权信息修改为读取配置</li>
<li>修复用户归属部门无法修改为空问题</li>
<li>修复固定头部时出现的导航栏偏移问题</li>
<li>修复v3时间控件between选择后清空报错问题</li>
<li>修复comboReadDict属性下多个sheet出现的报错</li>
<li>修复表单构建移除所有控件后切换路由回来空白问题</li>
<li>优化布局设置显示</li>
<li>优化字典组件值宽松匹配</li>
<li>优化获取字典类型值的方法</li>
<li>优化生成代码下载的zip文件名</li>
<li>优化日志记录参数拼装提升效率</li>
<li>优化导入文件检查标题行不能为空</li>
<li>优化表单构建关闭页签销毁复制插件</li>
<li>优化Excel统计行数值的单元格样式显示</li>
<li>优化数据权限控制逻辑放开permission限制</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.9.0 - 2025-05-28">
<ol>
<li>优化菜单搜索查询页</li>
@@ -1059,7 +1095,7 @@
</template>
<script setup name="Index">
const version = ref('3.9.0')
const version = ref('3.9.1')
function goTarget(url) {
window.open(url, '__blank')

View File

@@ -59,7 +59,7 @@
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
<span>{{ footerContent }}</span>
</div>
</div>
</template>
@@ -69,8 +69,10 @@ import { getCodeImg } from "@/api/login"
import Cookies from "js-cookie"
import { encrypt, decrypt } from "@/utils/jsencrypt"
import useUserStore from '@/store/modules/user'
import defaultSettings from '@/settings'
const title = import.meta.env.VITE_APP_TITLE
const footerContent = defaultSettings.footerContent
const userStore = useUserStore()
const route = useRoute()
const router = useRouter()

View File

@@ -352,8 +352,8 @@ function reset() {
jobGroup: undefined,
invokeTarget: undefined,
cronExpression: undefined,
misfirePolicy: 1,
concurrent: 1,
misfirePolicy: '1',
concurrent: '1',
status: "0"
}
proxy.resetForm("jobRef")
@@ -412,8 +412,8 @@ function handleRun(row) {
proxy.$modal.confirm('确认要立即执行一次"' + row.jobName + '"任务吗?').then(function () {
return runJob(row.jobId, row.jobGroup)
}).then(() => {
proxy.$modal.msgSuccess("执行成功")})
.catch(() => {})
proxy.$modal.msgSuccess("执行成功")
}).catch(() => {})
}
/** 任务详细信息 */

View File

@@ -97,8 +97,8 @@ function resetQuery() {
/** 强退按钮操作 */
function handleForceLogout(row) {
proxy.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?').then(function () {
return forceLogout(row.tokenId)
proxy.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?').then(function () {
return forceLogout(row.tokenId)
}).then(() => {
getList()
proxy.$modal.msgSuccess("删除成功")

View File

@@ -166,7 +166,7 @@
<el-form-item label="操作方法:">{{ form.method }}</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="请求参数:">{{ form.operParam }}</el-form-item>
<el-form-item label="请求参数:" style="word-break: break-all; white-space: pre-wrap;">{{ form.operParam }}</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="返回参数:">{{ form.jsonResult }}</el-form-item>

View File

@@ -70,7 +70,7 @@
</el-form>
<!-- 底部 -->
<div class="el-register-footer">
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
<span>{{ footerContent }}</span>
</div>
</div>
</template>
@@ -78,8 +78,10 @@
<script setup>
import { ElMessageBox } from "element-plus"
import { getCodeImg, register } from "@/api/login"
import defaultSettings from '@/settings'
const title = import.meta.env.VITE_APP_TITLE
const footerContent = defaultSettings.footerContent
const router = useRouter()
const { proxy } = getCurrentInstance()

View File

@@ -141,12 +141,20 @@
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="dictRef" :model="form" :rules="rules" label-width="80px">
<el-form ref="dictRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="字典名称" prop="dictName">
<el-input v-model="form.dictName" placeholder="请输入字典名称" />
</el-form-item>
<el-form-item label="字典类型" prop="dictType">
<el-form-item prop="dictType">
<el-input v-model="form.dictType" placeholder="请输入字典类型" />
<template #label>
<span>
<el-tooltip content='数据存储中的Key值sys_user_sex' placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
字典类型
</span>
</template>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">

View File

@@ -249,13 +249,13 @@ function submitForm() {
proxy.$refs["postRef"].validate(valid => {
if (valid) {
if (form.value.postId != undefined) {
updatePost(form.value).then(response => {
updatePost(form.value).then(() => {
proxy.$modal.msgSuccess("修改成功")
open.value = false
getList()
})
} else {
addPost(form.value).then(response => {
addPost(form.value).then(() => {
proxy.$modal.msgSuccess("新增成功")
open.value = false
getList()

View File

@@ -164,7 +164,7 @@ function cancelAuthUser(row) {
}
/** 批量取消授权按钮操作 */
function cancelAuthUserAll(row) {
function cancelAuthUserAll() {
const roleId = queryParams.roleId
const uIds = userIds.value.join(",")
proxy.$modal.confirm("是否取消选中用户授权数据项?").then(function () {

View File

@@ -512,14 +512,14 @@ function submitForm() {
if (valid) {
if (form.value.roleId != undefined) {
form.value.menuIds = getMenuAllCheckedKeys()
updateRole(form.value).then(response => {
updateRole(form.value).then(() => {
proxy.$modal.msgSuccess("修改成功")
open.value = false
getList()
})
} else {
form.value.menuIds = getMenuAllCheckedKeys()
addRole(form.value).then(response => {
addRole(form.value).then(() => {
proxy.$modal.msgSuccess("新增成功")
open.value = false
getList()
@@ -566,7 +566,7 @@ function handleDataScope(row) {
function submitDataScope() {
if (form.value.roleId != undefined) {
form.value.deptIds = getDeptAllCheckedKeys()
dataScope(form.value).then(response => {
dataScope(form.value).then(() => {
proxy.$modal.msgSuccess("修改成功")
openDataScope.value = false
getList()

View File

@@ -95,7 +95,7 @@ function close() {
function submitForm() {
const userId = form.value.userId
const rIds = roleIds.value.join(",")
updateAuthRole({ userId: userId, roleIds: rIds }).then(response => {
updateAuthRole({ userId: userId, roleIds: rIds }).then(() => {
proxy.$modal.msgSuccess("授权成功")
close()
})

View File

@@ -199,7 +199,7 @@
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
<el-link type="primary" underline="never" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
</div>
</template>
</el-upload>
@@ -415,7 +415,7 @@ function handleResetPwd(row) {
}
},
}).then(({ value }) => {
resetUserPwd(row.userId, value).then(response => {
resetUserPwd(row.userId, value).then(() => {
proxy.$modal.msgSuccess("修改成功,新密码是:" + value)
})
}).catch(() => {})
@@ -524,7 +524,7 @@ function handleUpdate(row) {
form.value.roleIds = response.roleIds
open.value = true
title.value = "修改用户"
form.password = ""
form.value.password = ""
})
}
@@ -533,13 +533,13 @@ function submitForm() {
proxy.$refs["userRef"].validate(valid => {
if (valid) {
if (form.value.userId != undefined) {
updateUser(form.value).then(response => {
updateUser(form.value).then(() => {
proxy.$modal.msgSuccess("修改成功")
open.value = false
getList()
})
} else {
addUser(form.value).then(response => {
addUser(form.value).then(() => {
proxy.$modal.msgSuccess("新增成功")
open.value = false
getList()

View File

@@ -45,7 +45,7 @@ const rules = ref({
function submit() {
proxy.$refs.pwdRef.validate(valid => {
if (valid) {
updateUserPwd(user.oldPassword, user.newPassword).then(response => {
updateUserPwd(user.oldPassword, user.newPassword).then(() => {
proxy.$modal.msgSuccess("修改成功")
})
}

View File

@@ -44,7 +44,7 @@ const rules = ref({
function submit() {
proxy.$refs.userRef.validate(valid => {
if (valid) {
updateUserProfile(form.value).then(response => {
updateUserProfile(form.value).then(() => {
proxy.$modal.msgSuccess("修改成功")
props.user.phonenumber = form.value.phonenumber
props.user.email = form.value.email

View File

@@ -102,7 +102,7 @@ import beautifier from 'js-beautify'
import logo from '@/assets/logo/logo.png'
import { inputComponents, selectComponents, layoutComponents, formConf as formConfData } from '@/utils/generator/config'
import { beautifierConf } from '@/utils/index'
import drawingDefalut from '@/utils/generator/drawingDefalut'
import { drawingDefaultValue, initDrawingDefaultValue, cleanDrawingDefaultValue } from '@/utils/generator/drawingDefault'
import { makeUpHtml, vueTemplate, vueScript, cssStyle } from '@/utils/generator/html'
import { makeUpJs } from '@/utils/generator/js'
import { makeUpCss } from '@/utils/generator/css'
@@ -113,14 +113,16 @@ import RightPanel from './RightPanel'
import CodeTypeDialog from './CodeTypeDialog'
import { onMounted, watch } from 'vue'
const drawingList = ref(drawingDefalut)
initDrawingDefaultValue()
const drawingList = ref(drawingDefaultValue)
const { proxy } = getCurrentInstance()
const dialogVisible = ref(false)
const showFileName = ref(false)
const operationType = ref('')
const idGlobal = ref(100)
const activeData = ref(drawingDefalut[0])
const activeId = ref(drawingDefalut[0].formId)
const activeData = ref(drawingDefaultValue[0])
const activeId = ref(drawingDefaultValue[0].formId)
const generateConf = ref(null)
const formData = ref({})
const formConf = ref(formConfData)
@@ -145,6 +147,7 @@ function empty() {
proxy.$modal.confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then(() => {
idGlobal.value = 100
drawingList.value = []
cleanDrawingDefaultValue()
}
)
}
@@ -292,8 +295,9 @@ watch(activeId, (val) => {
oldActiveId = val
}, { immediate: true })
let clipboard = null
onMounted(() => {
const clipboard = new ClipboardJS('#copyNode', {
clipboard = new ClipboardJS('#copyNode', {
text: trigger => {
const codeStr = generateCode()
ElNotification({ title: '成功', message: '代码已复制到剪切板,可粘贴。', type: 'success' })
@@ -304,6 +308,9 @@ onMounted(() => {
proxy.$modal.msgError('代码复制失败')
})
})
onUnmounted(() => {
clipboard.destroy()
})
</script>
<style lang='scss'>

View File

@@ -224,7 +224,8 @@ function handleGenTable(row) {
proxy.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath)
})
} else {
proxy.$download.zip("/tool/gen/batchGenCode?tables=" + tbNames, "ruoyi.zip")
const zipName = Array.isArray(tbNames) ? "ruoyi.zip" : tbNames + ".zip"
proxy.$download.zip("/tool/gen/batchGenCode?tables=" + tbNames, zipName)
}
}

View File

@@ -1,12 +1,12 @@
import autoImport from 'unplugin-auto-import/vite'
export default function createAutoImport() {
return autoImport({
imports: [
'vue',
'vue-router',
'pinia'
],
dts: false
})
return autoImport({
imports: [
'vue',
'vue-router',
'pinia'
],
dts: false
})
}

View File

@@ -1,28 +1,28 @@
import compression from 'vite-plugin-compression'
export default function createCompression(env) {
const { VITE_BUILD_COMPRESS } = env
const plugin = []
if (VITE_BUILD_COMPRESS) {
const compressList = VITE_BUILD_COMPRESS.split(',')
if (compressList.includes('gzip')) {
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
plugin.push(
compression({
ext: '.gz',
deleteOriginFile: false
})
)
}
if (compressList.includes('brotli')) {
plugin.push(
compression({
ext: '.br',
algorithm: 'brotliCompress',
deleteOriginFile: false
})
)
}
const { VITE_BUILD_COMPRESS } = env
const plugin = []
if (VITE_BUILD_COMPRESS) {
const compressList = VITE_BUILD_COMPRESS.split(',')
if (compressList.includes('gzip')) {
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
plugin.push(
compression({
ext: '.gz',
deleteOriginFile: false
})
)
}
return plugin
if (compressList.includes('brotli')) {
plugin.push(
compression({
ext: '.br',
algorithm: 'brotliCompress',
deleteOriginFile: false
})
)
}
}
return plugin
}

View File

@@ -6,10 +6,10 @@ import createCompression from './compression'
import createSetupExtend from './setup-extend'
export default function createVitePlugins(viteEnv, isBuild = false) {
const vitePlugins = [vue()]
vitePlugins.push(createAutoImport())
vitePlugins.push(createSetupExtend())
vitePlugins.push(createSvgIcon(isBuild))
isBuild && vitePlugins.push(...createCompression(viteEnv))
return vitePlugins
const vitePlugins = [vue()]
vitePlugins.push(createAutoImport())
vitePlugins.push(createSetupExtend())
vitePlugins.push(createSvgIcon(isBuild))
isBuild && vitePlugins.push(...createCompression(viteEnv))
return vitePlugins
}

View File

@@ -1,5 +1,5 @@
import setupExtend from 'unplugin-vue-setup-extend-plus/vite'
export default function createSetupExtend() {
return setupExtend({})
return setupExtend({})
}

View File

@@ -2,9 +2,9 @@ import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'path'
export default function createSvgIcon(isBuild) {
return createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')],
symbolId: 'icon-[dir]-[name]',
svgoOptions: isBuild
})
return createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')],
symbolId: 'icon-[dir]-[name]',
svgoOptions: isBuild
})
}