text-compare-vue3
Version:
A powerful text comparison plugin for Vue.js with character-level diff support
143 lines (130 loc) • 3.54 kB
JavaScript
/**
* 计算文本差异
* @param {string} oldText - 原始文本
* @param {string} newText - 新文本
* @param {boolean} isDifferent - 是否存在差异
* @returns {Object} 包含 oldParts 和 newParts 的对象
*/
export function getDiffParts(oldText, newText, isDifferent = true) {
if (!isDifferent || !oldText || !newText) {
return {
oldParts: [{ value: oldText || '-', removed: false }],
newParts: [{ value: newText || '-', added: false }]
}
}
const oldParts = []
const newParts = []
// 将文本转换为字符数组
const oldChars = Array.from(oldText)
const newChars = Array.from(newText)
// 构建相似度矩阵
const matrix = []
for (let i = 0; i <= oldChars.length; i++) {
matrix[i] = []
for (let j = 0; j <= newChars.length; j++) {
if (i === 0 || j === 0) {
matrix[i][j] = 0
} else if (oldChars[i - 1] === newChars[j - 1]) {
matrix[i][j] = matrix[i - 1][j - 1] + 1
} else {
matrix[i][j] = Math.max(matrix[i - 1][j], matrix[i][j - 1])
}
}
}
// 回溯找出相同的部分
let i = oldChars.length
let j = newChars.length
const commonParts = []
while (i > 0 && j > 0) {
if (oldChars[i - 1] === newChars[j - 1]) {
commonParts.unshift({
char: oldChars[i - 1],
oldIndex: i - 1,
newIndex: j - 1
})
i--
j--
} else if (matrix[i - 1][j] > matrix[i][j - 1]) {
i--
} else {
j--
}
}
// 根据commonParts构建差异结果
let lastOldIndex = -1
let lastNewIndex = -1
// 处理开头的差异
if (commonParts.length > 0) {
if (commonParts[0].oldIndex > 0) {
oldParts.push({
value: oldText.slice(0, commonParts[0].oldIndex),
removed: true
})
}
if (commonParts[0].newIndex > 0) {
newParts.push({
value: newText.slice(0, commonParts[0].newIndex),
added: true
})
}
}
// 处理中间的差异
for (let k = 0; k < commonParts.length; k++) {
const current = commonParts[k]
const next = commonParts[k + 1]
// 添加相同的字符
oldParts.push({
value: current.char,
removed: false
})
newParts.push({
value: current.char,
added: false
})
if (next) {
// 处理旧文本中的差异
if (next.oldIndex - current.oldIndex > 1) {
oldParts.push({
value: oldText.slice(current.oldIndex + 1, next.oldIndex),
removed: true
})
}
// 处理新文本中的差异
if (next.newIndex - current.newIndex > 1) {
newParts.push({
value: newText.slice(current.newIndex + 1, next.newIndex),
added: true
})
}
}
lastOldIndex = current.oldIndex
lastNewIndex = current.newIndex
}
// 处理结尾的差异
if (commonParts.length > 0) {
const lastCommon = commonParts[commonParts.length - 1]
if (lastCommon.oldIndex < oldChars.length - 1) {
oldParts.push({
value: oldText.slice(lastCommon.oldIndex + 1),
removed: true
})
}
if (lastCommon.newIndex < newChars.length - 1) {
newParts.push({
value: newText.slice(lastCommon.newIndex + 1),
added: true
})
}
} else {
// 如果没有相同部分,则全部标记为差异
oldParts.push({
value: oldText,
removed: true
})
newParts.push({
value: newText,
added: true
})
}
return { oldParts, newParts }
}