统一PC前端布局密度(05-28-pc-frontend-layout-unification)
这个提交包含在:
@@ -18,7 +18,7 @@ export function EntityOverviewHero({
|
||||
{subtitle ? <p className="workbench-hero__subtitle">{subtitle}</p> : null}
|
||||
{meta ? <div className="mt-3 flex flex-wrap gap-2">{meta}</div> : null}
|
||||
</div>
|
||||
{actions ? <div className="shrink-0 rounded-2xl border border-white/70 bg-white/55 p-2 shadow-sm backdrop-blur">{actions}</div> : null}
|
||||
{actions ? <div className="shrink-0 rounded-lg border border-white/70 bg-white/55 p-2 shadow-sm backdrop-blur">{actions}</div> : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,20 +21,20 @@ export function ListWorkspace({ className, children }: WorkspaceChildrenProps) {
|
||||
|
||||
export function WorkspaceHeader({ eyebrow, title, subtitle, meta, actions, className }: WorkspaceHeaderProps) {
|
||||
return (
|
||||
<header className={cn("flex flex-col gap-4 md:flex-row md:items-end md:justify-between", className)}>
|
||||
<header className={cn("flex flex-col gap-3 md:flex-row md:items-end md:justify-between", className)}>
|
||||
<div className="min-w-0 max-w-3xl">
|
||||
{eyebrow ? <div className="text-xs font-bold uppercase text-primary">{eyebrow}</div> : null}
|
||||
<h1 className="mt-1 text-2xl font-semibold text-[var(--color-text-primary)] md:text-3xl">{title}</h1>
|
||||
{subtitle ? <div className="mt-2 text-sm leading-6 text-[var(--color-text-secondary)]">{subtitle}</div> : null}
|
||||
{meta ? <div className="mt-3 flex flex-wrap gap-2">{meta}</div> : null}
|
||||
<h1 className="mt-1 text-2xl font-semibold text-[var(--color-text-primary)] md:text-[1.65rem]">{title}</h1>
|
||||
{subtitle ? <div className="mt-1.5 text-sm leading-6 text-[var(--color-text-secondary)]">{subtitle}</div> : null}
|
||||
{meta ? <div className="mt-2.5 flex flex-wrap gap-2">{meta}</div> : null}
|
||||
</div>
|
||||
{actions ? <div className="flex shrink-0 flex-wrap items-center gap-2">{actions}</div> : null}
|
||||
{actions ? <div className="flex shrink-0 flex-wrap items-center gap-2 md:justify-end">{actions}</div> : null}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export function FilterBar({ className, children }: WorkspaceChildrenProps) {
|
||||
return <section className={cn("card-std p-4 md:p-5", className)}>{children}</section>;
|
||||
return <section className={cn("card-std p-3 md:p-3.5", className)}>{children}</section>;
|
||||
}
|
||||
|
||||
export function FilterGrid({ className, children }: WorkspaceChildrenProps) {
|
||||
@@ -66,7 +66,7 @@ export function WorkspaceSection({
|
||||
actions?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className={cn("card-std space-y-4 p-4 md:p-5", className)}>
|
||||
<section className={cn("card-std space-y-3 p-3.5 md:p-4", className)}>
|
||||
{title || description || actions ? (
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
||||
<div className="min-w-0">
|
||||
@@ -86,7 +86,7 @@ export function ActionToolbar({ className, children }: WorkspaceChildrenProps) {
|
||||
}
|
||||
|
||||
export function PrimaryTable({ className, children }: WorkspaceChildrenProps) {
|
||||
return <section className={cn("table-container p-0", className)}>{children}</section>;
|
||||
return <section className={cn("table-container ops-table-focus p-0", className)}>{children}</section>;
|
||||
}
|
||||
|
||||
export function WorkspaceEmptyState({
|
||||
@@ -97,7 +97,7 @@ export function WorkspaceEmptyState({
|
||||
children?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className={cn("rounded-xl border border-dashed border-zinc-300 px-4 py-8 text-center text-sm text-zinc-500", className)}>
|
||||
<div className={cn("rounded-lg border border-dashed border-zinc-300 px-4 py-7 text-center text-sm text-zinc-500", className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@@ -123,7 +123,7 @@ export function WorkspaceNotice({
|
||||
}[tone];
|
||||
|
||||
return (
|
||||
<section className={cn("rounded-xl border px-4 py-3 text-sm", toneClass, className)}>
|
||||
<section className={cn("rounded-lg border px-4 py-3 text-sm", toneClass, className)}>
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
||||
<div className="min-w-0">
|
||||
<div className="font-semibold">{title}</div>
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
--color-info: #00398a;
|
||||
--radius-sm: 0.5rem;
|
||||
--radius-md: 0.75rem;
|
||||
--radius-lg: 1rem;
|
||||
--radius-xl: 1rem;
|
||||
--radius-lg: 0.75rem;
|
||||
--radius-xl: 0.875rem;
|
||||
--radius-2xl: 1.25rem;
|
||||
--radius-panel: 1rem;
|
||||
--radius-panel: 0.75rem;
|
||||
--shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
--shadow-sm: 0 4px 14px rgba(15, 23, 42, 0.055), 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||
--shadow-md: 0 14px 34px rgba(0, 57, 138, 0.10);
|
||||
|
||||
+38
-25
@@ -46,7 +46,7 @@
|
||||
|
||||
.page-scaffold {
|
||||
animation: page-reveal 320ms ease-out both;
|
||||
@apply space-y-5;
|
||||
@apply space-y-4 md:space-y-5;
|
||||
}
|
||||
|
||||
.auth-shell {
|
||||
@@ -119,7 +119,7 @@
|
||||
border: 1px solid rgba(226, 232, 240, 0.92);
|
||||
border-radius: var(--radius-panel);
|
||||
box-shadow: var(--shadow-xs);
|
||||
@apply flex flex-col gap-4 px-5 py-5 md:flex-row md:items-end md:justify-between md:px-6;
|
||||
@apply flex flex-col gap-4 px-4 py-4 md:flex-row md:items-end md:justify-between md:px-5 md:py-4;
|
||||
}
|
||||
|
||||
.ops-status-strip {
|
||||
@@ -129,7 +129,7 @@
|
||||
.ops-status-strip .metric-card {
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-xs);
|
||||
@apply px-3.5 py-3.5;
|
||||
@apply min-h-[92px] px-3.5 py-3;
|
||||
}
|
||||
|
||||
.ops-status-strip .metric-card::before {
|
||||
@@ -153,7 +153,7 @@
|
||||
border-radius: var(--radius-lg);
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
box-shadow: var(--shadow-xs);
|
||||
@apply p-3 md:p-3.5;
|
||||
@apply p-3;
|
||||
}
|
||||
|
||||
.ops-command-bar__row {
|
||||
@@ -168,6 +168,11 @@
|
||||
@apply flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end;
|
||||
}
|
||||
|
||||
.ops-command-bar__actions > .select-sm,
|
||||
.ops-command-bar__actions > .select-std {
|
||||
min-width: 9rem;
|
||||
}
|
||||
|
||||
.ops-filter-label {
|
||||
color: var(--color-text-secondary);
|
||||
@apply text-xs font-semibold;
|
||||
@@ -181,6 +186,8 @@
|
||||
.ops-table-focus {
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-xs);
|
||||
border: 1px solid rgba(226, 232, 240, 0.94);
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
}
|
||||
|
||||
.ops-table-focus table {
|
||||
@@ -195,13 +202,13 @@
|
||||
.ops-table-focus th,
|
||||
.ops-table-focus .table-th {
|
||||
color: var(--color-text-tertiary);
|
||||
@apply px-4 py-3 text-xs font-semibold;
|
||||
@apply px-3.5 py-2.5 text-xs font-semibold;
|
||||
}
|
||||
|
||||
.ops-table-focus td,
|
||||
.ops-table-focus .table-td,
|
||||
.ops-table-focus .table-td-strong {
|
||||
@apply px-4 py-3 text-sm;
|
||||
@apply px-3.5 py-2.5 text-sm;
|
||||
}
|
||||
|
||||
.ops-table-focus tbody tr:hover {
|
||||
@@ -238,7 +245,7 @@
|
||||
}
|
||||
|
||||
.workbench-hero--compact {
|
||||
@apply gap-4 px-5 py-5 md:px-6 md:py-6;
|
||||
@apply gap-4 px-4 py-4 md:px-5 md:py-5;
|
||||
}
|
||||
|
||||
.workbench-hero--compact .workbench-hero__title {
|
||||
@@ -275,7 +282,7 @@
|
||||
.section-panel {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@apply p-4 md:p-5;
|
||||
@apply p-3.5 md:p-4;
|
||||
}
|
||||
|
||||
.section-panel::before {
|
||||
@@ -311,7 +318,7 @@
|
||||
border: 1px solid rgba(226, 232, 240, 0.96);
|
||||
border-radius: var(--radius-panel);
|
||||
box-shadow: var(--shadow-xs);
|
||||
@apply block px-4 py-4 transition duration-150 hover:border-slate-300 hover:bg-white;
|
||||
@apply block px-3.5 py-3.5 transition duration-150 hover:border-slate-300 hover:bg-white;
|
||||
}
|
||||
|
||||
.metric-card::before {
|
||||
@@ -348,12 +355,12 @@
|
||||
|
||||
.metric-card__value {
|
||||
color: var(--color-text-primary);
|
||||
@apply mt-2 text-xl font-semibold tracking-tight;
|
||||
@apply mt-1.5 text-xl font-semibold tracking-tight;
|
||||
}
|
||||
|
||||
.metric-card__note {
|
||||
color: var(--color-text-secondary);
|
||||
@apply mt-2 text-sm leading-6;
|
||||
@apply mt-1.5 text-sm leading-6;
|
||||
}
|
||||
|
||||
.info-chip {
|
||||
@@ -393,7 +400,7 @@
|
||||
.table-container {
|
||||
border-color: rgba(226, 232, 240, 0.92);
|
||||
background: var(--color-bg-surface-raised);
|
||||
@apply overflow-hidden rounded-2xl border shadow-sm backdrop-blur;
|
||||
@apply overflow-hidden rounded-lg border shadow-sm backdrop-blur;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
@@ -537,7 +544,7 @@
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(248, 250, 252, 0.88));
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-xs);
|
||||
@apply border p-4;
|
||||
@apply min-h-[92px] border p-3.5;
|
||||
}
|
||||
|
||||
.finance-metric-card__label {
|
||||
@@ -547,7 +554,7 @@
|
||||
|
||||
.finance-metric-card__value {
|
||||
color: var(--color-text-primary);
|
||||
@apply mt-2 text-2xl font-bold;
|
||||
@apply mt-1.5 text-xl font-semibold;
|
||||
}
|
||||
|
||||
.finance-workspace-grid {
|
||||
@@ -559,7 +566,7 @@
|
||||
background: rgba(255, 255, 255, 0.97);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-xs);
|
||||
@apply border p-4 md:p-5;
|
||||
@apply border p-3.5 md:p-4;
|
||||
}
|
||||
|
||||
.finance-list-card {
|
||||
@@ -580,11 +587,11 @@
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(248, 250, 252, 0.9));
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-xs);
|
||||
@apply border p-4 md:p-5;
|
||||
@apply border p-3.5 md:p-4;
|
||||
}
|
||||
|
||||
.admin-tile-grid {
|
||||
@apply grid gap-4 md:grid-cols-2 xl:grid-cols-3;
|
||||
@apply grid gap-3 md:grid-cols-2 xl:grid-cols-4;
|
||||
}
|
||||
|
||||
.admin-tile-card {
|
||||
@@ -592,7 +599,13 @@
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-xs);
|
||||
@apply border p-4 transition-colors hover:border-slate-300 hover:bg-slate-50/60 md:p-5;
|
||||
@apply border p-3.5 transition-colors hover:border-slate-300 hover:bg-slate-50/60 md:p-4;
|
||||
}
|
||||
|
||||
.admin-tile-card__icon {
|
||||
background: rgba(0, 57, 138, 0.08);
|
||||
color: var(--color-accent-primary);
|
||||
@apply flex size-9 shrink-0 items-center justify-center rounded-lg;
|
||||
}
|
||||
|
||||
.admin-workspace-grid {
|
||||
@@ -622,11 +635,11 @@
|
||||
}
|
||||
|
||||
.portal-home-grid {
|
||||
@apply grid gap-5 xl:grid-cols-[minmax(0,2fr),minmax(320px,0.92fr)] xl:items-stretch;
|
||||
@apply grid gap-4 xl:grid-cols-[minmax(0,1.72fr),minmax(300px,0.86fr)] xl:items-stretch;
|
||||
}
|
||||
|
||||
.portal-home-grid--collaboration {
|
||||
@apply xl:grid-cols-[minmax(0,1.55fr),minmax(320px,0.9fr)];
|
||||
@apply xl:grid-cols-[minmax(0,1.42fr),minmax(300px,0.92fr)];
|
||||
}
|
||||
|
||||
.portal-panel-fill {
|
||||
@@ -638,7 +651,7 @@
|
||||
border: 1px solid rgba(226, 232, 240, 0.94);
|
||||
border-radius: var(--radius-sm);
|
||||
box-shadow: var(--shadow-xs);
|
||||
@apply block px-3.5 py-3 transition duration-150;
|
||||
@apply block px-3 py-2.5 transition duration-150;
|
||||
}
|
||||
|
||||
.portal-list-card:hover {
|
||||
@@ -666,7 +679,7 @@
|
||||
border: 1px solid rgba(226, 232, 240, 0.94);
|
||||
border-radius: var(--radius-sm);
|
||||
box-shadow: var(--shadow-xs);
|
||||
@apply px-3 py-3 text-left transition duration-150;
|
||||
@apply px-3 py-2.5 text-left transition duration-150;
|
||||
}
|
||||
|
||||
.portal-action-tile:hover {
|
||||
@@ -700,7 +713,7 @@
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
border: 1px solid rgba(226, 232, 240, 0.94);
|
||||
border-radius: var(--radius-sm);
|
||||
@apply px-3.5 py-3;
|
||||
@apply px-3 py-2.5;
|
||||
}
|
||||
|
||||
.portal-team-list {
|
||||
@@ -728,7 +741,7 @@
|
||||
}
|
||||
|
||||
.portal-secondary-drawer {
|
||||
@apply p-3.5 md:p-4;
|
||||
@apply p-3 md:p-3.5;
|
||||
}
|
||||
|
||||
.portal-secondary-summary {
|
||||
@@ -749,7 +762,7 @@
|
||||
}
|
||||
|
||||
.portal-compact-group {
|
||||
@apply rounded-lg border border-slate-200 bg-white/80 px-3.5 py-3;
|
||||
@apply rounded-lg border border-slate-200 bg-white/80 px-3 py-2.5;
|
||||
}
|
||||
|
||||
.portal-compact-group__title {
|
||||
|
||||
@@ -685,7 +685,7 @@ export function FeatureFlagsPage() {
|
||||
</p>
|
||||
) : null}
|
||||
{!impactQuery.isLoading && !impactQuery.isError && impactQuery.data ? (
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-semibold text-zinc-900">前端导航</h4>
|
||||
{impactQuery.data.navigationItems.length ? (
|
||||
@@ -793,7 +793,7 @@ export function FeatureFlagsPage() {
|
||||
</Dialog>
|
||||
|
||||
{batchMode ? (
|
||||
<div className="fixed bottom-6 left-1/2 z-50 w-[min(960px,calc(100vw-2rem))] -translate-x-1/2 rounded-2xl border border-zinc-200 bg-white/95 p-4 shadow-xl backdrop-blur">
|
||||
<div className="fixed bottom-6 left-1/2 z-50 w-[min(960px,calc(100vw-2rem))] -translate-x-1/2 rounded-lg border border-zinc-200 bg-white/95 p-3.5 shadow-xl backdrop-blur">
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<div className="text-sm text-zinc-700">
|
||||
已选择 <span className="font-semibold text-zinc-900">{selectedItems.length}</span> 个功能开关
|
||||
|
||||
@@ -341,7 +341,7 @@ export function MasterDataPage() {
|
||||
<TabsTrigger value="suppliers">供应商</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="legal-entities" className="space-y-6">
|
||||
<TabsContent value="legal-entities" className="space-y-4">
|
||||
<FilterBar className="ops-command-bar !p-3 md:!p-3.5">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<h3 className="text-base font-semibold text-zinc-900">法体筛选</h3>
|
||||
@@ -454,7 +454,7 @@ export function MasterDataPage() {
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="cost-centers" className="space-y-6">
|
||||
<TabsContent value="cost-centers" className="space-y-4">
|
||||
<FilterBar className="ops-command-bar !p-3 md:!p-3.5">
|
||||
<div className="grid gap-3 md:grid-cols-3">
|
||||
<select
|
||||
@@ -613,7 +613,7 @@ export function MasterDataPage() {
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="document-categories" className="space-y-6">
|
||||
<TabsContent value="document-categories" className="space-y-4">
|
||||
<FilterBar className="ops-command-bar !p-3 md:!p-3.5">
|
||||
<div className="grid gap-3 md:grid-cols-3">
|
||||
<select
|
||||
@@ -780,14 +780,14 @@ export function MasterDataPage() {
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="holiday-calendars" className="space-y-6">
|
||||
<TabsContent value="holiday-calendars" className="space-y-4">
|
||||
<HolidayCalendarTab
|
||||
canManage={canManage}
|
||||
includeArchived={includeArchived}
|
||||
legalEntities={legalEntities}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="suppliers" className="space-y-6">
|
||||
<TabsContent value="suppliers" className="space-y-4">
|
||||
<SupplierTab
|
||||
canManage={canManage}
|
||||
includeArchived={includeArchived}
|
||||
|
||||
@@ -535,7 +535,7 @@ export function NotificationChannelsPage() {
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6 xl:grid-cols-[minmax(0,2fr)_minmax(320px,1fr)]">
|
||||
<div className="grid gap-4 xl:grid-cols-[minmax(0,2fr)_minmax(320px,1fr)]">
|
||||
<div className="admin-command-panel space-y-4">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
|
||||
@@ -524,7 +524,7 @@ function AdoptionSummaryPanel({ adoptionSummary }: { adoptionSummary: NonNullabl
|
||||
<div className="mt-2 text-2xl font-semibold text-zinc-900">{adoptionSummary.announcementCount}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-4 text-sm text-slate-600">
|
||||
<div className="mt-4 rounded-lg border border-slate-200 bg-slate-50 px-3.5 py-3 text-sm text-slate-600">
|
||||
<div className="font-medium text-zinc-900">本周观测提示</div>
|
||||
<ul className="mt-2 list-disc space-y-1 pl-5">
|
||||
{adoptionSummary.hints.map((hint) => (
|
||||
@@ -589,7 +589,7 @@ function GettingStartedCard({
|
||||
return (
|
||||
<SectionPanel
|
||||
title="快速上手"
|
||||
description="基础资料"
|
||||
description="基础设置"
|
||||
actions={(
|
||||
<button
|
||||
type="button"
|
||||
@@ -622,7 +622,7 @@ function GettingStartedCard({
|
||||
</Link>
|
||||
))}
|
||||
|
||||
<div className="rounded-2xl border border-slate-200 bg-slate-50 px-4 py-4">
|
||||
<div className="rounded-lg border border-slate-200 bg-slate-50 px-3.5 py-3">
|
||||
<div className="flex items-center justify-between gap-3 text-sm">
|
||||
<span className="font-medium text-zinc-900">完成进度</span>
|
||||
<span className="text-zinc-500">{completedCount}/{items.length}</span>
|
||||
@@ -726,10 +726,10 @@ function TodoAggregatePanel({ items }: { items: TodoAggregateItem[] }) {
|
||||
<Link key={`${item.title}-${item.href}-${item.sourceLabel}`} to={item.href} className="portal-list-card">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className={item.severity === "high"
|
||||
? "mt-0.5 flex size-9 items-center justify-center rounded-2xl bg-red-50 text-red-600"
|
||||
? "mt-0.5 flex size-9 items-center justify-center rounded-lg bg-red-50 text-red-600"
|
||||
: item.severity === "warning"
|
||||
? "mt-0.5 flex size-9 items-center justify-center rounded-2xl bg-amber-50 text-amber-600"
|
||||
: "mt-0.5 flex size-9 items-center justify-center rounded-2xl bg-zinc-100 text-zinc-600"}
|
||||
? "mt-0.5 flex size-9 items-center justify-center rounded-lg bg-amber-50 text-amber-600"
|
||||
: "mt-0.5 flex size-9 items-center justify-center rounded-lg bg-zinc-100 text-zinc-600"}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[18px]">{item.icon}</span>
|
||||
</div>
|
||||
@@ -896,14 +896,14 @@ export function PortalHomePage() {
|
||||
const gettingStartedCompletedCount = gettingStartedItems.filter((item) => item.completed).length;
|
||||
|
||||
if (portalQuery.isLoading) {
|
||||
return <div className="card-std p-6 text-sm text-zinc-500">加载工作台中...</div>;
|
||||
return <div className="card-std p-5 text-sm text-zinc-500">加载工作台中...</div>;
|
||||
}
|
||||
|
||||
if (portalQuery.isError || !portalQuery.data) {
|
||||
const message = isApiError(portalQuery.error)
|
||||
? `${portalQuery.error.code}: ${mapApiErrorMessage(portalQuery.error.code, portalQuery.error.details, portalQuery.error.message)}`
|
||||
: "工作台加载失败";
|
||||
return <div className="card-std p-6 text-sm text-red-600">{message}</div>;
|
||||
return <div className="card-std p-5 text-sm text-red-600">{message}</div>;
|
||||
}
|
||||
|
||||
const payload = portalQuery.data;
|
||||
@@ -1096,8 +1096,8 @@ export function PortalHomePage() {
|
||||
quickActions.push({
|
||||
key: "attendance",
|
||||
icon: "schedule",
|
||||
label: "考勤汇总",
|
||||
description: "企微摘要 / OA 修正",
|
||||
label: "考勤",
|
||||
description: "企微摘要",
|
||||
to: "/attendance",
|
||||
});
|
||||
if (payload.weeklyReport || payload.weeklySummary) {
|
||||
@@ -1105,7 +1105,7 @@ export function PortalHomePage() {
|
||||
key: "weekly",
|
||||
icon: "article",
|
||||
label: "提交周报",
|
||||
description: "填写 / 查看缺口",
|
||||
description: "填写 / 缺口",
|
||||
to: "/weekly-reports",
|
||||
});
|
||||
}
|
||||
@@ -1161,7 +1161,7 @@ export function PortalHomePage() {
|
||||
fallback={(
|
||||
<PortalSectionFallback
|
||||
title="快速上手"
|
||||
description="基础资料"
|
||||
description="基础设置"
|
||||
/>
|
||||
)}
|
||||
>
|
||||
@@ -1205,9 +1205,9 @@ export function PortalHomePage() {
|
||||
<>
|
||||
<SecondaryWorkspacePanel
|
||||
title="角色重点"
|
||||
description="常用入口"
|
||||
description="入口"
|
||||
>
|
||||
<div className="grid gap-6 xl:grid-cols-[minmax(0,1fr),minmax(0,1fr)] 2xl:grid-cols-[minmax(0,1fr),minmax(0,1fr),360px]">
|
||||
<div className="grid gap-4 xl:grid-cols-[minmax(0,1fr),minmax(0,1fr)] 2xl:grid-cols-[minmax(0,1fr),minmax(0,1fr),340px]">
|
||||
{roleCapabilities.canApproveTasks ? (
|
||||
<SummaryPanel title="审批处理" items={approvalSummaryItems} />
|
||||
) : null}
|
||||
@@ -1243,7 +1243,7 @@ export function PortalHomePage() {
|
||||
) : (
|
||||
<SecondaryWorkspacePanel
|
||||
title="更多入口"
|
||||
description="申请、资产、最近访问和项目指标"
|
||||
description="申请、资产、最近访问"
|
||||
>
|
||||
<div className="portal-secondary-compact-grid">
|
||||
<CompactSummaryLinks title="我的申请与资产" items={employeeSummaryItems} />
|
||||
|
||||
@@ -740,7 +740,7 @@ export function ReimbursementDetailPage() {
|
||||
|
||||
if (!reimbursementId) {
|
||||
return (
|
||||
<div className="card-std p-6">
|
||||
<div className="card-std p-5">
|
||||
<p className="text-sm text-red-600">缺少 reimbursementId 参数。</p>
|
||||
</div>
|
||||
);
|
||||
@@ -760,7 +760,7 @@ export function ReimbursementDetailPage() {
|
||||
: "加载报销详情失败";
|
||||
|
||||
return (
|
||||
<div className="card-std space-y-3 p-6">
|
||||
<div className="card-std space-y-3 p-5">
|
||||
<p className="text-sm text-red-600">{message}</p>
|
||||
<Button asChild variant="secondary">
|
||||
<Link to="/reimbursements">返回报销列表</Link>
|
||||
@@ -1230,7 +1230,7 @@ export function ReimbursementDetailPage() {
|
||||
</SectionPanel>
|
||||
|
||||
<SectionPanel title="明细与票据">
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-4" data-testid="reimbursement-items">
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-1">
|
||||
@@ -1465,7 +1465,7 @@ export function ReimbursementDetailPage() {
|
||||
</SectionPanel>
|
||||
|
||||
<SectionPanel title="预算与审核提示">
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
{budgetHint ? (
|
||||
<div className="detail-command-panel text-sm" data-testid="reimbursement-budget-hint">
|
||||
<h3 className="text-base font-semibold text-zinc-900">预算提示</h3>
|
||||
|
||||
@@ -838,7 +838,7 @@ export function ReimbursementsPage() {
|
||||
|
||||
{batchMode ? (
|
||||
<div className="fixed bottom-6 left-1/2 z-40 w-[min(760px,calc(100vw-2rem))] -translate-x-1/2">
|
||||
<div className="rounded-2xl border border-zinc-200 bg-white p-4 shadow-lg">
|
||||
<div className="rounded-lg border border-zinc-200 bg-white p-3.5 shadow-lg">
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<div className="text-sm text-zinc-700">已选 {selectedItems.length} 项可处理报销待办</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
@@ -869,7 +869,7 @@ export function ReimbursementsPage() {
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-2xl border border-zinc-200 bg-zinc-50 p-4 text-sm text-zinc-700">
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-3.5 text-sm text-zinc-700">
|
||||
共 {selectedItems.length} 项:
|
||||
<div className="mt-2 max-h-40 space-y-1 overflow-auto">
|
||||
{selectedItems.map((item) => (
|
||||
@@ -923,9 +923,9 @@ export function ReimbursementsPage() {
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="max-h-[60vh] space-y-2 overflow-auto rounded-2xl border border-zinc-200 bg-zinc-50 p-4">
|
||||
<div className="max-h-[60vh] space-y-2 overflow-auto rounded-lg border border-zinc-200 bg-zinc-50 p-3.5">
|
||||
{batchResult?.results.map((item) => (
|
||||
<div key={`${item.taskId}-${item.outcome}`} className="rounded-xl border border-zinc-200 bg-white px-4 py-3">
|
||||
<div key={`${item.taskId}-${item.outcome}`} className="rounded-lg border border-zinc-200 bg-white px-4 py-3">
|
||||
<div className="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
|
||||
<div>
|
||||
<div className="font-medium text-zinc-900">
|
||||
|
||||
@@ -2,14 +2,14 @@ import { Link } from "react-router-dom";
|
||||
import { ListWorkspace, WorkspaceHeader } from "../components/patterns/ListWorkspace";
|
||||
|
||||
const ITEMS = [
|
||||
{ to: "/system/workspace", title: "工作区设置", description: "调整小团队模式的导航与模块显隐。" },
|
||||
{ to: "/people", title: "人员管理", description: "处理入职、转正、调岗、离职与档案。" },
|
||||
{ to: "/system/approval-rules", title: "审批规则", description: "维护仍在使用的简单审批链与规则。" },
|
||||
{ to: "/system/expense-standards", title: "费用标准", description: "维护报销费用类型上限与 warn/block 管控模式。" },
|
||||
{ to: "/system/reimbursement-templates", title: "报销模板", description: "维护差旅费、招待费、办公用品等常见报销模板。" },
|
||||
{ to: "/system/master-data", title: "主数据", description: "维护费用、法体、成本中心等主数据。" },
|
||||
{ to: "/system/notification-channels", title: "通知渠道", description: "继续保留 IM/邮件等补充通知渠道。" },
|
||||
{ to: "/system/integrations/erp", title: "外部系统桥接", description: "查看财务与外部系统桥接能力及出入口。" },
|
||||
{ to: "/system/workspace", icon: "dashboard_customize", title: "工作区设置", description: "导航与模块显隐。" },
|
||||
{ to: "/people", icon: "badge", title: "人员管理", description: "入转调离与档案。" },
|
||||
{ to: "/system/approval-rules", icon: "account_tree", title: "审批规则", description: "审批链与规则。" },
|
||||
{ to: "/system/expense-standards", icon: "rule_settings", title: "费用标准", description: "费用上限与管控。" },
|
||||
{ to: "/system/reimbursement-templates", icon: "receipt_long", title: "报销模板", description: "常见报销模板。" },
|
||||
{ to: "/system/master-data", icon: "database", title: "主数据", description: "法体、成本中心、供应商。" },
|
||||
{ to: "/system/notification-channels", icon: "notifications", title: "通知渠道", description: "IM / 邮件补充通知。" },
|
||||
{ to: "/system/integrations/erp", icon: "hub", title: "外部系统桥接", description: "财务出入口与桥接。" },
|
||||
];
|
||||
|
||||
export function SystemSettingsPage() {
|
||||
@@ -26,8 +26,15 @@ export function SystemSettingsPage() {
|
||||
<div className="admin-tile-grid">
|
||||
{ITEMS.map((item) => (
|
||||
<Link key={item.to} to={item.to} className="admin-tile-card">
|
||||
<div className="text-base font-semibold text-zinc-900">{item.title}</div>
|
||||
<div className="mt-2 text-sm text-zinc-600">{item.description}</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="admin-tile-card__icon">
|
||||
<span className="material-symbols-outlined text-[20px]">{item.icon}</span>
|
||||
</span>
|
||||
<span className="min-w-0">
|
||||
<span className="block text-base font-semibold text-zinc-900">{item.title}</span>
|
||||
<span className="mt-1 block text-sm leading-5 text-zinc-600">{item.description}</span>
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -216,7 +216,7 @@ export function WorkflowTaskDetailPage() {
|
||||
|
||||
if (!taskId) {
|
||||
return (
|
||||
<div className="card-std p-6">
|
||||
<div className="card-std p-5">
|
||||
<p className="text-sm text-red-600">缺少 taskId 参数。</p>
|
||||
</div>
|
||||
);
|
||||
@@ -240,7 +240,7 @@ export function WorkflowTaskDetailPage() {
|
||||
: "加载失败,请返回列表重试";
|
||||
|
||||
return (
|
||||
<div className="card-std space-y-3 p-6">
|
||||
<div className="card-std space-y-3 p-5">
|
||||
<p className="text-sm text-red-600">{message}</p>
|
||||
<Button
|
||||
variant="secondary"
|
||||
|
||||
在新工单中引用
屏蔽一个用户