Commit ca450b0c by qlintonger xeno

feat: 使用LCS对比

parent 50112533
...@@ -20,7 +20,6 @@ declare module 'vue' { ...@@ -20,7 +20,6 @@ declare module 'vue' {
NLayoutHeader: typeof import('naive-ui')['NLayoutHeader'] NLayoutHeader: typeof import('naive-ui')['NLayoutHeader']
NMessageProvider: typeof import('naive-ui')['NMessageProvider'] NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NModalProvider: typeof import('naive-ui')['NModalProvider'] NModalProvider: typeof import('naive-ui')['NModalProvider']
NResult: typeof import('naive-ui')['NResult']
NSpace: typeof import('naive-ui')['NSpace'] NSpace: typeof import('naive-ui')['NSpace']
NTree: typeof import('naive-ui')['NTree'] NTree: typeof import('naive-ui')['NTree']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
......
...@@ -2,17 +2,18 @@ ...@@ -2,17 +2,18 @@
<!--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) 测试修改</TITLE> <TITLE>Remove the Engine's QEC(V2500-A5 series)</TITLE>
<TOPIC CK-LEVEL="C"> <TOPIC CK-LEVEL="C">
<TITLEC>飞机/发动机基本信息</TITLEC> <TITLEC>飞机/发动机基本信息</TITLEC>
<TITLE>AIRCRAFT/ENGINE INFORMATION</TITLE> <TITLE>AIRCRAFT/ENGINE INFORMATION</TITLE>
<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>
......
...@@ -24,3 +24,5 @@ export const nodeSet = [ ...@@ -24,3 +24,5 @@ export const nodeSet = [
'STEP', 'STEP',
'RECORD-LINE' 'RECORD-LINE'
] ]
export const contentHoldNode = ['PARA', 'TITLE']
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import { NewTreeModification, OldTreeModification, TreeRenderResult, TreeRenderResultFlatted } from '@/lib/XMLProcessor/src/typing' import { NewTreeModification, OldTreeModification, TreeRenderResult, TreeRenderResultFlatted } from '@/lib/XMLProcessor/src/typing'
import { md5 } from 'js-md5' import { md5 } from 'js-md5'
import {getUUID} from '../utils/uuid.ts' import {getUUID} from '../utils/uuid.ts'
import { cloneDeep } from 'lodash' import {dualCompare} from '@/lib/XMLProcessor/src/core/dualCompare.ts'
// 定义 Processing 类,用于处理 XML 数据 // 定义 Processing 类,用于处理 XML 数据
export class Processing { export class Processing {
...@@ -41,7 +41,8 @@ export class Processing { ...@@ -41,7 +41,8 @@ export class Processing {
dualCompareFromString( dualCompareFromString(
xmlStringOld: string, xmlStringOld: string,
xmlStringNew: string, xmlStringNew: string,
bothHandledNode: string[] bothHandledNode: string[],
contentHoldNode: string[]
): { ): {
dataForNew: NewTreeModification dataForNew: NewTreeModification
dataForOld: OldTreeModification dataForOld: OldTreeModification
...@@ -59,106 +60,24 @@ export class Processing { ...@@ -59,106 +60,24 @@ export class Processing {
treeA.documentElement.querySelectorAll(':not([data-key])').forEach((node) => node.remove()) treeA.documentElement.querySelectorAll(':not([data-key])').forEach((node) => node.remove())
treeB.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 resultAFlatted = this.flattenTree(resultA).filter((item) => {
const resultBFlatted = this.flattenTree(resultB) return contentHoldNode.includes(item.label)
})
const dataForOld: OldTreeModification = { const resultBFlatted = this.flattenTree(resultB).filter((item) => {
Changed: [], return contentHoldNode.includes(item.label)
Deleted: [] })
}
const dataForNew: NewTreeModification = {
Changed: [],
Added: []
}
// 获取序列号、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 results = dualCompare(resultAFlatted, resultBFlatted)
const stillDiffInOld = nonSameInOld.filter(a => { .filter(a=>a.type !=='same');
return !nodeChangedInOld.find(v => v.key === a.key);
});
let stillDiffInNew = nonSameInNew.filter(a => { console.log('results here', results)
return !nodeChangedInNew.find(v => v.key === a.key);
});
// 以PARA和TITLE构建一组树,只追踪到上一级父节点即可,仅对新增节点有效 const dataForOld: OldTreeModification = {
// 首先筛选出基层节点PARA和TITLE即可 Deleted: results.filter(item => item.type === 'deleted').map(a=>a.item)
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(); const dataForNew: NewTreeModification = {
let targetChildren = null Added: results.filter(item => item.type === 'added').map(a=>a.item)
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 { return {
dataForNew, 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' ...@@ -2,12 +2,12 @@ import { Processing } from '@/lib/XMLProcessor/src/core/Processing.ts'
import { Plugin, getCurrentInstance } from 'vue' import { Plugin, getCurrentInstance } from 'vue'
import TextA from '@/assets/file/CES-QEC-V250-A.xml?raw' import TextA from '@/assets/file/CES-QEC-V250-A.xml?raw'
import TextB from '@/assets/file/Trans-Convert.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() const p = new Processing()
// @ts-ignore // @ts-ignore
window.$p = function () { window.$p = function () {
let nodeV = p.dualCompareFromString(TextA, TextB, nodeSet) let nodeV = p.dualCompareFromString(TextA, TextB, nodeSet, contentHoldNode)
console.log('v-h', nodeV) console.log('v-h', nodeV)
} }
export const XMLProcessing: Plugin = function (app) { export const XMLProcessing: Plugin = function (app) {
......
...@@ -18,11 +18,9 @@ export type TreeRenderResultFlatted = { ...@@ -18,11 +18,9 @@ export type TreeRenderResultFlatted = {
} }
export type OldTreeModification = { export type OldTreeModification = {
Changed: TreeRenderResultFlatted[],
Deleted: TreeRenderResultFlatted[] Deleted: TreeRenderResultFlatted[]
} }
export type NewTreeModification = { export type NewTreeModification = {
Added: TreeRenderResultFlatted[], Added: TreeRenderResultFlatted[],
Changed: TreeRenderResultFlatted[],
} }
\ No newline at end of file
...@@ -6,7 +6,7 @@ import { getUUID } from '@/utils' ...@@ -6,7 +6,7 @@ import { getUUID } from '@/utils'
import { compareLeftData, compareRightData, showCompare } from '../constants/compare' import { compareLeftData, compareRightData, showCompare } from '../constants/compare'
import TextA from '@/assets/file/CES-QEC-V250-A.xml?raw' import TextA from '@/assets/file/CES-QEC-V250-A.xml?raw'
import TextB from '@/assets/file/Trans-Convert.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' import { handleLeftDifferent, handleRightDifferent } from './compare'
export const handleCreated = (editor: IDomEditor) => { export const handleCreated = (editor: IDomEditor) => {
...@@ -100,7 +100,7 @@ export const uploadXml = async () => { ...@@ -100,7 +100,7 @@ export const uploadXml = async () => {
export const compareXml = () => { export const compareXml = () => {
showCompare.value = true showCompare.value = true
const xmlProcessing = useXMLProcessing() const xmlProcessing = useXMLProcessing()
const res = xmlProcessing.dualCompareFromString(TextA, TextB, nodeSet) const res = xmlProcessing.dualCompareFromString(TextA, TextB, nodeSet, contentHoldNode)
console.log(res) console.log(res)
//渲染数据 //渲染数据
compareLeftData.value = res.xmlContentOld 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