Commit 5e7b8896 authored by rockyl's avatar rockyl

Merge branch 'dev' into res-group

parents b5e40cbb 2858b006
......@@ -9,6 +9,7 @@
"Still Close": "直接关闭",
"Save": "保存",
"Pack": "打包",
"Commit": "提交",
"Save And Preview": "保存并预览",
"Save And Close": "保存并关闭",
"Reset": "重置",
......@@ -235,7 +236,8 @@
"Merge conflicts": "合并冲突",
"There are still unresolved conflicts": "还有冲突未解决,不能保存!",
"The format of the JSON document is wrong": "JSON文档格式有误,请先更正!",
"The conflict has been resolved": "冲突已解决,确定保存吗?",
"The conflict has been resolved": "冲突已解决,确定提交吗?",
"All conflict has been resolved": "所有冲突已解决,确定保存吗?",
"Failed to update operator": "更新权限列表失败",
"Error delete self": "连自己都删? 不可以!",
"No permission": "没有权限喔!",
......@@ -355,15 +357,6 @@
"test": "测试环境",
"prod": "线上环境"
},
"QAList":[
"崔立强",
"李培培",
"劳奇峰",
"任建锋",
"张婷婷",
"王炽",
"王鸿源"
],
"tplOperates": [
"新增",
"修改",
......@@ -405,5 +398,45 @@
"查看冲突",
"手动合并",
"完成"
]
],
"categories": {
"options": {
"label": "项目配置",
"simple": true
},
"dependencies": {
"label": "依赖",
"simple": true
},
"dataMapping": {
"label": "数据映射",
"key": "name",
"name": "name"
},
"customs": {
"label": "自定义模块",
"key": "id",
"name": "id"
},
"mock": {
"label": "Mock",
"key": "path",
"name": "path"
},
"views": {
"label": "视图",
"key": "uuid",
"name": "name"
},
"assets": {
"label": "素材",
"key": "uuid",
"name": "name"
},
"processes": {
"label": "过程",
"key": "id",
"name": "name"
}
}
}
\ No newline at end of file
......@@ -706,7 +706,7 @@ export const projectStore = {
//localStorage.removeItem('project-' + projectID);
commit('setDirty', false);
},
async loadFromRemote({commit, dispatch}, projectID) {
async loadFromRemote({commit, dispatch}, {projectID}) {
await dispatch('loadPackageInfos');
const project = await projectApi.fetchOne(projectID);
if (project) {
......@@ -716,18 +716,21 @@ export const projectStore = {
throw new Error('Project does not exist')
}
},
async loadFromDataUrl({commit, dispatch}, {project, dataUrl}) {
await dispatch('loadPackageInfos');
project = await projectApi.fetchOne(project.id, false);
async loadFromDataUrl({commit, dispatch}, {projectID, dataUrl}) {
const projectData = await projectApi.fetchOneFromDataUrl(dataUrl);
if (projectData) {
project.data = projectData;
commit('updateProject', project);
await dispatch('loadFromData', {projectID, projectData})
dispatch('saveToLocal');
} else {
throw new Error('Project does not exist')
}
},
async loadFromData({commit, dispatch}, {projectID, projectData}) {
await dispatch('loadPackageInfos');
let project = await projectApi.fetchOne(projectID, false);
project.data = projectData;
commit('updateProject', project);
},
async saveToRemote({state, dispatch, getters, commit}, {remark, data}) {
let project = Object.assign({}, getters.project);
if (data) {
......
......@@ -488,7 +488,7 @@
width: 80vw !important;
}
.project-conflict-resolve-editor{
.code-conflict-resolve-editor{
.wrapper{
height: 100%;
padding: 10px;
......@@ -506,6 +506,27 @@
}
}
.project-conflict-resolve-editor{
.wrapper{
height: 50vh;
padding: 10px;
display: flex;
flex-direction: column;
.category-tab{
flex: 1;
.el-tabs__content{
height: 100%;
}
}
.compare-view{
}
}
}
.remote-change-class {
background-color: rgba(64, 200, 174, 0.2);
}
......
......@@ -427,5 +427,5 @@ export function deleteAssetsDepConfig(data) {
}
export function formatJson(source){
return JSON.stringify(JSON.parse(source), null, '\t')
return source ? JSON.stringify(JSON.parse(source), null, '\t') : '';
}
......@@ -170,10 +170,10 @@
this.ready = true;
});
},
async loadRemoteVersion(projectID, project, dataUrl) {
async loadRemoteVersion(projectID, project, dataUrl, projectData) {
if (projectID) {
this.ready = false;
let p = dataUrl ? this.loadFromDataUrl({project, dataUrl}) : this.loadFromRemote(projectID);
let p = projectData ? (this.loadFromData({projectID, projectData})) : dataUrl ? this.loadFromDataUrl({projectID, dataUrl}) : this.loadFromRemote({projectID});
await playWaiting(p, this.$t('Preparing')).catch(e => {
this.$alert(this.$t('Project does not exist'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'),
......@@ -230,7 +230,9 @@
return cancel;
},
async onConflictResolved(data) {
this.saveProject(true, data);
const {projectID, project} = this.$route.params;
await this.saveProject(true, data);
this.loadRemoteVersion(projectID, project, undefined, data);
},
checkAuth() {
let checkStatus = this.operators.includes(this.currentOperator);
......@@ -377,6 +379,7 @@
'loadFromLocal',
'loadFromRemote',
'loadFromDataUrl',
'loadFromData',
"saveToLocal",
"saveToRemote",
'updateEnv',
......
<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
custom-class="flex-dialog code-conflict-resolve-editor"
>
<div class="wrapper">
<el-steps :active="step" finish-status="success" align-center>
<el-step v-for="(item, index) in steps" :title="item" :key="index"></el-step>
</el-steps>
<div class="container">
<monaco-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>
<div slot="footer" class="dialog-footer">
<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="onCancel">{{$t('Cancel')}}</el-button>
<el-button size="mini" @click="onSave" type="primary" :disabled="step===0">{{$t('Commit')}}</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import {mapMutations} from 'vuex';
import MonacoEditor from "vue-monaco";
import {formatJson, monacoEditorOptions} from "../../../utils";
import SplitPanes from 'splitpanes'
import i18n from "@/i18n";
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: "CodeConflictResolveDialog",
components: {MonacoEditor, SplitPanes},
data() {
let monacoEditorOptionsSelf = Object.assign({
readOnly: true,
}, monacoEditorOptions);
return {
monacoEditorOptions: monacoEditorOptionsSelf,
visible: false,
step: 0,
remoteData: '',
localData: '',
steps: this.$t('project-conflicts-resolve-steps'),
}
},
mounted() {
this.codeLensProviders = [];
this.conflicts = [];
this.conflictIndex = 0;
this.decrations = [];
},
destroyed() {
},
methods: {
show(remoteData, localData) {
this.remoteData = '';
this.localData = '';
this.$nextTick(() => {
this.remoteData = this.formatJson(remoteData || '');
this.localData = this.formatJson(localData || '');
this.step = 0;
this.visible = true;
this.monacoEditorOptions.readOnly = true;
this.diff = null;
this.$nextTick(() => {
if (!this.inited) {
this.inited = true;
let diffEditor = this.$refs.diffEditor;
let editor = diffEditor.getEditor();
editor.onDidUpdateDiff(() => {
if (!this.diff) {
this.diff = editor.getLineChanges();
}
});
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 conflictMarkers) {
if (this.localData.includes(conflictFlag)) {
resolved = false;
break;
}
}
if (resolved) {
try {
JSON.parse(this.localData);
await this.$confirm(this.$t('The conflict has been resolved'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this.step++;
this.visible = false;
this.$emit('resolved', this.localData);
}).catch((e) => {
});
} catch (e) {
this.$alert(this.$t('The format of the JSON document is wrong'), {
type: 'warning'
});
}
} else {
this.$alert(this.$t('There are still unresolved conflicts'), {
type: 'warning'
});
}
},
onCancel() {
this.visible = false;
},
onClose() {
this.removeAllCodeLensProviders();
this.removeAllDecorations();
},
onOpen() {
},
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');
let mergedCodeLines = localCodeLines.concat();
//console.log(this.diff);
let offset = 0;
for (let {modifiedStartLineNumber, modifiedEndLineNumber, originalStartLineNumber, originalEndLineNumber} of this.diff) {
let remotePart = [];
for (let i = originalStartLineNumber, li = originalEndLineNumber; i <= li; i++) {
remotePart.push(remoteCodeLines[i - 1]);
}
let localPart = [];
for (let i = modifiedStartLineNumber, li = modifiedEndLineNumber; i <= li; i++) {
localPart.push(localCodeLines[i - 1]);
}
let merged = [];
merged.push(conflictMarkers[0]);
merged.push(...remotePart);
merged.push(conflictMarkers[1]);
merged.push(...localPart);
merged.push(conflictMarkers[2]);
//console.log(merged);
let startLine = modifiedStartLineNumber + offset + (modifiedEndLineNumber === 0 ? 1 : 0);
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');
}*/
replaceText(replacer);
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');
}
return null;
}
function replaceText(replacer) {
let op = {
range: {
startLineNumber: conflict.remoteLine + 1,
startColumn: 1,
endLineNumber: conflict.localLine + 1,
endColumn: Number.MAX_VALUE,
},
text: replacer,
};
if (replacer === null) {
op.range.startLineNumber = conflict.remoteLine;
op.range.startColumn = Number.MAX_VALUE;
}
model.pushEditOperations([], [op], () => []);
}
},
...mapMutations([
'modifyProjectDetails',
]),
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<el-dialog :title="$t('Project conflict resolver')" width="70%" :visible.sync="visible" @opened="onOpen"
<el-dialog :title="$t('Project conflict resolver')" width="80vw" :visible.sync="visible" @opened="onOpen"
@close="onClose"
:close-on-click-modal="false"
:append-to-body="true"
fullscreen
custom-class="flex-dialog project-conflict-resolve-editor"
>
<div class="wrapper">
<el-steps :active="step" finish-status="success" align-center>
<el-step v-for="(item, index) in steps" :title="item" :key="index"></el-step>
</el-steps>
<div class="container">
<monaco-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>
<span>冲突数:{{conflictCounting}}</span>
<el-tabs tab-position="left" class="category-tab">
<el-tab-pane v-for="(categoryConfig, category) in categories" :key="category"
:label="`${categoryConfig.label}(${conflictCountingMap[category]})`">
<list-compare-view class="compare-view" :compares="compareGroup[category]" :category="category"
@merge-conflict="mergeConflict"/>
</el-tab-pane>
</el-tabs>
</div>
<div slot="footer" class="dialog-footer">
<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="onCancel">{{$t('Cancel')}}</el-button>
<el-button size="mini" @click="onSave" type="primary" :disabled="step===0">{{$t('Save')}}</el-button>
<el-button size="mini" @click="onSave" type="primary" :disabled="conflictCounting > 0">{{$t('Save')}}
</el-button>
</div>
</div>
<code-conflict-resolve-dialog ref="codeConflictResolveDialog" @resolved="onCodeConflictResolved"/>
</el-dialog>
</template>
<script>
import {mapMutations} from 'vuex';
import MonacoEditor from "vue-monaco";
import {formatJson, monacoEditorOptions} from "../../../utils";
import SplitPanes from 'splitpanes'
import i18n from "@/i18n";
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;
import ListCompareView from "./ProjectConflictResolveDialog/ListCompareView";
import CodeConflictResolveDialog from "./CodeConflictResolveDialog";
export default {
name: "ProjectConflictResolveDialog",
components: {MonacoEditor, SplitPanes},
components: {CodeConflictResolveDialog, ListCompareView},
data() {
let monacoEditorOptionsSelf = Object.assign({
readOnly: true,
}, monacoEditorOptions);
let categories = this.$t('categories');
let conflictCountingMap = {};
for (let key in categories) {
conflictCountingMap[key] = 0;
}
return {
monacoEditorOptions: monacoEditorOptionsSelf,
visible: false,
step: 0,
remoteData: '',
localData: '',
steps: this.$t('project-conflicts-resolve-steps'),
categories,
compareGroup: {},
conflictCountingMap,
}
},
mounted() {
this.codeLensProviders = [];
this.conflicts = [];
this.conflictIndex = 0;
this.decrations = [];
},
destroyed() {
computed: {
conflictCounting() {
let t = 0;
for (let count of Object.values(this.conflictCountingMap)) {
t += count;
}
return t;
}
},
methods: {
show(remoteData, localData) {
this.remoteData = '';
this.localData = '';
this.$nextTick(() => {
this.remoteData = this.formatJson(remoteData);
this.localData = this.formatJson(localData);
this.step = 0;
this.visible = true;
this.monacoEditorOptions.readOnly = true;
this.diff = null;
this.$nextTick(() => {
if (!this.inited) {
this.inited = true;
let diffEditor = this.$refs.diffEditor;
let editor = diffEditor.getEditor();
editor.onDidUpdateDiff(() => {
if (!this.diff) {
this.diff = editor.getLineChanges();
}
});
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 conflictMarkers) {
if (this.localData.includes(conflictFlag)) {
resolved = false;
break;
}
try {
this.analyseCompare(remoteData, localData);
} catch (e) {
console.log(e);
}
if (resolved) {
try {
JSON.parse(this.localData);
await this.$confirm(this.$t('The conflict has been resolved'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this.step++;
this.visible = false;
this.$emit('resolved', this.localData);
}).catch((e) => {
});
} catch (e) {
this.$alert(this.$t('The format of the JSON document is wrong'), {
type: 'warning'
});
}
} else {
this.$alert(this.$t('There are still unresolved conflicts'), {
type: 'warning'
});
}
},
onCancel() {
this.visible = false;
this.visible = true;
},
onClose() {
this.removeAllCodeLensProviders();
this.removeAllDecorations();
},
onOpen() {
},
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();
}
async onSave() {
await this.$confirm(this.$t('All conflict has been resolved'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this.step++;
this.visible = false;
this.$emit('resolved', JSON.stringify(this.localObj));
}).catch((e) => {
});
this.visible = false;
},
removeAllDecorations() {
let editor = this.$refs.editor;
let resolveEditor = editor.getEditor();
onCancel() {
resolveEditor.deltaDecorations(this.decrations, []);
this.decrations.splice(0);
this.visible = false;
},
delayTrack() {
console.log('delayTrack');
let lines = this.localData.split('\n');
analyseCompare(remoteData, localData) {
let remoteJson = JSON.parse(remoteData);
let localJson = JSON.parse(localData);
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;
this.localObj = localJson;
let that = this;
conflicts.push(currentConflict);
currentConflict = null;
}
for (let key in this.categories) {
this.conflictCountingMap[key] = 0;
}
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'}
},)
let compareGroup = {};
for (let category in this.categories) {
let categoryConfig = this.categories[category];
let compares = [];
if (categoryConfig.simple) {
addCompare(category, compares, remoteJson[category], localJson[category]);
} else {
let {key, name} = categoryConfig;
let remote = remoteJson[category] || [];
let local = localJson[category] || [];
let analysedUUIDs = [];
for (let remoteItem of remote) {
const localItem = local.find(item => item[key] === remoteItem[key]);
addCompare(category, compares, remoteItem, localItem, remoteItem[key], name);
analysedUUIDs.push(remoteItem[key]);
}
if (localLine - splitLine > 1) {
deltaDecorations.push({
range: {
startLineNumber: splitLine + 2,
endLineNumber: localLine,
},
options: {isWholeLine: true, className: 'local-change-class'}
},)
for (let localItem of local) {
if (analysedUUIDs.includes(localItem[key])) {
continue;
}
const remoteItem = remote.find(item => item[key] === localItem[key]);
addCompare(category, compares, remoteItem, localItem, localItem[key], name);
analysedUUIDs.push(localItem[key]);
}
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;
}
compareGroup[category] = compares;
}
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');
let mergedCodeLines = localCodeLines.concat();
this.compareGroup = compareGroup;
//console.log(this.diff);
let offset = 0;
for (let {modifiedStartLineNumber, modifiedEndLineNumber, originalStartLineNumber, originalEndLineNumber} of this.diff) {
let remotePart = [];
for (let i = originalStartLineNumber, li = originalEndLineNumber; i <= li; i++) {
remotePart.push(remoteCodeLines[i - 1]);
function addCompare(category, compares, remote, local, key, name = 'name') {
let remoteStr = remote ? JSON.stringify(remote) : '';
let localStr = local ? JSON.stringify(local) : '';
let compare = {
resolved: !((!remote || !local) || remoteStr !== localStr),
};
if (remote) {
compare.remote = {name: remote[name], key, data: remoteStr, obj: remote};
}
let localPart = [];
for (let i = modifiedStartLineNumber, li = modifiedEndLineNumber; i <= li; i++) {
localPart.push(localCodeLines[i - 1]);
if (local) {
compare.local = {name: local[name], key, data: localStr, obj: local};
}
let merged = [];
merged.push(conflictMarkers[0]);
merged.push(...remotePart);
merged.push(conflictMarkers[1]);
merged.push(...localPart);
merged.push(conflictMarkers[2]);
//console.log(merged);
compares.push(compare);
let startLine = modifiedStartLineNumber + offset + (modifiedEndLineNumber === 0 ? 1 : 0);
mergedCodeLines.splice(startLine - 1, modifiedEndLineNumber - modifiedStartLineNumber + 1, ...merged);
offset += 3 + remotePart.length;
if (!compare.resolved) {
that.conflictCountingMap[category]++;
}
}
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;
mergeConflict(action, conflict, category) {
const {remote, local} = conflict;
switch (action) {
case 0:
replacer = getTextWithRange(conflict.remoteLine, conflict.splitLine);
case 'remote':
this.reSaveConflict(conflict, remote, category);
break;
case 1:
replacer = getTextWithRange(conflict.splitLine, conflict.localLine);
case 'local':
this.reSaveConflict(conflict, local, category);
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;
}
}
case 'manual':
this.lastConflict = conflict;
this.lastCategory = category;
this.$refs.codeConflictResolveDialog.show(remote ? remote.data : '', local ? local.data : '');
break;
}
/*if (replacer !== undefined) {
replaceText(replacer);
//this.localData = lines.join('\n');
}*/
replaceText(replacer);
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');
},
onCodeConflictResolved(data) {
if (this.lastConflict) {
this.reSaveConflict(this.lastConflict, data, this.lastCategory);
}
},
reSaveConflict(conflict, result, category) {
let resultData;
if (result) {
if (typeof result.obj === 'string') {
resultData = JSON.parse(result.obj);
} else {
resultData = result.obj;
}
return null;
}
function replaceText(replacer) {
let op = {
range: {
startLineNumber: conflict.remoteLine + 1,
startColumn: 1,
endLineNumber: conflict.localLine + 1,
endColumn: Number.MAX_VALUE,
},
text: replacer,
};
if (replacer === null) {
op.range.startLineNumber = conflict.remoteLine;
op.range.startColumn = Number.MAX_VALUE;
let data = this.localObj[category];
let categoryConfig = this.categories[category];
if (categoryConfig.simple) {
if (resultData) {
this.localObj[category] = resultData;
} else {
delete this.localObj[category];
}
} else {
if (conflict.local) {
data.some((item, index) => {
if (item[categoryConfig.key] === conflict.local.key) {
if (resultData) {
data[index] = resultData;
} else {
data.splice(index, 1);
}
return true;
}
});
}
model.pushEditOperations([], [op], () => []);
}
},
...mapMutations([
'modifyProjectDetails',
]),
this.$set(conflict, 'resolved', true);
this.conflictCountingMap[category]--;
}
}
}
</script>
......
<template>
<el-table
:data="compares"
stripe
size="mini"
height="100%">
<el-table-column
prop="remote.name"
label="远程版本">
</el-table-column>
<el-table-column
prop="local.name"
label="本地版本">
</el-table-column>
<el-table-column
label="操作"
width="280px"
>
<template slot-scope="scope">
<template v-if="!scope.row.resolved">
<el-button size="mini" type="success" @click="mergeConflict('remote', scope.row)">接受远程</el-button>
<el-button size="mini" type="success" @click="mergeConflict('local', scope.row)">接受本地</el-button>
<el-button size="mini" type="warning" @click="mergeConflict('manual', scope.row)">手动合并</el-button>
</template>
<span v-else>无冲突</span>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
name: "ListCompareView",
props: ['category', 'compares'],
data() {
return {}
},
mounted() {
this.update();
},
watch: {
conflicts() {
this.update();
},
},
methods: {
update() {
},
mergeConflict(action, conflict) {
this.$emit('merge-conflict', action, conflict, this.category);
},
}
}
</script>
<style scoped>
</style>
\ 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