style: 收集箱布局深化——卡片流、OS滚动、操作区fill按钮
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Card, Descriptions, Tag, Typography } from "antd";
|
||||
import { Card, Descriptions, Flex, Tag, Typography } from "antd";
|
||||
|
||||
import type { Material, MaterialType } from "../types";
|
||||
|
||||
@@ -19,31 +19,32 @@ export function MaterialContent({ material }: MaterialContentProps) {
|
||||
const typeLabel = MATERIAL_TYPE_LABELS[material.materialType] ?? material.materialType;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography.Title level={4}>素材详情</Typography.Title>
|
||||
<Card>
|
||||
<Typography.Paragraph>{material.description}</Typography.Paragraph>
|
||||
{material.processedContent && (
|
||||
<Typography.Paragraph
|
||||
style={{
|
||||
background: "var(--ant-color-fill-quaternary)",
|
||||
padding: 12,
|
||||
borderRadius: 6,
|
||||
whiteSpace: "pre-wrap",
|
||||
}}
|
||||
>
|
||||
{material.processedContent}
|
||||
</Typography.Paragraph>
|
||||
)}
|
||||
<Descriptions column={1} size="small">
|
||||
<Descriptions.Item label="状态">
|
||||
<Tag color={statusInfo.color}>{statusInfo.label}</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="素材类型">{typeLabel}</Descriptions.Item>
|
||||
<Descriptions.Item label="关联时间">{material.associatedDate}</Descriptions.Item>
|
||||
<Descriptions.Item label="创建时间">{formatRelativeTime(material.createdAt)}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Flex gap={12} vertical>
|
||||
<Card size="small" title="基本信息">
|
||||
<Flex gap={12} vertical>
|
||||
<Typography.Paragraph>{material.description}</Typography.Paragraph>
|
||||
{material.processedContent && (
|
||||
<Typography.Paragraph
|
||||
style={{
|
||||
background: "var(--ant-color-fill-quaternary)",
|
||||
padding: 12,
|
||||
borderRadius: 6,
|
||||
whiteSpace: "pre-wrap",
|
||||
}}
|
||||
>
|
||||
{material.processedContent}
|
||||
</Typography.Paragraph>
|
||||
)}
|
||||
<Descriptions column={1} size="small">
|
||||
<Descriptions.Item label="状态">
|
||||
<Tag color={statusInfo.color}>{statusInfo.label}</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="素材类型">{typeLabel}</Descriptions.Item>
|
||||
<Descriptions.Item label="关联时间">{material.associatedDate}</Descriptions.Item>
|
||||
<Descriptions.Item label="创建时间">{formatRelativeTime(material.createdAt)}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Flex>
|
||||
</Card>
|
||||
</>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { CheckOutlined, CloseOutlined, RedoOutlined } from "@ant-design/icons";
|
||||
import { App as AntApp, Button, Empty, Result, Space, Spin, Tag } from "antd";
|
||||
import "overlayscrollbars/styles/overlayscrollbars.css";
|
||||
import { App as AntApp, Button, Empty, Result, Space, Spin, Typography } from "antd";
|
||||
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
|
||||
|
||||
import { useMaterial } from "../../../shared/hooks/use-materials";
|
||||
import { STATUS_MAP } from "./constants";
|
||||
import { MaterialContent } from "./MaterialContent";
|
||||
|
||||
const OS_OPTIONS = {
|
||||
overflow: { x: "hidden", y: "scroll" },
|
||||
scrollbars: { autoHide: "move", theme: "os-theme-custom" },
|
||||
} as const;
|
||||
|
||||
interface MaterialDetailPanelProps {
|
||||
materialId: null | string;
|
||||
onApprove: (materialId: string) => Promise<void>;
|
||||
@@ -23,8 +29,13 @@ export function MaterialDetailPanel({
|
||||
if (!materialId) {
|
||||
return (
|
||||
<div className="app-inbox-panel">
|
||||
<div className="app-inbox-content">
|
||||
<OverlayScrollbarsComponent className="app-inbox-content" options={OS_OPTIONS}>
|
||||
<Empty description="请在左侧选择素材" />
|
||||
</OverlayScrollbarsComponent>
|
||||
<div className="app-inbox-action-bar">
|
||||
<Typography.Text style={{ flex: 1, textAlign: "center", color: "var(--ant-color-text-tertiary)" }}>
|
||||
请先选择素材
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -48,9 +59,9 @@ function MaterialDetailPanelInner({ materialId, onApprove, onDiscard, onRetry, p
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="app-inbox-panel">
|
||||
<div className="app-inbox-content">
|
||||
<OverlayScrollbarsComponent className="app-inbox-content" options={OS_OPTIONS}>
|
||||
<Spin />
|
||||
</div>
|
||||
</OverlayScrollbarsComponent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -58,9 +69,9 @@ function MaterialDetailPanelInner({ materialId, onApprove, onDiscard, onRetry, p
|
||||
if (error) {
|
||||
return (
|
||||
<div className="app-inbox-panel">
|
||||
<div className="app-inbox-content">
|
||||
<OverlayScrollbarsComponent className="app-inbox-content" options={OS_OPTIONS}>
|
||||
<Result subTitle="加载素材详情失败" />
|
||||
</div>
|
||||
</OverlayScrollbarsComponent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -68,9 +79,9 @@ function MaterialDetailPanelInner({ materialId, onApprove, onDiscard, onRetry, p
|
||||
if (!data || !materialId) {
|
||||
return (
|
||||
<div className="app-inbox-panel">
|
||||
<div className="app-inbox-content">
|
||||
<OverlayScrollbarsComponent className="app-inbox-content" options={OS_OPTIONS}>
|
||||
<Empty description="请在左侧选择素材" />
|
||||
</div>
|
||||
</OverlayScrollbarsComponent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -104,31 +115,39 @@ function MaterialDetailPanelInner({ materialId, onApprove, onDiscard, onRetry, p
|
||||
}
|
||||
};
|
||||
|
||||
const statusInfo = STATUS_MAP[data.status] ?? { color: "default", label: data.status };
|
||||
|
||||
return (
|
||||
<div className="app-inbox-panel">
|
||||
<div className="app-inbox-content">
|
||||
<OverlayScrollbarsComponent className="app-inbox-content" options={OS_OPTIONS}>
|
||||
<MaterialContent material={data} />
|
||||
</div>
|
||||
</OverlayScrollbarsComponent>
|
||||
<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}
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={data.status !== "review"}
|
||||
icon={<CheckOutlined />}
|
||||
onClick={() => void handleApprove()}
|
||||
>
|
||||
通过
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
danger
|
||||
disabled={data.status !== "review"}
|
||||
icon={<CloseOutlined />}
|
||||
onClick={() => void handleDiscard()}
|
||||
>
|
||||
放弃
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={data.status !== "failed"}
|
||||
icon={<RedoOutlined />}
|
||||
onClick={() => void handleRetry()}
|
||||
>
|
||||
重试
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -334,6 +334,7 @@ body {
|
||||
|
||||
.app-inbox-page {
|
||||
display: flex;
|
||||
gap: var(--ant-margin-sm);
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -342,6 +343,7 @@ body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: var(--ant-margin-sm);
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
@@ -349,17 +351,17 @@ body {
|
||||
.app-inbox-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: var(--ant-padding-xl);
|
||||
overflow-y: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.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);
|
||||
justify-content: flex-end;
|
||||
padding: var(--ant-padding-sm);
|
||||
border: 1px solid var(--ant-color-border-secondary);
|
||||
border-radius: var(--ant-border-radius-lg);
|
||||
background: var(--ant-color-bg-container);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user