Commit 7f96abe1 by qlintonger xeno

feat: 使用diff_match_patch来对比完毕+1

parent 98bdf0a0
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"dayjs": "^1.11.11", "dayjs": "^1.11.11",
"diff": "^7.0.0",
"js-md5": "^0.8.3", "js-md5": "^0.8.3",
"less": "^4.2.0", "less": "^4.2.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
...@@ -1949,6 +1950,15 @@ ...@@ -1949,6 +1950,15 @@
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
"dev": true "dev": true
}, },
"node_modules/diff": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
"integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/dlv": { "node_modules/dlv": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz", "resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz",
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"dayjs": "^1.11.11", "dayjs": "^1.11.11",
"diff": "^7.0.0",
"js-md5": "^0.8.3", "js-md5": "^0.8.3",
"less": "^4.2.0", "less": "^4.2.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
......
...@@ -2,14 +2,15 @@ ...@@ -2,14 +2,15 @@
<!--Arbortext, Inc., 1988-2013, v.4002--> <!--Arbortext, Inc., 1988-2013, v.4002-->
<!DOCTYPE JOBCARD PUBLIC "-//CEA-TEXT//DTD JOBCARD-VER1//EN" "JOBCARD.dtd" [ <!DOCTYPE JOBCARD PUBLIC "-//CEA-TEXT//DTD JOBCARD-VER1//EN" "JOBCARD.dtd" [
<!ENTITY nbsp "&#160;"> <!ENTITY nbsp "&#160;">
<!ENTITY rsquo "&#8217;"> <!ENTITY rsquo "&#8217;">
]> ]>
<JOBCARD> <JOBCARD>
<EOTK-HEADER></EOTK-HEADER> <EOTK-HEADER></EOTK-HEADER>
<CEP> <CEP>
<EFFECT EFFRG="001999"></EFFECT> <EFFECT EFFRG="001999"></EFFECT>
<TITLEC>发动机QEC拆卸(V2500-A5系列)</TITLEC> <TITLEC>发动机QEC拆卸(V2500-A5系列)</TITLEC>
<TITLE>Remove the Engine's QEC(V2500-A5 series)XXXX</TITLE> <TITLE>Remove the Engine's QEC(V2500-A5 series)XXX</TITLE>
<TITLE>Remove the Engine's QEC(V2500-A5 series)XXX</TITLE>
<TOPIC CK-LEVEL="C"> <TOPIC CK-LEVEL="C">
<TITLEC>飞机/发动机基本信息</TITLEC> <TITLEC>飞机/发动机基本信息</TITLEC>
<TITLE>AIRCRAFT/ENGINE INFORMATION</TITLE> <TITLE>AIRCRAFT/ENGINE INFORMATION</TITLE>
...@@ -26,6 +27,10 @@ ...@@ -26,6 +27,10 @@
</NOTE> </NOTE>
<SIGNOFF/> <SIGNOFF/>
</STEP> </STEP>
</TOPIC>
<TOPIC CK-LEVEL="C">
<TITLEC>飞机/发动机基本信息</TITLEC>
<TITLE>AIRCRAFT/ENGINE INFORMATION</TITLE>
<STEP CK-LEVEL="C"> <STEP CK-LEVEL="C">
<EFFECT EFFRG="001999"></EFFECT> <EFFECT EFFRG="001999"></EFFECT>
<RECORD-LINE> <RECORD-LINE>
......
export function reconstructTree(data: any[]) {
// Step 1: Sort data by chained array to ensure parent-child order
const sortedData = [...data].sort((a, b) => {
const aChained = a.chained;
const bChained = b.chained;
for (let i = 0; i < Math.max(aChained.length, bChained.length); i++) {
const aVal = aChained[i] || 0;
const bVal = bChained[i] || 0;
if (aVal !== bVal) return aVal - bVal;
}
return 0;
});
// Step 2: Build a map for quick lookup and to track children
const nodeMap = new Map();
const result: any[] = [];
sortedData.forEach(item => {
nodeMap.set(item.key, { ...item, children: [] });
});
// Step 3: Assign children to parents based on chained hierarchy
sortedData.forEach(item => {
const node = nodeMap.get(item.key);
const chained = item.chained;
// Find the parent by looking for the closest shorter chained array
let parent = null;
for (let i = chained.length - 1; i > 0; i--) {
const parentChained = chained.slice(0, i);
const parentKey = sortedData.find(d =>
d.chained.length === parentChained.length &&
d.chained.every((val: any, idx: number) => val === parentChained[idx])
)?.key;
if (parentKey) {
parent = nodeMap.get(parentKey);
break;
}
}
if (parent) {
parent.children.push(node);
} else {
result.push(node);
}
});
// Step 4: Convert to XML
function createXmlNode(node: any, doc: Document) {
const element = doc.createElement(node.label);
// Set data-key attribute
element.setAttribute('data-key', node.key);
// Set data-type attribute if type is not null
if (node.type !== null) {
element.setAttribute('data-type', node.type);
}
// Set textContent for TITLE or PARA nodes
if (node.label === 'TITLE' || node.label === 'PARA') {
element.textContent = node.textContent;
}
// Recursively add children
node.children.forEach((child: any) => {
element.appendChild(createXmlNode(child, doc));
});
return element;
}
// Create XML document
const doc = document.implementation.createDocument('', '', null);
// Use the first element (JOBCARD) as the root if it exists
if (result.length > 0) {
const rootNode = createXmlNode(result[0], doc)
doc.appendChild(rootNode)
}
// Serialize to XML string
const serializer = new XMLSerializer();
return serializer.serializeToString(doc);
}
\ No newline at end of file
import { TreeRenderResultFlatted } from '@/lib/XMLProcessor/src/typing'
import { cloneDeep } from 'lodash'
export function dualCompare(oldItems: TreeRenderResultFlatted[], newItems: TreeRenderResultFlatted[]) {
const oldItemsCount = oldItems.length
const newItemsCount = newItems.length
const dp: number[][] = Array.from({ length: oldItemsCount + 1 }, () => Array(newItemsCount + 1).fill(0))
for (let i = 1; i <= oldItemsCount; i++) {
for (let j = 1; j <= newItemsCount; j++) {
if (oldItems[i - 1].hash === newItems[j - 1].hash) {
dp[i][j] = dp[i - 1][j - 1] + 1
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])
}
}
}
let i = oldItemsCount,
j = newItemsCount
const result: Array<{ type: 'same' | 'deleted' | 'added' | 'changedOld' | 'changedNew'; item: TreeRenderResultFlatted }> = []
while (i > 0 && j > 0) {
if (oldItems[i - 1].hash === newItems[j - 1].hash) {
// result.unshift({ type: 'same', item: cloneDeep(oldItems[i - 1]) })
i--
j--
} else if (dp[i - 1][j] > dp[i][j - 1]) {
result.unshift({
type: 'deleted',
item: cloneDeep(oldItems[i - 1])
})
i--
} else {
result.unshift({
type: 'added',
item: cloneDeep(newItems[j - 1])
})
j--
}
}
while (i > 0) {
result.unshift({
type: 'deleted',
item: cloneDeep(oldItems[i - 1])
})
i--
}
while (j > 0) {
result.unshift({
type: 'added',
item: cloneDeep(newItems[j - 1])
})
j--
}
return result
}
...@@ -19,6 +19,10 @@ export type TreeRenderResultFlatted = { ...@@ -19,6 +19,10 @@ export type TreeRenderResultFlatted = {
node: Element node: Element
} }
export type TreeReconstructed = TreeRenderResultFlatted & {
type: 'added' | 'removed' | null | 'placeholder'
}
export type OldTreeModification = { export type OldTreeModification = {
Deleted: TreeRenderResultFlatted[] Deleted: TreeRenderResultFlatted[]
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment