feat(channel): 模型标签输入 + $/MTok 价格单位 + 左开右闭区间 + i18n
- 模型输入改为标签列表(输入回车添加,支持粘贴批量导入) - 价格显示单位改为 $/MTok(每百万 token),提交时自动转换 - Token 模式增加图片输出价格字段(适配 Gemini 图片模型按 token 计费) - 区间边界改为左开右闭 (min, max],右边界包含 - 默认价格作为未命中区间时的回退价格 - 添加完整中英文 i18n 翻译
This commit is contained in:
@@ -287,7 +287,7 @@ import { useAppStore } from '@/stores/app'
|
||||
import { adminAPI } from '@/api/admin'
|
||||
import type { Channel, ChannelModelPricing, CreateChannelRequest, UpdateChannelRequest } from '@/api/admin/channels'
|
||||
import type { PricingFormEntry } from '@/components/admin/channel/types'
|
||||
import { toNullableNumber, apiIntervalsToForm, formIntervalsToAPI } from '@/components/admin/channel/types'
|
||||
import { mTokToPerToken, perTokenToMTok, apiIntervalsToForm, formIntervalsToAPI } from '@/components/admin/channel/types'
|
||||
import type { AdminGroup } from '@/types'
|
||||
import type { Column } from '@/components/common/types'
|
||||
import AppLayout from '@/components/layout/AppLayout.vue'
|
||||
@@ -412,13 +412,12 @@ function toggleGroup(groupId: number) {
|
||||
// ── Pricing helpers ──
|
||||
function addPricingEntry() {
|
||||
form.model_pricing.push({
|
||||
modelsInput: '',
|
||||
models: [],
|
||||
billing_mode: 'token',
|
||||
input_price: null,
|
||||
output_price: null,
|
||||
cache_write_price: null,
|
||||
cache_read_price: null,
|
||||
per_request_price: null,
|
||||
image_output_price: null,
|
||||
intervals: []
|
||||
})
|
||||
@@ -434,29 +433,28 @@ function removePricingEntry(idx: number) {
|
||||
|
||||
function formPricingToAPI(): ChannelModelPricing[] {
|
||||
return form.model_pricing
|
||||
.filter(e => e.modelsInput.trim())
|
||||
.filter(e => e.models.length > 0)
|
||||
.map(e => ({
|
||||
models: e.modelsInput.split(',').map(m => m.trim()).filter(Boolean),
|
||||
models: e.models,
|
||||
billing_mode: e.billing_mode,
|
||||
input_price: toNullableNumber(e.input_price),
|
||||
output_price: toNullableNumber(e.output_price),
|
||||
cache_write_price: toNullableNumber(e.cache_write_price),
|
||||
cache_read_price: toNullableNumber(e.cache_read_price),
|
||||
image_output_price: toNullableNumber(e.image_output_price),
|
||||
input_price: mTokToPerToken(e.input_price),
|
||||
output_price: mTokToPerToken(e.output_price),
|
||||
cache_write_price: mTokToPerToken(e.cache_write_price),
|
||||
cache_read_price: mTokToPerToken(e.cache_read_price),
|
||||
image_output_price: mTokToPerToken(e.image_output_price),
|
||||
intervals: formIntervalsToAPI(e.intervals || [])
|
||||
}))
|
||||
}
|
||||
|
||||
function apiPricingToForm(pricing: ChannelModelPricing[]): PricingFormEntry[] {
|
||||
return pricing.map(p => ({
|
||||
modelsInput: p.models.join(', '),
|
||||
models: p.models || [],
|
||||
billing_mode: p.billing_mode,
|
||||
input_price: p.input_price,
|
||||
output_price: p.output_price,
|
||||
cache_write_price: p.cache_write_price,
|
||||
cache_read_price: p.cache_read_price,
|
||||
per_request_price: null,
|
||||
image_output_price: p.image_output_price,
|
||||
input_price: perTokenToMTok(p.input_price),
|
||||
output_price: perTokenToMTok(p.output_price),
|
||||
cache_write_price: perTokenToMTok(p.cache_write_price),
|
||||
cache_read_price: perTokenToMTok(p.cache_read_price),
|
||||
image_output_price: perTokenToMTok(p.image_output_price),
|
||||
intervals: apiIntervalsToForm(p.intervals || [])
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user