修改文本编辑器;编辑区添加tab页

This commit is contained in:
jmal 2020-06-23 08:54:07 +08:00
parent c7774cf867
commit f7557eef6d
3 changed files with 276 additions and 123 deletions

View File

@ -26,4 +26,12 @@ export default {
data data
}) })
}, },
// 编辑文档 /markdown/edit
editMarkdownByPath: function(data) {
return request({
url: '/markdown/edit1',
method: 'post',
data
})
},
} }

View File

@ -77,11 +77,41 @@ export default {
render (h) { render (h) {
return ( return (
<div class="monaco_editor_container" style={this.style}></div> <div class="monaco_editor_container" style={this.style} on-keydown={event => {
const isMac = navigator.platform.startsWith('Mac');
const {key, code, keyCode, ctrlKey, metaKey} = event;
const isCmd = isMac && metaKey || !isMac && ctrlKey;
console.log('keydown',isCmd)
if (!isCmd) {
return;
}
const isS = key === 's' || code === 'KeyS' || keyCode === 83;
if (isS) {
this.$emit('save', this._getValue());
event.stopPropagation();
event.preventDefault();
}
}}
></div>
); );
}, },
methods: { methods: {
keyDown(event){
const isMac = navigator.platform.startsWith('Mac');
const {key, code, keyCode, ctrlKey, metaKey} = event;
const isCmd = isMac && metaKey || !isMac && ctrlKey;
console.log('keydown',isCmd)
if (!isCmd) {
return;
}
const isS = key === 's' || code === 'KeyS' || keyCode === 83;
if (isS) {
this.$emit('save', this._getValue());
event.stopPropagation();
event.preventDefault();
}
},
initMonaco() { initMonaco() {
const { value, language, theme, options } = this; const { value, language, theme, options } = this;
Object.assign(options, this._editorBeforeMount()); //编辑器初始化前 Object.assign(options, this._editorBeforeMount()); //编辑器初始化前
@ -95,6 +125,23 @@ export default {
this._editorMounted(this.editor); //编辑器初始化后 this._editorMounted(this.editor); //编辑器初始化后
}, },
onKeyDown(event) {
const isMac = navigator.platform.startsWith('Mac');
const {key, code, keyCode, ctrlKey, metaKey} = event;
const isCmd = isMac && metaKey || !isMac && ctrlKey;
if (!isCmd) {
return;
}
const isS = key === 's' || code === 'KeyS' || keyCode === 83;
if (isS && this.textPreviewVisible) {
if(this.newContent !== ''){
this.save()
}
event.stopPropagation();
event.preventDefault();
}
},
_getEditor() { _getEditor() {
if(!this.editor) return null; if(!this.editor) return null;
return this.diffEditor ? this.editor.modifiedEditor : this.editor; return this.diffEditor ? this.editor.modifiedEditor : this.editor;
@ -141,8 +188,8 @@ export default {
} }
}, },
_emitChange(value, event) { _emitChange(value) {
this.$emit('change', value, event); this.$emit('change', value);
this.$emit('input', value); this.$emit('input', value);
} }
} }

View File

@ -15,42 +15,56 @@
<div slot="title" class="simtext-header-title"> <div slot="title" class="simtext-header-title">
<span class="title-name">{{file.name}}</span> <span class="title-name">{{file.name}}</span>
<div class="title-extension"> <div class="title-extension">
<el-button v-if="isShowUpdateBtn" @click="update" :class="lightTheme?'':'dark-button'" :loading="updating">Ctrl-S 保存</el-button> <el-button v-if="isShowUpdateBtn" @click="update" :class="lightTheme?'':'dark-button'" size="small" :loading="updating">保存所有</el-button>
<!-- <el-button @click="changePreviewMode">{{previewMode?'预览模式':'源码模式'}}</el-button> --> <!-- <el-button @click="changePreviewMode">{{previewMode?'预览模式':'源码模式'}}</el-button> -->
<el-button @click="skinning" :class="lightTheme?'':'dark-button'" :icon="lightTheme?'el-icon-moon':'el-icon-sunny'" circle></el-button> <el-button @click="skinning" :class="lightTheme?'':'dark-button'" size="small" :icon="lightTheme?'el-icon-moon':'el-icon-sunny'" circle></el-button>
<button class="title-extension-button" @click="fullScreen"> <button class="title-extension-button" @click="fullScreen" size="small">
<svg-icon :icon-class="fullscreen?'normalscreen':'fullscreen'"></svg-icon> <svg-icon :icon-class="fullscreen?'normalscreen':'fullscreen'"></svg-icon>
</button> </button>
</div> </div>
</div> </div>
<div class="content" @keydown="onKeyDown"> <div class="content">
<div class="editor_main_storey"></div> <!--<div class="editor_main_storey"></div>-->
<div class="file-contents" :style="{width: contentsWidth+'px',height: editorHieght+'px'}"> <div class="file-contents" :style="{width: contentsWidth+'px',height: editorHieght+31+'px'}">
<div class="dir-tools" :style="{width: contentsWidth+5+'px'}"> <div class="dir-tools" :style="{width: contentsWidth+2+'px',minWidth: 272+'px'}">
<el-button-group> <el-button-group>
<el-button :class="lightTheme?'':'dark-button'" size="mini" icon="el-icon-arrow-left" @click="upperLeve">上一级</el-button> <el-button :class="lightTheme?'light-button':'dark-button'" size="small" icon="el-icon-arrow-left" @click="upperLeve">上一级</el-button>
<el-button :class="lightTheme?'':'dark-button'" size="mini" icon="el-icon-refresh" @click="refresh">刷新</el-button> <el-button :class="lightTheme?'light-button':'dark-button'" size="small" icon="el-icon-refresh" @click="refresh">刷新</el-button>
<el-button :class="lightTheme?'':'dark-button'" size="mini" icon="el-icon-plus">新建</el-button> <el-button :class="lightTheme?'light-button':'dark-button'" size="small" icon="el-icon-plus">新建</el-button>
<el-button :class="lightTheme?'':'dark-button'" size="mini" icon="el-icon-search">搜索</el-button> <el-button :class="lightTheme?'light-button':'dark-button'" size="small" icon="el-icon-search">搜索</el-button>
</el-button-group> </el-button-group>
</div> </div>
<fancy-tree ref="fancTree" v-if="directoryTreeData.length > 0" :lightTheme="lightTheme" :directoryTreeData="directoryTreeData" @treeNodeClick="treeNodeClick"></fancy-tree> <div class="content-tree" :style="{width: contentsWidth+'px',height: editorHieght+'px'}">
<fancy-tree ref="fancTree" v-if="directoryTreeData.length > 0" :lightTheme="lightTheme" :directoryTreeData="directoryTreeData" @treeNodeClick="treeNodeClick"></fancy-tree>
</div>
</div> </div>
<div class="editor-resize" style="width: 5px;cursor: col-resize;"></div> <div class="editor-resize" style="width: 3px;cursor: col-resize;"></div>
<div class="editor"> <div :style="{width: editorWidth-3+'px'}">
<MonacoEditor <el-tabs v-model="editableTabsValue" type="card" closable @tab-remove="removeTab">
v-if="textPreviewVisible" <el-tab-pane
ref="monacoEditor" v-for="(item,index) in editableTabs"
:width="editorWidth" :key="item.name"
:height="editorHieght" :label="item.title"
:theme="lightTheme?'vs':'vs-dark'" :name="item.name"
:language="language" >
:diffEditor="diffEditor" <div class="editor">
original="..." <MonacoEditor
:value="content" v-if="textPreviewVisible"
:options="options" ref="monacoEditor"
@change="change" :width="editorWidth"
></MonacoEditor> :height="editorHieght"
:theme="lightTheme?'vs':'vs-dark'"
:language="language"
:diffEditor="diffEditor"
original="..."
:value="item.content"
:options="options"
@change="change($event,index)"
@save="save($event,index)"
></MonacoEditor>
</div>
</el-tab-pane>
</el-tabs>
</div> </div>
</div> </div>
</el-dialog> </el-dialog>
@ -130,7 +144,9 @@
border: '1px solid #565656!important', border: '1px solid #565656!important',
color: '#ffffff!important' color: '#ffffff!important'
}, },
directoryTreeData: [] directoryTreeData: [],
editableTabsValue: '1',
editableTabs: [],
} }
}, },
mounted() { mounted() {
@ -156,6 +172,7 @@
}, },
watch: { //监听file的变化,进行相应的操作即可 watch: { //监听file的变化,进行相应的操作即可
file: function (file) { file: function (file) {
this.editableTabs = []
this.editorWidth = document.body.clientWidth * this.dialogWidth - this.contentsWidth this.editorWidth = document.body.clientWidth * this.dialogWidth - this.contentsWidth
this.editorHieght = document.body.clientHeight * this.dialogWidth - 50 this.editorHieght = document.body.clientHeight * this.dialogWidth - 50
@ -201,6 +218,15 @@
this.textPreviewVisible = true this.textPreviewVisible = true
this.content = res.data.contentText this.content = res.data.contentText
// 加载tabs
this.editableTabs.push({
title: res.data.name,
copyTitle: res.data.name,
name: res.data.path.substring(1,res.data.path.length)+res.data.name,
content: res.data.contentText
})
this.editableTabsValue = res.data.path.substring(1,res.data.path.length)+res.data.name
// 界面的渲染后的初始化工作 // 界面的渲染后的初始化工作
this.$nextTick(()=>{ this.$nextTick(()=>{
this.dragControllerDiv() this.dragControllerDiv()
@ -226,6 +252,8 @@
dragControllerDiv() { dragControllerDiv() {
let resize = document.querySelector('.el-dialog__body .content .editor-resize'); let resize = document.querySelector('.el-dialog__body .content .editor-resize');
let left = document.querySelector('.file-contents'); let left = document.querySelector('.file-contents');
let leftTools = document.querySelector('.file-contents .dir-tools');
let leftTree = document.querySelector('.file-contents .content-tree');
if(resize){ if(resize){
// 鼠标按下事件 // 鼠标按下事件
const that = this const that = this
@ -237,8 +265,13 @@
let endX = e.clientX; let endX = e.clientX;
// 移动的距离。负数向左移动,正数向右移动 // 移动的距离。负数向左移动,正数向右移动
let moveLen = endX - startX let moveLen = endX - startX
left.style.width = (contentsStartWidth + moveLen)+'px' // left.style.width = (contentsStartWidth + moveLen)+'px'
that.editorWidth = document.body.clientWidth * that.dialogWidth - (contentsStartWidth + moveLen) // leftTree.style.width = (contentsStartWidth + moveLen)+'px'
console.log(leftTools.style.minWidth.split('\px')[0])
if((contentsStartWidth + moveLen) > leftTools.style.minWidth.split('\px')[0]){
that.contentsWidth = contentsStartWidth + moveLen
that.editorWidth = document.body.clientWidth * that.dialogWidth - (contentsStartWidth + moveLen)
}
} }
// 鼠标松开事件 // 鼠标松开事件
document.onmouseup = function (evt) { document.onmouseup = function (evt) {
@ -284,7 +317,17 @@
username: this.$store.state.user.name username: this.$store.state.user.name
}).then((res)=>{ }).then((res)=>{
this.loading.close() this.loading.close()
this.content = res.data.contentText if(this.editableTabs.findIndex(tab=> tab.name===row.path) < 0){
console.log('添加一个tab')
// 添加一个tab
this.editableTabs.push({
title: row.name,
copyTitle: row.name,
name: row.path,
content: res.data.contentText
})
}
this.editableTabsValue = row.path
}).catch(() => { }).catch(() => {
this.loading.close() this.loading.close()
}) })
@ -311,26 +354,39 @@
this.$emit('update:status', this.textPreviewVisible) this.$emit('update:status', this.textPreviewVisible)
this.isShowUpdateBtn = false this.isShowUpdateBtn = false
}, },
change(code) { change(value,index) {
this.isShowUpdateBtn = true if(value === this.editableTabs[index].content){
this.newContent = code if(this.editableTabs[index].copyTitle !== this.editableTabs[index].title){
if(code === this.content){ this.editableTabs[index].title = this.editableTabs[index].copyTitle
this.isShowUpdateBtn = false // 没有任何改变
if(this.editableTabs.findIndex(tab=>tab.title !== tab.copyTitle) < 0){
this.isShowUpdateBtn = false
}
}
}else{
if(this.editableTabs[index].copyTitle === this.editableTabs[index].title){
this.editableTabs[index].title += '*'
this.isShowUpdateBtn = true
}
}
// this.isShowUpdateBtn = true
// this.newContent = code
// if(code === this.content){
// this.isShowUpdateBtn = false
// }
},
save(value,index) {
if(value !== this.editableTabs[index].content && this.isShowUpdateBtn){
this.update(value,this.editableTabs[index].name)
} }
}, },
save() { update(value,path) {
if(this.isShowUpdateBtn){
this.update()
}
},
update() {
this.updating = true this.updating = true
markdownApi.editMarkdown({ markdownApi.editMarkdownByPath({
fileId: this.file.id, relativePath: path,
userId: this.$store.state.user.userId,
username: this.$store.state.user.name, username: this.$store.state.user.name,
filename: this.file.name, contentText: value
contentText: this.newContent
}).then(() => { }).then(() => {
this.updating = false this.updating = false
this.isShowUpdateBtn = false this.isShowUpdateBtn = false
@ -388,22 +444,22 @@
changePreviewMode() { changePreviewMode() {
this.previewMode = !this.previewMode this.previewMode = !this.previewMode
}, },
onKeyDown(event) { removeTab(targetName) {
const isMac = navigator.platform.startsWith('Mac'); let tabs = this.editableTabs;
const {key, code, keyCode, ctrlKey, metaKey} = event; let activeName = this.editableTabsValue;
const isCmd = isMac && metaKey || !isMac && ctrlKey; if (activeName === targetName) {
if (!isCmd) { tabs.forEach((tab, index) => {
return; if (tab.name === targetName) {
let nextTab = tabs[index + 1] || tabs[index - 1];
if (nextTab) {
activeName = nextTab.name;
}
}
});
} }
const isS = key === 's' || code === 'KeyS' || keyCode === 83; this.editableTabsValue = activeName;
if (isS && this.textPreviewVisible) { this.editableTabs = tabs.filter(tab => tab.name !== targetName);
if(this.newContent !== ''){ }
this.save()
}
event.stopPropagation();
event.preventDefault();
}
},
} }
} }
</script> </script>
@ -450,6 +506,15 @@
margin: 0 !important; margin: 0 !important;
overflow: hidden; overflow: hidden;
.el-button--small.is-circle {
padding: 9px 10px;
}
.light-button {
background: #ececec;
border: 1px solid #ececec;
}
.dark-button { .dark-button {
background: #565656; background: #565656;
border: 1px solid #565656; border: 1px solid #565656;
@ -486,6 +551,7 @@
.title-extension { .title-extension {
float: right; float: right;
margin-right: 30px; margin-right: 30px;
margin-top: 3px;
display: -webkit-box; display: -webkit-box;
display: -ms-flexbox; display: -ms-flexbox;
display: flex; display: flex;
@ -517,7 +583,15 @@
} }
.content { .content {
border-top: unset!important; border-top: unset!important;
display: flex; display: inline-flex;
.el-tabs__header {
margin: 0 0 0;
.el-tabs__item {
height: 31px;
line-height: 31px;
}
}
.editor_main_storey { .editor_main_storey {
display: inline-block; display: inline-block;
@ -528,43 +602,58 @@
background: linear-gradient(rgba(221,221,221,1), rgba(255, 255, 255, 0)); background: linear-gradient(rgba(221,221,221,1), rgba(255, 255, 255, 0));
} }
.el-tabs__nav-wrap {
margin-left: -1px;
}
.el-tabs--card>.el-tabs__header .el-tabs__nav {
border-radius: 0 0 0 0;
}
.el-tabs__nav-next, .el-tabs__nav-prev {
line-height: 31px;
}
.file-contents{ .file-contents{
overflow-y: scroll; .content-tree{
overflow-x: hidden; overflow-y: scroll;
overflow-x: hidden;
@include scrollBarLightStyle; @include scrollBarLightStyle;
background: #fff; background: #fff;
box-shadow: inset #eaeaea -7px 0px; box-shadow: inset #ececec -7px 0px;
#dir-tree { #dir-tree {
ul.fancytree-container{ ul.fancytree-container{
padding: 3px 0 0 0; padding: 3px 0 0 0;
}
}
.svg-icon {
font-size: 18px;
}
#dir-tree {
.fancytree-plain span.fancytree-active span.fancytree-title{
background-color: #409eff30;
border-color: #ffffff;
}
.fancytree-plain span.fancytree-node:hover span.fancytree-title{
background-color: #409eff30;
border-color: #ffffff;
}
.fancytree-plain.fancytree-container.fancytree-treefocus span.fancytree-active span.fancytree-title{
background-color: #409eff30;
border-color: #ffffff;
}
} }
} }
.svg-icon {
font-size: 18px;
}
#dir-tree {
.fancytree-plain span.fancytree-active span.fancytree-title{
background-color: #409eff30;
border-color: #ffffff;
}
.fancytree-plain span.fancytree-node:hover span.fancytree-title{
background-color: #409eff30;
border-color: #ffffff;
}
.fancytree-plain.fancytree-container.fancytree-treefocus span.fancytree-active span.fancytree-title{
background-color: #409eff30;
border-color: #ffffff;
}
}
.dir-tools { .dir-tools {
.el-button--mini, .el-button--mini.is-round { background: #ececec;
padding: 7px 10px; border-right: 5px solid #fff;
.el-button--small {
padding: 9px 10px;
} }
} }
@ -577,29 +666,38 @@
} }
.file-contents{ .file-contents{
@include scrollBarDarkStyle;
background: $bg-color;
box-shadow: inset #2d2d2d -7px 0px;
#dir-tree {
span.fancytree-title {
color: #dedede;
}
ul.fancytree-container {
background: #1e1e1e;
}
.fancytree-plain span.fancytree-active span.fancytree-title{
background-color: $tree-title-bg-color;
border-color: $bg-color;
}
.fancytree-plain span.fancytree-node:hover span.fancytree-title{ background: #565656;
background-color: $tree-title-bg-color;
border-color: $bg-color;
}
.fancytree-plain.fancytree-container.fancytree-treefocus span.fancytree-active span.fancytree-title{ .dir-tools {
background-color: $tree-title-bg-color; background: #565656;
border-color: $bg-color; }
.content-tree {
@include scrollBarDarkStyle;
background: $bg-color;
box-shadow: inset #2d2d2d -7px 0px;
#dir-tree {
span.fancytree-title {
color: #dedede;
}
ul.fancytree-container {
background: #1e1e1e;
}
.fancytree-plain span.fancytree-active span.fancytree-title{
background-color: $tree-title-bg-color;
border-color: $bg-color;
}
.fancytree-plain span.fancytree-node:hover span.fancytree-title{
background-color: $tree-title-bg-color;
border-color: $bg-color;
}
.fancytree-plain.fancytree-container.fancytree-treefocus span.fancytree-active span.fancytree-title{
background-color: $tree-title-bg-color;
border-color: $bg-color;
}
} }
} }
} }
@ -608,13 +706,13 @@
} }
} }
.monaco-editor { /*.monaco-editor {*/
padding: 3px 0 0 0; /*padding: 3px 0 0 0;*/
.scroll-decoration{ /*.scroll-decoration{*/
box-shadow: unset!important; /*box-shadow: unset!important;*/
} /*}*/
} /*}*/
} }
} }