Commit ca450b0c by qlintonger xeno

feat: 使用LCS对比

parent 50112533
......@@ -20,7 +20,6 @@ declare module 'vue' {
NLayoutHeader: typeof import('naive-ui')['NLayoutHeader']
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NModalProvider: typeof import('naive-ui')['NModalProvider']
NResult: typeof import('naive-ui')['NResult']
NSpace: typeof import('naive-ui')['NSpace']
NTree: typeof import('naive-ui')['NTree']
RouterLink: typeof import('vue-router')['RouterLink']
......
......@@ -2,17 +2,18 @@
<!--Arbortext, Inc., 1988-2013, v.4002-->
<!DOCTYPE JOBCARD PUBLIC "-//CEA-TEXT//DTD JOBCARD-VER1//EN" "JOBCARD.dtd" [
<!ENTITY nbsp "&#160;">
<!ENTITY rsquo "&#8217;">
<!ENTITY rsquo "&#8217;">
]>
<JOBCARD>
<EOTK-HEADER></EOTK-HEADER>
<CEP>
<EFFECT EFFRG="001999"></EFFECT>
<TITLEC>发动机QEC拆卸(V2500-A5系列)</TITLEC>
<TITLE>Remove the Engine's QEC(V2500-A5 series) 测试修改</TITLE>
<TITLE>Remove the Engine's QEC(V2500-A5 series)</TITLE>
<TOPIC CK-LEVEL="C">
<TITLEC>飞机/发动机基本信息</TITLEC>
<TITLE>AIRCRAFT/ENGINE INFORMATION</TITLE>
<TITLE>AIRCRAFT/ENGINE INFORMATION</TITLE>
<STEP CK-LEVEL="C">
<EFFECT EFFRG="001999"></EFFECT>
<RECORD-LINE>
......
......@@ -24,3 +24,5 @@ export const nodeSet = [
'STEP',
'RECORD-LINE'
]
export const contentHoldNode = ['PARA', 'TITLE']
\ No newline at end of file
......@@ -2,7 +2,7 @@
import { NewTreeModification, OldTreeModification, TreeRenderResult, TreeRenderResultFlatted } from '@/lib/XMLProcessor/src/typing'
import { md5 } from 'js-md5'
import {getUUID} from '../utils/uuid.ts'
import { cloneDeep } from 'lodash'
import {dualCompare} from '@/lib/XMLProcessor/src/core/dualCompare.ts'
// 定义 Processing 类,用于处理 XML 数据
export class Processing {
......@@ -41,7 +41,8 @@ export class Processing {
dualCompareFromString(
xmlStringOld: string,
xmlStringNew: string,
bothHandledNode: string[]
bothHandledNode: string[],
contentHoldNode: string[]
): {
dataForNew: NewTreeModification
dataForOld: OldTreeModification
......@@ -59,106 +60,24 @@ export class Processing {
treeA.documentElement.querySelectorAll(':not([data-key])').forEach((node) => node.remove())
treeB.documentElement.querySelectorAll(':not([data-key])').forEach((node) => node.remove())
// 扁平化所有节点
const resultAFlatted = this.flattenTree(resultA)
const resultBFlatted = this.flattenTree(resultB)
const resultAFlatted = this.flattenTree(resultA).filter((item) => {
return contentHoldNode.includes(item.label)
})
const resultBFlatted = this.flattenTree(resultB).filter((item) => {
return contentHoldNode.includes(item.label)
})
const results = dualCompare(resultAFlatted, resultBFlatted)
.filter(a=>a.type !=='same');
console.log('results here', results)
const dataForOld: OldTreeModification = {
Changed: [],
Deleted: []
Deleted: results.filter(item => item.type === 'deleted').map(a=>a.item)
}
const dataForNew: NewTreeModification = {
Changed: [],
Added: []
Added: results.filter(item => item.type === 'added').map(a=>a.item)
}
// 获取序列号、hash以及标签中任一不相同的节点
const nonSameInOld = resultAFlatted.filter(a => {
return !resultBFlatted.find(v =>
v.chained.join('$') === a.chained.join('$') &&
v.hash === a.hash &&
a.label === v.label
);
});
const nonSameInNew = resultBFlatted.filter(a => {
return !resultAFlatted.find(v =>
v.chained.join('$') === a.chained.join('$') &&
v.hash === a.hash &&
a.label === v.label
);
});
// 如果标签和序列号相同,但是hash不同,则视为修改
const nodeChangedInOld = nonSameInOld.filter(a => {
return nonSameInNew.find(v =>
v.chained.join('$') === a.chained.join('$') &&
a.label === v.label
);
});
const nodeChangedInNew = nonSameInNew.filter(a => {
return nonSameInOld.find(v =>
v.chained.join('$') === a.chained.join('$') &&
a.label === v.label
);
});
// 再获取剩下来不同的节点
const stillDiffInOld = nonSameInOld.filter(a => {
return !nodeChangedInOld.find(v => v.key === a.key);
});
let stillDiffInNew = nonSameInNew.filter(a => {
return !nodeChangedInNew.find(v => v.key === a.key);
});
// 以PARA和TITLE构建一组树,只追踪到上一级父节点即可,仅对新增节点有效
// 首先筛选出基层节点PARA和TITLE即可
let allBaseNodeInNew = stillDiffInNew.filter(a => {
return a.label === 'PARA' || a.label === 'TITLE';
});
// 构建树节点
let newDom = allBaseNodeInNew.map(a => {
let chainedParentNode: TreeRenderResultFlatted[] = [a];
let currentA = treeB.documentElement.querySelector(`[data-key="${a.key}"]`)
while (currentA) {
currentA = currentA.parentElement
if (!currentA || currentA.tagName === 'JOBCARD') {
break
}
let keyVal = currentA.getAttribute('data-key');
let foundParentInNew = stillDiffInNew.find(v=>v.key === keyVal);
if (foundParentInNew) {
chainedParentNode.push(foundParentInNew)
}
}
chainedParentNode.reverse();
let targetChildren = null
return chainedParentNode.reduce((acc, cur) => {
if (!acc) {
// @ts-ignore
cur.children = []
acc = cur
} else {
targetChildren.push(cur)
// @ts-ignore
cur.children = []
}
// @ts-ignore
targetChildren = cur.children
return acc;
}, null as null | TreeRenderResult);
});
// 将修改的节点添加到结果中
dataForOld.Changed.push(...nodeChangedInOld);
dataForNew.Changed.push(...nodeChangedInNew);
// 将删除的节点添加到结果中
dataForOld.Deleted.push(...stillDiffInOld);
// 将新增的节点添加到结果中
// @ts-ignore
dataForNew.Added.push(...newDom);
return {
dataForNew,
......
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 && oldItems[i - 1].label === newItems[j - 1].label) {
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'; item: TreeRenderResultFlatted }> = []
while (i > 0 && j > 0) {
if (oldItems[i - 1].hash === newItems[j - 1].hash && oldItems[i - 1].label === newItems[j - 1].label) {
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
}
......@@ -2,12 +2,12 @@ import { Processing } from '@/lib/XMLProcessor/src/core/Processing.ts'
import { Plugin, getCurrentInstance } from 'vue'
import TextA from '@/assets/file/CES-QEC-V250-A.xml?raw'
import TextB from '@/assets/file/Trans-Convert.xml?raw'
import { nodeSet } from '@/configs/node.config'
import { nodeSet, contentHoldNode } from '@/configs/node.config'
const p = new Processing()
// @ts-ignore
window.$p = function () {
let nodeV = p.dualCompareFromString(TextA, TextB, nodeSet)
let nodeV = p.dualCompareFromString(TextA, TextB, nodeSet, contentHoldNode)
console.log('v-h', nodeV)
}
export const XMLProcessing: Plugin = function (app) {
......
......@@ -18,11 +18,9 @@ export type TreeRenderResultFlatted = {
}
export type OldTreeModification = {
Changed: TreeRenderResultFlatted[],
Deleted: TreeRenderResultFlatted[]
}
export type NewTreeModification = {
Added: TreeRenderResultFlatted[],
Changed: TreeRenderResultFlatted[],
}
\ No newline at end of file
......@@ -6,7 +6,7 @@ import { getUUID } from '@/utils'
import { compareLeftData, compareRightData, showCompare } from '../constants/compare'
import TextA from '@/assets/file/CES-QEC-V250-A.xml?raw'
import TextB from '@/assets/file/Trans-Convert.xml?raw'
import { nodeSet } from '@/configs/node.config'
import { nodeSet, contentHoldNode } from '@/configs/node.config'
import { handleLeftDifferent, handleRightDifferent } from './compare'
export const handleCreated = (editor: IDomEditor) => {
......@@ -100,7 +100,7 @@ export const uploadXml = async () => {
export const compareXml = () => {
showCompare.value = true
const xmlProcessing = useXMLProcessing()
const res = xmlProcessing.dualCompareFromString(TextA, TextB, nodeSet)
const res = xmlProcessing.dualCompareFromString(TextA, TextB, nodeSet, contentHoldNode)
console.log(res)
//渲染数据
compareLeftData.value = res.xmlContentOld
......
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