From 790d5d22ca99dcb1a091b54a41e8d6c374af20ad Mon Sep 17 00:00:00 2001 From: likun <906102152@qq.com> Date: Fri, 16 May 2025 10:07:49 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84ui=E5=BA=95=E5=B1=82=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E8=B7=AF=E7=94=B1+=E5=8A=A8=E6=80=81=E8=8F=9C?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/apps/user/service/service_user.go | 1 + ui/src/components/restful/table.vue | 25 +++- ui/src/components/restful/tableUser.vue | 4 +- ui/src/permission.js | 83 +++++------ ui/src/router/index.js | 76 +--------- ui/src/stores/localCache.js | 3 + ui/src/stores/piniaIndex.js | 3 + ui/src/stores/project.js | 30 ---- ui/src/stores/user.js | 189 ++++++++++++++++++++++++ ui/src/utils/request.js | 8 +- ui/src/views/Home.vue | 16 +- ui/src/views/Login.vue | 16 +- ui/src/views/project/project.vue | 3 +- ui/src/views/project/project_op.vue | 17 ++- ui/src/views/user/character.vue | 3 +- ui/src/views/user/user.vue | 3 +- ui/src/views/welcome.vue | 3 +- 17 files changed, 291 insertions(+), 192 deletions(-) create mode 100644 ui/src/stores/piniaIndex.js delete mode 100644 ui/src/stores/project.js create mode 100644 ui/src/stores/user.js diff --git a/admin/apps/user/service/service_user.go b/admin/apps/user/service/service_user.go index 61e04a0..e624f39 100644 --- a/admin/apps/user/service/service_user.go +++ b/admin/apps/user/service/service_user.go @@ -14,6 +14,7 @@ import ( func (svc *Service) CheckToken(token string, userId int) error { if err := tokenlib.ValidToken(token, userId); err != nil { + xlog.Warnf("valid token:%v user:%v error:%v", token, userId, err) return err } diff --git a/ui/src/components/restful/table.vue b/ui/src/components/restful/table.vue index db29c1e..2c8ebe0 100644 --- a/ui/src/components/restful/table.vue +++ b/ui/src/components/restful/table.vue @@ -14,11 +14,19 @@ import LocalCache from "@/stores/localCache.js"; import empty from '@/components/restful/empty.vue'; import {getWhereConditionDesc} from "@/utils/string.js"; import UserDetail from "@/components/game/userDetail.vue"; +import router from "@/router/index.js"; const props = defineProps({ rowClickDialogBtns: Array, }) +let rowClickDialogBtns = [] +if (props.rowClickDialogBtns) { + rowClickDialogBtns = props.rowClickDialogBtns +} + +// console.log("btns:", rowClickDialogBtns) + const cachedResource = LocalCache.getCache("resource"); const listRsp = ref({fields_desc: [], rows: []}) @@ -28,7 +36,7 @@ const resource_raw_node = cachedResource; const hasListPermit = resource_raw_node.meta.methods.get !== undefined && resource_raw_node.meta.methods.get === true; const globalClickBtns = resource_raw_node.meta.global_click_btns ? resource_raw_node.meta.global_click_btns : []; let rowClickBtns = resource_raw_node.meta.row_click_btns ? resource_raw_node.meta.row_click_btns : [] -rowClickBtns.push(...props.rowClickDialogBtns) +rowClickBtns.push(...rowClickDialogBtns) const rowClickBtnVisibleList = reactive(rowClickBtns.map(() => false)) const rowClickBtnSelectRow = ref(null) @@ -176,6 +184,7 @@ const dialogObjectForm = ref({ }) const route = useRoute() +let hasRouteQueryParams = false if (route.query.from != undefined && route.query.from != "") { Object.keys((route.query)).forEach(key => { const value = route.query[key] @@ -183,10 +192,11 @@ if (route.query.from != undefined && route.query.from != "") { return } dialogObjectForm.value[key] = value - console.log("进入页面,来自查询参数的数据:", key, value) + // console.log("进入页面,来自查询参数的数据:", key, value) }) - console.log("进入页面,来自查询参数的数据:", route.query) + // console.log("进入页面,来自查询参数的数据:", route.query) dialogAddVisible.value = true + hasRouteQueryParams = true } @@ -390,7 +400,7 @@ function deleteItem(row) { } const handleCloseDialog = () => { - console.log("关闭添加/编辑弹窗") + // console.log("关闭添加/编辑弹窗") dialogAddVisible.value = false dialogEditVisible.value = false dialogObjectForm.value = { @@ -399,6 +409,13 @@ const handleCloseDialog = () => { selectedItem.value = null selectedItemNum.value = 0 selectedItemBag.value = null + + if (hasRouteQueryParams) { + router.replace({ + path: route.path, + query: {} + }) + } } const loadingRemoteItems = ref(false) diff --git a/ui/src/components/restful/tableUser.vue b/ui/src/components/restful/tableUser.vue index 32e2fe7..17b3da0 100644 --- a/ui/src/components/restful/tableUser.vue +++ b/ui/src/components/restful/tableUser.vue @@ -6,6 +6,7 @@ import {useRoute} from 'vue-router'; import LocalCache from "@/stores/localCache.js"; import {getPermissionTreeByProjects, getSelectedPermissions} from "@/utils/permission.js"; import empty from '@/components/restful/empty.vue'; +import {getProjects} from "@/stores/user.js"; const cachedResource = LocalCache.getCache("resource"); @@ -29,8 +30,7 @@ const item = ref({ }) // console.log("enter table, resource:", cachedResource) - -const projectsRoute = LocalCache.getCache("projectsRoute") +const projectsRoute = getProjects() const permitTreeDefaultProps = {children: 'children', label: 'label'} const permitTree = ref([]) diff --git a/ui/src/permission.js b/ui/src/permission.js index 904ad52..4c01d68 100644 --- a/ui/src/permission.js +++ b/ui/src/permission.js @@ -1,59 +1,46 @@ -import router, {isGetUserInfo, projectOpTreeRoutes, setProjectOperationRoutes} from './router' -import {cachedProject, cachedProjectResource} from '@/stores/project.js' -import {getUserInfo} from "@/api/sys.js"; -import LocalCache from "@/stores/localCache.js"; -import ExpireCache from "@/stores/expireCache.js"; +import router from './router' + + +import {getToken} from '@/stores/user.js' +import userStore from "@/stores/user.js"; router.beforeEach((to, from, next) => { - if (to.path === '/login') { - console.log("进入登录页面") - next() - } else { - const token = ExpireCache.getCache("token"); - if (!token) { - // 没有token - console.log("重定向到login") - next('/login?redirect=/home') // 没有token都重定向到登录页面 - return - } + const token = getToken(); + // console.log("token:", token) + // alert(token.token) + if (token && token.token !== undefined && token.token !== '') { + if (to.path === '/login') { + // 有token 跳过登录 + console.log("有token走登录,跳过登录", token) + next({path: '/'}) + } else { - // 有token就去拉最新的用户数据,动态生成菜单 - if (to.path.startsWith('/project')) { - const pathSegments = to.path.split("/").filter(segment => segment !== ""); - if (pathSegments.length === 3) { + console.log("跳到页面:" + to.path + ",当前所有路由:", router.getRoutes()) - // const projectId = pathSegments[pathSegments.length - 2] - // const resource = pathSegments[pathSegments.length - 1] - - // 进行资源操作点击 - const oldResource = LocalCache.getCache("resource") - if (oldResource.path !== to.path) { - LocalCache.setCache("resource", to) - } + if (!userStore().hasGetUserInfo()) { + // console.log("访问页面获取用户信息。。。", to.path) + userStore().getUserInfo().then(() => { + // console.log("获取用户信息成功,继续:", to.path) + next({...to, replace: true}) + }).catch(err => { + userStore().logout().then(() => { + ElMessage.error(err) + next({path: '/'}) + }) + }) + } else { + console.log("获取过用户数据,跳过获取。。。") + next() } } - - if (isGetUserInfo.value === false) { - getUserInfo().then((res) => { - const projectList = ref([]) - // res.data.user - // res.data.token - projectList.value = res.data.projects - LocalCache.setCache('user', res.data.user_info) - setProjectOperationRoutes(projectList.value) - // console.log("all routes:", router.getRoutes()) - next({...to, replace: true}); // 在已有页面里查找 - }, (err) => { - console.log("跳转路径:", to.path, " 报错:", err) - LocalCache.deleteCache("token") - LocalCache.deleteCache("user") - LocalCache.deleteCache("projectsRoute") - }) - } else { - console.log("访问页面" + to.path + "已经请求过用户数据,跳过获取") - // console.log("op tree routes length valid:", projectOpTreeRoutes.value.length) + } else { + if (to.path === '/login') { + // alert("登录页面重复走登录,排查为什么!") next() + return } + console.log("token无效,走登录。", token) + next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 } }) diff --git a/ui/src/router/index.js b/ui/src/router/index.js index 96b8a91..92b1324 100644 --- a/ui/src/router/index.js +++ b/ui/src/router/index.js @@ -2,10 +2,6 @@ import {createRouter, createWebHistory} from 'vue-router' import Home from '../views/Home.vue' import LocalCache from "@/stores/localCache.js"; -export const isGetUserInfo = ref(false) // 是否获取过后端路由接口,用于第一次进页面或者刷新页面拉取路由恢复菜单 -export const projectOpTreeRoutes = ref([]) // 项目操作的树形路由,用于菜单树生成 -export const projectOpFlatRoutes = ref([]) // 项目操作的展平路由,用于直接挂钩到父组件(即home页面),否则点击子菜单无法在父页面内容区显示) - export const constProjectResourceRoute = { path: '/project', name: 'project', @@ -62,7 +58,7 @@ export const constHomeChildrenRoutes = [ constProjectResourceRoute, ] -const homeRoute = { +export const homeRoute = { path: '/', name: 'home', component: Home, @@ -83,73 +79,3 @@ const router = createRouter({ }) export default router - -export function setProjectOperationRoutes(projectList) { - // console.log("resourceList:", projectList) - - isGetUserInfo.value = true - projectOpTreeRoutes.value = [] - projectOpFlatRoutes.value = [] - - for (let i = 0; i < projectList.length; i++) { - const project = projectList[i] - let projectHasAnyResourcePermission = false // 判断项目是否至少有一个资源权限,否则就不显示这个项目菜单 - const projectRoute = { - path: '/project/' + project.project_id, - name: project.project_id, - meta: { - projectId: project.project_id, - projectName: project.project_name, - }, - // component: () => { - // return import('@/components/restful/table.vue') - // }, - children: [], - props: true - } - const resourceList = project.resource_list - resourceList.forEach((resource) => { - const routePath = projectRoute.path + "/" + resource.resource - const resourceRoute = { - path: routePath, - name: projectRoute.name + "_" + resource.resource, - meta: { - desc: resource.desc, - projectId: project.project_id, - resource: resource.resource, - resource_url: routePath, - methods: {}, - global_click_btns: resource.global_btns, - row_click_btns: resource.row_btns, - }, - component: () => import('@/views/project/project_op.vue'), - props: true - } - if (resource.resource === 'cdkey') { - resourceRoute.component = () => import('@/views/project/project_cdkey.vue') - } - resource.show_methods.forEach((method) => { - if (method == "get") { - projectHasAnyResourcePermission = true - } - resourceRoute.meta.methods[method] = true - }) - - // router.addRoute(projectRoute) - - projectRoute.children.push(resourceRoute) - projectOpFlatRoutes.value.push(resourceRoute) - // console.log("add resource route:", resourceRoute) - }) - - if (projectHasAnyResourcePermission) { - projectOpTreeRoutes.value.push(projectRoute) - } - homeRoute.children = constHomeChildrenRoutes.concat(projectOpFlatRoutes.value) - router.addRoute(homeRoute) - } - - LocalCache.setCache("projectsRoute", projectList) - LocalCache.setCache("homeRoute", homeRoute) - // console.log("重新获取了用户数据,刷新路由表:", router.getRoutes()) -} diff --git a/ui/src/stores/localCache.js b/ui/src/stores/localCache.js index 5906e4c..b13ed90 100644 --- a/ui/src/stores/localCache.js +++ b/ui/src/stores/localCache.js @@ -1,6 +1,7 @@ class LocalCache { // 添加 setCache(key, value) { + // console.log("设置cache:", key, value) window.localStorage.setItem(key, JSON.stringify(value)) } @@ -11,6 +12,8 @@ class LocalCache { if (value) { return JSON.parse(value) } + // console.log("获取到cache token:", key, value) + return undefined } // 删除 diff --git a/ui/src/stores/piniaIndex.js b/ui/src/stores/piniaIndex.js new file mode 100644 index 0000000..f10f389 --- /dev/null +++ b/ui/src/stores/piniaIndex.js @@ -0,0 +1,3 @@ +const store = createPinia() + +export default store \ No newline at end of file diff --git a/ui/src/stores/project.js b/ui/src/stores/project.js deleted file mode 100644 index 8721b2e..0000000 --- a/ui/src/stores/project.js +++ /dev/null @@ -1,30 +0,0 @@ -import {ref} from 'vue' -import {defineStore} from 'pinia' - -/* - 项目pinia缓存,刷新页面就清空了,适合一些页面跳转传参逻辑 - */ - -export const cachedProject = defineStore('clickedProject', () => { - const project = ref({}) - const set = (setProject) => project.value = setProject - const get = () => project.value - return { - set, - get, - persist: true - } -}, {persist: true}) - -export const cachedProjectResource = defineStore('clickedProject', () => { - const setResource = ref({}) - const set = (setResource) => setResource.value = setResource - const get = () => setResource.value - return { - set, - get, - persist: true - } -}, { - persist: true -}) \ No newline at end of file diff --git a/ui/src/stores/user.js b/ui/src/stores/user.js new file mode 100644 index 0000000..e8f33a0 --- /dev/null +++ b/ui/src/stores/user.js @@ -0,0 +1,189 @@ +import LocalCache from "@/stores/localCache.js"; +import {getUserInfo, login} from "@/api/sys.js"; +import {defineStore} from 'pinia'; +import router, {constHomeChildrenRoutes, homeRoute} from "@/router/index.js"; + +const userStore = defineStore( + 'user', + { + state: () => ({ + tokenInfo: getToken(), // token expire_at + userInfo: getCacheUserInfo(), // userid nick_name icon character permissions + projects: getProjects(), // project_id project_name resource_list resource_total_list + dynamicMenuItems: [], // 动态项目菜单 + dynamicRouteChildren: [], // 动态项目路由 + isGetUserInfo: false, + }), + actions: { + hasGetUserInfo() { + this.generateDynamicRoutes() + return this.isGetUserInfo + }, + getDynamicRouteChildren() { + return this.dynamicRouteChildren; + }, + pushDynamicRouteChildren(child) { + this.dynamicRouteChildren.push(child); + }, + pushDynamicMenuItems(item) { + this.dynamicMenuItems.push(item); + }, + clearDynamicRouteChildren(child) { + this.dynamicRouteChildren = [] + }, + clearDynamicMenuItems(item) { + this.dynamicMenuItems = [] + }, + login(userName, password) { + return new Promise((resolve, reject) => { + login({user: userName, password: password}).then(res => { + this.userInfo = res.data.user_info + this.tokenInfo = res.data.token_info + this.projects = res.data.projects + // console.log("登录响应成功,设置token:", this.tokenInfo) + setToken(this.tokenInfo) + setUserInfo(this.userInfo) + setProjects(this.projects) + this.generateDynamicRoutes() + this.isGetUserInfo = true + resolve(); + }).catch(err => { + reject(err) + }) + }) + }, + + getUserInfo() { + return new Promise((resolve, reject) => { + getUserInfo().then(res => { + this.userInfo = res.data.user_info + this.tokenInfo = res.data.token_info + this.projects = res.data.projects + setToken(this.tokenInfo) + setUserInfo(this.userInfo) + setProjects(this.projects) + this.generateDynamicRoutes() + this.isGetUserInfo = true + resolve(); + }).catch(err => { + reject(err) + }) + }) + }, + + logout() { + console.log("走logout清理缓存。。") + this.userInfo = null + this.tokenInfo = null + this.projects = null + clearUserInfo() + clearToken() + clearProjects() + }, + + generateDynamicRoutes() { + this.clearDynamicRouteChildren() + this.clearDynamicMenuItems() + const projectList = this.projects; + for (let i = 0; i < projectList.length; i++) { + const project = projectList[i] + let projectHasAnyResourcePermission = false // 判断项目是否至少有一个资源权限,否则就不显示这个项目菜单 + const projectRoute = { + path: '/project/' + project.project_id, + name: project.project_id, + meta: { + projectId: project.project_id, + projectName: project.project_name, + }, + // component: () => { + // return import('@/components/restful/table.vue') + // }, + children: [], + props: true + } + const resourceList = project.resource_list + resourceList.forEach((resource) => { + const routePath = projectRoute.path + "/" + resource.resource + const resourceRoute = { + path: routePath, + name: projectRoute.name + "_" + resource.resource, + meta: { + desc: resource.desc, + projectId: project.project_id, + resource: resource.resource, + resource_url: routePath, + methods: {}, + global_click_btns: resource.global_btns, + row_click_btns: resource.row_btns, + }, + component: () => import('@/views/project/project_op.vue'), + props: true + } + if (resource.resource === 'cdkey') { + resourceRoute.component = () => import('@/views/project/project_cdkey.vue') + } + resource.show_methods.forEach((method) => { + if (method == "get") { + projectHasAnyResourcePermission = true + } + resourceRoute.meta.methods[method] = true + }) + + // router.addRoute(projectRoute) + + projectRoute.children.push(resourceRoute) + this.pushDynamicRouteChildren(resourceRoute) + // console.log("add resource route:", resourceRoute) + }) + + if (projectHasAnyResourcePermission) { + this.pushDynamicMenuItems(projectRoute) + } + } + + console.log("pinia重新生成路由。。") + + homeRoute.children = constHomeChildrenRoutes.concat(this.getDynamicRouteChildren()) + router.addRoute(homeRoute) + } + } + } +) + +export const getToken = () => { + return LocalCache.getCache("tokenInfo") +} + +const setToken = (token) => { + LocalCache.setCache("tokenInfo", token) +} + +export const clearToken = () => { + LocalCache.deleteCache("tokenInfo") +} + +const getCacheUserInfo = () => { + return LocalCache.getCache("userInfo") +} + +export const clearUserInfo = () => { + LocalCache.deleteCache("userInfo") +} + +const setUserInfo = (userInfo) => { + LocalCache.setCache("userInfo", userInfo) +} + +export const getProjects = () => { + return LocalCache.getCache("projects") +} + +export const clearProjects = () => { + LocalCache.deleteCache("projects") +} + +const setProjects = (projects) => { + LocalCache.setCache("projects", projects) +} + +export default userStore \ No newline at end of file diff --git a/ui/src/utils/request.js b/ui/src/utils/request.js index 39d48db..3689bd8 100644 --- a/ui/src/utils/request.js +++ b/ui/src/utils/request.js @@ -6,6 +6,7 @@ import LocalCache from "@/stores/localCache.js"; // import ExpireCache from "@/stores/expireCache"; import errcodeDetail from '@/components/widget/errcodeDetail.vue' +import userStore from "@/stores/user.js"; // 创建axios实例 const service = axios.create({ @@ -28,11 +29,11 @@ const service = axios.create({ const reqInterceptor = (config) => { - let user = LocalCache.getCache("user"); - let token = LocalCache.getCache("token"); + let user = userStore().userInfo; + let token = userStore().tokenInfo; let userId = user ? parseInt(user.user_id, 10) : 0; - token = token ? token : ""; + token = token ? token.token : ""; config.headers = config.headers || {} config.headers.UserId = userId; @@ -63,6 +64,7 @@ const resInterceptor = (res) => { if (code === 5) { // token过期 console.log("token无效,重新登录!") + userStore().logout() location.href = "/login" return Promise.reject() } diff --git a/ui/src/views/Home.vue b/ui/src/views/Home.vue index ac6080b..d4449eb 100644 --- a/ui/src/views/Home.vue +++ b/ui/src/views/Home.vue @@ -2,19 +2,22 @@ import {useRoute, useRouter} from 'vue-router' import {computed} from 'vue' import avatarUrl from '@/assets/icon/header.png' -import {constUserChildrenRoutes, isGetUserInfo, projectOpTreeRoutes} from '@/router/index.js' +import {constUserChildrenRoutes} from '@/router/index.js' import LocalCache from "@/stores/localCache.js"; import welcome from '@/views/welcome.vue' +import userStore from "@/stores/user.js"; const router = useRouter() const route = useRoute() -const userInfo = LocalCache.getCache("user") -const nickName = userInfo.nick_name +const nickName = userStore().userInfo.nick_name // 计算当前激活的菜单项 const activeMenu = computed(() => route.path) +const dynamicMenuItems = userStore().dynamicMenuItems + +// console.log("动态菜单:", dynamicMenuItems) const hasClickedMenu = ref(false) @@ -48,10 +51,7 @@ function logout() { cancelButtonText: '取消', type: 'warning' }).then(() => { - isGetUserInfo.value = false - LocalCache.deleteCache("user") - LocalCache.deleteCache("token") - LocalCache.deleteCache("projectsRoute") + userStore().logout() router.push('/login') }).catch(() => { }); @@ -97,7 +97,7 @@ function logout() { -