fix(web): 切换账号残留上一账号的标签/菜单;审核按钮不再对已通过的行显示
问题 1(创作者登录后还能看到「工作台」)根因:tagsView.delAllVisitedViews() 会「保留 affix 标签」, 它判断的是 userInfo 是否还在,而 userStore.reset() 是先清标签、后清 userInfo —— 顺序反了, 写死路由表里 affix 的「工作台」标签就跨账号活了下来。整页刷新时不复现,所以只有"退出后不刷新直接登录" 才看得到(PM 复现不到就是这个原因)。修法与加固: - userStore.reset():先清账号信息再清标签,并调 permissionStore.clearRoutes() 一并清菜单状态 - permissionStore 增加 routersOwner(当前菜单属于哪个账号)+ clearRoutes();守卫发现账号变了会先 resetRouter() 摘掉上一个账号动态加的路由,再重建(避免两套菜单并存、也避免创作者还能直接进 /dashboard) - 守卫里"菜单拉取失败沿用上次缓存"这条:换账号后不再沿用(宁可先给空菜单) 问题 2(创作者入驻/审核):真实 UI 全流程(新增→审核→开通→凭据→登录落地)本身能跑通, 但复现出「审核」按钮对**所有状态**都显示 —— 后端状态机是 通过可从 pending/rejected/frozen 发起、 驳回只允许 pending、冻结只允许 approved,对「已通过」的行点审核提交必然 200003 红错。修法: - 已通过的行不再显示「审核」(保留 冻结/重置密码/开通账号) - 审核弹窗里的「驳回」只对 pending 显示,其余状态只能通过 顺带修:views/common/DashboardView 取列表时 res 可能为 undefined(拦截器把业务错误吞成 undefined), 账号切换瞬间会抛 Cannot read properties of undefined,已加可选链。
This commit is contained in:
1 parent
8ce43ccd4c
commit
4509323ba8
6 files changed
+57
-11
No files matched your search
+18
-4
@@ -1,4 +1,4 @@
|
|||||||
import router from './router'
|
import router, { resetRouter } from './router'
|
||||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
import { useTitle } from '@/hooks/web/useTitle'
|
import { useTitle } from '@/hooks/web/useTitle'
|
||||||
@@ -25,10 +25,21 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
next({ path: '/' })
|
next({ path: '/' })
|
||||||
} else {
|
} else {
|
||||||
if (permissionStore.getIsAddRouters) {
|
if (permissionStore.getIsAddRouters) {
|
||||||
next()
|
// 已加过路由:只有还是同一个账号才能直接放行。
|
||||||
return
|
// 换账号时(例如超管退出后创作者登录)必须重建,否则侧边栏/路由会残留上一个账号的
|
||||||
|
const username = userStore.getUserInfo?.username || ''
|
||||||
|
if (permissionStore.getRoutersOwner === username) {
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 摘掉上一个账号动态加进去的路由,避免两套菜单并存
|
||||||
|
resetRouter()
|
||||||
|
permissionStore.setIsAddRouters(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const username = userStore.getUserInfo?.username || ''
|
||||||
|
const ownerChanged = permissionStore.getRoutersOwner !== username
|
||||||
|
|
||||||
// 开发者可根据实际情况进行修改
|
// 开发者可根据实际情况进行修改
|
||||||
const roleRouters = userStore.getRoleRouters || []
|
const roleRouters = userStore.getRoleRouters || []
|
||||||
|
|
||||||
@@ -38,7 +49,6 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
// 每次进入应用都重新拉菜单与权限:后台改了菜单或角色权限,刷新即可生效,不必重新登录
|
// 每次进入应用都重新拉菜单与权限:后台改了菜单或角色权限,刷新即可生效,不必重新登录
|
||||||
let routers = (userStore.getRoleRouters || []) as AppCustomRouteRecordRaw[]
|
let routers = (userStore.getRoleRouters || []) as AppCustomRouteRecordRaw[]
|
||||||
try {
|
try {
|
||||||
const username = userStore.getUserInfo?.username || ''
|
|
||||||
const [profileRes, menuRes] = await Promise.all([
|
const [profileRes, menuRes] = await Promise.all([
|
||||||
getProfileApi(),
|
getProfileApi(),
|
||||||
getAdminRoleApi({ roleName: username })
|
getAdminRoleApi({ roleName: username })
|
||||||
@@ -49,6 +59,9 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
if (menuRes?.data?.length) {
|
if (menuRes?.data?.length) {
|
||||||
routers = menuRes.data
|
routers = menuRes.data
|
||||||
userStore.setRoleRouters(routers)
|
userStore.setRoleRouters(routers)
|
||||||
|
} else if (ownerChanged) {
|
||||||
|
// 换账号后菜单拉不到就别沿用上一个账号的缓存,宁可先给空菜单
|
||||||
|
routers = []
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// 拉取失败时退回上次缓存的菜单,不阻塞进入
|
// 拉取失败时退回上次缓存的菜单,不阻塞进入
|
||||||
@@ -64,6 +77,7 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
permissionStore.getAddRouters.forEach((route) => {
|
permissionStore.getAddRouters.forEach((route) => {
|
||||||
router.addRoute(route as unknown as RouteRecordRaw) // 动态添加可访问路由表
|
router.addRoute(route as unknown as RouteRecordRaw) // 动态添加可访问路由表
|
||||||
})
|
})
|
||||||
|
permissionStore.setRoutersOwner(username)
|
||||||
|
|
||||||
// 根路径按"这个账号能进的第一个菜单"落地:路由表里写死的 redirect 是 /dashboard/index,
|
// 根路径按"这个账号能进的第一个菜单"落地:路由表里写死的 redirect 是 /dashboard/index,
|
||||||
// 创作者没有运营后台权限,访问 '/' 会直接掉 404
|
// 创作者没有运营后台权限,访问 '/' 会直接掉 404
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ export interface PermissionState {
|
|||||||
addRouters: AppRouteRecordRaw[]
|
addRouters: AppRouteRecordRaw[]
|
||||||
isAddRouters: boolean
|
isAddRouters: boolean
|
||||||
menuTabRouters: AppRouteRecordRaw[]
|
menuTabRouters: AppRouteRecordRaw[]
|
||||||
|
/** 当前这套菜单属于哪个账号:账号变了必须重建,不能沿用上一个账号的路由 */
|
||||||
|
routersOwner: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const usePermissionStore = defineStore('permission', {
|
export const usePermissionStore = defineStore('permission', {
|
||||||
@@ -20,7 +22,8 @@ export const usePermissionStore = defineStore('permission', {
|
|||||||
routers: [],
|
routers: [],
|
||||||
addRouters: [],
|
addRouters: [],
|
||||||
isAddRouters: false,
|
isAddRouters: false,
|
||||||
menuTabRouters: []
|
menuTabRouters: [],
|
||||||
|
routersOwner: ''
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getRouters(): AppRouteRecordRaw[] {
|
getRouters(): AppRouteRecordRaw[] {
|
||||||
@@ -34,6 +37,9 @@ export const usePermissionStore = defineStore('permission', {
|
|||||||
},
|
},
|
||||||
getMenuTabRouters(): AppRouteRecordRaw[] {
|
getMenuTabRouters(): AppRouteRecordRaw[] {
|
||||||
return this.menuTabRouters
|
return this.menuTabRouters
|
||||||
|
},
|
||||||
|
getRoutersOwner(): string {
|
||||||
|
return this.routersOwner
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
@@ -75,10 +81,21 @@ export const usePermissionStore = defineStore('permission', {
|
|||||||
},
|
},
|
||||||
setMenuTabRouters(routers: AppRouteRecordRaw[]): void {
|
setMenuTabRouters(routers: AppRouteRecordRaw[]): void {
|
||||||
this.menuTabRouters = routers
|
this.menuTabRouters = routers
|
||||||
|
},
|
||||||
|
setRoutersOwner(owner: string): void {
|
||||||
|
this.routersOwner = owner
|
||||||
|
},
|
||||||
|
/** 登出 / 切换账号时清空菜单与路由状态(由 userStore.reset 调用) */
|
||||||
|
clearRoutes(): void {
|
||||||
|
this.routers = []
|
||||||
|
this.addRouters = []
|
||||||
|
this.menuTabRouters = []
|
||||||
|
this.isAddRouters = false
|
||||||
|
this.routersOwner = ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
persist: {
|
persist: {
|
||||||
paths: ['routers', 'addRouters', 'menuTabRouters']
|
paths: ['routers', 'addRouters', 'menuTabRouters', 'routersOwner']
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ElMessageBox } from 'element-plus'
|
|||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { loginOutApi } from '@/api/login'
|
import { loginOutApi } from '@/api/login'
|
||||||
import { useTagsViewStore } from './tagsView'
|
import { useTagsViewStore } from './tagsView'
|
||||||
|
import { usePermissionStore } from './permission'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
|
|
||||||
interface UserState {
|
interface UserState {
|
||||||
@@ -78,10 +79,15 @@ export const useUserStore = defineStore('user', {
|
|||||||
},
|
},
|
||||||
reset() {
|
reset() {
|
||||||
const tagsViewStore = useTagsViewStore()
|
const tagsViewStore = useTagsViewStore()
|
||||||
tagsViewStore.delAllViews()
|
const permissionStore = usePermissionStore()
|
||||||
|
// 顺序要紧:delAllVisitedViews() 会「保留 affix 标签」,而它判断的正是 userInfo 是否还在。
|
||||||
|
// 先清账号信息再清标签,否则写死路由表里 affix 的「工作台」会在切换账号后残留。
|
||||||
this.setToken('')
|
this.setToken('')
|
||||||
this.setUserInfo(undefined)
|
this.setUserInfo(undefined)
|
||||||
this.setRoleRouters([])
|
this.setRoleRouters([])
|
||||||
|
tagsViewStore.delAllViews()
|
||||||
|
// 菜单/路由一起清:否则换账号后 permissionStore 里还是上一个账号的路由表
|
||||||
|
permissionStore.clearRoutes()
|
||||||
router.replace('/login')
|
router.replace('/login')
|
||||||
},
|
},
|
||||||
logout() {
|
logout() {
|
||||||
|
|||||||
@@ -231,6 +231,8 @@ const getRole = async () => {
|
|||||||
permissionStore.getAddRouters.forEach((route) => {
|
permissionStore.getAddRouters.forEach((route) => {
|
||||||
addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
|
addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
|
||||||
})
|
})
|
||||||
|
// 记住这套菜单属于哪个账号:换账号时守卫据此重建
|
||||||
|
permissionStore.setRoutersOwner(params.roleName)
|
||||||
permissionStore.setIsAddRouters(true)
|
permissionStore.setIsAddRouters(true)
|
||||||
push({ path: redirect.value || landingPath() })
|
push({ path: redirect.value || landingPath() })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,9 @@ onMounted(async () => {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await pageDramasApi({ pageIndex: 1, pageSize: 200 })
|
const res = await pageDramasApi({ pageIndex: 1, pageSize: 200 })
|
||||||
dramas.value = res.data?.list || []
|
// 接口失败时 axios 拦截器会把业务错误吞成 undefined,这里不能让页面跟着崩
|
||||||
total.value = res.data?.total || 0
|
dramas.value = res?.data?.list || []
|
||||||
|
total.value = res?.data?.total || 0
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,12 @@ const {
|
|||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="table-actions">
|
<div class="table-actions">
|
||||||
<el-button type="primary" size="small" @click="openDocuments(row)">资质</el-button>
|
<el-button type="primary" size="small" @click="openDocuments(row)">资质</el-button>
|
||||||
<el-button v-if="canAudit" size="small" @click="openAudit(row, 'APPROVE')"
|
<!-- 状态机:通过可从 pending/rejected/frozen 发起,驳回只允许 pending,冻结只允许 approved。
|
||||||
|
已通过的行走「冻结 / 重置密码」,不再给「审核」入口(点了必然被后端拒) -->
|
||||||
|
<el-button
|
||||||
|
v-if="canAudit && row.status !== 'approved'"
|
||||||
|
size="small"
|
||||||
|
@click="openAudit(row, 'APPROVE')"
|
||||||
>审核</el-button
|
>审核</el-button
|
||||||
>
|
>
|
||||||
<el-button
|
<el-button
|
||||||
@@ -243,7 +248,8 @@ const {
|
|||||||
<el-form-item label="审核动作">
|
<el-form-item label="审核动作">
|
||||||
<el-radio-group v-model="auditForm.action">
|
<el-radio-group v-model="auditForm.action">
|
||||||
<el-radio value="APPROVE">通过</el-radio>
|
<el-radio value="APPROVE">通过</el-radio>
|
||||||
<el-radio value="REJECT">驳回</el-radio>
|
<!-- 驳回只对「待审核」开放(与后端 requireState 一致);已驳回/已冻结的行重新审核时只能「通过」 -->
|
||||||
|
<el-radio v-if="auditTarget?.status === 'pending'" value="REJECT">驳回</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="auditForm.action === 'REJECT'" label="驳回原因码">
|
<el-form-item v-if="auditForm.action === 'REJECT'" label="驳回原因码">
|
||||||
|
|||||||
Reference in new issue
Block a user