fix:修改bug: 虚流量关闭的时候还是显示了
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m46s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m46s
This commit is contained in:
80
src/App.vue
80
src/App.vue
@@ -16,6 +16,7 @@
|
||||
import { setThemeTransitionClass } from '@/utils'
|
||||
import { checkStorageCompatibility } from '@/utils'
|
||||
import ChunkErrorBoundary from '@/components/core/others/ChunkErrorBoundary.vue'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { language } = storeToRefs(userStore)
|
||||
@@ -25,6 +26,12 @@
|
||||
en: en
|
||||
}
|
||||
|
||||
const VERSION_CHECK_INTERVAL = 5 * 60 * 1000
|
||||
let versionCheckTimer: number | undefined
|
||||
let currentEntrySignature = ''
|
||||
let upgradePromptVisible = false
|
||||
let removeVisibilityChangeListener: (() => void) | undefined
|
||||
|
||||
onBeforeMount(() => {
|
||||
setThemeTransitionClass(true)
|
||||
})
|
||||
@@ -36,10 +43,19 @@
|
||||
setThemeTransitionClass(false)
|
||||
// 系统升级
|
||||
systemUpgrade()
|
||||
// 检查前端版本更新
|
||||
startVersionUpdateCheck()
|
||||
// 获取用户信息
|
||||
getUserInfo()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (versionCheckTimer) {
|
||||
window.clearInterval(versionCheckTimer)
|
||||
}
|
||||
removeVisibilityChangeListener?.()
|
||||
})
|
||||
|
||||
// 获取用户信息
|
||||
const getUserInfo = async () => {
|
||||
if (userStore.isLogin && userStore.accessToken) {
|
||||
@@ -55,4 +71,68 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getEntrySignature = (html: string) => {
|
||||
const matches = html.match(/(?:src|href)="[^"]*\/assets\/[^""]+\.(?:js|css)"/g)
|
||||
return matches?.sort().join('|') || ''
|
||||
}
|
||||
|
||||
const getLoadedEntrySignature = () => {
|
||||
return Array.from(document.querySelectorAll<HTMLScriptElement | HTMLLinkElement>('script[src],link[href]'))
|
||||
.map((element) => ('src' in element ? element.src : element.href))
|
||||
.filter((url) => /\/assets\/.*\.(js|css)(\?|$)/.test(url))
|
||||
.sort()
|
||||
.join('|')
|
||||
}
|
||||
|
||||
const checkVersionUpdate = async () => {
|
||||
if (upgradePromptVisible || document.hidden) return
|
||||
|
||||
try {
|
||||
const response = await fetch(`${window.location.origin}${import.meta.env.BASE_URL}`, {
|
||||
cache: 'no-store'
|
||||
})
|
||||
const html = await response.text()
|
||||
const latestSignature = getEntrySignature(html)
|
||||
|
||||
if (!latestSignature) return
|
||||
|
||||
if (!currentEntrySignature) {
|
||||
currentEntrySignature = latestSignature
|
||||
return
|
||||
}
|
||||
|
||||
if (currentEntrySignature !== latestSignature) {
|
||||
upgradePromptVisible = true
|
||||
await ElMessageBox.alert('系统已发布新版本,请刷新页面后继续使用。', '发现新版本', {
|
||||
confirmButtonText: '立即刷新',
|
||||
type: 'warning',
|
||||
closeOnClickModal: false,
|
||||
closeOnPressEscape: false
|
||||
})
|
||||
window.location.reload()
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('检查版本更新失败:', error)
|
||||
} finally {
|
||||
upgradePromptVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
const startVersionUpdateCheck = () => {
|
||||
if (import.meta.env.DEV) return
|
||||
|
||||
currentEntrySignature = getLoadedEntrySignature()
|
||||
checkVersionUpdate()
|
||||
versionCheckTimer = window.setInterval(checkVersionUpdate, VERSION_CHECK_INTERVAL)
|
||||
const handleVisibilityChange = () => {
|
||||
if (!document.hidden) {
|
||||
checkVersionUpdate()
|
||||
}
|
||||
}
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange)
|
||||
removeVisibilityChangeListener = () => {
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
v-model="searchIccid"
|
||||
placeholder="请输入虚拟号、ICCID、IMEI、SN或MSISDN"
|
||||
clearable
|
||||
style="width: 500px"
|
||||
class="asset-search-input"
|
||||
@focus="iccidInputFocused = true"
|
||||
@blur="iccidInputFocused = false"
|
||||
@keyup.enter="handleSearch"
|
||||
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
<ElButton type="primary" @click="handleSearch" :icon="Search" style="margin-left: 16px">
|
||||
<ElButton class="search-action-button" type="primary" @click="handleSearch" :icon="Search">
|
||||
查询
|
||||
</ElButton>
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
@@ -159,6 +160,12 @@
|
||||
|
||||
.iccid-input-wrapper {
|
||||
position: relative;
|
||||
flex: 1 1 360px;
|
||||
min-width: 240px;
|
||||
|
||||
.asset-search-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.iccid-magnifier {
|
||||
position: absolute;
|
||||
@@ -182,6 +189,7 @@
|
||||
|
||||
// 内容区域
|
||||
.magnifier-content {
|
||||
max-width: min(720px, calc(100vw - 48px));
|
||||
padding: 16px 24px;
|
||||
font-family: 'Courier New', Consolas, monospace;
|
||||
font-size: 28px;
|
||||
@@ -189,6 +197,8 @@
|
||||
line-height: 1.4;
|
||||
color: var(--el-text-color-primary);
|
||||
letter-spacing: 3px;
|
||||
overflow-wrap: anywhere;
|
||||
white-space: normal;
|
||||
background-color: var(--el-bg-color, #fff);
|
||||
border: 2px solid var(--el-color-primary);
|
||||
border-radius: 8px;
|
||||
@@ -239,6 +249,56 @@
|
||||
:deep(.el-card__body) {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
:deep(.el-card__header) .card-header {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
|
||||
.el-button {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.iccid-search {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
|
||||
.iccid-input-wrapper {
|
||||
flex-basis: auto;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
|
||||
.iccid-magnifier {
|
||||
right: 0;
|
||||
left: 0;
|
||||
white-space: normal;
|
||||
|
||||
.magnifier-arrow {
|
||||
left: 24px;
|
||||
}
|
||||
|
||||
.magnifier-content {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
font-size: 18px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-action-button,
|
||||
.operation-button-group,
|
||||
.operation-button-group .el-button {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.operation-button-group {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<ElDescriptions :column="4" border>
|
||||
<ElDescriptions :column="descriptionsColumn" border>
|
||||
<!-- 卡专属字段 -->
|
||||
<template v-if="cardInfo?.asset_type === 'card'">
|
||||
<ElDescriptionsItem label="ICCID">{{ cardInfo?.iccid || '-' }}</ElDescriptionsItem>
|
||||
@@ -269,7 +269,7 @@
|
||||
<!-- 设备实时信息 -->
|
||||
<ElDivider content-position="left">设备实时信息</ElDivider>
|
||||
<div v-if="hasDeviceRealtimeSection" v-loading="realtimeLoading" style="margin-top: 16px">
|
||||
<ElDescriptions :column="4" border>
|
||||
<ElDescriptions :column="descriptionsColumn" border>
|
||||
<!-- 基本状态 -->
|
||||
<ElDescriptionsItem label="在线状态">
|
||||
<ElTag :type="getOnlineStatusType(deviceRealtime?.online_status)" size="small">
|
||||
@@ -461,6 +461,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import {
|
||||
ElCard,
|
||||
ElDescriptions,
|
||||
@@ -585,6 +586,7 @@
|
||||
|
||||
const { hasAuth } = useAuth()
|
||||
const userStore = useUserStore()
|
||||
const { width } = useWindowSize()
|
||||
|
||||
const CARD_MONTH_USAGE_PERMISSION = 'asset_info:view_card_month_usage'
|
||||
|
||||
@@ -609,6 +611,12 @@
|
||||
return ![3, 4].includes(userType) && hasAuth(CARD_MONTH_USAGE_PERMISSION)
|
||||
})
|
||||
|
||||
const descriptionsColumn = computed(() => {
|
||||
if (width.value <= 640) return 1
|
||||
if (width.value <= 1024) return 2
|
||||
return 4
|
||||
})
|
||||
|
||||
// Emits
|
||||
interface Emits {
|
||||
(e: 'update:pollingEnabled', value: boolean): void
|
||||
@@ -786,6 +794,7 @@
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -808,6 +817,56 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__body) {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__cell) {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
min-width: 760px;
|
||||
}
|
||||
|
||||
.card-operations,
|
||||
.device-operations {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
|
||||
.el-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
.card-header {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
|
||||
.card-header-left,
|
||||
.card-header-right {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card-header-right {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.card-operations,
|
||||
.device-operations {
|
||||
justify-content: stretch;
|
||||
|
||||
.el-button {
|
||||
flex: 1 1 120px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
/>
|
||||
</div>
|
||||
<!-- 虚流量 -->
|
||||
<div v-if="canViewCurrentPackageVirtualUsage" class="flow-stat-section">
|
||||
<div v-if="canShowCurrentPackageVirtualUsage" class="flow-stat-section">
|
||||
<div class="flow-stat-label">虚流量使用</div>
|
||||
<div class="flow-stat-row">
|
||||
<span class="stat-item">
|
||||
@@ -186,7 +186,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 套餐详情表格 -->
|
||||
<ElDescriptions :column="4" border class="package-info-table">
|
||||
<ElDescriptions :column="descriptionsColumn" border class="package-info-table">
|
||||
<ElDescriptionsItem label="订单号">
|
||||
{{ currentPackage.order_no || '-' }}
|
||||
</ElDescriptionsItem>
|
||||
@@ -232,6 +232,7 @@
|
||||
ElEmpty,
|
||||
ElAlert
|
||||
} from 'element-plus'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import { useAssetFormatters } from '../composables/useAssetFormatters'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
@@ -250,21 +251,12 @@
|
||||
} = useAssetFormatters()
|
||||
|
||||
const { hasAuth } = useAuth()
|
||||
const { width } = useWindowSize()
|
||||
const userStore = useUserStore()
|
||||
const { info: userInfo } = storeToRefs(userStore)
|
||||
const isAdminOrPlatform = computed(
|
||||
() => userInfo.value.user_type === 1 || userInfo.value.user_type === 2
|
||||
)
|
||||
const canViewCurrentPackageRealUsage = computed(() =>
|
||||
hasAuth('asset_info:view_current_package_real_usage')
|
||||
)
|
||||
const canViewCurrentPackageVirtualUsage = computed(() =>
|
||||
hasAuth('asset_info:view_current_package_virtual_usage')
|
||||
)
|
||||
const shouldShowTrafficCard = computed(() => {
|
||||
if (!isAdminOrPlatform.value) return true
|
||||
return canViewCurrentPackageRealUsage.value || canViewCurrentPackageVirtualUsage.value
|
||||
})
|
||||
|
||||
interface Props {
|
||||
currentPackage: AssetPackageUsageRecord | null
|
||||
@@ -283,6 +275,26 @@
|
||||
boundDeviceName: ''
|
||||
})
|
||||
|
||||
const canViewCurrentPackageRealUsage = computed(() =>
|
||||
hasAuth('asset_info:view_current_package_real_usage')
|
||||
)
|
||||
const canViewCurrentPackageVirtualUsage = computed(() =>
|
||||
hasAuth('asset_info:view_current_package_virtual_usage')
|
||||
)
|
||||
const canShowCurrentPackageVirtualUsage = computed(
|
||||
() => canViewCurrentPackageVirtualUsage.value && props.currentPackage?.enable_virtual_data !== false
|
||||
)
|
||||
const shouldShowTrafficCard = computed(() => {
|
||||
if (!isAdminOrPlatform.value) return true
|
||||
return canViewCurrentPackageRealUsage.value || canShowCurrentPackageVirtualUsage.value
|
||||
})
|
||||
|
||||
const descriptionsColumn = computed(() => {
|
||||
if (width.value <= 640) return 1
|
||||
if (width.value <= 1024) return 2
|
||||
return 4
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
showRecharge: []
|
||||
navigateToDevice: [deviceNo: string]
|
||||
@@ -307,10 +319,11 @@
|
||||
|
||||
<style scoped lang="scss">
|
||||
.current-package-card {
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.card-header {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.header-title {
|
||||
font-size: 16px;
|
||||
@@ -324,9 +337,10 @@
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.flow-stats-container {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
.flow-stats-container {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
min-width: 0;
|
||||
|
||||
.flow-stat-section {
|
||||
flex: 1;
|
||||
@@ -341,6 +355,7 @@
|
||||
|
||||
.flow-stat-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
@@ -366,6 +381,7 @@
|
||||
|
||||
.flow-stats-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 32px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
@@ -433,5 +449,54 @@
|
||||
.package-info-table {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__body) {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__cell) {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@media (width <= 992px) {
|
||||
.flow-progress-card {
|
||||
.flow-stats-container {
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
.card-header {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
|
||||
.header-actions {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-progress-card {
|
||||
padding: 14px;
|
||||
|
||||
.flow-stats-row,
|
||||
.flow-stat-row {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.progress-wrapper {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.progress-percentage {
|
||||
min-width: 0;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
v-model="filterForm.operation_type"
|
||||
placeholder="操作类型"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
class="operation-type-select"
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<ElOption
|
||||
@@ -22,7 +22,7 @@
|
||||
v-model="filterForm.result_status"
|
||||
placeholder="执行结果"
|
||||
clearable
|
||||
style="width: 120px"
|
||||
class="result-status-select"
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<ElOption label="成功" value="success" />
|
||||
@@ -99,6 +99,7 @@
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
<ElPagination
|
||||
class="logs-pagination"
|
||||
v-if="total > 0"
|
||||
v-model:current-page="pagination.page"
|
||||
v-model:page-size="pagination.page_size"
|
||||
@@ -107,7 +108,6 @@
|
||||
layout="total, prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
@size-change="handleSizeChange"
|
||||
style="margin-top: 16px; justify-content: flex-end"
|
||||
/>
|
||||
</div>
|
||||
</ElCard>
|
||||
@@ -619,11 +619,26 @@
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
min-width: 0;
|
||||
|
||||
.operation-type-select {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.result-status-select {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logs-table-wrapper {
|
||||
overflow-x: auto;
|
||||
|
||||
.logs-table {
|
||||
width: max-content;
|
||||
min-width: 980px;
|
||||
|
||||
:deep(.el-table__header-wrapper) {
|
||||
th {
|
||||
background-color: var(--el-fill-color-light);
|
||||
@@ -668,6 +683,56 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logs-pagination {
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 1200px) {
|
||||
.card-header {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
|
||||
.filter-section {
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
.card-header {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
|
||||
.filter-section {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
|
||||
:deep(.el-select),
|
||||
.el-button,
|
||||
.el-tag {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logs-table-wrapper {
|
||||
margin-right: -12px;
|
||||
margin-left: -12px;
|
||||
padding: 0 12px;
|
||||
|
||||
:deep(.el-pagination) {
|
||||
justify-content: flex-start !important;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<ElTableColumn label="流量" min-width="450">
|
||||
<template #default="scope">
|
||||
<div class="traffic-progress">
|
||||
<template v-if="isAdminOrPlatform && shouldShowAdminTrafficBreakdown">
|
||||
<template v-if="isAdminOrPlatform && shouldShowAdminTrafficBreakdown(scope.row)">
|
||||
<div v-if="canViewCurrentPackageRealUsage" class="progress-text">
|
||||
<span class="used"
|
||||
>已使用真流量: {{ formatDataSize(scope.row.real_used_mb || 0) }}</span
|
||||
@@ -93,7 +93,7 @@
|
||||
"
|
||||
/>
|
||||
<div
|
||||
v-if="canViewCurrentPackageVirtualUsage"
|
||||
v-if="canShowPackageVirtualUsage(scope.row)"
|
||||
class="progress-text"
|
||||
:style="{ marginTop: canViewCurrentPackageRealUsage ? '8px' : '0' }"
|
||||
>
|
||||
@@ -104,7 +104,7 @@
|
||||
>停机阈值: {{ formatDataSize(scope.row.virtual_total_mb || 0) }}</span
|
||||
>
|
||||
</div>
|
||||
<div v-if="canViewCurrentPackageVirtualUsage" class="progress-wrapper">
|
||||
<div v-if="canShowPackageVirtualUsage(scope.row)" class="progress-wrapper">
|
||||
<div class="progress-track">
|
||||
<ElProgress
|
||||
style="margin-top: 4px"
|
||||
@@ -285,11 +285,6 @@
|
||||
const isAdminOrPlatform = computed(
|
||||
() => userInfo.value.user_type === 1 || userInfo.value.user_type === 2
|
||||
)
|
||||
const shouldShowAdminTrafficBreakdown = computed(() => {
|
||||
if (!isAdminOrPlatform.value) return true
|
||||
return canViewCurrentPackageRealUsage.value || canViewCurrentPackageVirtualUsage.value
|
||||
})
|
||||
|
||||
// 退款申请对话框
|
||||
const createRefundDialogVisible = ref(false)
|
||||
const currentRefundOrderNo = ref<string | undefined>(undefined)
|
||||
@@ -310,6 +305,14 @@
|
||||
// Props 使用 API 类型
|
||||
type PackageInfo = AssetPackageUsageRecord
|
||||
|
||||
const canShowPackageVirtualUsage = (pkg: PackageInfo) =>
|
||||
canViewCurrentPackageVirtualUsage.value && pkg.enable_virtual_data !== false
|
||||
|
||||
const shouldShowAdminTrafficBreakdown = (pkg: PackageInfo) => {
|
||||
if (!isAdminOrPlatform.value) return true
|
||||
return canViewCurrentPackageRealUsage.value || canShowPackageVirtualUsage(pkg)
|
||||
}
|
||||
|
||||
interface Props {
|
||||
packageList: PackageInfo[]
|
||||
total?: number
|
||||
@@ -459,6 +462,7 @@
|
||||
.traffic-progress {
|
||||
.progress-text {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -517,6 +521,49 @@
|
||||
margin-top: 16px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
.card-header {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
.card-header-right,
|
||||
.card-header-right .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.package-table-wrapper {
|
||||
.package-table {
|
||||
min-width: 900px;
|
||||
}
|
||||
|
||||
.traffic-progress {
|
||||
.progress-text {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.progress-wrapper {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.progress-percentage {
|
||||
min-width: 0;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
justify-content: flex-start;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
{{ walletInfo.status_text || '-' }}
|
||||
</ElTag>
|
||||
<template v-if="walletInfo">
|
||||
<ElDivider direction="vertical" />
|
||||
<ElDivider class="wallet-divider" direction="vertical" />
|
||||
<span class="balance-item"
|
||||
>💰 总余额 <strong>{{ formatAmount(walletInfo.balance) }}</strong></span
|
||||
>
|
||||
<ElDivider direction="vertical" />
|
||||
<ElDivider class="wallet-divider" direction="vertical" />
|
||||
<span class="balance-item"
|
||||
>✅ 可用 <strong>{{ formatAmount(walletInfo.available_balance) }}</strong></span
|
||||
>
|
||||
<ElDivider direction="vertical" />
|
||||
<ElDivider class="wallet-divider" direction="vertical" />
|
||||
<span class="balance-item"
|
||||
>🔒 冻结 <strong>{{ formatAmount(walletInfo.frozen_balance) }}</strong></span
|
||||
>
|
||||
@@ -27,7 +27,7 @@
|
||||
v-model="filterForm.transaction_type"
|
||||
placeholder="交易类型"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
class="transaction-type-select"
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<ElOption label="充值" value="recharge" />
|
||||
@@ -40,7 +40,7 @@
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
style="width: 360px"
|
||||
class="date-range-picker"
|
||||
/>
|
||||
<ElButton type="primary" @click="handleQuery">查询</ElButton>
|
||||
<ElButton @click="handleReset()">重置</ElButton>
|
||||
@@ -356,6 +356,10 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.wallet-divider {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.balance-item {
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-regular);
|
||||
@@ -372,10 +376,22 @@
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
min-width: 0;
|
||||
|
||||
.transaction-type-select {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.date-range-picker {
|
||||
width: min(360px, 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wallet-table-wrapper {
|
||||
overflow-x: auto;
|
||||
|
||||
.wallet-empty-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -384,6 +400,9 @@
|
||||
}
|
||||
|
||||
.wallet-table {
|
||||
width: max-content;
|
||||
min-width: 900px;
|
||||
|
||||
:deep(.el-table__header) {
|
||||
th {
|
||||
font-weight: 600;
|
||||
@@ -396,6 +415,84 @@
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 1200px) {
|
||||
.wallet-header {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
|
||||
.header-left,
|
||||
.filter-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-section {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
.wallet-header {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
|
||||
.header-left,
|
||||
.filter-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter-section {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
|
||||
:deep(.el-select),
|
||||
:deep(.el-date-editor),
|
||||
.el-button,
|
||||
.el-tag {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.header-left {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.wallet-divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.balance-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wallet-table-wrapper {
|
||||
.wallet-table {
|
||||
min-width: 900px;
|
||||
}
|
||||
|
||||
.pagination-wrapper {
|
||||
justify-content: flex-start;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 480px) {
|
||||
.wallet-table-wrapper {
|
||||
margin-right: -12px;
|
||||
margin-left: -12px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,6 +691,8 @@
|
||||
|
||||
.single-card-page {
|
||||
padding: 20px;
|
||||
max-width: 100%;
|
||||
overflow-x: hidden;
|
||||
|
||||
// 资产查询卡片
|
||||
.search-card {
|
||||
@@ -828,11 +830,14 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
min-width: 0;
|
||||
|
||||
// 行布局
|
||||
.row {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
|
||||
&.full-width {
|
||||
.info-card {
|
||||
@@ -900,13 +905,15 @@
|
||||
// 信息卡片通用样式
|
||||
.info-card {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
|
||||
:deep(.el-card__header) {
|
||||
padding: 20px 24px 16px;
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
.card-header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 16px;
|
||||
@@ -1296,6 +1303,8 @@
|
||||
}
|
||||
|
||||
.wallet-table-wrapper {
|
||||
overflow-x: auto;
|
||||
|
||||
.wallet-table {
|
||||
:deep(.el-table__header) {
|
||||
th {
|
||||
@@ -1363,6 +1372,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state-card,
|
||||
.empty-state {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
:deep(.el-pagination) {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.traffic-info .traffic-overview {
|
||||
.progress-stats {
|
||||
flex-direction: column;
|
||||
@@ -1381,6 +1399,22 @@
|
||||
}
|
||||
|
||||
@media (width <= 480px) {
|
||||
padding: 10px;
|
||||
|
||||
.main-content-layout {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
:deep(.el-card__body) {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
:deep(.el-card__header) {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.traffic-info .traffic-overview {
|
||||
.progress-percentage {
|
||||
font-size: 20px;
|
||||
|
||||
Reference in New Issue
Block a user