Просмотр исходного кода

docs: expand display pricing frontend spec

master
fengsilin 1 неделю назад
Родитель
Сommit
302987989b
1 измененных файлов: 75 добавлений и 5 удалений
  1. +75
    -5
      docs/superpowers/specs/2026-06-30-model-display-pricing-design.md

+ 75
- 5
docs/superpowers/specs/2026-06-30-model-display-pricing-design.md Просмотреть файл

@@ -140,11 +140,23 @@ Use wildcard model parameters, matching `/api/pricing/user/*model`, so model nam

## Frontend Design

The display pricing UI is part of the existing model pricing page. It should be implemented as a display override on top of the current `/api/pricing` response, not as a new standalone public page.

Affected frontend entry points:

- `web/src/hooks/model-pricing/useModelPricingData.jsx`: keep loading `/api/pricing`; no extra fetch is needed for public display.
- `web/src/helpers/utils.jsx`: add shared helpers so table, card, and detail views render the same display price.
- `web/src/components/table/model-pricing/view/table/PricingTableColumns.jsx`: compact price column display.
- `web/src/components/table/model-pricing/view/card/PricingCardView.jsx`: compact card display.
- `web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx`: full detail table display.
- `web/src/components/table/model-pricing/modal/components/UserPriceComparison.jsx`: hide when display pricing exists.

Add display pricing helpers in the model pricing utilities:

- `hasDisplayPricing(record)`
- `summarizeDisplayPricing({ record, displayPrice })`
- `formatDisplayPricingItem({ item, displayPrice })`
- `normalizeDisplayPricingItems(record)`
- `formatDisplayPricingItem({ item, displayPrice, t })`
- `summarizeDisplayPricing({ record, displayPrice, t })`

Display calculation:

@@ -155,26 +167,58 @@ discounted = displayPrice(item.price * item.discount_rate)

If `discount_rate` is `1`, show only the regular price. If lower than `1`, show discounted price and optionally the original price with strikethrough.

Unit labels should be translated in the frontend. The API returns stable unit keys such as `second`, `call`, `image`, and `video`; the UI maps them to localized labels such as `/ second` or `/ sec` depending on existing i18n conventions.

Display priority:

1. If `record.display_pricing` exists and has items, render display pricing.
2. Otherwise, keep the existing matrix pricing, per-token pricing, and per-call pricing behavior.

List and card compact display:
### Pricing Table Display

In the main table price column, display pricing replaces the current matrix/per-token/per-call price block for that model.

For multiple display pricing items with the same unit, show a converted price range:

```text
CNY 2.41 - CNY 4.22 / second
Display price
```

If all visible items share one price:
For one item, or when all items have the same discounted price:

```text
CNY 2.41 / second
Display price
```

Detail view:
For multiple units, group by unit and render one short line per unit:

```text
CNY 2.41 - CNY 4.22 / second
CNY 1.00 / image
Display price
```

The compact table view should not render `official_supplier_tip`; that belongs in the detail view.

### Pricing Card Display

Cards should use the same summary output as the table column, with at most the first three display items shown below the range:

```text
CNY 2.41 - CNY 4.22 / second
768p-6s CNY 2.41 / second
768p-10s CNY 2.90 / second
1080p CNY 4.22 / second
+ 2 more
```

If the card is too narrow on mobile, keep only the range and the `Display price` label. Do not render a horizontal table inside the card.

### Detail Side Sheet Display

When `display_pricing` exists, `ModelPricingTable` should render a dedicated display pricing table instead of the existing group price table or matrix table.

| Specification | Supplier Tip | Price | Discount |
| --- | --- | --- | --- |
@@ -182,14 +226,40 @@ Detail view:
| 768p-10s | Text-to-video and image-to-video, 768P 10s | CNY 2.90 / second | - |
| 1080p | Text-to-video and image-to-video, 1080P | CNY 4.22 / second | - |

For discounted items, render the discounted price as the primary value and the original converted price as secondary strikethrough text:

```text
CNY 1.93 / second
Original CNY 2.41, 20% off
```

Every display pricing block must include an i18n note with this meaning:

```text
Display price is only an estimate. Actual consumption is based on the final quota deduction after the task completes.
```

The note must appear in the detail side sheet and in any compact tooltip/popover if one is added later. The main table and card can show only a short `Display price` label to avoid visual noise.

### User Price Comparison

When a model has `display_pricing`, hide the old `UserPriceComparison` discount card. The detail side sheet should show the display pricing table and the estimate note instead. The current discount card is derived from real billing fields and would be misleading for models whose visible price is only an estimate.

`/api/pricing/user/*model` may still return `display_pricing` for consistency, but the frontend should not calculate user-specific discounted display pricing from `original_price` or `user_price`.

### Admin Editing UI

The first implementation may manage `ModelDisplayPricing` through JSON option editing or a minimal admin form. If a structured form is added, it should expose only display fields:

- `specification`
- `official_supplier_tip`
- `price` as USD base price
- `unit`
- `sort_order`
- `discount_rate`

The editor must label `price` as USD base display price and include a warning that it does not affect real billing.

## Billing Isolation

The implementation must not reference display pricing in:


Загрузка…
Отмена
Сохранить