style: 统一项目内字体单位为rem并调整布局尺寸

将项目中所有硬编码的px字体尺寸、容器宽度高度统一替换为rem单位,统一整体UI样式的尺寸规范,同时调整部分布局容器的最大宽度适配新的单位体系
This commit is contained in:
2026-07-03 15:42:57 +08:00
parent edb2db5966
commit 885d17cec7
17 changed files with 56 additions and 52 deletions

View File

@@ -84,10 +84,10 @@ export default function ChannelPage() {
</Form.Item>
<Space size="large" align="start">
<Form.Item label="排序权重" field="sortOrder">
<InputNumber min={0} style={{ width: 200 }} />
<InputNumber min={0} style={{ width: "12.5rem" }} />
</Form.Item>
<Form.Item label="状态" field="status">
<Select style={{ width: 200 }} disabled={Boolean(editingChannel?.isDefault)}>
<Select style={{ width: "12.5rem" }} disabled={Boolean(editingChannel?.isDefault)}>
<Select.Option value="active"></Select.Option>
<Select.Option value="disabled"></Select.Option>
</Select>
@@ -160,11 +160,11 @@ export default function ChannelPage() {
/>
</Panel>
<Modal title="新增频道" visible={createVisible} onCancel={() => setCreateVisible(false)} footer={null} style={{ width: 640 }}>
<Modal title="新增频道" visible={createVisible} onCancel={() => setCreateVisible(false)} footer={null} style={{ width: "40rem" }}>
{renderForm(createChannel, { sortOrder: 0, status: "active", isDefault: false })}
</Modal>
<Modal title="编辑频道" visible={Boolean(editingChannel)} onCancel={() => setEditingChannel(null)} footer={null} style={{ width: 640 }}>
<Modal title="编辑频道" visible={Boolean(editingChannel)} onCancel={() => setEditingChannel(null)} footer={null} style={{ width: "40rem" }}>
{editingChannel && renderForm(updateChannel, {
name: editingChannel.name,
code: editingChannel.code,

View File

@@ -28,9 +28,9 @@ function MetricCard({ label, value, icon }: MetricCardProps & { icon: string })
<div className="flex h-11 w-11 items-center justify-center rounded-xl bg-white/80 shadow-sm ring-1 ring-black/[0.03]">
{METRIC_ICONS[icon]}
</div>
<span className="text-[18px] font-medium text-muted">{label}</span>
<span className="text-[1.125rem] font-medium text-muted">{label}</span>
</div>
<div className="text-[30px] font-bold tracking-tight text-ink">{value}</div>
<div className="text-[1.875rem] font-bold tracking-tight text-ink">{value}</div>
</div>
);
}

View File

@@ -181,7 +181,7 @@ export function DramaDetail({ dramaId }: DramaDetailPageProps) {
</Panel>
{drama && (
<Modal title="编辑短剧" visible={editVisible} onCancel={() => setEditVisible(false)} footer={null} style={{ width: 720 }}>
<Modal title="编辑短剧" visible={editVisible} onCancel={() => setEditVisible(false)} footer={null} style={{ width: "45rem" }}>
<Form
key={drama.id}
layout="vertical"

View File

@@ -164,7 +164,7 @@ export function DramaList() {
/>
</Panel>
<Modal title="创建短剧" visible={visible} onCancel={() => setVisible(false)} footer={null} style={{ width: 640 }}>
<Modal title="创建短剧" visible={visible} onCancel={() => setVisible(false)} footer={null} style={{ width: "40rem" }}>
<Form layout="vertical" onSubmit={createDrama} initialValues={{ status: "draft", isRecommended: false, sortOrder: 0, totalEpisodes: 1, channelId: defaultChannel?.id }}>
<Form.Item label="标题" field="title" rules={[{ required: true }]}>
<Input placeholder="请输入短剧标题" />

View File

@@ -117,7 +117,7 @@ export function EpisodeTable({ episodes, loading, uploadingEpisodeId, onUploadVi
/>
{editingEpisode && (
<Modal title={`编辑第 ${editingEpisode.episodeNo}`} visible={Boolean(editingEpisode)} onCancel={() => setEditingEpisode(null)} footer={null} style={{ width: 680 }}>
<Modal title={`编辑第 ${editingEpisode.episodeNo}`} visible={Boolean(editingEpisode)} onCancel={() => setEditingEpisode(null)} footer={null} style={{ width: "42.5rem" }}>
<Form
key={editingEpisode.id}
layout="vertical"

View File

@@ -5,10 +5,10 @@ import type { LoginPanelProps } from "../interface";
export const LoginPanel = ({ loading, onSubmit }: LoginPanelProps) => {
return (
<div className="flex items-center justify-center bg-canvas bg-[image:linear-gradient(180deg,rgba(255,255,255,0.86),rgba(242,243,245,0.94))] p-12">
<div className="w-full max-w-[420px] rounded-2xl border border-line/40 bg-white p-10 shadow-login">
<div className="w-full max-w-[26.25rem] rounded-2xl border border-line/40 bg-white p-10 shadow-login">
<div className="mb-8 flex flex-col items-center text-center">
<h2 className="mb-2 w-full mt-0 text-[22px] font-bold text-ink"></h2>
<p className="m-0 w-full text-[13px] text-muted">使</p>
<h2 className="mb-2 w-full mt-0 text-[1.375rem] font-bold text-ink"></h2>
<p className="m-0 w-full text-[0.8125rem] text-muted">使</p>
</div>
<Form layout="vertical" onSubmit={onSubmit} initialValues={{ username: "admin", password: "admin123" }}>
<Form.Item label="用户名" field="username" rules={[{ required: true }]}>
@@ -17,7 +17,7 @@ export const LoginPanel = ({ loading, onSubmit }: LoginPanelProps) => {
<Form.Item label="密码" field="password" rules={[{ required: true }]}>
<Input.Password prefix={<IconLock className="text-muted" />} placeholder="请输入密码" size="large" />
</Form.Item>
<Button type="primary" htmlType="submit" loading={loading} long size="large" className="!h-11 mt-6 !rounded-xl !text-[15px] !font-semibold !shadow-brand/30">
<Button type="primary" htmlType="submit" loading={loading} long size="large" className="!h-11 mt-6 !rounded-xl !text-[0.9375rem] !font-semibold !shadow-brand/30">
</Button>
</Form>

View File

@@ -3,25 +3,25 @@ export function LoginVisual() {
<div className="relative hidden flex-col justify-between overflow-hidden px-14 pb-12 pt-11 text-white after:pointer-events-none after:absolute after:inset-0 after:bg-[linear-gradient(180deg,rgba(255,255,255,0.08),rgba(255,255,255,0))] min-[901px]:flex bg-[linear-gradient(135deg,rgba(15,23,42,0.98)_0%,rgba(22,93,255,0.94)_54%,rgba(15,118,110,0.96)_100%),radial-gradient(circle_at_18%_18%,rgba(255,255,255,0.18),transparent_28%),radial-gradient(circle_at_82%_72%,rgba(255,255,255,0.12),transparent_32%)]">
<div className="relative z-[1] flex items-center gap-2.5 font-bold">
<span className="grid h-9 w-9 place-items-center rounded-xl bg-white/20 text-sm font-bold backdrop-blur-sm">CT</span>
<span className="text-white/90 text-[15px]">cth-tk-admin</span>
<span className="text-white/90 text-[0.9375rem]">cth-tk-admin</span>
</div>
<div className="relative z-[1] max-w-[640px]">
<span className="mb-5 inline-flex rounded-full border border-white/20 bg-white/10 px-4 py-2 text-[13px] text-white/80 backdrop-blur-sm">TikTok </span>
<h1 className="mb-5 mt-0 text-[44px] font-bold leading-[1.16] tracking-tight">CTH TikTok </h1>
<p className="m-0 max-w-[560px] text-base leading-[1.8] text-white/70"></p>
<div className="relative z-[1] max-w-[40rem]">
<span className="mb-5 inline-flex rounded-full border border-white/20 bg-white/10 px-4 py-2 text-[0.8125rem] text-white/80 backdrop-blur-sm">TikTok </span>
<h1 className="mb-5 mt-0 text-[2.75rem] font-bold leading-[1.16] tracking-tight">CTH TikTok </h1>
<p className="m-0 max-w-[35rem] text-base leading-[1.8] text-white/70"></p>
</div>
<div className="relative z-[1] grid max-w-[640px] grid-cols-3 gap-4">
<div className="relative z-[1] grid max-w-[40rem] grid-cols-3 gap-4">
<div className="rounded-xl border border-white/15 bg-white/10 p-5 backdrop-blur-md transition-all duration-300 hover:bg-white/15 hover:border-white/25">
<strong className="mb-2 block text-xl font-semibold"></strong>
<span className="text-[13px] text-white/65"></span>
<span className="text-[0.8125rem] text-white/65"></span>
</div>
<div className="rounded-xl border border-white/15 bg-white/10 p-5 backdrop-blur-md transition-all duration-300 hover:bg-white/15 hover:border-white/25">
<strong className="mb-2 block text-xl font-semibold"></strong>
<span className="text-[13px] text-white/65"></span>
<span className="text-[0.8125rem] text-white/65"></span>
</div>
<div className="rounded-xl border border-white/15 bg-white/10 p-5 backdrop-blur-md transition-all duration-300 hover:bg-white/15 hover:border-white/25">
<strong className="mb-2 block text-xl font-semibold"></strong>
<span className="text-[13px] text-white/65"></span>
<span className="text-[0.8125rem] text-white/65"></span>
</div>
</div>
</div>

View File

@@ -23,7 +23,7 @@ function LoginPage() {
};
return (
<div className="grid min-h-screen grid-cols-1 bg-canvas min-[901px]:grid-cols-[minmax(520px,0.95fr)_minmax(420px,1fr)]">
<div className="grid min-h-screen grid-cols-1 bg-canvas min-[901px]:grid-cols-[minmax(32.5rem,0.95fr)_minmax(26.25rem,1fr)]">
<LoginVisual />
<LoginPanel loading={loading} apiBaseUrl={authApi.getApiBaseUrl()} onSubmit={onSubmit} />
</div>

View File

@@ -89,7 +89,7 @@ function PersonnelPage() {
),
},
]} />
<Modal title="新增人员" visible={createVisible} onCancel={() => setCreateVisible(false)} footer={null} style={{ width: 640 }}>
<Modal title="新增人员" visible={createVisible} onCancel={() => setCreateVisible(false)} footer={null} style={{ width: "40rem" }}>
<Form form={createForm} layout="vertical" onSubmit={createPersonnel}>
<Form.Item label="用户名" field="username" rules={[{ required: true }]}>
<Input placeholder="请输入登录用户名" />
@@ -120,7 +120,7 @@ function PersonnelPage() {
confirmLoading={assigning}
onOk={saveAssignedRoles}
onCancel={() => setAssignVisible(false)}
style={{ width: 640 }}
style={{ width: "40rem" }}
>
<Select
mode="multiple"

View File

@@ -14,8 +14,8 @@ export function ProfileInfoList({ profile }: ProfileInfoListProps) {
function ProfileInfoItem({ label, value }: ProfileInfoItemProps) {
return (
<div className="rounded-xl border border-line/50 bg-canvas/50 p-4 transition-all duration-200 hover:border-line hover:bg-canvas">
<span className="mb-2 block text-[12px] font-medium uppercase tracking-wider text-muted">{label}</span>
<strong className="break-words text-[15px] font-semibold text-ink">{value}</strong>
<span className="mb-2 block text-[0.75rem] font-medium uppercase tracking-wider text-muted">{label}</span>
<strong className="break-words text-[0.9375rem] font-semibold text-ink">{value}</strong>
</div>
);
}

View File

@@ -9,7 +9,7 @@ export function ProfileSummary({ profile }: ProfileSummaryProps) {
</Avatar>
<div>
<h2 className="mb-1 mt-0 text-xl font-bold text-ink">{profile?.nickname || profile?.username || "管理员"}</h2>
<p className="m-0 text-[13px] text-muted">{profile?.email || "未设置邮箱"}</p>
<p className="m-0 text-[0.8125rem] text-muted">{profile?.email || "未设置邮箱"}</p>
</div>
</div>
);

View File

@@ -118,7 +118,7 @@ export function PermissionTreePicker({ permissions, value, onChange }: Permissio
const checkedKeys = value.map((id) => permissionKey(id));
return (
<div className="mb-[15px] h-[360px] overflow-auto rounded-xl border border-line/60 bg-canvas/30 px-4 py-3">
<div className="mb-[0.9375rem] h-[22.5rem] overflow-auto rounded-xl border border-line/60 bg-canvas/30 px-4 py-3">
<Tree
blockNode
checkable

View File

@@ -109,7 +109,7 @@ function RolesPage() {
},
]}
/>
<Modal title="新增角色" visible={createVisible} onCancel={() => setCreateVisible(false)} footer={null} style={{ width: 640 }}>
<Modal title="新增角色" visible={createVisible} onCancel={() => setCreateVisible(false)} footer={null} style={{ width: "40rem" }}>
<Form form={createForm} layout="vertical" onSubmit={createRole}>
<Form.Item label="角色编码" field="code" rules={[{ required: true }]}>
<Input placeholder="请输入角色编码,例如 drama_operator" />
@@ -138,7 +138,7 @@ function RolesPage() {
confirmLoading={savingPermissions}
onOk={saveRolePermissions}
onCancel={() => setAssignmentVisible(false)}
style={{ width: 640 }}
style={{ width: "40rem" }}
>
<PermissionTreePicker permissions={permissions.data.list} value={selectedPermissionIds} onChange={setSelectedPermissionIds} />
</Modal>

View File

@@ -11,7 +11,7 @@ export function NoAccessPage() {
<div className="mb-5 flex h-16 w-16 items-center justify-center rounded-2xl bg-warning-light">
<IconLock className="text-3xl text-warning" />
</div>
<Text type="secondary" className="text-center text-[14px]">访</Text>
<Text type="secondary" className="text-center text-[0.875rem]">访</Text>
</div>
</Panel>
);

View File

@@ -13,7 +13,7 @@ export function Panel({ title, extra, children, className = "", noPadding = fals
<div className={`section-card ${className}`}>
{(title || extra) && (
<div className="flex items-center justify-between border-b border-line/50 px-6 py-4">
{title && <h3 className="m-0 text-[15px] font-semibold text-ink">{title}</h3>}
{title && <h3 className="m-0 text-[0.9375rem] font-semibold text-ink">{title}</h3>}
{extra && <div>{extra}</div>}
</div>
)}

View File

@@ -3,6 +3,10 @@
@tailwind utilities;
@layer base {
html {
font-size: 18px;
}
::-webkit-scrollbar {
@apply w-1.5 h-1.5;
}
@@ -42,7 +46,7 @@
}
.form-help {
@apply -mt-3 mb-[18px] text-[13px] leading-[1.6] text-muted;
@apply -mt-3 mb-[1.125rem] text-[0.8125rem] leading-[1.6] text-muted;
}
.stat-card {
@@ -61,7 +65,7 @@
/* ── Menu ───────────────────────────────────────────── */
.arco-menu-item {
margin: 2px 8px !important;
margin: 0.125rem 0.5rem !important;
transition: all 0.2s ease !important;
}
@@ -76,7 +80,7 @@
}
.arco-menu-inline-header {
margin: 2px 8px !important;
margin: 0.125rem 0.5rem !important;
}
.arco-menu-inline-header:hover {
@@ -96,12 +100,12 @@
.arco-card-header {
border-bottom: 1px solid theme('colors.line / 50%') !important;
padding: 14px 20px !important;
padding: 0.875rem 1.25rem !important;
}
.arco-card-header-title {
font-weight: 600 !important;
font-size: 15px !important;
font-size: 0.9375rem !important;
color: theme('colors.ink') !important;
}
@@ -114,7 +118,7 @@
background-color: theme('colors.header-bg') !important;
font-weight: 600 !important;
color: theme('colors.secondary') !important;
font-size: 13px !important;
font-size: 0.8125rem !important;
letter-spacing: 0.01em;
border-bottom: 1px solid theme('colors.line / 80%') !important;
}
@@ -134,7 +138,7 @@
/* ── Pagination ─────────────────────────────────────── */
.arco-pagination {
margin-top: 16px !important;
margin-top: 1rem !important;
}
.arco-pagination-item {
@@ -188,21 +192,21 @@
.arco-modal-header {
border-bottom: 1px solid theme('colors.line / 60%') !important;
padding: 16px 24px !important;
padding: 1rem 1.5rem !important;
}
.arco-modal-title {
font-weight: 600 !important;
font-size: 16px !important;
font-size: 1rem !important;
}
.arco-modal-body {
padding: 24px !important;
padding: 1.5rem !important;
}
.arco-modal-footer {
border-top: 1px solid theme('colors.line / 60%') !important;
padding: 12px 24px !important;
padding: 0.75rem 1.5rem !important;
}
/* ── Input & Select ─────────────────────────────────── */
@@ -249,7 +253,7 @@
.arco-form-item-label {
font-weight: 500 !important;
color: theme('colors.secondary') !important;
font-size: 13px !important;
font-size: 0.8125rem !important;
}
/* ── Tag ────────────────────────────────────────────── */
@@ -271,7 +275,7 @@
/* ── Dropdown ───────────────────────────────────────── */
.arco-dropdown-menu {
padding: 4px !important;
padding: 0.25rem !important;
}
.arco-dropdown-menu-item {

View File

@@ -52,14 +52,14 @@ export function AdminLayout() {
<Layout className="h-screen bg-canvas">
<Sider
className="border-r border-line/40 bg-gradient-to-b from-white to-panel"
width={224}
width={"14rem"}
collapsed={collapsed}
collapsible
trigger={null}
>
<div className="flex h-[60px] items-center gap-2.5 px-5">
<div className="flex h-[3.75rem] items-center gap-2.5 px-5">
<span className="grid h-9 w-9 place-items-center rounded-xl bg-brand text-sm font-bold text-white shadow-md shadow-brand/25">TK</span>
{!collapsed && <span className="text-[15px] font-bold tracking-tight text-ink">TK短剧管理后台</span>}
{!collapsed && <span className="text-[0.9375rem] font-bold tracking-tight text-ink">TK短剧管理后台</span>}
</div>
<div className="mx-3 mb-2 h-px bg-line/50" />
<Menu
@@ -73,7 +73,7 @@ export function AdminLayout() {
</Menu>
</Sider>
<Layout className="flex flex-col overflow-hidden">
<Header className="flex h-[60px] shrink-0 items-center justify-between border-b border-line/40 bg-white/90 px-6 backdrop-blur-lg">
<Header className="flex h-[3.75rem] shrink-0 items-center justify-between border-b border-line/40 bg-white/90 px-6 backdrop-blur-lg">
<Space size={12}>
<Button
type="text"
@@ -82,7 +82,7 @@ export function AdminLayout() {
className="!text-muted hover:!text-ink !rounded-lg"
/>
<div className="flex items-center gap-2">
<strong className="text-[15px] font-semibold text-ink">{current?.item.label || "工作台"}</strong>
<strong className="text-[0.9375rem] font-semibold text-ink">{current?.item.label || "工作台"}</strong>
</div>
</Space>
<Dropdown
@@ -101,7 +101,7 @@ export function AdminLayout() {
>
<button className="inline-flex cursor-pointer items-center gap-2.5 rounded-xl border border-transparent bg-transparent px-3 py-2 text-ink transition-all duration-200 hover:bg-canvas/80" type="button">
<Avatar size={32} className="!bg-brand !text-white !text-[13px] !font-semibold !shadow-sm !shadow-brand/20">{profile?.username?.slice(0, 1).toUpperCase() || "A"}</Avatar>
<Text className="!text-[13px] !font-medium">{profile?.nickname || profile?.username || "管理员"}</Text>
<Text className="!text-[0.8125rem] !font-medium">{profile?.nickname || profile?.username || "管理员"}</Text>
</button>
</Dropdown>
</Header>