1513 lines
55 KiB
Vue
1513 lines
55 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref } from 'vue'
|
|
|
|
const currentTab = ref('my-packages') // my-packages | commission | packages | assets | tags
|
|
const currentView = ref('list') // list | detail
|
|
|
|
// 套餐状态枚举
|
|
const packageStatus = {
|
|
0: { name: '待生效', color: '#ff9800', bgColor: '#fff3e0' },
|
|
1: { name: '生效中', color: '#4caf50', bgColor: '#e8f5e9' },
|
|
2: { name: '已用完', color: '#9e9e9e', bgColor: '#f5f5f5' },
|
|
3: { name: '已过期', color: '#f44336', bgColor: '#ffebee' },
|
|
4: { name: '已失效', color: '#757575', bgColor: '#eeeeee' },
|
|
}
|
|
|
|
// 套餐类型
|
|
const packageTypeMap = {
|
|
formal: '正式套餐',
|
|
addon: '加油包',
|
|
}
|
|
|
|
// 套餐列表数据 (示例)
|
|
const packageList = ref([
|
|
{
|
|
package_usage_id: 1,
|
|
package_id: 101,
|
|
package_name: '月付套餐 50GB',
|
|
package_type: 'formal',
|
|
status: 1,
|
|
status_name: '生效中',
|
|
data_limit_mb: 51200, // 50GB
|
|
data_usage_mb: 12800, // 12.5GB
|
|
virtual_limit_mb: 102400, // 虚流量100GB
|
|
virtual_used_mb: 25600, // 已用虚流量25GB
|
|
virtual_remain_mb: 76800, // 剩余虚流量75GB
|
|
virtual_ratio: 0.5, // 真实/虚拟比例
|
|
priority: 1,
|
|
usage_type: 'single_card',
|
|
master_usage_id: null,
|
|
activated_at: '2024-03-01T10:00:00Z',
|
|
expires_at: '2024-04-01T10:00:00Z',
|
|
created_at: '2024-02-25T15:30:00Z',
|
|
},
|
|
{
|
|
package_usage_id: 2,
|
|
package_id: 102,
|
|
package_name: '加油包 20GB',
|
|
package_type: 'addon',
|
|
status: 1,
|
|
status_name: '生效中',
|
|
data_limit_mb: 20480, // 20GB
|
|
data_usage_mb: 5120, // 5GB
|
|
virtual_limit_mb: 40960,
|
|
virtual_used_mb: 10240,
|
|
virtual_remain_mb: 30720,
|
|
virtual_ratio: 0.5,
|
|
priority: 2,
|
|
usage_type: 'single_card',
|
|
master_usage_id: 1,
|
|
activated_at: '2024-03-15T14:00:00Z',
|
|
expires_at: '2024-04-15T14:00:00Z',
|
|
created_at: '2024-03-15T13:00:00Z',
|
|
},
|
|
{
|
|
package_usage_id: 3,
|
|
package_id: 103,
|
|
package_name: '年付套餐 500GB',
|
|
package_type: 'formal',
|
|
status: 0,
|
|
status_name: '待生效',
|
|
data_limit_mb: 512000, // 500GB
|
|
data_usage_mb: 0,
|
|
virtual_limit_mb: 1024000,
|
|
virtual_used_mb: 0,
|
|
virtual_remain_mb: 1024000,
|
|
virtual_ratio: 0.5,
|
|
priority: 3,
|
|
usage_type: 'device',
|
|
master_usage_id: null,
|
|
activated_at: null,
|
|
expires_at: null,
|
|
created_at: '2024-03-18T09:00:00Z',
|
|
},
|
|
{
|
|
package_usage_id: 4,
|
|
package_id: 104,
|
|
package_name: '月付套餐 30GB',
|
|
package_type: 'formal',
|
|
status: 2,
|
|
status_name: '已用完',
|
|
data_limit_mb: 30720, // 30GB
|
|
data_usage_mb: 30720, // 已用完
|
|
virtual_limit_mb: 61440,
|
|
virtual_used_mb: 61440,
|
|
virtual_remain_mb: 0,
|
|
virtual_ratio: 0.5,
|
|
priority: 4,
|
|
usage_type: 'single_card',
|
|
master_usage_id: null,
|
|
activated_at: '2024-02-01T10:00:00Z',
|
|
expires_at: '2024-03-01T10:00:00Z',
|
|
created_at: '2024-01-28T11:00:00Z',
|
|
},
|
|
])
|
|
|
|
// 选中的套餐详情
|
|
const selectedPackage = ref<any>(null)
|
|
|
|
// 选中的标签 (用于分配)
|
|
const selectedTag = ref<any>(null)
|
|
|
|
// 标签分配模式
|
|
const isAssignMode = ref(false)
|
|
|
|
// 切换Tab
|
|
function switchTab(tab: string) {
|
|
currentTab.value = tab
|
|
currentView.value = 'list'
|
|
selectedPackage.value = null
|
|
}
|
|
|
|
// 查看详情
|
|
function navigateTo(view: string) {
|
|
currentView.value = view
|
|
if (view === 'list') {
|
|
selectedPackage.value = null
|
|
selectedTag.value = null
|
|
isAssignMode.value = false
|
|
}
|
|
}
|
|
|
|
// 进入标签分配模式
|
|
function enterAssignMode(tag: any) {
|
|
selectedTag.value = tag
|
|
isAssignMode.value = true
|
|
currentView.value = 'assign'
|
|
}
|
|
|
|
// 切换资产标签
|
|
function toggleAssetTag(asset: any, tagName: string) {
|
|
const index = asset.tags.indexOf(tagName)
|
|
if (index > -1) {
|
|
// 已有该标签,移除
|
|
asset.tags.splice(index, 1)
|
|
}
|
|
else {
|
|
// 没有该标签,添加
|
|
asset.tags.push(tagName)
|
|
}
|
|
}
|
|
|
|
// 查看套餐详情
|
|
function viewPackageDetail(pkg: any) {
|
|
selectedPackage.value = pkg
|
|
currentView.value = 'detail'
|
|
}
|
|
|
|
// 格式化流量 (MB to GB)
|
|
function formatDataSize(mb: number): string {
|
|
if (mb < 1024)
|
|
return `${mb}MB`
|
|
return `${(mb / 1024).toFixed(1)}GB`
|
|
}
|
|
|
|
// 计算使用百分比
|
|
function calcUsagePercent(used: number, total: number): number {
|
|
if (total === 0)
|
|
return 0
|
|
return Math.min(Math.round((used / total) * 100), 100)
|
|
}
|
|
|
|
// 格式化时间
|
|
function formatDate(dateStr: string | null): string {
|
|
if (!dateStr)
|
|
return '--'
|
|
const date = new Date(dateStr)
|
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
|
}
|
|
|
|
// 格式化日期时间
|
|
function formatDateTime(dateStr: string | null): string {
|
|
if (!dateStr)
|
|
return '--'
|
|
const date = new Date(dateStr)
|
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
|
|
}
|
|
|
|
// 计算剩余天数
|
|
function calcRemainingDays(expiresAt: string | null): number | null {
|
|
if (!expiresAt)
|
|
return null
|
|
const now = new Date()
|
|
const expireDate = new Date(expiresAt)
|
|
const diff = expireDate.getTime() - now.getTime()
|
|
return Math.ceil(diff / (1000 * 60 * 60 * 24))
|
|
}
|
|
|
|
// 统计数据
|
|
const stats = computed(() => {
|
|
const total = packageList.value.length
|
|
const active = packageList.value.filter(p => p.status === 1).length
|
|
const pending = packageList.value.filter(p => p.status === 0).length
|
|
const expired = packageList.value.filter(p => p.status === 3 || p.status === 4).length
|
|
|
|
return [
|
|
{ label: '套餐总数', value: total.toString(), unit: '个', icon: 'i-mdi-package-variant', color: '#2196f3' },
|
|
{ label: '生效中', value: active.toString(), unit: '个', icon: 'i-mdi-check-circle', color: '#4caf50' },
|
|
{ label: '待生效', value: pending.toString(), unit: '个', icon: 'i-mdi-clock', color: '#ff9800' },
|
|
{ label: '已过期', value: expired.toString(), unit: '个', icon: 'i-mdi-alert-circle', color: '#f44336' },
|
|
]
|
|
})
|
|
|
|
// 标签列表数据 (示例)
|
|
const tagList = ref([
|
|
{ id: 1, name: '热门推荐', color: '#ff6700', asset_count: 12, description: '热门高转化资产' },
|
|
{ id: 2, name: '新品上架', color: '#4caf50', asset_count: 8, description: '最近30天上架' },
|
|
{ id: 3, name: 'VIP专享', color: '#2196f3', asset_count: 6, description: 'VIP用户专属' },
|
|
{ id: 4, name: '限时优惠', color: '#ff9800', asset_count: 15, description: '限时促销活动' },
|
|
{ id: 5, name: '高佣金', color: '#9c27b0', asset_count: 10, description: '佣金比例较高' },
|
|
{ id: 6, name: '长期有效', color: '#00bcd4', asset_count: 20, description: '长期有效套餐' },
|
|
])
|
|
|
|
// 资产列表数据 (示例)
|
|
const assetList = ref([
|
|
{
|
|
id: 1,
|
|
name: '资产名称 A1',
|
|
code: 'AST-2024-001',
|
|
tags: ['热门'],
|
|
status: 'active',
|
|
package_count: 2,
|
|
total_data_mb: 71680, // 70GB
|
|
},
|
|
{
|
|
id: 2,
|
|
name: '资产名称 B2',
|
|
code: 'AST-2024-002',
|
|
tags: ['新品'],
|
|
status: 'active',
|
|
package_count: 1,
|
|
total_data_mb: 51200, // 50GB
|
|
},
|
|
{
|
|
id: 3,
|
|
name: '资产名称 C3',
|
|
code: 'AST-2024-003',
|
|
tags: [],
|
|
status: 'inactive',
|
|
package_count: 0,
|
|
total_data_mb: 0,
|
|
},
|
|
])
|
|
|
|
// 佣金概览数据 (示例)
|
|
const commissionOverview = ref({
|
|
shop_id: 1,
|
|
shop_name: '示例店铺',
|
|
total_commission: 125800, // 1258.00元 (分)
|
|
available_commission: 85000, // 850.00元
|
|
withdrawn_commission: 30000, // 300.00元
|
|
withdrawing_commission: 10000, // 100.00元
|
|
frozen_commission: 800, // 8.00元
|
|
unwithdraw_commission: 95800, // 958.00元
|
|
})
|
|
|
|
// 佣金统计数据 (示例)
|
|
const commissionStats = ref({
|
|
total_amount: 125800, // 1258.00元
|
|
total_count: 156,
|
|
cost_diff_amount: 95000, // 950.00元
|
|
cost_diff_count: 120,
|
|
cost_diff_percent: 755, // 75.5%
|
|
one_time_amount: 30800, // 308.00元
|
|
one_time_count: 36,
|
|
one_time_percent: 245, // 24.5%
|
|
})
|
|
|
|
// 每日佣金统计 (示例)
|
|
const dailyCommission = ref([
|
|
{ date: '2024-03-18', total_amount: 12800, total_count: 8 },
|
|
{ date: '2024-03-17', total_amount: 15600, total_count: 12 },
|
|
{ date: '2024-03-16', total_amount: 9800, total_count: 6 },
|
|
{ date: '2024-03-15', total_amount: 18900, total_count: 15 },
|
|
{ date: '2024-03-14', total_amount: 11200, total_count: 9 },
|
|
])
|
|
|
|
// 佣金明细列表 (示例)
|
|
const commissionDetails = ref([
|
|
{
|
|
id: 1,
|
|
order_id: 1001,
|
|
shop_id: 1,
|
|
amount: 12800, // 128.00元
|
|
commission_source: 'cost_diff',
|
|
status: 1,
|
|
status_name: '已入账',
|
|
created_at: '2024-03-18T14:32:00Z',
|
|
},
|
|
{
|
|
id: 2,
|
|
order_id: 1002,
|
|
shop_id: 1,
|
|
amount: 5600, // 56.00元
|
|
commission_source: 'one_time',
|
|
status: 1,
|
|
status_name: '已入账',
|
|
created_at: '2024-03-18T10:15:00Z',
|
|
},
|
|
{
|
|
id: 3,
|
|
order_id: 1003,
|
|
shop_id: 1,
|
|
amount: 8900, // 89.00元
|
|
commission_source: 'cost_diff',
|
|
status: 1,
|
|
status_name: '已入账',
|
|
created_at: '2024-03-17T16:48:00Z',
|
|
},
|
|
{
|
|
id: 4,
|
|
order_id: 1004,
|
|
shop_id: 1,
|
|
amount: 3200, // 32.00元
|
|
commission_source: 'one_time',
|
|
status: 2,
|
|
status_name: '已失效',
|
|
created_at: '2024-03-17T09:20:00Z',
|
|
},
|
|
])
|
|
|
|
// 佣金来源映射
|
|
const commissionSourceMap: Record<string, string> = {
|
|
cost_diff: '成本价差',
|
|
one_time: '一次性佣金',
|
|
tier_bonus: '梯度奖励',
|
|
}
|
|
|
|
// 格式化金额 (分 to 元)
|
|
function formatAmount(cents: number): string {
|
|
return (cents / 100).toFixed(2)
|
|
}
|
|
|
|
// 我的套餐使用情况数据 (示例)
|
|
const myPackageUsage = ref({
|
|
main_package: {
|
|
package_usage_id: 1,
|
|
package_id: 101,
|
|
package_name: '月付套餐 50GB',
|
|
status: 1,
|
|
status_text: '生效中',
|
|
total_mb: 51200, // 50GB
|
|
used_mb: 12800, // 12.5GB
|
|
priority: 1,
|
|
activated_at: '2024-03-01T10:00:00Z',
|
|
expires_at: '2024-04-01T10:00:00Z',
|
|
},
|
|
addon_packages: [
|
|
{
|
|
package_usage_id: 2,
|
|
package_id: 102,
|
|
package_name: '加油包 20GB',
|
|
status: 1,
|
|
status_text: '生效中',
|
|
total_mb: 20480, // 20GB
|
|
used_mb: 5120, // 5GB
|
|
priority: 2,
|
|
activated_at: '2024-03-15T14:00:00Z',
|
|
expires_at: '2024-04-15T14:00:00Z',
|
|
},
|
|
{
|
|
package_usage_id: 5,
|
|
package_id: 105,
|
|
package_name: '加油包 10GB',
|
|
status: 0,
|
|
status_text: '待生效',
|
|
total_mb: 10240, // 10GB
|
|
used_mb: 0,
|
|
priority: 3,
|
|
activated_at: null,
|
|
expires_at: null,
|
|
},
|
|
],
|
|
total: {
|
|
total_mb: 81920, // 80GB (50+20+10)
|
|
used_mb: 17920, // 17.5GB (12.5+5)
|
|
},
|
|
})
|
|
|
|
// 资产统计
|
|
const assetStats = computed(() => {
|
|
const total = assetList.value.length
|
|
const active = assetList.value.filter(a => a.status === 'active').length
|
|
const totalPackages = assetList.value.reduce((sum, a) => sum + a.package_count, 0)
|
|
const totalData = assetList.value.reduce((sum, a) => sum + a.total_data_mb, 0)
|
|
|
|
return [
|
|
{ label: '资产总数', value: total.toString(), unit: '个', icon: 'i-mdi-package-variant', color: '#2196f3' },
|
|
{ label: '激活中', value: active.toString(), unit: '个', icon: 'i-mdi-check-circle', color: '#4caf50' },
|
|
{ label: '套餐总数', value: totalPackages.toString(), unit: '个', icon: 'i-mdi-gift', color: '#ff9800' },
|
|
{ label: '总流量', value: formatDataSize(totalData), unit: '', icon: 'i-mdi-database', color: '#ff6700' },
|
|
]
|
|
})
|
|
|
|
// 筛选状态
|
|
const filterStatus = ref<number | null>(null)
|
|
|
|
// 筛选后的套餐列表
|
|
const filteredPackages = computed(() => {
|
|
if (filterStatus.value === null) {
|
|
return packageList.value
|
|
}
|
|
return packageList.value.filter(p => p.status === filterStatus.value)
|
|
})
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<view class="mobile-agent-system bg-[#fafafa] min-h-screen pb-safe">
|
|
<!-- 主内容区 -->
|
|
<view class="px-4 pt-4">
|
|
<!-- Tab 切换 -->
|
|
<view class="grid grid-cols-5 gap-1 bg-[#f5f5f5] rounded-full p-1 mb-4">
|
|
<button
|
|
:class="{
|
|
'bg-white shadow-sm text-[#212121]': currentTab === 'my-packages',
|
|
'text-[#9e9e9e]': currentTab !== 'my-packages'
|
|
}"
|
|
class="py-2 rounded-full text-11px font-500 transition-all duration-200"
|
|
@click="switchTab('my-packages')"
|
|
>
|
|
我的套餐
|
|
</button>
|
|
<button
|
|
:class="{
|
|
'bg-white shadow-sm text-[#212121]': currentTab === 'commission',
|
|
'text-[#9e9e9e]': currentTab !== 'commission'
|
|
}"
|
|
class="py-2 rounded-full text-11px font-500 transition-all duration-200"
|
|
@click="switchTab('commission')"
|
|
>
|
|
我的佣金
|
|
</button>
|
|
<button
|
|
:class="{
|
|
'bg-white shadow-sm text-[#212121]': currentTab === 'packages',
|
|
'text-[#9e9e9e]': currentTab !== 'packages'
|
|
}"
|
|
class="py-2 rounded-full text-11px font-500 transition-all duration-200"
|
|
@click="switchTab('packages')"
|
|
>
|
|
套餐列表
|
|
</button>
|
|
<button
|
|
:class="{
|
|
'bg-white shadow-sm text-[#212121]': currentTab === 'assets',
|
|
'text-[#9e9e9e]': currentTab !== 'assets'
|
|
}"
|
|
class="py-2 rounded-full text-11px font-500 transition-all duration-200"
|
|
@click="switchTab('assets')"
|
|
>
|
|
资产列表
|
|
</button>
|
|
<button
|
|
:class="{
|
|
'bg-white shadow-sm text-[#212121]': currentTab === 'tags',
|
|
'text-[#9e9e9e]': currentTab !== 'tags'
|
|
}"
|
|
class="py-2 rounded-full text-11px font-500 transition-all duration-200"
|
|
@click="switchTab('tags')"
|
|
>
|
|
标签管理
|
|
</button>
|
|
</view>
|
|
|
|
<!-- 我的佣金 -->
|
|
<view v-if="currentTab === 'commission'">
|
|
<!-- 佣金概览卡片 (绿色渐变) -->
|
|
<view class="
|
|
bg-gradient-to-br from-[#4caf50] to-[#66bb6a]
|
|
rounded-16px p-6 mb-4
|
|
shadow-[0_8px_24px_rgba(76,175,80,0.2)]
|
|
">
|
|
<text class="text-13px text-white/70 block mb-2">可提现佣金</text>
|
|
|
|
<text class="text-48px font-700 text-white block mb-4">
|
|
¥{{ formatAmount(commissionOverview.available_commission) }}
|
|
</text>
|
|
|
|
<view class="flex items-center gap-6 mb-4">
|
|
<view>
|
|
<text class="text-11px text-white/60 block">累计佣金</text>
|
|
<text class="text-16px font-600 text-white">¥{{ formatAmount(commissionOverview.total_commission) }}</text>
|
|
</view>
|
|
<view class="w-1px h-24px bg-white/20" />
|
|
<view>
|
|
<text class="text-11px text-white/60 block">已提现</text>
|
|
<text class="text-16px font-600 text-white">¥{{ formatAmount(commissionOverview.withdrawn_commission) }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<button class="
|
|
w-full
|
|
bg-white text-[#4caf50]
|
|
py-3 rounded-8px
|
|
text-15px font-600
|
|
active:scale-98
|
|
transition-transform duration-200
|
|
">
|
|
立即提现
|
|
</button>
|
|
</view>
|
|
|
|
<!-- 佣金状态卡片组 -->
|
|
<view class="grid grid-cols-3 gap-2 mb-4">
|
|
<view class="bg-white rounded-8px p-3 text-center shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
<text class="text-11px text-[#9e9e9e] block mb-1">提现中</text>
|
|
<text class="text-16px font-600 text-[#ff9800]">¥{{ formatAmount(commissionOverview.withdrawing_commission) }}</text>
|
|
</view>
|
|
<view class="bg-white rounded-8px p-3 text-center shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
<text class="text-11px text-[#9e9e9e] block mb-1">冻结中</text>
|
|
<text class="text-16px font-600 text-[#f44336]">¥{{ formatAmount(commissionOverview.frozen_commission) }}</text>
|
|
</view>
|
|
<view class="bg-white rounded-8px p-3 text-center shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
<text class="text-11px text-[#9e9e9e] block mb-1">未提现</text>
|
|
<text class="text-16px font-600 text-[#2196f3]">¥{{ formatAmount(commissionOverview.unwithdraw_commission) }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 佣金统计 -->
|
|
<view class="bg-white rounded-12px p-4 mb-4 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
<text class="text-16px font-600 text-[#212121] block mb-3">佣金统计</text>
|
|
|
|
<view class="space-y-3">
|
|
<!-- 总计 -->
|
|
<view class="flex items-center justify-between pb-3 border-b border-[#eeeeee]">
|
|
<view>
|
|
<text class="text-13px text-[#757575] block">总收入</text>
|
|
<text class="text-11px text-[#bdbdbd]">{{ commissionStats.total_count }} 笔</text>
|
|
</view>
|
|
<text class="text-20px font-700 text-[#212121]">
|
|
¥{{ formatAmount(commissionStats.total_amount) }}
|
|
</text>
|
|
</view>
|
|
|
|
<!-- 成本价差 -->
|
|
<view class="flex items-center justify-between">
|
|
<view class="flex-1">
|
|
<view class="flex items-center gap-2 mb-1">
|
|
<text class="text-13px text-[#757575]">成本价差</text>
|
|
<span class="px-2 py-0.5 bg-[#e3f2fd] text-[#2196f3] text-10px rounded-full">
|
|
{{ (commissionStats.cost_diff_percent / 10).toFixed(1) }}%
|
|
</span>
|
|
</view>
|
|
<text class="text-11px text-[#bdbdbd]">{{ commissionStats.cost_diff_count }} 笔</text>
|
|
</view>
|
|
<text class="text-18px font-600 text-[#212121]">
|
|
¥{{ formatAmount(commissionStats.cost_diff_amount) }}
|
|
</text>
|
|
</view>
|
|
|
|
<!-- 一次性佣金 -->
|
|
<view class="flex items-center justify-between">
|
|
<view class="flex-1">
|
|
<view class="flex items-center gap-2 mb-1">
|
|
<text class="text-13px text-[#757575]">一次性佣金</text>
|
|
<span class="px-2 py-0.5 bg-[#fff3e0] text-[#ff9800] text-10px rounded-full">
|
|
{{ (commissionStats.one_time_percent / 10).toFixed(1) }}%
|
|
</span>
|
|
</view>
|
|
<text class="text-11px text-[#bdbdbd]">{{ commissionStats.one_time_count }} 笔</text>
|
|
</view>
|
|
<text class="text-18px font-600 text-[#212121]">
|
|
¥{{ formatAmount(commissionStats.one_time_amount) }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 每日佣金统计 -->
|
|
<view class="bg-white rounded-12px p-4 mb-4 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
<view class="flex items-center justify-between mb-3">
|
|
<text class="text-16px font-600 text-[#212121]">每日收入</text>
|
|
<text class="text-12px text-[#9e9e9e]">近7日</text>
|
|
</view>
|
|
|
|
<view class="space-y-2">
|
|
<view
|
|
v-for="item in dailyCommission"
|
|
:key="item.date"
|
|
class="
|
|
flex items-center justify-between
|
|
p-3 rounded-8px
|
|
bg-[#fafafa]
|
|
"
|
|
>
|
|
<view>
|
|
<text class="text-13px text-[#212121] block mb-1">{{ item.date }}</text>
|
|
<text class="text-11px text-[#9e9e9e]">{{ item.total_count }} 笔</text>
|
|
</view>
|
|
<text class="text-16px font-600 text-[#4caf50]">
|
|
+¥{{ formatAmount(item.total_amount) }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 佣金明细 -->
|
|
<view class="bg-white rounded-12px p-4 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
<view class="flex items-center justify-between mb-3">
|
|
<text class="text-16px font-600 text-[#212121]">佣金明细</text>
|
|
<button class="text-13px text-[var(--brand-primary)]">查看全部 →</button>
|
|
</view>
|
|
|
|
<view class="space-y-3">
|
|
<view
|
|
v-for="detail in commissionDetails.slice(0, 5)"
|
|
:key="detail.id"
|
|
class="
|
|
flex items-center justify-between
|
|
pb-3
|
|
border-b border-[#eeeeee]
|
|
last:border-0
|
|
"
|
|
>
|
|
<view class="flex-1">
|
|
<view class="flex items-center gap-2 mb-1">
|
|
<text class="text-14px font-500 text-[#212121]">
|
|
订单 #{{ detail.order_id }}
|
|
</text>
|
|
<span
|
|
:style="{
|
|
color: detail.status === 1 ? '#4caf50' : '#f44336',
|
|
backgroundColor: detail.status === 1 ? '#e8f5e9' : '#ffebee'
|
|
}"
|
|
class="px-2 py-0.5 text-10px rounded-full"
|
|
>
|
|
{{ detail.status_name }}
|
|
</span>
|
|
</view>
|
|
<view class="flex items-center gap-3 text-11px text-[#9e9e9e]">
|
|
<text>{{ commissionSourceMap[detail.commission_source] }}</text>
|
|
<text>{{ formatDateTime(detail.created_at) }}</text>
|
|
</view>
|
|
</view>
|
|
<text
|
|
:class="{
|
|
'text-[#4caf50]': detail.status === 1,
|
|
'text-[#9e9e9e]': detail.status === 2
|
|
}"
|
|
class="text-18px font-600"
|
|
>
|
|
+¥{{ formatAmount(detail.amount) }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 我的套餐使用情况 -->
|
|
<view v-if="currentTab === 'my-packages'">
|
|
<!-- 总计卡片 (橙色渐变) -->
|
|
<view class="
|
|
bg-gradient-to-br from-[var(--brand-primary)] to-[var(--brand-primary-light)]
|
|
rounded-16px p-6 mb-4
|
|
shadow-brand-colored
|
|
">
|
|
<text class="text-13px text-white/70 block mb-2">套餐总计</text>
|
|
|
|
<view class="flex items-end gap-2 mb-4">
|
|
<text class="text-40px font-700 text-white">
|
|
{{ formatDataSize(myPackageUsage.total.used_mb) }}
|
|
</text>
|
|
<text class="text-16px text-white/80 mb-1">
|
|
/ {{ formatDataSize(myPackageUsage.total.total_mb) }}
|
|
</text>
|
|
</view>
|
|
|
|
<!-- 进度条 -->
|
|
<view class="w-full h-8px bg-white/20 rounded-full overflow-hidden mb-4">
|
|
<view
|
|
:style="{
|
|
width: calcUsagePercent(myPackageUsage.total.used_mb, myPackageUsage.total.total_mb) + '%'
|
|
}"
|
|
class="h-full bg-white rounded-full transition-all duration-300"
|
|
/>
|
|
</view>
|
|
|
|
<!-- 统计 -->
|
|
<view class="flex items-center justify-between text-white/80 text-12px">
|
|
<text>使用率: {{ calcUsagePercent(myPackageUsage.total.used_mb, myPackageUsage.total.total_mb) }}%</text>
|
|
<text>剩余: {{ formatDataSize(myPackageUsage.total.total_mb - myPackageUsage.total.used_mb) }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 主套餐 -->
|
|
<view v-if="myPackageUsage.main_package" class="mb-4">
|
|
<text class="text-15px font-600 text-[#212121] block mb-3">主套餐</text>
|
|
|
|
<view class="
|
|
bg-white rounded-12px p-4
|
|
shadow-[0_2px_8px_rgba(0,0,0,0.04)]
|
|
border-2 border-[var(--brand-primary)]/20
|
|
">
|
|
<view class="flex items-start justify-between mb-3">
|
|
<text class="text-16px font-600 text-[#212121]">
|
|
{{ myPackageUsage.main_package.package_name }}
|
|
</text>
|
|
<span
|
|
:style="{
|
|
color: packageStatus[myPackageUsage.main_package.status].color,
|
|
backgroundColor: packageStatus[myPackageUsage.main_package.status].bgColor
|
|
}"
|
|
class="px-3 py-1 text-12px font-500 rounded-full"
|
|
>
|
|
{{ myPackageUsage.main_package.status_text }}
|
|
</span>
|
|
</view>
|
|
|
|
<!-- 流量进度 -->
|
|
<view class="mb-3">
|
|
<view class="flex items-center justify-between mb-1">
|
|
<text class="text-12px text-[#757575]">流量使用</text>
|
|
<text class="text-13px font-600 text-[#212121]">
|
|
{{ formatDataSize(myPackageUsage.main_package.used_mb) }} /
|
|
{{ formatDataSize(myPackageUsage.main_package.total_mb) }}
|
|
</text>
|
|
</view>
|
|
<view class="w-full h-8px bg-[#f5f5f5] rounded-full overflow-hidden">
|
|
<view
|
|
:style="{
|
|
width: calcUsagePercent(myPackageUsage.main_package.used_mb, myPackageUsage.main_package.total_mb) + '%',
|
|
backgroundColor: myPackageUsage.main_package.status === 2 ? '#f44336' : '#4caf50'
|
|
}"
|
|
class="h-full transition-all duration-300 rounded-full"
|
|
/>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 时间信息 -->
|
|
<view class="flex items-center justify-between text-11px text-[#bdbdbd]">
|
|
<view class="flex items-center gap-1">
|
|
<i class="i-mdi-clock-outline w-12px h-12px" />
|
|
<text>激活: {{ formatDate(myPackageUsage.main_package.activated_at) }}</text>
|
|
</view>
|
|
<view class="flex items-center gap-1">
|
|
<i class="i-mdi-calendar-clock w-12px h-12px" />
|
|
<text>到期: {{ formatDate(myPackageUsage.main_package.expires_at) }}</text>
|
|
<template v-if="myPackageUsage.main_package.status === 1 && calcRemainingDays(myPackageUsage.main_package.expires_at)">
|
|
<text
|
|
:class="{
|
|
'text-[#f44336]': calcRemainingDays(myPackageUsage.main_package.expires_at)! <= 7,
|
|
'text-[#ff9800]': calcRemainingDays(myPackageUsage.main_package.expires_at)! > 7 && calcRemainingDays(myPackageUsage.main_package.expires_at)! <= 30
|
|
}"
|
|
>
|
|
(剩{{ calcRemainingDays(myPackageUsage.main_package.expires_at) }}天)
|
|
</text>
|
|
</template>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 加油包列表 -->
|
|
<view v-if="myPackageUsage.addon_packages && myPackageUsage.addon_packages.length > 0">
|
|
<view class="flex items-center justify-between mb-3">
|
|
<text class="text-15px font-600 text-[#212121]">加油包</text>
|
|
<text class="text-12px text-[#9e9e9e]">
|
|
{{ myPackageUsage.addon_packages.length }} 个
|
|
</text>
|
|
</view>
|
|
|
|
<view class="space-y-2">
|
|
<view
|
|
v-for="addon in myPackageUsage.addon_packages"
|
|
:key="addon.package_usage_id"
|
|
class="
|
|
bg-white rounded-12px p-4
|
|
shadow-[0_2px_8px_rgba(0,0,0,0.04)]
|
|
border border-[#eeeeee]
|
|
"
|
|
>
|
|
<view class="flex items-start justify-between mb-2">
|
|
<view class="flex-1">
|
|
<text class="text-15px font-600 text-[#212121] block mb-1">
|
|
{{ addon.package_name }}
|
|
</text>
|
|
<text class="text-11px text-[#9e9e9e]">
|
|
优先级: {{ addon.priority }}
|
|
</text>
|
|
</view>
|
|
<span
|
|
:style="{
|
|
color: packageStatus[addon.status].color,
|
|
backgroundColor: packageStatus[addon.status].bgColor
|
|
}"
|
|
class="px-2 py-1 text-11px font-500 rounded-full"
|
|
>
|
|
{{ addon.status_text }}
|
|
</span>
|
|
</view>
|
|
|
|
<!-- 流量进度 -->
|
|
<view class="mb-2">
|
|
<view class="flex items-center justify-between mb-1">
|
|
<text class="text-11px text-[#757575]">流量</text>
|
|
<text class="text-12px font-600 text-[#212121]">
|
|
{{ formatDataSize(addon.used_mb) }} / {{ formatDataSize(addon.total_mb) }}
|
|
</text>
|
|
</view>
|
|
<view class="w-full h-6px bg-[#f5f5f5] rounded-full overflow-hidden">
|
|
<view
|
|
:style="{
|
|
width: calcUsagePercent(addon.used_mb, addon.total_mb) + '%',
|
|
backgroundColor: addon.status === 2 ? '#f44336' : addon.status === 1 ? '#4caf50' : '#9e9e9e'
|
|
}"
|
|
class="h-full transition-all duration-300 rounded-full"
|
|
/>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 时间 -->
|
|
<view v-if="addon.status !== 0" class="flex items-center justify-between text-11px text-[#bdbdbd]">
|
|
<text>激活: {{ formatDate(addon.activated_at) }}</text>
|
|
<text>到期: {{ formatDate(addon.expires_at) }}</text>
|
|
</view>
|
|
<text v-else class="text-11px text-[#ff9800]">等待激活</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 无加油包 -->
|
|
<view v-else class="bg-white rounded-12px p-6 text-center">
|
|
<i class="i-mdi-gift-outline w-48px h-48px text-[#e0e0e0] mb-2" />
|
|
<text class="text-13px text-[#9e9e9e] block mb-3">暂无加油包</text>
|
|
<button class="
|
|
bg-[var(--brand-primary)] text-white
|
|
px-5 py-2 rounded-8px
|
|
text-13px font-500
|
|
">
|
|
购买加油包
|
|
</button>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 套餐列表 -->
|
|
<view v-if="currentTab === 'packages' && currentView === 'list'">
|
|
<!-- 统计卡片 -->
|
|
<view class="grid grid-cols-2 gap-3 mb-4">
|
|
<view
|
|
v-for="stat in stats"
|
|
:key="stat.label"
|
|
class="bg-white rounded-12px p-4 shadow-[0_2px_8px_rgba(0,0,0,0.04)]"
|
|
>
|
|
<view class="flex items-center justify-between mb-3">
|
|
<text class="text-13px text-[#9e9e9e]">{{ stat.label }}</text>
|
|
<view
|
|
:style="{ backgroundColor: stat.color + '20' }"
|
|
class="w-32px h-32px rounded-full center"
|
|
>
|
|
<i :class="stat.icon" :style="{ color: stat.color }" class="w-18px h-18px" />
|
|
</view>
|
|
</view>
|
|
|
|
<text class="text-24px font-700 text-[#212121] block">
|
|
{{ stat.value }}
|
|
</text>
|
|
<text class="text-11px text-[#bdbdbd]">{{ stat.unit }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 筛选标签 -->
|
|
<view class="flex gap-2 mb-3 overflow-x-auto pb-2">
|
|
<button
|
|
:class="{
|
|
'bg-[var(--brand-primary-light)] text-[var(--brand-primary)]': filterStatus === null,
|
|
'text-[#9e9e9e]': filterStatus !== null
|
|
}"
|
|
class="px-4 py-1.5 rounded-full text-13px font-500 whitespace-nowrap"
|
|
@click="filterStatus = null"
|
|
>
|
|
全部 ({{ packageList.length }})
|
|
</button>
|
|
<button
|
|
:class="{
|
|
'bg-[#e8f5e9] text-[#4caf50]': filterStatus === 1,
|
|
'text-[#9e9e9e]': filterStatus !== 1
|
|
}"
|
|
class="px-4 py-1.5 rounded-full text-13px font-500 whitespace-nowrap"
|
|
@click="filterStatus = 1"
|
|
>
|
|
生效中
|
|
</button>
|
|
<button
|
|
:class="{
|
|
'bg-[#fff3e0] text-[#ff9800]': filterStatus === 0,
|
|
'text-[#9e9e9e]': filterStatus !== 0
|
|
}"
|
|
class="px-4 py-1.5 rounded-full text-13px font-500 whitespace-nowrap"
|
|
@click="filterStatus = 0"
|
|
>
|
|
待生效
|
|
</button>
|
|
<button
|
|
:class="{
|
|
'bg-[#ffebee] text-[#f44336]': filterStatus === 3,
|
|
'text-[#9e9e9e]': filterStatus !== 3
|
|
}"
|
|
class="px-4 py-1.5 rounded-full text-13px font-500 whitespace-nowrap"
|
|
@click="filterStatus = 3"
|
|
>
|
|
已过期
|
|
</button>
|
|
</view>
|
|
|
|
<!-- 套餐卡片列表 -->
|
|
<view class="space-y-3">
|
|
<view
|
|
v-for="pkg in filteredPackages"
|
|
:key="pkg.package_usage_id"
|
|
class="
|
|
bg-white rounded-12px p-4
|
|
shadow-[0_2px_8px_rgba(0,0,0,0.04)]
|
|
border border-[#eeeeee]
|
|
cursor-pointer
|
|
"
|
|
@click="viewPackageDetail(pkg)"
|
|
>
|
|
<!-- 套餐名称 + 状态 -->
|
|
<view class="flex items-start justify-between mb-3">
|
|
<view class="flex-1">
|
|
<text class="text-16px font-600 text-[#212121] block mb-1">
|
|
{{ pkg.package_name }}
|
|
</text>
|
|
<view class="flex items-center gap-2">
|
|
<span
|
|
:style="{ color: pkg.package_type === 'addon' ? '#ff9800' : '#2196f3' }"
|
|
class="text-11px"
|
|
>
|
|
{{ packageTypeMap[pkg.package_type] }}
|
|
</span>
|
|
<span class="text-11px text-[#bdbdbd]">
|
|
优先级: {{ pkg.priority }}
|
|
</span>
|
|
</view>
|
|
</view>
|
|
|
|
<span
|
|
:style="{
|
|
color: packageStatus[pkg.status].color,
|
|
backgroundColor: packageStatus[pkg.status].bgColor
|
|
}"
|
|
class="px-3 py-1 text-12px font-500 rounded-full"
|
|
>
|
|
{{ pkg.status_name }}
|
|
</span>
|
|
</view>
|
|
|
|
<!-- 流量使用情况 -->
|
|
<view class="mb-3">
|
|
<!-- 虚流量 -->
|
|
<view class="mb-2">
|
|
<view class="flex items-center justify-between mb-1">
|
|
<text class="text-12px text-[#757575]">虚流量使用</text>
|
|
<text class="text-13px font-600 text-[#212121]">
|
|
{{ formatDataSize(pkg.virtual_used_mb) }} / {{ formatDataSize(pkg.virtual_limit_mb) }}
|
|
</text>
|
|
</view>
|
|
<view class="w-full h-8px bg-[#f5f5f5] rounded-full overflow-hidden">
|
|
<view
|
|
:style="{
|
|
width: calcUsagePercent(pkg.virtual_used_mb, pkg.virtual_limit_mb) + '%',
|
|
backgroundColor: pkg.status === 2 ? '#f44336' : pkg.status === 1 ? '#4caf50' : '#9e9e9e'
|
|
}"
|
|
class="h-full transition-all duration-300 rounded-full"
|
|
/>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 真流量 -->
|
|
<view class="flex items-center justify-between">
|
|
<text class="text-11px text-[#9e9e9e]">真流量</text>
|
|
<text class="text-11px text-[#757575]">
|
|
{{ formatDataSize(pkg.data_usage_mb) }} / {{ formatDataSize(pkg.data_limit_mb) }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 时间信息 -->
|
|
<view class="flex items-center justify-between text-11px text-[#bdbdbd]">
|
|
<view class="flex items-center gap-1">
|
|
<i class="i-mdi-clock-outline w-12px h-12px" />
|
|
<text>激活: {{ formatDate(pkg.activated_at) }}</text>
|
|
</view>
|
|
<view class="flex items-center gap-1">
|
|
<i class="i-mdi-calendar-clock w-12px h-12px" />
|
|
<text>到期: {{ formatDate(pkg.expires_at) }}</text>
|
|
<template v-if="pkg.status === 1 && calcRemainingDays(pkg.expires_at)">
|
|
<text
|
|
:class="{
|
|
'text-[#f44336]': calcRemainingDays(pkg.expires_at)! <= 7,
|
|
'text-[#ff9800]': calcRemainingDays(pkg.expires_at)! > 7 && calcRemainingDays(pkg.expires_at)! <= 30
|
|
}"
|
|
>
|
|
(剩{{ calcRemainingDays(pkg.expires_at) }}天)
|
|
</text>
|
|
</template>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 空状态 -->
|
|
<view v-if="filteredPackages.length === 0" class="center flex-col py-16">
|
|
<i class="i-mdi-package-variant-closed w-64px h-64px text-[#e0e0e0] mb-3" />
|
|
<text class="text-14px text-[#9e9e9e]">暂无套餐数据</text>
|
|
</view>
|
|
</view>
|
|
|
|
|
|
<!-- 资产列表 -->
|
|
<view v-if="currentTab === 'assets' && currentView === 'list'">
|
|
<!-- 统计卡片 -->
|
|
<view class="grid grid-cols-2 gap-3 mb-4">
|
|
<view
|
|
v-for="stat in assetStats"
|
|
:key="stat.label"
|
|
class="bg-white rounded-12px p-4 shadow-[0_2px_8px_rgba(0,0,0,0.04)]"
|
|
>
|
|
<view class="flex items-center justify-between mb-3">
|
|
<text class="text-13px text-[#9e9e9e]">{{ stat.label }}</text>
|
|
<view
|
|
:style="{ backgroundColor: stat.color + '20' }"
|
|
class="w-32px h-32px rounded-full center"
|
|
>
|
|
<i :class="stat.icon" :style="{ color: stat.color }" class="w-18px h-18px" />
|
|
</view>
|
|
</view>
|
|
|
|
<text class="text-24px font-700 text-[#212121] block">
|
|
{{ stat.value }}
|
|
</text>
|
|
<text v-if="stat.unit" class="text-11px text-[#bdbdbd]">{{ stat.unit }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 资产卡片列表 -->
|
|
<view class="space-y-3">
|
|
<view
|
|
v-for="asset in assetList"
|
|
:key="asset.id"
|
|
class="
|
|
bg-white rounded-12px p-4
|
|
shadow-[0_2px_8px_rgba(0,0,0,0.04)]
|
|
border border-[#eeeeee]
|
|
cursor-pointer
|
|
"
|
|
>
|
|
<view class="flex items-start gap-3">
|
|
<!-- 缩略图 -->
|
|
<view class="w-56px h-56px bg-[#f5f5f5] rounded-8px center flex-shrink-0">
|
|
<i class="i-mdi-package-variant w-28px h-28px text-[#bdbdbd]" />
|
|
</view>
|
|
|
|
<!-- 信息 -->
|
|
<view class="flex-1">
|
|
<text class="text-15px font-600 text-[#212121] block mb-1">
|
|
{{ asset.name }}
|
|
</text>
|
|
<text class="text-12px text-[#9e9e9e] block mb-2">
|
|
编号: {{ asset.code }}
|
|
</text>
|
|
|
|
<view class="flex items-center justify-between">
|
|
<!-- 标签 -->
|
|
<view class="flex gap-1">
|
|
<span
|
|
v-for="tag in asset.tags"
|
|
:key="tag"
|
|
class="px-2 py-0.5 bg-[var(--brand-primary-light)] text-[var(--brand-primary)] text-11px rounded-full"
|
|
>
|
|
{{ tag }}
|
|
</span>
|
|
<span
|
|
v-if="asset.status === 'active'"
|
|
class="px-2 py-0.5 bg-[#e8f5e9] text-[#4caf50] text-11px rounded-full"
|
|
>
|
|
激活
|
|
</span>
|
|
<span
|
|
v-else
|
|
class="px-2 py-0.5 bg-[#f5f5f5] text-[#9e9e9e] text-11px rounded-full"
|
|
>
|
|
未激活
|
|
</span>
|
|
</view>
|
|
|
|
<!-- 箭头 -->
|
|
<i class="i-mdi-chevron-right w-20px h-20px text-[#bdbdbd]" />
|
|
</view>
|
|
|
|
<!-- 套餐信息 -->
|
|
<view class="flex items-center gap-4 mt-2 text-11px text-[#757575]">
|
|
<view class="flex items-center gap-1">
|
|
<i class="i-mdi-gift w-12px h-12px" />
|
|
<text>{{ asset.package_count }} 个套餐</text>
|
|
</view>
|
|
<view class="flex items-center gap-1">
|
|
<i class="i-mdi-database w-12px h-12px" />
|
|
<text>{{ formatDataSize(asset.total_data_mb) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 空状态 -->
|
|
<view v-if="assetList.length === 0" class="center flex-col py-16">
|
|
<i class="i-mdi-package-variant-closed w-64px h-64px text-[#e0e0e0] mb-3" />
|
|
<text class="text-14px text-[#9e9e9e]">暂无资产数据</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 标签管理 -->
|
|
<view v-if="currentTab === 'tags' && currentView === 'list'">
|
|
<!-- 标签统计 -->
|
|
<view class="bg-white rounded-12px p-4 mb-4 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
<view class="flex items-center justify-between">
|
|
<view class="flex-1 text-center border-r border-[#eeeeee]">
|
|
<text class="text-24px font-700 text-[#212121] block">{{ tagList.length }}</text>
|
|
<text class="text-11px text-[#9e9e9e]">标签总数</text>
|
|
</view>
|
|
<view class="flex-1 text-center">
|
|
<text class="text-24px font-700 text-[var(--brand-primary)] block">
|
|
{{ tagList.reduce((sum, t) => sum + t.asset_count, 0) }}
|
|
</text>
|
|
<text class="text-11px text-[#9e9e9e]">已分配资产</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 标签网格 -->
|
|
<view class="grid grid-cols-2 gap-3">
|
|
<view
|
|
v-for="tag in tagList"
|
|
:key="tag.id"
|
|
class="
|
|
bg-white rounded-12px p-4
|
|
shadow-[0_2px_8px_rgba(0,0,0,0.04)]
|
|
border-2 border-[#eeeeee]
|
|
cursor-pointer
|
|
"
|
|
>
|
|
<view class="flex items-center justify-between mb-3">
|
|
<view
|
|
:style="{ backgroundColor: tag.color }"
|
|
class="w-40px h-40px rounded-full center"
|
|
>
|
|
<i class="i-mdi-tag w-20px h-20px text-white" />
|
|
</view>
|
|
|
|
<button
|
|
class="w-32px h-32px center hover:bg-[#f5f5f5] rounded-full"
|
|
@click.stop
|
|
>
|
|
<i class="i-mdi-dots-vertical w-18px h-18px text-[#9e9e9e]" />
|
|
</button>
|
|
</view>
|
|
|
|
<text class="text-15px font-600 text-[#212121] block mb-1">
|
|
{{ tag.name }}
|
|
</text>
|
|
<text class="text-12px text-[#9e9e9e] block mb-3">
|
|
{{ tag.description }}
|
|
</text>
|
|
|
|
<view class="flex items-center justify-between">
|
|
<text class="text-11px text-[#bdbdbd]">
|
|
已分配 {{ tag.asset_count }} 个资产
|
|
</text>
|
|
<button
|
|
class="text-12px text-[var(--brand-primary)] font-500"
|
|
@click="enterAssignMode(tag)"
|
|
>
|
|
分配 →
|
|
</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 创建标签按钮 -->
|
|
<button class="
|
|
w-full
|
|
mt-4
|
|
bg-[var(--brand-primary)] text-white
|
|
py-3 rounded-12px
|
|
text-14px font-500
|
|
flex items-center justify-center gap-2
|
|
shadow-brand-xs
|
|
">
|
|
<i class="i-mdi-plus w-18px h-18px" />
|
|
创建新标签
|
|
</button>
|
|
</view>
|
|
|
|
<!-- 标签分配页面 -->
|
|
<view v-if="currentTab === 'tags' && currentView === 'assign' && selectedTag">
|
|
<!-- 返回按钮 -->
|
|
<button
|
|
class="flex items-center gap-2 mb-4 text-[#757575]"
|
|
@click="navigateTo('list')"
|
|
>
|
|
<i class="i-mdi-chevron-left w-20px h-20px" />
|
|
<text class="text-14px">返回</text>
|
|
</button>
|
|
|
|
<!-- 标签信息卡片 -->
|
|
<view class="bg-white rounded-12px p-4 mb-4 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
<view class="flex items-center gap-3">
|
|
<view
|
|
:style="{ backgroundColor: selectedTag.color }"
|
|
class="w-48px h-48px rounded-full center"
|
|
>
|
|
<i class="i-mdi-tag w-24px h-24px text-white" />
|
|
</view>
|
|
<view class="flex-1">
|
|
<text class="text-18px font-700 text-[#212121] block mb-1">
|
|
{{ selectedTag.name }}
|
|
</text>
|
|
<text class="text-12px text-[#9e9e9e]">
|
|
{{ selectedTag.description }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 资产列表 (可勾选) -->
|
|
<text class="text-15px font-600 text-[#212121] block mb-3">
|
|
选择要分配的资产 ({{ assetList.filter(a => a.tags.includes(selectedTag.name)).length }}/{{ assetList.length }})
|
|
</text>
|
|
|
|
<view class="space-y-2 mb-4">
|
|
<view
|
|
v-for="asset in assetList"
|
|
:key="asset.id"
|
|
class="
|
|
bg-white rounded-12px p-4
|
|
shadow-[0_2px_8px_rgba(0,0,0,0.04)]
|
|
border border-[#eeeeee]
|
|
flex items-center gap-3
|
|
cursor-pointer
|
|
"
|
|
@click="toggleAssetTag(asset, selectedTag.name)"
|
|
>
|
|
<!-- 勾选框 -->
|
|
<view
|
|
:class="{
|
|
'bg-[var(--brand-primary)] border-[var(--brand-primary)]': asset.tags.includes(selectedTag.name),
|
|
'bg-white border-[#e0e0e0]': !asset.tags.includes(selectedTag.name)
|
|
}"
|
|
class="
|
|
w-20px h-20px
|
|
rounded-4px
|
|
border-2
|
|
center
|
|
transition-all duration-200
|
|
"
|
|
>
|
|
<i
|
|
v-if="asset.tags.includes(selectedTag.name)"
|
|
class="i-mdi-check w-14px h-14px text-white"
|
|
/>
|
|
</view>
|
|
|
|
<!-- 资产信息 -->
|
|
<view class="flex-1">
|
|
<text class="text-14px font-500 text-[#212121] block mb-1">
|
|
{{ asset.name }}
|
|
</text>
|
|
<text class="text-11px text-[#9e9e9e]">
|
|
编号: {{ asset.code }}
|
|
</text>
|
|
</view>
|
|
|
|
<!-- 状态 -->
|
|
<span
|
|
v-if="asset.status === 'active'"
|
|
class="px-2 py-0.5 bg-[#e8f5e9] text-[#4caf50] text-11px rounded-full"
|
|
>
|
|
激活
|
|
</span>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 保存按钮 -->
|
|
<view class="fixed bottom-0 left-0 right-0 bg-white p-4 pb-safe border-t border-[#eeeeee]">
|
|
<button class="
|
|
w-full
|
|
bg-[var(--brand-primary)] text-white
|
|
py-3 rounded-8px
|
|
text-15px font-600
|
|
shadow-brand-xs
|
|
">
|
|
保存分配
|
|
</button>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 套餐详情 -->
|
|
<view v-if="currentTab === 'packages' && currentView === 'detail' && selectedPackage">
|
|
<!-- 返回按钮 -->
|
|
<button
|
|
class="flex items-center gap-2 mb-4 text-[#757575]"
|
|
@click="navigateTo('list')"
|
|
>
|
|
<i class="i-mdi-chevron-left w-20px h-20px" />
|
|
<text class="text-14px">返回</text>
|
|
</button>
|
|
|
|
<!-- 套餐基本信息卡片 -->
|
|
<view class="bg-white rounded-12px p-4 mb-3 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
<view class="flex items-start justify-between mb-3">
|
|
<text class="text-18px font-700 text-[#212121]">
|
|
{{ selectedPackage.package_name }}
|
|
</text>
|
|
<span
|
|
:style="{
|
|
color: packageStatus[selectedPackage.status].color,
|
|
backgroundColor: packageStatus[selectedPackage.status].bgColor
|
|
}"
|
|
class="px-3 py-1.5 text-13px font-600 rounded-full"
|
|
>
|
|
{{ selectedPackage.status_name }}
|
|
</span>
|
|
</view>
|
|
|
|
<view class="grid grid-cols-2 gap-3">
|
|
<view class="flex items-center gap-2">
|
|
<i class="i-mdi-tag-outline w-16px h-16px text-[#9e9e9e]" />
|
|
<text class="text-13px text-[#757575]">
|
|
套餐类型: <text class="text-[#212121] font-500">{{ packageTypeMap[selectedPackage.package_type] }}</text>
|
|
</text>
|
|
</view>
|
|
<view class="flex items-center gap-2">
|
|
<i class="i-mdi-sort-numeric-ascending w-16px h-16px text-[#9e9e9e]" />
|
|
<text class="text-13px text-[#757575]">
|
|
优先级: <text class="text-[#212121] font-500">{{ selectedPackage.priority }}</text>
|
|
</text>
|
|
</view>
|
|
<view v-if="selectedPackage.master_usage_id" class="flex items-center gap-2 col-span-2">
|
|
<i class="i-mdi-link-variant w-16px h-16px text-[#9e9e9e]" />
|
|
<text class="text-13px text-[#757575]">
|
|
主套餐ID: <text class="text-[#212121] font-500">{{ selectedPackage.master_usage_id }}</text>
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 流量使用详情 -->
|
|
<view class="bg-white rounded-12px p-4 mb-3 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
<text class="text-16px font-600 text-[#212121] block mb-3">流量使用</text>
|
|
|
|
<!-- 虚流量统计 -->
|
|
<view class="mb-4">
|
|
<view class="flex items-center justify-between mb-2">
|
|
<text class="text-14px text-[#757575]">虚流量使用</text>
|
|
<text class="text-18px font-700 text-[#212121]">
|
|
{{ calcUsagePercent(selectedPackage.virtual_used_mb, selectedPackage.virtual_limit_mb) }}%
|
|
</text>
|
|
</view>
|
|
|
|
<view class="w-full h-12px bg-[#f5f5f5] rounded-full overflow-hidden mb-2">
|
|
<view
|
|
:style="{
|
|
width: calcUsagePercent(selectedPackage.virtual_used_mb, selectedPackage.virtual_limit_mb) + '%',
|
|
backgroundColor: selectedPackage.status === 2 ? '#f44336' : '#4caf50'
|
|
}"
|
|
class="h-full transition-all duration-300 rounded-full"
|
|
/>
|
|
</view>
|
|
|
|
<view class="grid grid-cols-3 gap-2 text-12px">
|
|
<view class="text-center p-2 bg-[#f5f5f5] rounded-8px">
|
|
<text class="text-[#9e9e9e] block mb-1">已用</text>
|
|
<text class="text-[#212121] font-600">{{ formatDataSize(selectedPackage.virtual_used_mb) }}</text>
|
|
</view>
|
|
<view class="text-center p-2 bg-[#f5f5f5] rounded-8px">
|
|
<text class="text-[#9e9e9e] block mb-1">剩余</text>
|
|
<text class="text-[#4caf50] font-600">{{ formatDataSize(selectedPackage.virtual_remain_mb) }}</text>
|
|
</view>
|
|
<view class="text-center p-2 bg-[#f5f5f5] rounded-8px">
|
|
<text class="text-[#9e9e9e] block mb-1">总量</text>
|
|
<text class="text-[#212121] font-600">{{ formatDataSize(selectedPackage.virtual_limit_mb) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 真流量统计 -->
|
|
<view class="pb-3 border-b border-[#eeeeee] mb-3">
|
|
<view class="flex items-center justify-between mb-2">
|
|
<text class="text-14px text-[#757575]">真流量使用</text>
|
|
<text class="text-16px font-600 text-[#212121]">
|
|
{{ formatDataSize(selectedPackage.data_usage_mb) }} / {{ formatDataSize(selectedPackage.data_limit_mb) }}
|
|
</text>
|
|
</view>
|
|
|
|
<view class="w-full h-8px bg-[#f5f5f5] rounded-full overflow-hidden">
|
|
<view
|
|
:style="{
|
|
width: calcUsagePercent(selectedPackage.data_usage_mb, selectedPackage.data_limit_mb) + '%',
|
|
backgroundColor: selectedPackage.status === 2 ? '#f44336' : '#2196f3'
|
|
}"
|
|
class="h-full transition-all duration-300 rounded-full"
|
|
/>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 虚流量比例 -->
|
|
<view class="flex items-center justify-between p-3 bg-[#fff7f0] rounded-8px">
|
|
<view class="flex items-center gap-2">
|
|
<i class="i-mdi-calculator w-18px h-18px text-[var(--brand-primary)]" />
|
|
<text class="text-13px text-[#757575]">虚流量比例</text>
|
|
</view>
|
|
<text class="text-16px font-700 text-[var(--brand-primary)]">
|
|
1 : {{ (1 / selectedPackage.virtual_ratio).toFixed(1) }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 时间信息 -->
|
|
<view class="bg-white rounded-12px p-4 mb-3 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
<text class="text-16px font-600 text-[#212121] block mb-3">时间信息</text>
|
|
|
|
<view class="space-y-3">
|
|
<view class="flex items-center justify-between">
|
|
<view class="flex items-center gap-2">
|
|
<i class="i-mdi-calendar-plus w-18px h-18px text-[#9e9e9e]" />
|
|
<text class="text-14px text-[#757575]">创建时间</text>
|
|
</view>
|
|
<text class="text-14px text-[#212121] font-500">
|
|
{{ formatDateTime(selectedPackage.created_at) }}
|
|
</text>
|
|
</view>
|
|
|
|
<view v-if="selectedPackage.activated_at" class="flex items-center justify-between">
|
|
<view class="flex items-center gap-2">
|
|
<i class="i-mdi-play-circle w-18px h-18px text-[#4caf50]" />
|
|
<text class="text-14px text-[#757575]">激活时间</text>
|
|
</view>
|
|
<text class="text-14px text-[#212121] font-500">
|
|
{{ formatDateTime(selectedPackage.activated_at) }}
|
|
</text>
|
|
</view>
|
|
|
|
<view v-if="selectedPackage.expires_at" class="flex items-center justify-between">
|
|
<view class="flex items-center gap-2">
|
|
<i class="i-mdi-calendar-clock w-18px h-18px text-[#f44336]" />
|
|
<text class="text-14px text-[#757575]">到期时间</text>
|
|
</view>
|
|
<view class="text-right">
|
|
<text class="text-14px text-[#212121] font-500 block">
|
|
{{ formatDateTime(selectedPackage.expires_at) }}
|
|
</text>
|
|
<text
|
|
v-if="calcRemainingDays(selectedPackage.expires_at)"
|
|
:class="{
|
|
'text-[#f44336]': calcRemainingDays(selectedPackage.expires_at)! <= 7,
|
|
'text-[#ff9800]': calcRemainingDays(selectedPackage.expires_at)! > 7 && calcRemainingDays(selectedPackage.expires_at)! <= 30,
|
|
'text-[#4caf50]': calcRemainingDays(selectedPackage.expires_at)! > 30
|
|
}"
|
|
class="text-12px"
|
|
>
|
|
剩余 {{ calcRemainingDays(selectedPackage.expires_at) }} 天
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 操作按钮 -->
|
|
<view class="grid grid-cols-2 gap-3">
|
|
<button class="
|
|
bg-white text-[#757575]
|
|
border border-[#e0e0e0]
|
|
py-3 rounded-8px
|
|
text-14px font-500
|
|
">
|
|
续费套餐
|
|
</button>
|
|
<button class="
|
|
bg-[var(--brand-primary)] text-white
|
|
py-3 rounded-8px
|
|
text-14px font-500
|
|
shadow-brand-xs
|
|
">
|
|
购买加油包
|
|
</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.mobile-agent-system {
|
|
-webkit-overflow-scrolling: touch;
|
|
}
|
|
|
|
::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
</style>
|