【升级】v3.1.0已更新,更新细节说明可查看对应公众号推文

pull/228/head v3.1.0
俞宝山 2024-07-23 02:14:22 +08:00
parent b2da2975f4
commit 92adbfb5a1
31 changed files with 636 additions and 274 deletions

View File

@ -12,7 +12,7 @@
.app-loading__logo {margin-bottom: 30px;} .app-loading__logo {margin-bottom: 30px;}
.app-loading__logo img {width: 90px;vertical-align: bottom;} .app-loading__logo img {width: 90px;vertical-align: bottom;}
.app-loading__title {font-size: 24px;color: #333;margin-top: 30px;} .app-loading__title {font-size: 24px;color: #333;margin-top: 30px;}
.h-full { height: 100% } .app-main { height: 100% }
@keyframes loader { @keyframes loader {
0% { 0% {
transform: rotate(0deg); transform: rotate(0deg);
@ -29,7 +29,7 @@
<strong>We're sorry but Snowy2.0 doesn't work properly without JavaScript <strong>We're sorry but Snowy2.0 doesn't work properly without JavaScript
enabled. Please enable it to continue.</strong> enabled. Please enable it to continue.</strong>
</noscript> </noscript>
<div id="app" class="h-full"></div> <div id="app" class="app-main"></div>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
</body> </body>
</html> </html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

View File

@ -415,9 +415,12 @@
} else { } else {
data.localDataSource = r.records data.localDataSource = r.records
} }
data.localLoading = false
getTableProps() getTableProps()
}) })
.catch(() => {})
.finally(() => {
data.localLoading = false
})
} }
} }

View File

@ -19,7 +19,7 @@
<div class="right-menu-item" @click="closeOtherTabs"> <div class="right-menu-item" @click="closeOtherTabs">
<close-outlined class="snowy-header-tags-right" /> <close-outlined class="snowy-header-tags-right" />
<div class="pl-3 snowy-header-tags-right-font">关闭其他标签</div> <div class="pl-3 snowy-header-tags-right-font">关闭其他</div>
</div> </div>
<div class="right-menu-item" @click="maximize"> <div class="right-menu-item" @click="maximize">
@ -28,7 +28,7 @@
</div> </div>
<div class="right-menu-item" @click="openWindow"> <div class="right-menu-item" @click="openWindow">
<select-outlined class="snowy-header-tags-right" /> <select-outlined class="snowy-header-tags-right" />
<div class="pl-3 snowy-header-tags-right-font">新窗口打开</div> <div class="pl-3 snowy-header-tags-right-font">新窗口</div>
</div> </div>
</xn-context-menu> </xn-context-menu>
<a-tabs <a-tabs
@ -366,7 +366,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
text-align: center; text-align: center;
padding: 10px 20px; padding: 10px 10px;
color: #333; color: #333;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
@ -433,7 +433,7 @@
opacity: 0; opacity: 0;
} }
.snowy-header-tags-right { .snowy-header-tags-right {
margin-right: 10px; margin-right: 5px;
color: var(--font-color); color: var(--font-color);
} }
.snowy-header-tags-right-font { .snowy-header-tags-right-font {

View File

@ -72,7 +72,7 @@
</template> </template>
<script setup> <script setup>
import { globalStore, keepAliveStore } from '@/store' import { globalStore, keepAliveStore, viewTagsStore } from '@/store'
import { themeEnum } from '@/layout/enum/themeEnum' import { themeEnum } from '@/layout/enum/themeEnum'
import { layoutEnum } from '@/layout/enum/layoutEnum' import { layoutEnum } from '@/layout/enum/layoutEnum'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
@ -82,6 +82,8 @@
import DoubleRowMenu from '@/layout/menu/doubleRowMenu.vue' import DoubleRowMenu from '@/layout/menu/doubleRowMenu.vue'
import TopMenu from '@/layout/menu/topMenu.vue' import TopMenu from '@/layout/menu/topMenu.vue'
import { NextLoading } from '@/utils/loading' import { NextLoading } from '@/utils/loading'
import { useMenuStore } from '@/store/menu'
import { userStore } from '@/store/user'
const store = globalStore() const store = globalStore()
const kStore = keepAliveStore() const kStore = keepAliveStore()
@ -156,7 +158,13 @@
}) })
// //
const showThis = () => { const showThis = () => {
pMenu.value = route.meta.breadcrumb ? route.meta.breadcrumb[0] : {} // route使 useRouter
router.getRoutes().filter((item) => {
if (item.path === route.path) {
pMenu.value = item.meta.breadcrumb ? item.meta.breadcrumb[0] : {}
}
})
// pMenu.value = route.meta.breadcrumb ? route.meta.breadcrumb[0] : {}
// //
nextTick(() => { nextTick(() => {
// //
@ -199,26 +207,39 @@
} }
}) })
} }
const init = () => {
// -start // -start
moduleMenu.value = router.getMenu() moduleMenu.value = router.getMenu()
// //
const menuModuleId = tool.data.get('SNOWY_MENU_MODULE_ID') const menuModuleId = tool.data.get('SNOWY_MENU_MODULE_ID')
if (menuModuleId) { if (menuModuleId) {
// //
const module = router.getMenu().filter((item) => item.id === menuModuleId) const module = router.getMenu().filter((item) => item.id === menuModuleId)
if (module.length > 0) { if (module.length > 0) {
menu.value = module[0].children menu.value = module[0].children
} else {
menu.value = router.getMenu()[0].children
}
} else { } else {
menu.value = router.getMenu()[0].children menu.value = router.getMenu()[0].children
} }
} else { showThis()
menu.value = router.getMenu()[0].children
} }
showThis()
watchEffect(() => {
const menuStore = useMenuStore()
if (menuStore.refreshFlag) {
init()
//
viewTagsStore().updateOrRemoveViewTags(router.getRoutes())
menuStore.changeRefreshFlag(false)
}
})
onMounted(() => { onMounted(() => {
// loading // loading
NextLoading.done() NextLoading.done()
showThis()
onLayoutResize() onLayoutResize()
window.addEventListener('resize', onLayoutResize) window.addEventListener('resize', onLayoutResize)
window.addEventListener('resize', getNav) window.addEventListener('resize', getNav)
@ -227,6 +248,8 @@
settingFixedWidth() settingFixedWidth()
nextTick(() => { nextTick(() => {
getNav(menu.value) getNav(menu.value)
//
userStore().refreshUserLoginUserInfo()
}) })
}) })
// //

View File

@ -8,11 +8,13 @@
</template> </template>
<script setup> <script setup>
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useMenuStore } from '@/store/menu'
const router = useRouter() const router = useRouter()
const gohome = () => { const gohome = () => {
location.href = '/' location.href = '/'
} }
const goback = () => { const goback = () => {
router.go(-1) router.go(-1)
useMenuStore().changeRefreshFlag(true)
} }
</script> </script>

View File

@ -18,9 +18,9 @@ import whiteListRouters from './whiteList'
import userRoutes from '@/config/route' import userRoutes from '@/config/route'
import tool from '@/utils/tool' import tool from '@/utils/tool'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
const modules = import.meta.glob('/src/views/**/**.vue') import { globalStore } from '@/store'
import { globalStore, searchStore } from '@/store'
import { NextLoading } from '@/utils/loading' import { NextLoading } from '@/utils/loading'
import { useMenuStore } from '@/store/menu'
// 进度条配置 // 进度条配置
NProgress.configure({ showSpinner: false, speed: 500 }) NProgress.configure({ showSpinner: false, speed: 500 })
@ -72,13 +72,21 @@ router.beforeEach(async (to, from, next) => {
return false return false
} }
if (!isGetRouter.value) {
// 初始化菜单加载,代码位置不能变动
const menuStore = useMenuStore()
menuStore.refreshMenu()
isGetRouter.value = true
next({ ...to, replace: true })
return false
}
const token = tool.data.get('TOKEN') const token = tool.data.get('TOKEN')
// 页面刷新加载loading // 页面刷新加载loading
if (from.path === '/' && to.path !== '/login' && !window.nextLoading && token) { if (from.path === '/' && to.path !== '/login' && !window.nextLoading && token) {
NextLoading.start() NextLoading.start()
} }
if (to.path === '/login') { if (to.path === '/login') {
// 当用户输入了login路由将其跳转首页即可 // 当用户输入了login路由将其跳转首页即可
if (token) { if (token) {
@ -89,7 +97,6 @@ router.beforeEach(async (to, from, next) => {
} }
// 删除路由(替换当前layout路由) // 删除路由(替换当前layout路由)
router.addRoute(routes[0]) router.addRoute(routes[0])
isGetRouter.value = false
next() next()
return false return false
} else { } else {
@ -108,27 +115,6 @@ router.beforeEach(async (to, from, next) => {
if (to.meta.fullpage) { if (to.meta.fullpage) {
to.matched = [to.matched[to.matched.length - 1]] to.matched = [to.matched[to.matched.length - 1]]
} }
// 加载动态/静态路由
if (!isGetRouter.value) {
const apiMenu = tool.data.get('MENU') || []
if (apiMenu.length === 0) {
// 创建默认模块,显示默认菜单
apiMenu[0] = cloneDeep(userRoutes.module[0])
}
const childrenApiMenu = apiMenu[0].children
apiMenu[0].children = [...(childrenApiMenu ? childrenApiMenu : []), ...userRoutes.menu]
let menuRouter = filterAsyncRouter(apiMenu)
menuRouter = flatAsyncRoutes(menuRouter)
menuRouter.forEach((item) => {
router.addRoute('layout', item)
})
const search_store = searchStore()
search_store.init(menuRouter)
isGetRouter.value = true
next({ ...to, replace: true })
return false
}
beforeEach(to, from) beforeEach(to, from)
next() next()
}) })
@ -150,7 +136,8 @@ router.onError((error) => {
// 入侵追加自定义方法、对象 // 入侵追加自定义方法、对象
router.getMenu = () => { router.getMenu = () => {
let apiMenu = tool.data.get('MENU') || [] const menuStore = useMenuStore()
let apiMenu = menuStore.menuData.value || tool.data.get('MENU') || []
// 增加固定路由 // 增加固定路由
if (apiMenu.length === 0) { if (apiMenu.length === 0) {
// 创建默认模块,显示默认菜单 // 创建默认模块,显示默认菜单
@ -182,64 +169,4 @@ const filterUrl = (map) => {
return newMap return newMap
} }
// 转换
const filterAsyncRouter = (routerMap) => {
const accessedRouters = []
routerMap.forEach((item) => {
item.meta = item.meta ? item.meta : {}
// 处理外部链接特殊路由
if (item.meta.type === 'iframe') {
item.meta.url = item.path
item.path = `/${item.name}`
}
// MAP转路由对象
const route = {
path: item.path,
name: item.name,
meta: item.meta,
redirect: item.redirect,
children: item.children ? filterAsyncRouter(item.children) : null,
component: loadComponent(item.component)
}
accessedRouters.push(route)
})
return accessedRouters
}
const loadComponent = (component) => {
if (component) {
if (component.includes('/')) {
return modules[`/src/views/${component}.vue`]
}
return modules[`/src/views/${component}/index.vue`]
} else {
return () => import(/* @vite-ignore */ `/src/layout/other/empty.vue`)
}
}
// 路由扁平化
const flatAsyncRoutes = (routes, breadcrumb = []) => {
const res = []
routes.forEach((route) => {
const tmp = { ...route }
if (tmp.children) {
const childrenBreadcrumb = [...breadcrumb]
childrenBreadcrumb.push(route)
const tmpRoute = { ...route }
tmpRoute.meta.breadcrumb = childrenBreadcrumb
delete tmpRoute.children
res.push(tmpRoute)
const childrenRoutes = flatAsyncRoutes(tmp.children, childrenBreadcrumb)
childrenRoutes.map((item) => {
res.push(item)
})
} else {
const tmpBreadcrumb = [...breadcrumb]
tmpBreadcrumb.push(tmp)
tmp.meta.breadcrumb = tmpBreadcrumb
res.push(tmp)
}
})
return res
}
export default router export default router

View File

@ -15,6 +15,7 @@ import routerUtil from '@/utils/routerUtil'
import Layout from '@/layout/index.vue' import Layout from '@/layout/index.vue'
import Login from '@/views/auth/login/login.vue' import Login from '@/views/auth/login/login.vue'
import Findpwd from '@/views/auth/findPwd/index.vue' import Findpwd from '@/views/auth/findPwd/index.vue'
import Callback from '@/views/auth/login/callback.vue'
// 系统路由 // 系统路由
const routes = [ const routes = [
@ -41,7 +42,7 @@ const routes = [
}, },
{ {
path: '/callback', path: '/callback',
component: () => import('@/views/auth/login/callback.vue'), component: Callback,
meta: { meta: {
title: '三方登录' title: '三方登录'
} }

View File

@ -0,0 +1,142 @@
/**
* Copyright [2022] [https://www.xiaonuo.vip]
* Snowy采用APACHE LICENSE 2.0开源协议您在使用过程中需要注意以下几点
* 1.请不要删除和修改根目录下的LICENSE文件
* 2.请不要删除和修改Snowy源码头部的版权声明
* 3.本项目代码可免费商业使用商业使用请保留源码和相关描述文件的项目出处作者声明等
* 4.分发源码时候请注明软件出处 https://www.xiaonuo.vip
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
import { defineStore } from 'pinia'
import tool from '@/utils/tool'
import { cloneDeep } from 'lodash-es'
import userRoutes from '@/config/route'
import { searchStore } from '@/store/search'
import router from '@/router'
import userCenterApi from '@/api/sys/userCenterApi'
import whiteList from '@/router/whiteList'
import routesData from '@/router/systemRouter'
const modules = import.meta.glob('/src/views/**/**.vue')
export const useMenuStore = defineStore('menuStore', () => {
const menuData = ref([])
const refreshFlag = ref(false)
// 改变刷新标志
const changeRefreshFlag = (flag) => {
refreshFlag.value = flag
}
// 加载菜单
const loadMenu = () => {
// 获取用户菜单
const apiMenu = tool.data.get('MENU') || []
if (apiMenu.length === 0) {
// 创建默认模块,显示默认菜单
apiMenu[0] = cloneDeep(userRoutes.module[0])
}
const childrenApiMenu = apiMenu[0].children
apiMenu[0].children = [...(childrenApiMenu ? childrenApiMenu : []), ...userRoutes.menu]
let menuRouter = filterAsyncRouter(apiMenu)
menuRouter = flatAsyncRoutes(menuRouter)
menuData.value = menuRouter
// 初始化搜索
const search_store = searchStore()
search_store.init(menuRouter)
}
// 过滤异步路由
const filterAsyncRouter = (routerMap) => {
const accessedRouters = []
routerMap.forEach((item) => {
item.meta = item.meta ? item.meta : {}
// 处理外部链接特殊路由
if (item.meta.type === 'iframe') {
item.meta.url = item.path
item.path = `/${item.name}`
}
// MAP转路由对象
const route = {
path: item.path,
name: item.name,
meta: item.meta,
redirect: item.redirect,
children: item.children ? filterAsyncRouter(item.children) : null,
component: loadComponent(item.component)
}
accessedRouters.push(route)
})
return accessedRouters
}
// 将异步路由扁平化
const flatAsyncRoutes = (routes, breadcrumb = []) => {
const res = []
routes.forEach((route) => {
const tmp = { ...route }
if (tmp.children) {
const childrenBreadcrumb = [...breadcrumb]
childrenBreadcrumb.push(route)
const tmpRoute = { ...route }
tmpRoute.meta.breadcrumb = childrenBreadcrumb
delete tmpRoute.children
res.push(tmpRoute)
const childrenRoutes = flatAsyncRoutes(tmp.children, childrenBreadcrumb)
childrenRoutes.map((item) => {
res.push(item)
})
} else {
const tmpBreadcrumb = [...breadcrumb]
tmpBreadcrumb.push(tmp)
tmp.meta.breadcrumb = tmpBreadcrumb
res.push(tmp)
}
})
return res
}
// 动态加载组件
const loadComponent = (component) => {
if (component) {
if (component.includes('/')) {
return modules[`/src/views/${component}.vue`]
}
return modules[`/src/views/${component}/index.vue`]
} else {
return () => import(/* @vite-ignore */ `/src/layout/other/empty.vue`)
}
}
// 从路由中移除菜单
const removeFromRouter = () => {
const routes = router.getRoutes()
// 遍历所有路由
routes.forEach((route) => {
// 过滤白名单
if (
whiteList.filter((e) => e.path === route.path).length > 0 ||
routesData.filter((e) => e.path === route.path).length > 0
) {
return
}
if (route.name && route.name !== 'layout') {
router.removeRoute(route.name)
}
})
}
// 获取用户菜单
const fetchMenu = async () => {
const menu = await userCenterApi.userLoginMenu()
tool.data.set('MENU', menu)
refreshMenu()
}
// 刷新菜单
const refreshMenu = () => {
loadMenu()
removeFromRouter()
addToRouter()
changeRefreshFlag(true)
}
// 将菜单添加到路由
const addToRouter = () => {
menuData.value.forEach((item) => {
router.addRoute('layout', item)
})
}
return { menuData, loadMenu, addToRouter, refreshMenu, changeRefreshFlag, refreshFlag, fetchMenu }
})

View File

@ -0,0 +1,32 @@
/**
* Copyright [2022] [https://www.xiaonuo.vip]
* Snowy采用APACHE LICENSE 2.0开源协议您在使用过程中需要注意以下几点
* 1.请不要删除和修改根目录下的LICENSE文件
* 2.请不要删除和修改Snowy源码头部的版权声明
* 3.本项目代码可免费商业使用商业使用请保留源码和相关描述文件的项目出处作者声明等
* 4.分发源码时候请注明软件出处 https://www.xiaonuo.vip
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
import { defineStore } from 'pinia'
import loginApi from '@/api/auth/loginApi'
import { useGlobalStore } from '@/store'
import tool from '@/utils/tool'
export const userStore = defineStore('userStore', () => {
// 初始化用户信息
const initUserInfo = async () => {
const data = await loginApi.getLoginUser()
const globalStore = useGlobalStore()
globalStore.setUserInfo(data)
tool.data.set('USER_INFO', data)
}
// 刷新登录用户信息
const refreshUserLoginUserInfo = () => {
loginApi.getLoginUser().then((data) => {
const globalStore = useGlobalStore()
globalStore.setUserInfo(data)
tool.data.set('USER_INFO', data)
})
}
return { initUserInfo, refreshUserLoginUserInfo }
})

View File

@ -45,6 +45,26 @@ export const viewTagsStore = defineStore('viewTags', () => {
} }
}) })
} }
// 更新或删除视图标签
const updateOrRemoveViewTags = (routes) => {
if (routes && routes.length > 0) {
viewTags.value.forEach((item, index) => {
const target = routes.find((route) => route.path === item.fullPath)
if (!target) {
// 路由不存在,删除
viewTags.value.splice(index, 1)
} else {
// 路由存在,更新
viewTags.value = viewTags.value.map((item) => {
if (item.fullPath === target.path) {
return { ...item, meta: target.meta }
}
return item
})
}
})
}
}
const updateViewTagsTitle = (title = '') => { const updateViewTagsTitle = (title = '') => {
const nowFullPath = location.hash.substring(1) const nowFullPath = location.hash.substring(1)
viewTags.value.forEach((item) => { viewTags.value.forEach((item) => {
@ -63,6 +83,7 @@ export const viewTagsStore = defineStore('viewTags', () => {
removeViewTags, removeViewTags,
updateViewTags, updateViewTags,
updateViewTagsTitle, updateViewTagsTitle,
clearViewTags clearViewTags,
updateOrRemoveViewTags
} }
}) })

View File

@ -258,7 +258,7 @@ a, button, input, textarea {
} }
/*页面最大化*/ /*页面最大化*/
.admin-ui.main-maximize { .app-main.main-maximize {
.main-maximize-exit { .main-maximize-exit {
display: block; display: block;
} }

View File

@ -8,12 +8,6 @@
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作 * 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip * 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/ */
/**
* 全局代码错误捕捉
* 比如 null.length 就会被捕捉到
*/
import { notification } from 'ant-design-vue'
export default (error) => { export default (error) => {
// 过滤HTTP请求错误 // 过滤HTTP请求错误
if (error.code) { if (error.code) {
@ -30,10 +24,6 @@ export default (error) => {
} }
const errorName = errorMap[error.name] || '未知错误' const errorName = errorMap[error.name] || '未知错误'
nextTick(() => { nextTick(() => {
notification.error({ console.error(errorName)
message: '错误',
description: errorName
})
console.error(error)
}) })
} }

View File

@ -1,6 +1,22 @@
<template> <template>
<div class="login_background"> <div class="login-wrapper">
<div class="login_background_front"></div> <div class="login_background">
<div class="logo_background">
<a
:class="{ 'no-link': !sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL }"
:href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL"
target="_blank"
@click="handleLink"
>
<img :alt="sysBaseConfig.SNOWY_SYS_NAME" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
<label>{{ sysBaseConfig.SNOWY_SYS_NAME }}</label>
</a>
</div>
<div class="version">
<p>{{ sysBaseConfig.SNOWY_SYS_DEFAULT_DESCRRIPTION }}</p>
<p>{{ sysBaseConfig.SNOWY_SYS_COPYRIGHT }} {{ sysBaseConfig.SNOWY_SYS_VERSION }}</p>
</div>
</div>
<div class="login_main"> <div class="login_main">
<div class="login-form"> <div class="login-form">
<a-card> <a-card>
@ -22,46 +38,40 @@
</template> </template>
<script setup> <script setup>
import phoneFindForm from './phoneFindForm.vue' import PhoneFindForm from './phoneFindForm.vue'
import emailFindForm from './emailFindForm.vue' import EmailFindForm from './emailFindForm.vue'
import { globalStore } from '@/store'
const store = globalStore()
const activeKey = ref('userPhone') const activeKey = ref('userPhone')
const sysBaseConfig = computed(() => {
return store.sysBaseConfig
})
// logo
const handleLink = (e) => {
if (!sysBaseConfig.value.SNOWY_SYS_COPYRIGHT_URL) {
e?.stopPropagation()
e?.preventDefault()
}
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.login-wrapper {
width: 100vw;
height: 100vh;
overflow: hidden;
background-color: #fff;
display: flex;
}
.login_background { .login_background {
width: 100%; width: 50%;
min-height: 100vh; height: 100%;
overflow: hidden; overflow: hidden;
background-size: cover; background-size: cover;
background-position: center; background-position: center;
background-image: url(/img/login_background.png); background-image: url(/img/login_background.png);
} position: relative;
.login_background_front {
width: 450px;
height: 450px;
margin-left: 100px;
margin-top: 15%;
overflow: hidden;
/*position: relative;*/
background-size: cover;
background-position: center;
background-image: url(/img/login_background_front.png);
animation-name: myfirst;
animation-duration: 5s;
animation-timing-function: linear;
animation-delay: 1s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-play-state: running;
/* Safari and Chrome: */
-webkit-animation-name: myfirst;
-webkit-animation-duration: 5s;
-webkit-animation-timing-function: linear;
-webkit-animation-delay: 1s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: alternate;
-webkit-animation-play-state: running;
} }
@keyframes myfirst { @keyframes myfirst {
0% { 0% {
@ -122,18 +132,15 @@
} }
/*background-image:linear-gradient(transparent, #000);*/ /*background-image:linear-gradient(transparent, #000);*/
.login_main { .login_main {
flex: 1; width: 50%;
overflow: auto; height: 100%;
display: flex; display: flex;
justify-content: center;
} }
.login-form { .login-form {
top: 15%;
right: 15%;
position: absolute;
width: 450px; width: 450px;
margin-left: 10%; position: absolute;
margin-top: 20px; top: 21.8%;
padding: 10px 0;
} }
.login-header { .login-header {
margin-bottom: 20px; margin-bottom: 20px;
@ -154,7 +161,7 @@
.login-header h2 { .login-header h2 {
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: bold;
margin-top: 40px; margin-top: 10px;
} }
.login-oauth { .login-oauth {
display: flex; display: flex;
@ -165,6 +172,62 @@
top: 20px; top: 20px;
right: 20px; right: 20px;
} }
.logo_background {
position: absolute;
left: 0;
top: 56px;
height: 60px;
padding-left: 56px;
width: 100%;
background: -webkit-gradient(
linear,
right top,
left top,
from(rgba(67, 147, 250, 0.5)),
to(rgba(133, 182, 252, 0.5))
);
background: linear-gradient(120deg, rgb(255 255 255 / 90%), rgba(255, 255, 255, 0));
display: flex;
align-items: center;
}
.logo_background img {
height: 40px;
margin-right: 10px;
}
.logo_background label {
font-size: 24px;
color: #fff;
}
.logo_background a {
text-decoration: none;
cursor: pointer;
display: flex;
align-items: center;
}
.logo_background a.no-link,
.logo_background a.no-link label {
cursor: default;
}
.logo_background a label {
font-size: 24px;
color: #fff;
cursor: pointer;
}
.login_background .version {
width: 100%;
font-size: 14px;
color: #fff;
font-weight: 300;
padding: 0 56px;
box-sizing: border-box;
position: absolute;
bottom: 12px;
}
.login_background .version p {
line-height: 22px;
text-align: center;
margin-bottom: 6px;
}
@media (max-width: 1200px) { @media (max-width: 1200px) {
.login-form { .login-form {
width: 340px; width: 340px;
@ -172,16 +235,27 @@
} }
@media (max-width: 1000px) { @media (max-width: 1000px) {
.login_main { .login_main {
display: block; width: 100%;
position: absolute;
left: 0;
right: 0;
} }
.login_background_front { .login_background_front {
display: none; display: none;
} }
.logo_background{
padding-left:40px;
}
.login-form { .login-form {
width: 100%; width: 100%;
padding: 20px 40px; padding: 20px 40px;
right: 0 !important; top: 15%;
top: 0 !important; }
.login_background .version {
padding: 0 20px;
}
.login_background .version p:first-child {
display: none;
} }
} }
</style> </style>

View File

@ -1,6 +1,22 @@
<template> <template>
<div class="login_background"> <div class="login-wrapper">
<div class="login_background_front"></div> <div class="login_background">
<div class="logo_background">
<a
:class="{ 'no-link': !sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL }"
:href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL"
target="_blank"
@click="handleLink"
>
<img :alt="sysBaseConfig.SNOWY_SYS_NAME" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
<label>{{ sysBaseConfig.SNOWY_SYS_NAME }}</label>
</a>
</div>
<div class="version">
<p>{{ sysBaseConfig.SNOWY_SYS_DEFAULT_DESCRRIPTION }}</p>
<p>{{ sysBaseConfig.SNOWY_SYS_COPYRIGHT }} {{ sysBaseConfig.SNOWY_SYS_VERSION }}</p>
</div>
</div>
<div class="login_main"> <div class="login_main">
<div class="login-form"> <div class="login-form">
<a-card> <a-card>
@ -26,6 +42,12 @@
import loginApi from '@/api/auth/loginApi' import loginApi from '@/api/auth/loginApi'
import userCenterApi from '@/api/sys/userCenterApi' import userCenterApi from '@/api/sys/userCenterApi'
import dictApi from '@/api/dev/dictApi' import dictApi from '@/api/dev/dictApi'
import { globalStore } from '@/store'
const store = globalStore()
const sysBaseConfig = computed(() => {
return store.sysBaseConfig
})
onMounted(() => { onMounted(() => {
// url // url
@ -69,6 +91,13 @@
window.location.href = '/login' window.location.href = '/login'
}) })
}) })
// logo
const handleLink = (e) => {
if (!sysBaseConfig.value.SNOWY_SYS_COPYRIGHT_URL) {
e?.stopPropagation()
e?.preventDefault()
}
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -7,13 +7,21 @@
width: 100%; width: 100%;
height: 40px; height: 40px;
} }
.login-wrapper{
width: 100vw;
height:100vh;
overflow: hidden;
background-color: #fff;
display: flex;
}
.login_background { .login_background {
width: 100%; width: 50%;
min-height: 100vh; height: 100%;
overflow: hidden; overflow: hidden;
background-size: cover; background-size: cover;
background-position: center; background-position: center;
background-image: url(/img/login_background.png); background-image: url(/img/login_background.png);
position: relative;
} }
.login_background_front { .login_background_front {
width: 450px; width: 450px;
@ -92,45 +100,74 @@
} }
/*background-image:linear-gradient(transparent, #000);*/ /*background-image:linear-gradient(transparent, #000);*/
.login_main { .login_main {
flex: 1; width: 50%;
overflow: auto; height: 100%;
display: flex; display: flex;
justify-content: center;
} }
.login-form { .login-form {
top: 15%;
right: 15%;
position: absolute;
width: 450px; width: 450px;
margin-left: 10%; position: absolute;
margin-top: 20px; top:21.8%
padding: 10px 0;
} }
.login-header { .login-header {
margin-bottom: 20px; margin-bottom: 20px;
} }
.login-header .logo {
display: flex;
align-items: center;
}
.login-header .logo img {
width: 35px;
height: 35px;
vertical-align: bottom;
margin-right: 10px;
}
.login-header .logo label {
font-size: 24px;
}
.login-header h2 { .login-header h2 {
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: bold;
margin-top: 40px; margin-top: 10px;
} }
.login_config { .login_config {
position: absolute; position: absolute;
top: 20px; top: 20px;
right: 20px; right: 20px;
} }
.logo_background{
position: absolute;
left: 0;
top: 56px;
height: 60px;
padding-left: 56px;
width: 100%;
background: linear-gradient(120deg, rgb(255 255 255 / 90%), rgba(255, 255, 255, 0));
display: flex;
align-items: center;
}
.logo_background a{
text-decoration: none;
cursor: pointer;
display: flex;
align-items: center;
}
.logo_background a.no-link,
.logo_background a.no-link label{
cursor: default;
}
.logo_background img{
height:40px;
margin-right: 10px;
}
.logo_background a label{
font-size:24px;
color:#fff;
cursor: pointer;
}
.login_background .version{
width: 100%;
font-size: 14px;
color:#fff;
font-weight: 300;
padding: 0 56px;
box-sizing: border-box;
position: absolute;
bottom:12px;
}
.login_background .version p{
line-height: 22px;
text-align: center;
margin-bottom:6px;
}
@media (max-width: 1200px) { @media (max-width: 1200px) {
.login-form { .login-form {
width: 340px; width: 340px;
@ -138,7 +175,10 @@
} }
@media (max-width: 1000px) { @media (max-width: 1000px) {
.login_main { .login_main {
display: block; width: 100%;
position: absolute;
left:0;
right:0;
} }
.login_background_front { .login_background_front {
display: none; display: none;
@ -146,7 +186,15 @@
.login-form { .login-form {
width: 100%; width: 100%;
padding: 20px 40px; padding: 20px 40px;
right: 0 !important; top:15%
top: 0 !important; }
.logo_background{
padding-left:40px;
}
.login_background .version{
padding:0 20px;
}
.login_background .version p:first-child{
display: none;
} }
} }

View File

@ -1,6 +1,22 @@
<template> <template>
<div class="login_background"> <div class="login-wrapper">
<div class="login_background_front"></div> <div class="login_background">
<div class="logo_background">
<a
:class="{ 'no-link': !sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL }"
:href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL"
target="_blank"
@click="handleLink"
>
<img :alt="sysBaseConfig.SNOWY_SYS_NAME" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
<label>{{ sysBaseConfig.SNOWY_SYS_NAME }}</label>
</a>
</div>
<div class="version">
<p>{{ sysBaseConfig.SNOWY_SYS_DEFAULT_DESCRRIPTION }}</p>
<p>{{ sysBaseConfig.SNOWY_SYS_COPYRIGHT }} {{ sysBaseConfig.SNOWY_SYS_VERSION }}</p>
</div>
</div>
<div class="login_main"> <div class="login_main">
<div class="login_config"> <div class="login_config">
<a-dropdown> <a-dropdown>
@ -23,11 +39,7 @@
<div class="login-form"> <div class="login-form">
<a-card> <a-card>
<div class="login-header"> <div class="login-header">
<div class="logo"> <h2>{{ $t('login.signInTitle') }}</h2>
<img :alt="sysBaseConfig.SNOWY_SYS_NAME" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
<label>{{ sysBaseConfig.SNOWY_SYS_NAME }}</label>
</div>
<!--<h2>{{ $t('login.signInTitle') }}</h2>-->
</div> </div>
<a-tabs v-model:activeKey="activeKey"> <a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="userAccount" :tab="$t('login.accountPassword')"> <a-tab-pane key="userAccount" :tab="$t('login.accountPassword')">
@ -202,7 +214,6 @@
rules.validCode = [required(proxy.$t('login.validError'), 'blur')] rules.validCode = [required(proxy.$t('login.validError'), 'blur')]
} }
} }
// //
const loginCaptcha = () => { const loginCaptcha = () => {
loginApi.getPicCaptcha().then((data) => { loginApi.getPicCaptcha().then((data) => {
@ -224,15 +235,6 @@
validCode: ruleForm.validCode, validCode: ruleForm.validCode,
validCodeReqNo: ruleForm.validCodeReqNo validCodeReqNo: ruleForm.validCodeReqNo
} }
// token
// loginApi.login(loginData).then((loginToken) => {
// afterLogin(loginToken)
// }).catch(() => {
// loading.value = false
// loginCaptcha()
// })
// token // token
try { try {
const loginToken = await loginApi.login(loginData) const loginToken = await loginApi.login(loginData)
@ -249,6 +251,13 @@
const configLang = (key) => { const configLang = (key) => {
config.value.lang = key config.value.lang = key
} }
// logo
const handleLink = (e) => {
if (!sysBaseConfig.value.SNOWY_SYS_COPYRIGHT_URL) {
e?.stopPropagation()
e?.preventDefault()
}
}
</script> </script>
<style lang="less"> <style lang="less">
@import 'login'; @import 'login';

View File

@ -1,24 +1,22 @@
import loginApi from '@/api/auth/loginApi'
import userCenterApi from '@/api/sys/userCenterApi' import userCenterApi from '@/api/sys/userCenterApi'
import dictApi from '@/api/dev/dictApi' import dictApi from '@/api/dev/dictApi'
import router from '@/router' import router from '@/router'
import tool from '@/utils/tool' import tool from '@/utils/tool'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useGlobalStore } from '@/store'
import routerUtil from '@/utils/routerUtil' import routerUtil from '@/utils/routerUtil'
import { useMenuStore } from '@/store/menu'
import { userStore } from '@/store/user'
export const afterLogin = async (loginToken) => { export const afterLogin = async (loginToken) => {
const menuStore = useMenuStore()
tool.data.set('TOKEN', loginToken) tool.data.set('TOKEN', loginToken)
// 获取登录的用户信息 // 初始化用户信息
const loginUser = await loginApi.getLoginUser() await userStore().initUserInfo()
const globalStore = useGlobalStore()
globalStore.setUserInfo(loginUser)
tool.data.set('USER_INFO', loginUser)
// 获取用户的菜单 // 获取用户的菜单
const menu = await userCenterApi.userLoginMenu() const menu = await userCenterApi.userLoginMenu()
let indexMenu = routerUtil.getIndexMenu(menu).path let indexMenu = routerUtil.getIndexMenu(menu).path
tool.data.set('MENU', menu) await menuStore.fetchMenu()
// 重置系统默认应用 // 重置系统默认应用
tool.data.set('SNOWY_MENU_MODULE_ID', menu[0].id) tool.data.set('SNOWY_MENU_MODULE_ID', menu[0].id)
message.success('登录成功') message.success('登录成功')

View File

@ -123,6 +123,7 @@
<script setup name="sysResourceMenuForm"> <script setup name="sysResourceMenuForm">
import { required } from '@/utils/formRules' import { required } from '@/utils/formRules'
import { cloneDeep } from 'lodash-es'
import SnowflakeId from 'snowflake-id' import SnowflakeId from 'snowflake-id'
import tool from '@/utils/tool' import tool from '@/utils/tool'
import menuApi from '@/api/sys/resource/menuApi' import menuApi from '@/api/sys/resource/menuApi'
@ -141,7 +142,8 @@
// ID // ID
const moduleId = ref('') const moduleId = ref('')
// //
const onOpen = (record, module) => { const onOpen = (data, module) => {
const record = cloneDeep(data)
moduleId.value = module moduleId.value = module
visible.value = true visible.value = true
if (record) { if (record) {

View File

@ -111,8 +111,8 @@
</template> </template>
</s-table> </s-table>
</a-card> </a-card>
<Form ref="formRef" @successful="tableRef.refresh(true)" /> <Form ref="formRef" @successful="handleSuccess" />
<changeModuleForm ref="changeModuleFormRef" @successful="tableRef.refresh(true)" /> <changeModuleForm ref="changeModuleFormRef" @successful="handleSuccess" />
<Button ref="buttonRef" /> <Button ref="buttonRef" />
</template> </template>
@ -121,6 +121,7 @@
import Form from './form.vue' import Form from './form.vue'
import ChangeModuleForm from './changeModuleForm.vue' import ChangeModuleForm from './changeModuleForm.vue'
import Button from '../button/index.vue' import Button from '../button/index.vue'
import { useMenuStore } from '@/store/menu'
const searchFormState = ref({}) const searchFormState = ref({})
const tableRef = ref(null) const tableRef = ref(null)
const formRef = ref() const formRef = ref()
@ -242,12 +243,24 @@
] ]
menuApi.menuDelete(params).then(() => { menuApi.menuDelete(params).then(() => {
tableRef.value.refresh(true) tableRef.value.refresh(true)
refreshCacheMenu()
}) })
} }
// //
const deleteBatchMenu = (params) => { const deleteBatchMenu = (params) => {
menuApi.menuDelete(params).then(() => { menuApi.menuDelete(params).then(() => {
tableRef.value.clearRefreshSelected() tableRef.value.clearRefreshSelected()
refreshCacheMenu()
}) })
} }
//
const handleSuccess = () => {
tableRef.value.refresh(true)
refreshCacheMenu()
}
//
const refreshCacheMenu = () => {
const menuStore = useMenuStore()
menuStore.fetchMenu()
}
</script> </script>

View File

@ -108,6 +108,7 @@
import { SearchOutlined } from '@ant-design/icons-vue' import { SearchOutlined } from '@ant-design/icons-vue'
import roleApi from '@/api/sys/roleApi' import roleApi from '@/api/sys/roleApi'
import ScopeDefineOrg from './scopeDefineOrg.vue' import ScopeDefineOrg from './scopeDefineOrg.vue'
import { userStore } from '@/store/user'
const visible = ref(false) const visible = ref(false)
const spinningLoading = ref(false) const spinningLoading = ref(false)
@ -248,7 +249,7 @@
scopeDefineOrgModal.value.onOpen(data.id, checkKeysStr) scopeDefineOrgModal.value.onOpen(data.id, checkKeysStr)
} else { } else {
// , // ,
handleDatascope(false, record.id, null) handleDatascope(false, data.id, null)
} }
} }
// //
@ -365,6 +366,10 @@
.then(() => { .then(() => {
onClose() onClose()
emit('successful') emit('successful')
//
nextTick(() => {
userStore().refreshUserLoginUserInfo()
})
}) })
.finally(() => { .finally(() => {
submitLoading.value = false submitLoading.value = false

View File

@ -56,10 +56,9 @@
</template> </template>
<script setup name="grantResourceForm"> <script setup name="grantResourceForm">
import { nextTick } from 'vue'
import tool from '@/utils/tool'
import roleApi from '@/api/sys/roleApi' import roleApi from '@/api/sys/roleApi'
import userCenterApi from '@/api/sys/userCenterApi' import { useMenuStore } from '@/store/menu'
import { userStore } from '@/store/user'
const spinningLoading = ref(false) const spinningLoading = ref(false)
const firstShowMap = ref({}) const firstShowMap = ref({})
const emit = defineEmits({ successful: null }) const emit = defineEmits({ successful: null })
@ -283,19 +282,17 @@
.then(() => { .then(() => {
onClose() onClose()
emit('successful') emit('successful')
refreshCacheMenu() refreshCache()
}) })
.finally(() => { .finally(() => {
submitLoading.value = false submitLoading.value = false
}) })
} }
// //
const refreshCacheMenu = () => { const refreshCache = () => {
nextTick(() => { const menuStore = useMenuStore()
userCenterApi.userLoginMenu().then((res) => { menuStore.fetchMenu()
tool.data.set('MENU', res) userStore().refreshUserLoginUserInfo()
})
})
} }
// //
defineExpose({ defineExpose({

View File

@ -109,6 +109,7 @@
import userApi from '@/api/sys/userApi' import userApi from '@/api/sys/userApi'
import roleApi from '@/api/sys/roleApi' import roleApi from '@/api/sys/roleApi'
import ScopeDefineOrg from './scopeDefineOrg.vue' import ScopeDefineOrg from './scopeDefineOrg.vue'
import { userStore } from '@/store/user'
const visible = ref(false) const visible = ref(false)
const spinningLoading = ref(false) const spinningLoading = ref(false)
@ -249,7 +250,7 @@
scopeDefineOrgModal.value.onOpen(data.id, checkKeysStr) scopeDefineOrgModal.value.onOpen(data.id, checkKeysStr)
} else { } else {
// , // ,
handleDatascope(false, record.id, null) handleDatascope(false, data.id, null)
} }
} }
// //
@ -366,6 +367,10 @@
.then(() => { .then(() => {
onClose() onClose()
emit('successful') emit('successful')
//
nextTick(() => {
userStore().refreshUserLoginUserInfo()
})
}) })
.finally(() => { .finally(() => {
submitLoading.value = false submitLoading.value = false

View File

@ -56,11 +56,10 @@
</template> </template>
<script setup name="grantResourceForm"> <script setup name="grantResourceForm">
import { nextTick } from 'vue'
import tool from '@/utils/tool'
import userApi from '@/api/sys/userApi' import userApi from '@/api/sys/userApi'
import roleApi from '@/api/sys/roleApi' import roleApi from '@/api/sys/roleApi'
import userCenterApi from '@/api/sys/userCenterApi' import { useMenuStore } from '@/store/menu'
import { userStore } from '@/store/user'
const spinningLoading = ref(false) const spinningLoading = ref(false)
const firstShowMap = ref({}) const firstShowMap = ref({})
const emit = defineEmits({ successful: null }) const emit = defineEmits({ successful: null })
@ -284,20 +283,19 @@
.then(() => { .then(() => {
onClose() onClose()
emit('successful') emit('successful')
refreshCacheMenu() refreshCache()
}) })
.finally(() => { .finally(() => {
submitLoading.value = false submitLoading.value = false
}) })
} }
// //
const refreshCacheMenu = () => { const refreshCache = () => {
nextTick(() => { const menuStore = useMenuStore()
userCenterApi.userLoginMenu().then((res) => { menuStore.fetchMenu()
tool.data.set('MENU', res) userStore().refreshUserLoginUserInfo()
})
})
} }
// //
defineExpose({ defineExpose({
onOpen onOpen

View File

@ -46,14 +46,14 @@ public class AuthConfigure implements WebMvcConfigurer {
/** /**
* Sa-Token * Sa-Token
* * <p>
* Controller * Controller
* 1.@SaCheckLogin: * 1.@SaCheckLogin:
* 2.@SaCheckRole("admin"): * 2.@SaCheckRole("admin"):
* 3.@SaCheckPermission("user:add"): * 3.@SaCheckPermission("user:add"):
* 4.@SaCheckSafe: * 4.@SaCheckSafe:
* 5.@SaCheckBasic: HttpBasic Basic * 5.@SaCheckBasic: HttpBasic Basic
* * </p>
* Controller访 * Controller访
**/ **/
@Override @Override
@ -75,10 +75,11 @@ public class AuthConfigure implements WebMvcConfigurer {
return new StpLogic(SaClientTypeEnum.C.getValue()).setConfig(saTokenConfig); return new StpLogic(SaClientTypeEnum.C.getValue()).setConfig(saTokenConfig);
} }
@SuppressWarnings("ALL")
@Autowired @Autowired
public void rewriteSaStrategy() { public void rewriteSaStrategy() {
// 重写Sa-Token的注解处理器增加注解合并功能 // 重写Sa-Token的注解处理器增加注解合并功能
SaStrategy.me.getAnnotation = AnnotatedElementUtils::getMergedAnnotation; SaStrategy.instance.getAnnotation = AnnotatedElementUtils::getMergedAnnotation;
} }
/** /**
@ -98,17 +99,13 @@ public class AuthConfigure implements WebMvcConfigurer {
*/ */
@Override @Override
public List<String> getPermissionList(Object loginId, String loginType) { public List<String> getPermissionList(Object loginId, String loginType) {
Object permissionListObject;
if (SaClientTypeEnum.B.getValue().equals(loginType)) { if (SaClientTypeEnum.B.getValue().equals(loginType)) {
// return StpLoginUserUtil.getLoginUser().getPermissionCodeList(); permissionListObject = commonCacheOperator.get(CacheConstant.AUTH_B_PERMISSION_LIST_CACHE_KEY + loginId);
Object permissionListObject = commonCacheOperator.get(CacheConstant.AUTH_B_PERMISSION_LIST_CACHE_KEY+loginId);
List<String> permissionList = JSONUtil.parseArray(permissionListObject).toList(String.class);
return permissionList;
} else { } else {
// return StpClientLoginUserUtil.getClientLoginUser().getPermissionCodeList(); permissionListObject = commonCacheOperator.get(CacheConstant.AUTH_C_PERMISSION_LIST_CACHE_KEY + loginId);
Object permissionListObject = commonCacheOperator.get(CacheConstant.AUTH_C_PERMISSION_LIST_CACHE_KEY+loginId);
List<String> permissionList = JSONUtil.parseArray(permissionListObject).toList(String.class);
return permissionList;
} }
return JSONUtil.parseArray(permissionListObject).toList(String.class);
} }
/** /**

View File

@ -356,6 +356,19 @@ public class AuthServiceImpl implements AuthService {
} }
// 执行登录 // 执行登录
StpUtil.login(saBaseLoginUser.getId(), new SaLoginModel().setDevice(device).setExtra("name", saBaseLoginUser.getName())); StpUtil.login(saBaseLoginUser.getId(), new SaLoginModel().setDevice(device).setExtra("name", saBaseLoginUser.getName()));
// 填充B端用户信息并更新缓存
fillSaBaseLoginUserAndUpdateCache(saBaseLoginUser);
// 返回token
return StpUtil.getTokenInfo().tokenValue;
}
/**
* B
*
* @author xuyuxiang
* @date 2024/7/22 22:00
*/
private void fillSaBaseLoginUserAndUpdateCache(SaBaseLoginUser saBaseLoginUser) {
// 角色集合 // 角色集合
List<JSONObject> roleList = loginUserApi.getRoleListByUserId(saBaseLoginUser.getId()); List<JSONObject> roleList = loginUserApi.getRoleListByUserId(saBaseLoginUser.getId());
// 角色id集合 // 角色id集合
@ -374,16 +387,14 @@ public class AuthServiceImpl implements AuthService {
// 获取权限码 // 获取权限码
List<String> permissionCodeList = saBaseLoginUser.getDataScopeList().stream() List<String> permissionCodeList = saBaseLoginUser.getDataScopeList().stream()
.map(SaBaseLoginUser.DataScope::getApiUrl).collect(Collectors.toList()); .map(SaBaseLoginUser.DataScope::getApiUrl).collect(Collectors.toList());
// 设置权限码
saBaseLoginUser.setPermissionCodeList(permissionCodeList); saBaseLoginUser.setPermissionCodeList(permissionCodeList);
// 权限码列表存入缓存 // 权限码列表存入缓存
commonCacheOperator.put(CacheConstant.AUTH_B_PERMISSION_LIST_CACHE_KEY + saBaseLoginUser.getId(),permissionCodeList); commonCacheOperator.put(CacheConstant.AUTH_B_PERMISSION_LIST_CACHE_KEY + saBaseLoginUser.getId(),permissionCodeList);
// 获取角色码 // 获取角色码
saBaseLoginUser.setRoleCodeList(roleCodeList); saBaseLoginUser.setRoleCodeList(roleCodeList);
// 缓存用户信息此处使用TokenSession为了指定时间内无操作则自动下线 // 缓存用户信息此处使用TokenSession为了指定时间内无操作则自动下线
StpUtil.getTokenSession().set("loginUser", saBaseLoginUser); StpUtil.getTokenSession().set("loginUser", saBaseLoginUser);
// 返回token
return StpUtil.getTokenInfo().tokenValue;
} }
/** /**
@ -399,6 +410,19 @@ public class AuthServiceImpl implements AuthService {
} }
// 执行登录 // 执行登录
StpClientUtil.login(saBaseClientLoginUser.getId(), new SaLoginModel().setDevice(device).setExtra("name", saBaseClientLoginUser.getName())); StpClientUtil.login(saBaseClientLoginUser.getId(), new SaLoginModel().setDevice(device).setExtra("name", saBaseClientLoginUser.getName()));
// 填充C端用户信息并更新缓存
fillSaBaseClientLoginUserAndUpdateCache(saBaseClientLoginUser);
// 返回token
return StpClientUtil.getTokenInfo().tokenValue;
}
/**
* C
*
* @author xuyuxiang
* @date 2024/7/22 22:00
*/
private void fillSaBaseClientLoginUserAndUpdateCache(SaBaseClientLoginUser saBaseClientLoginUser) {
// 角色集合 // 角色集合
List<JSONObject> roleList = loginUserApi.getRoleListByUserId(saBaseClientLoginUser.getId()); List<JSONObject> roleList = loginUserApi.getRoleListByUserId(saBaseClientLoginUser.getId());
// 角色id集合 // 角色id集合
@ -417,6 +441,7 @@ public class AuthServiceImpl implements AuthService {
// 获取权限码 // 获取权限码
List<String> permissionCodeList = saBaseClientLoginUser.getDataScopeList().stream() List<String> permissionCodeList = saBaseClientLoginUser.getDataScopeList().stream()
.map(SaBaseClientLoginUser.DataScope::getApiUrl).collect(Collectors.toList()); .map(SaBaseClientLoginUser.DataScope::getApiUrl).collect(Collectors.toList());
// 设置权限码
saBaseClientLoginUser.setPermissionCodeList(permissionCodeList); saBaseClientLoginUser.setPermissionCodeList(permissionCodeList);
// 权限码列表存入缓存 // 权限码列表存入缓存
commonCacheOperator.put(CacheConstant.AUTH_C_PERMISSION_LIST_CACHE_KEY + saBaseClientLoginUser.getId(),permissionCodeList); commonCacheOperator.put(CacheConstant.AUTH_C_PERMISSION_LIST_CACHE_KEY + saBaseClientLoginUser.getId(),permissionCodeList);
@ -424,8 +449,6 @@ public class AuthServiceImpl implements AuthService {
saBaseClientLoginUser.setRoleCodeList(roleCodeList); saBaseClientLoginUser.setRoleCodeList(roleCodeList);
// 缓存用户信息此处使用TokenSession为了指定时间内无操作则自动下线 // 缓存用户信息此处使用TokenSession为了指定时间内无操作则自动下线
StpClientUtil.getTokenSession().set("loginUser", saBaseClientLoginUser); StpClientUtil.getTokenSession().set("loginUser", saBaseClientLoginUser);
// 返回token
return StpClientUtil.getTokenInfo().tokenValue;
} }
/** /**
@ -436,10 +459,19 @@ public class AuthServiceImpl implements AuthService {
**/ **/
@Override @Override
public SaBaseLoginUser getLoginUser() { public SaBaseLoginUser getLoginUser() {
// 获取当前缓存的用户信息
SaBaseLoginUser saBaseLoginUser = StpLoginUserUtil.getLoginUser(); SaBaseLoginUser saBaseLoginUser = StpLoginUserUtil.getLoginUser();
// 获取B端用户信息
saBaseLoginUser = loginUserApi.getUserById(saBaseLoginUser.getId());
// 填充B端用户信息并更新缓存
fillSaBaseLoginUserAndUpdateCache(saBaseLoginUser);
// 去掉密码
saBaseLoginUser.setPassword(null); saBaseLoginUser.setPassword(null);
// 去掉权限码
saBaseLoginUser.setPermissionCodeList(null); saBaseLoginUser.setPermissionCodeList(null);
// 去掉数据范围
saBaseLoginUser.setDataScopeList(null); saBaseLoginUser.setDataScopeList(null);
// 返回
return saBaseLoginUser; return saBaseLoginUser;
} }
@ -451,10 +483,19 @@ public class AuthServiceImpl implements AuthService {
**/ **/
@Override @Override
public SaBaseClientLoginUser getClientLoginUser() { public SaBaseClientLoginUser getClientLoginUser() {
// 获取当前缓存的用户信息
SaBaseClientLoginUser saBaseClientLoginUser = StpClientLoginUserUtil.getClientLoginUser(); SaBaseClientLoginUser saBaseClientLoginUser = StpClientLoginUserUtil.getClientLoginUser();
// 获取C端用户信息
saBaseClientLoginUser = clientLoginUserApi.getClientUserById(saBaseClientLoginUser.getId());
// 填充C端用户信息并更新缓存
fillSaBaseClientLoginUserAndUpdateCache(saBaseClientLoginUser);
// 去掉密码
saBaseClientLoginUser.setPassword(null); saBaseClientLoginUser.setPassword(null);
// 去掉权限码
saBaseClientLoginUser.setPermissionCodeList(null); saBaseClientLoginUser.setPermissionCodeList(null);
// 去掉数据范围
saBaseClientLoginUser.setDataScopeList(null); saBaseClientLoginUser.setDataScopeList(null);
// 返回
return saBaseClientLoginUser; return saBaseClientLoginUser;
} }

View File

@ -127,16 +127,6 @@ public class DevSlideshowServiceImpl extends ServiceImpl<DevSlideshowMapper, Dev
List<JSONObject> resultList = CollectionUtil.newArrayList(); List<JSONObject> resultList = CollectionUtil.newArrayList();
List<DevSlideshow> slideshowList = this.list(new LambdaQueryWrapper<DevSlideshow>().eq(DevSlideshow::getStatus, List<DevSlideshow> slideshowList = this.list(new LambdaQueryWrapper<DevSlideshow>().eq(DevSlideshow::getStatus,
DevSlideshowStatusEnum.ENABLE.getValue()).orderByDesc(DevSlideshow::getSortCode)); DevSlideshowStatusEnum.ENABLE.getValue()).orderByDesc(DevSlideshow::getSortCode));
if (slideshowList.size() == 0) {
// 如果库里未配置,则补充一条静态的,避免图片为空
JSONObject staticObj = new JSONObject();
staticObj.set("id", IdWorker.getIdStr());
staticObj.set("title", "静态文件");
staticObj.set("image", DevConstants.STATIC_SLIDESHOW_IMAGE);
staticObj.set("pathDetails", null);
resultList.add(staticObj);
return resultList;
}
slideshowList.forEach((item) -> { slideshowList.forEach((item) -> {
JSONArray slideshowPlaceArray = JSONUtil.parseArray(item.getPlace()); JSONArray slideshowPlaceArray = JSONUtil.parseArray(item.getPlace());
slideshowPlaceArray.forEach((placeArray) -> { slideshowPlaceArray.forEach((placeArray) -> {
@ -157,6 +147,16 @@ public class DevSlideshowServiceImpl extends ServiceImpl<DevSlideshowMapper, Dev
} }
}); });
}); });
if (resultList.size() == 0) {
// 如果库里未配置,则补充一条静态的,避免图片为空
JSONObject staticObj = new JSONObject();
staticObj.set("id", IdWorker.getIdStr());
staticObj.set("title", "静态文件");
staticObj.set("image", DevConstants.STATIC_SLIDESHOW_IMAGE);
staticObj.set("pathDetails", null);
resultList.add(staticObj);
return resultList;
}
return resultList; return resultList;
} }
} }

View File

@ -212,6 +212,9 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
private void checkParam(SysMenuAddParam sysMenuAddParam) { private void checkParam(SysMenuAddParam sysMenuAddParam) {
SysResourceMenuTypeEnum.validate(sysMenuAddParam.getMenuType()); SysResourceMenuTypeEnum.validate(sysMenuAddParam.getMenuType());
if(sysMenuAddParam.getTitle().contains(StrUtil.DASHED)) {
throw new CommonException("title不可包含特殊字符【-】");
}
if(SysResourceMenuTypeEnum.MENU.getValue().equals(sysMenuAddParam.getMenuType())) { if(SysResourceMenuTypeEnum.MENU.getValue().equals(sysMenuAddParam.getMenuType())) {
if(ObjectUtil.isEmpty(sysMenuAddParam.getName())) { if(ObjectUtil.isEmpty(sysMenuAddParam.getName())) {
throw new CommonException("name不能为空"); throw new CommonException("name不能为空");
@ -227,7 +230,6 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
sysMenuAddParam.setName(null); sysMenuAddParam.setName(null);
sysMenuAddParam.setComponent(null); sysMenuAddParam.setComponent(null);
} }
} }
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -284,6 +286,9 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
private void checkParam(SysMenuEditParam sysMenuEditParam) { private void checkParam(SysMenuEditParam sysMenuEditParam) {
SysResourceMenuTypeEnum.validate(sysMenuEditParam.getMenuType()); SysResourceMenuTypeEnum.validate(sysMenuEditParam.getMenuType());
if(sysMenuEditParam.getTitle().contains(StrUtil.DASHED)) {
throw new CommonException("title不可包含特殊字符【-】");
}
if(SysResourceMenuTypeEnum.MENU.getValue().equals(sysMenuEditParam.getMenuType())) { if(SysResourceMenuTypeEnum.MENU.getValue().equals(sysMenuEditParam.getMenuType())) {
if(ObjectUtil.isEmpty(sysMenuEditParam.getName())) { if(ObjectUtil.isEmpty(sysMenuEditParam.getName())) {
throw new CommonException("name不能为空"); throw new CommonException("name不能为空");