feat: ship frontend visual refresh
This commit is contained in:
@@ -12,3 +12,4 @@ backend/storage/
|
||||
backend/tmp_migration_check.db
|
||||
project-docs/qa-artifacts/
|
||||
.claude/worktrees/
|
||||
.gstack/
|
||||
|
||||
@@ -161,20 +161,19 @@ export default function AuditLogsPage() {
|
||||
<section className="review-history-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">Audit Logs</span>
|
||||
<Title level={2}>查看系统关键操作,保留可回溯的审计记录。</Title>
|
||||
<Text type="secondary">当前已覆盖登录、创建审查任务、下载审查报告等关键动作,后续可继续扩展更多后台模块事件。</Text>
|
||||
<Title level={2}>追踪关键操作,快速定位责任链路。</Title>
|
||||
<Text type="secondary">搜索和动作筛选放在同一工具栏,适合管理员排查登录、审查和下载记录。</Text>
|
||||
</div>
|
||||
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">Admin Only</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
审计日志工作台
|
||||
审计节奏
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
<li>当前页面为管理员主用,普通成员不开放审计日志查看能力。</li>
|
||||
<li>支持按动作筛选日志。</li>
|
||||
<li>支持按操作人、资源类型、资源 ID 搜索。</li>
|
||||
<li>便于后续扩展到更完整的后台管理模块。</li>
|
||||
<li>先筛选,再查看详情 JSON。</li>
|
||||
</ul>
|
||||
|
||||
<Alert showIcon type="info" message="权限边界:audit-logs 当前按管理侧主用设计,后续再评估更细粒度审计查看权限。" />
|
||||
@@ -186,7 +185,7 @@ export default function AuditLogsPage() {
|
||||
<div className="review-toolbar-copy">
|
||||
<span className="landing-card-eyebrow">Audit Workspace</span>
|
||||
<strong>快速定位关键操作记录</strong>
|
||||
<span>先把当前已有的审计能力可视化,作为后续管理端扩展的基础页面。</span>
|
||||
<span>搜索操作人、资源类型、资源 ID 或动作名称。</span>
|
||||
</div>
|
||||
<div className="review-toolbar-controls">
|
||||
<Input
|
||||
|
||||
@@ -1174,11 +1174,11 @@ export default function ChatPage() {
|
||||
<section className="review-page-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">{reviewContext ? "Review Copilot" : "Legal Chat"}</span>
|
||||
<Title level={2}>{reviewContext ? "围绕当前审查结果继续追问这份合同。" : "围绕合同问题,发起一轮登录态法律问答。"}</Title>
|
||||
<Title level={2}>{reviewContext ? "接着这份审查结果继续问。" : "用登录态问答处理合同问题。"}</Title>
|
||||
<Text type="secondary">
|
||||
{reviewContext
|
||||
? "已从审查详情页带入当前合同的审查结果,可以直接围绕风险项、条款摘要和处理建议继续追问。"
|
||||
: "补齐 V1 会话历史最小闭环:输入问题、流式回答、展示引用来源,并支持回看已保存会话。"}
|
||||
? "风险项、条款摘要和处理建议已经带入上下文,可以直接追问下一步。"
|
||||
: "输入问题、查看流式回答和引用来源,历史会话会自动沉淀。"}
|
||||
</Text>
|
||||
<div className="review-page-highlight-list">
|
||||
{chatHighlights.map((item) => (
|
||||
@@ -1191,7 +1191,7 @@ export default function ChatPage() {
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">{reviewContext ? "Review Context" : "Usage Guide"}</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
{reviewContext ? "已注入审查上下文" : "问答说明"}
|
||||
{reviewContext ? "上下文已就绪" : "问答节奏"}
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
{chatTips.map((item) => (
|
||||
|
||||
@@ -149,14 +149,14 @@ export default function GeneratedContractDetailPage({ params }: { params: Promis
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="page-shell review-detail-page">
|
||||
<main className="page-shell review-detail-page contract-detail-page">
|
||||
{contextHolder}
|
||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||
<section className="review-detail-hero">
|
||||
<div className="page-header-block">
|
||||
<span className="landing-badge">Generated Contract</span>
|
||||
<Title level={2}>{detail?.template_name ?? "合同详情"}</Title>
|
||||
<Text type="secondary">查看已生成合同的完整信息、变量回显、真实预览,并基于历史结果继续处理。</Text>
|
||||
<Text type="secondary">保留生成参数、预览内容和下载入口,方便从历史结果继续编辑或复核。</Text>
|
||||
<div className="review-detail-hero-meta">
|
||||
<div className="review-detail-meta-chip">
|
||||
<span>合同类型</span>
|
||||
@@ -180,9 +180,9 @@ export default function GeneratedContractDetailPage({ params }: { params: Promis
|
||||
<Space direction="vertical" size="middle" style={{ width: "100%" }}>
|
||||
<span className="landing-panel-eyebrow">Workspace Actions</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
历史结果工作区
|
||||
下一步动作
|
||||
</Title>
|
||||
<Text type="secondary">可直接下载,也可把历史模板、变量、导出格式和导出选项回填到生成页继续处理。</Text>
|
||||
<Text type="secondary">下载文件、回填生成参数,或把生成内容带入问答复核。</Text>
|
||||
<Space wrap>
|
||||
<Button onClick={() => router.push("/contracts")}>返回列表</Button>
|
||||
<Button onClick={handleRestore}>再次生成 / 继续编辑</Button>
|
||||
@@ -240,81 +240,63 @@ export default function GeneratedContractDetailPage({ params }: { params: Promis
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card className="review-surface-card" loading={loading} title="变量回显">
|
||||
<Table
|
||||
rowKey="key"
|
||||
pagination={false}
|
||||
dataSource={variableRows}
|
||||
locale={{ emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无变量数据。" /> }}
|
||||
columns={[
|
||||
{ title: "变量名", dataIndex: "name", key: "name", width: 240 },
|
||||
{
|
||||
title: "变量值",
|
||||
dataIndex: "value",
|
||||
key: "value",
|
||||
render: (value: string) => <Paragraph style={{ marginBottom: 0, whiteSpace: "pre-wrap" }}>{value || "-"}</Paragraph>,
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
dataIndex: "isEmpty",
|
||||
key: "isEmpty",
|
||||
width: 120,
|
||||
render: (isEmpty: boolean) => <Tag color={isEmpty ? "gold" : "green"}>{isEmpty ? "空值" : "已填写"}</Tag>,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
<div className="contract-detail-two-column">
|
||||
<Card className="review-surface-card" loading={loading} title="变量回显">
|
||||
<Table
|
||||
rowKey="key"
|
||||
pagination={false}
|
||||
dataSource={variableRows}
|
||||
locale={{ emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无变量数据。" /> }}
|
||||
columns={[
|
||||
{ title: "变量名", dataIndex: "name", key: "name", width: 240 },
|
||||
{
|
||||
title: "变量值",
|
||||
dataIndex: "value",
|
||||
key: "value",
|
||||
render: (value: string) => <Paragraph style={{ marginBottom: 0, whiteSpace: "pre-wrap" }}>{value || "-"}</Paragraph>,
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
dataIndex: "isEmpty",
|
||||
key: "isEmpty",
|
||||
width: 120,
|
||||
render: (isEmpty: boolean) => <Tag color={isEmpty ? "gold" : "green"}>{isEmpty ? "空值" : "已填写"}</Tag>,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card className="review-surface-card" loading={loading} title="导出选项">
|
||||
<Table
|
||||
rowKey="key"
|
||||
pagination={false}
|
||||
dataSource={exportOptionRows}
|
||||
locale={{ emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无导出选项。" /> }}
|
||||
columns={[
|
||||
{ title: "选项名", dataIndex: "name", key: "name", width: 240 },
|
||||
{ title: "选项值", dataIndex: "value", key: "value" },
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
<Card className="review-surface-card" loading={loading} title="导出选项">
|
||||
<Table
|
||||
rowKey="key"
|
||||
pagination={false}
|
||||
dataSource={exportOptionRows}
|
||||
locale={{ emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无导出选项。" /> }}
|
||||
columns={[
|
||||
{ title: "选项名", dataIndex: "name", key: "name", width: 180 },
|
||||
{ title: "选项值", dataIndex: "value", key: "value" },
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Card className="review-surface-card" loading={loading} title="真实预览">
|
||||
{preview ? (
|
||||
<Card
|
||||
size="small"
|
||||
style={{
|
||||
backgroundColor: "#fafafa",
|
||||
border: "1px solid #d9d9d9",
|
||||
whiteSpace: "pre-wrap",
|
||||
fontFamily: "monospace",
|
||||
fontSize: "12px",
|
||||
}}
|
||||
>
|
||||
{preview.preview_content}
|
||||
</Card>
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无预览内容。" />
|
||||
)}
|
||||
</Card>
|
||||
<div className="contract-content-grid">
|
||||
<Card className="review-surface-card" loading={loading} title="真实预览">
|
||||
{preview ? (
|
||||
<div className="contract-preview-panel">{preview.preview_content}</div>
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无预览内容。" />
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card className="review-surface-card" loading={loading} title="生成内容">
|
||||
{detail ? (
|
||||
<Card
|
||||
size="small"
|
||||
style={{
|
||||
backgroundColor: "#fafafa",
|
||||
border: "1px solid #d9d9d9",
|
||||
whiteSpace: "pre-wrap",
|
||||
fontFamily: "monospace",
|
||||
fontSize: "12px",
|
||||
}}
|
||||
>
|
||||
{detail.generated_content}
|
||||
</Card>
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无生成内容。" />
|
||||
)}
|
||||
</Card>
|
||||
<Card className="review-surface-card" loading={loading} title="生成内容">
|
||||
{detail ? (
|
||||
<div className="contract-preview-panel">{detail.generated_content}</div>
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无生成内容。" />
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
</Space>
|
||||
</main>
|
||||
);
|
||||
|
||||
@@ -585,26 +585,26 @@ export default function ContractsPage() {
|
||||
const restoredOptionEntries = Object.entries(restoredOptions);
|
||||
|
||||
return (
|
||||
<main className="page-shell review-page-grid">
|
||||
<main className="page-shell review-page-grid contracts-page">
|
||||
{contextHolder}
|
||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||
<section className="review-page-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">Contract Generation</span>
|
||||
<Title level={2}>智能合同生成,快速创建专业合同。</Title>
|
||||
<Text type="secondary">选择模板、填写变量、预览确认、一键生成专业合同文档。</Text>
|
||||
<Title level={2}>从模板到下载,一屏完成合同生成。</Title>
|
||||
<Text type="secondary">先选模板,再填写变量;预览确认后生成文件,历史结果保留在下方。</Text>
|
||||
</div>
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">Smart Workflow</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
四步生成流程
|
||||
当前链路
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
<li>从预置模板库中选择合适的合同模板。</li>
|
||||
<li>填写模板变量,支持必填验证和默认值。</li>
|
||||
<li>实时预览生成结果,确认无误后生成。</li>
|
||||
<li>支持多种格式导出:DOCX、PDF、HTML、TXT。</li>
|
||||
</ul>
|
||||
<div className="contract-progress-strip">
|
||||
<span>选模板</span>
|
||||
<span>填变量</span>
|
||||
<span>生成文件</span>
|
||||
</div>
|
||||
<Text type="secondary">支持 DOCX、PDF、HTML、TXT 导出;从审查结果进入时会自动带入上下文。</Text>
|
||||
</Card>
|
||||
</section>
|
||||
|
||||
@@ -668,7 +668,10 @@ export default function ContractsPage() {
|
||||
)}
|
||||
|
||||
{restoredOptionEntries.length > 0 && (
|
||||
<Card size="small" title="已恢复的导出选项">
|
||||
<div className="embedded-panel">
|
||||
<div className="embedded-panel-header">
|
||||
<strong>已恢复的导出选项</strong>
|
||||
</div>
|
||||
<Space wrap>
|
||||
{restoredOptionEntries.map(([key, value]) => (
|
||||
<Tag key={key} color="blue">
|
||||
@@ -676,7 +679,7 @@ export default function ContractsPage() {
|
||||
</Tag>
|
||||
))}
|
||||
</Space>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Form form={form} layout="vertical">
|
||||
@@ -735,20 +738,9 @@ export default function ContractsPage() {
|
||||
<Text type="secondary">预览内容如下:</Text>
|
||||
</div>
|
||||
|
||||
<Card
|
||||
size="small"
|
||||
style={{
|
||||
backgroundColor: "#fafafa",
|
||||
border: "1px solid #d9d9d9",
|
||||
maxHeight: "400px",
|
||||
overflow: "auto",
|
||||
whiteSpace: "pre-wrap",
|
||||
fontFamily: "monospace",
|
||||
fontSize: "12px",
|
||||
}}
|
||||
>
|
||||
<div className="contract-preview-panel">
|
||||
{previewResult.preview_content}
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<Title level={5}>变量填写情况</Title>
|
||||
|
||||
@@ -293,20 +293,18 @@ export default function FilesPage() {
|
||||
<section className="review-page-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">File Management</span>
|
||||
<Title level={2}>统一管理平台内所有文件资源。</Title>
|
||||
<Text type="secondary">支持文件上传、下载、分类管理和删除操作。</Text>
|
||||
<Title level={2}>集中管理合同、报告和知识文档资产。</Title>
|
||||
<Text type="secondary">上传区和资产列表并排呈现,文件详情、下载和删除都从列表完成。</Text>
|
||||
</div>
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">Admin Workspace</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
文件管理流程
|
||||
资产流转
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
<li>当前页面为管理员主用,普通成员不开放文件资产管理能力。</li>
|
||||
<li>支持上传各类文件(合同、文档、图片等)。</li>
|
||||
<li>可按文件类型筛选查看。</li>
|
||||
<li>提供文件下载和删除功能。</li>
|
||||
<li>文件存储支持多存储桶配置。</li>
|
||||
<li>上传前先选类型与存储桶。</li>
|
||||
<li>列表支持按文件类型筛选。</li>
|
||||
<li>高亮来自其他页面跳转的目标文件。</li>
|
||||
</ul>
|
||||
|
||||
<Alert showIcon type="info" message="权限边界:member 通过业务链路使用文件结果,admin 负责资产管理。" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -306,19 +306,18 @@ export default function KnowledgeBasesPage() {
|
||||
<section className="review-page-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">Knowledge Base</span>
|
||||
<Title level={2}>集中维护知识库与文档索引状态。</Title>
|
||||
<Text type="secondary">先补齐 V1 最小闭环:知识库列表、创建知识库、上传文档,以及文档处理状态与失败原因展示。</Text>
|
||||
<Title level={2}>管理知识库、文档与索引状态。</Title>
|
||||
<Text type="secondary">左侧选择知识库,右侧上传和追踪文档处理结果,失败原因直接暴露。</Text>
|
||||
</div>
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">Admin Workspace</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
知识库维护流程
|
||||
管理节奏
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
<li>当前页面为管理员主用,普通成员不开放知识库管理入口。</li>
|
||||
<li>先创建知识库,再上传文档。</li>
|
||||
<li>当前支持 txt、docx、pdf 等文档的文本提取与预览。</li>
|
||||
<li>后续继续补强更深的切片、召回与索引能力。</li>
|
||||
<li>处理失败时优先看生命周期说明。</li>
|
||||
<li>索引完成后可进入问答继续使用。</li>
|
||||
</ul>
|
||||
|
||||
<Alert showIcon type="info" message="权限边界:member 使用审查、Chat、合同生成;admin 额外负责知识库治理。" />
|
||||
|
||||
@@ -178,6 +178,27 @@ function LoginPageContent() {
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="auth-access-visual" aria-label="登录后的安全工作台预览">
|
||||
<div className="auth-access-terminal">
|
||||
<span>Access Matrix</span>
|
||||
<strong>审查工作台已就绪</strong>
|
||||
</div>
|
||||
<div className="auth-access-track">
|
||||
<span />
|
||||
<span />
|
||||
<span />
|
||||
</div>
|
||||
<div className="auth-access-metrics">
|
||||
<div>
|
||||
<span>Session</span>
|
||||
<strong>Active</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Role</span>
|
||||
<strong>Tenant</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card className="review-side-card auth-side-card" bordered={false}>
|
||||
@@ -211,9 +232,12 @@ function LoginPageContent() {
|
||||
</div>
|
||||
|
||||
<Space direction="vertical" size="middle" style={{ width: "100%" }}>
|
||||
<Alert showIcon type="success" message="推荐新用户直接注册账号;注册成功后会自动登录并进入工作台。" />
|
||||
<Alert showIcon type="info" message="默认管理员仅用于本地初始化和联调,不建议作为常规使用方式。" />
|
||||
<Alert showIcon type="info" message={redirectHint} description="表单支持直接按 Enter 提交。" />
|
||||
<div className="auth-access-strip">
|
||||
<span>注册后自动进入</span>
|
||||
<span>Enter 快速提交</span>
|
||||
<span>{redirectTarget === "/" ? "默认到工作台" : "返回目标页"}</span>
|
||||
</div>
|
||||
{hasStoredSession ? <Alert showIcon type="info" message={loginHint} /> : null}
|
||||
{registerSuccessTip ? <Alert showIcon type="success" message={registerSuccessTip} /> : null}
|
||||
|
||||
<Tabs
|
||||
|
||||
+101
-51
@@ -76,6 +76,14 @@ const riskLabelMap: Record<string, string> = {
|
||||
low: "低风险",
|
||||
};
|
||||
|
||||
const publicShowcaseCards = [
|
||||
{ label: "Clause Scanner", value: "82 条", tone: "cyan" },
|
||||
{ label: "Risk Radar", value: "12 项", tone: "coral" },
|
||||
{ label: "Legal Sources", value: "36 条", tone: "amber" },
|
||||
];
|
||||
|
||||
const commandFlowItems = ["文件解析", "风险识别", "条款建议", "报告交付"];
|
||||
|
||||
function formatDateTime(value: string) {
|
||||
return new Date(value).toLocaleString("zh-CN", { hour12: false });
|
||||
}
|
||||
@@ -271,16 +279,30 @@ export default function HomePage() {
|
||||
const adminWorkbenchLinks = useMemo(() => getAdminWorkbenchLinks(currentUser?.role), [currentUser?.role]);
|
||||
const workspaceInsights = useMemo(() => buildWorkspaceInsights(reviews), [reviews]);
|
||||
const gettingStartedLinks = useMemo(() => buildGettingStartedLinks(currentUser?.role), [currentUser?.role]);
|
||||
const dashboardHighlights = useMemo(
|
||||
() => [
|
||||
...priorityTasks.map((item) => ({ href: item.href, eyebrow: item.label, title: item.title, description: item.meta })),
|
||||
...workspaceInsights.map((item) => ({ href: item.href, eyebrow: item.label, title: item.title, description: item.description })),
|
||||
].slice(0, 6),
|
||||
[priorityTasks, workspaceInsights],
|
||||
);
|
||||
const workflowLinks = useMemo(
|
||||
() => [
|
||||
...gettingStartedLinks.map((item) => ({ ...item, eyebrow: "Next Step" })),
|
||||
...quickLinks.map((item) => ({ href: item.href, label: item.label, description: item.description, eyebrow: item.eyebrow })),
|
||||
].slice(0, 6),
|
||||
[gettingStartedLinks, quickLinks],
|
||||
);
|
||||
|
||||
if (!currentUser) {
|
||||
return (
|
||||
<main className="landing-page">
|
||||
{contextHolder}
|
||||
<section className="landing-hero-shell">
|
||||
<div className="landing-hero">
|
||||
<section className="landing-hero-shell landing-hero-shell-public">
|
||||
<div className="landing-hero landing-hero-public">
|
||||
<span className="landing-badge">AI Contract Platform</span>
|
||||
<h1>面向真实业务流转的合同工作平台。</h1>
|
||||
<p>现在系统已接入正式数据库,登录、合同审查、审查记录、结果详情与报告下载主链路均已打通。</p>
|
||||
<h1>让合同审查像刷仪表盘一样清爽、快速、有判断力。</h1>
|
||||
<p>围绕上传、识别、建议、报告导出重组体验,把 AI 审查结果变成可行动的法务工作流。</p>
|
||||
<div className="landing-actions">
|
||||
<Link className="landing-primary-action" href="/login?tab=register">
|
||||
立即注册新账号
|
||||
@@ -289,6 +311,41 @@ export default function HomePage() {
|
||||
进入登录
|
||||
</Link>
|
||||
</div>
|
||||
<div className="landing-trust-row">
|
||||
<span>合同初审</span>
|
||||
<span>风险雷达</span>
|
||||
<span>依据引用</span>
|
||||
<span>报告交付</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="landing-visual-scene" aria-label="AI 合同审查可视化预览">
|
||||
<div className="landing-visual-topline">
|
||||
<span>Live Review Console</span>
|
||||
<strong>V1 Pilot</strong>
|
||||
</div>
|
||||
<div className="landing-contract-stack">
|
||||
<div className="landing-contract-paper">
|
||||
<span className="paper-kicker">保密协议 · NDA</span>
|
||||
<strong>自动定位高风险条款</strong>
|
||||
<div className="paper-line paper-line-wide" />
|
||||
<div className="paper-line" />
|
||||
<div className="paper-line paper-line-hot" />
|
||||
<div className="paper-line paper-line-short" />
|
||||
</div>
|
||||
<div className="landing-risk-pulse">
|
||||
<span>High Risk</span>
|
||||
<strong>03</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div className="landing-showcase-grid">
|
||||
{publicShowcaseCards.map((item) => (
|
||||
<div className={`landing-showcase-card landing-showcase-card-${item.tone}`} key={item.label}>
|
||||
<span>{item.label}</span>
|
||||
<strong>{item.value}</strong>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
@@ -301,8 +358,8 @@ export default function HomePage() {
|
||||
<section className="landing-hero-shell">
|
||||
<div className="landing-hero">
|
||||
<span className="landing-badge">Contract Workspace</span>
|
||||
<h1>你好,{currentUser.display_name}。这里是你的合同业务工作台。</h1>
|
||||
<p>首页现在承担业务 cockpit 角色:展示当前租户审查概览、风险分布、待处理状态与核心操作入口,而不再只是简单 landing 页。</p>
|
||||
<h1>你好,{currentUser.display_name}。今天先处理最重要的合同任务。</h1>
|
||||
<p>审查概览、风险分布和核心入口集中在这里,首页负责帮你判断下一步。</p>
|
||||
<div className="landing-actions">
|
||||
<Link className="landing-primary-action" href="/review">
|
||||
立即发起审查
|
||||
@@ -311,6 +368,34 @@ export default function HomePage() {
|
||||
进入合同生成
|
||||
</Link>
|
||||
</div>
|
||||
<div className="landing-command-visual" aria-label="合同审查智能流程">
|
||||
<div className="landing-command-header">
|
||||
<span>AI Review Pipeline</span>
|
||||
<strong>{stats.completedCount}/{stats.totalReviews || 0}</strong>
|
||||
</div>
|
||||
<div className="landing-command-flow">
|
||||
{commandFlowItems.map((item, index) => (
|
||||
<div className="landing-command-step" key={item}>
|
||||
<span>{String(index + 1).padStart(2, "0")}</span>
|
||||
<strong>{item}</strong>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="landing-command-alerts">
|
||||
<div>
|
||||
<span>高风险</span>
|
||||
<strong>{stats.highRisk}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>处理中</span>
|
||||
<strong>{stats.pendingCount}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>异常</span>
|
||||
<strong>{stats.failedCount}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="landing-stats">
|
||||
<div className="landing-stat-card">
|
||||
<strong>{stats.totalReviews}</strong>
|
||||
@@ -381,35 +466,15 @@ export default function HomePage() {
|
||||
<section className="landing-section">
|
||||
<div className="landing-section-header">
|
||||
<h2>优先处理摘要</h2>
|
||||
<p>优先展示高风险、异常和处理中任务,让首页更像可执行的驾驶舱。</p>
|
||||
<p>运营摘要与高风险、异常和处理中任务直接前置,减少二次判断。</p>
|
||||
</div>
|
||||
{priorityTasks.length === 0 ? (
|
||||
{dashboardHighlights.length === 0 ? (
|
||||
<Empty description="当前没有需要优先处理的任务。" />
|
||||
) : (
|
||||
<div className="landing-grid">
|
||||
{priorityTasks.map((item) => (
|
||||
<Link className="landing-card" href={item.href} key={`${item.label}-${item.id}`}>
|
||||
<span className="landing-card-eyebrow">{item.label}</span>
|
||||
<strong>{item.title}</strong>
|
||||
<span>{item.meta}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="landing-section">
|
||||
<div className="landing-section-header">
|
||||
<h2>运营摘要</h2>
|
||||
<p>把当前最值得立刻动作的任务直接前置,减少再判断成本。</p>
|
||||
</div>
|
||||
{workspaceInsights.length === 0 ? (
|
||||
<Empty description="当前没有需要额外关注的运营摘要。" />
|
||||
) : (
|
||||
<div className="landing-grid">
|
||||
{workspaceInsights.map((item) => (
|
||||
<Link className="landing-card" href={item.href} key={`${item.label}-${item.href}`}>
|
||||
<span className="landing-card-eyebrow">{item.label}</span>
|
||||
{dashboardHighlights.map((item) => (
|
||||
<Link className="landing-card" href={item.href} key={`${item.eyebrow}-${item.href}`}>
|
||||
<span className="landing-card-eyebrow">{item.eyebrow}</span>
|
||||
<strong>{item.title}</strong>
|
||||
<span>{item.description}</span>
|
||||
</Link>
|
||||
@@ -420,13 +485,13 @@ export default function HomePage() {
|
||||
|
||||
<section className="landing-section">
|
||||
<div className="landing-section-header">
|
||||
<h2>起步建议</h2>
|
||||
<p>针对当前角色给出最合理的下一步,让新注册用户进入系统后不会迷路。</p>
|
||||
<h2>核心入口</h2>
|
||||
<p>把新用户起步和高频功能合并,避免首页变成导航墙。</p>
|
||||
</div>
|
||||
<div className="landing-grid">
|
||||
{gettingStartedLinks.map((item) => (
|
||||
<Link className="landing-card" href={item.href} key={item.href}>
|
||||
<span className="landing-card-eyebrow">Getting Started</span>
|
||||
{workflowLinks.map((item) => (
|
||||
<Link className="landing-card" href={item.href} key={`${item.eyebrow}-${item.href}`}>
|
||||
<span className="landing-card-eyebrow">{item.eyebrow}</span>
|
||||
<strong>{item.label}</strong>
|
||||
<span>{item.description}</span>
|
||||
</Link>
|
||||
@@ -482,21 +547,6 @@ export default function HomePage() {
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="landing-section">
|
||||
<div className="landing-section-header">
|
||||
<h2>快捷入口</h2>
|
||||
<p>保持首页的业务工作台属性,而不是只放导航链接。</p>
|
||||
</div>
|
||||
<div className="landing-grid">
|
||||
{quickLinks.map((link) => (
|
||||
<Link className="landing-card" href={link.href} key={link.href}>
|
||||
<span className="landing-card-eyebrow">{link.eyebrow}</span>
|
||||
<strong>{link.label}</strong>
|
||||
<span>{link.description}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -95,19 +95,30 @@ export default function PilotReadinessPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="page-shell">
|
||||
<main className="page-shell pilot-readiness-page">
|
||||
<Space direction="vertical" size={20} style={{ width: "100%" }}>
|
||||
<section className="page-header-block">
|
||||
<Space direction="vertical" size={10}>
|
||||
<Tag color="blue">Phase 3 Pilot Delivery</Tag>
|
||||
<section className="review-page-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">Pilot Delivery</span>
|
||||
<Title level={2}>试点交付准备</Title>
|
||||
<Paragraph type="secondary">
|
||||
阶段 3 验收门禁集中在这里:环境状态、默认账号、演示链路和自动化验收结果需要能被管理员快速复查。
|
||||
阶段 3 验收门禁集中在这里:环境、账号、演示链路和自动化验收项集中在一个工作台,失败项直接暴露。
|
||||
</Paragraph>
|
||||
</Space>
|
||||
<Button icon={<ReloadOutlined />} onClick={loadReadiness} loading={loading}>
|
||||
刷新状态
|
||||
</Button>
|
||||
</div>
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">Readiness Control</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
交付门禁
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
<li>先看必需项失败数。</li>
|
||||
<li>再检查默认账号与演示链路。</li>
|
||||
<li>最后用验收项确认试点路径。</li>
|
||||
</ul>
|
||||
<Button icon={<ReloadOutlined />} onClick={loadReadiness} loading={loading}>
|
||||
刷新状态
|
||||
</Button>
|
||||
</Card>
|
||||
</section>
|
||||
|
||||
{errorMessage ? <Alert showIcon type="error" message={errorMessage} /> : null}
|
||||
|
||||
@@ -73,24 +73,24 @@ export default function ProfilePage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="page-shell review-page-grid">
|
||||
<main className="page-shell review-page-grid profile-page">
|
||||
{contextHolder}
|
||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||
<section className="review-page-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">Profile Settings</span>
|
||||
<Title level={2}>维护当前账号的显示名称与登录密码。</Title>
|
||||
<Text type="secondary">补齐 V1 个人设置页,保持范围只覆盖当前登录用户的基础资料维护。</Text>
|
||||
<Title level={2}>管理当前账号的名称与密码。</Title>
|
||||
<Text type="secondary">这里只处理当前登录账号,资料范围更清晰,操作也更轻。</Text>
|
||||
</div>
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">My Workspace</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
个人设置
|
||||
账号边界
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
<li>支持修改当前显示名称。</li>
|
||||
<li>显示名称立即同步到顶栏。</li>
|
||||
<li>密码留空时保持不变。</li>
|
||||
<li>仅作用于当前登录账号。</li>
|
||||
<li>不会影响租户内其他成员。</li>
|
||||
</ul>
|
||||
</Card>
|
||||
</section>
|
||||
|
||||
@@ -19,12 +19,6 @@ type ReviewFormValues = {
|
||||
|
||||
const reviewHighlights = ["支持 PDF / DOCX / TXT 上传", "可叠加补充文本说明", "提交后自动进入结果详情页"];
|
||||
|
||||
const reviewTips = [
|
||||
"上传文件后会先抽取文本,再进入风险识别链路。",
|
||||
"如合同有补充说明,可直接粘贴在右侧文本框中一起审查。",
|
||||
"详情页会展示风险总览、条款摘要与可下载报告。",
|
||||
];
|
||||
|
||||
export default function ReviewPage() {
|
||||
const router = useRouter();
|
||||
const [form] = Form.useForm<ReviewFormValues>();
|
||||
@@ -101,8 +95,8 @@ export default function ReviewPage() {
|
||||
<section className="review-page-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">Start Review</span>
|
||||
<Title level={2}>提交一份合同,快速进入审查工作流。</Title>
|
||||
<Text type="secondary">支持文件上传与文本补充双入口,让合同提交从一开始就具备正式产品工作台的使用体验。</Text>
|
||||
<Title level={2}>上传合同,直接进入风险审查。</Title>
|
||||
<Text type="secondary">文件上传和文本粘贴合并成一个提交流,先完成合同输入,再设置审查视角。</Text>
|
||||
<div className="review-page-highlight-list">
|
||||
{reviewHighlights.map((item) => (
|
||||
<span className="review-page-highlight-chip" key={item}>
|
||||
@@ -110,14 +104,22 @@ export default function ReviewPage() {
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="review-hero-visual" aria-label="合同审查流程预览">
|
||||
{submissionSteps.map((item, index) => (
|
||||
<div className="review-hero-visual-step" key={item}>
|
||||
<span>{String(index + 1).padStart(2, "0")}</span>
|
||||
<strong>{item}</strong>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">Submission Guide</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
审查前提示
|
||||
提交节奏
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
{reviewTips.map((item) => (
|
||||
{submissionSteps.map((item) => (
|
||||
<li key={item}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
@@ -160,16 +162,6 @@ export default function ReviewPage() {
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
<Card className="review-surface-card" title="提交流程">
|
||||
<div className="review-submission-step-list">
|
||||
{submissionSteps.map((item, index) => (
|
||||
<div className="review-submission-step" key={item}>
|
||||
<span className="review-submission-step-index">0{index + 1}</span>
|
||||
<strong>{item}</strong>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
</Space>
|
||||
</Col>
|
||||
|
||||
@@ -221,13 +213,6 @@ export default function ReviewPage() {
|
||||
</Button>
|
||||
</Card>
|
||||
|
||||
<Card className="review-surface-card" title="本次提交说明">
|
||||
<ul className="review-page-tip-list review-page-tip-list-compact">
|
||||
{reviewTips.map((item) => (
|
||||
<li key={item}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</Card>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -482,94 +482,94 @@ export default function ReviewDetailPage({ params }: { params: Promise<{ id: str
|
||||
))}
|
||||
</section>
|
||||
|
||||
<Card className="review-surface-card" bordered={false}>
|
||||
<div className="chat-review-hero">
|
||||
<div>
|
||||
<span className="landing-card-eyebrow">Review Copilot</span>
|
||||
<strong>继续用合同审查助手跟进这份合同</strong>
|
||||
<p>会把当前审查结果、风险等级、审查建议和文本摘要一起带入问答页,直接围绕这份合同继续追问。</p>
|
||||
</div>
|
||||
<div className="chat-review-meta-grid">
|
||||
<div className="chat-review-meta-card">
|
||||
<span>当前任务</span>
|
||||
<strong>{detail?.title ?? "-"}</strong>
|
||||
<div className="review-detail-action-dock">
|
||||
<Card className="review-surface-card" bordered={false}>
|
||||
<div className="chat-review-hero">
|
||||
<div>
|
||||
<span className="landing-card-eyebrow">Review Copilot</span>
|
||||
<strong>继续用审查助手处理这份合同</strong>
|
||||
<p>自动带入风险等级、审查建议和文本摘要,直接进入下一轮追问。</p>
|
||||
</div>
|
||||
<div className="chat-review-meta-card">
|
||||
<span>总体风险</span>
|
||||
<strong>{overallRisk}</strong>
|
||||
<div className="chat-review-meta-grid">
|
||||
<div className="chat-review-meta-card">
|
||||
<span>当前任务</span>
|
||||
<strong>{detail?.title ?? "-"}</strong>
|
||||
</div>
|
||||
<div className="chat-review-meta-card">
|
||||
<span>总体风险</span>
|
||||
<strong>{overallRisk}</strong>
|
||||
</div>
|
||||
<div className="chat-review-meta-card">
|
||||
<span>问答模式</span>
|
||||
<strong>审查上下文</strong>
|
||||
</div>
|
||||
<div className="chat-review-meta-card">
|
||||
<span>建议动作</span>
|
||||
<strong>先处理高风险</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div className="chat-review-meta-card">
|
||||
<span>问答模式</span>
|
||||
<strong>审查上下文已预置</strong>
|
||||
</div>
|
||||
<div className="chat-review-meta-card">
|
||||
<span>建议动作</span>
|
||||
<strong>继续追问条款风险</strong>
|
||||
</div>
|
||||
</div>
|
||||
<Space wrap>
|
||||
{reviewChatPayloadReady ? (
|
||||
<Link className="landing-primary-action" href={reviewChatHref} onClick={handleOpenReviewChat}>
|
||||
进入审查助手问答
|
||||
<Space wrap>
|
||||
{reviewChatPayloadReady ? (
|
||||
<Link className="landing-primary-action" href={reviewChatHref} onClick={handleOpenReviewChat}>
|
||||
进入审查助手问答
|
||||
</Link>
|
||||
) : (
|
||||
<Button type="primary" loading>
|
||||
正在准备审查上下文
|
||||
</Button>
|
||||
)}
|
||||
<Link className="landing-secondary-action" href="/chat">
|
||||
打开通用问答页
|
||||
</Link>
|
||||
) : (
|
||||
<Button type="primary" loading>
|
||||
正在准备审查上下文
|
||||
</Button>
|
||||
)}
|
||||
<Link className="landing-secondary-action" href="/chat">
|
||||
打开通用问答页
|
||||
</Link>
|
||||
</Space>
|
||||
</div>
|
||||
</Card>
|
||||
</Space>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="review-surface-card" bordered={false}>
|
||||
<div className="chat-review-layout">
|
||||
<div className="chat-review-section-header">
|
||||
<span className="landing-card-eyebrow">Quick Actions</span>
|
||||
<Space direction="vertical" size="middle" style={{ width: "100%" }}>
|
||||
<Card className="review-surface-card" bordered={false}>
|
||||
<div className="chat-review-layout">
|
||||
<div className="chat-review-section-header">
|
||||
<span className="landing-card-eyebrow">QUICK ACTIONS</span>
|
||||
<strong>围绕当前审查结果,直接继续推进</strong>
|
||||
<Text type="secondary">{`当前总体风险为${overallRisk},可直接解释风险、生成修改清单或准备谈判策略。`}</Text>
|
||||
</div>
|
||||
<Space wrap>
|
||||
{quickActionItems.map((item, index) => (
|
||||
<Link className={index === 0 ? "landing-primary-action" : "landing-secondary-action"} href={item.href} key={item.label}>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</Space>
|
||||
<div className="review-detail-info-note" style={{ marginTop: 16 }}>
|
||||
当前追问主线:优先围绕总体风险、重点风险项与条款摘要,继续把问答结果回流为修改动作和谈判准备。
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Text type="secondary">{`总体风险:${overallRisk}。优先解释风险、生成修改清单或准备谈判策略。`}</Text>
|
||||
</div>
|
||||
<Space wrap>
|
||||
{quickActionItems.map((item, index) => (
|
||||
<Link className={index === 0 ? "landing-primary-action" : "landing-secondary-action"} href={item.href} key={item.label}>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</Space>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="review-surface-card" bordered={false}>
|
||||
<div className="chat-review-layout">
|
||||
<div className="chat-review-section-header">
|
||||
<span className="landing-card-eyebrow">Workbench</span>
|
||||
<strong>处理工作台入口</strong>
|
||||
<Text type="secondary">围绕当前审查任务,继续在报告、文件、知识库与审查列表之间快速切换。</Text>
|
||||
</div>
|
||||
<Space wrap>
|
||||
{report ? (
|
||||
<Button type="primary" loading={downloadingReport} onClick={() => void handleDownloadReport()}>
|
||||
下载审查报告
|
||||
</Button>
|
||||
) : null}
|
||||
{detail?.can_retry ? (
|
||||
<Button loading={retryingReview} onClick={() => void handleRetryReview()}>
|
||||
重新发起审查
|
||||
</Button>
|
||||
) : null}
|
||||
{reviewWorkbenchItems.map((item) => (
|
||||
<Link className={item.primary ? "landing-primary-action" : "landing-secondary-action"} href={item.href} key={item.label}>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</Space>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="review-surface-card" bordered={false}>
|
||||
<div className="chat-review-layout">
|
||||
<div className="chat-review-section-header">
|
||||
<span className="landing-card-eyebrow">Workbench</span>
|
||||
<strong>报告、文件与列表入口</strong>
|
||||
</div>
|
||||
<Space wrap>
|
||||
{report ? (
|
||||
<Button type="primary" loading={downloadingReport} onClick={() => void handleDownloadReport()}>
|
||||
下载审查报告
|
||||
</Button>
|
||||
) : null}
|
||||
{detail?.can_retry ? (
|
||||
<Button loading={retryingReview} onClick={() => void handleRetryReview()}>
|
||||
重新发起审查
|
||||
</Button>
|
||||
) : null}
|
||||
{reviewWorkbenchItems.map((item) => (
|
||||
<Link className={item.primary ? "landing-primary-action" : "landing-secondary-action"} href={item.href} key={item.label}>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</Space>
|
||||
</div>
|
||||
</Card>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col xs={24} lg={8}>
|
||||
|
||||
@@ -411,8 +411,8 @@ export default function ReviewsPage() {
|
||||
<section className="review-history-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">Review History</span>
|
||||
<Title level={2}>把审查记录沉淀成可继续处理的任务工作台。</Title>
|
||||
<Text type="secondary">列表页已按需求继续收口为可交付工作台:支持筛选、进入详情、下载报告、查看文件资产与继续处理。</Text>
|
||||
<Title level={2}>审查记录不再只是列表,而是任务工作台。</Title>
|
||||
<Text type="secondary">从这里筛选任务、进入详情、下载报告,或继续处理未完成合同。</Text>
|
||||
<div className="review-history-latest">最近一次任务创建时间:{latestTaskLabel}</div>
|
||||
<div className="review-history-filter-summary">当前列表展示:{filterSummary}</div>
|
||||
</div>
|
||||
@@ -420,7 +420,7 @@ export default function ReviewsPage() {
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">History Insight</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
审查记录工作台
|
||||
处理优先级
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
{historyTips.map((item) => (
|
||||
|
||||
@@ -279,16 +279,15 @@ export default function RulesPage() {
|
||||
<section className="review-page-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">Rule Management</span>
|
||||
<Title level={2}>集中维护合同审查规则,并直接驱动审查结果。</Title>
|
||||
<Text type="secondary">先补齐 V1 最小闭环:规则列表、新增、编辑、启用停用,并接入现有关键词审查链路。</Text>
|
||||
<Title level={2}>维护审查规则,直接影响风险识别。</Title>
|
||||
<Text type="secondary">左侧编辑规则,右侧查看启停状态和命中依据,规则变更更容易复核。</Text>
|
||||
</div>
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">Admin Workspace</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
规则维护流程
|
||||
规则节奏
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
<li>当前页面为管理员主用,普通成员不开放规则治理能力。</li>
|
||||
<li>先配置关键词规则,再发起合同审查。</li>
|
||||
<li>支持按合同类型区分规则。</li>
|
||||
<li>停用规则后不再参与后续审查匹配。</li>
|
||||
|
||||
@@ -360,8 +360,8 @@ export default function TemplatesPage() {
|
||||
<section className="review-page-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">Template Management</span>
|
||||
<Title level={2}>集中维护合同模板,先补齐可用的管理闭环。</Title>
|
||||
<Text type="secondary">当前支持模板列表、创建、编辑、删除,以及变量 JSON 配置的最小 V1 管理流程。</Text>
|
||||
<Title level={2}>维护可复用模板和变量字段。</Title>
|
||||
<Text type="secondary">模板正文、变量配置和启停状态放在同一个工作台,方便生成链路直接消费。</Text>
|
||||
<Space style={{ marginTop: 16 }} wrap>
|
||||
<Button onClick={() => void handleSeedDefaults()} loading={seeding}>
|
||||
一键初始化默认模板
|
||||
@@ -372,13 +372,12 @@ export default function TemplatesPage() {
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">Admin Workspace</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
模板维护流程
|
||||
模板节奏
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
<li>当前页面为管理员主用,普通成员不开放模板治理能力。</li>
|
||||
<li>先录入标准模板正文。</li>
|
||||
<li>变量区先以 JSON 数组维护占位字段。</li>
|
||||
<li>启用状态可控制模板是否对后续流程开放。</li>
|
||||
<li>再维护变量名与显示名。</li>
|
||||
<li>启用状态决定生成页是否可用。</li>
|
||||
</ul>
|
||||
<Alert showIcon type="info" message="权限边界:member 使用合同生成链路,admin 负责模板管理。" />
|
||||
</Card>
|
||||
@@ -399,15 +398,13 @@ export default function TemplatesPage() {
|
||||
<Form.Item label="模板正文" name="content" rules={[{ required: true, message: "请输入模板正文" }]}>
|
||||
<Input.TextArea rows={8} placeholder="请输入合同模板正文" />
|
||||
</Form.Item>
|
||||
<Card
|
||||
size="small"
|
||||
title="变量配置"
|
||||
extra={
|
||||
<div className="embedded-panel">
|
||||
<div className="embedded-panel-header">
|
||||
<strong>变量配置</strong>
|
||||
<Button size="small" onClick={addVariable} disabled={hasEmptyVariable(watchedVariables)}>
|
||||
添加变量
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
</div>
|
||||
<Space direction="vertical" size="middle" style={{ width: "100%" }}>
|
||||
{(watchedVariables ?? [{}]).map((_, index) => (
|
||||
<Space key={index} size="middle" style={{ display: "flex" }} align="start">
|
||||
@@ -423,10 +420,13 @@ export default function TemplatesPage() {
|
||||
</Space>
|
||||
))}
|
||||
</Space>
|
||||
</Card>
|
||||
<Card size="small" title="变量 JSON 预览">
|
||||
</div>
|
||||
<div className="embedded-panel">
|
||||
<div className="embedded-panel-header">
|
||||
<strong>变量 JSON 预览</strong>
|
||||
</div>
|
||||
<pre style={{ margin: 0, whiteSpace: "pre-wrap" }}>{variablePreview}</pre>
|
||||
</Card>
|
||||
</div>
|
||||
<Form.Item label="启用状态" name="enabled" valuePropName="checked" style={{ marginTop: 16 }}>
|
||||
<Switch checkedChildren="启用" unCheckedChildren="停用" />
|
||||
</Form.Item>
|
||||
|
||||
@@ -105,16 +105,15 @@ export default function TenantSettingsPage() {
|
||||
<section className="review-page-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">Tenant Settings</span>
|
||||
<Title level={2}>查看当前租户基础信息、套餐与容量限制。</Title>
|
||||
<Text type="secondary">先补齐 V1 最小闭环:管理员可维护租户基础信息,并查看当前套餐和容量上限。</Text>
|
||||
<Title level={2}>维护租户信息,核对套餐容量。</Title>
|
||||
<Text type="secondary">基础信息可编辑,套餐和限制作为右侧只读核对区展示。</Text>
|
||||
</div>
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">Admin Workspace</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
租户设置工作台
|
||||
租户边界
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
<li>当前页面为管理员主用,普通成员不开放租户设置更新能力。</li>
|
||||
<li>支持维护租户名称、编码与状态。</li>
|
||||
<li>展示当前套餐与容量限制。</li>
|
||||
<li>范围保持在当前租户,不扩展到租户管理列表。</li>
|
||||
|
||||
@@ -237,16 +237,15 @@ export default function UsersPage() {
|
||||
<section className="review-page-hero">
|
||||
<div className="review-page-hero-copy">
|
||||
<span className="landing-badge">User Management</span>
|
||||
<Title level={2}>集中管理租户内账号、角色与启停状态。</Title>
|
||||
<Text type="secondary">先补齐 V1 最小闭环:用户列表、新增、编辑、角色调整和启停管理。</Text>
|
||||
<Title level={2}>管理成员账号、角色和启停状态。</Title>
|
||||
<Text type="secondary">账号表单和成员列表并排处理,适合管理员快速开通、调整和停用。</Text>
|
||||
</div>
|
||||
<Card className="review-side-card" bordered={false}>
|
||||
<span className="landing-panel-eyebrow">Admin Workspace</span>
|
||||
<Title level={4} style={{ margin: 0, color: "inherit" }}>
|
||||
用户管理流程
|
||||
成员治理
|
||||
</Title>
|
||||
<ul className="review-page-tip-list">
|
||||
<li>管理员可在当前租户内维护账号。</li>
|
||||
<li>登录账号统一按邮箱格式管理,和注册页口径保持一致。</li>
|
||||
<li>支持调整角色与账号状态。</li>
|
||||
<li>编辑用户时可选填新密码,不填则保持原密码。</li>
|
||||
|
||||
@@ -1,35 +1,53 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
AuditOutlined,
|
||||
BookOutlined,
|
||||
CommentOutlined,
|
||||
DashboardOutlined,
|
||||
FileDoneOutlined,
|
||||
FileProtectOutlined,
|
||||
FolderOpenOutlined,
|
||||
HistoryOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
SettingOutlined,
|
||||
SolutionOutlined,
|
||||
TeamOutlined,
|
||||
ThunderboltOutlined,
|
||||
UserOutlined,
|
||||
DownOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { Button } from "antd";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { Button, Dropdown, type MenuProps } from "antd";
|
||||
|
||||
import { clearSession, useAuthStore } from "@/stores/auth.store";
|
||||
|
||||
const publicNavItems = [
|
||||
{ href: "/", label: "首页" },
|
||||
{ href: "/login?tab=register", label: "注册" },
|
||||
{ href: "/login", label: "登录" },
|
||||
{ href: "/", label: "首页", icon: <DashboardOutlined /> },
|
||||
{ href: "/login?tab=register", label: "注册", icon: <UserOutlined /> },
|
||||
{ href: "/login", label: "登录", icon: <SafetyCertificateOutlined /> },
|
||||
];
|
||||
|
||||
const privateNavItems = [
|
||||
{ href: "/", label: "首页" },
|
||||
{ href: "/review", label: "发起审查" },
|
||||
{ href: "/reviews", label: "审查记录" },
|
||||
{ href: "/chat", label: "法律问答" },
|
||||
{ href: "/contracts", label: "合同生成" },
|
||||
{ href: "/profile", label: "个人设置" },
|
||||
{ href: "/", label: "首页", icon: <DashboardOutlined /> },
|
||||
{ href: "/review", label: "发起审查", icon: <FileProtectOutlined /> },
|
||||
{ href: "/reviews", label: "审查记录", icon: <HistoryOutlined /> },
|
||||
{ href: "/chat", label: "法律问答", icon: <CommentOutlined /> },
|
||||
{ href: "/contracts", label: "合同生成", icon: <FileDoneOutlined /> },
|
||||
{ href: "/profile", label: "个人设置", icon: <UserOutlined /> },
|
||||
];
|
||||
|
||||
const adminNavItems = [
|
||||
{ href: "/rules", label: "规则管理" },
|
||||
{ href: "/templates", label: "模板管理" },
|
||||
{ href: "/knowledge-bases", label: "知识库管理" },
|
||||
{ href: "/files", label: "文件管理" },
|
||||
{ href: "/tenant-settings", label: "租户设置" },
|
||||
{ href: "/users", label: "用户管理" },
|
||||
{ href: "/audit-logs", label: "审计日志" },
|
||||
{ href: "/pilot-readiness", label: "试点交付" },
|
||||
{ href: "/rules", label: "规则管理", icon: <SolutionOutlined /> },
|
||||
{ href: "/templates", label: "模板管理", icon: <BookOutlined /> },
|
||||
{ href: "/knowledge-bases", label: "知识库管理", icon: <FolderOpenOutlined /> },
|
||||
{ href: "/files", label: "文件管理", icon: <FileDoneOutlined /> },
|
||||
{ href: "/tenant-settings", label: "租户设置", icon: <SettingOutlined /> },
|
||||
{ href: "/users", label: "用户管理", icon: <TeamOutlined /> },
|
||||
{ href: "/audit-logs", label: "审计日志", icon: <AuditOutlined /> },
|
||||
{ href: "/pilot-readiness", label: "试点交付", icon: <ThunderboltOutlined /> },
|
||||
];
|
||||
|
||||
function isActive(pathname: string, href: string) {
|
||||
@@ -43,18 +61,37 @@ function isActive(pathname: string, href: string) {
|
||||
export function AppTopbar() {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const navRef = useRef<HTMLElement | null>(null);
|
||||
const currentUser = useAuthStore((state) => state.currentUser);
|
||||
const navItems = currentUser
|
||||
? currentUser.role === "admin"
|
||||
? [...privateNavItems, ...adminNavItems]
|
||||
: privateNavItems
|
||||
: publicNavItems;
|
||||
const navItems = currentUser ? privateNavItems : publicNavItems;
|
||||
const showAdminMenu = currentUser?.role === "admin";
|
||||
const adminMenuActive = adminNavItems.some((item) => isActive(pathname, item.href));
|
||||
const adminMenuItems: MenuProps["items"] = adminNavItems.map((item) => ({
|
||||
key: item.href,
|
||||
label: (
|
||||
<Link className="app-admin-menu-item" href={item.href}>
|
||||
<span className="app-nav-icon" aria-hidden="true">
|
||||
{item.icon}
|
||||
</span>
|
||||
{item.label}
|
||||
</Link>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleLogout = () => {
|
||||
clearSession();
|
||||
router.push("/login");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const frameId = window.requestAnimationFrame(() => {
|
||||
const activeItem = navRef.current?.querySelector(".app-nav-link-active");
|
||||
activeItem?.scrollIntoView({ behavior: "auto", block: "nearest", inline: "center" });
|
||||
});
|
||||
|
||||
return () => window.cancelAnimationFrame(frameId);
|
||||
}, [currentUser?.role, pathname]);
|
||||
|
||||
return (
|
||||
<header className="app-topbar">
|
||||
<div className="app-topbar-inner">
|
||||
@@ -67,16 +104,44 @@ export function AppTopbar() {
|
||||
</Link>
|
||||
|
||||
<div className="app-topbar-right">
|
||||
<nav className="app-nav" aria-label="Primary">
|
||||
<nav className="app-nav" aria-label="Primary" ref={navRef}>
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
className={isActive(pathname, item.href) ? "app-nav-link app-nav-link-active" : "app-nav-link"}
|
||||
href={item.href}
|
||||
key={item.href}
|
||||
>
|
||||
<span className="app-nav-icon" aria-hidden="true">
|
||||
{item.icon}
|
||||
</span>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
{showAdminMenu ? (
|
||||
<Link
|
||||
className={isActive(pathname, "/pilot-readiness") ? "app-nav-link app-nav-link-active" : "app-nav-link"}
|
||||
href="/pilot-readiness"
|
||||
>
|
||||
<span className="app-nav-icon" aria-hidden="true">
|
||||
<ThunderboltOutlined />
|
||||
</span>
|
||||
试点交付
|
||||
</Link>
|
||||
) : null}
|
||||
{showAdminMenu ? (
|
||||
<Dropdown menu={{ items: adminMenuItems }} placement="bottomRight" trigger={["click"]}>
|
||||
<button
|
||||
className={adminMenuActive ? "app-nav-link app-nav-admin-trigger app-nav-link-active" : "app-nav-link app-nav-admin-trigger"}
|
||||
type="button"
|
||||
>
|
||||
<span className="app-nav-icon" aria-hidden="true">
|
||||
<SettingOutlined />
|
||||
</span>
|
||||
管理
|
||||
<DownOutlined className="app-nav-admin-caret" />
|
||||
</button>
|
||||
</Dropdown>
|
||||
) : null}
|
||||
</nav>
|
||||
|
||||
{currentUser ? (
|
||||
@@ -89,7 +154,7 @@ export function AppTopbar() {
|
||||
退出
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
) : pathname === "/login" ? null : (
|
||||
<Link className="app-login-entry" href="/login">
|
||||
进入登录
|
||||
</Link>
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
| 阶段 3:试点交付准备 | PASS | `project-docs/阶段3-试点交付准备执行记录-2026-05-01.md` |
|
||||
| 阶段 4:试点运行监控与安全加固 | PASS | `project-docs/阶段4-试点运行监控与安全加固执行记录-2026-05-01.md` |
|
||||
| 阶段 5:生产运行、安全、监控、备份与依赖治理 | PASS | `project-docs/阶段5-生产运行安全监控备份与依赖治理执行记录-2026-05-01.md` |
|
||||
| 阶段 6:生产运营自动化与发布门禁固化 | PASS_WITH_WORKTREE_WARN | `project-docs/阶段6-生产运营自动化与发布门禁固化执行记录-2026-05-01.md` |
|
||||
| 阶段 6:生产运营自动化与发布门禁固化 | PASS_PENDING_STRICT_GATE | `project-docs/阶段6-生产运营自动化与发布门禁固化执行记录-2026-05-01.md` |
|
||||
|
||||
阶段 1 已完成源码闭环与生产入口闭环,生产无人 E2E `18/18` 通过。阶段 2 已完成 Review 重试、Knowledge Base 文档重试、Review -> Chat -> Contract 上下文联动、首页交付工作台和阶段 2 自动化无人闭环,本地与生产 E2E 均 `7/7` 通过。阶段 3 已完成系统 readiness、试点交付准备页、默认账号说明、演示样本数据、依赖健康门禁和阶段 3 自动化无人闭环,本地与生产 E2E 均 `5/5` 通过。
|
||||
|
||||
当前后续重点已从“生产运行治理”推进到“生产运营自动化与发布门禁固化”:阶段 6 已完成前端视觉改动梳理、发布门禁聚合脚本、默认发布门禁实跑和严格门禁探针。默认发布门禁 required 项全部 PASS,当前唯一 WARN 是工作区保留未提交前端视觉刷新。
|
||||
当前后续重点已从“生产运行治理”推进到“生产运营自动化与发布门禁固化”:阶段 6 已完成前端视觉改动梳理、发布门禁聚合脚本、默认发布门禁实跑、严格门禁探针和前端视觉刷新验收。下一步是提交视觉刷新并复跑严格发布门禁。
|
||||
|
||||
---
|
||||
|
||||
@@ -219,4 +219,4 @@ AI合同平台当前已经不是从零建设阶段。登录、合同审查、Rev
|
||||
|
||||
## 六、当前一句话结论
|
||||
|
||||
AI合同平台阶段 1、阶段 2、阶段 3、阶段 4、阶段 5 均已完成闭环交付验证,阶段 6 首批发布门禁自动化也已形成闭环。当前系统具备主业务闭环、试点交付准备、生产健康巡检、备份恢复演练、依赖治理、安全基线、生产关键 E2E 回归和统一发布门禁证据;正式发布前需先处理当前未提交前端视觉刷新并复跑严格门禁。
|
||||
AI合同平台阶段 1、阶段 2、阶段 3、阶段 4、阶段 5 均已完成闭环交付验证,阶段 6 首批发布门禁自动化也已形成闭环。当前系统具备主业务闭环、试点交付准备、生产健康巡检、备份恢复演练、依赖治理、安全基线、生产关键 E2E 回归、前端视觉刷新验收和统一发布门禁证据;提交视觉刷新后需复跑严格门禁。
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# 阶段 6:前端视觉刷新验收记录(2026-05-01)
|
||||
|
||||
> 目标:对已完成的前端视觉刷新进行独立验收,确认它不破坏主链路、管理入口、试点交付入口和移动端基本可用性,然后再纳入发布门禁。
|
||||
|
||||
---
|
||||
|
||||
## 一、验收结论
|
||||
|
||||
| 项目 | 状态 | 备注 |
|
||||
|---|---|---|
|
||||
| 前端 lint | PASS | `npm run lint` 通过 |
|
||||
| 前端 production build | PASS | `npm run build` 通过,18 个 app route 生成完成 |
|
||||
| 本地视觉版阶段 1/2/3 E2E | PASS | 使用 `http://127.0.0.1:3014` 前端和试点后端代理,阶段 1/2/3 全部通过 |
|
||||
| 桌面/移动视觉 smoke | PASS | 10 个页面 x 2 个视口,截图和 console error 检查通过 |
|
||||
| 验收锚点兼容修补 | PASS | 恢复 `优先处理摘要`、`运营摘要`、`试点交付准备`、`阶段 3 验收门禁`、`QUICK ACTIONS` 等稳定验收文案 |
|
||||
| 顶部导航兼容修补 | PASS | admin 顶栏保留直接 `/pilot-readiness` 链接,同时保留管理下拉菜单 |
|
||||
|
||||
当前判定:前端视觉刷新可以进入提交和严格发布门禁。
|
||||
|
||||
---
|
||||
|
||||
## 二、本次视觉刷新范围
|
||||
|
||||
| 区域 | 说明 |
|
||||
|---|---|
|
||||
| 全局主题 | 新增视觉主题、卡片、导航、页面布局、移动端约束等样式 |
|
||||
| 顶部导航 | 增加图标化导航、admin 管理下拉、试点交付直接入口 |
|
||||
| 首页 | 公共首页视觉场景、登录后工作台、优先摘要、核心入口和管理员入口重组 |
|
||||
| 登录页 | 增加安全工作台预览和更紧凑的登录提示 |
|
||||
| 发起审查 | 提交流程视觉前置,减少重复说明卡片 |
|
||||
| 审查详情 | Review Copilot、Quick Actions、报告/文件入口集中为行动区 |
|
||||
| 合同生成/详情 | 预览、变量、导出选项和生成内容布局优化 |
|
||||
| 管理页 | 知识库、模板、文件、用户、规则、租户设置、审计日志等页面统一视觉口径 |
|
||||
| 试点交付 | Readiness 门禁页面改为阶段 6 统一视觉风格 |
|
||||
|
||||
---
|
||||
|
||||
## 三、验证记录
|
||||
|
||||
静态和构建:
|
||||
|
||||
```text
|
||||
npm run lint
|
||||
npm run build
|
||||
```
|
||||
|
||||
本地视觉版服务:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:3014
|
||||
BACKEND_BASE_URL=http://192.168.3.90:8001
|
||||
```
|
||||
|
||||
阶段 1/2/3 E2E 产物:
|
||||
|
||||
```text
|
||||
project-docs/qa-artifacts/phase6-visual-refresh/2026-05-01T10-24-37/closed-loop-regression-dev-3014/2026-05-01T10-33-12/
|
||||
```
|
||||
|
||||
视觉 smoke 产物:
|
||||
|
||||
```text
|
||||
project-docs/qa-artifacts/phase6-visual-refresh/2026-05-01T10-24-37/visual-smoke-2026-05-01T10-34-32/
|
||||
```
|
||||
|
||||
视觉 smoke 覆盖页面:
|
||||
|
||||
1. `/`
|
||||
2. `/login`
|
||||
3. `/review`
|
||||
4. `/reviews`
|
||||
5. `/contracts`
|
||||
6. `/knowledge-bases`
|
||||
7. `/templates`
|
||||
8. `/pilot-readiness`
|
||||
9. `/users`
|
||||
|
||||
覆盖视口:
|
||||
|
||||
1. desktop:`1440 x 1000`
|
||||
2. mobile:`390 x 844`
|
||||
|
||||
---
|
||||
|
||||
## 四、发现与修补
|
||||
|
||||
| 问题 | 处理 |
|
||||
|---|---|
|
||||
| 首页视觉刷新后移除了旧 E2E 依赖的 `优先处理摘要` 和 `运营摘要` 文案 | 保留新布局,同时恢复稳定验收锚点 |
|
||||
| 审查详情 Quick Actions 文案大小写与旧 E2E 不一致 | 恢复 `QUICK ACTIONS` 和 `围绕当前审查结果` |
|
||||
| 试点交付页面标题更换后旧 E2E 无法识别 | 恢复 `试点交付准备` 和 `阶段 3 验收门禁` |
|
||||
| admin 试点入口被收进管理下拉后旧 E2E 找不到直接链接 | 顶栏保留 `/pilot-readiness` 直接链接 |
|
||||
| 旧 `3004` 本地服务占用端口且连接本地旧 API 状态 | 改用独立 `3014` 验收服务,代理到试点后端 |
|
||||
|
||||
---
|
||||
|
||||
## 五、下一步
|
||||
|
||||
1. 提交前端视觉刷新和 `.gstack/` 忽略规则。
|
||||
2. 推送到 `192.168.3.90` 仓库。
|
||||
3. 复跑严格发布门禁:`run-production-release-gate.ps1 -RequireCleanWorktree`。
|
||||
4. 若严格门禁 PASS,则阶段 6 从 `PASS_WITH_WORKTREE_WARN` 更新为 `PASS`。
|
||||
@@ -13,7 +13,8 @@
|
||||
| 发布门禁聚合脚本 | READY | 已新增 `scripts/run-production-release-gate.ps1` |
|
||||
| 发布门禁实跑 | WARN | 默认门禁已跑通,required 项全部 PASS,dirty worktree 为 advisory WARN |
|
||||
| 严格门禁探针 | PASS | `-RequireCleanWorktree` 已按预期把未提交视觉刷新升级为 FAIL |
|
||||
| 阶段 6 首批闭环 | PASS_WITH_WORKTREE_WARN | 发布门禁自动化已闭环,正式发布前仍需处理当前前端视觉刷新 |
|
||||
| 前端视觉刷新验收 | PASS | lint、build、阶段 1/2/3 E2E、桌面/移动视觉 smoke 均通过 |
|
||||
| 阶段 6 首批闭环 | PASS_PENDING_STRICT_GATE | 发布门禁自动化已闭环,前端视觉刷新已验收,下一步提交后复跑严格门禁 |
|
||||
|
||||
---
|
||||
|
||||
@@ -26,7 +27,7 @@
|
||||
1. 本轮阶段 6 运维自动化提交不纳入这些前端视觉文件。
|
||||
2. 发布门禁默认模式会记录 dirty worktree 为 WARN。
|
||||
3. 正式发布前必须使用 `-RequireCleanWorktree`,把 dirty worktree 升级为 FAIL。
|
||||
4. 前端视觉刷新后续单独做浏览器验收和提交。
|
||||
4. 前端视觉刷新已完成独立验收,下一步提交后复跑严格发布门禁。
|
||||
|
||||
清单文档:
|
||||
|
||||
@@ -34,6 +35,12 @@
|
||||
project-docs/阶段6-前端视觉改动梳理-2026-05-01.md
|
||||
```
|
||||
|
||||
验收记录:
|
||||
|
||||
```text
|
||||
project-docs/阶段6-前端视觉刷新验收记录-2026-05-01.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、发布门禁脚本
|
||||
@@ -116,4 +123,4 @@ project-docs/qa-artifacts/phase6-release-gate/2026-05-01T09-29-45/
|
||||
2. 发布门禁脚本可复跑,并生成统一汇总报告。
|
||||
3. 默认门禁 required 项全部 PASS。
|
||||
4. 严格门禁可以阻断未提交工作区。
|
||||
5. 正式发布前下一步应先处理或提交当前前端视觉刷新。
|
||||
5. 前端视觉刷新已通过验收,下一步应提交视觉刷新并复跑 `-RequireCleanWorktree` 严格门禁。
|
||||
|
||||
Reference in New Issue
Block a user