From ba88fb6bf0d1a2d459edaf276f9852b454017af8 Mon Sep 17 00:00:00 2001 From: chengqian Date: Mon, 27 Jul 2026 18:30:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E8=A1=A8=E6=A0=BC=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/BasePagination.vue | 223 ++++++++++++++++++ web/tenant/src/router/tenant.ts | 23 +- web/tenant/src/styles/main.css | 60 +++++ web/tenant/src/views/Layout.vue | 9 +- web/tenant/src/views/finance/OrdersPage.vue | 40 +--- .../src/views/finance/TransactionsPage.vue | 32 +-- web/tenant/src/views/service/ApiKeysPage.vue | 16 +- .../views/service/RequestAuditLogsPage.vue | 39 +-- web/tenant/src/views/service/TaskLogsPage.vue | 30 +-- .../src/views/service/UsageLogsPage.vue | 26 +- .../src/views/settings/LoginHistoryPage.vue | 33 +-- web/tenant/src/views/support/TicketsPage.vue | 37 +-- web/tenant/src/views/team/InvitationsPage.vue | 26 +- web/tenant/src/views/team/MembersPage.vue | 9 +- web/tenant/src/views/team/ProjectsPage.vue | 16 +- 15 files changed, 363 insertions(+), 256 deletions(-) create mode 100644 web/tenant/src/components/common/BasePagination.vue diff --git a/web/tenant/src/components/common/BasePagination.vue b/web/tenant/src/components/common/BasePagination.vue new file mode 100644 index 0000000..c5abef9 --- /dev/null +++ b/web/tenant/src/components/common/BasePagination.vue @@ -0,0 +1,223 @@ + + + + + diff --git a/web/tenant/src/router/tenant.ts b/web/tenant/src/router/tenant.ts index 8de8845..32ee79e 100644 --- a/web/tenant/src/router/tenant.ts +++ b/web/tenant/src/router/tenant.ts @@ -8,6 +8,7 @@ declare module 'vue-router' { icon?: string sort?: number roles?: string[] + viewportTable?: boolean description?: string keywords?: string } @@ -65,13 +66,13 @@ const tenantRoutes: RouteRecordRaw[] = [ path: 'members', name: 'TenantMembers', component: () => import('@/views/team/MembersPage.vue'), - meta: { title: '成员管理', icon: 'users', sort: 20, roles: ['owner', 'admin'] }, + meta: { title: '成员管理', icon: 'users', sort: 20, roles: ['owner', 'admin'], viewportTable: true }, }, { path: 'members/invitations', name: 'TenantInvitations', component: () => import('@/views/team/InvitationsPage.vue'), - meta: { title: '邀请记录', roles: ['owner', 'admin'] }, + meta: { title: '邀请记录', roles: ['owner', 'admin'], viewportTable: true }, }, { path: 'members/:id', @@ -83,7 +84,7 @@ const tenantRoutes: RouteRecordRaw[] = [ path: 'projects', name: 'TenantProjects', component: () => import('@/views/team/ProjectsPage.vue'), - meta: { title: '项目管理', icon: 'project', sort: 21, roles: ['owner', 'admin'] }, + meta: { title: '项目管理', icon: 'project', sort: 21, roles: ['owner', 'admin'], viewportTable: true }, }, { path: 'projects/:id', @@ -108,25 +109,25 @@ const tenantRoutes: RouteRecordRaw[] = [ path: 'api-keys', name: 'TenantApiKeys', component: () => import('@/views/service/ApiKeysPage.vue'), - meta: { title: 'API 密钥', icon: 'key', sort: 32, roles: ['owner', 'admin', 'member'] }, + meta: { title: 'API 密钥', icon: 'key', sort: 32, roles: ['owner', 'admin', 'member'], viewportTable: true }, }, { path: 'usage-logs', name: 'TenantUsageLogs', component: () => import('@/views/service/UsageLogsPage.vue'), - meta: { title: '用量日志', icon: 'chart', sort: 33, roles: ['owner', 'admin', 'member'] }, + meta: { title: '用量日志', icon: 'chart', sort: 33, roles: ['owner', 'admin', 'member'], viewportTable: true }, }, { path: 'request-audit-logs', name: 'TenantRequestAuditLogs', component: () => import('@/views/service/RequestAuditLogsPage.vue'), - meta: { title: '请求审计日志', icon: 'clipboard', sort: 34, roles: ['owner', 'admin'] }, + meta: { title: '请求审计日志', icon: 'clipboard', sort: 34, roles: ['owner', 'admin'], viewportTable: true }, }, { path: 'task-logs', name: 'TenantTaskLogs', component: () => import('@/views/service/TaskLogsPage.vue'), - meta: { title: '任务日志', icon: 'clock', sort: 35, roles: ['owner', 'admin', 'member'] }, + meta: { title: '任务日志', icon: 'clock', sort: 35, roles: ['owner', 'admin', 'member'], viewportTable: true }, }, // 财务 (40-49) { @@ -139,13 +140,13 @@ const tenantRoutes: RouteRecordRaw[] = [ path: 'transactions', name: 'TenantTransactions', component: () => import('@/views/finance/TransactionsPage.vue'), - meta: { title: '交易记录', icon: 'document', sort: 41, roles: ['owner', 'admin'] }, + meta: { title: '交易记录', icon: 'document', sort: 41, roles: ['owner', 'admin'], viewportTable: true }, }, { path: 'orders', name: 'TenantOrders', component: () => import('@/views/finance/OrdersPage.vue'), - meta: { title: '订单记录', icon: 'receipt', sort: 42, roles: ['owner', 'admin'] }, + meta: { title: '订单记录', icon: 'receipt', sort: 42, roles: ['owner', 'admin'], viewportTable: true }, }, { path: 'redeem', @@ -176,7 +177,7 @@ const tenantRoutes: RouteRecordRaw[] = [ path: 'tickets', name: 'TenantTickets', component: () => import('@/views/support/TicketsPage.vue'), - meta: { title: '工单中心', icon: 'ticket', sort: 51, roles: ['owner', 'admin', 'member'] }, + meta: { title: '工单中心', icon: 'ticket', sort: 51, roles: ['owner', 'admin', 'member'], viewportTable: true }, }, { path: 'feedback', @@ -207,7 +208,7 @@ const tenantRoutes: RouteRecordRaw[] = [ path: 'login-history', name: 'TenantLoginHistory', component: () => import('@/views/settings/LoginHistoryPage.vue'), - meta: { title: '登录历史', roles: ['owner', 'admin', 'member'] }, + meta: { title: '登录历史', roles: ['owner', 'admin', 'member'], viewportTable: true }, }, { path: 'profile', diff --git a/web/tenant/src/styles/main.css b/web/tenant/src/styles/main.css index b54e7f4..41d22a5 100644 --- a/web/tenant/src/styles/main.css +++ b/web/tenant/src/styles/main.css @@ -384,6 +384,7 @@ body.modal-open { scrollbar-color: rgba(117, 104, 248, 0.28) transparent; } .table-container::-webkit-scrollbar { + width: 8px; height: 8px; } .table-container::-webkit-scrollbar-track { @@ -395,6 +396,65 @@ body.modal-open { background: linear-gradient(90deg, rgba(65, 168, 244, 0.4), rgba(117, 104, 248, 0.42)); background-clip: padding-box; } + +/* Desktop list pages fill the viewport; only their table rows scroll. */ +@media (min-width: 1024px) { + .tenant-layout { + display: flex; + height: 100dvh; + min-height: 0; + flex-direction: column; + overflow: hidden; + } + .tenant-shell { + display: flex; + min-height: 0; + flex: 1 1 0%; + flex-direction: column; + } + .tenant-main { + min-height: 0; + flex: 1 1 0%; + overflow-y: auto; + overscroll-behavior: contain; + } + .tenant-table-main { + overflow: hidden; + } + .tenant-table-main > .viewport-table-page { + height: 100%; + min-height: 0; + } + .viewport-table-page, + .viewport-table-panel, + .viewport-table-content { + display: flex; + min-height: 0; + flex-direction: column; + } + .viewport-table-panel { + flex: 1 1 0%; + overflow: hidden; + } + .viewport-table-content { + flex: 1 1 0%; + } + .viewport-table-scroll { + min-height: 0; + flex: 1 1 0%; + overflow: auto; + overscroll-behavior: contain; + } + .viewport-table-scroll .table thead th { + position: sticky; + top: 0; + z-index: 1; + } + .viewport-table-panel > .table-pagination, + .viewport-table-content > .table-pagination { + flex: 0 0 auto; + } +} .table-container-flush { border: 0; border-radius: 0; diff --git a/web/tenant/src/views/Layout.vue b/web/tenant/src/views/Layout.vue index 3728e8b..3aa4483 100644 --- a/web/tenant/src/views/Layout.vue +++ b/web/tenant/src/views/Layout.vue @@ -242,7 +242,7 @@ onBeforeUnmount(() => {