Commit cf25f3fb authored by rockyl's avatar rockyl

版本比对

parent 0b5f668e
......@@ -158,7 +158,7 @@
"Failed to save project": "保存项目失败",
"Save project successfully": "保存项目成功",
"There are conflicts in the project": "项目有冲突,请先解决冲突",
"Input version remark": "输入版本备注",
"Input version remark": "请务必认真输入版本备注",
"Input view name": "输入视图名",
"Invalid view name": "无效的视图名",
"Unsaved version found locally": "本地发现了未保存保本,请选择版本打开",
......@@ -238,6 +238,11 @@
"Failed to update operator": "更新权限列表失败",
"Error delete self": "连自己都删? 不可以!",
"No permission": "没有权限喔!",
"Accept Remote": "远程版本",
"Accept Local": "本地版本",
"Accept Both": "兼并",
"Previous Conflict": "上一个冲突",
"Next Conflict": "下一个冲突",
"eventGroup": {
"in": "接收",
"out": "派发"
......
......@@ -544,3 +544,19 @@
}
}
}
.remote-change-class {
background-color: rgba(64, 200, 174, 0.2);
}
.remote-change-marker-class {
background-color: rgba(64, 200, 174, 0.5);
}
.local-change-class {
background-color: rgba(64, 166, 255, 0.2);
}
.local-change-marker-class {
background-color: rgba(64, 166, 255, 0.5);
}
......@@ -200,7 +200,7 @@
await this.$prompt(this.$t('Input version remark'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'),
showClose: false,
inputPattern: /^.{0,256}$/,
inputPattern: /^.{1,256}$/,
}).then(
({value}) => {
remark = this.lastSaveRemark = value;
......
<template>
<el-dialog :title="$t('Project conflict resolver')" width="70%" :visible.sync="visible" @opened="onOpen"
@close="onClose"
:close-on-click-modal="false"
:append-to-body="true"
fullscreen
......@@ -7,17 +8,27 @@
>
<div class="wrapper">
<el-steps :active="step" finish-status="success" align-center>
<el-step v-for="item in steps" :title="item"></el-step>
<el-step v-for="(item, index) in steps" :title="item" :key="index"></el-step>
</el-steps>
<div class="container">
<monaco-editor
ref="editor"
ref="diffEditor"
class="editor"
v-model="localData"
language="json"
:options="monacoEditorOptions"
diff-editor
:original="remoteData"
v-show="step===0"
/>
<monaco-editor
ref="editor"
class="editor"
v-model="localData"
language="json"
:options="monacoEditorOptions"
v-show="step===1"
@change="onChange"
/>
</div>
</div>
......@@ -25,9 +36,14 @@
<div>
<el-button size="mini" @click="mergeConflicts" type="success" v-show="step===0">{{$t('Merge conflicts')}}
</el-button>
<el-button-group v-show="step===1">
<el-button size="mini" @click="revealPreviousConflict(-1)" type="success">{{$t('Previous Conflict')}}
</el-button>
<el-button size="mini" @click="revealPreviousConflict(1)" type="success">{{$t('Next Conflict')}}</el-button>
</el-button-group>
</div>
<div>
<el-button size="mini" @click="onClose">{{$t('Close')}}</el-button>
<el-button size="mini" @click="onCancel">{{$t('Cancel')}}</el-button>
<el-button size="mini" @click="onSave" type="primary" :disabled="step===0">{{$t('Save')}}</el-button>
</div>
</div>
......@@ -39,8 +55,14 @@
import MonacoEditor from "vue-monaco";
import {formatJson, monacoEditorOptions} from "../../../utils";
import SplitPanes from 'splitpanes'
import i18n from "@/i18n";
const conflictFlags = ['<<<<<<< REMOTE', '=======', '>>>>>>> LOCAL'];
const startHeaderMarker = '<<<<<<< REMOTE';
const splitterMarker = '=======';
const endFooterMarker = '>>>>>>> LOCAL';
const conflictMarkers = [startHeaderMarker, splitterMarker, endFooterMarker];
const lensTitles = [i18n.t('Accept Remote'), i18n.t('Accept Local'), i18n.t('Accept Both')];
const delayTrackDuration = 500;
export default {
name: "ProjectConflictResolveDialog",
......@@ -59,6 +81,12 @@
}
},
mounted() {
this.codeLensProviders = [];
this.conflicts = [];
this.conflictIndex = 0;
this.decrations = [];
},
destroyed() {
},
methods: {
......@@ -79,23 +107,25 @@
if (!this.inited) {
this.inited = true;
let modifiedEditor = this.$refs.editor.getModifiedEditor();
let editor = this.$refs.editor.getEditor();
let diffEditor = this.$refs.diffEditor;
let editor = diffEditor.getEditor();
editor.onDidUpdateDiff(() => {
if (!this.diff) {
this.diff = editor.getLineChanges();
}
});
modifiedEditor.onDidChangeModelDecorations(() => {
});
let resolveEditorIns = this.$refs.editor.getEditor();
this.mergeConflictCommand = resolveEditorIns.addCommand(0, (getService, action, conflict) => {
this.resolveConflict(action, conflict);
}, '');
}
});
});
},
async onSave() {
let resolved = true;
for (let conflictFlag of conflictFlags) {
for (let conflictFlag of conflictMarkers) {
if (this.localData.includes(conflictFlag)) {
resolved = false;
break;
......@@ -125,20 +155,172 @@
});
}
},
onClose() {
onCancel() {
this.visible = false;
},
onClose() {
this.removeAllCodeLensProviders();
this.removeAllDecorations();
},
onOpen() {
},
...mapMutations([
'modifyProjectDetails',
]),
formatJson(source) {
return formatJson(source);
},
onChange(t) {
if (this.timerTrack) {
clearTimeout(this.timerTrack);
this.timerTrack = null;
}
this.timerTrack = setTimeout(() => {
this.delayTrack()
}, delayTrackDuration);
},
removeAllCodeLensProviders() {
while (this.codeLensProviders.length > 0) {
let codeLensProvider = this.codeLensProviders.pop();
codeLensProvider.dispose();
}
},
removeAllDecorations() {
let editor = this.$refs.editor;
let resolveEditor = editor.getEditor();
resolveEditor.deltaDecorations(this.decrations, []);
this.decrations.splice(0);
},
delayTrack() {
console.log('delayTrack');
let lines = this.localData.split('\n');
this.conflicts.splice(0);
let currentConflict = null;
let conflicts = this.conflicts;
for (let i = 0, li = lines.length; i < li; i++) {
const line = lines[i];
if (!line) {
continue;
}
if (line.startsWith(startHeaderMarker)) {
if (currentConflict !== null) {
currentConflict = null;
break;
}
currentConflict = {remoteLine: i};
} else if (currentConflict && !currentConflict.splitLine && line.startsWith(splitterMarker)) {
currentConflict.splitLine = i;
} else if (currentConflict && currentConflict.splitLine && line.startsWith(endFooterMarker)) {
currentConflict.localLine = i;
conflicts.push(currentConflict);
currentConflict = null;
}
}
this.removeAllCodeLensProviders();
this.removeAllDecorations();
for (let conflict of conflicts) {
const {remoteLine, splitLine, localLine} = conflict;
let line = remoteLine + 1;
let range = {
startLineNumber: line,
startColumn: line,
endLineNumber: line,
endColumn: line
};
let id = line.toString();
let lenses = [];
for (let i = 0, li = lensTitles.length; i < li; i++) {
const lensTitle = lensTitles[i];
lenses.push({
range,
id,
command: {
id: this.mergeConflictCommand,
title: lensTitle,
arguments: [i, conflict],
}
})
}
let editor = this.$refs.editor;
let resolveEditor = editor.getEditor();
let codeLensProvider = editor.monaco.languages.registerCodeLensProvider('json', {
provideCodeLenses: function (model, token) {
return {
lenses,
dispose() {
}
};
},
resolveCodeLens: function (model, codeLens, token) {
return codeLens;
}
});
this.codeLensProviders.push(codeLensProvider);
this.revealPreviousConflict();
this.$nextTick(() => {
let deltaDecorations = [
{
range: {
startLineNumber: remoteLine + 1,
endLineNumber: remoteLine + 1,
},
options: {isWholeLine: true, className: 'remote-change-marker-class'}
},
];
if (splitLine - remoteLine > 1) {
deltaDecorations.push({
range: {
startLineNumber: remoteLine + 2,
endLineNumber: splitLine,
},
options: {isWholeLine: true, className: 'remote-change-class'}
},)
}
if (localLine - splitLine > 1) {
deltaDecorations.push({
range: {
startLineNumber: splitLine + 2,
endLineNumber: localLine,
},
options: {isWholeLine: true, className: 'local-change-class'}
},)
}
deltaDecorations.push({
range: {
startLineNumber: localLine + 1,
endLineNumber: localLine + 1,
},
options: {isWholeLine: true, className: 'local-change-marker-class'}
});
let decorations = resolveEditor.deltaDecorations([], deltaDecorations);
this.decrations.push(...decorations);
});
}
},
revealPreviousConflict(dir = 0) {
this.conflictIndex += dir;
if (this.conflictIndex < 0) {
this.conflictIndex += this.conflicts.length;
}
this.conflictIndex = this.conflictIndex % this.conflicts.length;
let conflict = this.conflicts[this.conflictIndex];
let resolveEditorIns = this.$refs.editor.getEditor();
resolveEditorIns.revealLineInCenter(conflict.remoteLine);
},
mergeConflicts() {
this.monacoEditorOptions.readOnly = false;
this.$nextTick(() => {
this.step++;
});
let remoteCodeLines = this.remoteData.split('\n');
let localCodeLines = this.localData.split('\n');
......@@ -158,20 +340,87 @@
}
let merged = [];
merged.push(conflictFlags[0]);
merged.push(conflictMarkers[0]);
merged.push(...remotePart);
merged.push(conflictFlags[1]);
merged.push(conflictMarkers[1]);
merged.push(...localPart);
merged.push(conflictFlags[2]);
merged.push(conflictMarkers[2]);
//console.log(merged);
mergedCodeLines.splice(modifiedStartLineNumber - 1 + offset, modifiedEndLineNumber - modifiedStartLineNumber + 1, ...merged);
let startLine = modifiedStartLineNumber + offset;
mergedCodeLines.splice(startLine - 1, modifiedEndLineNumber - modifiedStartLineNumber + 1, ...merged);
offset += 3 + remotePart.length;
}
this.localData = mergedCodeLines.join('\n');
this.delayTrack();
},
resolveConflict(action, conflict) {
let resolveEditorIns = this.$refs.editor.getEditor();
let model = resolveEditorIns.getModel();
let lines = this.localData.split('\n');
let replacer;
switch (action) {
case 0:
replacer = getTextWithRange(conflict.remoteLine, conflict.splitLine);
break;
case 1:
replacer = getTextWithRange(conflict.splitLine, conflict.localLine);
break;
case 2:
let remoteReplacer = getTextWithRange(conflict.remoteLine, conflict.splitLine);
let localReplacer = getTextWithRange(conflict.splitLine, conflict.localLine);
if (remoteReplacer === undefined) {
if (localReplacer !== undefined) {
replacer = localReplacer;
}
} else {
if (localReplacer === undefined) {
replacer = remoteReplacer;
} else {
replacer = remoteReplacer + '\n' + localReplacer;
}
}
break;
}
if (replacer !== undefined) {
replaceText(replacer);
//this.localData = lines.join('\n');
}
this.delayTrack();
this.conflictIndex--;
this.conflictIndex = Math.max(this.conflictIndex, 0);
function getTextWithRange(start, end) {
if (end - start > 1) {
let resultLines = [];
for (let i = start + 1; i < end; i++) {
resultLines.push(lines[i]);
}
return resultLines.join('\n');
}
}
function replaceText(replacer) {
model.pushEditOperations([], [{
range: {
startLineNumber: conflict.remoteLine + 1,
startColumn: 0,
endLineNumber: conflict.localLine + 1,
endColumn: Number.MAX_VALUE,
},
text: replacer,
}], () => []);
}
},
...mapMutations([
'modifyProjectDetails',
]),
}
}
</script>
......
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