🎨 refactor(ui): scope table scrolling to console cards & refine overall layout

Summary
Implement a dedicated, reusable scrolling mechanism for all console-table pages while keeping header and sidebar fixed, plus related layout improvements.

Key Changes
• Added `.table-scroll-card` utility class
 – Provides flex column layout and internal vertical scrolling
 – Desktop height: `calc(100vh - 110px)`; Mobile (<768 px) height: `calc(100vh - 77px)`
 – Hides scrollbars cross-browser (`-ms-overflow-style`, `scrollbar-width`, `::-webkit-scrollbar`)
• Replaced global `.semi-card` scrolling rules with `.table-scroll-card` to avoid affecting non-table cards
• Updated table components (Channels, Tokens, Users, Logs, MjLogs, TaskLogs, Redemptions) to use the new class
• PageLayout
 – Footer is now suppressed for all `/console` routes
 – Confirmed only central content area scrolls; header & sidebar remain fixed
• Restored hidden scrollbar rules for `.semi-layout-content` and removed unnecessary global overrides
• Minor CSS cleanup & comment improvements for readability

Result
Console table pages now fill the viewport with smooth, internal scrolling and no visible scrollbars, while other cards and pages remain unaffected.
This commit is contained in:
t0ng7u
2025-07-18 01:06:18 +08:00
parent 16e32c3f67
commit 218ad6bbe0
32 changed files with 59 additions and 38 deletions

View File

@@ -523,7 +523,7 @@ const LoginForm = () => {
{/* 背景模糊晕染球 */}
<div className="blur-ball blur-ball-indigo" style={{ top: '-80px', right: '-80px', transform: 'none' }} />
<div className="blur-ball blur-ball-teal" style={{ top: '50%', left: '-120px' }} />
<div className="w-full max-w-sm mt-[64px]">
<div className="w-full max-w-sm mt-[60px]">
{showEmailLogin || !(status.github_oauth || status.oidc_enabled || status.wechat_login || status.linuxdo_oauth || status.telegram_oauth)
? renderEmailLoginForm()
: renderOAuthOptions()}

View File

@@ -82,7 +82,7 @@ const PasswordResetConfirm = () => {
{/* 背景模糊晕染球 */}
<div className="blur-ball blur-ball-indigo" style={{ top: '-80px', right: '-80px', transform: 'none' }} />
<div className="blur-ball blur-ball-teal" style={{ top: '50%', left: '-120px' }} />
<div className="w-full max-w-sm mt-[64px]">
<div className="w-full max-w-sm mt-[60px]">
<div className="flex flex-col items-center">
<div className="w-full max-w-md">
<div className="flex items-center justify-center mb-6 gap-2">

View File

@@ -82,7 +82,7 @@ const PasswordResetForm = () => {
{/* 背景模糊晕染球 */}
<div className="blur-ball blur-ball-indigo" style={{ top: '-80px', right: '-80px', transform: 'none' }} />
<div className="blur-ball blur-ball-teal" style={{ top: '50%', left: '-120px' }} />
<div className="w-full max-w-sm mt-[64px]">
<div className="w-full max-w-sm mt-[60px]">
<div className="flex flex-col items-center">
<div className="w-full max-w-md">
<div className="flex items-center justify-center mb-6 gap-2">

View File

@@ -540,7 +540,7 @@ const RegisterForm = () => {
{/* 背景模糊晕染球 */}
<div className="blur-ball blur-ball-indigo" style={{ top: '-80px', right: '-80px', transform: 'none' }} />
<div className="blur-ball blur-ball-teal" style={{ top: '50%', left: '-120px' }} />
<div className="w-full max-w-sm mt-[64px]">
<div className="w-full max-w-sm mt-[60px]">
{showEmailRegister || !(status.github_oauth || status.oidc_enabled || status.wechat_login || status.linuxdo_oauth || status.telegram_oauth)
? renderEmailRegisterForm()
: renderOAuthOptions()}

View File

@@ -23,7 +23,7 @@ const PageLayout = () => {
const { i18n } = useTranslation();
const location = useLocation();
const shouldHideFooter = location.pathname === '/console/playground' || location.pathname.startsWith('/console/chat');
const shouldHideFooter = location.pathname.startsWith('/console');
const shouldInnerPadding = location.pathname.includes('/console') &&
!location.pathname.startsWith('/console/chat') &&

View File

@@ -379,7 +379,7 @@ const PersonalSetting = () => {
};
return (
<div className="bg-gray-50 mt-[64px]">
<div className="bg-gray-50 mt-[60px]">
<div className="flex justify-center">
<div className="w-full">
{/* 主卡片容器 */}

View File

@@ -1902,7 +1902,7 @@ const ChannelsTable = () => {
/>
<Card
className="!rounded-2xl"
className="table-scroll-card !rounded-2xl"
title={renderHeader()}
shadows='always'
bordered={false}

View File

@@ -1202,7 +1202,7 @@ const LogsTable = () => {
<>
{renderColumnSelector()}
<Card
className='!rounded-2xl mb-4'
className='table-scroll-card !rounded-2xl mb-4'
title={
<div className='flex flex-col w-full'>
<Spin spinning={loadingStat}>

View File

@@ -799,7 +799,7 @@ const LogsTable = () => {
{renderColumnSelector()}
<Layout>
<Card
className="!rounded-2xl mb-4"
className="table-scroll-card !rounded-2xl mb-4"
title={
<div className="flex flex-col w-full">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-2 w-full">

View File

@@ -574,7 +574,7 @@ const RedemptionsTable = () => {
></EditRedemption>
<Card
className="!rounded-2xl"
className="table-scroll-card !rounded-2xl"
title={renderHeader()}
shadows='always'
bordered={false}

View File

@@ -649,7 +649,7 @@ const LogsTable = () => {
{renderColumnSelector()}
<Layout>
<Card
className="!rounded-2xl mb-4"
className="table-scroll-card !rounded-2xl mb-4"
title={
<div className="flex flex-col w-full">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-2 w-full">

View File

@@ -872,7 +872,7 @@ const TokensTable = () => {
></EditToken>
<Card
className="!rounded-2xl"
className="table-scroll-card !rounded-2xl"
title={renderHeader()}
shadows='always'
bordered={false}

View File

@@ -639,7 +639,7 @@ const UsersTable = () => {
></EditUser>
<Card
className="!rounded-2xl"
className="table-scroll-card !rounded-2xl"
title={renderHeader()}
shadows='always'
bordered={false}

View File

@@ -14,22 +14,16 @@
}
/* ==================== 全局基础样式 ==================== */
/* 侧边栏宽度相关的 CSS 变量,配合 .sidebar-collapsed 类和媒体查询实现响应式布局 */
:root {
--sidebar-width: 180px;
/* 展开时宽度 */
--sidebar-width-collapsed: 60px; /* 折叠后宽度,显示图标栏 */
/* 折叠后宽度 */
--sidebar-width-collapsed: 60px;
--sidebar-current-width: var(--sidebar-width);
}
/* 当 body 上存在 .sidebar-collapsed 类时,使用折叠宽度 */
body.sidebar-collapsed {
--sidebar-current-width: var(--sidebar-width-collapsed);
}
/* 移除了在移动端强制设为 0 的限制,改由 React 控制是否渲染侧边栏以实现显示/隐藏 */
body {
font-family: Lato, 'Helvetica Neue', Arial, Helvetica, 'Microsoft YaHei', sans-serif;
color: var(--semi-color-text-0);
@@ -615,4 +609,31 @@ html:not(.dark) .blur-ball-indigo {
html:not(.dark) .blur-ball-teal {
opacity: 0.2;
}
/* ==================== 表格卡片滚动设置 ==================== */
.table-scroll-card {
display: flex;
flex-direction: column;
height: calc(100vh - 110px);
max-height: calc(100vh - 110px);
}
.table-scroll-card .semi-card-body {
flex: 1 1 auto;
overflow-y: auto;
-ms-overflow-style: none;
scrollbar-width: none;
}
.table-scroll-card .semi-card-body::-webkit-scrollbar {
display: none;
}
@media (max-width: 767px) {
.table-scroll-card {
height: calc(100vh - 77px);
max-height: calc(100vh - 77px);
}
}

View File

@@ -105,7 +105,7 @@ const About = () => {
);
return (
<div className="mt-[64px] px-2">
<div className="mt-[60px] px-2">
{aboutLoaded && about === '' ? (
<div className="flex justify-center items-center h-screen p-8">
<Empty

View File

@@ -3,7 +3,7 @@ import ChannelsTable from '../../components/table/ChannelsTable';
const File = () => {
return (
<div className="mt-[64px] px-2">
<div className="mt-[60px] px-2">
<ChannelsTable />
</div>
);

View File

@@ -42,7 +42,7 @@ const ChatPage = () => {
allow='camera;microphone'
/>
) : (
<div className="fixed inset-0 w-screen h-screen flex items-center justify-center bg-white/80 z-[1000] mt-[64px]">
<div className="fixed inset-0 w-screen h-screen flex items-center justify-center bg-white/80 z-[1000] mt-[60px]">
<div className="flex flex-col items-center">
<Spin
size="large"

View File

@@ -17,7 +17,7 @@ const chat2page = () => {
}
return (
<div className="mt-[64px] px-2">
<div className="mt-[60px] px-2">
<h3>正在加载请稍候...</h3>
</div>
);

View File

@@ -1120,7 +1120,7 @@ const Detail = (props) => {
}, []);
return (
<div className="bg-gray-50 h-full mt-[64px] px-2">
<div className="bg-gray-50 h-full mt-[60px] px-2">
<div className="flex items-center justify-between mb-4">
<h2
className="text-2xl font-semibold text-gray-800 transition-opacity duration-1000 ease-in-out"

View File

@@ -274,7 +274,7 @@ const Home = () => {
className="w-full h-screen border-none"
/>
) : (
<div className="mt-[64px]" dangerouslySetInnerHTML={{ __html: homePageContent }} />
<div className="mt-[60px]" dangerouslySetInnerHTML={{ __html: homePageContent }} />
)}
</div>
)}

View File

@@ -2,7 +2,7 @@ import React from 'react';
import LogsTable from '../../components/table/LogsTable';
const Token = () => (
<div className="mt-[64px] px-2">
<div className="mt-[60px] px-2">
<LogsTable />
</div>
);

View File

@@ -2,7 +2,7 @@ import React from 'react';
import MjLogsTable from '../../components/table/MjLogsTable';
const Midjourney = () => (
<div className="mt-[64px] px-2">
<div className="mt-[60px] px-2">
<MjLogsTable />
</div>
);

View File

@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
const NotFound = () => {
const { t } = useTranslation();
return (
<div className="flex justify-center items-center h-screen p-8 mt-[64px]">
<div className="flex justify-center items-center h-screen p-8 mt-[60px]">
<Empty
image={<IllustrationNotFound style={{ width: 250, height: 250 }} />}
darkModeImage={<IllustrationNotFoundDark style={{ width: 250, height: 250 }} />}

View File

@@ -352,7 +352,7 @@ const Playground = () => {
}, [setMessage, saveMessagesImmediately]);
return (
<div className="h-full bg-gray-50 mt-[64px]">
<div className="h-full bg-gray-50 mt-[60px]">
<Layout style={{ height: '100%', background: 'transparent' }} className="flex flex-col md:flex-row">
{(showSettings || !isMobile) && (
<Layout.Sider

View File

@@ -2,7 +2,7 @@ import React from 'react';
import ModelPricing from '../../components/table/ModelPricing.js';
const Pricing = () => (
<div className="mt-[64px] px-2">
<div className="mt-[60px] px-2">
<ModelPricing />
</div>
);

View File

@@ -3,7 +3,7 @@ import RedemptionsTable from '../../components/table/RedemptionsTable';
const Redemption = () => {
return (
<div className="mt-[64px] px-2">
<div className="mt-[60px] px-2">
<RedemptionsTable />
</div>
);

View File

@@ -150,7 +150,7 @@ const Setting = () => {
}
}, [location.search]);
return (
<div className="mt-[64px] px-2">
<div className="mt-[60px] px-2">
<Layout>
<Layout.Content>
<Tabs

View File

@@ -133,7 +133,7 @@ const Setup = () => {
};
return (
<div className="bg-gray-50 mt-[64px]">
<div className="bg-gray-50 mt-[60px]">
<Layout>
<Layout.Content>
<div className="flex justify-center px-4 py-8">

View File

@@ -2,7 +2,7 @@ import React from 'react';
import TaskLogsTable from '../../components/table/TaskLogsTable.js';
const Task = () => (
<div className="mt-[64px] px-2">
<div className="mt-[60px] px-2">
<TaskLogsTable />
</div>
);

View File

@@ -3,7 +3,7 @@ import TokensTable from '../../components/table/TokensTable';
const Token = () => {
return (
<div className="mt-[64px] px-2">
<div className="mt-[60px] px-2">
<TokensTable />
</div>
);

View File

@@ -382,7 +382,7 @@ const TopUp = () => {
};
return (
<div className='mx-auto relative min-h-screen lg:min-h-0 mt-[64px]'>
<div className='mx-auto relative min-h-screen lg:min-h-0 mt-[60px]'>
{/* 划转模态框 */}
<Modal
title={

View File

@@ -3,7 +3,7 @@ import UsersTable from '../../components/table/UsersTable';
const User = () => {
return (
<div className="mt-[64px] px-2">
<div className="mt-[60px] px-2">
<UsersTable />
</div>
);