From 83f1dbf22faed38d21a8418ed263459b1767e5eb Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 1 Oct 2026 01:51:47 +0800 Subject: [PATCH 1/5] feat(ui): stacked table cells so wide admin tables fit without sideways scrolling Console theme, option B of the ui-tune table-width round (owner-approved 2026-09-30): related columns become lines of one cell, each part keeping its own cell slot, header label, sort control and help tip. Nothing is hidden; rows may grow taller. - DataTable: Column.stack / stackLayout / cellLabel / stackBelow. A stacked host draws its parts' header labels (a sortable part sorts on a click on its label or with its own sort button, whose name carries the order) and its cell lines. Parts are columns again from stackBelow up and whenever the theme is off (upstream's column order); phone cards are unchanged. - columnStack.ts: stackColumns() applies the column settings to stacked tables (a hidden part drops its line, a hidden host keeps its parts' lines). - Accounts always stacked (9 columns; the 5 optional columns join cells); admin usage and the admin error list below 1800px, the ops error dialog at every width (it is at most 1240px wide); user usage, user errors and IP management below 1400px; API keys use the compact recipe up to 1440px. - tables.css (after the page files): the layout rules, all under html.flat-theme; existing rules keyed on a merged column also name its line. - Account names and e-mails break at word joins, model ids only after a slash (an outlier part over 32 characters also after its hyphens); without the theme both render as upstream. Co-Authored-By: Claude Opus 5.5 --- .../src/components/admin/usage/UsageTable.vue | 18 +- frontend/src/components/common/DataTable.vue | 279 +++++- .../common/__tests__/DataTable.spec.ts | 149 +++ frontend/src/components/common/columnStack.ts | 112 +++ frontend/src/components/common/types.ts | 29 + .../user/UserErrorRequestsTable.vue | 27 +- .../src/i18n/locales/en/admin/accounts.ts | 4 + frontend/src/i18n/locales/en/common.ts | 3 + .../src/i18n/locales/zh/admin/accounts.ts | 4 + frontend/src/i18n/locales/zh/common.ts | 3 + frontend/src/main.ts | 1 + frontend/src/styles/console/behaviour.css | 1 + frontend/src/styles/console/data.css | 4 +- .../src/styles/console/pages/accounts.css | 20 +- .../styles/console/pages/dashboard-usage.css | 3 +- .../pages/users-groups-proxies-keys.css | 4 + frontend/src/styles/console/tables.css | 870 ++++++++++++++++++ frontend/src/utils/softWrap.ts | 77 ++ frontend/src/views/admin/AccountsView.vue | 43 +- frontend/src/views/admin/ProxiesView.vue | 12 +- frontend/src/views/admin/UsageView.vue | 14 +- .../__tests__/AccountsView.bulkEdit.spec.ts | 5 +- .../__tests__/AccountsView.console.spec.ts | 13 +- .../AccountsView.priorityColumn.spec.ts | 9 +- .../AccountsView.usageWindowsHint.spec.ts | 12 +- .../admin/ops/components/OpsErrorLogTable.vue | 46 +- frontend/src/views/user/UsageView.vue | 10 +- 27 files changed, 1677 insertions(+), 95 deletions(-) create mode 100644 frontend/src/components/common/columnStack.ts create mode 100644 frontend/src/styles/console/tables.css create mode 100644 frontend/src/utils/softWrap.ts diff --git a/frontend/src/components/admin/usage/UsageTable.vue b/frontend/src/components/admin/usage/UsageTable.vue index ff0e836c30b6..37e887a9fa81 100644 --- a/frontend/src/components/admin/usage/UsageTable.vue +++ b/frontend/src/components/admin/usage/UsageTable.vue @@ -60,25 +60,25 @@ class="break-all" :class="i === 0 ? 'font-medium text-gray-900 dark:text-white' : 'text-gray-500 dark:text-gray-400'" :style="i > 0 ? `padding-left: ${i * 0.75}rem` : ''"> - ↳{{ step }} + ↳
- {{ row.model }} +
- ↳{{ row.upstream_model }} + ↳
- {{ row.model }} +
- ↳ {{ t('usage.upstreamResponseModel') }}:{{ row.upstream_response_model }} + ↳ {{ t('usage.upstreamResponseModel') }}:{{ ' ' }} + flatThemeActive.value ? modelWrapParts(value) : [value == null ? '' : String(value)] + /** Compute the account-billed cost for display: (account_stats_cost ?? total_cost) * rate_multiplier */ function accountBilled(row: { total_cost?: number | null; account_stats_cost?: number | null; account_rate_multiplier?: number | null }): number { const base = row.account_stats_cost != null ? row.account_stats_cost : (row.total_cost ?? 0) diff --git a/frontend/src/components/common/DataTable.vue b/frontend/src/components/common/DataTable.vue index 9709ce44765e..0a08c89c062f 100644 --- a/frontend/src/components/common/DataTable.vue +++ b/frontend/src/components/common/DataTable.vue @@ -118,51 +118,108 @@ />
- - {{ column.label }} - - + + + + +
+
- - - + + {{ part.label }} + + +
+
@@ -173,7 +230,7 @@
- +
@@ -233,9 +290,10 @@ /> - @@ -251,6 +310,41 @@ ? column.formatter(item.row[column.key], item.row) : item.row[column.key] }} + +
+
+ {{ column.cellLabel }} +
+ + {{ column.formatter + ? column.formatter(item.row[column.key], item.row) + : item.row[column.key] }} + +
+
+
+ {{ part.cellLabel === true ? part.label : part.cellLabel }} +
+ + {{ part.formatter + ? part.formatter(item.row[part.key], item.row) + : item.row[part.key] }} + +
+
+
@@ -265,10 +359,11 @@