修复:原来的统一去了404, 现在加了403, 和部分bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m54s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m54s
This commit is contained in:
@@ -110,14 +110,22 @@ async function handleRouteGuard(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试刷新路由重新注册
|
// 路由未匹配时,判断是 404 还是 403
|
||||||
if (userStore.isLogin) {
|
if (userStore.isLogin) {
|
||||||
isRouteRegistered.value = false
|
// 检查路由是否存在于 asyncRoutes 中
|
||||||
await handleDynamicRoutes(to, router, next)
|
const routeExistsInAsync = checkRouteExistsInAsyncRoutes(to.path, asyncRoutes)
|
||||||
return
|
console.log(`[路由检查] 目标路径: ${to.path}, 是否存在于asyncRoutes: ${routeExistsInAsync}`)
|
||||||
|
|
||||||
|
if (routeExistsInAsync) {
|
||||||
|
// 路由存在于 asyncRoutes 但未注册,说明用户没有权限
|
||||||
|
console.warn('路由存在但用户无权限访问:', to.path)
|
||||||
|
next(RoutesAlias.Exception403 || '/exception/403')
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果以上都不匹配,跳转到404
|
// 路由不存在,跳转到 404
|
||||||
|
console.log(`[路由检查] 路径 ${to.path} 不存在于asyncRoutes,跳转404`)
|
||||||
next(RoutesAlias.Exception404)
|
next(RoutesAlias.Exception404)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,7 +382,7 @@ function convertBackendMenuToRoute(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 递归处理所有层级的 children
|
// 递归处理后端返回的 children
|
||||||
if (menu.children && menu.children.length > 0) {
|
if (menu.children && menu.children.length > 0) {
|
||||||
const children = menu.children
|
const children = menu.children
|
||||||
.map((child: any) => convertBackendMenuToRoute(child, routeMap, menuUrl))
|
.map((child: any) => convertBackendMenuToRoute(child, routeMap, menuUrl))
|
||||||
@@ -453,6 +461,88 @@ function isValidMenuList(menuList: AppRouteRecord[]): boolean {
|
|||||||
return Array.isArray(menuList) && menuList.length > 0
|
return Array.isArray(menuList) && menuList.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查路由是否存在于 asyncRoutes 中
|
||||||
|
* 支持动态参数匹配,如 /account-management/enterprise-customer/customer-accounts/3000 匹配 enterprise-customer/customer-accounts/:id
|
||||||
|
* @param targetPath 目标路由路径,如 /account-management/enterprise-customer/customer-accounts/3000
|
||||||
|
* @param routes 路由配置数组
|
||||||
|
* @param parentPath 父路由路径
|
||||||
|
*/
|
||||||
|
function checkRouteExistsInAsyncRoutes(
|
||||||
|
targetPath: string,
|
||||||
|
routes: AppRouteRecord[],
|
||||||
|
parentPath = ''
|
||||||
|
): boolean {
|
||||||
|
for (const route of routes) {
|
||||||
|
// 构建完整路径
|
||||||
|
const fullPath = route.path.startsWith('/')
|
||||||
|
? route.path
|
||||||
|
: parentPath
|
||||||
|
? `${parentPath}/${route.path}`.replace(/\/+/g, '/')
|
||||||
|
: `/${route.path}`
|
||||||
|
|
||||||
|
// 检查当前路由是否匹配(支持动态参数)
|
||||||
|
const isMatch = matchRoutePath(targetPath, fullPath)
|
||||||
|
|
||||||
|
if (isMatch) {
|
||||||
|
console.log(`[路由匹配成功] 目标: ${targetPath}, 匹配到: ${fullPath}`)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归检查子路由
|
||||||
|
if (route.children && route.children.length > 0) {
|
||||||
|
if (checkRouteExistsInAsyncRoutes(targetPath, route.children, fullPath)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配路由路径,支持动态参数
|
||||||
|
* @param targetPath 实际路径,如 /account-management/enterprise-customer/customer-accounts/3000
|
||||||
|
* @param routePath 路由定义路径,如 /account-management/enterprise-customer/customer-accounts/:id
|
||||||
|
*/
|
||||||
|
function matchRoutePath(targetPath: string, routePath: string): boolean {
|
||||||
|
// 移除查询参数
|
||||||
|
const cleanTargetPath = targetPath.split('?')[0]
|
||||||
|
const cleanRoutePath = routePath.split('?')[0]
|
||||||
|
|
||||||
|
// 如果完全匹配,直接返回
|
||||||
|
if (cleanTargetPath === cleanRoutePath) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将路径分割成段
|
||||||
|
const targetSegments = cleanTargetPath.split('/').filter(Boolean)
|
||||||
|
const routeSegments = cleanRoutePath.split('/').filter(Boolean)
|
||||||
|
|
||||||
|
// 段数必须相同
|
||||||
|
if (targetSegments.length !== routeSegments.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 逐段比较
|
||||||
|
for (let i = 0; i < routeSegments.length; i++) {
|
||||||
|
const routeSegment = routeSegments[i]
|
||||||
|
const targetSegment = targetSegments[i]
|
||||||
|
|
||||||
|
// 如果是动态参数(以 : 开头),跳过比较
|
||||||
|
if (routeSegment.startsWith(':')) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不是动态参数,必须完全匹配
|
||||||
|
if (routeSegment !== targetSegment) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重置路由相关状态
|
* 重置路由相关状态
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -107,10 +107,12 @@ export const hasRoutePermission = (
|
|||||||
*/
|
*/
|
||||||
function checkMenuAccess(path: string, menus: any[]): boolean {
|
function checkMenuAccess(path: string, menus: any[]): boolean {
|
||||||
for (const menu of menus) {
|
for (const menu of menus) {
|
||||||
// 检查当前菜单的 URL 是否匹配
|
// 检查当前菜单的 URL 是否匹配(支持动态参数)
|
||||||
if (menu.url && (path === menu.url || path.startsWith(menu.url + '/'))) {
|
if (menu.url && matchMenuPath(path, menu.url)) {
|
||||||
|
console.log(`[菜单权限检查] 路径 ${path} 匹配菜单 ${menu.url}`)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 递归检查子菜单
|
// 递归检查子菜单
|
||||||
if (menu.children && menu.children.length > 0) {
|
if (menu.children && menu.children.length > 0) {
|
||||||
if (checkMenuAccess(path, menu.children)) {
|
if (checkMenuAccess(path, menu.children)) {
|
||||||
@@ -118,9 +120,53 @@ function checkMenuAccess(path: string, menus: any[]): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log(`[菜单权限检查] 路径 ${path} 未找到匹配的菜单`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配菜单路径,支持动态参数
|
||||||
|
* @param actualPath 实际访问路径,如 /account-management/enterprise-customer/customer-accounts/3000
|
||||||
|
* @param menuPath 菜单定义路径,如 /account-management/enterprise-customer/customer-accounts/:id
|
||||||
|
*/
|
||||||
|
function matchMenuPath(actualPath: string, menuPath: string): boolean {
|
||||||
|
// 移除查询参数
|
||||||
|
const cleanActualPath = actualPath.split('?')[0]
|
||||||
|
const cleanMenuPath = menuPath.split('?')[0]
|
||||||
|
|
||||||
|
// 如果完全匹配,直接返回
|
||||||
|
if (cleanActualPath === cleanMenuPath) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将路径分割成段
|
||||||
|
const actualSegments = cleanActualPath.split('/').filter(Boolean)
|
||||||
|
const menuSegments = cleanMenuPath.split('/').filter(Boolean)
|
||||||
|
|
||||||
|
// 段数必须相同
|
||||||
|
if (actualSegments.length !== menuSegments.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 逐段比较
|
||||||
|
for (let i = 0; i < menuSegments.length; i++) {
|
||||||
|
const menuSegment = menuSegments[i]
|
||||||
|
const actualSegment = actualSegments[i]
|
||||||
|
|
||||||
|
// 如果是动态参数(以 : 开头),跳过比较
|
||||||
|
if (menuSegment.startsWith(':')) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不是动态参数,必须完全匹配
|
||||||
|
if (menuSegment !== actualSegment) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查 Token 是否有效
|
* 检查 Token 是否有效
|
||||||
* 简单检查,真实项目中应该验证 JWT 或者调用后端接口
|
* 简单检查,真实项目中应该验证 JWT 或者调用后端接口
|
||||||
|
|||||||
@@ -95,18 +95,6 @@
|
|||||||
<span class="dialog-title">分配角色</span>
|
<span class="dialog-title">分配角色</span>
|
||||||
<div class="account-info">
|
<div class="account-info">
|
||||||
<span class="account-name">{{ currentAccountName }}</span>
|
<span class="account-name">{{ currentAccountName }}</span>
|
||||||
<ElTag v-if="currentAccountType === 1" type="danger" size="small" style="margin-left: 8px">
|
|
||||||
超级管理员不能分配角色
|
|
||||||
</ElTag>
|
|
||||||
<ElTag v-if="currentAccountType === 2" type="info" size="small" style="margin-left: 8px">
|
|
||||||
平台用户只能分配一个平台角色
|
|
||||||
</ElTag>
|
|
||||||
<ElTag v-if="currentAccountType === 3" type="warning" size="small" style="margin-left: 8px">
|
|
||||||
代理账号只能分配一个客户角色
|
|
||||||
</ElTag>
|
|
||||||
<ElTag v-if="currentAccountType === 4" type="warning" size="small" style="margin-left: 8px">
|
|
||||||
企业账号只能分配一个客户角色
|
|
||||||
</ElTag>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -67,6 +67,7 @@
|
|||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
|
@row-contextmenu="handleRowContextMenu"
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<ElTableColumn type="selection" width="55" />
|
<ElTableColumn type="selection" width="55" />
|
||||||
@@ -106,7 +107,7 @@
|
|||||||
@selection-change="handleAvailableCardsSelectionChange"
|
@selection-change="handleAvailableCardsSelectionChange"
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<ElTableColumn type="selection" width="55" />
|
<ElTableColumn type="selection" width="55" :selectable="checkCardSelectable" />
|
||||||
<ElTableColumn
|
<ElTableColumn
|
||||||
v-for="col in availableCardColumns"
|
v-for="col in availableCardColumns"
|
||||||
:key="col.prop || col.type"
|
:key="col.prop || col.type"
|
||||||
@@ -209,6 +210,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ElDialog>
|
</ElDialog>
|
||||||
|
|
||||||
|
<!-- 表格行操作右键菜单 -->
|
||||||
|
<ArtMenuRight
|
||||||
|
ref="cardOperationMenuRef"
|
||||||
|
:menu-items="cardOperationMenuItems"
|
||||||
|
:menu-width="140"
|
||||||
|
@select="handleCardOperationMenuSelect"
|
||||||
|
/>
|
||||||
</ElCard>
|
</ElCard>
|
||||||
</div>
|
</div>
|
||||||
</ArtTableFullScreen>
|
</ArtTableFullScreen>
|
||||||
@@ -224,6 +233,8 @@
|
|||||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||||
import { useAuth } from '@/composables/useAuth'
|
import { useAuth } from '@/composables/useAuth'
|
||||||
import { formatDateTime } from '@/utils/business/format'
|
import { formatDateTime } from '@/utils/business/format'
|
||||||
|
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
|
||||||
|
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
|
||||||
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
||||||
import { BgColorEnum } from '@/enums/appEnum'
|
import { BgColorEnum } from '@/enums/appEnum'
|
||||||
import type {
|
import type {
|
||||||
@@ -264,6 +275,11 @@
|
|||||||
const availableCardsLoading = ref(false)
|
const availableCardsLoading = ref(false)
|
||||||
const availableCardsList = ref<StandaloneIotCard[]>([])
|
const availableCardsList = ref<StandaloneIotCard[]>([])
|
||||||
const selectedAvailableCards = ref<StandaloneIotCard[]>([])
|
const selectedAvailableCards = ref<StandaloneIotCard[]>([])
|
||||||
|
const allocatedCardIccids = ref<Set<string>>(new Set())
|
||||||
|
|
||||||
|
// 右键菜单相关
|
||||||
|
const cardOperationMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
|
||||||
|
const currentOperatingCard = ref<EnterpriseCardItem | null>(null)
|
||||||
|
|
||||||
// 卡搜索表单初始值
|
// 卡搜索表单初始值
|
||||||
const initialCardSearchState = {
|
const initialCardSearchState = {
|
||||||
@@ -433,8 +449,7 @@
|
|||||||
{ label: '状态', prop: 'status' },
|
{ label: '状态', prop: 'status' },
|
||||||
{ label: '状态名称', prop: 'status_name' },
|
{ label: '状态名称', prop: 'status_name' },
|
||||||
{ label: '网络状态', prop: 'network_status' },
|
{ label: '网络状态', prop: 'network_status' },
|
||||||
{ label: '网络状态名称', prop: 'network_status_name' },
|
{ label: '网络状态名称', prop: 'network_status_name' }
|
||||||
{ label: '操作', prop: 'operation' }
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const cardList = ref<EnterpriseCardItem[]>([])
|
const cardList = ref<EnterpriseCardItem[]>([])
|
||||||
@@ -540,41 +555,6 @@
|
|||||||
prop: 'network_status_name',
|
prop: 'network_status_name',
|
||||||
label: '网络状态名称',
|
label: '网络状态名称',
|
||||||
width: 130
|
width: 130
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'operation',
|
|
||||||
label: '操作',
|
|
||||||
width: 100,
|
|
||||||
fixed: 'right',
|
|
||||||
formatter: (row: EnterpriseCardItem) => {
|
|
||||||
const buttons = []
|
|
||||||
|
|
||||||
if (row.network_status === 0) {
|
|
||||||
// 停机状态,显示复机按钮
|
|
||||||
if (hasAuth('enterprise_cards:resume')) {
|
|
||||||
buttons.push(
|
|
||||||
h(ArtButtonTable, {
|
|
||||||
text: '复机',
|
|
||||||
iconClass: BgColorEnum.SUCCESS,
|
|
||||||
onClick: () => handleResume(row)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 开机状态,显示停机按钮
|
|
||||||
if (hasAuth('enterprise_cards:suspend')) {
|
|
||||||
buttons.push(
|
|
||||||
h(ArtButtonTable, {
|
|
||||||
text: '停机',
|
|
||||||
iconClass: BgColorEnum.ERROR,
|
|
||||||
onClick: () => handleSuspend(row)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -849,10 +829,17 @@
|
|||||||
getAvailableCardsList()
|
getAvailableCardsList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查卡是否可选(已授权的卡不可选)
|
||||||
|
const checkCardSelectable = (row: StandaloneIotCard) => {
|
||||||
|
return !allocatedCardIccids.value.has(row.iccid)
|
||||||
|
}
|
||||||
|
|
||||||
// 显示授权对话框
|
// 显示授权对话框
|
||||||
const showAllocateDialog = () => {
|
const showAllocateDialog = () => {
|
||||||
allocateDialogVisible.value = true
|
allocateDialogVisible.value = true
|
||||||
selectedAvailableCards.value = []
|
selectedAvailableCards.value = []
|
||||||
|
// 收集已授权的卡的ICCID
|
||||||
|
allocatedCardIccids.value = new Set(cardList.value.map((card) => card.iccid))
|
||||||
// 重置搜索条件
|
// 重置搜索条件
|
||||||
Object.assign(cardSearchForm, { ...initialCardSearchState })
|
Object.assign(cardSearchForm, { ...initialCardSearchState })
|
||||||
cardPagination.page = 1
|
cardPagination.page = 1
|
||||||
@@ -954,6 +941,56 @@
|
|||||||
getTableData()
|
getTableData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 右键菜单项配置
|
||||||
|
const cardOperationMenuItems = computed((): MenuItemType[] => {
|
||||||
|
const items: MenuItemType[] = []
|
||||||
|
|
||||||
|
if (!currentOperatingCard.value) return items
|
||||||
|
|
||||||
|
if (currentOperatingCard.value.network_status === 0) {
|
||||||
|
// 停机状态 - 显示复机
|
||||||
|
if (hasAuth('enterprise_cards:resume')) {
|
||||||
|
items.push({
|
||||||
|
key: 'resume',
|
||||||
|
label: '复机'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 开机状态 - 显示停机
|
||||||
|
if (hasAuth('enterprise_cards:suspend')) {
|
||||||
|
items.push({
|
||||||
|
key: 'suspend',
|
||||||
|
label: '停机'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理右键菜单
|
||||||
|
const handleRowContextMenu = (row: EnterpriseCardItem, column: any, event: MouseEvent) => {
|
||||||
|
event.preventDefault()
|
||||||
|
currentOperatingCard.value = row
|
||||||
|
nextTick(() => {
|
||||||
|
cardOperationMenuRef.value?.show(event)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理菜单选择
|
||||||
|
const handleCardOperationMenuSelect = (item: MenuItemType) => {
|
||||||
|
if (!currentOperatingCard.value) return
|
||||||
|
|
||||||
|
switch (item.key) {
|
||||||
|
case 'suspend':
|
||||||
|
handleSuspend(currentOperatingCard.value)
|
||||||
|
break
|
||||||
|
case 'resume':
|
||||||
|
handleResume(currentOperatingCard.value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 停机卡
|
// 停机卡
|
||||||
const handleSuspend = (row: EnterpriseCardItem) => {
|
const handleSuspend = (row: EnterpriseCardItem) => {
|
||||||
ElMessageBox.confirm('确定要停机该卡吗?', '停机卡', {
|
ElMessageBox.confirm('确定要停机该卡吗?', '停机卡', {
|
||||||
|
|||||||
@@ -747,8 +747,7 @@
|
|||||||
// 查看客户账号
|
// 查看客户账号
|
||||||
const viewCustomerAccounts = (row: EnterpriseItem) => {
|
const viewCustomerAccounts = (row: EnterpriseItem) => {
|
||||||
router.push({
|
router.push({
|
||||||
name: 'EnterpriseCustomerAccounts',
|
path: `/account-management/enterprise-customer/customer-accounts/${row.id}`,
|
||||||
params: { id: row.id },
|
|
||||||
query: { type: 'enterprise' }
|
query: { type: 'enterprise' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,7 +342,12 @@
|
|||||||
</ElDialog>
|
</ElDialog>
|
||||||
|
|
||||||
<!-- 套餐系列绑定结果对话框 -->
|
<!-- 套餐系列绑定结果对话框 -->
|
||||||
<ElDialog v-model="seriesBindingResultDialogVisible" title="设置结果" width="700px">
|
<ElDialog
|
||||||
|
v-model="seriesBindingResultDialogVisible"
|
||||||
|
title="设置结果"
|
||||||
|
width="700px"
|
||||||
|
@close="handleSeriesBindingResultDialogClose"
|
||||||
|
>
|
||||||
<ElDescriptions :column="2" border>
|
<ElDescriptions :column="2" border>
|
||||||
<ElDescriptionsItem label="成功数">
|
<ElDescriptionsItem label="成功数">
|
||||||
<ElTag type="success">{{ seriesBindingResult.success_count }}</ElTag>
|
<ElTag type="success">{{ seriesBindingResult.success_count }}</ElTag>
|
||||||
@@ -1295,22 +1300,21 @@
|
|||||||
try {
|
try {
|
||||||
const res = await CardService.allocateStandaloneCards(params)
|
const res = await CardService.allocateStandaloneCards(params)
|
||||||
|
|
||||||
|
// code === 0 表示操作成功(接口调用成功),显示结果对话框
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
allocationResult.value = res.data
|
allocationResult.value = res.data
|
||||||
resultTitle.value = '批量分配结果'
|
resultTitle.value = '批量分配结果'
|
||||||
allocateDialogVisible.value = false
|
allocateDialogVisible.value = false
|
||||||
resultDialogVisible.value = true
|
resultDialogVisible.value = true
|
||||||
|
|
||||||
// 清空选择
|
// 清空选择
|
||||||
if (tableRef.value) {
|
|
||||||
tableRef.value.clearSelection()
|
|
||||||
}
|
|
||||||
selectedCards.value = []
|
selectedCards.value = []
|
||||||
|
|
||||||
// 刷新列表
|
// 刷新列表
|
||||||
getTableData()
|
await getTableData()
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
ElMessage.error('批量分配失败,请重试')
|
|
||||||
} finally {
|
} finally {
|
||||||
allocateLoading.value = false
|
allocateLoading.value = false
|
||||||
}
|
}
|
||||||
@@ -1348,18 +1352,21 @@
|
|||||||
try {
|
try {
|
||||||
const res = await CardService.recallStandaloneCards(params)
|
const res = await CardService.recallStandaloneCards(params)
|
||||||
|
|
||||||
|
// code === 0 表示操作成功(接口调用成功),显示结果对话框
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
allocationResult.value = res.data
|
allocationResult.value = res.data
|
||||||
resultTitle.value = '批量回收结果'
|
resultTitle.value = '批量回收结果'
|
||||||
recallDialogVisible.value = false
|
recallDialogVisible.value = false
|
||||||
resultDialogVisible.value = true
|
resultDialogVisible.value = true
|
||||||
|
|
||||||
// 清空选择
|
// 清空选择
|
||||||
if (tableRef.value) {
|
|
||||||
tableRef.value.clearSelection()
|
|
||||||
}
|
|
||||||
selectedCards.value = []
|
selectedCards.value = []
|
||||||
|
|
||||||
// 刷新列表
|
// 刷新列表
|
||||||
getTableData()
|
await getTableData()
|
||||||
|
} else {
|
||||||
|
// code !== 0 才是真正的失败(接口调用失败)
|
||||||
|
ElMessage.error(res.msg || '批量回收失败,请重试')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@@ -1424,6 +1431,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关闭套餐系列绑定结果对话框
|
||||||
|
const handleSeriesBindingResultDialogClose = () => {
|
||||||
|
// 刷新列表
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
// 执行套餐系列绑定
|
// 执行套餐系列绑定
|
||||||
const handleSeriesBinding = async () => {
|
const handleSeriesBinding = async () => {
|
||||||
if (!seriesBindingFormRef.value) return
|
if (!seriesBindingFormRef.value) return
|
||||||
@@ -1450,13 +1463,10 @@
|
|||||||
seriesBindingResultDialogVisible.value = true
|
seriesBindingResultDialogVisible.value = true
|
||||||
|
|
||||||
// 清空选择
|
// 清空选择
|
||||||
if (tableRef.value) {
|
|
||||||
tableRef.value.clearSelection()
|
|
||||||
}
|
|
||||||
selectedCards.value = []
|
selectedCards.value = []
|
||||||
|
|
||||||
// 刷新列表
|
// 立即刷新列表
|
||||||
getTableData()
|
await getTableData()
|
||||||
|
|
||||||
// 显示消息提示
|
// 显示消息提示
|
||||||
if (res.data.fail_count === 0) {
|
if (res.data.fail_count === 0) {
|
||||||
@@ -1471,6 +1481,7 @@
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
ElMessage.error('批量设置套餐系列失败,请重试')
|
||||||
} finally {
|
} finally {
|
||||||
seriesBindingLoading.value = false
|
seriesBindingLoading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,14 +99,6 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
<div class="account-info">
|
<div class="account-info">
|
||||||
<span class="account-name">{{ currentAccountName }}</span>
|
<span class="account-name">{{ currentAccountName }}</span>
|
||||||
<ElTag
|
|
||||||
v-if="currentAccountType === 3"
|
|
||||||
type="warning"
|
|
||||||
size="small"
|
|
||||||
style="margin-left: 8px"
|
|
||||||
>
|
|
||||||
代理账号只能分配一个客户角色
|
|
||||||
</ElTag>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -776,18 +768,26 @@
|
|||||||
border-color: var(--el-border-color);
|
border-color: var(--el-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-info {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-checkbox) {
|
:deep(.el-checkbox) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.el-checkbox__label {
|
.el-checkbox__label {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.role-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
> span:first-child {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -800,6 +800,18 @@
|
|||||||
|
|
||||||
.role-info {
|
.role-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
> span:first-child {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-button {
|
.el-button {
|
||||||
|
|||||||
@@ -480,7 +480,7 @@
|
|||||||
{
|
{
|
||||||
prop: 'real_data_mb',
|
prop: 'real_data_mb',
|
||||||
label: '真流量',
|
label: '真流量',
|
||||||
width: 100,
|
width: 120,
|
||||||
formatter: (row: PackageResponse) => `${row.real_data_mb}MB`
|
formatter: (row: PackageResponse) => `${row.real_data_mb}MB`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user