1
0
forked from ruoyi/RuoYi-Vue3

14 Commits

Author SHA1 Message Date
RuoYi
686fd011a9 优化topbar顶部菜单样式 2025-12-18 14:40:52 +08:00
RuoYi
63cfd07f74 若依 3.9.1 2025-12-18 09:07:10 +08:00
RuoYi
d8ef2c3dde 默认固定头部 2025-12-18 09:03:24 +08:00
RuoYi
e430dae0dc 优化字典组件值宽松匹配 2025-12-16 16:41:30 +08:00
RuoYi
88ae28cf5a 优化字典组件数字类型值处理逻辑 2025-12-16 15:46:44 +08:00
RuoYi
eee40b33ba 优化index页面标题读取配置 2025-12-16 14:51:07 +08:00
RuoYi
f6db068c14 优化日志详细请求参数显示 2025-12-16 14:48:26 +08:00
RuoYi
73efecc5c2 菜单导航设置支持纯顶部 2025-12-16 11:41:45 +08:00
RuoYi
168ad6a3b6 行内表单默认设置固定宽度 2025-12-10 09:37:56 +08:00
RuoYi
d2b4332589 优化表单构建关闭页签销毁复制插件 2025-12-04 13:03:38 +08:00
RuoYi
b086e172ae 优化生成代码下载的zip文件名 2025-12-03 10:26:48 +08:00
RuoYi
e7d8010390 登录/注册页面底部版权信息修改为读取配置 2025-12-02 15:29:17 +08:00
RuoYi
b51b7cfa04 修正命名Default 2025-12-02 13:11:06 +08:00
RuoYi
199c9951f5 修复表单构建移除所有控件后切换路由回来空白问题 2025-12-02 13:07:15 +08:00
23 changed files with 416 additions and 80 deletions

View File

@@ -1,11 +1,11 @@
<p align="center"> <p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png"> <img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p> </p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.0</h1> <h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.9.1</h1>
<h4 align="center">基于SpringBoot+Vue3前后端分离的Java快速开发框架</h4> <h4 align="center">基于SpringBoot+Vue3前后端分离的Java快速开发框架</h4>
<p align="center"> <p align="center">
<a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a> <a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.0-brightgreen.svg"></a> <a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.9.1-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a> <a href="https://gitee.com/y_project/RuoYi-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p> </p>

View File

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

View File

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

View File

@@ -60,6 +60,14 @@
color: inherit; color: inherit;
} }
.el-form--inline {
.el-form-item {
.el-input, .el-cascader, .el-select, .el-autocomplete {
width: 200px;
}
}
}
.el-form .el-form-item__label { .el-form .el-form-item__label {
font-weight: 700; font-weight: 700;
} }
@@ -148,6 +156,16 @@
width: inherit; 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 { .el-table .el-dropdown-link {
cursor: pointer; cursor: pointer;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,12 @@
<template> <template>
<div class="navbar"> <div class="navbar" :class="'nav' + settingsStore.navType">
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" /> <breadcrumb v-if="settingsStore.navType == 1" id="breadcrumb-container" class="breadcrumb-container" />
<top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" /> <top-nav v-if="settingsStore.navType == 2" id="topmenu-container" class="topmenu-container" />
<template v-if="settingsStore.navType == 3">
<logo v-show="settingsStore.sidebarLogo" :collapse="false"></logo>
<top-bar id="topbar-container" class="topbar-container" />
</template>
<div class="right-menu"> <div class="right-menu">
<template v-if="appStore.device !== 'mobile'"> <template v-if="appStore.device !== 'mobile'">
@@ -57,6 +61,8 @@
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import Breadcrumb from '@/components/Breadcrumb' import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav' import TopNav from '@/components/TopNav'
import TopBar from './TopBar'
import Logo from './Sidebar/Logo'
import Hamburger from '@/components/Hamburger' import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull' import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect' import SizeSelect from '@/components/SizeSelect'
@@ -111,20 +117,33 @@ function toggleTheme() {
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
.navbar.nav3 {
.hamburger-container {
display: none !important;
}
}
.navbar { .navbar {
height: 50px; height: 50px;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
background: var(--navbar-bg); background: var(--navbar-bg);
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
display: flex;
align-items: center;
// padding: 0 8px;
box-sizing: border-box;
.hamburger-container { .hamburger-container {
line-height: 46px; line-height: 46px;
height: 100%; height: 100%;
float: left;
cursor: pointer; cursor: pointer;
transition: background 0.3s; transition: background 0.3s;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
display: flex;
align-items: center;
flex-shrink: 0;
margin-right: 8px;
&:hover { &:hover {
background: rgba(0, 0, 0, 0.025); background: rgba(0, 0, 0, 0.025);
@@ -132,7 +151,7 @@ function toggleTheme() {
} }
.breadcrumb-container { .breadcrumb-container {
float: left; flex-shrink: 0;
} }
.topmenu-container { .topmenu-container {
@@ -140,16 +159,26 @@ function toggleTheme() {
left: 50px; left: 50px;
} }
.topbar-container {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
overflow: hidden;
margin-left: 8px;
}
.errLog-container { .errLog-container {
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
} }
.right-menu { .right-menu {
float: right;
height: 100%; height: 100%;
line-height: 50px; line-height: 50px;
display: flex; display: flex;
align-items: center;
margin-left: auto;
&:focus { &:focus {
outline: none; outline: none;

View File

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

View File

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

View File

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

View File

@@ -15,9 +15,9 @@ export default {
showSettings: true, showSettings: true,
/** /**
* 是否显示顶部导航 * 菜单导航模式 1、纯左侧 2、混合左侧+顶部) 3、纯顶部
*/ */
topNav: false, navType: 1,
/** /**
* 是否显示 tagsView * 是否显示 tagsView
@@ -32,7 +32,7 @@ export default {
/** /**
* 是否固定头部 * 是否固定头部
*/ */
fixedHeader: false, fixedHeader: true,
/** /**
* 是否显示logo * 是否显示logo

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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