🎨 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:
@@ -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()}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -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') &&
|
||||
|
||||
@@ -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">
|
||||
{/* 主卡片容器 */}
|
||||
|
||||
@@ -1902,7 +1902,7 @@ const ChannelsTable = () => {
|
||||
/>
|
||||
|
||||
<Card
|
||||
className="!rounded-2xl"
|
||||
className="table-scroll-card !rounded-2xl"
|
||||
title={renderHeader()}
|
||||
shadows='always'
|
||||
bordered={false}
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -574,7 +574,7 @@ const RedemptionsTable = () => {
|
||||
></EditRedemption>
|
||||
|
||||
<Card
|
||||
className="!rounded-2xl"
|
||||
className="table-scroll-card !rounded-2xl"
|
||||
title={renderHeader()}
|
||||
shadows='always'
|
||||
bordered={false}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -872,7 +872,7 @@ const TokensTable = () => {
|
||||
></EditToken>
|
||||
|
||||
<Card
|
||||
className="!rounded-2xl"
|
||||
className="table-scroll-card !rounded-2xl"
|
||||
title={renderHeader()}
|
||||
shadows='always'
|
||||
bordered={false}
|
||||
|
||||
@@ -639,7 +639,7 @@ const UsersTable = () => {
|
||||
></EditUser>
|
||||
|
||||
<Card
|
||||
className="!rounded-2xl"
|
||||
className="table-scroll-card !rounded-2xl"
|
||||
title={renderHeader()}
|
||||
shadows='always'
|
||||
bordered={false}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -17,7 +17,7 @@ const chat2page = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-[64px] px-2">
|
||||
<div className="mt-[60px] px-2">
|
||||
<h3>正在加载,请稍候...</h3>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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 }} />}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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={
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user