feat(channels): themed model popover + group-badge with rate, subscription & exclusivity
Pricing popover: - Teleport to body + fixed-position re-measuring on hover/scroll/resize so it escapes the card's overflow-hidden clip that was chopping off the lower half of the panel. - Header + border adopt the platform theme via platformBorderClass / platformBadgeLightClass so each model card reads at a glance. Accessible groups: - Backend AvailableGroupRef / user DTO now expose subscription_type, rate_multiplier and is_exclusive. User-specific rates continue to come from /groups/rates (same pattern as the API keys page). - Table renders groups through the shared GroupBadge, which already deepens subscription-type colors and shows default vs custom rate as <s>default</s> <b>custom</b>. - Exclusive groups are labelled with a purple shield row, public groups with a grey globe row so admins and users can tell at a glance which groups they got via explicit grant vs. public access. i18n keys for exclusive / public / their tooltips added to zh + en.
This commit is contained in:
@@ -48,10 +48,17 @@ func (h *AvailableChannelHandler) featureEnabled(c *gin.Context) bool {
|
||||
}
|
||||
|
||||
// userAvailableGroup 用户可见的分组概要(白名单字段)。
|
||||
//
|
||||
// 前端据此区分专属 vs 公开分组(IsExclusive)、订阅 vs 标准分组(SubscriptionType,
|
||||
// 订阅视觉加深),并用 RateMultiplier 作为默认倍率;用户专属倍率前端走
|
||||
// /groups/rates,和 API 密钥页面保持一致。
|
||||
type userAvailableGroup struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Platform string `json:"platform"`
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Platform string `json:"platform"`
|
||||
SubscriptionType string `json:"subscription_type"`
|
||||
RateMultiplier float64 `json:"rate_multiplier"`
|
||||
IsExclusive bool `json:"is_exclusive"`
|
||||
}
|
||||
|
||||
// userSupportedModelPricing 用户可见的定价字段白名单。
|
||||
@@ -206,9 +213,12 @@ func filterUserVisibleGroups(
|
||||
continue
|
||||
}
|
||||
visible = append(visible, userAvailableGroup{
|
||||
ID: g.ID,
|
||||
Name: g.Name,
|
||||
Platform: g.Platform,
|
||||
ID: g.ID,
|
||||
Name: g.Name,
|
||||
Platform: g.Platform,
|
||||
SubscriptionType: g.SubscriptionType,
|
||||
RateMultiplier: g.RateMultiplier,
|
||||
IsExclusive: g.IsExclusive,
|
||||
})
|
||||
}
|
||||
return visible
|
||||
|
||||
@@ -101,6 +101,17 @@ func TestUserAvailableChannel_FieldWhitelist(t *testing.T) {
|
||||
require.Truef(t, exists, "platform section must expose %q", key)
|
||||
}
|
||||
|
||||
// Group DTO 暴露区分专属/公开、订阅类型、默认倍率所需的字段,
|
||||
// 前端据此渲染 GroupBadge 并与 API 密钥页保持一致的视觉。
|
||||
rawGroup, err := json.Marshal(row.Platforms[0].Groups[0])
|
||||
require.NoError(t, err)
|
||||
var groupDecoded map[string]any
|
||||
require.NoError(t, json.Unmarshal(rawGroup, &groupDecoded))
|
||||
for _, key := range []string{"id", "name", "platform", "subscription_type", "rate_multiplier", "is_exclusive"} {
|
||||
_, exists := groupDecoded[key]
|
||||
require.Truef(t, exists, "group DTO must expose %q", key)
|
||||
}
|
||||
|
||||
// pricing interval 白名单:不应暴露 id / sort_order。
|
||||
pricing := toUserPricing(&service.ChannelModelPricing{
|
||||
BillingMode: service.BillingModeToken,
|
||||
|
||||
Reference in New Issue
Block a user