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_BASE_API = '/dev-api'

4
.env.production

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

4
.env.staging

@ -1,10 +1,10 @@
# 页面标题
VITE_APP_TITLE = 若依管理系统
VITE_APP_TITLE = 美美学堂管理系统
# 生产环境配置
VITE_APP_ENV = 'staging'
# 若依管理系统/生产环境
# 美美学堂管理系统/生产环境
VITE_APP_BASE_API = '/stage-api'
# 是否在打包时开启压缩,支持 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)
Copyright (c) 2018 RuoYi
Copyright (c) 2018 mmxt
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

24
README.md

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

8
package.json

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

2
src/api/system/user.js

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

2
src/assets/styles/index.scss

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

119
src/components/HeaderSearch/index.vue

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

160
src/components/ImageUpload/index.vue

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

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

@ -5,9 +5,9 @@
</template>
<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)
}
}
</script>

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

@ -5,9 +5,9 @@
</template>
<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)
}
}
</script>

2
src/directive/common/copyText.js

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

8
src/directive/permission/hasPermi.js

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

8
src/directive/permission/hasRole.js

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

81
src/layout/components/Navbar.vue

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

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

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

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

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

2
src/main.js

@ -27,7 +27,7 @@ import './permission' // permission control
import { useDict } from '@/utils/dict'
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'

2
src/plugins/download.js

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

4
src/router/index.js

@ -1,4 +1,4 @@
import { createWebHistory, createRouter } from 'vue-router'
import { createWebHashHistory, createRouter } from 'vue-router'
/* Layout */
import Layout from '@/layout'
@ -161,7 +161,7 @@ export const dynamicRoutes = [
]
const router = createRouter({
history: createWebHistory(),
history: createWebHashHistory(),
routes: constantRoutes,
scrollBehavior(to, from, 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) {
let timeout, args, context, timestamp, result
const later = function() {
const later = function () {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
@ -235,7 +235,7 @@ export function debounce(func, wait, immediate) {
}
}
return function(...args) {
return function (...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout

10
src/utils/request.js

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

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

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

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

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

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

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

7
vite.config.js

@ -2,7 +2,8 @@ import { defineConfig, loadEnv } from 'vite'
import path from 'path'
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/
export default defineConfig(({ mode, command }) => {
@ -11,8 +12,8 @@ export default defineConfig(({ mode, command }) => {
return {
// 部署生产环境和开发环境下的URL。
// 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
base: VITE_APP_ENV === 'production' ? '/' : '/',
// 例如 https://www.mmxt.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.mmxt.vip/admin/,则设置 baseUrl 为 /admin/。
base: VITE_APP_ENV === 'production' ? './' : '/',
plugins: createVitePlugins(env, command === 'build'),
resolve: {
// 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) {
const compressList = VITE_BUILD_COMPRESS.split(',')
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(
compression({
ext: '.gz',

Loading…
Cancel
Save