From 7cab70c7823be1fe2b905e9517018685c3d0d383 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Fri, 8 May 2026 14:00:39 +0200 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20Phase=205f-vi=20=E2=80=94=20p?= =?UTF-8?q?er-inbound=20client=20expand-row=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each multi-user inbound row in the list now expands to show its client roster, mirroring the legacy aClientTable component. - ClientRowTable.vue: inner a-table with full desktop column set (action icons / enable / online / client-with-status-dot / traffic with progress bar / all-time / expiry with reset cycle) and a collapsed mobile variant (single dropdown menu + popover info). Self-contained: stats are looked up via a per-inbound email->stats Map; per-client confirms (reset/delete) live on the row. - The component emits typed events (edit/qrcode/info/reset-traffic/ delete/toggle-enable) — InboundsPage routes them back to the existing client and info modals (with `findClientIndex` so the modal opens focused on the right client). - InboundList.vue: hooks ClientRowTable into the a-table's expandedRowRender slot; row-class-name `hide-expand-icon` and a scoped CSS rule hide the chevron for non-multi-user inbounds (HTTP/Mixed/Tunnel/WireGuard/SS-single) so they keep looking flat. - toggle-enable-client routes through updateClient with the same `{id, settings: '{"clients": [...]}'}` shape as the other modals, so backend parsing stays single-pathed. Co-Authored-By: Claude Opus 4.7 --- .../src/pages/inbounds/ClientRowTable.vue | 501 ++++++++++++++++++ frontend/src/pages/inbounds/InboundList.vue | 39 ++ frontend/src/pages/inbounds/InboundsPage.vue | 92 +++- 3 files changed, 625 insertions(+), 7 deletions(-) create mode 100644 frontend/src/pages/inbounds/ClientRowTable.vue diff --git a/frontend/src/pages/inbounds/ClientRowTable.vue b/frontend/src/pages/inbounds/ClientRowTable.vue new file mode 100644 index 00000000..71e9f400 --- /dev/null +++ b/frontend/src/pages/inbounds/ClientRowTable.vue @@ -0,0 +1,501 @@ + + + + + diff --git a/frontend/src/pages/inbounds/InboundList.vue b/frontend/src/pages/inbounds/InboundList.vue index 0f0468c2..47508fc8 100644 --- a/frontend/src/pages/inbounds/InboundList.vue +++ b/frontend/src/pages/inbounds/InboundList.vue @@ -27,16 +27,19 @@ import { import { HttpUtil, ObjectUtil, SizeFormatter, IntlUtil, ColorUtils } from '@/utils'; import { DBInbound } from '@/models/dbinbound.js'; import { Inbound } from '@/models/inbound.js'; +import ClientRowTable from './ClientRowTable.vue'; const props = defineProps({ dbInbounds: { type: Array, required: true }, clientCount: { type: Object, required: true }, onlineClients: { type: Array, required: true }, + lastOnlineMap: { type: Object, default: () => ({}) }, refreshing: { type: Boolean, default: false }, expireDiff: { type: Number, default: 0 }, trafficDiff: { type: Number, default: 0 }, pageSize: { type: Number, default: 0 }, isMobile: { type: Boolean, default: false }, + isDarkTheme: { type: Boolean, default: false }, subEnable: { type: Boolean, default: false }, }); @@ -45,6 +48,13 @@ const emit = defineEmits([ 'add-inbound', 'general-action', 'row-action', + // Per-client events surfaced from the expand-row table. + 'edit-client', + 'qrcode-client', + 'info-client', + 'reset-traffic-client', + 'delete-client', + 'toggle-enable-client', ]); // ============ Toolbar / search & filter ============================= @@ -302,7 +312,30 @@ function showQrCodeMenu(dbInbound) { :scroll="isMobile ? {} : { x: 1000 }" :style="{ marginTop: '10px' }" size="small" + :row-class-name="(r) => (r.isMultiUser() ? '' : 'hide-expand-icon')" > + + +