mirror of https://gitee.com/xiaonuobase/snowy
parent
b2da2975f4
commit
92adbfb5a1
|
@ -12,7 +12,7 @@
|
|||
.app-loading__logo {margin-bottom: 30px;}
|
||||
.app-loading__logo img {width: 90px;vertical-align: bottom;}
|
||||
.app-loading__title {font-size: 24px;color: #333;margin-top: 30px;}
|
||||
.h-full { height: 100% }
|
||||
.app-main { height: 100% }
|
||||
@keyframes loader {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
|
@ -29,7 +29,7 @@
|
|||
<strong>We're sorry but Snowy2.0 doesn't work properly without JavaScript
|
||||
enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app" class="h-full"></div>
|
||||
<div id="app" class="app-main"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</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 |
|
@ -415,9 +415,12 @@
|
|||
} else {
|
||||
data.localDataSource = r.records
|
||||
}
|
||||
data.localLoading = false
|
||||
getTableProps()
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
data.localLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
<div class="right-menu-item" @click="closeOtherTabs">
|
||||
<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 class="right-menu-item" @click="maximize">
|
||||
|
@ -28,7 +28,7 @@
|
|||
</div>
|
||||
<div class="right-menu-item" @click="openWindow">
|
||||
<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>
|
||||
</xn-context-menu>
|
||||
<a-tabs
|
||||
|
@ -366,7 +366,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 10px 20px;
|
||||
padding: 10px 10px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
|
@ -433,7 +433,7 @@
|
|||
opacity: 0;
|
||||
}
|
||||
.snowy-header-tags-right {
|
||||
margin-right: 10px;
|
||||
margin-right: 5px;
|
||||
color: var(--font-color);
|
||||
}
|
||||
.snowy-header-tags-right-font {
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { globalStore, keepAliveStore } from '@/store'
|
||||
import { globalStore, keepAliveStore, viewTagsStore } from '@/store'
|
||||
import { themeEnum } from '@/layout/enum/themeEnum'
|
||||
import { layoutEnum } from '@/layout/enum/layoutEnum'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
@ -82,6 +82,8 @@
|
|||
import DoubleRowMenu from '@/layout/menu/doubleRowMenu.vue'
|
||||
import TopMenu from '@/layout/menu/topMenu.vue'
|
||||
import { NextLoading } from '@/utils/loading'
|
||||
import { useMenuStore } from '@/store/menu'
|
||||
import { userStore } from '@/store/user'
|
||||
|
||||
const store = globalStore()
|
||||
const kStore = keepAliveStore()
|
||||
|
@ -156,7 +158,13 @@
|
|||
})
|
||||
// 路由监听高亮
|
||||
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(() => {
|
||||
// 取得默认路由地址并设置展开
|
||||
|
@ -199,26 +207,39 @@
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 执行-start
|
||||
moduleMenu.value = router.getMenu()
|
||||
// 获取缓存中的菜单模块是哪个
|
||||
const menuModuleId = tool.data.get('SNOWY_MENU_MODULE_ID')
|
||||
if (menuModuleId) {
|
||||
// 防止切换一个无此应用的人
|
||||
const module = router.getMenu().filter((item) => item.id === menuModuleId)
|
||||
if (module.length > 0) {
|
||||
menu.value = module[0].children
|
||||
const init = () => {
|
||||
// 执行-start
|
||||
moduleMenu.value = router.getMenu()
|
||||
// 获取缓存中的菜单模块是哪个
|
||||
const menuModuleId = tool.data.get('SNOWY_MENU_MODULE_ID')
|
||||
if (menuModuleId) {
|
||||
// 防止切换一个无此应用的人
|
||||
const module = router.getMenu().filter((item) => item.id === menuModuleId)
|
||||
if (module.length > 0) {
|
||||
menu.value = module[0].children
|
||||
} else {
|
||||
menu.value = router.getMenu()[0].children
|
||||
}
|
||||
} else {
|
||||
menu.value = router.getMenu()[0].children
|
||||
}
|
||||
} else {
|
||||
menu.value = router.getMenu()[0].children
|
||||
showThis()
|
||||
}
|
||||
showThis()
|
||||
|
||||
watchEffect(() => {
|
||||
const menuStore = useMenuStore()
|
||||
if (menuStore.refreshFlag) {
|
||||
init()
|
||||
// 更新标签
|
||||
viewTagsStore().updateOrRemoveViewTags(router.getRoutes())
|
||||
menuStore.changeRefreshFlag(false)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// 取消loading
|
||||
NextLoading.done()
|
||||
showThis()
|
||||
onLayoutResize()
|
||||
window.addEventListener('resize', onLayoutResize)
|
||||
window.addEventListener('resize', getNav)
|
||||
|
@ -227,6 +248,8 @@
|
|||
settingFixedWidth()
|
||||
nextTick(() => {
|
||||
getNav(menu.value)
|
||||
// 刷新登录信息,不影响其他
|
||||
userStore().refreshUserLoginUserInfo()
|
||||
})
|
||||
})
|
||||
// 动态获取横向导航栏隐藏数量
|
||||
|
|
|
@ -8,11 +8,13 @@
|
|||
</template>
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMenuStore } from '@/store/menu'
|
||||
const router = useRouter()
|
||||
const gohome = () => {
|
||||
location.href = '/'
|
||||
}
|
||||
const goback = () => {
|
||||
router.go(-1)
|
||||
useMenuStore().changeRefreshFlag(true)
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -18,9 +18,9 @@ import whiteListRouters from './whiteList'
|
|||
import userRoutes from '@/config/route'
|
||||
import tool from '@/utils/tool'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
const modules = import.meta.glob('/src/views/**/**.vue')
|
||||
import { globalStore, searchStore } from '@/store'
|
||||
import { globalStore } from '@/store'
|
||||
import { NextLoading } from '@/utils/loading'
|
||||
import { useMenuStore } from '@/store/menu'
|
||||
|
||||
// 进度条配置
|
||||
NProgress.configure({ showSpinner: false, speed: 500 })
|
||||
|
@ -72,13 +72,21 @@ router.beforeEach(async (to, from, next) => {
|
|||
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')
|
||||
|
||||
// 页面刷新,加载loading
|
||||
if (from.path === '/' && to.path !== '/login' && !window.nextLoading && token) {
|
||||
NextLoading.start()
|
||||
}
|
||||
|
||||
if (to.path === '/login') {
|
||||
// 当用户输入了login路由,将其跳转首页即可
|
||||
if (token) {
|
||||
|
@ -89,7 +97,6 @@ router.beforeEach(async (to, from, next) => {
|
|||
}
|
||||
// 删除路由(替换当前layout路由)
|
||||
router.addRoute(routes[0])
|
||||
isGetRouter.value = false
|
||||
next()
|
||||
return false
|
||||
} else {
|
||||
|
@ -108,27 +115,6 @@ router.beforeEach(async (to, from, next) => {
|
|||
if (to.meta.fullpage) {
|
||||
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)
|
||||
next()
|
||||
})
|
||||
|
@ -150,7 +136,8 @@ router.onError((error) => {
|
|||
|
||||
// 入侵追加自定义方法、对象
|
||||
router.getMenu = () => {
|
||||
let apiMenu = tool.data.get('MENU') || []
|
||||
const menuStore = useMenuStore()
|
||||
let apiMenu = menuStore.menuData.value || tool.data.get('MENU') || []
|
||||
// 增加固定路由
|
||||
if (apiMenu.length === 0) {
|
||||
// 创建默认模块,显示默认菜单
|
||||
|
@ -182,64 +169,4 @@ const filterUrl = (map) => {
|
|||
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
|
||||
|
|
|
@ -15,6 +15,7 @@ import routerUtil from '@/utils/routerUtil'
|
|||
import Layout from '@/layout/index.vue'
|
||||
import Login from '@/views/auth/login/login.vue'
|
||||
import Findpwd from '@/views/auth/findPwd/index.vue'
|
||||
import Callback from '@/views/auth/login/callback.vue'
|
||||
|
||||
// 系统路由
|
||||
const routes = [
|
||||
|
@ -41,7 +42,7 @@ const routes = [
|
|||
},
|
||||
{
|
||||
path: '/callback',
|
||||
component: () => import('@/views/auth/login/callback.vue'),
|
||||
component: Callback,
|
||||
meta: {
|
||||
title: '三方登录'
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
})
|
|
@ -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 }
|
||||
})
|
|
@ -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 nowFullPath = location.hash.substring(1)
|
||||
viewTags.value.forEach((item) => {
|
||||
|
@ -63,6 +83,7 @@ export const viewTagsStore = defineStore('viewTags', () => {
|
|||
removeViewTags,
|
||||
updateViewTags,
|
||||
updateViewTagsTitle,
|
||||
clearViewTags
|
||||
clearViewTags,
|
||||
updateOrRemoveViewTags
|
||||
}
|
||||
})
|
||||
|
|
|
@ -258,7 +258,7 @@ a, button, input, textarea {
|
|||
}
|
||||
|
||||
/*页面最大化*/
|
||||
.admin-ui.main-maximize {
|
||||
.app-main.main-maximize {
|
||||
.main-maximize-exit {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -8,12 +8,6 @@
|
|||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
/**
|
||||
* 全局代码错误捕捉
|
||||
* 比如 null.length 就会被捕捉到
|
||||
*/
|
||||
import { notification } from 'ant-design-vue'
|
||||
|
||||
export default (error) => {
|
||||
// 过滤HTTP请求错误
|
||||
if (error.code) {
|
||||
|
@ -30,10 +24,6 @@ export default (error) => {
|
|||
}
|
||||
const errorName = errorMap[error.name] || '未知错误'
|
||||
nextTick(() => {
|
||||
notification.error({
|
||||
message: '错误',
|
||||
description: errorName
|
||||
})
|
||||
console.error(error)
|
||||
console.error(errorName)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,22 @@
|
|||
<template>
|
||||
<div class="login_background">
|
||||
<div class="login_background_front"></div>
|
||||
<div class="login-wrapper">
|
||||
<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-form">
|
||||
<a-card>
|
||||
|
@ -22,46 +38,40 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import phoneFindForm from './phoneFindForm.vue'
|
||||
import emailFindForm from './emailFindForm.vue'
|
||||
import PhoneFindForm from './phoneFindForm.vue'
|
||||
import EmailFindForm from './emailFindForm.vue'
|
||||
import { globalStore } from '@/store'
|
||||
|
||||
const store = globalStore()
|
||||
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>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.login-wrapper {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
}
|
||||
.login_background {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-image: url(/img/login_background.png);
|
||||
}
|
||||
.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;
|
||||
position: relative;
|
||||
}
|
||||
@keyframes myfirst {
|
||||
0% {
|
||||
|
@ -122,18 +132,15 @@
|
|||
}
|
||||
/*background-image:linear-gradient(transparent, #000);*/
|
||||
.login_main {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.login-form {
|
||||
top: 15%;
|
||||
right: 15%;
|
||||
position: absolute;
|
||||
width: 450px;
|
||||
margin-left: 10%;
|
||||
margin-top: 20px;
|
||||
padding: 10px 0;
|
||||
position: absolute;
|
||||
top: 21.8%;
|
||||
}
|
||||
.login-header {
|
||||
margin-bottom: 20px;
|
||||
|
@ -154,7 +161,7 @@
|
|||
.login-header h2 {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-top: 40px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.login-oauth {
|
||||
display: flex;
|
||||
|
@ -165,6 +172,62 @@
|
|||
top: 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) {
|
||||
.login-form {
|
||||
width: 340px;
|
||||
|
@ -172,16 +235,27 @@
|
|||
}
|
||||
@media (max-width: 1000px) {
|
||||
.login_main {
|
||||
display: block;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.login_background_front {
|
||||
display: none;
|
||||
}
|
||||
.logo_background{
|
||||
padding-left:40px;
|
||||
}
|
||||
.login-form {
|
||||
width: 100%;
|
||||
padding: 20px 40px;
|
||||
right: 0 !important;
|
||||
top: 0 !important;
|
||||
top: 15%;
|
||||
}
|
||||
.login_background .version {
|
||||
padding: 0 20px;
|
||||
}
|
||||
.login_background .version p:first-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,22 @@
|
|||
<template>
|
||||
<div class="login_background">
|
||||
<div class="login_background_front"></div>
|
||||
<div class="login-wrapper">
|
||||
<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-form">
|
||||
<a-card>
|
||||
|
@ -26,6 +42,12 @@
|
|||
import loginApi from '@/api/auth/loginApi'
|
||||
import userCenterApi from '@/api/sys/userCenterApi'
|
||||
import dictApi from '@/api/dev/dictApi'
|
||||
import { globalStore } from '@/store'
|
||||
|
||||
const store = globalStore()
|
||||
const sysBaseConfig = computed(() => {
|
||||
return store.sysBaseConfig
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// 获取当前url
|
||||
|
@ -69,6 +91,13 @@
|
|||
window.location.href = '/login'
|
||||
})
|
||||
})
|
||||
// logo链接
|
||||
const handleLink = (e) => {
|
||||
if (!sysBaseConfig.value.SNOWY_SYS_COPYRIGHT_URL) {
|
||||
e?.stopPropagation()
|
||||
e?.preventDefault()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -7,13 +7,21 @@
|
|||
width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
.login-wrapper{
|
||||
width: 100vw;
|
||||
height:100vh;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
}
|
||||
.login_background {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-image: url(/img/login_background.png);
|
||||
position: relative;
|
||||
}
|
||||
.login_background_front {
|
||||
width: 450px;
|
||||
|
@ -92,45 +100,74 @@
|
|||
}
|
||||
/*background-image:linear-gradient(transparent, #000);*/
|
||||
.login_main {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.login-form {
|
||||
top: 15%;
|
||||
right: 15%;
|
||||
position: absolute;
|
||||
width: 450px;
|
||||
margin-left: 10%;
|
||||
margin-top: 20px;
|
||||
padding: 10px 0;
|
||||
position: absolute;
|
||||
top:21.8%
|
||||
}
|
||||
.login-header {
|
||||
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 {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-top: 40px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.login_config {
|
||||
position: absolute;
|
||||
top: 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) {
|
||||
.login-form {
|
||||
width: 340px;
|
||||
|
@ -138,7 +175,10 @@
|
|||
}
|
||||
@media (max-width: 1000px) {
|
||||
.login_main {
|
||||
display: block;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left:0;
|
||||
right:0;
|
||||
}
|
||||
.login_background_front {
|
||||
display: none;
|
||||
|
@ -146,7 +186,15 @@
|
|||
.login-form {
|
||||
width: 100%;
|
||||
padding: 20px 40px;
|
||||
right: 0 !important;
|
||||
top: 0 !important;
|
||||
top:15%
|
||||
}
|
||||
.logo_background{
|
||||
padding-left:40px;
|
||||
}
|
||||
.login_background .version{
|
||||
padding:0 20px;
|
||||
}
|
||||
.login_background .version p:first-child{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,22 @@
|
|||
<template>
|
||||
<div class="login_background">
|
||||
<div class="login_background_front"></div>
|
||||
<div class="login-wrapper">
|
||||
<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_config">
|
||||
<a-dropdown>
|
||||
|
@ -23,11 +39,7 @@
|
|||
<div class="login-form">
|
||||
<a-card>
|
||||
<div class="login-header">
|
||||
<div class="logo">
|
||||
<img :alt="sysBaseConfig.SNOWY_SYS_NAME" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
|
||||
<label>{{ sysBaseConfig.SNOWY_SYS_NAME }}</label>
|
||||
</div>
|
||||
<!--<h2>{{ $t('login.signInTitle') }}</h2>-->
|
||||
<h2>{{ $t('login.signInTitle') }}</h2>
|
||||
</div>
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane key="userAccount" :tab="$t('login.accountPassword')">
|
||||
|
@ -202,7 +214,6 @@
|
|||
rules.validCode = [required(proxy.$t('login.validError'), 'blur')]
|
||||
}
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
const loginCaptcha = () => {
|
||||
loginApi.getPicCaptcha().then((data) => {
|
||||
|
@ -224,15 +235,6 @@
|
|||
validCode: ruleForm.validCode,
|
||||
validCodeReqNo: ruleForm.validCodeReqNo
|
||||
}
|
||||
|
||||
// 获取token
|
||||
// loginApi.login(loginData).then((loginToken) => {
|
||||
// afterLogin(loginToken)
|
||||
// }).catch(() => {
|
||||
// loading.value = false
|
||||
// loginCaptcha()
|
||||
// })
|
||||
|
||||
// 获取token
|
||||
try {
|
||||
const loginToken = await loginApi.login(loginData)
|
||||
|
@ -249,6 +251,13 @@
|
|||
const configLang = (key) => {
|
||||
config.value.lang = key
|
||||
}
|
||||
// logo链接
|
||||
const handleLink = (e) => {
|
||||
if (!sysBaseConfig.value.SNOWY_SYS_COPYRIGHT_URL) {
|
||||
e?.stopPropagation()
|
||||
e?.preventDefault()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import 'login';
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
import loginApi from '@/api/auth/loginApi'
|
||||
import userCenterApi from '@/api/sys/userCenterApi'
|
||||
import dictApi from '@/api/dev/dictApi'
|
||||
import router from '@/router'
|
||||
import tool from '@/utils/tool'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { useGlobalStore } from '@/store'
|
||||
import routerUtil from '@/utils/routerUtil'
|
||||
import { useMenuStore } from '@/store/menu'
|
||||
import { userStore } from '@/store/user'
|
||||
|
||||
export const afterLogin = async (loginToken) => {
|
||||
const menuStore = useMenuStore()
|
||||
tool.data.set('TOKEN', loginToken)
|
||||
// 获取登录的用户信息
|
||||
const loginUser = await loginApi.getLoginUser()
|
||||
const globalStore = useGlobalStore()
|
||||
globalStore.setUserInfo(loginUser)
|
||||
tool.data.set('USER_INFO', loginUser)
|
||||
// 初始化用户信息
|
||||
await userStore().initUserInfo()
|
||||
|
||||
// 获取用户的菜单
|
||||
const menu = await userCenterApi.userLoginMenu()
|
||||
let indexMenu = routerUtil.getIndexMenu(menu).path
|
||||
tool.data.set('MENU', menu)
|
||||
await menuStore.fetchMenu()
|
||||
// 重置系统默认应用
|
||||
tool.data.set('SNOWY_MENU_MODULE_ID', menu[0].id)
|
||||
message.success('登录成功')
|
||||
|
|
|
@ -123,6 +123,7 @@
|
|||
|
||||
<script setup name="sysResourceMenuForm">
|
||||
import { required } from '@/utils/formRules'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import SnowflakeId from 'snowflake-id'
|
||||
import tool from '@/utils/tool'
|
||||
import menuApi from '@/api/sys/resource/menuApi'
|
||||
|
@ -141,7 +142,8 @@
|
|||
// 模块ID
|
||||
const moduleId = ref('')
|
||||
// 打开抽屉
|
||||
const onOpen = (record, module) => {
|
||||
const onOpen = (data, module) => {
|
||||
const record = cloneDeep(data)
|
||||
moduleId.value = module
|
||||
visible.value = true
|
||||
if (record) {
|
||||
|
|
|
@ -111,8 +111,8 @@
|
|||
</template>
|
||||
</s-table>
|
||||
</a-card>
|
||||
<Form ref="formRef" @successful="tableRef.refresh(true)" />
|
||||
<changeModuleForm ref="changeModuleFormRef" @successful="tableRef.refresh(true)" />
|
||||
<Form ref="formRef" @successful="handleSuccess" />
|
||||
<changeModuleForm ref="changeModuleFormRef" @successful="handleSuccess" />
|
||||
<Button ref="buttonRef" />
|
||||
</template>
|
||||
|
||||
|
@ -121,6 +121,7 @@
|
|||
import Form from './form.vue'
|
||||
import ChangeModuleForm from './changeModuleForm.vue'
|
||||
import Button from '../button/index.vue'
|
||||
import { useMenuStore } from '@/store/menu'
|
||||
const searchFormState = ref({})
|
||||
const tableRef = ref(null)
|
||||
const formRef = ref()
|
||||
|
@ -242,12 +243,24 @@
|
|||
]
|
||||
menuApi.menuDelete(params).then(() => {
|
||||
tableRef.value.refresh(true)
|
||||
refreshCacheMenu()
|
||||
})
|
||||
}
|
||||
// 批量删除
|
||||
const deleteBatchMenu = (params) => {
|
||||
menuApi.menuDelete(params).then(() => {
|
||||
tableRef.value.clearRefreshSelected()
|
||||
refreshCacheMenu()
|
||||
})
|
||||
}
|
||||
// 成功回调
|
||||
const handleSuccess = () => {
|
||||
tableRef.value.refresh(true)
|
||||
refreshCacheMenu()
|
||||
}
|
||||
// 刷新缓存的菜单
|
||||
const refreshCacheMenu = () => {
|
||||
const menuStore = useMenuStore()
|
||||
menuStore.fetchMenu()
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -108,6 +108,7 @@
|
|||
import { SearchOutlined } from '@ant-design/icons-vue'
|
||||
import roleApi from '@/api/sys/roleApi'
|
||||
import ScopeDefineOrg from './scopeDefineOrg.vue'
|
||||
import { userStore } from '@/store/user'
|
||||
|
||||
const visible = ref(false)
|
||||
const spinningLoading = ref(false)
|
||||
|
@ -248,7 +249,7 @@
|
|||
scopeDefineOrgModal.value.onOpen(data.id, checkKeysStr)
|
||||
} else {
|
||||
// 清理缓存中的结构,去掉就行
|
||||
handleDatascope(false, record.id, null)
|
||||
handleDatascope(false, data.id, null)
|
||||
}
|
||||
}
|
||||
// 自定义数据弹窗回调
|
||||
|
@ -365,6 +366,10 @@
|
|||
.then(() => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
// 刷新权限
|
||||
nextTick(() => {
|
||||
userStore().refreshUserLoginUserInfo()
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
|
|
|
@ -56,10 +56,9 @@
|
|||
</template>
|
||||
|
||||
<script setup name="grantResourceForm">
|
||||
import { nextTick } from 'vue'
|
||||
import tool from '@/utils/tool'
|
||||
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 firstShowMap = ref({})
|
||||
const emit = defineEmits({ successful: null })
|
||||
|
@ -283,19 +282,17 @@
|
|||
.then(() => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
refreshCacheMenu()
|
||||
refreshCache()
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
}
|
||||
// 刷新缓存的菜单
|
||||
const refreshCacheMenu = () => {
|
||||
nextTick(() => {
|
||||
userCenterApi.userLoginMenu().then((res) => {
|
||||
tool.data.set('MENU', res)
|
||||
})
|
||||
})
|
||||
// 刷新缓存
|
||||
const refreshCache = () => {
|
||||
const menuStore = useMenuStore()
|
||||
menuStore.fetchMenu()
|
||||
userStore().refreshUserLoginUserInfo()
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
|
|
|
@ -109,6 +109,7 @@
|
|||
import userApi from '@/api/sys/userApi'
|
||||
import roleApi from '@/api/sys/roleApi'
|
||||
import ScopeDefineOrg from './scopeDefineOrg.vue'
|
||||
import { userStore } from '@/store/user'
|
||||
|
||||
const visible = ref(false)
|
||||
const spinningLoading = ref(false)
|
||||
|
@ -249,7 +250,7 @@
|
|||
scopeDefineOrgModal.value.onOpen(data.id, checkKeysStr)
|
||||
} else {
|
||||
// 清理缓存中的结构,去掉就行
|
||||
handleDatascope(false, record.id, null)
|
||||
handleDatascope(false, data.id, null)
|
||||
}
|
||||
}
|
||||
// 自定义数据弹窗回调
|
||||
|
@ -366,6 +367,10 @@
|
|||
.then(() => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
// 刷新权限
|
||||
nextTick(() => {
|
||||
userStore().refreshUserLoginUserInfo()
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
|
|
|
@ -56,11 +56,10 @@
|
|||
</template>
|
||||
|
||||
<script setup name="grantResourceForm">
|
||||
import { nextTick } from 'vue'
|
||||
import tool from '@/utils/tool'
|
||||
import userApi from '@/api/sys/userApi'
|
||||
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 firstShowMap = ref({})
|
||||
const emit = defineEmits({ successful: null })
|
||||
|
@ -284,20 +283,19 @@
|
|||
.then(() => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
refreshCacheMenu()
|
||||
refreshCache()
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
}
|
||||
// 刷新缓存的菜单
|
||||
const refreshCacheMenu = () => {
|
||||
nextTick(() => {
|
||||
userCenterApi.userLoginMenu().then((res) => {
|
||||
tool.data.set('MENU', res)
|
||||
})
|
||||
})
|
||||
// 刷新缓存
|
||||
const refreshCache = () => {
|
||||
const menuStore = useMenuStore()
|
||||
menuStore.fetchMenu()
|
||||
userStore().refreshUserLoginUserInfo()
|
||||
}
|
||||
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
onOpen
|
||||
|
|
|
@ -46,14 +46,14 @@ public class AuthConfigure implements WebMvcConfigurer {
|
|||
|
||||
/**
|
||||
* 注册Sa-Token的注解拦截器,打开注解式鉴权功能
|
||||
*
|
||||
* <p>
|
||||
* 注解的方式有以下几中,注解既可以加在接口方法上,也可加在Controller类上:
|
||||
* 1.@SaCheckLogin: 登录认证 —— 只有登录之后才能进入该方法(常用)
|
||||
* 2.@SaCheckRole("admin"): 角色认证 —— 必须具有指定角色标识才能进入该方法(常用)
|
||||
* 3.@SaCheckPermission("user:add"): 权限认证 —— 必须具有指定权限才能进入该方法(常用)
|
||||
* 4.@SaCheckSafe: 二级认证校验 —— 必须二级认证之后才能进入该方法
|
||||
* 5.@SaCheckBasic: HttpBasic认证 —— 只有通过 Basic 认证后才能进入该方法
|
||||
*
|
||||
* </p>
|
||||
* 在Controller中创建一个接口,默认不需要登录也不需要任何权限都可以访问的,只有加了上述注解才会校验
|
||||
**/
|
||||
@Override
|
||||
|
@ -75,10 +75,11 @@ public class AuthConfigure implements WebMvcConfigurer {
|
|||
return new StpLogic(SaClientTypeEnum.C.getValue()).setConfig(saTokenConfig);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
@Autowired
|
||||
public void rewriteSaStrategy() {
|
||||
// 重写Sa-Token的注解处理器,增加注解合并功能
|
||||
SaStrategy.me.getAnnotation = AnnotatedElementUtils::getMergedAnnotation;
|
||||
SaStrategy.instance.getAnnotation = AnnotatedElementUtils::getMergedAnnotation;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,17 +99,13 @@ public class AuthConfigure implements WebMvcConfigurer {
|
|||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
Object permissionListObject;
|
||||
if (SaClientTypeEnum.B.getValue().equals(loginType)) {
|
||||
// return StpLoginUserUtil.getLoginUser().getPermissionCodeList();
|
||||
Object permissionListObject = commonCacheOperator.get(CacheConstant.AUTH_B_PERMISSION_LIST_CACHE_KEY+loginId);
|
||||
List<String> permissionList = JSONUtil.parseArray(permissionListObject).toList(String.class);
|
||||
return permissionList;
|
||||
permissionListObject = commonCacheOperator.get(CacheConstant.AUTH_B_PERMISSION_LIST_CACHE_KEY + loginId);
|
||||
} else {
|
||||
// return StpClientLoginUserUtil.getClientLoginUser().getPermissionCodeList();
|
||||
Object permissionListObject = commonCacheOperator.get(CacheConstant.AUTH_C_PERMISSION_LIST_CACHE_KEY+loginId);
|
||||
List<String> permissionList = JSONUtil.parseArray(permissionListObject).toList(String.class);
|
||||
return permissionList;
|
||||
permissionListObject = commonCacheOperator.get(CacheConstant.AUTH_C_PERMISSION_LIST_CACHE_KEY + loginId);
|
||||
}
|
||||
return JSONUtil.parseArray(permissionListObject).toList(String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -356,6 +356,19 @@ public class AuthServiceImpl implements AuthService {
|
|||
}
|
||||
// 执行登录
|
||||
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());
|
||||
// 角色id集合
|
||||
|
@ -374,16 +387,14 @@ public class AuthServiceImpl implements AuthService {
|
|||
// 获取权限码
|
||||
List<String> permissionCodeList = saBaseLoginUser.getDataScopeList().stream()
|
||||
.map(SaBaseLoginUser.DataScope::getApiUrl).collect(Collectors.toList());
|
||||
// 设置权限码
|
||||
saBaseLoginUser.setPermissionCodeList(permissionCodeList);
|
||||
// 权限码列表存入缓存
|
||||
commonCacheOperator.put(CacheConstant.AUTH_B_PERMISSION_LIST_CACHE_KEY + saBaseLoginUser.getId(),permissionCodeList);
|
||||
|
||||
// 获取角色码
|
||||
saBaseLoginUser.setRoleCodeList(roleCodeList);
|
||||
// 缓存用户信息,此处使用TokenSession为了指定时间内无操作则自动下线
|
||||
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()));
|
||||
// 填充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());
|
||||
// 角色id集合
|
||||
|
@ -417,6 +441,7 @@ public class AuthServiceImpl implements AuthService {
|
|||
// 获取权限码
|
||||
List<String> permissionCodeList = saBaseClientLoginUser.getDataScopeList().stream()
|
||||
.map(SaBaseClientLoginUser.DataScope::getApiUrl).collect(Collectors.toList());
|
||||
// 设置权限码
|
||||
saBaseClientLoginUser.setPermissionCodeList(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);
|
||||
// 缓存用户信息,此处使用TokenSession为了指定时间内无操作则自动下线
|
||||
StpClientUtil.getTokenSession().set("loginUser", saBaseClientLoginUser);
|
||||
// 返回token
|
||||
return StpClientUtil.getTokenInfo().tokenValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -436,10 +459,19 @@ public class AuthServiceImpl implements AuthService {
|
|||
**/
|
||||
@Override
|
||||
public SaBaseLoginUser getLoginUser() {
|
||||
// 获取当前缓存的用户信息
|
||||
SaBaseLoginUser saBaseLoginUser = StpLoginUserUtil.getLoginUser();
|
||||
// 获取B端用户信息
|
||||
saBaseLoginUser = loginUserApi.getUserById(saBaseLoginUser.getId());
|
||||
// 填充B端用户信息并更新缓存
|
||||
fillSaBaseLoginUserAndUpdateCache(saBaseLoginUser);
|
||||
// 去掉密码
|
||||
saBaseLoginUser.setPassword(null);
|
||||
// 去掉权限码
|
||||
saBaseLoginUser.setPermissionCodeList(null);
|
||||
// 去掉数据范围
|
||||
saBaseLoginUser.setDataScopeList(null);
|
||||
// 返回
|
||||
return saBaseLoginUser;
|
||||
}
|
||||
|
||||
|
@ -451,10 +483,19 @@ public class AuthServiceImpl implements AuthService {
|
|||
**/
|
||||
@Override
|
||||
public SaBaseClientLoginUser getClientLoginUser() {
|
||||
// 获取当前缓存的用户信息
|
||||
SaBaseClientLoginUser saBaseClientLoginUser = StpClientLoginUserUtil.getClientLoginUser();
|
||||
// 获取C端用户信息
|
||||
saBaseClientLoginUser = clientLoginUserApi.getClientUserById(saBaseClientLoginUser.getId());
|
||||
// 填充C端用户信息并更新缓存
|
||||
fillSaBaseClientLoginUserAndUpdateCache(saBaseClientLoginUser);
|
||||
// 去掉密码
|
||||
saBaseClientLoginUser.setPassword(null);
|
||||
// 去掉权限码
|
||||
saBaseClientLoginUser.setPermissionCodeList(null);
|
||||
// 去掉数据范围
|
||||
saBaseClientLoginUser.setDataScopeList(null);
|
||||
// 返回
|
||||
return saBaseClientLoginUser;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,16 +127,6 @@ public class DevSlideshowServiceImpl extends ServiceImpl<DevSlideshowMapper, Dev
|
|||
List<JSONObject> resultList = CollectionUtil.newArrayList();
|
||||
List<DevSlideshow> slideshowList = this.list(new LambdaQueryWrapper<DevSlideshow>().eq(DevSlideshow::getStatus,
|
||||
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) -> {
|
||||
JSONArray slideshowPlaceArray = JSONUtil.parseArray(item.getPlace());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -212,6 +212,9 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
|||
|
||||
private void checkParam(SysMenuAddParam sysMenuAddParam) {
|
||||
SysResourceMenuTypeEnum.validate(sysMenuAddParam.getMenuType());
|
||||
if(sysMenuAddParam.getTitle().contains(StrUtil.DASHED)) {
|
||||
throw new CommonException("title不可包含特殊字符【-】");
|
||||
}
|
||||
if(SysResourceMenuTypeEnum.MENU.getValue().equals(sysMenuAddParam.getMenuType())) {
|
||||
if(ObjectUtil.isEmpty(sysMenuAddParam.getName())) {
|
||||
throw new CommonException("name不能为空");
|
||||
|
@ -227,7 +230,6 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
|||
sysMenuAddParam.setName(null);
|
||||
sysMenuAddParam.setComponent(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
|
@ -284,6 +286,9 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
|||
|
||||
private void checkParam(SysMenuEditParam sysMenuEditParam) {
|
||||
SysResourceMenuTypeEnum.validate(sysMenuEditParam.getMenuType());
|
||||
if(sysMenuEditParam.getTitle().contains(StrUtil.DASHED)) {
|
||||
throw new CommonException("title不可包含特殊字符【-】");
|
||||
}
|
||||
if(SysResourceMenuTypeEnum.MENU.getValue().equals(sysMenuEditParam.getMenuType())) {
|
||||
if(ObjectUtil.isEmpty(sysMenuEditParam.getName())) {
|
||||
throw new CommonException("name不能为空");
|
||||
|
|
Loading…
Reference in New Issue