feat: 用户列表显示订阅分组及剩余天数
- User模型新增Subscriptions关联 - 用户列表批量加载订阅信息避免N+1查询 - GroupBadge组件支持显示剩余天数(过期红色、<=3天红色、<=7天橙色) - 用户管理页面新增订阅分组列
This commit is contained in:
@@ -9,9 +9,9 @@
|
||||
<PlatformIcon v-if="platform" :platform="platform" size="sm" />
|
||||
<!-- Group name -->
|
||||
<span class="truncate">{{ name }}</span>
|
||||
<!-- Right side label: subscription shows "订阅", standard shows rate multiplier -->
|
||||
<!-- Right side label -->
|
||||
<span
|
||||
v-if="showRate"
|
||||
v-if="showLabel"
|
||||
:class="labelClass"
|
||||
>
|
||||
{{ labelText }}
|
||||
@@ -31,39 +31,73 @@ interface Props {
|
||||
subscriptionType?: SubscriptionType
|
||||
rateMultiplier?: number
|
||||
showRate?: boolean
|
||||
daysRemaining?: number | null // 剩余天数(订阅类型时使用)
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
subscriptionType: 'standard',
|
||||
showRate: true
|
||||
showRate: true,
|
||||
daysRemaining: null
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const isSubscription = computed(() => props.subscriptionType === 'subscription')
|
||||
|
||||
// Label text: subscription shows localized text, standard shows rate
|
||||
// 是否显示右侧标签
|
||||
const showLabel = computed(() => {
|
||||
if (!props.showRate) return false
|
||||
// 订阅类型:显示天数或"订阅"
|
||||
if (isSubscription.value) return true
|
||||
// 标准类型:显示倍率
|
||||
return props.rateMultiplier !== undefined
|
||||
})
|
||||
|
||||
// Label text
|
||||
const labelText = computed(() => {
|
||||
if (isSubscription.value) {
|
||||
// 如果有剩余天数,显示天数
|
||||
if (props.daysRemaining !== null && props.daysRemaining !== undefined) {
|
||||
if (props.daysRemaining <= 0) {
|
||||
return t('admin.users.expired')
|
||||
}
|
||||
return t('admin.users.daysRemaining', { days: props.daysRemaining })
|
||||
}
|
||||
// 否则显示"订阅"
|
||||
return t('groups.subscription')
|
||||
}
|
||||
return props.rateMultiplier !== undefined ? `${props.rateMultiplier}x` : ''
|
||||
})
|
||||
|
||||
// Label style based on type
|
||||
// Label style based on type and days remaining
|
||||
const labelClass = computed(() => {
|
||||
const base = 'px-1.5 py-0.5 rounded text-[10px] font-semibold'
|
||||
if (isSubscription.value) {
|
||||
// Subscription: more prominent style with border
|
||||
if (props.platform === 'anthropic') {
|
||||
return `${base} bg-orange-200/60 text-orange-800 dark:bg-orange-800/40 dark:text-orange-300`
|
||||
} else if (props.platform === 'openai') {
|
||||
return `${base} bg-emerald-200/60 text-emerald-800 dark:bg-emerald-800/40 dark:text-emerald-300`
|
||||
}
|
||||
return `${base} bg-violet-200/60 text-violet-800 dark:bg-violet-800/40 dark:text-violet-300`
|
||||
|
||||
if (!isSubscription.value) {
|
||||
// Standard: subtle background
|
||||
return `${base} bg-black/10 dark:bg-white/10`
|
||||
}
|
||||
// Standard: subtle background
|
||||
return `${base} bg-black/10 dark:bg-white/10`
|
||||
|
||||
// 订阅类型:根据剩余天数显示不同颜色
|
||||
if (props.daysRemaining !== null && props.daysRemaining !== undefined) {
|
||||
if (props.daysRemaining <= 0 || props.daysRemaining <= 3) {
|
||||
// 已过期或紧急(<=3天):红色
|
||||
return `${base} bg-red-200/80 text-red-800 dark:bg-red-800/50 dark:text-red-300`
|
||||
}
|
||||
if (props.daysRemaining <= 7) {
|
||||
// 警告(<=7天):橙色
|
||||
return `${base} bg-amber-200/80 text-amber-800 dark:bg-amber-800/50 dark:text-amber-300`
|
||||
}
|
||||
}
|
||||
|
||||
// 正常状态或无天数:根据平台显示主题色
|
||||
if (props.platform === 'anthropic') {
|
||||
return `${base} bg-orange-200/60 text-orange-800 dark:bg-orange-800/40 dark:text-orange-300`
|
||||
}
|
||||
if (props.platform === 'openai') {
|
||||
return `${base} bg-emerald-200/60 text-emerald-800 dark:bg-emerald-800/40 dark:text-emerald-300`
|
||||
}
|
||||
return `${base} bg-violet-200/60 text-violet-800 dark:bg-violet-800/40 dark:text-violet-300`
|
||||
})
|
||||
|
||||
// Badge color based on platform and subscription type
|
||||
|
||||
Reference in New Issue
Block a user