mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-11-02 00:30:32 +00:00
fix: 高峰倍率全链路透传、计费术语修正与边界处理
- 高峰倍率信息透传至可用渠道、支付计划、结算信息等 API,前端 GroupBadge / GroupOptionItem / SubscriptionPlanCard / PaymentView / SubscriptionsView 统一展示高峰时段与倍率标签 - 修正计费术语:"文本倍率" → "token 倍率",明确高峰倍率同时作用于 token 计费的图片 token,图片按次计费不受高峰影响 - 允许高峰倍率 multiplier=0,支持高峰时段免费策略 - 切换分组类型为 standard 时自动清除高峰倍率配置 - 长上下文计费与标准计费路径改用内部实现,移除冗余中间调用 - 前端高峰倍率相关控件文案改为 i18n - 新增多组高峰倍率相关单元测试
This commit is contained in:
@@ -20,6 +20,9 @@
|
||||
{{ labelText }}
|
||||
</template>
|
||||
</span>
|
||||
<span v-if="hasPeakRate" :class="peakRateClass" :title="peakRateTitle">
|
||||
{{ peakRateText }}
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -35,6 +38,10 @@ interface Props {
|
||||
subscriptionType?: SubscriptionType
|
||||
rateMultiplier?: number
|
||||
userRateMultiplier?: number | null // 用户专属倍率
|
||||
peakRateEnabled?: boolean
|
||||
peakStart?: string
|
||||
peakEnd?: string
|
||||
peakRateMultiplier?: number
|
||||
showRate?: boolean
|
||||
daysRemaining?: number | null // 剩余天数(订阅类型时使用)
|
||||
/**
|
||||
@@ -50,6 +57,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
showRate: true,
|
||||
daysRemaining: null,
|
||||
userRateMultiplier: null,
|
||||
peakRateEnabled: false,
|
||||
alwaysShowRate: false
|
||||
})
|
||||
|
||||
@@ -67,6 +75,18 @@ const hasCustomRate = computed(() => {
|
||||
)
|
||||
})
|
||||
|
||||
const hasPeakRate = computed(() => {
|
||||
return Boolean(props.showRate && props.peakRateEnabled && props.peakStart && props.peakEnd)
|
||||
})
|
||||
|
||||
const peakRateText = computed(() => {
|
||||
return `${props.peakStart}-${props.peakEnd} ×${props.peakRateMultiplier ?? 1}`
|
||||
})
|
||||
|
||||
const peakRateTitle = computed(() => {
|
||||
return `高峰倍率:${peakRateText.value}`
|
||||
})
|
||||
|
||||
// 是否显示右侧标签
|
||||
const showLabel = computed(() => {
|
||||
if (!props.showRate) return false
|
||||
@@ -127,6 +147,10 @@ const labelClass = computed(() => {
|
||||
return `${base} bg-violet-200/60 text-violet-800 dark:bg-violet-800/40 dark:text-violet-300`
|
||||
})
|
||||
|
||||
const peakRateClass = computed(() => {
|
||||
return 'px-1.5 py-0.5 rounded text-[10px] font-semibold bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300'
|
||||
})
|
||||
|
||||
// Badge color based on platform and subscription type
|
||||
const badgeClass = computed(() => {
|
||||
if (props.platform === 'anthropic') {
|
||||
|
||||
@@ -24,16 +24,25 @@
|
||||
|
||||
<!-- Right: rate pill + checkmark (vertically centered to first row) -->
|
||||
<div class="flex shrink-0 items-center gap-2 pt-0.5">
|
||||
<!-- Rate pill (platform color) -->
|
||||
<span v-if="rateMultiplier !== undefined" :class="['inline-flex items-center whitespace-nowrap rounded-full px-3 py-1 text-xs font-semibold', ratePillClass]">
|
||||
<template v-if="hasCustomRate">
|
||||
<span class="mr-1 line-through opacity-50">{{ rateMultiplier }}x</span>
|
||||
<span class="font-bold">{{ userRateMultiplier }}x</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ rateMultiplier }}x {{ t('admin.groups.rateLabel') }}
|
||||
</template>
|
||||
</span>
|
||||
<div class="flex shrink-0 flex-col items-end gap-1">
|
||||
<!-- Rate pill (platform color) -->
|
||||
<span v-if="rateMultiplier !== undefined" :class="['inline-flex items-center whitespace-nowrap rounded-full px-3 py-1 text-xs font-semibold', ratePillClass]">
|
||||
<template v-if="hasCustomRate">
|
||||
<span class="mr-1 line-through opacity-50">{{ rateMultiplier }}x</span>
|
||||
<span class="font-bold">{{ userRateMultiplier }}x</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ rateMultiplier }}x {{ t('admin.groups.rateLabel') }}
|
||||
</template>
|
||||
</span>
|
||||
<span
|
||||
v-if="hasPeakRate"
|
||||
class="inline-flex items-center whitespace-nowrap rounded-full bg-amber-50 px-3 py-1 text-xs font-semibold text-amber-700 dark:bg-amber-900/20 dark:text-amber-300"
|
||||
:title="peakRateTitle"
|
||||
>
|
||||
{{ peakRateText }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- Checkmark -->
|
||||
<svg
|
||||
v-if="showCheckmark && selected"
|
||||
@@ -63,6 +72,10 @@ interface Props {
|
||||
subscriptionType?: SubscriptionType
|
||||
rateMultiplier?: number
|
||||
userRateMultiplier?: number | null
|
||||
peakRateEnabled?: boolean
|
||||
peakStart?: string
|
||||
peakEnd?: string
|
||||
peakRateMultiplier?: number
|
||||
description?: string | null
|
||||
selected?: boolean
|
||||
showCheckmark?: boolean
|
||||
@@ -72,7 +85,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
subscriptionType: 'standard',
|
||||
selected: false,
|
||||
showCheckmark: true,
|
||||
userRateMultiplier: null
|
||||
userRateMultiplier: null,
|
||||
peakRateEnabled: false
|
||||
})
|
||||
|
||||
// Whether user has a custom rate different from default
|
||||
@@ -85,6 +99,18 @@ const hasCustomRate = computed(() => {
|
||||
)
|
||||
})
|
||||
|
||||
const hasPeakRate = computed(() => {
|
||||
return Boolean(props.peakRateEnabled && props.peakStart && props.peakEnd)
|
||||
})
|
||||
|
||||
const peakRateText = computed(() => {
|
||||
return `${props.peakStart}-${props.peakEnd} ×${props.peakRateMultiplier ?? 1}`
|
||||
})
|
||||
|
||||
const peakRateTitle = computed(() => {
|
||||
return `高峰倍率:${peakRateText.value}`
|
||||
})
|
||||
|
||||
// Rate pill color matches platform badge color
|
||||
const ratePillClass = computed(() => {
|
||||
switch (props.platform) {
|
||||
|
||||
Reference in New Issue
Block a user