/** * Monaco Editor 差异对比演示页面 * * 功能: * - 展示两份合同文本的差异对比 * - 支持逐行高亮显示差异 * - 提供差异导航功能 * - 后续可扩展文件上传功能 */ import { type MetaFunction } from "@remix-run/node"; import { useState, useRef } from "react"; import { DiffEditor } from "@monaco-editor/react"; import type { editor } from "monaco-editor"; export const meta: MetaFunction = () => { return [ { title: "Monaco Diff Editor 演示 - 合同对比" }, { name: "description", content: "使用 Monaco Editor 进行合同文本差异对比" } ]; }; // 示例合同文本 A(原始版本) const CONTRACT_A = `中国烟草合同(原始版本) 第一条 合同双方 甲方:中国烟草总公司广东省公司 乙方:XX供应商有限公司 第二条 合同标的 甲方向乙方采购烟草包装材料,具体型号为: 1. 硬盒包装纸 10000箱 2. 烟用滤棒 5000箱 总金额:人民币壹佰万元整(¥1,000,000.00) 第三条 交付时间 乙方应在签订合同后30个工作日内完成全部交付。 第四条 质量标准 产品应符合国家烟草行业标准 YC/T 207-2014。 第五条 付款方式 甲方在收到货物并验收合格后,于15个工作日内支付全部款项。 第六条 违约责任 1. 乙方延期交付,每延迟一天支付合同总额0.5%的违约金。 2. 产品质量不合格,乙方应无偿更换并承担相应损失。 第七条 争议解决 本合同履行过程中发生的争议,由双方协商解决;协商不成的,提交广州仲裁委员会仲裁。 第八条 其他约定 本合同一式两份,甲乙双方各执一份,具有同等法律效力。 签订日期:2024年1月15日 `; // 示例合同文本 B(修改版本) const CONTRACT_B = `中国烟草合同(修订版本) 第一条 合同双方 甲方:中国烟草总公司广东省公司 乙方:XX供应商有限公司 第二条 合同标的 甲方向乙方采购烟草包装材料,具体型号为: 1. 硬盒包装纸 15000箱(数量增加) 2. 烟用滤棒 5000箱 3. 商标纸 3000箱(新增项目) 总金额:人民币壹佰伍拾万元整(¥1,500,000.00) 第三条 交付时间 乙方应在签订合同后45个工作日内完成全部交付。 如遇不可抗力,交付时间可顺延,但乙方应及时通知甲方。 第四条 质量标准 产品应符合国家烟草行业标准 YC/T 207-2014 及甲方企业标准。 第五条 付款方式 1. 签订合同后,甲方支付合同总额30%作为预付款; 2. 收到货物并验收合格后,于15个工作日内支付剩余70%款项。 第六条 违约责任 1. 乙方延期交付,每延迟一天支付合同总额1%的违约金(违约金比例提高)。 2. 产品质量不合格,乙方应无偿更换并承担相应损失。 3. 甲方延期付款,每延迟一天支付未付款项0.05%的违约金。 第七条 保密条款(新增) 双方应对合同内容及执行过程中获悉的商业秘密承担保密义务,保密期限为合同终止后2年。 第八条 争议解决 本合同履行过程中发生的争议,由双方协商解决;协商不成的,提交广州仲裁委员会仲裁。 第九条 其他约定 本合同一式两份,甲乙双方各执一份,具有同等法律效力。 签订日期:2024年3月20日 `; export default function MonacoDemoPage() { const [originalText, setOriginalText] = useState(CONTRACT_A); const [modifiedText, setModifiedText] = useState(CONTRACT_B); const diffEditorRef = useRef(null); const [diffCount, setDiffCount] = useState(0); const [currentDiff, setCurrentDiff] = useState(0); // Monaco Editor 挂载后的回调 const handleEditorDidMount = (editor: editor.IStandaloneDiffEditor) => { diffEditorRef.current = editor; // 获取差异数量 const lineChanges = editor.getLineChanges(); if (lineChanges) { setDiffCount(lineChanges.length); console.log(`发现 ${lineChanges.length} 处差异:`, lineChanges); } }; // 跳转到下一个差异 const goToNextDiff = () => { if (!diffEditorRef.current) return; const lineChanges = diffEditorRef.current.getLineChanges(); if (!lineChanges || lineChanges.length === 0) return; const nextIndex = (currentDiff + 1) % lineChanges.length; const nextChange = lineChanges[nextIndex]; // 跳转到差异位置(修改后的编辑器) const modifiedEditor = diffEditorRef.current.getModifiedEditor(); modifiedEditor.revealLineInCenter(nextChange.modifiedStartLineNumber); modifiedEditor.setPosition({ lineNumber: nextChange.modifiedStartLineNumber, column: 1 }); setCurrentDiff(nextIndex); }; // 跳转到上一个差异 const goToPreviousDiff = () => { if (!diffEditorRef.current) return; const lineChanges = diffEditorRef.current.getLineChanges(); if (!lineChanges || lineChanges.length === 0) return; const prevIndex = currentDiff === 0 ? lineChanges.length - 1 : currentDiff - 1; const prevChange = lineChanges[prevIndex]; // 跳转到差异位置(修改后的编辑器) const modifiedEditor = diffEditorRef.current.getModifiedEditor(); modifiedEditor.revealLineInCenter(prevChange.modifiedStartLineNumber); modifiedEditor.setPosition({ lineNumber: prevChange.modifiedStartLineNumber, column: 1 }); setCurrentDiff(prevIndex); }; // 重置为示例文本 const resetToExample = () => { setOriginalText(CONTRACT_A); setModifiedText(CONTRACT_B); setCurrentDiff(0); // 重新计算差异数量 setTimeout(() => { if (diffEditorRef.current) { const lineChanges = diffEditorRef.current.getLineChanges(); if (lineChanges) { setDiffCount(lineChanges.length); } } }, 100); }; return (
{/* 页面头部 */}

Monaco Editor - 合同差异对比演示

使用 Monaco Diff Editor 逐行对比两份合同文本的差异

{/* 工具栏 */}
{/* 差异统计 */}
发现 {diffCount} 处差异 {diffCount > 0 && ` (当前: ${currentDiff + 1}/${diffCount})`}
{/* 导航按钮 */}
{/* 重置按钮 */} {/* 未来扩展:上传按钮 */}
{/* 说明信息 */}
差异高亮说明:
  • 绿色:新增的内容
  • 红色:删除的内容
  • 黄色背景:修改的行内差异
{/* Diff Editor 主体 */}
{/* 页面底部信息 */}
基于 Monaco Editor (VS Code 核心编辑器) 提示:可使用鼠标滚轮缩放,Ctrl+F 搜索
); }