模型广场:分时时段行明确不含高峰倍率口径并披露叠加
- 实扣倍率为 基础 × 高峰 × 分时;时段行价格与整表一致,按不含高峰的口径展示 - 分组启用高峰时,时段行 tooltip 披露与高峰窗口重叠的部分实付再乘高峰倍率 - PlazaGroupSection 把高峰窗口描述与倍率传入价格表
This commit is contained in:
@@ -61,6 +61,8 @@
|
||||
:user-rate-multiplier="group.user_rate_multiplier ?? null"
|
||||
:image-rate-independent="group.image_rate_independent"
|
||||
:image-rate-multiplier="group.image_rate_multiplier"
|
||||
:peak-window="peakWindow"
|
||||
:peak-rate-multiplier="group.peak_rate_multiplier"
|
||||
/>
|
||||
<p v-else class="px-5 py-4 text-center text-sm text-gray-400 dark:text-dark-500">
|
||||
{{ t('modelPlaza.detail.noModels') }}
|
||||
@@ -88,14 +90,19 @@ const props = defineProps<{
|
||||
const { t } = useI18n()
|
||||
const appStore = useAppStore()
|
||||
|
||||
const peakNote = computed(() => {
|
||||
/** 高峰窗口描述(含倍率与服务器时区标注);分组未启用高峰为空串。 */
|
||||
const peakWindow = computed(() => {
|
||||
if (!hasPeakRate(props.group)) return ''
|
||||
const window = formatPeakRateWindow(
|
||||
return formatPeakRateWindow(
|
||||
props.group,
|
||||
serverTimezoneLabel(appStore.cachedPublicSettings?.server_utc_offset)
|
||||
)
|
||||
})
|
||||
|
||||
const peakNote = computed(() => {
|
||||
if (!peakWindow.value) return ''
|
||||
return t('modelPlaza.detail.peakNote', {
|
||||
window,
|
||||
window: peakWindow.value,
|
||||
multiplier: props.group.peak_rate_multiplier
|
||||
})
|
||||
})
|
||||
|
||||
@@ -308,6 +308,13 @@ const props = defineProps<{
|
||||
/** 生图独立倍率:true 时图片计费模型的实付倍率取 imageRateMultiplier,不取分组/专属倍率。 */
|
||||
imageRateIndependent?: boolean
|
||||
imageRateMultiplier?: number | null
|
||||
/**
|
||||
* 高峰窗口描述(含倍率与服务器时区标注),空串/缺省 = 分组未启用高峰。
|
||||
* 表格所有价格均为不含高峰因子的口径,该窗口仅用于分时时段行的 tooltip 披露:
|
||||
* 与高峰重叠的部分实付还会再乘高峰倍率。
|
||||
*/
|
||||
peakWindow?: string
|
||||
peakRateMultiplier?: number | null
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -428,12 +435,22 @@ function timePeriods(m: PlazaModel): PlazaTimePricingPeriod[] {
|
||||
return m.time_pricing?.periods ?? []
|
||||
}
|
||||
|
||||
/** 时段行 tooltip:仅工作日生效的配置换用带周末回落说明的文案。 */
|
||||
/**
|
||||
* 时段行 tooltip:仅工作日生效的配置换用带周末回落说明的文案;
|
||||
* 分组启用高峰倍率时追加披露——本行价格不含高峰因子,与高峰窗口重叠的部分实付再乘高峰倍率。
|
||||
*/
|
||||
function timePricingRowHint(m: PlazaModel): string {
|
||||
const key = m.time_pricing?.weekdays_only
|
||||
? 'modelPlaza.table.timePricingRowHintWeekdays'
|
||||
: 'modelPlaza.table.timePricingRowHint'
|
||||
return t(key, { timezone: m.time_pricing?.timezone })
|
||||
let hint = t(key, { timezone: m.time_pricing?.timezone })
|
||||
if (props.peakWindow) {
|
||||
hint += t('modelPlaza.table.timePricingRowHintPeak', {
|
||||
window: props.peakWindow,
|
||||
multiplier: props.peakRateMultiplier ?? 1
|
||||
})
|
||||
}
|
||||
return hint
|
||||
}
|
||||
|
||||
/** “00:30–08:30”;整分钟的 HH:mm:ss 省略秒。 */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import PlazaGroupSection from '../PlazaGroupSection.vue'
|
||||
import PlazaModelPricingTable from '../PlazaModelPricingTable.vue'
|
||||
import type { ModelPlazaGroup, PlazaModel } from '@/api/modelPlaza'
|
||||
|
||||
vi.mock('vue-i18n', async () => {
|
||||
@@ -113,3 +114,26 @@ describe('PlazaGroupSection 长上下文说明', () => {
|
||||
expect(wrapper.text()).not.toContain(NOTE)
|
||||
})
|
||||
})
|
||||
|
||||
describe('PlazaGroupSection 高峰配置传递', () => {
|
||||
it('分组启用高峰时把窗口描述与倍率传给价格表', () => {
|
||||
const wrapper = mountSection(
|
||||
group({
|
||||
subscription_type: 'subscription',
|
||||
peak_rate_enabled: true,
|
||||
peak_start: '14:00',
|
||||
peak_end: '18:00',
|
||||
peak_rate_multiplier: 1.5
|
||||
})
|
||||
)
|
||||
const table = wrapper.findComponent(PlazaModelPricingTable)
|
||||
// appStore mock 无 server_utc_offset,窗口描述不带时区标注
|
||||
expect(table.props('peakWindow')).toBe('14:00-18:00 ×1.5')
|
||||
expect(table.props('peakRateMultiplier')).toBe(1.5)
|
||||
})
|
||||
|
||||
it('分组未启用高峰时窗口描述为空串', () => {
|
||||
const wrapper = mountSection(group())
|
||||
expect(wrapper.findComponent(PlazaModelPricingTable).props('peakWindow')).toBe('')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -43,7 +43,12 @@ function mountTable(
|
||||
models: PlazaModel[],
|
||||
rateMultiplier: number,
|
||||
userRateMultiplier?: number | null,
|
||||
extraProps?: { imageRateIndependent?: boolean; imageRateMultiplier?: number | null }
|
||||
extraProps?: {
|
||||
imageRateIndependent?: boolean
|
||||
imageRateMultiplier?: number | null
|
||||
peakWindow?: string
|
||||
peakRateMultiplier?: number | null
|
||||
}
|
||||
) {
|
||||
return mount(PlazaModelPricingTable, {
|
||||
props: { models, rateMultiplier, userRateMultiplier: userRateMultiplier ?? null, ...extraProps }
|
||||
@@ -602,6 +607,25 @@ describe('PlazaModelPricingTable 分时计价', () => {
|
||||
expect(wrapper.find('[title="modelPlaza.table.timePricingRowHint"]').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('分组启用高峰倍率时时段行 tooltip 追加高峰披露,价格与倍率列保持不含高峰的口径', () => {
|
||||
const wrapper = mountTable([timePricedModel()], 0.8, null, {
|
||||
peakWindow: '14:00-18:00 ×1.5 (UTC+08:00)',
|
||||
peakRateMultiplier: 1.5
|
||||
})
|
||||
const nightCells = wrapper.findAll('tbody tr')[1].findAll('td')
|
||||
const title = nightCells[0].find('[title*="modelPlaza.table.timePricingRowHint"]').attributes('title')
|
||||
expect(title).toContain('modelPlaza.table.timePricingRowHintPeak')
|
||||
// 行内数字仍是 基础倍率 × 时段倍率(0.8 × 0.5),高峰只进披露不进价格
|
||||
expect(nightCells[1].text()).toContain('$1.20')
|
||||
expect(nightCells[7].text()).toContain('0.4x')
|
||||
})
|
||||
|
||||
it('分组未启用高峰(peakWindow 缺省)时 tooltip 不含高峰披露', () => {
|
||||
const wrapper = mountTable([timePricedModel()], 1)
|
||||
const badge = wrapper.find('[title*="modelPlaza.table.timePricingRowHint"]')
|
||||
expect(badge.attributes('title')).not.toContain('modelPlaza.table.timePricingRowHintPeak')
|
||||
})
|
||||
|
||||
it('无分时倍率时只有一行,不渲染时段标注', () => {
|
||||
const wrapper = mountTable([tokenModel()], 1)
|
||||
expect(wrapper.findAll('tbody tr')).toHaveLength(1)
|
||||
|
||||
@@ -607,6 +607,8 @@ export default {
|
||||
timePricingRowHint: 'Requests made within this period ({timezone} time) are billed at the prices in this row',
|
||||
timePricingRowHintWeekdays:
|
||||
'On weekdays (Mon–Fri) only, requests made within this period ({timezone} time) are billed at the prices in this row; weekends use the standard prices',
|
||||
timePricingRowHintPeak:
|
||||
'; prices in this row exclude the peak-hour rate — where this period overlaps the peak hours {window}, the overlapping portion is additionally multiplied by ×{multiplier}',
|
||||
timePricingWeekdays: 'Weekdays',
|
||||
timePricingRateHint: 'Effective rate {rate} × period multiplier {multiplier}',
|
||||
paidPrice: 'Your Price (Discounted)',
|
||||
|
||||
@@ -612,6 +612,7 @@ export default {
|
||||
timePricingRowHint: '按 {timezone} 时间,在该时段内发起的请求按本行价格计费',
|
||||
timePricingRowHintWeekdays:
|
||||
'按 {timezone} 时间,仅工作日(周一至周五)在该时段内发起的请求按本行价格计费,周末全天按标准价',
|
||||
timePricingRowHintPeak: ';本行价格未含高峰倍率,与高峰时段 {window} 重叠的部分实付再乘 ×{multiplier}',
|
||||
timePricingWeekdays: '工作日',
|
||||
timePricingRateHint: '生效倍率 {rate} × 时段倍率 {multiplier}',
|
||||
paidPrice: '实付价格(折后)',
|
||||
|
||||
Reference in New Issue
Block a user