fix: 资产信息字段显示隐藏根据角色
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m24s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m24s
This commit is contained in:
@@ -3,18 +3,17 @@
|
||||
*/
|
||||
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElNotification, ElMessage } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { HOME_PAGE, RoutesAlias } from '@/router/routesAlias'
|
||||
import { RoutesAlias } from '@/router/routesAlias'
|
||||
import AppConfig from '@/config'
|
||||
import { AuthService } from '@/api/modules'
|
||||
import { ApiStatus } from '@/utils/http/status'
|
||||
import { saveCredentials, getRememberedCredentials } from '@/utils/auth/rememberPassword'
|
||||
import { usernameRules, passwordRules } from '@/utils/auth/loginValidation'
|
||||
import { getRedirectPath } from '@/router/guards/permission'
|
||||
|
||||
/**
|
||||
* 登录表单数据
|
||||
@@ -28,7 +27,6 @@ interface LoginForm {
|
||||
export function useLogin() {
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 表单引用
|
||||
@@ -108,9 +106,24 @@ export function useLogin() {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查用户类型 - 禁止企业账号登录
|
||||
if (response.data.user && response.data.user.user_type === 4) {
|
||||
ElMessage.error('企业账号无法登录后台管理系统,请使用平台/代理账号登录')
|
||||
if (!response.data.user) {
|
||||
ElMessage.error(t('login.error.getUserInfoFailed'))
|
||||
return
|
||||
}
|
||||
|
||||
// 登录成功但没有后台菜单或权限时,仅提示并停止后续流程。
|
||||
// 这里不保存登录态,也不触发后续跳转,避免进入路由守卫后再连续调用退出接口。
|
||||
const hasPermissions =
|
||||
Array.isArray(response.data.permissions) && response.data.permissions.length > 0
|
||||
const hasMenus = Array.isArray(response.data.menus) && response.data.menus.length > 0
|
||||
|
||||
if (!hasPermissions || !hasMenus) {
|
||||
ElMessage.warning({
|
||||
message: '当前账号暂无任何权限,请联系管理员分配角色和权限',
|
||||
duration: 4000,
|
||||
showClose: true
|
||||
})
|
||||
saveCredentials(formData.username, formData.password, formData.rememberPassword)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -118,13 +131,8 @@ export function useLogin() {
|
||||
userStore.setToken(response.data.access_token, response.data.refresh_token)
|
||||
|
||||
// 保存用户信息
|
||||
if (response.data.user) {
|
||||
userStore.setUserInfo(response.data.user)
|
||||
userStore.setLoginStatus(true)
|
||||
} else {
|
||||
ElMessage.error(t('login.error.getUserInfoFailed'))
|
||||
return
|
||||
}
|
||||
userStore.setUserInfo(response.data.user)
|
||||
userStore.setLoginStatus(true)
|
||||
|
||||
// 保存权限、菜单和按钮
|
||||
if (response.data.permissions) {
|
||||
@@ -137,19 +145,6 @@ export function useLogin() {
|
||||
userStore.setButtons(response.data.buttons)
|
||||
}
|
||||
|
||||
// 检查权限和菜单是否为空
|
||||
const hasPermissions = response.data.permissions && response.data.permissions.length > 0
|
||||
const hasMenus = response.data.menus && response.data.menus.length > 0
|
||||
|
||||
if (!hasPermissions || !hasMenus) {
|
||||
ElMessage.warning({
|
||||
message: '当前账号暂无任何权限,请联系管理员分配角色和权限',
|
||||
duration: 4000,
|
||||
showClose: true
|
||||
})
|
||||
// 虽然没有权限,但仍然允许登录,让用户看到空白页面而不是停留在登录页
|
||||
}
|
||||
|
||||
// 保存记住密码
|
||||
saveCredentials(formData.username, formData.password, formData.rememberPassword)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const axiosInstance = axios.create({
|
||||
baseURL: import.meta.env.DEV ? '' : import.meta.env.VITE_API_URL, // 开发服务器使用代理,其他模式使用完整URL
|
||||
withCredentials: false, // 异步请求携带cookie
|
||||
transformRequest: [
|
||||
(data, headers) => {
|
||||
(data) => {
|
||||
// 如果是 FormData,不进行转换
|
||||
if (data instanceof FormData) {
|
||||
return data
|
||||
@@ -72,6 +72,7 @@ let failedQueue: Array<{
|
||||
resolve: (value?: any) => void
|
||||
reject: (reason?: any) => void
|
||||
}> = [] // 失败队列,存储因 token 过期而失败的请求
|
||||
let isHandlingAuthExpiry = false
|
||||
|
||||
// 处理失败队列
|
||||
const processQueue = (error: any, token: string | null = null) => {
|
||||
@@ -222,6 +223,11 @@ function handleErrorMessage(
|
||||
const backendMessage = error.response?.data?.msg
|
||||
const httpStatus = error.response?.status
|
||||
|
||||
// 401 过期类错误由 clearLocalStateAndRedirect 统一提示,避免重复弹出后端报错。
|
||||
if (httpStatus === 401 || error.response?.data?.code === ApiStatus.unauthorized) {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是404错误,且未配置显示404错误,则不显示任何提示
|
||||
if (httpStatus === 404 && !show404Error) {
|
||||
return
|
||||
@@ -306,9 +312,13 @@ const api = {
|
||||
|
||||
// 仅清理本地状态(不调用接口)- 用于401等情况
|
||||
const clearLocalStateAndRedirect = () => {
|
||||
if (isHandlingAuthExpiry) return
|
||||
isHandlingAuthExpiry = true
|
||||
|
||||
ElMessage.error('登录已过期,请重新登录')
|
||||
setTimeout(() => {
|
||||
useUserStore().clearLocalState()
|
||||
isHandlingAuthExpiry = false
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
|
||||
@@ -32,15 +32,13 @@
|
||||
<ElDescriptionsItem label="IMEI">{{
|
||||
cardInfo?.gateway_card_imei || '-'
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="运营商账户">{{
|
||||
cardInfo?.carrier_name || '-'
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="运营商账户">{{ carrierAccountDisplay }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="实名状态">
|
||||
<ElTag :type="getRealNameStatusType(cardInfo?.real_name_status)" size="small">
|
||||
{{ getRealNameStatusName(cardInfo?.real_name_status) }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="实名认证策略">
|
||||
<ElDescriptionsItem v-if="canShowSensitiveFields" label="实名认证策略">
|
||||
{{ getRealnamePolicyName(cardInfo?.realname_policy) }}
|
||||
</ElDescriptionsItem>
|
||||
|
||||
@@ -157,11 +155,13 @@
|
||||
<ElDescriptionsItem label="最大卡槽数">{{
|
||||
cardInfo?.max_sim_slots || '-'
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="实名认证策略">
|
||||
<ElDescriptionsItem v-if="canShowSensitiveFields" label="实名认证策略">
|
||||
{{ getRealnamePolicyName(cardInfo?.realname_policy) }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="设备类型">{{ cardInfo?.device_type || '-' }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="制造商">{{ cardInfo?.manufacturer || '-' }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem v-if="canShowSensitiveFields" label="制造商">{{
|
||||
cardInfo?.manufacturer || '-'
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="最后在线时间">{{
|
||||
formatDateTime(cardInfo?.last_online_time) || '-'
|
||||
}}</ElDescriptionsItem>
|
||||
@@ -524,6 +524,7 @@
|
||||
iccid?: string
|
||||
imei?: string
|
||||
msisdn?: string
|
||||
carrier_type?: string
|
||||
carrier_name?: string
|
||||
real_name_status?: number
|
||||
activation_status_name?: string
|
||||
@@ -643,6 +644,22 @@
|
||||
return ![3, 4].includes(userType) && hasAuth(CARD_MONTH_USAGE_PERMISSION)
|
||||
})
|
||||
|
||||
const isAgentOrEnterpriseAccount = computed(() => {
|
||||
const userType = Number(userStore.info.user_type ?? 0)
|
||||
return [3, 4].includes(userType)
|
||||
})
|
||||
|
||||
const canShowSensitiveFields = computed(() => {
|
||||
return !isAgentOrEnterpriseAccount.value
|
||||
})
|
||||
|
||||
const carrierAccountDisplay = computed(() => {
|
||||
if (isAgentOrEnterpriseAccount.value) {
|
||||
return getCarrierTypeName(props.cardInfo?.carrier_type)
|
||||
}
|
||||
return props.cardInfo?.carrier_name || '-'
|
||||
})
|
||||
|
||||
const descriptionsColumn = computed(() => {
|
||||
if (width.value <= 640) return 1
|
||||
if (width.value <= 1024) return 2
|
||||
@@ -679,6 +696,7 @@
|
||||
getRealNameStatusName,
|
||||
getRealNameStatusType,
|
||||
getRealnamePolicyName,
|
||||
getCarrierTypeName,
|
||||
getAssetStatusName,
|
||||
getAssetStatusType,
|
||||
getOnlineStatusName,
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
* 资产信息格式化工具函数
|
||||
*/
|
||||
|
||||
import { getCarrierTypeLabel } from '@/config/constants/carrierTypes'
|
||||
import type { CarrierType } from '@/types/api'
|
||||
|
||||
type TagType = 'primary' | 'success' | 'warning' | 'info' | 'danger'
|
||||
|
||||
export function useAssetFormatters() {
|
||||
@@ -76,6 +79,13 @@ export function useAssetFormatters() {
|
||||
return policyMap[policy] || '未知'
|
||||
}
|
||||
|
||||
// 获取运营商类型名称
|
||||
const getCarrierTypeName = (carrierType?: string | null) => {
|
||||
const normalizedCarrierType = carrierType?.trim()
|
||||
if (!normalizedCarrierType) return '-'
|
||||
return getCarrierTypeLabel(normalizedCarrierType as CarrierType)
|
||||
}
|
||||
|
||||
// 获取资产状态名称
|
||||
const getAssetStatusName = (status?: number) => {
|
||||
if (status === undefined || status === null) return '未知'
|
||||
@@ -252,6 +262,7 @@ export function useAssetFormatters() {
|
||||
getRealNameStatusName,
|
||||
getRealNameStatusType,
|
||||
getRealnamePolicyName,
|
||||
getCarrierTypeName,
|
||||
getAssetStatusName,
|
||||
getAssetStatusType,
|
||||
getOnlineStatusName,
|
||||
|
||||
@@ -14,6 +14,7 @@ export interface AssetInfo {
|
||||
iccid?: string
|
||||
imei?: string
|
||||
msisdn?: string
|
||||
carrier_type?: string
|
||||
carrier_name?: string
|
||||
real_name_status?: number
|
||||
activation_status_name?: string
|
||||
|
||||
Reference in New Issue
Block a user