fix(engine): 修复 DOCX 公式因 undefined 虚根在 Word 中丢失的缺陷
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
Trash2,
|
||||
Shield,
|
||||
Layers,
|
||||
FileText,
|
||||
} from 'lucide-react';
|
||||
|
||||
interface DeckDrawerProps {
|
||||
@@ -56,7 +57,29 @@ export const DeckDrawer: React.FC<DeckDrawerProps> = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const renderBadge = (state: DocLifecycleState, isClaimed: boolean) => {
|
||||
const renderBadge = (state: DocLifecycleState, isClaimed: boolean, isTemplateOnly?: boolean) => {
|
||||
if (isTemplateOnly || state === 'template') {
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
fontSize: '11px',
|
||||
fontWeight: 600,
|
||||
color: '#0284c7',
|
||||
background: '#e0f2fe',
|
||||
border: '1px solid #bae6fd',
|
||||
padding: '2px 8px',
|
||||
borderRadius: 'var(--r-pill)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '3px',
|
||||
}}
|
||||
title="该文档为项目标准预置模板副本,点击可全景查阅大纲与章节规范"
|
||||
>
|
||||
<FileText size={11} />
|
||||
<span>📄 初始模板</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
switch (state) {
|
||||
case 'approved':
|
||||
return (
|
||||
@@ -388,7 +411,7 @@ export const DeckDrawer: React.FC<DeckDrawerProps> = ({
|
||||
<span style={{ fontSize: '12.5px', fontWeight: 600, color: 'var(--text-strong)' }}>
|
||||
{doc.title}
|
||||
</span>
|
||||
{renderBadge(doc.lifecycleState, doc.isClaimed)}
|
||||
{renderBadge(doc.lifecycleState, doc.isClaimed, doc.isTemplateOnly)}
|
||||
</div>
|
||||
|
||||
<div style={{ fontSize: '11px', color: 'var(--text-muted)', display: 'flex', gap: '12px' }}>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert';
|
||||
import { DeliverableItem } from '../../types/workbench';
|
||||
|
||||
test('isTemplateOnly 交付物应正确识别为初始模板展示态', () => {
|
||||
const item: DeliverableItem = {
|
||||
docId: 'doc-p101-intake',
|
||||
projectId: 101,
|
||||
taskKey: 'intake',
|
||||
title: '用户初始需求收集表',
|
||||
stage: 'V0.1',
|
||||
category: 'workflow',
|
||||
creatorId: 1,
|
||||
creatorName: '张工',
|
||||
isFixedWorkflow: true,
|
||||
isTemplateOnly: true,
|
||||
lifecycleState: 'template',
|
||||
isClaimed: false,
|
||||
author: '张工',
|
||||
reviewer: '李主管',
|
||||
approver: '王总监',
|
||||
updatedAt: '刚刚',
|
||||
};
|
||||
|
||||
assert.strictEqual(item.isTemplateOnly, true);
|
||||
const badgeLabel = item.isTemplateOnly ? '📄 初始模板' : item.lifecycleState;
|
||||
assert.strictEqual(badgeLabel, '📄 初始模板');
|
||||
});
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from './docxRenderer';
|
||||
import { buildXlsxWorkbook, exportDocumentToXlsx } from './xlsxRenderer';
|
||||
import { Packer, ImportedXmlComponent } from 'docx';
|
||||
import JSZip from 'jszip';
|
||||
|
||||
const SAMPLE_MD = `# 4K超高清荧光内窥镜系统立项与可行性报告
|
||||
|
||||
@@ -59,11 +60,13 @@ test('DOCX 渲染器行内文本与公式解析测试 (parseInlineContent)', ()
|
||||
|
||||
test('DOCX 渲染器 LaTeX 原生 OMML 转换引擎测试 (latexToOmml)', () => {
|
||||
const latex = 'SNR = 10 \\cdot \\log_{10}\\left(\\frac{P_{signal}}{P_{noise}}\\right)';
|
||||
const displayComp = latexToOmml(latex, true);
|
||||
assert.ok(displayComp, '展示级公式应当成功转换为 m:oMathPara 组件');
|
||||
const comp = latexToOmml(latex);
|
||||
assert.ok(comp, '公式应当成功转换为 m:oMath 组件');
|
||||
assert.equal((comp as any).rootKey, 'm:oMath', 'rootKey 必须为 m:oMath,绝不能为 undefined');
|
||||
|
||||
const inlineComp = latexToOmml('\\sigma_{ambient}', false);
|
||||
const inlineComp = latexToOmml('\\sigma_{ambient}');
|
||||
assert.ok(inlineComp, '行内公式应当成功转换为 m:oMath 组件');
|
||||
assert.equal((inlineComp as any).rootKey, 'm:oMath');
|
||||
});
|
||||
|
||||
test('DOCX 渲染器 Mermaid 语法净化与容错测试 (sanitizeMermaidCode)', () => {
|
||||
@@ -100,6 +103,12 @@ test('DOCX 渲染器全量编译测试 (buildDocxModel & exportDocumentToDocx)',
|
||||
const buffer = await Packer.toBuffer(doc);
|
||||
assert.ok(buffer.length > 5000, `生成的 DOCX Buffer 应当大于 5KB,实际为 ${buffer.length} 字节`);
|
||||
|
||||
// 关键回归防线:解压验证 word/document.xml 中杜绝 <undefined> 且包含合法的 <m:oMath>
|
||||
const zip = await JSZip.loadAsync(buffer);
|
||||
const docXml = await zip.file('word/document.xml')!.async('text');
|
||||
assert.ok(!docXml.includes('<undefined>'), 'Word document.xml 绝对不得包含 <undefined> 破坏性标签');
|
||||
assert.ok(docXml.includes('<m:oMath'), 'Word document.xml 必须包含合法的微软 Office Math (<m:oMath>) 节点');
|
||||
|
||||
// 验证 exportDocumentToDocx 输出 Blob 正常
|
||||
const blob = await exportDocumentToDocx(ast);
|
||||
assert.equal(blob.type, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
|
||||
|
||||
Reference in New Issue
Block a user