@leonwerth/vue-diff
Version:
Vue diff
329 lines (304 loc) • 9.2 kB
text/typescript
import { diff_match_patch as DiffMatchPatch } from 'diff-match-patch';
import { getDiffType, renderLines, uuid } from './utils';
export interface DiffsProps {
name?: string;
type?: string;
id?: number;
key?: string;
last?: string;
next?: string;
uid?: string;
}
export interface isDiffsProps {
chWords: boolean;
lineNum: number;
type: string;
value: string;
}
export interface DiffGroupProps {
name: string;
value: number;
key: string;
cards: Array<DiffsProps>;
}
const Compare = {
// 对比转换回html
changeStrToHtml(str: string) {
let str1 = str
.replace(/\¶\;\<br\>/g, '\n')
.replace(/\>\;/g, '>')
.replace(/\<\;/g, '<')
.replace(/\&\;/g, '&');
// .replace(/list\-style\:none\;/, '')
// .replace(/text\-decoration\:none\;/, '');
return str1.substring(6, str1.length - 7);
},
// 对比转换html标签
diff_prettyHtml(diffs: any) {
var html = [];
var pattern_amp = /&/g;
var pattern_lt = /</g;
var pattern_gt = />/g;
var pattern_para = /\n/g;
for (var x = 0; x < diffs.length; x++) {
var op = diffs[x][0]; // Operation (insert, delete, equal)
var data = diffs[x][1]; // Text of change.
var text = data
.replace(pattern_amp, '&')
.replace(pattern_lt, '<')
.replace(pattern_gt, '>')
.replace(pattern_para, '¶<br>');
html[x] = text;
}
return html.join('');
},
// 合并函数不带颜色
concatHTMLWithoutColor(
prev: string | undefined,
current: string | undefined,
) {
var dmp = new DiffMatchPatch();
var d = dmp.diff_main(prev as string, current as string);
let html = '';
d.forEach(str => {
html += str[1];
});
return html;
},
// 合并函数
concatHTML(prev: string | undefined, current: string | undefined) {
var dmp = new DiffMatchPatch();
var d = dmp.diff_main(prev as string, current as string);
dmp.diff_cleanupSemantic(d);
var ds = dmp.diff_prettyHtml(d);
// 拿到对比完之后的数组
let html = this.changeStrToHtml(ds);
return html;
},
// 两屏对比函数
diffScreens(
prevHtml: string | any,
currentHtml: string | any,
id: string,
) {
if(!prevHtml && !currentHtml){
console.log('没有获取到要对比的字符串', prevHtml, currentHtml)
return
}
const dmp = new DiffMatchPatch();
const diff = dmp.diff_main(prevHtml, currentHtml);
// console.log(diff, '对比结果')
dmp.diff_cleanupSemantic(diff);
let word = diff
.filter(result => getDiffType(result[0]) !== 'removed')
.map((result, idx) => {
// let t = '';
// if (getDiffType(result[0]) === 'added') {
// t = `<span class='modified ${id}'>${result[1]}</span>`;
// } else {
// t = result[1];
// }
return result[1];
})
.join('');
return word;
},
// 两屏对比塞入颜色
compareHTML(html1: string, html2: string) {
// console.log(Htmlj1.value, Htmlj2.value, 'kkkkkkk')
const result = renderLines('split', html1, html2);
// console.log(result, '两屏对比结果');
let isDiffs: Array<Array<isDiffsProps>> = [];
let newHTML1 = '';
let newHTML2 = '';
result.forEach((res, index) => {
let left = res[0];
let id = `pos${index}`;
if (!left.chkWords) {
// class="vue-diff-cell-${left.type}"
newHTML1 += `<span>${left.value}</span>`;
} else {
let diffText = Compare.diffScreens(res[1].value, left.value, id);
// class="vue-diff-cell-${left.type}" id="diff-left-${index}"
newHTML1 += `<span>${diffText}</span>`;
// 给左边标记添加事件
// setTimeout(() => {
// let dom: any = document.getElementById(`diff-left-${index}`)
// console.dir(dom)
// dom.onclick = function (e: any) {
// console.log(diffText)
// }
// }, 1000)
}
let right = res[0];
if (!right.chkWords) {
// class="vue-diff-cell-${right.type}"
newHTML2 += `<span>${right.value}</span>`;
} else {
let diffText = Compare.diffScreens(right.value, res[1].value, id);
// class="vue-diff-cell-added" id="diff-right-${index}"
newHTML2 += `<span>${diffText}</span>`;
// 给右边标记添加事件
// setTimeout(() => {
// let dom: any = document.getElementById(`diff-right-${index}`)
// console.dir(dom)
// dom.onclick = function (e: any) {
// console.log(diffText)
// }
// }, 1000)
}
// 计算不同的地方
let i0 = res[0];
let i1 = res[1];
if (i0.type !== i1.type) {
const d: any = this.computeTypes(res, index, id);
isDiffs = isDiffs.concat(d);
}
});
const Diffs: any = this.uniqText(isDiffs);
return { newHTML1, newHTML2, Diffs };
},
// 去重
uniqText(isDiffs: Array<Array<isDiffsProps>>) {
let textArr: Array<string> = [];
isDiffs.forEach(item => {
let str = JSON.stringify(item);
if (!textArr.includes(str)) {
textArr.push(str);
}
});
let newArr: Array<DiffsProps> = [];
textArr.forEach(str => {
newArr.push(JSON.parse(str));
});
let add = newArr.filter(item => item.type === 'add');
let remove = newArr.filter(item => item.type === 'remove');
let edit = newArr.filter(item => item.type === 'edit');
return {
Diffs: newArr,
DiffsCount: [
{
name: '全部',
value: newArr.length,
key: 'all',
cards: newArr as Array<DiffsProps>,
},
{
name: '删除',
value: remove.length,
key: 'remove',
cards: remove as Array<DiffsProps>,
},
{
name: '新增',
value: add.length,
key: 'add',
cards: add as Array<DiffsProps>,
},
{
name: '修改',
value: edit.length,
key: 'edit',
cards: edit as Array<DiffsProps>,
},
],
};
},
// 计算 删除、新增、编辑 不同类的文件
computeTypes(arr: Array<any>, idx: number, id: string) {
const dCartInfo: Array<DiffsProps> = [];
const dText = this.getDiffArr(arr[0].value, arr[1].value);
const clearTag = (nextText: string) => {
// // 处理 >< 尖括号标签
let text: string | undefined = nextText;
if (nextText.includes('>') && nextText.includes('<')) {
let result2 = nextText.match(/\>(.*?)\</g);
let result3 = result2?.map(item => {
return item.replace(/[\> \<]/g, '');
});
text = result3?.join('');
}
return text;
};
dText.forEach((dArr, index) => {
// 随机id
const uid = uuid();
let edit = false;
// 新增(1 是新增 新增的上一行是-1 则是编辑)
if (dArr[0] === 1) {
if (dText[index - 1] && dText[index - 1][0] === -1) {
edit = true;
dCartInfo.push({
name: '修改',
type: 'edit',
id: idx,
key: id,
uid: uid,
last: clearTag(dText[index - 1][1]),
next: clearTag(dArr[1]),
});
}
if (!edit) {
dCartInfo.push({
name: '新增',
type: 'add',
id: idx,
key: id,
uid: uid,
last: '',
next: clearTag(dArr[1]),
});
}
}
// 删除(-1 是删除 删除的下一行是1 则是编辑)
if (dArr[0] === -1) {
if (dText[index + 1] && dText[index + 1][0] === 1) {
edit = true;
dCartInfo.push({
name: '修改',
type: 'edit',
id: idx,
key: id,
uid: uid,
last: clearTag(dArr[1]),
next: clearTag(dText[index + 1][1]),
});
}
if (!edit) {
dCartInfo.push({
name: '删除',
type: 'remove',
id: idx,
key: id,
uid: uid,
last: '',
next: clearTag(dArr[1]),
});
}
}
});
return dCartInfo; //this.doingDate();
},
// 去掉对 html 特殊处理的标签
doingDate(dCartInfo: Array<DiffsProps>) {
// 过滤两边没有结果的数据(主要是替换标签导致的)
const res = dCartInfo.filter((item: DiffsProps) => item.last || item.next);
// 过滤重复数据
const uniqArr: any = [];
const diffResult: DiffsProps | any = [];
res.forEach((item: DiffsProps | any) => {
if (!uniqArr.includes(item.last + item.next + item.type)) {
uniqArr.push(item.last + item.next + item.type);
diffResult.push(item);
}
});
// console.log(diffResult, '888888888');
return diffResult;
},
getDiffArr(prev: string, current: string) {
var dmp = new DiffMatchPatch();
var d = dmp.diff_main(prev as string, current as string);
return d;
},
};
export default Compare;