目录
一、效果图
二、代码
1、maven
<dependency>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils</artifactId>
<version>4.11</version>
</dependency>
2、工具类
/**
* 文本变更对比
*
* @author zenglingyao
* @date 2023/04/11
*/
public class DiffTextUtil {
/**
* 发现差异
*
* @return {@link Map}
*/
public static Map<String, List<?>> findDiff(List<String> a, List<String> b) {
//两文件的不同点
Patch<String> patch = DiffUtils.diff(a, b);
List<Integer[]> diffPosition = new ArrayList<>();
List<Integer> insertList = new ArrayList<>();
List<Integer> sourceChange = new ArrayList<>();
List<Integer> targetChange = new ArrayList<>();
List<Integer> deleteList = new ArrayList<>();
List<AbstractDelta<String>> deltas = patch.getDeltas();
for (AbstractDelta<String> abstractDelta : deltas) {
int sourceStart = abstractDelta.getSource().getPosition();
int sourceLast = abstractDelta.getSource().last();
int targetStart = abstractDelta.getTarget().getPosition();
int targetLast = abstractDelta.getTarget().last();
switch (abstractDelta.getType()){
case INSERT:
insertList.addAll(getAll(targetStart, targetLast));
break;
case CHANGE:
sourceChange.addAll(getAll(sourceStart, sourceLast));
targetChange.addAll(getAll(targetStart, targetLast));
break;
case DELETE:
deleteList.addAll(getAll(sourceStart, sourceLast));
break;
default:
continue;
}
diffPosition.add(new Integer[]{sourceStart, targetStart});
}
HashMap<String, List<?>> hashMap = new HashMap<>();
hashMap.put("sourceList", a);
hashMap.put("targetList", b);
hashMap.put("insertList", insertList);
hashMap.put("sourceChange", sourceChange);
hashMap.put("targetChange", targetChange);
hashMap.put("deleteList", deleteList);
hashMap.put("diffPosition", diffPosition);
return hashMap;
}
private static List<Integer> getAll(int start, int end) {
List<Integer> result = new ArrayList<>(end - start + 1);
for (int i = start; i <= end; i++) {
result.add(i);
}
return result;
}
}
2、接口
@RestController
@RequestMapping("/api/demo")
public class DiffTextController {
@RequestMapping("diff")
public Map findDiff() {
List<String> a = null;
List<String> b = null;
try {
a = FileUtils.readLines(new File("E:\\test\\diff1.conf"));
b = FileUtils.readLines(new File("E:\\test\\diff2.conf"));
} catch (IOException ignore) {
}
return DiffTextUtil.findDiff(a, b);
}
}
3、前端
<template>
<div>
<el-row>
<el-col :span="12">
<div style="overflow-y: auto; height: 90vh">
<div
v-for="(item,index) in sourceList"
:key="index"
:id="'sourceList' + index"
style="min-height: 23px; padding: 3px 0"
:class="deleteList.includes(index) ? 'red' : sourceChange.includes(index) ? 'blue' : ''"
>{{ item }}</div>
</div>
</el-col>
<el-col :span="12">
<div style="overflow-y: auto; height: 90vh">
<div
v-for="(item,index) in targetList"
:key="index"
:id="'targetList' + index"
style="min-height: 23px; padding: 3px 0"
:class="insertList.includes(index) ? 'green' : targetChange.includes(index) ? 'blue' : ''"
>{{ item}}</div>
</div>
</el-col>
</el-row>
<el-row style="background-color: darkgrey; margin-top: 10px">
共{{diffPosition.length}}处差异
跳转到
<el-input
v-model="diffCurrent"
size="mini"
style="width: 80px; margin-right: 5px;"
></el-input>
<el-tooltip class="item" effect="dark" content="跳转" placement="top">
<el-button @click="jump()" size="mini" icon="el-icon-position" type="primary" style="margin: 0 10px" circle></el-button>
</el-tooltip>
<i class="el-icon-top" @click="jumpTop()" style="margin: 0 10px"></i>
<i class="el-icon-bottom" @click="jumpBottom()"></i>
</el-row>
</div>
</template>
<script>
export default {
name: "DiffDemo",
data() {
return {
sourceList: [],
targetList: [],
insertList: [],
sourceChange: [],
targetChange: [],
deleteList: [],
diffPosition: [],
diffCurrent: 1
}
},
methods: {
init() {
this.post("/api/demo/diff", {}, d=>{
this.sourceList = d.sourceList;
this.targetList = d.targetList;
this.insertList = d.insertList;
this.sourceChange = d.sourceChange;
this.targetChange = d.targetChange;
this.deleteList = d.deleteList;
this.diffPosition = d.diffPosition;
});
},
checkDiffCurrent() {
return this.diffCurrent > 0 && this.diffCurrent <= this.diffPosition.length;
},
goPosition() {
location.href = "#sourceList" + this.diffPosition[this.diffCurrent - 1][0];
setTimeout(()=>{
location.href = "#targetList" + + this.diffPosition[this.diffCurrent - 1][1];
},0)
},
initPosition() {
this.diffCurrent = 1;
this.goPosition();
},
jump() {
if (this.checkDiffCurrent()) {
this.goPosition();
}else {
this.initPosition();
}
},
jumpTop() {
if (this.checkDiffCurrent()) {
if (this.diffCurrent === 1){
return;
}
this.diffCurrent--;
this.goPosition();
}else {
this.initPosition();
}
},
jumpBottom() {
if (this.checkDiffCurrent()) {
if (this.diffCurrent === this.diffPosition.length){
return;
}
this.diffCurrent++;
this.goPosition();
}else {
this.initPosition();
}
}
},
created() {
this.init();
let that = this;
setTimeout(function (){
that.initPosition();
}, 0)
},
}
</script>
<style scoped>
.green {
background-color: #a8ec7c;
}
.red {
background-color: #e88686;
}
.blue {
background-color: #7bb5fd;
}
</style>
三、讲解
1、后端
DiffTextUtil.findDiff 参数为两个需要对比的字符串列表,核心代码是
Patch<String> patch = DiffUtils.diff(a, b);
List<AbstractDelta<String>> deltas = patch.getDeltas();
第一个参数是源 (a),第二个参数是目的(b)
遍历对比的结果记录,找到新增、修改、删除对应的下标记录下来
insertList 记录目的新增下标 到前端变绿色用
sourceChange 记录源改变下标 到前端变蓝色用
targetChange 记录目的改变下标 到前端变蓝色用
deleteList 记录删除下标 到前端变红色用
diffPosition 记录每处差异开始位置,包括源和目的下标 到前端跳转到下一处差异按钮用
2、前端
- 跳转功能通过 HTML锚点 实现
- js 的 setTimeout 方法,我调用时时间参数传的0,看起来没有,实际上可以等前面axios回调执行完再执行需要执行的方法
- this.post 是我自己封装的axios,用的时候可以自己改一下