feat: 收集箱右侧区域布局优化——上下两段式,操作栏固定底部始终显示

This commit is contained in:
2026-06-08 10:51:17 +08:00
parent 2713897bdb
commit b4e05a4a16
4 changed files with 73 additions and 61 deletions

View File

@@ -1,22 +1,14 @@
import { Card, Descriptions, Tag, Typography } from "antd";
import type { Material, MaterialStatus, MaterialType } from "../types";
import type { Material, MaterialType } from "../types";
import { formatRelativeTime } from "../../../shared/utils/time";
import { STATUS_MAP } from "./constants";
interface MaterialContentProps {
material: Material;
}
const STATUS_MAP: Record<MaterialStatus, { color: string; label: string }> = {
approved: { color: "green", label: "已通过" },
discarded: { color: "red", label: "已放弃" },
failed: { color: "magenta", label: "失败" },
pending: { color: "gold", label: "待处理" },
processing: { color: "blue", label: "处理中" },
review: { color: "orange", label: "待审核" },
};
const MATERIAL_TYPE_LABELS: Record<MaterialType, string> = {
general: "通用",
meeting: "会议",
@@ -27,7 +19,7 @@ export function MaterialContent({ material }: MaterialContentProps) {
const typeLabel = MATERIAL_TYPE_LABELS[material.materialType] ?? material.materialType;
return (
<div className="app-inbox-content">
<>
<Typography.Title level={4}></Typography.Title>
<Card>
<Typography.Paragraph>{material.description}</Typography.Paragraph>
@@ -52,6 +44,6 @@ export function MaterialContent({ material }: MaterialContentProps) {
<Descriptions.Item label="创建时间">{formatRelativeTime(material.createdAt)}</Descriptions.Item>
</Descriptions>
</Card>
</div>
</>
);
}

View File

@@ -1,9 +1,8 @@
import { CheckOutlined, CloseOutlined, RedoOutlined } from "@ant-design/icons";
import { App as AntApp, Button, Empty, Result, Space, Spin } from "antd";
import type { Material } from "../types";
import { App as AntApp, Button, Empty, Result, Space, Spin, Tag } from "antd";
import { useMaterial } from "../../../shared/hooks/use-materials";
import { STATUS_MAP } from "./constants";
import { MaterialContent } from "./MaterialContent";
interface MaterialDetailPanelProps {
@@ -23,8 +22,10 @@ export function MaterialDetailPanel({
}: MaterialDetailPanelProps) {
if (!materialId) {
return (
<div className="app-inbox-content">
<Empty description="请在左侧选择素材" />
<div className="app-inbox-panel">
<div className="app-inbox-content">
<Empty description="请在左侧选择素材" />
</div>
</div>
);
}
@@ -46,24 +47,30 @@ function MaterialDetailPanelInner({ materialId, onApprove, onDiscard, onRetry, p
if (isLoading) {
return (
<div className="app-inbox-content">
<Spin />
<div className="app-inbox-panel">
<div className="app-inbox-content">
<Spin />
</div>
</div>
);
}
if (error) {
return (
<div className="app-inbox-content">
<Result subTitle="加载素材详情失败" />
<div className="app-inbox-panel">
<div className="app-inbox-content">
<Result subTitle="加载素材详情失败" />
</div>
</div>
);
}
if (!data || !materialId) {
return (
<div className="app-inbox-content">
<Empty description="请在左侧选择素材" />
<div className="app-inbox-panel">
<div className="app-inbox-content">
<Empty description="请在左侧选择素材" />
</div>
</div>
);
}
@@ -97,44 +104,32 @@ function MaterialDetailPanelInner({ materialId, onApprove, onDiscard, onRetry, p
}
};
const statusInfo = STATUS_MAP[data.status] ?? { color: "default", label: data.status };
return (
<div className="app-inbox-content">
<MaterialContent material={data} />
<ActionButtons material={data} onApprove={handleApprove} onDiscard={handleDiscard} onRetry={handleRetry} />
<div className="app-inbox-panel">
<div className="app-inbox-content">
<MaterialContent material={data} />
</div>
<div className="app-inbox-action-bar">
<Tag color={statusInfo.color}>{statusInfo.label}</Tag>
{data.status === "review" ? (
<Space>
<Button icon={<CheckOutlined />} onClick={() => void handleApprove()} type="primary">
</Button>
<Button danger icon={<CloseOutlined />} onClick={() => void handleDiscard()}>
</Button>
</Space>
) : data.status === "failed" ? (
<Space>
<Button icon={<RedoOutlined />} onClick={() => void handleRetry()}>
</Button>
</Space>
) : null}
</div>
</div>
);
}
interface ActionButtonsProps {
material: Material;
onApprove: () => Promise<void>;
onDiscard: () => Promise<void>;
onRetry: () => Promise<void>;
}
function ActionButtons({ material, onApprove, onDiscard, onRetry }: ActionButtonsProps) {
if (material.status === "review") {
return (
<Space style={{ marginTop: 16 }}>
<Button icon={<CheckOutlined />} onClick={() => void onApprove()} type="primary">
</Button>
<Button danger icon={<CloseOutlined />} onClick={() => void onDiscard()}>
</Button>
</Space>
);
}
if (material.status === "failed") {
return (
<Space style={{ marginTop: 16 }}>
<Button icon={<RedoOutlined />} onClick={() => void onRetry()}>
</Button>
</Space>
);
}
return null;
}

View File

@@ -0,0 +1,10 @@
import type { MaterialStatus } from "../types";
export const STATUS_MAP: Record<MaterialStatus, { color: string; label: string }> = {
approved: { color: "green", label: "已通过" },
discarded: { color: "red", label: "已放弃" },
failed: { color: "magenta", label: "失败" },
pending: { color: "gold", label: "待处理" },
processing: { color: "blue", label: "处理中" },
review: { color: "orange", label: "待审核" },
};

View File

@@ -338,16 +338,31 @@ body {
overflow: hidden;
}
.app-inbox-content {
.app-inbox-panel {
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
min-width: 0;
}
.app-inbox-content {
flex: 1;
min-height: 0;
padding: var(--ant-padding-xl);
overflow-y: auto;
}
.app-inbox-action-bar {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: space-between;
padding: var(--ant-padding-sm) var(--ant-padding-xl);
border-top: 1px solid var(--ant-color-border-secondary);
background: var(--ant-color-bg-container);
}
.app-inbox-datepicker {
width: 100%;
}