zhouyulong 3 weeks ago
parent
commit
fc5edb7578
  1. 4
      .env.development
  2. 4
      .env.production
  3. 4
      .env.staging
  4. 2
      .github/FUNDING.yml
  5. 2
      LICENSE
  6. 24
      README.md
  7. 8
      package.json
  8. 2
      src/api/system/user.js
  9. 2
      src/assets/styles/index.scss
  10. 308
      src/assets/styles/ruoyi.scss
  11. 152
      src/components/FileUpload/index.vue
  12. 119
      src/components/HeaderSearch/index.vue
  13. 160
      src/components/ImageUpload/index.vue
  14. 6
      src/components/RuoYi/Doc/index.vue
  15. 6
      src/components/RuoYi/Git/index.vue
  16. 2
      src/directive/common/copyText.js
  17. 8
      src/directive/permission/hasPermi.js
  18. 8
      src/directive/permission/hasRole.js
  19. 81
      src/layout/components/Navbar.vue
  20. 42
      src/layout/components/Sidebar/SidebarItem.vue
  21. 156
      src/layout/components/TagsView/index.vue
  22. 2
      src/main.js
  23. 2
      src/plugins/download.js
  24. 4
      src/router/index.js
  25. 2
      src/settings.js
  26. 6
      src/utils/index.js
  27. 10
      src/utils/request.js
  28. 228
      src/utils/ruoyi.js
  29. 1158
      src/views/index.vue
  30. 282
      src/views/monitor/job/index.vue
  31. 85
      src/views/tool/gen/editTable.vue
  32. 101
      src/views/tool/gen/genInfoForm.vue
  33. 248
      src/views/tool/gen/index.vue
  34. 7
      vite.config.js
  35. 2
      vite/plugins/compression.js

4
.env.development

@ -1,8 +1,8 @@
# 页面标题 # 页面标题
VITE_APP_TITLE = 若依管理系统 VITE_APP_TITLE = 美美学堂管理系统
# 开发环境配置 # 开发环境配置
VITE_APP_ENV = 'development' VITE_APP_ENV = 'development'
# 若依管理系统/开发环境 # 美美学堂管理系统/开发环境
VITE_APP_BASE_API = '/dev-api' VITE_APP_BASE_API = '/dev-api'

4
.env.production

@ -1,10 +1,10 @@
# 页面标题 # 页面标题
VITE_APP_TITLE = 若依管理系统 VITE_APP_TITLE = 美美学堂管理系统
# 生产环境配置 # 生产环境配置
VITE_APP_ENV = 'production' VITE_APP_ENV = 'production'
# 若依管理系统/生产环境 # 美美学堂管理系统/生产环境
VITE_APP_BASE_API = '/prod-api' VITE_APP_BASE_API = '/prod-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli # 是否在打包时开启压缩,支持 gzip 和 brotli

4
.env.staging

@ -1,10 +1,10 @@
# 页面标题 # 页面标题
VITE_APP_TITLE = 若依管理系统 VITE_APP_TITLE = 美美学堂管理系统
# 生产环境配置 # 生产环境配置
VITE_APP_ENV = 'staging' VITE_APP_ENV = 'staging'
# 若依管理系统/生产环境 # 美美学堂管理系统/生产环境
VITE_APP_BASE_API = '/stage-api' VITE_APP_BASE_API = '/stage-api'
# 是否在打包时开启压缩,支持 gzip 和 brotli # 是否在打包时开启压缩,支持 gzip 和 brotli

2
.github/FUNDING.yml

@ -1 +1 @@
custom: http://doc.ruoyi.vip/ruoyi-vue/other/donate.html custom: http://doc.mmxt.vip/mmxt-vue/other/donate.html

2
LICENSE

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2018 RuoYi Copyright (c) 2018 mmxt
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in

24
README.md

@ -1,29 +1,29 @@
<p align="center"> <p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png"> <img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p> </p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.1</h1> <h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">mmxt v3.9.1</h1>
<h4 align="center">基于SpringBoot+Vue3前后端分离的Java快速开发框架</h4> <h4 align="center">基于SpringBoot+Vue3前后端分离的Java快速开发框架</h4>
<p align="center"> <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/mmxt-Vue/stargazers"><img src="https://gitee.com/y_project/mmxt-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.1-brightgreen.svg"></a> <a href="https://gitee.com/y_project/mmxt-Vue"><img src="https://img.shields.io/badge/mmxt-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> <a href="https://gitee.com/y_project/mmxt-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p> </p>
## 平台简介 ## 平台简介
* 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。 * 本仓库为前端技术栈 [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) 版本。 * 配套后端代码仓库地址[mmxt-Vue](https://gitee.com/y_project/mmxt-Vue) 或 [mmxt-Vue-fast](https://gitcode.com/yangzongzhuan/mmxt-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)。 * 前端技术栈([Vue2](https://cn.vuejs.org) + [Element](https://github.com/ElemeFE/element) + [Vue CLI](https://cli.vuejs.org/zh)),请移步[mmxt-Vue](https://gitee.com/y_project/mmxt-Vue/tree/master/mmxt-ui)。
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)&nbsp;&nbsp; * 阿里云折扣场:[点我进入](http://aly.mmxt.vip),腾讯云秒杀场:[点我进入](http://txy.mmxt.vip)&nbsp;&nbsp;
## 前端运行 ## 前端运行
```bash ```bash
# 克隆项目 # 克隆项目
git clone https://github.com/yangzongzhuan/RuoYi-Vue3.git git clone https://github.com/yangzongzhuan/mmxt-Vue3.git
# 进入项目目录 # 进入项目目录
cd RuoYi-Vue3 cd mmxt-Vue3
# 安装依赖 # 安装依赖
yarn --registry=https://registry.npmmirror.com yarn --registry=https://registry.npmmirror.com
@ -62,8 +62,8 @@ yarn dev
- admin/admin123 - admin/admin123
- 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。 - 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。
演示地址:http://vue.ruoyi.vip 演示地址:http://vue.mmxt.vip
文档地址:http://doc.ruoyi.vip 文档地址:http://doc.mmxt.vip
## 演示图 ## 演示图
@ -103,6 +103,6 @@ yarn dev
</table> </table>
## 若依前后端分离交流群 ## 美美学堂前后端分离交流群
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) 点击按钮入群。

8
package.json

@ -1,8 +1,8 @@
{ {
"name": "ruoyi", "name": "mmxt",
"version": "3.9.1", "version": "3.9.1",
"description": "若依管理系统", "description": "美美学堂管理系统",
"author": "若依", "author": "美美学堂",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
@ -13,7 +13,7 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://gitee.com/y_project/RuoYi-Vue.git" "url": "https://gitee.com/y_project/mmxt-Vue.git"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "2.3.1", "@element-plus/icons-vue": "2.3.1",

2
src/api/system/user.js

@ -1,5 +1,5 @@
import request from '@/utils/request' import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi"; import { parseStrEmpty } from "@/utils/mmxt";
// 查询用户列表 // 查询用户列表
export function listUser(query) { export function listUser(query) {

2
src/assets/styles/index.scss

@ -3,7 +3,7 @@
@use './element-ui.scss'; @use './element-ui.scss';
@use './sidebar.scss'; @use './sidebar.scss';
@use './btn.scss'; @use './btn.scss';
@use './ruoyi.scss'; @use './mmxt.scss';
body { body {
height: 100%; height: 100%;

308
src/assets/styles/ruoyi.scss

@ -1,308 +0,0 @@
/**
* 通用css样式布局处理
* Copyright (c) 2019 ruoyi
*/
/** 基础通用 **/
.pt5 {
padding-top: 5px;
}
.pr5 {
padding-right: 5px;
}
.pb5 {
padding-bottom: 5px;
}
.mt5 {
margin-top: 5px;
}
.mr5 {
margin-right: 5px;
}
.mb5 {
margin-bottom: 5px;
}
.mb8 {
margin-bottom: 8px;
}
.ml5 {
margin-left: 5px;
}
.mt10 {
margin-top: 10px;
}
.mr10 {
margin-right: 10px;
}
.mb10 {
margin-bottom: 10px;
}
.ml10 {
margin-left: 10px;
}
.mt20 {
margin-top: 20px;
}
.mr20 {
margin-right: 20px;
}
.mb20 {
margin-bottom: 20px;
}
.ml20 {
margin-left: 20px;
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
font-family: inherit;
font-weight: 500;
line-height: 1.1;
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;
}
.el-dialog:not(.is-fullscreen) {
margin-top: 6vh !important;
}
.el-dialog.scrollbar .el-dialog__body {
overflow: auto;
overflow-x: hidden;
max-height: 70vh;
padding: 10px 20px 0;
}
.el-table {
.el-table__header-wrapper, .el-table__fixed-header-wrapper {
th {
word-break: break-word;
background-color: #f8f8f9 !important;
color: #515a6e;
height: 40px !important;
font-size: 13px;
}
}
.el-table__body-wrapper {
.el-button [class*="el-icon-"] + span {
margin-left: 1px;
}
}
}
/** 表单布局 **/
.form-header {
font-size:15px;
color:#6379bb;
border-bottom:1px solid #ddd;
margin:8px 10px 25px 10px;
padding-bottom:5px
}
/** 表格布局 **/
.pagination-container {
display: flex;
justify-content: flex-end;
margin-top: 20px;
background-color: transparent !important;
}
/* 弹窗中的分页器 */
.el-dialog .pagination-container {
position: static !important;
margin: 10px 0 0 0;
padding: 0 !important;
.el-pagination {
position: static;
}
}
/* 移动端适配 */
@media (max-width: 768px) {
.pagination-container {
.el-pagination {
> .el-pagination__jump {
display: none !important;
}
> .el-pagination__sizes {
display: none !important;
}
}
}
}
/* tree border */
.tree-border {
margin-top: 5px;
border: 1px solid var(--el-border-color-light, #e5e6e7);
background: var(--el-bg-color, #FFFFFF) none;
border-radius:4px;
width: 100%;
}
.el-table .fixed-width .el-button--small {
padding-left: 0;
padding-right: 0;
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;
color: #409EFF;
margin-left: 10px;
}
.el-table .el-dropdown, .el-icon-arrow-down {
font-size: 12px;
}
.el-tree-node__content > .el-checkbox {
margin-right: 8px;
}
.list-group-striped > .list-group-item {
border-left: 0;
border-right: 0;
border-radius: 0;
padding-left: 0;
padding-right: 0;
}
.list-group {
padding-left: 0px;
list-style: none;
}
.list-group-item {
border-bottom: 1px solid #e7eaec;
border-top: 1px solid #e7eaec;
margin-bottom: -1px;
padding: 11px 0px;
font-size: 13px;
}
.pull-right {
float: right !important;
}
.el-card__header {
padding: 14px 15px 7px !important;
min-height: 40px;
}
.el-card__body {
padding: 15px 20px 20px 20px !important;
}
.card-box {
margin-bottom: 10px;
}
/* button color */
.el-button--cyan.is-active,
.el-button--cyan:active {
background: #20B2AA;
border-color: #20B2AA;
color: #FFFFFF;
}
.el-button--cyan:focus,
.el-button--cyan:hover {
background: #48D1CC;
border-color: #48D1CC;
color: #FFFFFF;
}
.el-button--cyan {
background-color: #20B2AA;
border-color: #20B2AA;
color: #FFFFFF;
}
/* text color */
.text-navy {
color: #1ab394;
}
.text-primary {
color: inherit;
}
.text-success {
color: #1c84c6;
}
.text-info {
color: #23c6c8;
}
.text-warning {
color: #f8ac59;
}
.text-danger {
color: #ed5565;
}
.text-muted {
color: #888888;
}
/* image */
.img-circle {
border-radius: 50%;
}
.img-lg {
width: 120px;
height: 120px;
}
.avatar-upload-preview {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
width: 200px;
height: 200px;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
}
/* 拖拽列样式 */
.sortable-ghost{
opacity: .8;
color: #fff!important;
background: #42b983!important;
}
/* 表格右侧工具栏样式 */
.top-right-btn {
margin-left: auto;
}
/* 分割面板样式 */
.splitpanes.default-theme .splitpanes__pane {
background-color: var(--splitpanes-default-bg) !important;
}

152
src/components/FileUpload/index.vue

@ -1,21 +1,8 @@
<template> <template>
<div class="upload-file"> <div class="upload-file">
<el-upload <el-upload multiple :action="uploadFileUrl" :before-upload="handleBeforeUpload" :file-list="fileList" :data="data"
multiple :limit="limit" :on-error="handleUploadError" :on-exceed="handleExceed" :on-success="handleUploadSuccess"
:action="uploadFileUrl" :show-file-list="false" :headers="headers" class="upload-file-uploader" ref="fileUpload" v-if="!disabled">
:before-upload="handleBeforeUpload"
:file-list="fileList"
:data="data"
:limit="limit"
:on-error="handleUploadError"
:on-exceed="handleExceed"
:on-success="handleUploadSuccess"
:show-file-list="false"
:headers="headers"
class="upload-file-uploader"
ref="fileUpload"
v-if="!disabled"
>
<!-- 上传按钮 --> <!-- 上传按钮 -->
<el-button type="primary">选取文件</el-button> <el-button type="primary">选取文件</el-button>
</el-upload> </el-upload>
@ -27,7 +14,8 @@
的文件 的文件
</div> </div>
<!-- 文件列表 --> <!-- 文件列表 -->
<transition-group ref="uploadFileList" class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul"> <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"> <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="false" target="_blank">
<span class="el-icon-document"> {{ getFileName(file.name) }} </span> <span class="el-icon-document"> {{ getFileName(file.name) }} </span>
@ -41,10 +29,10 @@
</template> </template>
<script setup> <script setup>
import { getToken } from "@/utils/auth" import { getToken } from "@/utils/auth"
import Sortable from 'sortablejs' // import Sortable from 'sortablejs'
const props = defineProps({ const props = defineProps({
modelValue: [String, Object, Array], modelValue: [String, Object, Array],
// //
action: { action: {
@ -85,21 +73,21 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: true default: true
} }
}) })
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const emit = defineEmits() const emit = defineEmits()
const number = ref(0) const number = ref(0)
const uploadList = ref([]) const uploadList = ref([])
const baseUrl = import.meta.env.VITE_APP_BASE_API const baseUrl = import.meta.env.VITE_APP_BASE_API
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + props.action) // const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + props.action) //
const headers = ref({ Authorization: "Bearer " + getToken() }) const headers = ref({ Authorization: "Bearer " + getToken() })
const fileList = ref([]) const fileList = ref([])
const showTip = computed( const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize) () => props.isShowTip && (props.fileType || props.fileSize)
) )
watch(() => props.modelValue, val => { watch(() => props.modelValue, val => {
if (val) { if (val) {
let temp = 1 let temp = 1
// //
@ -116,10 +104,10 @@ watch(() => props.modelValue, val => {
fileList.value = [] fileList.value = []
return [] return []
} }
},{ deep: true, immediate: true }) }, { deep: true, immediate: true })
// //
function handleBeforeUpload(file) { function handleBeforeUpload(file) {
// //
if (props.fileType.length) { if (props.fileType.length) {
const fileName = file.name.split('.') const fileName = file.name.split('.')
@ -146,21 +134,21 @@ function handleBeforeUpload(file) {
proxy.$modal.loading("正在上传文件,请稍候...") proxy.$modal.loading("正在上传文件,请稍候...")
number.value++ number.value++
return true return true
} }
// //
function handleExceed() { function handleExceed() {
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`) proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`)
} }
// //
function handleUploadError(err) { function handleUploadError(err) {
proxy.$modal.msgError("上传文件失败") proxy.$modal.msgError("上传文件失败")
proxy.$modal.closeLoading() proxy.$modal.closeLoading()
} }
// //
function handleUploadSuccess(res, file) { function handleUploadSuccess(res, file) {
if (res.code === 200) { if (res.code === 200) {
uploadList.value.push({ name: res.fileName, url: res.fileName }) uploadList.value.push({ name: res.fileName, url: res.fileName })
uploadedSuccessfully() uploadedSuccessfully()
@ -171,16 +159,16 @@ function handleUploadSuccess(res, file) {
proxy.$refs.fileUpload.handleRemove(file) proxy.$refs.fileUpload.handleRemove(file)
uploadedSuccessfully() uploadedSuccessfully()
} }
} }
// //
function handleDelete(index) { function handleDelete(index) {
fileList.value.splice(index, 1) fileList.value.splice(index, 1)
emit("update:modelValue", listToString(fileList.value)) emit("update:modelValue", listToString(fileList.value))
} }
// //
function uploadedSuccessfully() { function uploadedSuccessfully() {
if (number.value > 0 && uploadList.value.length === number.value) { if (number.value > 0 && uploadList.value.length === number.value) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value) fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value)
uploadList.value = [] uploadList.value = []
@ -188,20 +176,20 @@ function uploadedSuccessfully() {
emit("update:modelValue", listToString(fileList.value)) emit("update:modelValue", listToString(fileList.value))
proxy.$modal.closeLoading() proxy.$modal.closeLoading()
} }
} }
// //
function getFileName(name) { function getFileName(name) {
// url // url
if (name.lastIndexOf("/") > -1) { if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1) return name.slice(name.lastIndexOf("/") + 1)
} else { } else {
return name return name
} }
} }
// //
function listToString(list, separator) { function listToString(list, separator) {
let strs = "" let strs = ""
separator = separator || "," separator = separator || ","
for (let i in list) { for (let i in list) {
@ -210,47 +198,51 @@ function listToString(list, separator) {
} }
} }
return strs != '' ? strs.substr(0, strs.length - 1) : '' return strs != '' ? strs.substr(0, strs.length - 1) : ''
} }
// //
onMounted(() => { onMounted(() => {
if (props.drag && !props.disabled) { if (props.drag && !props.disabled) {
nextTick(() => { // nextTick(() => {
const element = proxy.$refs.uploadFileList?.$el || proxy.$refs.uploadFileList // const element = proxy.$refs.uploadFileList?.$el || proxy.$refs.uploadFileList
Sortable.create(element, { // Sortable.create(element, {
ghostClass: 'file-upload-darg', // ghostClass: 'file-upload-darg',
onEnd: (evt) => { // onEnd: (evt) => {
const movedItem = fileList.value.splice(evt.oldIndex, 1)[0] // const movedItem = fileList.value.splice(evt.oldIndex, 1)[0]
fileList.value.splice(evt.newIndex, 0, movedItem) // fileList.value.splice(evt.newIndex, 0, movedItem)
emit('update:modelValue', listToString(fileList.value)) // emit('update:modelValue', listToString(fileList.value))
// }
// })
// })
} }
}) })
})
}
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.file-upload-darg { .file-upload-darg {
opacity: 0.5; opacity: 0.5;
background: #c8ebfb; background: #c8ebfb;
} }
.upload-file-uploader {
.upload-file-uploader {
margin-bottom: 5px; margin-bottom: 5px;
} }
.upload-file-list .el-upload-list__item {
.upload-file-list .el-upload-list__item {
border: 1px solid #e4e7ed; border: 1px solid #e4e7ed;
line-height: 2; line-height: 2;
margin-bottom: 10px; margin-bottom: 10px;
position: relative; position: relative;
transition: none !important; transition: none !important;
} }
.upload-file-list .ele-upload-list__item-content {
.upload-file-list .ele-upload-list__item-content {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
color: inherit; color: inherit;
} }
.ele-upload-list__item-content-action .el-link {
.ele-upload-list__item-content-action .el-link {
margin-right: 10px; margin-right: 10px;
} }
</style> </style>

119
src/components/HeaderSearch/index.vue

@ -1,30 +1,16 @@
<template> <template>
<div class="header-search"> <div class="header-search">
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" /> <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
<el-dialog <el-dialog v-model="show" width="600" @close="close" :show-close="false" append-to-body>
v-model="show" <el-input v-model="search" ref="headerSearchSelectRef" size="large" @input="querySearch" prefix-icon="Search"
width="600" placeholder="菜单搜索,支持标题、URL模糊查询" clearable @keyup.enter="selectActiveResult"
@close="close" @keydown.up.prevent="navigateResult('up')" @keydown.down.prevent="navigateResult('down')">
:show-close="false"
append-to-body
>
<el-input
v-model="search"
ref="headerSearchSelectRef"
size="large"
@input="querySearch"
prefix-icon="Search"
placeholder="菜单搜索,支持标题、URL模糊查询"
clearable
@keyup.enter="selectActiveResult"
@keydown.up.prevent="navigateResult('up')"
@keydown.down.prevent="navigateResult('down')"
>
</el-input> </el-input>
<div class="result-wrap"> <div class="result-wrap">
<el-scrollbar> <el-scrollbar>
<div class="search-item" tabindex="1" v-for="(item, index) in options" :key="item.path" :style="activeStyle(index)" @mouseenter="activeIndex = index" @mouseleave="activeIndex = -1"> <div class="search-item" tabindex="1" v-for="(item, index) in options" :key="item.path"
:style="activeStyle(index)" @mouseenter="activeIndex = index" @mouseleave="activeIndex = -1">
<div class="left"> <div class="left">
<svg-icon class="menu-icon" :icon-class="item.icon" /> <svg-icon class="menu-icon" :icon-class="item.icon" />
</div> </div>
@ -36,7 +22,7 @@
{{ item.path }} {{ item.path }}
</div> </div>
</div> </div>
<svg-icon icon-class="enter" v-show="index === activeIndex"/> <svg-icon icon-class="enter" v-show="index === activeIndex" />
</div> </div>
</el-scrollbar> </el-scrollbar>
</div> </div>
@ -45,40 +31,40 @@
</template> </template>
<script setup> <script setup>
import Fuse from 'fuse.js' import Fuse from 'fuse.js'
import { getNormalPath } from '@/utils/ruoyi' import { getNormalPath } from '@/utils/mmxt'
import { isHttp } from '@/utils/validate' import { isHttp } from '@/utils/validate'
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission' import usePermissionStore from '@/store/modules/permission'
const search = ref('') const search = ref('')
const options = ref([]) const options = ref([])
const searchPool = ref([]) const searchPool = ref([])
const activeIndex = ref(-1) const activeIndex = ref(-1)
const show = ref(false) const show = ref(false)
const fuse = ref(undefined) const fuse = ref(undefined)
const headerSearchSelectRef = ref(null) const headerSearchSelectRef = ref(null)
const router = useRouter() const router = useRouter()
const theme = computed(() => useSettingsStore().theme) const theme = computed(() => useSettingsStore().theme)
const routes = computed(() => usePermissionStore().defaultRoutes) const routes = computed(() => usePermissionStore().defaultRoutes)
function click() { function click() {
show.value = !show.value show.value = !show.value
if (show.value) { if (show.value) {
headerSearchSelectRef.value && headerSearchSelectRef.value.focus() headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
options.value = searchPool.value options.value = searchPool.value
} }
} }
function close() { function close() {
headerSearchSelectRef.value && headerSearchSelectRef.value.blur() headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
search.value = '' search.value = ''
options.value = [] options.value = []
show.value = false show.value = false
activeIndex.value = -1 activeIndex.value = -1
} }
function change(val) { function change(val) {
const path = val.path const path = val.path
const query = val.query const query = val.query
if (isHttp(path)) { if (isHttp(path)) {
@ -98,9 +84,9 @@ function change(val) {
nextTick(() => { nextTick(() => {
show.value = false show.value = false
}) })
} }
function initFuse(list) { function initFuse(list) {
fuse.value = new Fuse(list, { fuse.value = new Fuse(list, {
shouldSort: true, shouldSort: true,
threshold: 0.4, threshold: 0.4,
@ -115,11 +101,11 @@ function initFuse(list) {
weight: 0.3 weight: 0.3
}] }]
}) })
} }
// Filter out the routes that can be displayed in the sidebar // Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title // And generate the internationalized title
function generateRoutes(routes, basePath = '', prefixTitle = []) { function generateRoutes(routes, basePath = '', prefixTitle = []) {
let res = [] let res = []
for (const r of routes) { for (const r of routes) {
@ -154,58 +140,58 @@ function generateRoutes(routes, basePath = '', prefixTitle = []) {
} }
} }
return res return res
} }
function querySearch(query) { function querySearch(query) {
activeIndex.value = -1 activeIndex.value = -1
if (query !== '') { if (query !== '') {
options.value = fuse.value.search(query).map((item) => item.item) ?? searchPool.value options.value = fuse.value.search(query).map((item) => item.item) ?? searchPool.value
} else { } else {
options.value = searchPool.value options.value = searchPool.value
} }
} }
function activeStyle(index) { function activeStyle(index) {
if (index !== activeIndex.value) return {} if (index !== activeIndex.value) return {}
return { return {
"background-color": theme.value, "background-color": theme.value,
"color": "#fff" "color": "#fff"
} }
} }
function navigateResult(direction) { function navigateResult(direction) {
if (direction === "up") { if (direction === "up") {
activeIndex.value = activeIndex.value <= 0 ? options.value.length - 1 : activeIndex.value - 1 activeIndex.value = activeIndex.value <= 0 ? options.value.length - 1 : activeIndex.value - 1
} else if (direction === "down") { } else if (direction === "down") {
activeIndex.value = activeIndex.value >= options.value.length - 1 ? 0 : activeIndex.value + 1 activeIndex.value = activeIndex.value >= options.value.length - 1 ? 0 : activeIndex.value + 1
} }
} }
function selectActiveResult() { function selectActiveResult() {
if (options.value.length > 0 && activeIndex.value >= 0) { if (options.value.length > 0 && activeIndex.value >= 0) {
change(options.value[activeIndex.value]) change(options.value[activeIndex.value])
} }
} }
onMounted(() => { onMounted(() => {
searchPool.value = generateRoutes(routes.value) searchPool.value = generateRoutes(routes.value)
}) })
watch(searchPool, (list) => { watch(searchPool, (list) => {
initFuse(list) initFuse(list)
}) })
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
.header-search { .header-search {
.search-icon { .search-icon {
cursor: pointer; cursor: pointer;
font-size: 18px; font-size: 18px;
vertical-align: middle; vertical-align: middle;
} }
} }
.result-wrap { .result-wrap {
height: 280px; height: 280px;
margin: 6px 0; margin: 6px 0;
@ -238,6 +224,7 @@ watch(searchPool, (list) => {
.menu-path { .menu-path {
height: 20px; height: 20px;
} }
.menu-path { .menu-path {
color: #ccc; color: #ccc;
font-size: 10px; font-size: 10px;
@ -248,5 +235,5 @@ watch(searchPool, (list) => {
.search-item:hover { .search-item:hover {
cursor: pointer; cursor: pointer;
} }
} }
</style> </style>

160
src/components/ImageUpload/index.vue

@ -1,25 +1,13 @@
<template> <template>
<div class="component-upload-image"> <div class="component-upload-image">
<el-upload <el-upload multiple :disabled="disabled" :action="uploadImgUrl" list-type="picture-card"
multiple :on-success="handleUploadSuccess" :before-upload="handleBeforeUpload" :data="data" :limit="limit"
:disabled="disabled" :on-error="handleUploadError" :on-exceed="handleExceed" ref="imageUpload" :before-remove="handleDelete"
:action="uploadImgUrl" :show-file-list="true" :headers="headers" :file-list="fileList" :on-preview="handlePictureCardPreview"
list-type="picture-card" :class="{ hide: fileList.length >= limit }">
:on-success="handleUploadSuccess" <el-icon class="avatar-uploader-icon">
:before-upload="handleBeforeUpload" <plus />
:data="data" </el-icon>
:limit="limit"
:on-error="handleUploadError"
:on-exceed="handleExceed"
ref="imageUpload"
:before-remove="handleDelete"
:show-file-list="true"
:headers="headers"
:file-list="fileList"
:on-preview="handlePictureCardPreview"
:class="{ hide: fileList.length >= limit }"
>
<el-icon class="avatar-uploader-icon"><plus /></el-icon>
</el-upload> </el-upload>
<!-- 上传提示 --> <!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip && !disabled"> <div class="el-upload__tip" v-if="showTip && !disabled">
@ -33,26 +21,18 @@
的文件 的文件
</div> </div>
<el-dialog <el-dialog v-model="dialogVisible" title="预览" width="800px" append-to-body>
v-model="dialogVisible" <img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
title="预览"
width="800px"
append-to-body
>
<img
:src="dialogImageUrl"
style="display: block; max-width: 100%; margin: 0 auto"
/>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { getToken } from "@/utils/auth" import { getToken } from "@/utils/auth"
import { isExternal } from "@/utils/validate" import { isExternal } from "@/utils/validate"
import Sortable from 'sortablejs' // import Sortable from 'sortablejs'
const props = defineProps({ const props = defineProps({
modelValue: [String, Object, Array], modelValue: [String, Object, Array],
// //
action: { action: {
@ -93,23 +73,23 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: true default: true
} }
}) })
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const emit = defineEmits() const emit = defineEmits()
const number = ref(0) const number = ref(0)
const uploadList = ref([]) const uploadList = ref([])
const dialogImageUrl = ref("") const dialogImageUrl = ref("")
const dialogVisible = ref(false) const dialogVisible = ref(false)
const baseUrl = import.meta.env.VITE_APP_BASE_API const baseUrl = import.meta.env.VITE_APP_BASE_API
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + props.action) // const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + props.action) //
const headers = ref({ Authorization: "Bearer " + getToken() }) const headers = ref({ Authorization: "Bearer " + getToken() })
const fileList = ref([]) const fileList = ref([])
const showTip = computed( const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize) () => props.isShowTip && (props.fileType || props.fileSize)
) )
watch(() => props.modelValue, val => { watch(() => props.modelValue, val => {
if (val) { if (val) {
// //
const list = Array.isArray(val) ? val : props.modelValue.split(",") const list = Array.isArray(val) ? val : props.modelValue.split(",")
@ -128,10 +108,10 @@ watch(() => props.modelValue, val => {
fileList.value = [] fileList.value = []
return [] return []
} }
},{ deep: true, immediate: true }) }, { deep: true, immediate: true })
// loading // loading
function handleBeforeUpload(file) { function handleBeforeUpload(file) {
let isImg = false let isImg = false
if (props.fileType.length) { if (props.fileType.length) {
let fileExtension = "" let fileExtension = ""
@ -163,15 +143,15 @@ function handleBeforeUpload(file) {
} }
proxy.$modal.loading("正在上传图片,请稍候...") proxy.$modal.loading("正在上传图片,请稍候...")
number.value++ number.value++
} }
// //
function handleExceed() { function handleExceed() {
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`) proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`)
} }
// //
function handleUploadSuccess(res, file) { function handleUploadSuccess(res, file) {
if (res.code === 200) { if (res.code === 200) {
uploadList.value.push({ name: res.fileName, url: res.fileName }) uploadList.value.push({ name: res.fileName, url: res.fileName })
uploadedSuccessfully() uploadedSuccessfully()
@ -182,20 +162,20 @@ function handleUploadSuccess(res, file) {
proxy.$refs.imageUpload.handleRemove(file) proxy.$refs.imageUpload.handleRemove(file)
uploadedSuccessfully() uploadedSuccessfully()
} }
} }
// //
function handleDelete(file) { function handleDelete(file) {
const findex = fileList.value.map(f => f.name).indexOf(file.name) const findex = fileList.value.map(f => f.name).indexOf(file.name)
if (findex > -1 && uploadList.value.length === number.value) { if (findex > -1 && uploadList.value.length === number.value) {
fileList.value.splice(findex, 1) fileList.value.splice(findex, 1)
emit("update:modelValue", listToString(fileList.value)) emit("update:modelValue", listToString(fileList.value))
return false return false
} }
} }
// //
function uploadedSuccessfully() { function uploadedSuccessfully() {
if (number.value > 0 && uploadList.value.length === number.value) { if (number.value > 0 && uploadList.value.length === number.value) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value) fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value)
uploadList.value = [] uploadList.value = []
@ -203,22 +183,22 @@ function uploadedSuccessfully() {
emit("update:modelValue", listToString(fileList.value)) emit("update:modelValue", listToString(fileList.value))
proxy.$modal.closeLoading() proxy.$modal.closeLoading()
} }
} }
// //
function handleUploadError() { function handleUploadError() {
proxy.$modal.msgError("上传图片失败") proxy.$modal.msgError("上传图片失败")
proxy.$modal.closeLoading() proxy.$modal.closeLoading()
} }
// //
function handlePictureCardPreview(file) { function handlePictureCardPreview(file) {
dialogImageUrl.value = file.url dialogImageUrl.value = file.url
dialogVisible.value = true dialogVisible.value = true
} }
// //
function listToString(list, separator) { function listToString(list, separator) {
let strs = "" let strs = ""
separator = separator || "," separator = separator || ","
for (let i in list) { for (let i in list) {
@ -227,32 +207,32 @@ function listToString(list, separator) {
} }
} }
return strs != "" ? strs.substr(0, strs.length - 1) : "" return strs != "" ? strs.substr(0, strs.length - 1) : ""
} }
// //
onMounted(() => { onMounted(() => {
if (props.drag && !props.disabled) { if (props.drag && !props.disabled) {
nextTick(() => { // nextTick(() => {
const element = proxy.$refs.imageUpload?.$el?.querySelector('.el-upload-list') // const element = proxy.$refs.imageUpload?.$el?.querySelector('.el-upload-list')
Sortable.create(element, { // Sortable.create(element, {
onEnd: (evt) => { // onEnd: (evt) => {
const movedItem = fileList.value.splice(evt.oldIndex, 1)[0] // const movedItem = fileList.value.splice(evt.oldIndex, 1)[0]
fileList.value.splice(evt.newIndex, 0, movedItem) // fileList.value.splice(evt.newIndex, 0, movedItem)
emit('update:modelValue', listToString(fileList.value)) // emit('update:modelValue', listToString(fileList.value))
// }
// })
// })
} }
}) })
})
}
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
// .el-upload--picture-card // .el-upload--picture-card
:deep(.hide .el-upload--picture-card) { :deep(.hide .el-upload--picture-card) {
display: none; display: none;
} }
:deep(.el-upload.el-upload--picture-card.is-disabled) { :deep(.el-upload.el-upload--picture-card.is-disabled) {
display: none !important; display: none !important;
} }
</style> </style>

6
src/components/RuoYi/Doc/index.vue

@ -5,9 +5,9 @@
</template> </template>
<script setup> <script setup>
const url = ref('http://doc.ruoyi.vip/ruoyi-vue') const url = ref('http://doc.mmxt.vip/mmxt-vue')
function goto() { function goto() {
window.open(url.value) window.open(url.value)
} }
</script> </script>

6
src/components/RuoYi/Git/index.vue

@ -5,9 +5,9 @@
</template> </template>
<script setup> <script setup>
const url = ref('https://gitee.com/y_project/RuoYi-Vue') const url = ref('https://gitee.com/y_project/mmxt-Vue')
function goto() { function goto() {
window.open(url.value) window.open(url.value)
} }
</script> </script>

2
src/directive/common/copyText.js

@ -1,6 +1,6 @@
/** /**
* v-copyText 复制文本内容 * v-copyText 复制文本内容
* Copyright (c) 2022 ruoyi * Copyright (c) 2022 mmxt
*/ */
export default { export default {
beforeMount(el, { value, arg }) { beforeMount(el, { value, arg }) {

8
src/directive/permission/hasPermi.js

@ -1,7 +1,7 @@
/** /**
* v-hasPermi 操作权限处理 * v-hasPermi 操作权限处理
* Copyright (c) 2019 ruoyi * Copyright (c) 2019 mmxt
*/ */
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
export default { export default {

8
src/directive/permission/hasRole.js

@ -1,7 +1,7 @@
/** /**
* v-hasRole 角色权限处理 * v-hasRole 角色权限处理
* Copyright (c) 2019 ruoyi * Copyright (c) 2019 mmxt
*/ */
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
export default { export default {

81
src/layout/components/Navbar.vue

@ -1,6 +1,7 @@
<template> <template>
<div class="navbar" :class="'nav' + settingsStore.navType"> <div class="navbar" :class="'nav' + settingsStore.navType">
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container"
@toggleClick="toggleSideBar" />
<breadcrumb v-if="settingsStore.navType == 1" id="breadcrumb-container" class="breadcrumb-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" /> <top-nav v-if="settingsStore.navType == 2" id="topmenu-container" class="topmenu-container" />
<template v-if="settingsStore.navType == 3"> <template v-if="settingsStore.navType == 3">
@ -12,14 +13,6 @@
<template v-if="appStore.device !== 'mobile'"> <template v-if="appStore.device !== 'mobile'">
<header-search id="header-search" class="right-menu-item" /> <header-search id="header-search" class="right-menu-item" />
<el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip content="文档地址" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip>
<screenfull id="screenfull" class="right-menu-item hover-effect" /> <screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="主题模式" effect="dark" placement="bottom"> <el-tooltip content="主题模式" effect="dark" placement="bottom">
@ -58,30 +51,30 @@
</template> </template>
<script setup> <script setup>
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import Breadcrumb from '@/components/Breadcrumb' import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav' import TopNav from '@/components/TopNav'
import TopBar from './TopBar' import TopBar from './TopBar'
import Logo from './Sidebar/Logo' import Logo from './Sidebar/Logo'
import Hamburger from '@/components/Hamburger' import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull' import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect' import SizeSelect from '@/components/SizeSelect'
import HeaderSearch from '@/components/HeaderSearch' import HeaderSearch from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git' import mmxtGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc' import mmxtDoc from '@/components/RuoYi/Doc'
import useAppStore from '@/store/modules/app' import useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings'
const appStore = useAppStore() const appStore = useAppStore()
const userStore = useUserStore() const userStore = useUserStore()
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore()
function toggleSideBar() { function toggleSideBar() {
appStore.toggleSideBar() appStore.toggleSideBar()
} }
function handleCommand(command) { function handleCommand(command) {
switch (command) { switch (command) {
case "setLayout": case "setLayout":
setLayout() setLayout()
@ -92,9 +85,9 @@ function handleCommand(command) {
default: default:
break break
} }
} }
function logout() { function logout() {
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', { ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
@ -104,14 +97,14 @@ function logout() {
location.href = '/index' location.href = '/index'
}) })
}).catch(() => { }) }).catch(() => { })
} }
const emits = defineEmits(['setLayout']) const emits = defineEmits(['setLayout'])
function setLayout() { function setLayout() {
emits('setLayout') emits('setLayout')
} }
async function toggleTheme(event) { async function toggleTheme(event) {
const x = event?.clientX || window.innerWidth / 2 const x = event?.clientX || window.innerWidth / 2
const y = event?.clientY || window.innerHeight / 2 const y = event?.clientY || window.innerHeight / 2
const wasDark = settingsStore.isDark const wasDark = settingsStore.isDark
@ -149,17 +142,17 @@ async function toggleTheme(event) {
console.warn("View transition failed, falling back to immediate toggle:", error) console.warn("View transition failed, falling back to immediate toggle:", error)
settingsStore.toggleTheme() settingsStore.toggleTheme()
} }
} }
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
.navbar.nav3 { .navbar.nav3 {
.hamburger-container { .hamburger-container {
display: none !important; display: none !important;
} }
} }
.navbar { .navbar {
height: 50px; height: 50px;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
@ -268,7 +261,7 @@ async function toggleTheme(event) {
border-radius: 50%; border-radius: 50%;
} }
.user-nickname{ .user-nickname {
position: relative; position: relative;
left: 0px; left: 0px;
bottom: 10px; bottom: 10px;
@ -286,5 +279,5 @@ async function toggleTheme(event) {
} }
} }
} }
} }
</style> </style>

42
src/layout/components/Sidebar/SidebarItem.vue

@ -1,10 +1,12 @@
<template> <template>
<div v-if="!item.hidden"> <div v-if="!item.hidden">
<template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow"> <template
v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)"> <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }"> <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
<svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/> <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
<template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span></template> <template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{
onlyOneChild.meta.title }}</span></template>
</el-menu-item> </el-menu-item>
</app-link> </app-link>
</template> </template>
@ -15,24 +17,18 @@
<span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span> <span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>
</template> </template>
<sidebar-item <sidebar-item v-for="(child, index) in item.children" :key="child.path + index" :is-nest="true" :item="child"
v-for="(child, index) in item.children" :base-path="resolvePath(child.path)" class="nest-menu" />
:key="child.path + index"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
class="nest-menu"
/>
</el-sub-menu> </el-sub-menu>
</div> </div>
</template> </template>
<script setup> <script setup>
import { isExternal } from '@/utils/validate' import { isExternal } from '@/utils/validate'
import AppLink from './Link' import AppLink from './Link'
import { getNormalPath } from '@/utils/ruoyi' import { getNormalPath } from '@/utils/mmxt'
const props = defineProps({ const props = defineProps({
// route object // route object
item: { item: {
type: Object, type: Object,
@ -46,11 +42,11 @@ const props = defineProps({
type: String, type: String,
default: '' default: ''
} }
}) })
const onlyOneChild = ref({}) const onlyOneChild = ref({})
function hasOneShowingChild(children = [], parent) { function hasOneShowingChild(children = [], parent) {
if (!children) { if (!children) {
children = [] children = []
} }
@ -74,9 +70,9 @@ function hasOneShowingChild(children = [], parent) {
} }
return false return false
} }
function resolvePath(routePath, routeQuery) { function resolvePath(routePath, routeQuery) {
if (isExternal(routePath)) { if (isExternal(routePath)) {
return routePath return routePath
} }
@ -88,13 +84,13 @@ function resolvePath(routePath, routeQuery) {
return { path: getNormalPath(props.basePath + '/' + routePath), query: query } return { path: getNormalPath(props.basePath + '/' + routePath), query: query }
} }
return getNormalPath(props.basePath + '/' + routePath) return getNormalPath(props.basePath + '/' + routePath)
} }
function hasTitle(title){ function hasTitle(title) {
if (title.length > 5) { if (title.length > 5) {
return title return title
} else { } else {
return "" return ""
} }
} }
</script> </script>

156
src/layout/components/TagsView/index.vue

@ -1,17 +1,11 @@
<template> <template>
<div id="tags-view-container" class="tags-view-container"> <div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPaneRef" class="tags-view-wrapper" @scroll="handleScroll"> <scroll-pane ref="scrollPaneRef" class="tags-view-wrapper" @scroll="handleScroll">
<router-link <router-link v-for="tag in visitedViews" :key="tag.path" :data-path="tag.path"
v-for="tag in visitedViews"
:key="tag.path"
:data-path="tag.path"
:class="{ 'active': isActive(tag), 'has-icon': tagsIcon }" :class="{ 'active': isActive(tag), 'has-icon': tagsIcon }"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" class="tags-view-item"
class="tags-view-item" :style="activeStyle(tag)" @click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
:style="activeStyle(tag)" @contextmenu.prevent="openMenu(tag, $event)">
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
@contextmenu.prevent="openMenu(tag, $event)"
>
<svg-icon v-if="tagsIcon && tag.meta && tag.meta.icon && tag.meta.icon !== '#'" :icon-class="tag.meta.icon" /> <svg-icon v-if="tagsIcon && tag.meta && tag.meta.icon && tag.meta.icon !== '#'" :icon-class="tag.meta.icon" />
{{ tag.title }} {{ tag.title }}
<span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)"> <span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
@ -43,79 +37,79 @@
</template> </template>
<script setup> <script setup>
import ScrollPane from './ScrollPane' import ScrollPane from './ScrollPane'
import { getNormalPath } from '@/utils/ruoyi' import { getNormalPath } from '@/utils/mmxt'
import useTagsViewStore from '@/store/modules/tagsView' import useTagsViewStore from '@/store/modules/tagsView'
import useSettingsStore from '@/store/modules/settings' import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission' import usePermissionStore from '@/store/modules/permission'
const visible = ref(false) const visible = ref(false)
const top = ref(0) const top = ref(0)
const left = ref(0) const left = ref(0)
const selectedTag = ref({}) const selectedTag = ref({})
const affixTags = ref([]) const affixTags = ref([])
const scrollPaneRef = ref(null) const scrollPaneRef = ref(null)
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const visitedViews = computed(() => useTagsViewStore().visitedViews) const visitedViews = computed(() => useTagsViewStore().visitedViews)
const routes = computed(() => usePermissionStore().routes) const routes = computed(() => usePermissionStore().routes)
const theme = computed(() => useSettingsStore().theme) const theme = computed(() => useSettingsStore().theme)
const tagsIcon = computed(() => useSettingsStore().tagsIcon) const tagsIcon = computed(() => useSettingsStore().tagsIcon)
watch(route, () => { watch(route, () => {
addTags() addTags()
moveToCurrentTag() moveToCurrentTag()
}) })
watch(visible, (value) => { watch(visible, (value) => {
if (value) { if (value) {
document.body.addEventListener('click', closeMenu) document.body.addEventListener('click', closeMenu)
} else { } else {
document.body.removeEventListener('click', closeMenu) document.body.removeEventListener('click', closeMenu)
} }
}) })
onMounted(() => { onMounted(() => {
initTags() initTags()
addTags() addTags()
}) })
function isActive(r) { function isActive(r) {
return r.path === route.path return r.path === route.path
} }
function activeStyle(tag) { function activeStyle(tag) {
if (!isActive(tag)) return {} if (!isActive(tag)) return {}
return { return {
"background-color": theme.value, "background-color": theme.value,
"border-color": theme.value "border-color": theme.value
} }
} }
function isAffix(tag) { function isAffix(tag) {
return tag.meta && tag.meta.affix return tag.meta && tag.meta.affix
} }
function isFirstView() { function isFirstView() {
try { try {
return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath
} catch (err) { } catch (err) {
return false return false
} }
} }
function isLastView() { function isLastView() {
try { try {
return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath
} catch (err) { } catch (err) {
return false return false
} }
} }
function filterAffixTags(routes, basePath = '') { function filterAffixTags(routes, basePath = '') {
let tags = [] let tags = []
routes.forEach(route => { routes.forEach(route => {
if (route.meta && route.meta.affix) { if (route.meta && route.meta.affix) {
@ -135,9 +129,9 @@ function filterAffixTags(routes, basePath = '') {
} }
}) })
return tags return tags
} }
function initTags() { function initTags() {
const res = filterAffixTags(routes.value) const res = filterAffixTags(routes.value)
affixTags.value = res affixTags.value = res
for (const tag of res) { for (const tag of res) {
@ -146,16 +140,16 @@ function initTags() {
useTagsViewStore().addVisitedView(tag) useTagsViewStore().addVisitedView(tag)
} }
} }
} }
function addTags() { function addTags() {
const { name } = route const { name } = route
if (name) { if (name) {
useTagsViewStore().addView(route) useTagsViewStore().addView(route)
} }
} }
function moveToCurrentTag() { function moveToCurrentTag() {
nextTick(() => { nextTick(() => {
for (const r of visitedViews.value) { for (const r of visitedViews.value) {
if (r.path === route.path) { if (r.path === route.path) {
@ -167,56 +161,56 @@ function moveToCurrentTag() {
} }
} }
}) })
} }
function refreshSelectedTag(view) { function refreshSelectedTag(view) {
proxy.$tab.refreshPage(view) proxy.$tab.refreshPage(view)
if (route.meta.link) { if (route.meta.link) {
useTagsViewStore().delIframeView(route) useTagsViewStore().delIframeView(route)
} }
} }
function closeSelectedTag(view) { function closeSelectedTag(view) {
proxy.$tab.closePage(view).then(({ visitedViews }) => { proxy.$tab.closePage(view).then(({ visitedViews }) => {
if (isActive(view)) { if (isActive(view)) {
toLastView(visitedViews, view) toLastView(visitedViews, view)
} }
}) })
} }
function closeRightTags() { function closeRightTags() {
proxy.$tab.closeRightPage(selectedTag.value).then(visitedViews => { proxy.$tab.closeRightPage(selectedTag.value).then(visitedViews => {
if (!visitedViews.find(i => i.fullPath === route.fullPath)) { if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
toLastView(visitedViews) toLastView(visitedViews)
} }
}) })
} }
function closeLeftTags() { function closeLeftTags() {
proxy.$tab.closeLeftPage(selectedTag.value).then(visitedViews => { proxy.$tab.closeLeftPage(selectedTag.value).then(visitedViews => {
if (!visitedViews.find(i => i.fullPath === route.fullPath)) { if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
toLastView(visitedViews) toLastView(visitedViews)
} }
}) })
} }
function closeOthersTags() { function closeOthersTags() {
router.push(selectedTag.value).catch(() => { }) router.push(selectedTag.value).catch(() => { })
proxy.$tab.closeOtherPage(selectedTag.value).then(() => { proxy.$tab.closeOtherPage(selectedTag.value).then(() => {
moveToCurrentTag() moveToCurrentTag()
}) })
} }
function closeAllTags(view) { function closeAllTags(view) {
proxy.$tab.closeAllPage().then(({ visitedViews }) => { proxy.$tab.closeAllPage().then(({ visitedViews }) => {
if (affixTags.value.some(tag => tag.path === route.path)) { if (affixTags.value.some(tag => tag.path === route.path)) {
return return
} }
toLastView(visitedViews, view) toLastView(visitedViews, view)
}) })
} }
function toLastView(visitedViews, view) { function toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0] const latestView = visitedViews.slice(-1)[0]
if (latestView) { if (latestView) {
router.push(latestView.fullPath) router.push(latestView.fullPath)
@ -230,9 +224,9 @@ function toLastView(visitedViews, view) {
router.push('/') router.push('/')
} }
} }
} }
function openMenu(tag, e) { function openMenu(tag, e) {
const menuMinWidth = 105 const menuMinWidth = 105
const offsetLeft = proxy.$el.getBoundingClientRect().left // container margin left const offsetLeft = proxy.$el.getBoundingClientRect().left // container margin left
const offsetWidth = proxy.$el.offsetWidth // container width const offsetWidth = proxy.$el.offsetWidth // container width
@ -248,19 +242,19 @@ function openMenu(tag, e) {
top.value = e.clientY top.value = e.clientY
visible.value = true visible.value = true
selectedTag.value = tag selectedTag.value = tag
} }
function closeMenu() { function closeMenu() {
visible.value = false visible.value = false
} }
function handleScroll() { function handleScroll() {
closeMenu() closeMenu()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.tags-view-container { .tags-view-container {
height: 34px; height: 34px;
width: 100%; width: 100%;
background: var(--tags-bg, #fff); background: var(--tags-bg, #fff);
@ -337,12 +331,12 @@ function handleScroll() {
} }
} }
} }
} }
</style> </style>
<style lang="scss"> <style lang="scss">
//reset element css of el-icon-close //reset element css of el-icon-close
.tags-view-wrapper { .tags-view-wrapper {
.tags-view-item { .tags-view-item {
.el-icon-close { .el-icon-close {
width: 16px; width: 16px;
@ -367,5 +361,5 @@ function handleScroll() {
} }
} }
} }
} }
</style> </style>

2
src/main.js

@ -27,7 +27,7 @@ import './permission' // permission control
import { useDict } from '@/utils/dict' import { useDict } from '@/utils/dict'
import { getConfigKey } from "@/api/system/config" import { getConfigKey } from "@/api/system/config"
import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi' import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/mmxt'
// 分页组件 // 分页组件
import Pagination from '@/components/Pagination' import Pagination from '@/components/Pagination'

2
src/plugins/download.js

@ -3,7 +3,7 @@ import { ElLoading, ElMessage } from 'element-plus'
import { saveAs } from 'file-saver' import { saveAs } from 'file-saver'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode' import errorCode from '@/utils/errorCode'
import { blobValidate } from '@/utils/ruoyi' import { blobValidate } from '@/utils/mmxt'
const baseURL = import.meta.env.VITE_APP_BASE_API const baseURL = import.meta.env.VITE_APP_BASE_API
let downloadLoadingInstance let downloadLoadingInstance

4
src/router/index.js

@ -1,4 +1,4 @@
import { createWebHistory, createRouter } from 'vue-router' import { createWebHashHistory, createRouter } from 'vue-router'
/* Layout */ /* Layout */
import Layout from '@/layout' import Layout from '@/layout'
@ -161,7 +161,7 @@ export const dynamicRoutes = [
] ]
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHashHistory(),
routes: constantRoutes, routes: constantRoutes,
scrollBehavior(to, from, savedPosition) { scrollBehavior(to, from, savedPosition) {
if (savedPosition) { if (savedPosition) {

2
src/settings.js

@ -52,6 +52,6 @@ export default {
/** /**
* 底部版权文本内容 * 底部版权文本内容
*/ */
footerContent: 'Copyright © 2018-2026 RuoYi. All Rights Reserved.' footerContent: 'Copyright © 2018-2026 mmxt. All Rights Reserved.'
} }

6
src/utils/index.js

@ -1,4 +1,4 @@
import { parseTime } from './ruoyi' import { parseTime } from './mmxt'
/** /**
* 表格时间格式化 * 表格时间格式化
@ -218,7 +218,7 @@ export function getTime(type) {
export function debounce(func, wait, immediate) { export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result let timeout, args, context, timestamp, result
const later = function() { const later = function () {
// 据上一次触发时间间隔 // 据上一次触发时间间隔
const last = +new Date() - timestamp const last = +new Date() - timestamp
@ -235,7 +235,7 @@ export function debounce(func, wait, immediate) {
} }
} }
return function(...args) { return function (...args) {
context = this context = this
timestamp = +new Date() timestamp = +new Date()
const callNow = immediate && !timeout const callNow = immediate && !timeout

10
src/utils/request.js

@ -1,8 +1,8 @@
import axios from 'axios' import axios from 'axios'
import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus' import { ElNotification, ElMessageBox, ElMessage, ElLoading } from 'element-plus'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode' import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from '@/utils/ruoyi' import { tansParams, blobValidate } from '@/utils/mmxt'
import cache from '@/plugins/cache' import cache from '@/plugins/cache'
import { saveAs } from 'file-saver' import { saveAs } from 'file-saver'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
@ -29,6 +29,10 @@ service.interceptors.request.use(config => {
if (getToken() && !isToken) { if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
} }
// ✅ FormData 情况下不要设置 Content-Type
if (config.data instanceof FormData) {
delete config.headers['Content-Type']
}
// get请求映射params参数 // get请求映射params参数
if (config.method === 'get' && config.params) { if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params) let url = config.url + '?' + tansParams(config.params)
@ -106,7 +110,7 @@ service.interceptors.response.use(res => {
} else { } else {
return Promise.resolve(res.data) return Promise.resolve(res.data)
} }
}, },
error => { error => {
console.log('err' + error) console.log('err' + error)
let { message } = error let { message } = error

228
src/utils/ruoyi.js

@ -1,228 +0,0 @@
/**
* 通用js方法封装处理
* Copyright (c) 2019 ruoyi
*/
// 日期格式化
export function parseTime(time, pattern) {
if (arguments.length === 0 || !time) {
return null
}
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
} else if (typeof time === 'string') {
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '')
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
// 表单重置
export function resetForm(refName) {
if (this.$refs[refName]) {
this.$refs[refName].resetFields()
}
}
// 添加日期范围
export function addDateRange(params, dateRange, propName) {
let search = params
search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}
dateRange = Array.isArray(dateRange) ? dateRange : []
if (typeof (propName) === 'undefined') {
search.params['beginTime'] = dateRange[0]
search.params['endTime'] = dateRange[1]
} else {
search.params['begin' + propName] = dateRange[0]
search.params['end' + propName] = dateRange[1]
}
return search
}
// 回显数据字典
export function selectDictLabel(datas, value) {
if (value === undefined) {
return ""
}
var actions = []
Object.keys(datas).some((key) => {
if (datas[key].value == ('' + value)) {
actions.push(datas[key].label)
return true
}
})
if (actions.length === 0) {
actions.push(value)
}
return actions.join('')
}
// 回显数据字典(字符串、数组)
export function selectDictLabels(datas, value, separator) {
if (value === undefined || value.length ===0) {
return ""
}
if (Array.isArray(value)) {
value = value.join(",")
}
var actions = []
var currentSeparator = undefined === separator ? "," : separator
var temp = value.split(currentSeparator)
Object.keys(value.split(currentSeparator)).some((val) => {
var match = false
Object.keys(datas).some((key) => {
if (datas[key].value == ('' + temp[val])) {
actions.push(datas[key].label + currentSeparator)
match = true
}
})
if (!match) {
actions.push(temp[val] + currentSeparator)
}
})
return actions.join('').substring(0, actions.join('').length - 1)
}
// 字符串格式化(%s )
export function sprintf(str) {
var args = arguments, flag = true, i = 1
str = str.replace(/%s/g, function () {
var arg = args[i++]
if (typeof arg === 'undefined') {
flag = false
return ''
}
return arg
})
return flag ? str : ''
}
// 转换字符串,undefined,null等转化为""
export function parseStrEmpty(str) {
if (!str || str == "undefined" || str == "null") {
return ""
}
return str
}
// 数据合并
export function mergeRecursive(source, target) {
for (var p in target) {
try {
if (target[p].constructor == Object) {
source[p] = mergeRecursive(source[p], target[p])
} else {
source[p] = target[p]
}
} catch (e) {
source[p] = target[p]
}
}
return source
}
/**
* 构造树型结构数据
* @param {*} data 数据源
* @param {*} id id字段 默认 'id'
* @param {*} parentId 父节点字段 默认 'parentId'
* @param {*} children 孩子节点字段 默认 'children'
*/
export function handleTree(data, id, parentId, children) {
let config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children'
}
var childrenListMap = {}
var tree = []
for (let d of data) {
let 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]
if (!parentObj) {
tree.push(d)
} else {
parentObj[config.childrenList].push(d)
}
}
return tree
}
/**
* 参数处理
* @param {*} params 参数
*/
export function tansParams(params) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName]
var 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) + "="
result += subPart + encodeURIComponent(value[key]) + "&"
}
}
} else {
result += part + encodeURIComponent(value) + "&"
}
}
}
return result
}
// 返回项目路径
export function getNormalPath(p) {
if (p.length === 0 || !p || p == 'undefined') {
return p
}
let res = p.replace('//', '/')
if (res[res.length - 1] === '/') {
return res.slice(0, res.length - 1)
}
return res
}
// 验证是否为blob格式
export function blobValidate(data) {
return data.type !== 'application/json'
}

1158
src/views/index.vue

File diff suppressed because it is too large

282
src/views/monitor/job/index.vue

@ -2,32 +2,17 @@
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
<el-form-item label="任务名称" prop="jobName"> <el-form-item label="任务名称" prop="jobName">
<el-input <el-input v-model="queryParams.jobName" placeholder="请输入任务名称" clearable style="width: 200px"
v-model="queryParams.jobName" @keyup.enter="handleQuery" />
placeholder="请输入任务名称"
clearable
style="width: 200px"
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item label="任务组名" prop="jobGroup"> <el-form-item label="任务组名" prop="jobGroup">
<el-select v-model="queryParams.jobGroup" placeholder="请选择任务组名" clearable style="width: 200px"> <el-select v-model="queryParams.jobGroup" placeholder="请选择任务组名" clearable style="width: 200px">
<el-option <el-option v-for="dict in sys_job_group" :key="dict.value" :label="dict.label" :value="dict.value" />
v-for="dict in sys_job_group"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="任务状态" prop="status"> <el-form-item label="任务状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择任务状态" clearable style="width: 200px"> <el-select v-model="queryParams.status" placeholder="请选择任务状态" clearable style="width: 200px">
<el-option <el-option v-for="dict in sys_job_status" :key="dict.value" :label="dict.label" :value="dict.value" />
v-for="dict in sys_job_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -38,51 +23,24 @@
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button type="primary" plain icon="Plus" @click="handleAdd"
type="primary" v-hasPermi="['monitor:job:add']">新增</el-button>
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['monitor:job:add']"
>新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"
type="success" v-hasPermi="['monitor:job:edit']">修改</el-button>
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['monitor:job:edit']"
>修改</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
type="danger" v-hasPermi="['monitor:job:remove']">删除</el-button>
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['monitor:job:remove']"
>删除</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button type="warning" plain icon="Download" @click="handleExport"
type="warning" v-hasPermi="['monitor:job:export']">导出</el-button>
plain
icon="Download"
@click="handleExport"
v-hasPermi="['monitor:job:export']"
>导出</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button type="info" plain icon="Operation" @click="handleJobLog"
type="info" v-hasPermi="['monitor:job:query']">日志</el-button>
plain
icon="Operation"
@click="handleJobLog"
v-hasPermi="['monitor:job:query']"
>日志</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
@ -100,42 +58,38 @@
<el-table-column label="cron执行表达式" align="center" prop="cronExpression" :show-overflow-tooltip="true" /> <el-table-column label="cron执行表达式" align="center" prop="cronExpression" :show-overflow-tooltip="true" />
<el-table-column label="状态" align="center"> <el-table-column label="状态" align="center">
<template #default="scope"> <template #default="scope">
<el-switch <el-switch v-model="scope.row.status" active-value="0" inactive-value="1"
v-model="scope.row.status" @change="handleStatusChange(scope.row)"></el-switch>
active-value="0"
inactive-value="1"
@change="handleStatusChange(scope.row)"
></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['monitor:job:edit']"></el-button> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
v-hasPermi="['monitor:job:edit']"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['monitor:job:remove']"></el-button> <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
v-hasPermi="['monitor:job:remove']"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="执行一次" placement="top"> <el-tooltip content="执行一次" placement="top">
<el-button link type="primary" icon="CaretRight" @click="handleRun(scope.row)" v-hasPermi="['monitor:job:changeStatus']"></el-button> <el-button link type="primary" icon="CaretRight" @click="handleRun(scope.row)"
v-hasPermi="['monitor:job:changeStatus']"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="任务详细" placement="top"> <el-tooltip content="任务详细" placement="top">
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['monitor:job:query']"></el-button> <el-button link type="primary" icon="View" @click="handleView(scope.row)"
v-hasPermi="['monitor:job:query']"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="调度日志" placement="top"> <el-tooltip content="调度日志" placement="top">
<el-button link type="primary" icon="Operation" @click="handleJobLog(scope.row)" v-hasPermi="['monitor:job:query']"></el-button> <el-button link type="primary" icon="Operation" @click="handleJobLog(scope.row)"
v-hasPermi="['monitor:job:query']"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-show="total > 0" v-model:limit="queryParams.pageSize" @pagination="getList" />
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改定时任务对话框 --> <!-- 添加或修改定时任务对话框 -->
<el-dialog :title="title" v-model="open" width="820px" append-to-body> <el-dialog :title="title" v-model="open" width="820px" append-to-body>
@ -149,12 +103,8 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="任务分组" prop="jobGroup"> <el-form-item label="任务分组" prop="jobGroup">
<el-select v-model="form.jobGroup" placeholder="请选择"> <el-select v-model="form.jobGroup" placeholder="请选择">
<el-option <el-option v-for="dict in sys_job_group" :key="dict.value" :label="dict.label"
v-for="dict in sys_job_group" :value="dict.value"></el-option>
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -167,7 +117,7 @@
<template #content> <template #content>
<div> <div>
Bean调用示例ryTask.ryParams('ry') Bean调用示例ryTask.ryParams('ry')
<br />Class类调用示例com.ruoyi.quartz.task.RyTask.ryParams('ry') <br />Class类调用示例com.mmxt.quartz.task.RyTask.ryParams('ry')
<br />参数说明支持字符串布尔类型长整型浮点型整型 <br />参数说明支持字符串布尔类型长整型浮点型整型
</div> </div>
</template> </template>
@ -193,11 +143,8 @@
<el-col :span="24" v-if="form.jobId !== undefined"> <el-col :span="24" v-if="form.jobId !== undefined">
<el-form-item label="状态"> <el-form-item label="状态">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
<el-radio <el-radio v-for="dict in sys_job_status" :key="dict.value" :value="dict.value">{{ dict.label
v-for="dict in sys_job_status" }}</el-radio>
:key="dict.value"
:value="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -285,27 +232,27 @@
</template> </template>
<script setup name="Job"> <script setup name="Job">
import Crontab from '@/components/Crontab' import Crontab from '@/components/Crontab'
import { listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus } from "@/api/monitor/job" import { listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus } from "@/api/monitor/job"
const router = useRouter() const router = useRouter()
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const { sys_job_group, sys_job_status } = proxy.useDict("sys_job_group", "sys_job_status") const { sys_job_group, sys_job_status } = proxy.useDict("sys_job_group", "sys_job_status")
const jobList = ref([]) const jobList = ref([])
const open = ref(false) const open = ref(false)
const loading = ref(true) const loading = ref(true)
const showSearch = ref(true) const showSearch = ref(true)
const ids = ref([]) const ids = ref([])
const single = ref(true) const single = ref(true)
const multiple = ref(true) const multiple = ref(true)
const total = ref(0) const total = ref(0)
const title = ref("") const title = ref("")
const openView = ref(false) const openView = ref(false)
const openCron = ref(false) const openCron = ref(false)
const expression = ref("") const expression = ref("")
const data = reactive({ const data = reactive({
form: {}, form: {},
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
@ -319,33 +266,33 @@ const data = reactive({
invokeTarget: [{ required: true, message: "调用目标字符串不能为空", trigger: "blur" }], invokeTarget: [{ required: true, message: "调用目标字符串不能为空", trigger: "blur" }],
cronExpression: [{ required: true, message: "cron执行表达式不能为空", trigger: "change" }] cronExpression: [{ required: true, message: "cron执行表达式不能为空", trigger: "change" }]
} }
}) })
const { queryParams, form, rules } = toRefs(data) const { queryParams, form, rules } = toRefs(data)
/** 查询定时任务列表 */ /** 查询定时任务列表 */
function getList() { function getList() {
loading.value = true loading.value = true
listJob(queryParams.value).then(response => { listJob(queryParams.value).then(response => {
jobList.value = response.rows jobList.value = response.rows
total.value = response.total total.value = response.total
loading.value = false loading.value = false
}) })
} }
/** 任务组名字典翻译 */ /** 任务组名字典翻译 */
function jobGroupFormat(row, column) { function jobGroupFormat(row, column) {
return proxy.selectDictLabel(sys_job_group.value, row.jobGroup) return proxy.selectDictLabel(sys_job_group.value, row.jobGroup)
} }
/** 取消按钮 */ /** 取消按钮 */
function cancel() { function cancel() {
open.value = false open.value = false
reset() reset()
} }
/** 表单重置 */ /** 表单重置 */
function reset() { function reset() {
form.value = { form.value = {
jobId: undefined, jobId: undefined,
jobName: undefined, jobName: undefined,
@ -357,29 +304,29 @@ function reset() {
status: "0" status: "0"
} }
proxy.resetForm("jobRef") proxy.resetForm("jobRef")
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
function handleQuery() { function handleQuery() {
queryParams.value.pageNum = 1 queryParams.value.pageNum = 1
getList() getList()
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
function resetQuery() { function resetQuery() {
proxy.resetForm("queryRef") proxy.resetForm("queryRef")
handleQuery() handleQuery()
} }
// //
function handleSelectionChange(selection) { function handleSelectionChange(selection) {
ids.value = selection.map(item => item.jobId) ids.value = selection.map(item => item.jobId)
single.value = selection.length != 1 single.value = selection.length != 1
multiple.value = !selection.length multiple.value = !selection.length
} }
// //
function handleCommand(command, row) { function handleCommand(command, row) {
switch (command) { switch (command) {
case "handleRun": case "handleRun":
handleRun(row) handleRun(row)
@ -393,10 +340,10 @@ function handleCommand(command, row) {
default: default:
break break
} }
} }
// //
function handleStatusChange(row) { function handleStatusChange(row) {
let text = row.status === "0" ? "启用" : "停用" let text = row.status === "0" ? "启用" : "停用"
proxy.$modal.confirm('确认要"' + text + '""' + row.jobName + '"任务吗?').then(function () { proxy.$modal.confirm('确认要"' + text + '""' + row.jobName + '"任务吗?').then(function () {
return changeJobStatus(row.jobId, row.status) return changeJobStatus(row.jobId, row.status)
@ -405,51 +352,52 @@ function handleStatusChange(row) {
}).catch(function () { }).catch(function () {
row.status = row.status === "0" ? "1" : "0" row.status = row.status === "0" ? "1" : "0"
}) })
} }
/* 立即执行一次 */ /* 立即执行一次 */
function handleRun(row) { function handleRun(row) {
proxy.$modal.confirm('确认要立即执行一次"' + row.jobName + '"任务吗?').then(function () { proxy.$modal.confirm('确认要立即执行一次"' + row.jobName + '"任务吗?').then(function () {
return runJob(row.jobId, row.jobGroup) return runJob(row.jobId, row.jobGroup)
}).then(() => { }).then(() => {
proxy.$modal.msgSuccess("执行成功")}) proxy.$modal.msgSuccess("执行成功")
.catch(() => {}) })
} .catch(() => { })
}
/** 任务详细信息 */ /** 任务详细信息 */
function handleView(row) { function handleView(row) {
getJob(row.jobId).then(response => { getJob(row.jobId).then(response => {
form.value = response.data form.value = response.data
openView.value = true openView.value = true
}) })
} }
/** cron表达式按钮操作 */ /** cron表达式按钮操作 */
function handleShowCron() { function handleShowCron() {
expression.value = form.value.cronExpression expression.value = form.value.cronExpression
openCron.value = true openCron.value = true
} }
/** 确定后回传值 */ /** 确定后回传值 */
function crontabFill(value) { function crontabFill(value) {
form.value.cronExpression = value form.value.cronExpression = value
} }
/** 任务日志列表查询 */ /** 任务日志列表查询 */
function handleJobLog(row) { function handleJobLog(row) {
const jobId = row.jobId || 0 const jobId = row.jobId || 0
router.push('/monitor/job-log/index/' + jobId) router.push('/monitor/job-log/index/' + jobId)
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
function handleAdd() { function handleAdd() {
reset() reset()
open.value = true open.value = true
title.value = "添加任务" title.value = "添加任务"
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
function handleUpdate(row) { function handleUpdate(row) {
reset() reset()
const jobId = row.jobId || ids.value const jobId = row.jobId || ids.value
getJob(jobId).then(response => { getJob(jobId).then(response => {
@ -457,10 +405,10 @@ function handleUpdate(row) {
open.value = true open.value = true
title.value = "修改任务" title.value = "修改任务"
}) })
} }
/** 提交按钮 */ /** 提交按钮 */
function submitForm() { function submitForm() {
proxy.$refs["jobRef"].validate(valid => { proxy.$refs["jobRef"].validate(valid => {
if (valid) { if (valid) {
if (form.value.jobId != undefined) { if (form.value.jobId != undefined) {
@ -478,25 +426,25 @@ function submitForm() {
} }
} }
}) })
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
function handleDelete(row) { function handleDelete(row) {
const jobIds = row.jobId || ids.value const jobIds = row.jobId || ids.value
proxy.$modal.confirm('是否确认删除定时任务编号为"' + jobIds + '"的数据项?').then(function () { proxy.$modal.confirm('是否确认删除定时任务编号为"' + jobIds + '"的数据项?').then(function () {
return delJob(jobIds) return delJob(jobIds)
}).then(() => { }).then(() => {
getList() getList()
proxy.$modal.msgSuccess("删除成功") proxy.$modal.msgSuccess("删除成功")
}).catch(() => {}) }).catch(() => { })
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
function handleExport() { function handleExport() {
proxy.download("monitor/job/export", { proxy.download("monitor/job/export", {
...queryParams.value, ...queryParams.value,
}, `job_${new Date().getTime()}.xlsx`) }, `job_${new Date().getTime()}.xlsx`)
} }
getList() getList()
</script> </script>

85
src/views/tool/gen/editTable.vue

@ -6,19 +6,15 @@
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="字段信息" name="columnInfo"> <el-tab-pane label="字段信息" name="columnInfo">
<el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight"> <el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight">
<el-table-column label="序号" type="index" min-width="5%" class-name="allowDrag"/> <el-table-column label="序号" type="index" min-width="5%" class-name="allowDrag" />
<el-table-column label="字段列名" prop="columnName" min-width="10%" :show-overflow-tooltip="true" class-name="allowDrag"/> <el-table-column label="字段列名" prop="columnName" min-width="10%" :show-overflow-tooltip="true"
class-name="allowDrag" />
<el-table-column label="字段描述" min-width="10%"> <el-table-column label="字段描述" min-width="10%">
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.columnComment"></el-input> <el-input v-model="scope.row.columnComment"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="物理类型" prop="columnType" min-width="10%" :show-overflow-tooltip="true" />
label="物理类型"
prop="columnType"
min-width="10%"
:show-overflow-tooltip="true"
/>
<el-table-column label="Java类型" min-width="11%"> <el-table-column label="Java类型" min-width="11%">
<template #default="scope"> <template #default="scope">
<el-select v-model="scope.row.javaType"> <el-select v-model="scope.row.javaType">
@ -95,10 +91,7 @@
<el-table-column label="字典类型" min-width="12%"> <el-table-column label="字典类型" min-width="12%">
<template #default="scope"> <template #default="scope">
<el-select v-model="scope.row.dictType" clearable filterable placeholder="请选择"> <el-select v-model="scope.row.dictType" clearable filterable placeholder="请选择">
<el-option <el-option v-for="dict in dictOptions" :key="dict.dictType" :label="dict.dictName"
v-for="dict in dictOptions"
:key="dict.dictType"
:label="dict.dictName"
:value="dict.dictType"> :value="dict.dictType">
<span style="float: left">{{ dict.dictName }}</span> <span style="float: left">{{ dict.dictName }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ dict.dictType }}</span> <span style="float: right; color: #8492a6; font-size: 13px">{{ dict.dictType }}</span>
@ -122,24 +115,24 @@
</template> </template>
<script setup name="GenEdit"> <script setup name="GenEdit">
import { getGenTable, updateGenTable } from "@/api/tool/gen" import { getGenTable, updateGenTable } from "@/api/tool/gen"
import { optionselect as getDictOptionselect } from "@/api/system/dict/type" import { optionselect as getDictOptionselect } from "@/api/system/dict/type"
import basicInfoForm from "./basicInfoForm" import basicInfoForm from "./basicInfoForm"
import genInfoForm from "./genInfoForm" import genInfoForm from "./genInfoForm"
import Sortable from 'sortablejs' // import Sortable from 'sortablejs'
const route = useRoute() const route = useRoute()
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const activeName = ref("columnInfo") const activeName = ref("columnInfo")
const tableHeight = ref(document.documentElement.scrollHeight - 245 + "px") const tableHeight = ref(document.documentElement.scrollHeight - 245 + "px")
const tables = ref([]) const tables = ref([])
const columns = ref([]) const columns = ref([])
const dictOptions = ref([]) const dictOptions = ref([])
const info = ref({}) const info = ref({})
/** 提交按钮 */ /** 提交按钮 */
function submitForm() { function submitForm() {
const basicForm = proxy.$refs.basicInfo.$refs.basicInfoForm const basicForm = proxy.$refs.basicInfo.$refs.basicInfoForm
const genForm = proxy.$refs.genInfo.$refs.genInfoForm const genForm = proxy.$refs.genInfo.$refs.genInfoForm
Promise.all([basicForm, genForm].map(getFormPromise)).then(res => { Promise.all([basicForm, genForm].map(getFormPromise)).then(res => {
@ -163,22 +156,22 @@ function submitForm() {
proxy.$modal.msgError("表单校验未通过,请重新检查提交内容") proxy.$modal.msgError("表单校验未通过,请重新检查提交内容")
} }
}) })
} }
function getFormPromise(form) { function getFormPromise(form) {
return new Promise(resolve => { return new Promise(resolve => {
form.validate(res => { form.validate(res => {
resolve(res) resolve(res)
}) })
}) })
} }
function close() { function close() {
const obj = { path: "/tool/gen", query: { t: Date.now(), pageNum: route.query.pageNum } } const obj = { path: "/tool/gen", query: { t: Date.now(), pageNum: route.query.pageNum } }
proxy.$tab.closeOpenPage(obj) proxy.$tab.closeOpenPage(obj)
} }
(() => { (() => {
const tableId = route.params && route.params.tableId const tableId = route.params && route.params.tableId
if (tableId) { if (tableId) {
// //
@ -192,20 +185,20 @@ function close() {
dictOptions.value = response.data dictOptions.value = response.data
}) })
} }
})() })()
// //
onMounted(() => { onMounted(() => {
const element = document.querySelector('.el-table__body > tbody') const element = document.querySelector('.el-table__body > tbody')
Sortable.create(element, { // Sortable.create(element, {
handle: ".allowDrag", // handle: ".allowDrag",
onEnd: (evt) => { // onEnd: (evt) => {
const targetRow = columns.value.splice(evt.oldIndex, 1)[0] // const targetRow = columns.value.splice(evt.oldIndex, 1)[0]
columns.value.splice(evt.newIndex, 0, targetRow) // columns.value.splice(evt.newIndex, 0, targetRow)
for (const index in columns.value) { // for (const index in columns.value) {
columns.value[index].sort = parseInt(index) + 1 // columns.value[index].sort = parseInt(index) + 1
} // }
} // }
// })
}) })
})
</script> </script>

101
src/views/tool/gen/genInfoForm.vue

@ -26,7 +26,7 @@
<el-form-item prop="packageName"> <el-form-item prop="packageName">
<template #label> <template #label>
生成包路径 生成包路径
<el-tooltip content="生成在哪个java包下,例如 com.ruoyi.system" placement="top"> <el-tooltip content="生成在哪个java包下,例如 com.mmxt.system" placement="top">
<el-icon><question-filled /></el-icon> <el-icon><question-filled /></el-icon>
</el-tooltip> </el-tooltip>
</template> </template>
@ -91,14 +91,9 @@
<el-icon><question-filled /></el-icon> <el-icon><question-filled /></el-icon>
</el-tooltip> </el-tooltip>
</template> </template>
<el-tree-select <el-tree-select v-model="info.parentMenuId" :data="menuOptions"
v-model="info.parentMenuId" :props="{ value: 'menuId', label: 'menuName', children: 'children' }" value-key="menuId"
:data="menuOptions" placeholder="请选择系统菜单" check-strictly />
:props="{ value: 'menuId', label: 'menuName', children: 'children' }"
value-key="menuId"
placeholder="请选择系统菜单"
check-strictly
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -141,12 +136,8 @@
</el-tooltip> </el-tooltip>
</template> </template>
<el-select v-model="info.treeCode" placeholder="请选择"> <el-select v-model="info.treeCode" placeholder="请选择">
<el-option <el-option v-for="(column, index) in info.columns" :key="index"
v-for="(column, index) in info.columns" :label="column.columnName + ':' + column.columnComment" :value="column.columnName"></el-option>
:key="index"
:label="column.columnName + ':' + column.columnComment"
:value="column.columnName"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -159,12 +150,8 @@
</el-tooltip> </el-tooltip>
</template> </template>
<el-select v-model="info.treeParentCode" placeholder="请选择"> <el-select v-model="info.treeParentCode" placeholder="请选择">
<el-option <el-option v-for="(column, index) in info.columns" :key="index"
v-for="(column, index) in info.columns" :label="column.columnName + ':' + column.columnComment" :value="column.columnName"></el-option>
:key="index"
:label="column.columnName + ':' + column.columnComment"
:value="column.columnName"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -177,12 +164,8 @@
</el-tooltip> </el-tooltip>
</template> </template>
<el-select v-model="info.treeName" placeholder="请选择"> <el-select v-model="info.treeName" placeholder="请选择">
<el-option <el-option v-for="(column, index) in info.columns" :key="index"
v-for="(column, index) in info.columns" :label="column.columnName + ':' + column.columnComment" :value="column.columnName"></el-option>
:key="index"
:label="column.columnName + ':' + column.columnComment"
:value="column.columnName"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -201,12 +184,8 @@
</el-tooltip> </el-tooltip>
</template> </template>
<el-select v-model="info.subTableName" placeholder="请选择" @change="subSelectChange"> <el-select v-model="info.subTableName" placeholder="请选择" @change="subSelectChange">
<el-option <el-option v-for="(table, index) in tables" :key="index"
v-for="(table, index) in tables" :label="table.tableName + ':' + table.tableComment" :value="table.tableName"></el-option>
:key="index"
:label="table.tableName + ':' + table.tableComment"
:value="table.tableName"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -219,12 +198,8 @@
</el-tooltip> </el-tooltip>
</template> </template>
<el-select v-model="info.subTableFkName" placeholder="请选择"> <el-select v-model="info.subTableFkName" placeholder="请选择">
<el-option <el-option v-for="(column, index) in subColumns" :key="index"
v-for="(column, index) in subColumns" :label="column.columnName + ':' + column.columnComment" :value="column.columnName"></el-option>
:key="index"
:label="column.columnName + ':' + column.columnComment"
:value="column.columnName"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -235,13 +210,13 @@
</template> </template>
<script setup> <script setup>
import { listMenu } from "@/api/system/menu" import { listMenu } from "@/api/system/menu"
const subColumns = ref([]) const subColumns = ref([])
const menuOptions = ref([]) const menuOptions = ref([])
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const props = defineProps({ const props = defineProps({
info: { info: {
type: Object, type: Object,
default: null default: null
@ -250,29 +225,29 @@ const props = defineProps({
type: Array, type: Array,
default: null default: null
} }
}) })
// //
const rules = ref({ const rules = ref({
tplCategory: [{ required: true, message: "请选择生成模板", trigger: "blur" }], tplCategory: [{ required: true, message: "请选择生成模板", trigger: "blur" }],
packageName: [{ required: true, message: "请输入生成包路径", trigger: "blur" }], packageName: [{ required: true, message: "请输入生成包路径", trigger: "blur" }],
moduleName: [{ required: true, message: "请输入生成模块名", trigger: "blur" }], moduleName: [{ required: true, message: "请输入生成模块名", trigger: "blur" }],
businessName: [{ required: true, message: "请输入生成业务名", trigger: "blur" }], businessName: [{ required: true, message: "请输入生成业务名", trigger: "blur" }],
functionName: [{ required: true, message: "请输入生成功能名", trigger: "blur" }] functionName: [{ required: true, message: "请输入生成功能名", trigger: "blur" }]
}) })
function subSelectChange(value) { function subSelectChange(value) {
props.info.subTableFkName = "" props.info.subTableFkName = ""
} }
function tplSelectChange(value) { function tplSelectChange(value) {
if (value !== "sub") { if (value !== "sub") {
props.info.subTableName = "" props.info.subTableName = ""
props.info.subTableFkName = "" props.info.subTableFkName = ""
} }
} }
function setSubTableColumns(value) { function setSubTableColumns(value) {
for (var item in props.tables) { for (var item in props.tables) {
const name = props.tables[item].tableName const name = props.tables[item].tableName
if (value === name) { if (value === name) {
@ -280,26 +255,26 @@ function setSubTableColumns(value) {
break break
} }
} }
} }
/** 查询菜单下拉树结构 */ /** 查询菜单下拉树结构 */
function getMenuTreeselect() { function getMenuTreeselect() {
listMenu().then(response => { listMenu().then(response => {
menuOptions.value = proxy.handleTree(response.data, "menuId") menuOptions.value = proxy.handleTree(response.data, "menuId")
}) })
} }
onMounted(() => { onMounted(() => {
getMenuTreeselect() getMenuTreeselect()
}) })
watch(() => props.info.subTableName, val => { watch(() => props.info.subTableName, val => {
setSubTableColumns(val) setSubTableColumns(val)
}) })
watch(() => props.info.tplWebType, val => { watch(() => props.info.tplWebType, val => {
if (val === '') { if (val === '') {
props.info.tplWebType = "element-plus" props.info.tplWebType = "element-plus"
} }
}) })
</script> </script>

248
src/views/tool/gen/index.vue

@ -2,32 +2,16 @@
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
<el-form-item label="表名称" prop="tableName"> <el-form-item label="表名称" prop="tableName">
<el-input <el-input v-model="queryParams.tableName" placeholder="请输入表名称" clearable style="width: 200px"
v-model="queryParams.tableName" @keyup.enter="handleQuery" />
placeholder="请输入表名称"
clearable
style="width: 200px"
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item label="表描述" prop="tableComment"> <el-form-item label="表描述" prop="tableComment">
<el-input <el-input v-model="queryParams.tableComment" placeholder="请输入表描述" clearable style="width: 200px"
v-model="queryParams.tableComment" @keyup.enter="handleQuery" />
placeholder="请输入表描述"
clearable
style="width: 200px"
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item label="创建时间" style="width: 308px"> <el-form-item label="创建时间" style="width: 308px">
<el-date-picker <el-date-picker v-model="dateRange" value-format="YYYY-MM-DD" type="daterange" range-separator="-"
v-model="dateRange" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
@ -37,57 +21,29 @@
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button type="primary" plain icon="Download" :disabled="multiple" @click="handleGenTable"
type="primary" v-hasPermi="['tool:gen:code']">生成</el-button>
plain
icon="Download"
:disabled="multiple"
@click="handleGenTable"
v-hasPermi="['tool:gen:code']"
>生成</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button type="primary" plain icon="Plus" @click="openCreateTable" v-hasRole="['admin']">创建</el-button>
type="primary"
plain
icon="Plus"
@click="openCreateTable"
v-hasRole="['admin']"
>创建</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button type="info" plain icon="Upload" @click="openImportTable"
type="info" v-hasPermi="['tool:gen:import']">导入</el-button>
plain
icon="Upload"
@click="openImportTable"
v-hasPermi="['tool:gen:import']"
>导入</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button type="success" plain icon="Edit" :disabled="single" @click="handleEditTable"
type="success" v-hasPermi="['tool:gen:edit']">修改</el-button>
plain
icon="Edit"
:disabled="single"
@click="handleEditTable"
v-hasPermi="['tool:gen:edit']"
>修改</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
type="danger" v-hasPermi="['tool:gen:remove']">删除</el-button>
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['tool:gen:remove']"
>删除</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
<el-table ref="genRef" v-loading="loading" :data="tableList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange"> <el-table ref="genRef" v-loading="loading" :data="tableList" @selection-change="handleSelectionChange"
:default-sort="defaultSort" @sort-change="handleSortChange">
<el-table-column type="selection" align="center" width="55"></el-table-column> <el-table-column type="selection" align="center" width="55"></el-table-column>
<el-table-column label="序号" type="index" width="50" align="center"> <el-table-column label="序号" type="index" width="50" align="center">
<template #default="scope"> <template #default="scope">
@ -97,45 +53,45 @@
<el-table-column label="表名称" align="center" prop="tableName" :show-overflow-tooltip="true" /> <el-table-column label="表名称" align="center" prop="tableName" :show-overflow-tooltip="true" />
<el-table-column label="表描述" align="center" prop="tableComment" :show-overflow-tooltip="true" /> <el-table-column label="表描述" align="center" prop="tableComment" :show-overflow-tooltip="true" />
<el-table-column label="实体" align="center" prop="className" :show-overflow-tooltip="true" /> <el-table-column label="实体" align="center" prop="className" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" width="160" sortable="custom" :sort-orders="['descending', 'ascending']" /> <el-table-column label="创建时间" align="center" prop="createTime" width="160" sortable="custom"
<el-table-column label="更新时间" align="center" prop="updateTime" width="160" sortable="custom" :sort-orders="['descending', 'ascending']" /> :sort-orders="['descending', 'ascending']" />
<el-table-column label="更新时间" align="center" prop="updateTime" width="160" sortable="custom"
:sort-orders="['descending', 'ascending']" />
<el-table-column label="操作" align="center" width="330" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" width="330" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="预览" placement="top"> <el-tooltip content="预览" placement="top">
<el-button link type="primary" icon="View" @click="handlePreview(scope.row)" v-hasPermi="['tool:gen:preview']"></el-button> <el-button link type="primary" icon="View" @click="handlePreview(scope.row)"
v-hasPermi="['tool:gen:preview']"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="编辑" placement="top"> <el-tooltip content="编辑" placement="top">
<el-button link type="primary" icon="Edit" @click="handleEditTable(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button> <el-button link type="primary" icon="Edit" @click="handleEditTable(scope.row)"
v-hasPermi="['tool:gen:edit']"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['tool:gen:remove']"></el-button> <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
v-hasPermi="['tool:gen:remove']"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="同步" placement="top"> <el-tooltip content="同步" placement="top">
<el-button link type="primary" icon="Refresh" @click="handleSynchDb(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button> <el-button link type="primary" icon="Refresh" @click="handleSynchDb(scope.row)"
v-hasPermi="['tool:gen:edit']"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="生成代码" placement="top"> <el-tooltip content="生成代码" placement="top">
<el-button link type="primary" icon="Download" @click="handleGenTable(scope.row)" v-hasPermi="['tool:gen:code']"></el-button> <el-button link type="primary" icon="Download" @click="handleGenTable(scope.row)"
v-hasPermi="['tool:gen:code']"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination <pagination v-show="total>0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
v-show="total>0" @pagination="getList" />
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 预览界面 --> <!-- 预览界面 -->
<el-dialog :title="preview.title" v-model="preview.open" width="80%" top="5vh" append-to-body class="scrollbar"> <el-dialog :title="preview.title" v-model="preview.open" width="80%" top="5vh" append-to-body class="scrollbar">
<el-tabs v-model="preview.activeName"> <el-tabs v-model="preview.activeName">
<el-tab-pane <el-tab-pane v-for="(value, key) in preview.data"
v-for="(value, key) in preview.data"
:label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))" :label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))"
:name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))" :name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))" :key="value">
:key="value" <el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess"
> style="float:right">&nbsp;复制</el-link>
<el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right">&nbsp;复制</el-link>
<pre>{{ value }}</pre> <pre>{{ value }}</pre>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -146,27 +102,27 @@
</template> </template>
<script setup name="Gen"> <script setup name="Gen">
import { listTable, previewTable, delTable, genCode, synchDb } from "@/api/tool/gen" import { listTable, previewTable, delTable, genCode, synchDb } from "@/api/tool/gen"
import router from "@/router" import router from "@/router"
import importTable from "./importTable" import importTable from "./importTable"
import createTable from "./createTable" import createTable from "./createTable"
const route = useRoute() const route = useRoute()
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const tableList = ref([]) const tableList = ref([])
const loading = ref(true) const loading = ref(true)
const showSearch = ref(true) const showSearch = ref(true)
const ids = ref([]) const ids = ref([])
const single = ref(true) const single = ref(true)
const multiple = ref(true) const multiple = ref(true)
const total = ref(0) const total = ref(0)
const tableNames = ref([]) const tableNames = ref([])
const dateRange = ref([]) const dateRange = ref([])
const uniqueId = ref("") const uniqueId = ref("")
const defaultSort = ref({ prop: "createTime", order: "descending" }) const defaultSort = ref({ prop: "createTime", order: "descending" })
const data = reactive({ const data = reactive({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
@ -181,11 +137,11 @@ const data = reactive({
data: {}, data: {},
activeName: "domain.java" activeName: "domain.java"
} }
}) })
const { queryParams, preview } = toRefs(data) const { queryParams, preview } = toRefs(data)
onActivated(() => { onActivated(() => {
const time = route.query.t const time = route.query.t
if (time != null && time != uniqueId.value) { if (time != null && time != uniqueId.value) {
uniqueId.value = time uniqueId.value = time
@ -194,26 +150,26 @@ onActivated(() => {
proxy.resetForm("queryForm") proxy.resetForm("queryForm")
getList() getList()
} }
}) })
/** 查询表集合 */ /** 查询表集合 */
function getList() { function getList() {
loading.value = true loading.value = true
listTable(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => { listTable(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
tableList.value = response.rows tableList.value = response.rows
total.value = response.total total.value = response.total
loading.value = false loading.value = false
}) })
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
function handleQuery() { function handleQuery() {
queryParams.value.pageNum = 1 queryParams.value.pageNum = 1
getList() getList()
} }
/** 生成代码操作 */ /** 生成代码操作 */
function handleGenTable(row) { function handleGenTable(row) {
const tbNames = row.tableName || tableNames.value const tbNames = row.tableName || tableNames.value
if (tbNames == "") { if (tbNames == "") {
proxy.$modal.msgError("请选择要生成的数据") proxy.$modal.msgError("请选择要生成的数据")
@ -224,86 +180,86 @@ function handleGenTable(row) {
proxy.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath) proxy.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath)
}) })
} else { } else {
const zipName = Array.isArray(tbNames) ? "ruoyi.zip" : tbNames + ".zip" const zipName = Array.isArray(tbNames) ? "mmxt.zip" : tbNames + ".zip"
proxy.$download.zip("/tool/gen/batchGenCode?tables=" + tbNames, zipName) proxy.$download.zip("/tool/gen/batchGenCode?tables=" + tbNames, zipName)
} }
} }
/** 同步数据库操作 */ /** 同步数据库操作 */
function handleSynchDb(row) { function handleSynchDb(row) {
const tableName = row.tableName const tableName = row.tableName
proxy.$modal.confirm('确认要强制同步"' + tableName + '"表结构吗?').then(function () { proxy.$modal.confirm('确认要强制同步"' + tableName + '"表结构吗?').then(function () {
return synchDb(tableName) return synchDb(tableName)
}).then(() => { }).then(() => {
proxy.$modal.msgSuccess("同步成功") proxy.$modal.msgSuccess("同步成功")
}).catch(() => {}) }).catch(() => { })
} }
/** 打开导入表弹窗 */ /** 打开导入表弹窗 */
function openImportTable() { function openImportTable() {
proxy.$refs["importRef"].show() proxy.$refs["importRef"].show()
} }
/** 打开创建表弹窗 */ /** 打开创建表弹窗 */
function openCreateTable() { function openCreateTable() {
proxy.$refs["createRef"].show() proxy.$refs["createRef"].show()
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
function resetQuery() { function resetQuery() {
dateRange.value = [] dateRange.value = []
proxy.resetForm("queryRef") proxy.resetForm("queryRef")
queryParams.value.pageNum = 1 queryParams.value.pageNum = 1
proxy.$refs["genRef"].sort(defaultSort.value.prop, defaultSort.value.order) proxy.$refs["genRef"].sort(defaultSort.value.prop, defaultSort.value.order)
} }
/** 预览按钮 */ /** 预览按钮 */
function handlePreview(row) { function handlePreview(row) {
previewTable(row.tableId).then(response => { previewTable(row.tableId).then(response => {
preview.value.data = response.data preview.value.data = response.data
preview.value.open = true preview.value.open = true
preview.value.activeName = "domain.java" preview.value.activeName = "domain.java"
}) })
} }
/** 复制代码成功 */ /** 复制代码成功 */
function copyTextSuccess() { function copyTextSuccess() {
proxy.$modal.msgSuccess("复制成功") proxy.$modal.msgSuccess("复制成功")
} }
// //
function handleSelectionChange(selection) { function handleSelectionChange(selection) {
ids.value = selection.map(item => item.tableId) ids.value = selection.map(item => item.tableId)
tableNames.value = selection.map(item => item.tableName) tableNames.value = selection.map(item => item.tableName)
single.value = selection.length != 1 single.value = selection.length != 1
multiple.value = !selection.length multiple.value = !selection.length
} }
/** 排序触发事件 */ /** 排序触发事件 */
function handleSortChange(column, prop, order) { function handleSortChange(column, prop, order) {
queryParams.value.orderByColumn = column.prop queryParams.value.orderByColumn = column.prop
queryParams.value.isAsc = column.order queryParams.value.isAsc = column.order
getList() getList()
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
function handleEditTable(row) { function handleEditTable(row) {
const tableId = row.tableId || ids.value[0] const tableId = row.tableId || ids.value[0]
const tableName = row.tableName || tableNames.value[0] const tableName = row.tableName || tableNames.value[0]
const params = { pageNum: queryParams.value.pageNum } const params = { pageNum: queryParams.value.pageNum }
proxy.$tab.openPage("修改[" + tableName + "]生成配置", '/tool/gen-edit/index/' + tableId, params) proxy.$tab.openPage("修改[" + tableName + "]生成配置", '/tool/gen-edit/index/' + tableId, params)
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
function handleDelete(row) { function handleDelete(row) {
const tableIds = row.tableId || ids.value const tableIds = row.tableId || ids.value
proxy.$modal.confirm('是否确认删除表编号为"' + tableIds + '"的数据项?').then(function () { proxy.$modal.confirm('是否确认删除表编号为"' + tableIds + '"的数据项?').then(function () {
return delTable(tableIds) return delTable(tableIds)
}).then(() => { }).then(() => {
getList() getList()
proxy.$modal.msgSuccess("删除成功") proxy.$modal.msgSuccess("删除成功")
}).catch(() => {}) }).catch(() => { })
} }
getList() getList()
</script> </script>

7
vite.config.js

@ -2,7 +2,8 @@ import { defineConfig, loadEnv } from 'vite'
import path from 'path' import path from 'path'
import createVitePlugins from './vite/plugins' import createVitePlugins from './vite/plugins'
const baseUrl = 'http://localhost:8080' // 后端接口 const baseUrl = 'http://192.168.0.107:9020' // 后端接口
// const baseUrl = 'https://mmxt.arts-press.com/admin' // 后端接口
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig(({ mode, command }) => { export default defineConfig(({ mode, command }) => {
@ -11,8 +12,8 @@ export default defineConfig(({ mode, command }) => {
return { return {
// 部署生产环境和开发环境下的URL。 // 部署生产环境和开发环境下的URL。
// 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上 // 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。 // 例如 https://www.mmxt.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.mmxt.vip/admin/,则设置 baseUrl 为 /admin/。
base: VITE_APP_ENV === 'production' ? '/' : '/', base: VITE_APP_ENV === 'production' ? './' : '/',
plugins: createVitePlugins(env, command === 'build'), plugins: createVitePlugins(env, command === 'build'),
resolve: { resolve: {
// https://cn.vitejs.dev/config/#resolve-alias // https://cn.vitejs.dev/config/#resolve-alias

2
vite/plugins/compression.js

@ -6,7 +6,7 @@ export default function createCompression(env) {
if (VITE_BUILD_COMPRESS) { if (VITE_BUILD_COMPRESS) {
const compressList = VITE_BUILD_COMPRESS.split(',') const compressList = VITE_BUILD_COMPRESS.split(',')
if (compressList.includes('gzip')) { if (compressList.includes('gzip')) {
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件 // http://doc.mmxt.vip/mmxt-vue/other/faq.html#使用gzip解压缩静态文件
plugin.push( plugin.push(
compression({ compression({
ext: '.gz', ext: '.gz',

Loading…
Cancel
Save