1 Commits

Author SHA1 Message Date
RuoYi
8aca11c2a2 若依 3.6.6 2025-05-30 08:03:00 +08:00
29 changed files with 618 additions and 580 deletions

View File

@@ -1,11 +1,11 @@
<p align="center"> <p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-b99b286755aef70355a7084753f89cdb7c9.png"> <img alt="logo" src="https://oscimg.oschina.net/oscnet/up-b99b286755aef70355a7084753f89cdb7c9.png">
</p> </p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.6.5</h1> <h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.6.6</h1>
<h4 align="center">基于 Vue/Element UI 和 Spring Boot/Spring Cloud & Alibaba 前后端分离的分布式微服务架构</h4> <h4 align="center">基于 Vue/Element UI 和 Spring Boot/Spring Cloud & Alibaba 前后端分离的分布式微服务架构</h4>
<p align="center"> <p align="center">
<a href="https://gitee.com/y_project/RuoYi-Cloud/stargazers"><img src="https://gitee.com/y_project/RuoYi-Cloud/badge/star.svg?theme=dark"></a> <a href="https://gitee.com/y_project/RuoYi-Cloud/stargazers"><img src="https://gitee.com/y_project/RuoYi-Cloud/badge/star.svg?theme=dark"></a>
<a href="https://gitee.com/y_project/RuoYi-Cloud"><img src="https://img.shields.io/badge/RuoYi-v3.6.5-brightgreen.svg"></a> <a href="https://gitee.com/y_project/RuoYi-Cloud"><img src="https://img.shields.io/badge/RuoYi-v3.6.6-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Cloud/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a> <a href="https://gitee.com/y_project/RuoYi-Cloud/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p> </p>

View File

@@ -9,7 +9,7 @@ usage() {
# copy sql # copy sql
echo "begin copy sql " echo "begin copy sql "
cp ../sql/ry_20240629.sql ./mysql/db cp ../sql/ry_20250523.sql ./mysql/db
cp ../sql/ry_config_20250224.sql ./mysql/db cp ../sql/ry_config_20250224.sql ./mysql/db
# copy html # copy html

View File

@@ -6,14 +6,14 @@
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
<name>ruoyi</name> <name>ruoyi</name>
<url>http://www.ruoyi.vip</url> <url>http://www.ruoyi.vip</url>
<description>若依微服务系统</description> <description>若依微服务系统</description>
<properties> <properties>
<ruoyi.version>3.6.5</ruoyi.version> <ruoyi.version>3.6.6</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version> <java.version>1.8</java.version>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-api</artifactId> <artifactId>ruoyi-api</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-modules</artifactId> <artifactId>ruoyi-modules</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-modules</artifactId> <artifactId>ruoyi-modules</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-modules</artifactId> <artifactId>ruoyi-modules</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-modules</artifactId> <artifactId>ruoyi-modules</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -1,6 +1,6 @@
{ {
"name": "ruoyi", "name": "ruoyi",
"version": "3.6.5", "version": "3.6.6",
"description": "若依管理系统", "description": "若依管理系统",
"author": "若依", "author": "若依",
"license": "MIT", "license": "MIT",

View File

@@ -1,210 +1,208 @@
<template> <template>
<div class="navbar"> <div class="navbar">
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container" /> <breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container" />
<top-nav v-if="topNav" id="topmenu-container" class="topmenu-container" /> <top-nav v-if="topNav" id="topmenu-container" class="topmenu-container" />
<div class="right-menu"> <div class="right-menu">
<template v-if="device!=='mobile'"> <template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" /> <search id="header-search" class="right-menu-item" />
<el-tooltip content="源码地址" effect="dark" placement="bottom"> <el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /> <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip> </el-tooltip>
<el-tooltip content="文档地址" effect="dark" placement="bottom"> <el-tooltip content="文档地址" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" /> <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip> </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">
<size-select id="size-select" class="right-menu-item hover-effect" /> <size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip> </el-tooltip>
</template> </template>
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover"> <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover">
<div class="avatar-wrapper"> <div class="avatar-wrapper">
<img :src="avatar" class="user-avatar"> <img :src="avatar" class="user-avatar">
<span class="user-nickname"> {{ nickName }} </span> <span class="user-nickname"> {{ nickName }} </span>
</div> </div>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<router-link to="/user/profile"> <router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item> <el-dropdown-item>个人中心</el-dropdown-item>
</router-link> </router-link>
<el-dropdown-item divided @click.native="logout"> <el-dropdown-item divided @click.native="logout">
<span>退出登录</span> <span>退出登录</span>
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
<div class="right-menu-item hover-effect setting" @click="setLayout" v-if="setting"> <div class="right-menu-item hover-effect setting" @click="setLayout" v-if="setting">
<svg-icon icon-class="more-up" /> <svg-icon icon-class="more-up" />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb' import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav' import TopNav from '@/components/TopNav'
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 Search from '@/components/HeaderSearch' import Search from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git' import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc' import RuoYiDoc from '@/components/RuoYi/Doc'
export default { export default {
emits: ['setLayout'], emits: ['setLayout'],
components: { components: {
Breadcrumb, Breadcrumb,
TopNav, TopNav,
Hamburger, Hamburger,
Screenfull, Screenfull,
SizeSelect, SizeSelect,
Search, Search,
RuoYiGit, RuoYiGit,
RuoYiDoc RuoYiDoc
}, },
computed: { computed: {
...mapGetters([ ...mapGetters([
'sidebar', 'sidebar',
'avatar', 'avatar',
'device', 'device',
'nickName' 'nickName'
]), ]),
setting: { setting: {
get() { get() {
return this.$store.state.settings.showSettings return this.$store.state.settings.showSettings
} }
}, },
topNav: { topNav: {
get() { get() {
return this.$store.state.settings.topNav return this.$store.state.settings.topNav
} }
} }
}, },
methods: { methods: {
toggleSideBar() { toggleSideBar() {
this.$store.dispatch('app/toggleSideBar') this.$store.dispatch('app/toggleSideBar')
}, },
setLayout(event) { setLayout(event) {
this.$emit('setLayout') this.$emit('setLayout')
}, },
logout() { logout() {
this.$confirm('确定注销并退出系统吗?', '提示', { this.$confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
this.$store.dispatch('LogOut').then(() => { this.$store.dispatch('LogOut').then(() => {
// 使用 BASE_PATH 拼接路径 location.href = '/index'
const basePath = process.env.VUE_APP_BASE_PATH || '/'; // 优先从环境变量读取 })
location.href = `${basePath}index`; }).catch(() => {})
}) }
}).catch(() => {}) }
} }
} </script>
}
</script> <style lang="scss" scoped>
.navbar {
<style lang="scss" scoped> height: 50px;
.navbar { overflow: hidden;
height: 50px; position: relative;
overflow: hidden; background: #fff;
position: relative; box-shadow: 0 1px 4px rgba(0,21,41,.08);
background: #fff;
box-shadow: 0 1px 4px rgba(0,21,41,.08); .hamburger-container {
line-height: 46px;
.hamburger-container { height: 100%;
line-height: 46px; float: left;
height: 100%; cursor: pointer;
float: left; transition: background .3s;
cursor: pointer; -webkit-tap-highlight-color:transparent;
transition: background .3s;
-webkit-tap-highlight-color:transparent; &:hover {
background: rgba(0, 0, 0, .025)
&:hover { }
background: rgba(0, 0, 0, .025) }
}
} .breadcrumb-container {
float: left;
.breadcrumb-container { }
float: left;
} .topmenu-container {
position: absolute;
.topmenu-container { left: 50px;
position: absolute; }
left: 50px;
} .errLog-container {
display: inline-block;
.errLog-container { vertical-align: top;
display: inline-block; }
vertical-align: top;
} .right-menu {
float: right;
.right-menu { height: 100%;
float: right; line-height: 50px;
height: 100%;
line-height: 50px; &:focus {
outline: none;
&:focus { }
outline: none;
} .right-menu-item {
display: inline-block;
.right-menu-item { padding: 0 8px;
display: inline-block; height: 100%;
padding: 0 8px; font-size: 18px;
height: 100%; color: #5a5e66;
font-size: 18px; vertical-align: text-bottom;
color: #5a5e66;
vertical-align: text-bottom; &.hover-effect {
cursor: pointer;
&.hover-effect { transition: background .3s;
cursor: pointer;
transition: background .3s; &:hover {
background: rgba(0, 0, 0, .025)
&:hover { }
background: rgba(0, 0, 0, .025) }
} }
}
} .avatar-container {
margin-right: 0px;
.avatar-container { padding-right: 0px;
margin-right: 0px;
padding-right: 0px; .avatar-wrapper {
margin-top: 10px;
.avatar-wrapper { position: relative;
margin-top: 10px;
position: relative; .user-avatar {
cursor: pointer;
.user-avatar { width: 30px;
cursor: pointer; height: 30px;
width: 30px; border-radius: 50%;
height: 30px; }
border-radius: 50%;
} .user-nickname{
position: relative;
.user-nickname{ bottom: 10px;
position: relative; font-size: 14px;
bottom: 10px; font-weight: bold;
font-size: 14px; }
font-weight: bold;
} .el-icon-caret-bottom {
cursor: pointer;
.el-icon-caret-bottom { position: absolute;
cursor: pointer; right: -20px;
position: absolute; top: 25px;
right: -20px; font-size: 12px;
top: 25px; }
font-size: 12px; }
} }
} }
} }
} </style>
}
</style>

View File

@@ -1,186 +1,183 @@
import Vue from 'vue' import Vue from 'vue'
import Router from 'vue-router' import Router from 'vue-router'
Vue.use(Router) Vue.use(Router)
/* Layout */ /* Layout */
import Layout from '@/layout' import Layout from '@/layout'
/** /**
* Note: 路由配置项 * Note: 路由配置项
* *
* hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401login等页面或者如一些编辑页面/edit/1 * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401login等页面或者如一些编辑页面/edit/1
* alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时自动会变成嵌套的模式--如组件页面 * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时自动会变成嵌套的模式--如组件页面
* // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
* // 若你想不管路由下面的 children 声明的个数都显示你的根路由 * // 若你想不管路由下面的 children 声明的个数都显示你的根路由
* // 你可以设置 alwaysShow: true这样它就会忽略之前定义的规则一直显示根路由 * // 你可以设置 alwaysShow: true这样它就会忽略之前定义的规则一直显示根路由
* redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
* name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题 * name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
* query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
* roles: ['admin', 'common'] // 访问路由的角色权限 * roles: ['admin', 'common'] // 访问路由的角色权限
* permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限
* meta : { * meta : {
noCache: true // 如果设置为true则不会被 <keep-alive> 缓存(默认 false) noCache: true // 如果设置为true则不会被 <keep-alive> 缓存(默认 false)
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
icon: 'svg-name' // 设置该路由的图标对应路径src/assets/icons/svg icon: 'svg-name' // 设置该路由的图标对应路径src/assets/icons/svg
breadcrumb: false // 如果设置为false则不会在breadcrumb面包屑中显示 breadcrumb: false // 如果设置为false则不会在breadcrumb面包屑中显示
activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。
} }
*/ */
// 公共路由 // 公共路由
export const constantRoutes = [ export const constantRoutes = [
{ {
path: '/redirect', path: '/redirect',
component: Layout, component: Layout,
hidden: true, hidden: true,
children: [ children: [
{ {
path: '/redirect/:path(.*)', path: '/redirect/:path(.*)',
component: () => import('@/views/redirect') component: () => import('@/views/redirect')
} }
] ]
}, },
{ {
path: '/login', path: '/login',
component: () => import('@/views/login'), component: () => import('@/views/login'),
hidden: true hidden: true
}, },
{ {
path: '/register', path: '/register',
component: () => import('@/views/register'), component: () => import('@/views/register'),
hidden: true hidden: true
}, },
{ {
path: '/404', path: '/404',
component: () => import('@/views/error/404'), component: () => import('@/views/error/404'),
hidden: true hidden: true
}, },
{ {
path: '/401', path: '/401',
component: () => import('@/views/error/401'), component: () => import('@/views/error/401'),
hidden: true hidden: true
}, },
{ {
path: '', path: '',
component: Layout, component: Layout,
redirect: 'index', redirect: 'index',
children: [ children: [
{ {
path: 'index', path: 'index',
component: () => import('@/views/index'), component: () => import('@/views/index'),
name: 'Index', name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true } meta: { title: '首页', icon: 'dashboard', affix: true }
} }
] ]
}, },
{ {
path: '/user', path: '/user',
component: Layout, component: Layout,
hidden: true, hidden: true,
redirect: 'noredirect', redirect: 'noredirect',
children: [ children: [
{ {
path: 'profile', path: 'profile',
component: () => import('@/views/system/user/profile/index'), component: () => import('@/views/system/user/profile/index'),
name: 'Profile', name: 'Profile',
meta: { title: '个人中心', icon: 'user' } meta: { title: '个人中心', icon: 'user' }
} }
] ]
} }
] ]
// 动态路由,基于用户权限动态去加载 // 动态路由,基于用户权限动态去加载
export const dynamicRoutes = [ export const dynamicRoutes = [
{ {
path: '/system/user-auth', path: '/system/user-auth',
component: Layout, component: Layout,
hidden: true, hidden: true,
permissions: ['system:user:edit'], permissions: ['system:user:edit'],
children: [ children: [
{ {
path: 'role/:userId(\\d+)', path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole'), component: () => import('@/views/system/user/authRole'),
name: 'AuthRole', name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user' } meta: { title: '分配角色', activeMenu: '/system/user' }
} }
] ]
}, },
{ {
path: '/system/role-auth', path: '/system/role-auth',
component: Layout, component: Layout,
hidden: true, hidden: true,
permissions: ['system:role:edit'], permissions: ['system:role:edit'],
children: [ children: [
{ {
path: 'user/:roleId(\\d+)', path: 'user/:roleId(\\d+)',
component: () => import('@/views/system/role/authUser'), component: () => import('@/views/system/role/authUser'),
name: 'AuthUser', name: 'AuthUser',
meta: { title: '分配用户', activeMenu: '/system/role' } meta: { title: '分配用户', activeMenu: '/system/role' }
} }
] ]
}, },
{ {
path: '/system/dict-data', path: '/system/dict-data',
component: Layout, component: Layout,
hidden: true, hidden: true,
permissions: ['system:dict:list'], permissions: ['system:dict:list'],
children: [ children: [
{ {
path: 'index/:dictId(\\d+)', path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data'), component: () => import('@/views/system/dict/data'),
name: 'Data', name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict' } meta: { title: '字典数据', activeMenu: '/system/dict' }
} }
] ]
}, },
{ {
path: '/monitor/job-log', path: '/monitor/job-log',
component: Layout, component: Layout,
hidden: true, hidden: true,
permissions: ['monitor:job:list'], permissions: ['monitor:job:list'],
children: [ children: [
{ {
path: 'index/:jobId(\\d+)', path: 'index/:jobId(\\d+)',
component: () => import('@/views/monitor/job/log'), component: () => import('@/views/monitor/job/log'),
name: 'JobLog', name: 'JobLog',
meta: { title: '调度日志', activeMenu: '/monitor/job' } meta: { title: '调度日志', activeMenu: '/monitor/job' }
} }
] ]
}, },
{ {
path: '/tool/gen-edit', path: '/tool/gen-edit',
component: Layout, component: Layout,
hidden: true, hidden: true,
permissions: ['tool:gen:edit'], permissions: ['tool:gen:edit'],
children: [ children: [
{ {
path: 'index/:tableId(\\d+)', path: 'index/:tableId(\\d+)',
component: () => import('@/views/tool/gen/editTable'), component: () => import('@/views/tool/gen/editTable'),
name: 'GenEdit', name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen' } meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
} }
] ]
} }
] ]
// 防止连续点击多次路由报错 // 防止连续点击多次路由报错
let routerPush = Router.prototype.push let routerPush = Router.prototype.push
let routerReplace = Router.prototype.replace let routerReplace = Router.prototype.replace
// push // push
Router.prototype.push = function push(location) { Router.prototype.push = function push(location) {
return routerPush.call(this, location).catch(err => err) return routerPush.call(this, location).catch(err => err)
} }
// replace // replace
Router.prototype.replace = function push(location) { Router.prototype.replace = function push(location) {
return routerReplace.call(this, location).catch(err => err) return routerReplace.call(this, location).catch(err => err)
} }
export default new Router({ export default new Router({
mode: 'history', // 去掉url中的# mode: 'history', // 去掉url中的#
// 默认情况下Vue CLI 会假设你的应用是被部署在一个域名的根路径上 scrollBehavior: () => ({ y: 0 }),
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。 routes: constantRoutes
base: process.env.NODE_ENV === "production" ? "/" : "/", })
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})

View File

@@ -1,156 +1,152 @@
import axios from 'axios' import axios from 'axios'
import { Notification, MessageBox, Message, Loading } from 'element-ui' import { Notification, MessageBox, Message, Loading } from 'element-ui'
import store from '@/store' import store from '@/store'
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/ruoyi"
import cache from '@/plugins/cache' import cache from '@/plugins/cache'
import { saveAs } from 'file-saver' import { saveAs } from 'file-saver'
// 默认情况下Vue CLI 会假设你的应用是被部署在一个域名的根路径上 let downloadLoadingInstance
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。 // 是否显示重新登录
export const BASE_PATH = '/' export let isRelogin = { show: false }
let downloadLoadingInstance axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 是否显示重新登录 // 创建axios实例
export let isRelogin = { show: false } const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' baseURL: process.env.VUE_APP_BASE_API,
// 创建axios实例 // 超时
const service = axios.create({ timeout: 10000
// axios中请求配置有baseURL选项表示请求URL公共部分 })
baseURL: process.env.VUE_APP_BASE_API,
// 超时 // request拦截器
timeout: 10000 service.interceptors.request.use(config => {
}) // 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
// request拦截器 // 是否需要防止数据重复提交
service.interceptors.request.use(config => { const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
// 是否需要设置 token if (getToken() && !isToken) {
const isToken = (config.headers || {}).isToken === false config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
// 是否需要防止数据重复提交 }
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false // get请求映射params参数
if (getToken() && !isToken) { if (config.method === 'get' && config.params) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 let url = config.url + '?' + tansParams(config.params)
} url = url.slice(0, -1)
// get请求映射params参数 config.params = {}
if (config.method === 'get' && config.params) { config.url = url
let url = config.url + '?' + tansParams(config.params) }
url = url.slice(0, -1) if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
config.params = {} const requestObj = {
config.url = url url: config.url,
} data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { time: new Date().getTime()
const requestObj = { }
url: config.url, const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, const limitSize = 5 * 1024 * 1024 // 限制存放数据5M
time: new Date().getTime() if (requestSize >= limitSize) {
} console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制无法进行防重复提交验证。')
const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小 return config
const limitSize = 5 * 1024 * 1024 // 限制存放数据5M }
if (requestSize >= limitSize) { const sessionObj = cache.session.getJSON('sessionObj')
console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制无法进行防重复提交验证。') if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
return config cache.session.setJSON('sessionObj', requestObj)
} } else {
const sessionObj = cache.session.getJSON('sessionObj') const s_url = sessionObj.url // 请求地址
if (sessionObj === undefined || sessionObj === null || sessionObj === '') { const s_data = sessionObj.data // 请求数据
cache.session.setJSON('sessionObj', requestObj) const s_time = sessionObj.time // 请求时间
} else { const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交
const s_url = sessionObj.url // 请求地址 if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const s_data = sessionObj.data // 请求数据 const message = '数据正在处理,请勿重复提交'
const s_time = sessionObj.time // 请求时间 console.warn(`[${s_url}]: ` + message)
const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交 return Promise.reject(new Error(message))
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { } else {
const message = '数据正在处理,请勿重复提交' cache.session.setJSON('sessionObj', requestObj)
console.warn(`[${s_url}]: ` + message) }
return Promise.reject(new Error(message)) }
} else { }
cache.session.setJSON('sessionObj', requestObj) return config
} }, error => {
} console.log(error)
} Promise.reject(error)
return config })
}, error => {
console.log(error) // 响应拦截器
Promise.reject(error) service.interceptors.response.use(res => {
}) // 未设置状态码则默认成功状态
const code = res.data.code || 200
// 响应拦截器 // 获取错误信息
service.interceptors.response.use(res => { const msg = errorCode[code] || res.data.msg || errorCode['default']
// 未设置状态码则默认成功状态 // 二进制数据则直接返回
const code = res.data.code || 200 if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
// 获取错误信息 return res.data
const msg = errorCode[code] || res.data.msg || errorCode['default'] }
// 二进制数据则直接返回 if (code === 401) {
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { if (!isRelogin.show) {
return res.data isRelogin.show = true
} MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
if (code === 401) { isRelogin.show = false
if (!isRelogin.show) { store.dispatch('LogOut').then(() => {
isRelogin.show = true location.href = '/index'
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { })
isRelogin.show = false }).catch(() => {
store.dispatch('LogOut').then(() => { isRelogin.show = false
location.href = '/index' })
}) }
}).catch(() => { return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
isRelogin.show = false } else if (code === 500) {
}) Message({ message: msg, type: 'error' })
} return Promise.reject(new Error(msg))
return Promise.reject('无效的会话,或者会话已过期,请重新登录。') } else if (code === 601) {
} else if (code === 500) { Message({ message: msg, type: 'warning' })
Message({ message: msg, type: 'error' }) return Promise.reject('error')
return Promise.reject(new Error(msg)) } else if (code !== 200) {
} else if (code === 601) { Notification.error({ title: msg })
Message({ message: msg, type: 'warning' }) return Promise.reject('error')
return Promise.reject('error') } else {
} else if (code !== 200) { return res.data
Notification.error({ title: msg }) }
return Promise.reject('error') },
} else { error => {
return res.data console.log('err' + error)
} let { message } = error
}, if (message == "Network Error") {
error => { message = "后端接口连接异常"
console.log('err' + error) } else if (message.includes("timeout")) {
let { message } = error message = "系统接口请求超时"
if (message == "Network Error") { } else if (message.includes("Request failed with status code")) {
message = "后端接口连接异常" message = "系统接口" + message.substr(message.length - 3) + "异常"
} else if (message.includes("timeout")) { }
message = "系统接口请求超时" Message({ message: message, type: 'error', duration: 5 * 1000 })
} else if (message.includes("Request failed with status code")) { return Promise.reject(error)
message = "系统接口" + message.substr(message.length - 3) + "异常" }
} )
Message({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error) // 通用下载方法
} export function download(url, params, filename, config) {
) downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
return service.post(url, params, {
// 通用下载方法 transformRequest: [(params) => { return tansParams(params) }],
export function download(url, params, filename, config) { headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) responseType: 'blob',
return service.post(url, params, { ...config
transformRequest: [(params) => { return tansParams(params) }], }).then(async (data) => {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, const isBlob = blobValidate(data)
responseType: 'blob', if (isBlob) {
...config const blob = new Blob([data])
}).then(async (data) => { saveAs(blob, filename)
const isBlob = blobValidate(data) } else {
if (isBlob) { const resText = await data.text()
const blob = new Blob([data]) const rspObj = JSON.parse(resText)
saveAs(blob, filename) const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
} else { Message.error(errMsg)
const resText = await data.text() }
const rspObj = JSON.parse(resText) downloadLoadingInstance.close()
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] }).catch((r) => {
Message.error(errMsg) console.error(r)
} Message.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close() downloadLoadingInstance.close()
}).catch((r) => { })
console.error(r) }
Message.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close() export default service
})
}
export default service

View File

@@ -108,6 +108,53 @@
<span>更新日志</span> <span>更新日志</span>
</div> </div>
<el-collapse accordion> <el-collapse accordion>
<el-collapse-item title="v3.6.6 - 2025-05-30">
<ol>
<li>优化菜单搜索查询页</li>
<li>导航栏显示昵称&设置</li>
<li>用户管理支持分栏拖动</li>
<li>修改主题样式本地读取</li>
<li>菜单管理新增路由名称</li>
<li>添加底部版权信息&开关</li>
<li>分配角色禁用不允许勾选</li>
<li>添加页签图标显示开关功能</li>
<li>用户管理过滤掉已禁用部门</li>
<li>上传组件新增拖动排序属性</li>
<li>显隐列组件支持全选/全不选</li>
<li>白名单支持对通配符路径匹配</li>
<li>初始密码支持自定义修改策略</li>
<li>账号密码支持自定义更新周期</li>
<li>菜单面包屑导航支持多层级显示</li>
<li>支持富文本复制粘贴图片上传至url</li>
<li>支持文件&图片组件自定义地址&参数</li>
<li>更新ry-config的nacos表结构到最新版本</li>
<li>代码生成新增配置是否允许文件覆盖到本地</li>
<li>使用CacheRequestBody代替CacheRequestFilter</li>
<li>升级tomcat到最新版本9.0.105</li>
<li>升级fastjson到最新版2.0.57</li>
<li>升级commons.io到最新版本2.19.0</li>
<li>package.json移除runjs&eslint&vue-meta依赖</li>
<li>修复导出子列表对象只能在最后的问题</li>
<li>修复TopNav无法正确获取active的问题</li>
<li>修复默认关闭Tags-Views内链页面打不开</li>
<li>Excel注解支持wrapText是否允许内容换行</li>
<li>优化文件上传组件新增类型</li>
<li>优化导入带标题文件关闭清理</li>
<li>优化参数键值更换为多行文本</li>
<li>优化特殊字符密码修改失败问题</li>
<li>优化代码生成列表支持按时间排序</li>
<li>优化TopNav内链菜单点击没有高亮</li>
<li>优化文件异常输入流未关闭的问题</li>
<li>优化菜单管理切换Mini布局错乱问题</li>
<li>优化空指针异常时无法获取错误信息问题</li>
<li>优化文件&图片上传组件新增disabled属性</li>
<li>优化isAdmin方法,避免脱敏模块security依赖</li>
<li>优化定时任务字符包含多个括号导致数据错误</li>
<li>优化登录&注册页表头使用VUE_APP_TITLE配置值</li>
<li>优化导出Excel日期格式双击离开后与设定的格式不一致问题</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.6.5 - 2024-11-13"> <el-collapse-item title="v3.6.5 - 2024-11-13">
<ol> <ol>
<li>使用SpringDoc代替Swagger</li> <li>使用SpringDoc代替Swagger</li>
@@ -909,7 +956,7 @@ export default {
data() { data() {
return { return {
// 版本号 // 版本号
version: "3.6.5" version: "3.6.6"
} }
}, },
methods: { methods: {

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-visual</artifactId> <artifactId>ruoyi-visual</artifactId>
<version>3.6.5</version> <version>3.6.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>