修复bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 11m14s

This commit is contained in:
sexygoat
2026-04-08 10:19:25 +08:00
parent ff087abd44
commit b510b4539f
55 changed files with 2274 additions and 2612 deletions

View File

@@ -68,13 +68,11 @@
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
:row-class-name="getRowClassName"
:actions="getActions"
:actionsWidth="160"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@selection-change="handleSelectionChange"
@row-contextmenu="handleRowContextMenu"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
>
<template #default>
<ElTableColumn type="selection" width="55" />
@@ -82,9 +80,6 @@
</template>
</ArtTable>
<!-- 鼠标悬浮提示 -->
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
<!-- 批量分配对话框 -->
<ElDialog
v-model="allocateDialogVisible"
@@ -126,26 +121,32 @@
</ElRadioGroup>
</ElFormItem>
<ElFormItem v-if="allocateForm.selection_type === 'list'" label="ICCID列表">
<ElFormItem
v-if="allocateForm.selection_type === CardSelectionType.LIST"
label="ICCID列表"
>
<div>已选择 {{ selectedCards.length }} 张卡</div>
</ElFormItem>
<ElFormItem
v-if="allocateForm.selection_type === 'range'"
v-if="allocateForm.selection_type === CardSelectionType.RANGE"
label="起始ICCID"
prop="iccid_start"
>
<ElInput v-model="allocateForm.iccid_start" placeholder="请输入起始ICCID" />
</ElFormItem>
<ElFormItem
v-if="allocateForm.selection_type === 'range'"
v-if="allocateForm.selection_type === CardSelectionType.RANGE"
label="结束ICCID"
prop="iccid_end"
>
<ElInput v-model="allocateForm.iccid_end" placeholder="请输入结束ICCID" />
</ElFormItem>
<ElFormItem v-if="allocateForm.selection_type === 'filter'" label="运营商">
<ElFormItem
v-if="allocateForm.selection_type === CardSelectionType.FILTER"
label="运营商"
>
<ElSelect
v-model="allocateForm.carrier_id"
placeholder="请选择运营商"
@@ -157,7 +158,10 @@
<ElOption label="中国电信" :value="3" />
</ElSelect>
</ElFormItem>
<ElFormItem v-if="allocateForm.selection_type === 'filter'" label="卡状态">
<ElFormItem
v-if="allocateForm.selection_type === CardSelectionType.FILTER"
label="卡状态"
>
<ElSelect
v-model="allocateForm.status"
placeholder="请选择状态"
@@ -170,7 +174,10 @@
<ElOption label="已停用" :value="4" />
</ElSelect>
</ElFormItem>
<ElFormItem v-if="allocateForm.selection_type === 'filter'" label="批次号">
<ElFormItem
v-if="allocateForm.selection_type === CardSelectionType.FILTER"
label="批次号"
>
<ElInput v-model="allocateForm.batch_no" placeholder="请输入批次号" />
</ElFormItem>
@@ -209,26 +216,32 @@
</ElRadioGroup>
</ElFormItem>
<ElFormItem v-if="recallForm.selection_type === 'list'" label="ICCID列表">
<ElFormItem
v-if="recallForm.selection_type === CardSelectionType.LIST"
label="ICCID列表"
>
<div>已选择 {{ selectedCards.length }} 张卡</div>
</ElFormItem>
<ElFormItem
v-if="recallForm.selection_type === 'range'"
v-if="recallForm.selection_type === CardSelectionType.RANGE"
label="起始ICCID"
prop="iccid_start"
>
<ElInput v-model="recallForm.iccid_start" placeholder="请输入起始ICCID" />
</ElFormItem>
<ElFormItem
v-if="recallForm.selection_type === 'range'"
v-if="recallForm.selection_type === CardSelectionType.RANGE"
label="结束ICCID"
prop="iccid_end"
>
<ElInput v-model="recallForm.iccid_end" placeholder="请输入结束ICCID" />
</ElFormItem>
<ElFormItem v-if="recallForm.selection_type === 'filter'" label="运营商">
<ElFormItem
v-if="recallForm.selection_type === CardSelectionType.FILTER"
label="运营商"
>
<ElSelect
v-model="recallForm.carrier_id"
placeholder="请选择运营商"
@@ -240,7 +253,10 @@
<ElOption label="中国电信" :value="3" />
</ElSelect>
</ElFormItem>
<ElFormItem v-if="recallForm.selection_type === 'filter'" label="批次号">
<ElFormItem
v-if="recallForm.selection_type === CardSelectionType.FILTER"
label="批次号"
>
<ElInput v-model="recallForm.batch_no" placeholder="请输入批次号" />
</ElFormItem>
@@ -385,7 +401,7 @@
<!-- 卡详情对话框 -->
<ElDialog v-model="cardDetailDialogVisible" title="卡片详情" width="900px">
<div v-if="cardDetailLoading" style=" padding: 40px;text-align: center">
<div v-if="cardDetailLoading" style="padding: 40px; text-align: center">
<ElIcon class="is-loading" :size="40"><Loading /></ElIcon>
<div style="margin-top: 16px">加载中...</div>
</div>
@@ -465,7 +481,7 @@
<!-- 流量使用查询对话框 -->
<ElDialog v-model="flowUsageDialogVisible" title="流量使用查询" width="500px">
<div v-if="flowUsageLoading" style=" padding: 40px;text-align: center">
<div v-if="flowUsageLoading" style="padding: 40px; text-align: center">
<ElIcon class="is-loading" :size="40"><Loading /></ElIcon>
<div style="margin-top: 16px">查询中...</div>
</div>
@@ -494,7 +510,7 @@
<!-- 实名状态查询对话框 -->
<ElDialog v-model="realnameStatusDialogVisible" title="实名认证状态" width="500px">
<div v-if="realnameStatusLoading" style=" padding: 40px;text-align: center">
<div v-if="realnameStatusLoading" style="padding: 40px; text-align: center">
<ElIcon class="is-loading" :size="40"><Loading /></ElIcon>
<div style="margin-top: 16px">查询中...</div>
</div>
@@ -517,7 +533,7 @@
<!-- 卡实时状态查询对话框 -->
<ElDialog v-model="cardStatusDialogVisible" title="卡实时状态" width="500px">
<div v-if="cardStatusLoading" style=" padding: 40px;text-align: center">
<div v-if="cardStatusLoading" style="padding: 40px; text-align: center">
<ElIcon class="is-loading" :size="40"><Loading /></ElIcon>
<div style="margin-top: 16px">查询中...</div>
</div>
@@ -548,14 +564,6 @@
:menu-width="180"
@select="handleMoreMenuSelect"
/>
<!-- 表格行操作右键菜单 -->
<ArtMenuRight
ref="cardOperationMenuRef"
:menu-items="cardOperationMenuItems"
:menu-width="160"
@select="handleCardOperationMenuSelect"
/>
</ElCard>
</div>
</ArtTableFullScreen>
@@ -571,12 +579,9 @@
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth'
import { useTableContextMenu } from '@/composables/useTableContextMenu'
import { formatDateTime } from '@/utils/business/format'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import type {
StandaloneIotCard,
StandaloneCardStatus,
@@ -585,23 +590,14 @@
AllocateStandaloneCardsResponse,
BatchSetCardSeriesBindingResponse
} from '@/types/api/card'
import { CardSelectionType } from '@/types/api/card'
import type { PackageSeriesResponse } from '@/types/api'
import { RoutesAlias } from '@/router/routesAlias'
defineOptions({ name: 'StandaloneCardList' })
const { hasAuth } = useAuth()
const router = useRouter()
// 使用表格右键菜单功能
const {
showContextMenuHint,
hintPosition,
getRowClassName,
handleCellMouseEnter,
handleCellMouseLeave
} = useTableContextMenu()
const loading = ref(false)
const allocateDialogVisible = ref(false)
const allocateLoading = ref(false)
@@ -660,18 +656,21 @@
// 更多操作右键菜单
const moreMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
const cardOperationMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
const currentOperatingIccid = ref<string>('')
const currentOperatingCard = ref<StandaloneIotCard | null>(null)
// 店铺相关
const targetShopLoading = ref(false)
const fromShopLoading = ref(false)
const targetShopList = ref<any[]>([])
const fromShopList = ref<any[]>([])
// 搜索表单初始值
const initialSearchState = {
const initialSearchState: {
status: undefined | number
carrier_id: undefined | number
iccid: string
msisdn: string
virtual_no: string
is_distributed: undefined | number
[key: string]: any
} = {
status: undefined,
carrier_id: undefined,
iccid: '',
@@ -685,7 +684,7 @@
// 批量分配表单
const allocateForm = reactive<Partial<AllocateStandaloneCardsRequest>>({
selection_type: 'list',
selection_type: CardSelectionType.LIST,
to_shop_id: undefined,
iccids: [],
iccid_start: '',
@@ -703,8 +702,8 @@
iccid_start: [
{
required: true,
validator: (rule, value, callback) => {
if (allocateForm.selection_type === 'range' && !value) {
validator: (_rule, value, callback) => {
if (allocateForm.selection_type === CardSelectionType.RANGE && !value) {
callback(new Error('请输入起始ICCID'))
} else {
callback()
@@ -716,8 +715,8 @@
iccid_end: [
{
required: true,
validator: (rule, value, callback) => {
if (allocateForm.selection_type === 'range' && !value) {
validator: (_rule, value, callback) => {
if (allocateForm.selection_type === CardSelectionType.RANGE && !value) {
callback(new Error('请输入结束ICCID'))
} else {
callback()
@@ -730,7 +729,7 @@
// 批量回收表单
const recallForm = reactive<Partial<RecallStandaloneCardsRequest>>({
selection_type: 'list',
selection_type: CardSelectionType.LIST,
iccids: [],
iccid_start: '',
iccid_end: '',
@@ -745,8 +744,8 @@
iccid_start: [
{
required: true,
validator: (rule, value, callback) => {
if (recallForm.selection_type === 'range' && !value) {
validator: (_rule, value, callback) => {
if (recallForm.selection_type === CardSelectionType.RANGE && !value) {
callback(new Error('请输入起始ICCID'))
} else {
callback()
@@ -758,8 +757,8 @@
iccid_end: [
{
required: true,
validator: (rule, value, callback) => {
if (recallForm.selection_type === 'range' && !value) {
validator: (_rule, value, callback) => {
if (recallForm.selection_type === CardSelectionType.RANGE && !value) {
callback(new Error('请输入结束ICCID'))
} else {
callback()
@@ -907,7 +906,7 @@
const goToCardDetail = (iccid: string) => {
if (hasAuth('iot_card:view_detail')) {
router.push({
path: '/asset-management/single-card',
path: '/asset-management/asset-information',
query: {
iccid: iccid
}
@@ -983,13 +982,13 @@
{
prop: 'msisdn',
label: '卡接入号',
width: 130
width: 160
},
{
prop: 'virtual_no',
label: '虚拟号',
width: 130,
formatter: (row: StandaloneIotCard) => row.virtual_no || '-'
width: 160,
formatter: (row: StandaloneIotCard) => row.virtual_no
},
{
prop: 'card_category',
@@ -1081,6 +1080,63 @@
}
])
// 操作列配置
const getActions = (row: StandaloneIotCard) => {
const actions: any[] = []
// 查询流量按钮
if (hasAuth('iot_card:query_flow')) {
actions.push({
label: '查询流量',
handler: () => showFlowUsageDialog(row.iccid),
type: 'primary'
})
}
// 其他操作放到更多中
if (hasAuth('iot_card:query_realname_status')) {
actions.push({
label: '查询实名状态',
handler: () => handleCardOperation('realname-status', row.iccid),
type: 'primary'
})
}
if (hasAuth('iot_card:query_card_status')) {
actions.push({
label: '查询卡状态',
handler: () => handleCardOperation('card-status', row.iccid),
type: 'primary'
})
}
if (hasAuth('iot_card:start_card')) {
actions.push({
label: '启用此卡',
handler: () => handleCardOperation('start-card', row.iccid),
type: 'primary'
})
}
if (hasAuth('iot_card:stop_card')) {
actions.push({
label: '停用此卡',
handler: () => handleCardOperation('stop-card', row.iccid),
type: 'primary'
})
}
if (hasAuth('iot_card:manual_deactivate')) {
actions.push({
label: '手动停用',
handler: () => handleCardOperation('manual-deactivate', row.iccid),
type: 'primary'
})
}
return actions
}
onMounted(() => {
getTableData()
})
@@ -1089,7 +1145,7 @@
const getTableData = async () => {
loading.value = true
try {
const params = {
const params: Record<string, any> = {
page: pagination.page,
page_size: pagination.pageSize,
...formFilters
@@ -1174,33 +1230,6 @@
await loadTargetShops(query || undefined)
}
// 加载来源店铺列表
const loadFromShops = async (shopName?: string) => {
fromShopLoading.value = true
try {
const params: any = {
page: 1,
page_size: 20
}
if (shopName) {
params.shop_name = shopName
}
const res = await ShopService.getShops(params)
if (res.code === 0) {
fromShopList.value = res.data.items || []
}
} catch (error) {
console.error('获取来源店铺列表失败:', error)
} finally {
fromShopLoading.value = false
}
}
// 搜索来源店铺
const searchFromShops = async (query: string) => {
await loadFromShops(query || undefined)
}
// 显示批量分配对话框
const showAllocateDialog = () => {
if (selectedCards.value.length === 0) {
@@ -1209,7 +1238,7 @@
}
allocateDialogVisible.value = true
Object.assign(allocateForm, {
selection_type: 'list',
selection_type: CardSelectionType.LIST,
to_shop_id: undefined,
iccids: selectedCards.value.map((card) => card.iccid),
iccid_start: '',
@@ -1234,7 +1263,7 @@
}
recallDialogVisible.value = true
Object.assign(recallForm, {
selection_type: 'list',
selection_type: CardSelectionType.LIST,
iccids: selectedCards.value.map((card) => card.iccid),
iccid_start: '',
iccid_end: '',
@@ -1274,16 +1303,16 @@
remark: allocateForm.remark
}
if (allocateForm.selection_type === 'list') {
if (allocateForm.selection_type === CardSelectionType.LIST) {
params.iccids = selectedCards.value.map((card) => card.iccid)
if (params.iccids.length === 0) {
ElMessage.warning('请先选择要分配的卡')
return
}
} else if (allocateForm.selection_type === 'range') {
} else if (allocateForm.selection_type === CardSelectionType.RANGE) {
params.iccid_start = allocateForm.iccid_start
params.iccid_end = allocateForm.iccid_end
} else if (allocateForm.selection_type === 'filter') {
} else if (allocateForm.selection_type === CardSelectionType.FILTER) {
if (allocateForm.carrier_id) params.carrier_id = allocateForm.carrier_id
if (allocateForm.status) params.status = allocateForm.status
if (allocateForm.batch_no) params.batch_no = allocateForm.batch_no
@@ -1327,16 +1356,16 @@
remark: recallForm.remark
}
if (recallForm.selection_type === 'list') {
if (recallForm.selection_type === CardSelectionType.LIST) {
params.iccids = selectedCards.value.map((card) => card.iccid)
if (params.iccids.length === 0) {
ElMessage.warning('请先选择要回收的卡')
return
}
} else if (recallForm.selection_type === 'range') {
} else if (recallForm.selection_type === CardSelectionType.RANGE) {
params.iccid_start = recallForm.iccid_start
params.iccid_end = recallForm.iccid_end
} else if (recallForm.selection_type === 'filter') {
} else if (recallForm.selection_type === CardSelectionType.FILTER) {
if (recallForm.carrier_id) params.carrier_id = recallForm.carrier_id
if (recallForm.batch_no) params.batch_no = recallForm.batch_no
}
@@ -1358,7 +1387,7 @@
// 刷新列表
await getTableData()
} else {
// code !== 0 才是真正失败(接口调用失败)
// code !== 0 才是真正失败(接口调用失败)
ElMessage.error(res.msg || '批量回收失败,请重试')
}
} catch (error) {
@@ -1501,60 +1530,6 @@
})
// 卡操作菜单项配置
const cardOperationMenuItems = computed((): MenuItemType[] => {
const items: MenuItemType[] = []
if (hasAuth('iot_card:query_flow')) {
items.push({
key: 'query-flow',
label: '查询流量'
})
}
if (hasAuth('iot_card:query_realname_status')) {
items.push({
key: 'realname-status',
label: '查询实名状态'
})
}
if (hasAuth('iot_card:query_card_status')) {
items.push({
key: 'card-status',
label: '查询卡状态'
})
}
if (hasAuth('iot_card:start_card')) {
items.push({
key: 'start-card',
label: '启用此卡'
})
}
if (hasAuth('iot_card:stop_card')) {
items.push({
key: 'stop-card',
label: '停用此卡'
})
}
if (hasAuth('iot_card:manual_deactivate')) {
items.push({
key: 'manual-deactivate',
label: '手动停用'
})
}
return items
})
// 显示更多操作菜单 (右键)
const showMoreMenu = (e: MouseEvent) => {
e.preventDefault()
e.stopPropagation()
moreMenuRef.value?.show(e)
}
// 显示更多操作菜单 (左键点击)
const showMoreMenuOnClick = (e: MouseEvent) => {
@@ -1583,27 +1558,6 @@
}
}
// 显示卡操作菜单
const showCardOperationMenu = (e: MouseEvent, iccid: string, card?: StandaloneIotCard) => {
e.preventDefault()
e.stopPropagation()
currentOperatingIccid.value = iccid
currentOperatingCard.value = card || null
cardOperationMenuRef.value?.show(e)
}
// 处理卡操作菜单选择
const handleCardOperationMenuSelect = (item: MenuItemType) => {
const iccid = currentOperatingIccid.value
if (!iccid) return
if (item.key === 'query-flow') {
showFlowUsageDialog(iccid)
} else {
handleCardOperation(item.key, iccid)
}
}
// 网卡分销 - 正在开发中
const cardDistribution = () => {
ElMessage.info('功能正在开发中')
@@ -1645,7 +1599,7 @@
handleStopCard(iccid)
break
case 'manual-deactivate':
handleManualDeactivate()
handleManualDeactivate(iccid)
break
}
}
@@ -1668,7 +1622,7 @@
extend: res.data.extend
}
} else {
ElMessage.error(res.message || '查询失败')
ElMessage.error(res.msg || '查询失败')
flowUsageDialogVisible.value = false
}
} catch (error: any) {
@@ -1704,7 +1658,7 @@
extend: res.data.extend
}
} else {
ElMessage.error(res.message || '查询失败')
ElMessage.error(res.msg || '查询失败')
realnameStatusDialogVisible.value = false
}
} catch (error: any) {
@@ -1732,7 +1686,7 @@
extend: res.data.extend
}
} else {
ElMessage.error(res.message || '查询失败')
ElMessage.error(res.msg || '查询失败')
cardStatusDialogVisible.value = false
}
} catch (error: any) {
@@ -1752,11 +1706,11 @@
})
.then(async () => {
try {
// 使用接口:直接通过 ICCID 启用此卡
const res = await AssetService.startCard(iccid)
// 使用统一接口:通过 identifier (ICCID) 启用资产
const res = await AssetService.startAsset(iccid)
if (res.code === 0) {
ElMessage.success('启用成功')
getTableData()
await getTableData()
}
} catch (error: any) {
console.error('启用此卡失败:', error)
@@ -1776,11 +1730,11 @@
})
.then(async () => {
try {
// 使用接口:直接通过 ICCID 停用此卡
const res = await AssetService.stopCard(iccid)
// 使用统一接口:通过 identifier (ICCID) 停用资产
const res = await AssetService.stopAsset(iccid)
if (res.code === 0) {
ElMessage.success('停用成功')
getTableData()
await getTableData()
}
} catch (error: any) {
console.error('停用此卡失败:', error)
@@ -1792,15 +1746,9 @@
}
// 手动停用 IoT 卡(资产层面的停用)
const handleManualDeactivate = () => {
const card = currentOperatingCard.value
if (!card) {
ElMessage.error('未找到卡片信息')
return
}
const handleManualDeactivate = (iccid: string) => {
ElMessageBox.confirm(
`确定要手动停用卡片 ${card.iccid} 吗?此操作将在资产管理层面停用该卡。`,
`确定要手动停用卡片 ${iccid} 吗?此操作将在资产管理层面停用该卡。`,
'确认手动停用',
{
confirmButtonText: '确定',
@@ -1810,7 +1758,7 @@
)
.then(async () => {
try {
const res = await CardService.deactivateIotCard(card.id)
const res = await AssetService.deactivateAsset(iccid)
if (res.code === 0) {
ElMessage.success('手动停用成功')
await getTableData()
@@ -1823,18 +1771,9 @@
// 用户取消
})
}
// 处理表格行右键菜单
const handleRowContextMenu = (row: StandaloneIotCard, column: any, event: MouseEvent) => {
showCardOperationMenu(event, row.iccid, row)
}
</script>
<style lang="scss" scoped>
.standalone-card-list-page {
// Card list page styles
}
:deep(.el-table__row.table-row-with-context-menu) {
cursor: pointer;
}