Commit b909fba1 by qlintonger xeno

feat: 新增双向对比基本算法逻辑

parent 09a08709
...@@ -69,6 +69,10 @@ ...@@ -69,6 +69,10 @@
<PARA>Ref. EM TASK 71-00-02-000-026-B00 (A5-PPBU) - Remove The EBU Fan Case Brackets, Removal-026.</PARA> <PARA>Ref. EM TASK 71-00-02-000-026-B00 (A5-PPBU) - Remove The EBU Fan Case Brackets, Removal-026.</PARA>
</L2ITEM> </L2ITEM>
<L2ITEM> <L2ITEM>
<PARAC>拆卸EBU风扇机匣支架,按EM TASK 71-00-02-000-026-B00--EDC。</PARAC>
<PARA>Ref. EM TASK 71-00-02-000-026-B00 (A5-PPBU) - Remove The EBU Fan Case Brackets, Removal-026--EDC.</PARA>
</L2ITEM>
<L2ITEM>
<PARAC>拆卸低压中压机匣支架,按EM TASK 71-00-02-000-027-B00。</PARAC> <PARAC>拆卸低压中压机匣支架,按EM TASK 71-00-02-000-027-B00。</PARAC>
<PARA>Ref. EM TASK 71-00-02-000-027-B00 (A5-PPBU) - Remove The LP Compressor/Intermediate Case Brackets, Removal-027.</PARA> <PARA>Ref. EM TASK 71-00-02-000-027-B00 (A5-PPBU) - Remove The LP Compressor/Intermediate Case Brackets, Removal-027.</PARA>
</L2ITEM> </L2ITEM>
......
...@@ -9,17 +9,17 @@ ...@@ -9,17 +9,17 @@
<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)123</TITLE>
<TOPIC CK-LEVEL="C"> <TOPIC CK-LEVEL="C">
<TITLEC>飞机/发动机基本信息123</TITLEC> <TITLEC>飞机/发动机基本信息</TITLEC>
<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>-->
<PARAC>发动机序号</PARAC> <!-- <PARAC>发动机序号</PARAC>-->
<PARA>Engine SN</PARA> <!-- <PARA>Engine SN</PARA>-->
<RECORD></RECORD> <!-- <RECORD></RECORD>-->
</RECORD-LINE> <!-- </RECORD-LINE>-->
<NOTE> <NOTE>
<PARAC>开始工作前请记录。</PARAC> <PARAC>开始工作前请记录。</PARAC>
<PARA>Please record before starting work.</PARA> <PARA>Please record before starting work.</PARA>
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
</STEP> </STEP>
<STEP CK-LEVEL="C"> <STEP CK-LEVEL="C">
<EFFECT EFFRG="001999"></EFFECT> <EFFECT EFFRG="001999"></EFFECT>
<PARAC>按照EB-2020-V250-70-201最新版,完成发动机的出库检查---修改1</PARAC> <PARAC>按照EB-2020-V250-70-201最新版,完成发动机的出库检查。</PARAC>
<PARA>Finish the engine outgoing check ref thelatest version of EB-2020-V250-70-201.</PARA> <PARA>Finish the engine outgoing check ref thelatest version of EB-2020-V250-70-201.</PARA>
<SIGNOFF/> <SIGNOFF/>
</STEP> </STEP>
...@@ -69,6 +69,10 @@ ...@@ -69,6 +69,10 @@
<PARA>Ref. EM TASK 71-00-02-000-026-B00 (A5-PPBU) - Remove The EBU Fan Case Brackets, Removal-026.</PARA> <PARA>Ref. EM TASK 71-00-02-000-026-B00 (A5-PPBU) - Remove The EBU Fan Case Brackets, Removal-026.</PARA>
</L2ITEM> </L2ITEM>
<L2ITEM> <L2ITEM>
<PARAC>拆卸EBU风扇机匣支架,按EM TASK 71-00-02-000-026-B00--EDC。</PARAC>
<PARA>Ref. EM TASK 71-00-02-000-026-B00 (A5-PPBU) - Remove The EBU Fan Case Brackets, Removal-026--EDC.</PARA>
</L2ITEM>
<L2ITEM>
<PARAC>拆卸低压中压机匣支架,按EM TASK 71-00-02-000-027-B00。</PARAC> <PARAC>拆卸低压中压机匣支架,按EM TASK 71-00-02-000-027-B00。</PARAC>
<PARA>Ref. EM TASK 71-00-02-000-027-B00 (A5-PPBU) - Remove The LP Compressor/Intermediate Case Brackets, Removal-027.</PARA> <PARA>Ref. EM TASK 71-00-02-000-027-B00 (A5-PPBU) - Remove The LP Compressor/Intermediate Case Brackets, Removal-027.</PARA>
</L2ITEM> </L2ITEM>
...@@ -286,10 +290,10 @@ ...@@ -286,10 +290,10 @@
<PARAC>发动机QEC零件故检,清洗,集件和导线修理</PARAC> <PARAC>发动机QEC零件故检,清洗,集件和导线修理</PARAC>
<PARA>Remove the Engine's QEC</PARA> <PARA>Remove the Engine's QEC</PARA>
<LIST1> <LIST1>
<!-- <L1ITEM>--> <L1ITEM>
<!-- <PARAC>使用清洗剂清洗拆下QEC件</PARAC>--> <PARAC>使用清洗剂清洗拆下QEC件</PARAC>
<!-- <PARA>Clean all QEC parts with cleanser.</PARA>--> <PARA>Clean all QEC parts with cleanser.</PARA>
<!-- </L1ITEM>--> </L1ITEM>
<L1ITEM> <L1ITEM>
<PARAC>检查右侧空气系统管路,对开口处堵上堵盖。参考EM 71-00-02。</PARAC> <PARAC>检查右侧空气系统管路,对开口处堵上堵盖。参考EM 71-00-02。</PARAC>
<PARA>Inspect the air system tubes which installed on the engine&rsquo;s right side, Ref EM manual 71-00-02.</PARA> <PARA>Inspect the air system tubes which installed on the engine&rsquo;s right side, Ref EM manual 71-00-02.</PARA>
...@@ -314,10 +318,10 @@ ...@@ -314,10 +318,10 @@
<PARAC>目视检查IDG。对开口处堵上堵盖。</PARAC> <PARAC>目视检查IDG。对开口处堵上堵盖。</PARAC>
<PARA>Visual inspect IDG.</PARA> <PARA>Visual inspect IDG.</PARA>
</L1ITEM> </L1ITEM>
<!-- <L1ITEM>--> <L1ITEM>
<!-- <PARAC>目视检查液压泵。</PARAC>--> <PARAC>目视检查液压泵。</PARAC>
<!-- <PARA>Visual inspect hydraulic pump.</PARA>--> <PARA>Visual inspect hydraulic pump.</PARA>
<!-- </L1ITEM>--> </L1ITEM>
<L1ITEM> <L1ITEM>
<PARAC>目视检查高压活门。对开口处堵上堵盖。</PARAC> <PARAC>目视检查高压活门。对开口处堵上堵盖。</PARAC>
<PARA>Visual inspect high pressure bleed valve.</PARA> <PARA>Visual inspect high pressure bleed valve.</PARA>
......
// 引入 TreeRenderResult 类型,该类型定义在 @/lib/XMLProcessor/src/typing 模块中 // 引入 TreeRenderResult 类型,该类型定义在 @/lib/XMLProcessor/src/typing 模块中
import { TreeRenderResult } from '@/lib/XMLProcessor/src/typing'; import { NewTreeModification, OldTreeModification, TreeRenderResult, TreeRenderResultFlatted } from '@/lib/XMLProcessor/src/typing'
// 引入 UUID 类用于生成唯一标识符 // 引入 UUID 类用于生成唯一标识符
import { UUID } from 'uuidjs'; import { UUID } from 'uuidjs'
import { md5 } from 'js-md5' import { md5 } from 'js-md5'
// 定义 Processing 类,用于处理 XML 数据 // 定义 Processing 类,用于处理 XML 数据
export class Processing { export class Processing {
// 私有属性,用于解析 XML 字符串为 DOM 对象 // 私有属性,用于解析 XML 字符串为 DOM 对象
private domParser: DOMParser; private domParser: DOMParser
private xmlSerializer: XMLSerializer private xmlSerializer: XMLSerializer
// 构造函数,初始化 DOMParser 实例 // 构造函数,初始化 DOMParser 实例
constructor() { constructor() {
this.domParser = new DOMParser(); this.domParser = new DOMParser()
this.xmlSerializer = new XMLSerializer(); this.xmlSerializer = new XMLSerializer()
} }
serializeXML(xmlDOM: Document): string { serializeXML(xmlDOM: Document): string {
return this.xmlSerializer.serializeToString(xmlDOM); return this.xmlSerializer.serializeToString(xmlDOM)
} }
dualCompareFromString(xmlStringA: string, xmlStringB: string, bothHandledNode: string[]) { private flattenTree(renderResult: TreeRenderResult[]): TreeRenderResultFlatted[] {
const treeA = this.domParser.parseFromString(xmlStringA, 'text/xml'); const resp: TreeRenderResultFlatted[] = []
const treeB = this.domParser.parseFromString(xmlStringB, 'text/xml'); for (const item of renderResult) {
const resultA = this.innerHandle(treeA.documentElement, bothHandledNode); resp.push({
key: item.key,
label: item.label,
hash: item.hash,
index: item.index,
chained: item.chained
})
if (item.children) {
resp.push(...this.flattenTree(item.children))
}
}
return resp
}
dualCompareFromString(
xmlStringOld: string,
xmlStringNew: string,
bothHandledNode: string[]
): {
dataForNew: NewTreeModification
dataForOld: OldTreeModification
domOld: Document
domNew: Document
treeOld: TreeRenderResult[]
treeNew: TreeRenderResult[]
} {
const treeA = this.domParser.parseFromString(xmlStringOld, 'text/xml')
const treeB = this.domParser.parseFromString(xmlStringNew, 'text/xml')
const resultA = this.innerHandle(treeA.documentElement, bothHandledNode)
const resultB = this.innerHandle(treeB.documentElement, bothHandledNode) const resultB = this.innerHandle(treeB.documentElement, bothHandledNode)
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())
console.log('both -res', {resultA, resultB}) // 扁平化所有节点
const resultAFlatted = this.flattenTree(resultA)
const resultBFlatted = this.flattenTree(resultB)
const dataForOld: OldTreeModification = {
Changed: [],
Deleted: []
}
const dataForNew: NewTreeModification = {
Changed: [],
Added: []
}
// 获取老节点中hash不存在的节点
let nonExistedHashNodeForOld: TreeRenderResultFlatted[] = resultAFlatted.filter((item) => {
return !resultBFlatted.find((itemB) => itemB.hash === item.hash && itemB.chained.join('$') === item.chained.join('$'))
})
let nonExistedHashNodeForNew: TreeRenderResultFlatted[] = resultBFlatted.filter((item) => {
return !resultAFlatted.find((itemA) => itemA.hash === item.hash && itemA.chained.join('$') === item.chained.join('$'))
})
const hashDiffInOld: TreeRenderResultFlatted[] = nonExistedHashNodeForOld.filter(
(a) => !nonExistedHashNodeForNew.find((v) => v.hash === a.hash)
)
const hashDiffInNew: TreeRenderResultFlatted[] = nonExistedHashNodeForNew.filter(
(a) => !nonExistedHashNodeForOld.find((v) => v.hash === a.hash)
)
let nodeUpdatedForOld: TreeRenderResultFlatted[] = []
let nodeUpdatedForNew: TreeRenderResultFlatted[] = []
for (const item of hashDiffInOld) {
const sameIndexFound = hashDiffInNew.find((a) => a.chained.join('$') === item.chained.join('$'))
if (sameIndexFound) {
nodeUpdatedForOld.push(item)
nodeUpdatedForNew.push(sameIndexFound)
}
}
let newNodeInserted: TreeRenderResultFlatted[] = []
for (const item of hashDiffInNew) {
if (!nodeUpdatedForOld.find(a=>a.hash === item.hash) && !nodeUpdatedForNew.find(a=>a.hash ===item.hash)) {
newNodeInserted.push(item)
}
}
let oldNodeDeleted: TreeRenderResultFlatted[] = []
for (const item of hashDiffInOld) {
if (!nodeUpdatedForNew.find(a=>a.hash === item.hash) && !nodeUpdatedForOld.find(a=>a.hash ===item.hash)) {
oldNodeDeleted.push(item)
}
}
dataForOld.Changed = nodeUpdatedForOld
dataForOld.Deleted = oldNodeDeleted
dataForNew.Changed = nodeUpdatedForNew
dataForNew.Added = newNodeInserted
return {
dataForNew,
dataForOld,
domOld: treeA,
domNew: treeB,
treeOld: resultA,
treeNew: resultB
}
} }
// 处理 XML 字符串的方法,返回树形数据和 XML DOM 对象 // 处理 XML 字符串的方法,返回树形数据和 XML DOM 对象
processXML(xmlContent: string, handledNode: string[]): { processXML(
treeData: TreeRenderResult[]; xmlContent: string,
xmlDOM: Document; handledNode: string[]
): {
treeData: TreeRenderResult[]
xmlDOM: Document
xmlContent: string xmlContent: string
} { } {
// 使用 DOMParser 解析 XML 字符串 // 使用 DOMParser 解析 XML 字符串
const parsedTree = this.domParser.parseFromString(xmlContent, 'text/xml'); const parsedTree = this.domParser.parseFromString(xmlContent, 'text/xml')
// 获取 XML 文档的根元素 // 获取 XML 文档的根元素
const rootElement = parsedTree.documentElement; const rootElement = parsedTree.documentElement
const result = { const result = {
// 调用 innerHandle 方法处理根元素,生成树形数据 // 调用 innerHandle 方法处理根元素,生成树形数据
treeData: this.innerHandle(rootElement, handledNode), treeData: this.innerHandle(rootElement, handledNode),
...@@ -54,41 +142,48 @@ export class Processing { ...@@ -54,41 +142,48 @@ export class Processing {
} }
// 异步处理 XML 文件的方法,返回包含树形数据和 XML DOM 对象的 Promise // 异步处理 XML 文件的方法,返回包含树形数据和 XML DOM 对象的 Promise
async processFile(xmlFile: File, handledNode: string[]): Promise<{ treeData: TreeRenderResult[]; xmlDOM: Document;xmlContent: string }> { async processFile(
xmlFile: File,
handledNode: string[]
): Promise<{
treeData: TreeRenderResult[]
xmlDOM: Document
xmlContent: string
}> {
// 检查文件是否为有效的 XML 文件 // 检查文件是否为有效的 XML 文件
if (!(xmlFile instanceof File) || !xmlFile.type.includes('xml')) { if (!(xmlFile instanceof File) || !xmlFile.type.includes('xml')) {
return Promise.reject('The file is not a valid XML file.'); return Promise.reject('The file is not a valid XML file.')
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const fr = new FileReader(); const fr = new FileReader()
// 当文件读取完成时触发的事件处理函数 // 当文件读取完成时触发的事件处理函数
fr.onload = () => { fr.onload = () => {
const content = fr.result as string; const content = fr.result as string
try { try {
// 调用 processXML 方法处理读取的文件内容 // 调用 processXML 方法处理读取的文件内容
const { treeData, xmlDOM, xmlContent } = this.processXML(content, handledNode); const { treeData, xmlDOM, xmlContent } = this.processXML(content, handledNode)
// 解析成功,返回树形数据和 XML DOM 对象 // 解析成功,返回树形数据和 XML DOM 对象
resolve({ treeData, xmlDOM, xmlContent }); resolve({ treeData, xmlDOM, xmlContent })
} catch (error) { } catch (error) {
// 解析失败,拒绝 Promise 并返回错误信息 // 解析失败,拒绝 Promise 并返回错误信息
reject(error); reject(error)
}
} }
};
// 当文件读取出错时触发的事件处理函数 // 当文件读取出错时触发的事件处理函数
fr.onerror = () => { fr.onerror = () => {
reject('Error reading file.'); reject('Error reading file.')
}; }
// 以文本形式读取文件 // 以文本形式读取文件
fr.readAsText(xmlFile); fr.readAsText(xmlFile)
}); })
} }
// 私有方法,用于递归处理单个 DOM 节点,生成树形数据 // 私有方法,用于递归处理单个 DOM 节点,生成树形数据
private innerHandle(domNode: Element, handledNode: string[]): TreeRenderResult[] { private innerHandle(domNode: Element, handledNode: string[]): TreeRenderResult[] {
let treeData: TreeRenderResult[] = []; let treeData: TreeRenderResult[] = []
// 生成唯一的 key // 生成唯一的 key
let targetKey = domNode.getAttribute('data-key'); let targetKey = domNode.getAttribute('data-key')
if (!targetKey) { if (!targetKey) {
targetKey = 'g-' + UUID.generate() targetKey = 'g-' + UUID.generate()
} }
...@@ -100,37 +195,37 @@ export class Processing { ...@@ -100,37 +195,37 @@ export class Processing {
index: 0, index: 0,
chained: [0], chained: [0],
hash: md5.hex(domNode.textContent! || '') hash: md5.hex(domNode.textContent! || '')
}; }
// 为 DOM 节点设置 data-key 属性 // 为 DOM 节点设置 data-key 属性
domNode.setAttribute('data-key', targetKey); domNode.setAttribute('data-key', targetKey)
domNode.setAttribute('data-w-e-type', domNode.nodeName) domNode.setAttribute('data-w-e-type', domNode.nodeName)
domNode.setAttribute('data-indent-level', '0') domNode.setAttribute('data-indent-level', '0')
// 递归处理子节点 // 递归处理子节点
treeItem.children = this.innerGroupHandle(domNode.children, handledNode, [0]); treeItem.children = this.innerGroupHandle(domNode.children, handledNode, [0])
// 将树形数据项添加到结果数组中 // 将树形数据项添加到结果数组中
treeData.push(treeItem); treeData.push(treeItem)
return treeData; return treeData
} }
// 私有方法,用于递归处理一组 DOM 节点,生成树形数据 // 私有方法,用于递归处理一组 DOM 节点,生成树形数据
private innerGroupHandle(nodeSet: HTMLCollection, handledNode: string[], startChained: number[]): TreeRenderResult[] { private innerGroupHandle(nodeSet: HTMLCollection, handledNode: string[], startChained: number[]): TreeRenderResult[] {
let targetResult: TreeRenderResult[] = []; let targetResult: TreeRenderResult[] = []
let realIndex = -1; let realIndex = -1
// 遍历节点集合 // 遍历节点集合
for (let i = 0; i < nodeSet.length; i++) { for (let i = 0; i < nodeSet.length; i++) {
const node = nodeSet[i] as Element; const node = nodeSet[i] as Element
// 检查节点是否在处理列表中 // 检查节点是否在处理列表中
if (!handledNode.includes(node.nodeName)) { if (!handledNode.includes(node.nodeName)) {
// 如果节点有子节点,递归处理子节点 // 如果节点有子节点,递归处理子节点
if (node.children.length) { if (node.children.length) {
targetResult.push(...this.innerGroupHandle(node.children, handledNode, startChained)); targetResult.push(...this.innerGroupHandle(node.children, handledNode, startChained))
} }
continue; continue
} }
realIndex++; realIndex++
// 生成唯一的 key // 生成唯一的 key
let targetKey = node.getAttribute('data-key'); let targetKey = node.getAttribute('data-key')
if (!targetKey) { if (!targetKey) {
targetKey = 'g-' + UUID.generate() targetKey = 'g-' + UUID.generate()
} }
...@@ -141,18 +236,18 @@ export class Processing { ...@@ -141,18 +236,18 @@ export class Processing {
index: realIndex, index: realIndex,
chained: [...startChained, realIndex], chained: [...startChained, realIndex],
hash: md5.hex(node.textContent! || '') hash: md5.hex(node.textContent! || '')
}; }
// 为 DOM 节点设置 data-key 属性 // 为 DOM 节点设置 data-key 属性
node.setAttribute('data-key', targetKey); node.setAttribute('data-key', targetKey)
node.setAttribute('data-w-e-type', node.nodeName) node.setAttribute('data-w-e-type', node.nodeName)
node.setAttribute('data-indent-level', startChained.length.toString()) node.setAttribute('data-indent-level', startChained.length.toString())
// 如果节点有子节点,递归处理子节点 // 如果节点有子节点,递归处理子节点
if (node.children.length > 0) { if (node.children.length > 0) {
treeItem.children = this.innerGroupHandle(node.children, handledNode, [...startChained, realIndex]); treeItem.children = this.innerGroupHandle(node.children, handledNode, [...startChained, realIndex])
} }
// 将树形数据项添加到结果数组中 // 将树形数据项添加到结果数组中
targetResult.push(treeItem); targetResult.push(treeItem)
} }
return targetResult; return targetResult
} }
} }
...@@ -7,11 +7,12 @@ import {nodeSet} from '@/views/editor/constants/nodeParsed.ts' ...@@ -7,11 +7,12 @@ import {nodeSet} from '@/views/editor/constants/nodeParsed.ts'
const p = new Processing() const p = new Processing()
// @ts-ignore // @ts-ignore
window.$p = function() { window.$p = function() {
p.dualCompareFromString( let nodeV = p.dualCompareFromString(
TextA, TextA,
TextB, TextB,
nodeSet nodeSet
) )
console.log('v-h', nodeV)
} }
export const XMLProcessing: Plugin = function(app) { export const XMLProcessing: Plugin = function(app) {
app.provide('xmlProcessing', p); app.provide('xmlProcessing', p);
......
...@@ -6,3 +6,21 @@ export type TreeRenderResult = { ...@@ -6,3 +6,21 @@ export type TreeRenderResult = {
index: number, index: number,
chained: number[] chained: number[]
} }
export type TreeRenderResultFlatted = {
key: string,
label: string,
hash: string,
index: number,
chained: number[]
}
export type OldTreeModification = {
Changed: TreeRenderResultFlatted[],
Deleted: TreeRenderResultFlatted[]
}
export type NewTreeModification = {
Added: TreeRenderResultFlatted[],
Changed: TreeRenderResultFlatted[],
}
\ No newline at end of file
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