文本编辑器加上目录导航

This commit is contained in:
jmal 2020-07-12 23:44:16 +08:00
parent f93d3c944d
commit caa1133b97
5 changed files with 282 additions and 68 deletions

View File

@ -247,4 +247,12 @@ export default {
params
})
},
// 根据path添加文件/文件夹
addFile: function(params) {
return request({
url: '/addfile',
method: 'post',
params
})
},
}

View File

@ -0,0 +1,86 @@
<template>
<el-breadcrumb class="app-breadcrumb" separator="">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in pathList" :key="item.path">
<a v-if="index===0" class="home-link" @click.prevent="handleLink(item,index)"><svg-icon icon-class="home" style="font-size: 18px;"/></a>
<!--<span v-if="index===pathList.length-1" class="no-redirect">{{ item.title }}</span>-->
<!--<a v-if="index > 0 && index < pathList.length-1" @click.prevent="handleLink(item)">{{ item.title }}</a>-->
<breadcrumb-file-path :pathList="pathList" :item="item" :index="index" @clickLink="handleLink"></breadcrumb-file-path>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
import { strlen, substring10,substring } from '@/utils/number'
import BreadcrumbFilePath from "@/components/Breadcrumb/BreadcrumbFilePath";
export default {
name: 'FilePathNav',
components: {
BreadcrumbFilePath
},
props: {
path:{
type: String,
default: ''
}
},
data() {
return {
pathList: []
}
},
watch: {
path(path){
console.log('path', path)
this.pathList = [{folder:'',path: '/'}]
let list = path.split('/').filter((item)=>item.length>0)
list.forEach((pathName,index)=>{
const item = {
folder: pathName,
path: this.getPath(list,index)
}
this.pathList.push(item)
})
}
},
created() {
},
methods: {
strLength(str) {
return strlen(str)
},
substring10(str) {
return substring10(str)
},
substring(str,n){
return substring(str,n)
},
getPath(list,index){
let path = '/'
if(list && list.length > index){
for (let i = 0; i <= index; i++) {
path += list[i]+'/'
}
}
return path
},
handleLink(item) {
this.$emit('loadPath',item)
}
}
}
</script>
<style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 8px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>

View File

@ -1,5 +1,12 @@
<template>
<div>
<v-contextmenu ref="clickContextmenu" :theme="lightTheme?'default':'dark'" event-type="click">
<v-contextmenu-item v-for="item of menus" :key="item.operation" @click="contextmenuClick(item.operation)">
<svg-icon :icon-class="item.iconClass"></svg-icon>
{{item.label}}
</v-contextmenu-item>
</v-contextmenu>
<v-contextmenu ref="contextmenu" :theme="lightTheme?'default':'dark'" @contextmenu="contextmenu">
<v-contextmenu-item v-for="item of menus" :key="item.operation" @click="contextmenuClick(item.operation)">
<svg-icon :icon-class="item.iconClass"></svg-icon>
@ -10,7 +17,7 @@
<el-button-group>
<el-button :class="lightTheme?'light-button':'dark-button'" size="small" icon="el-icon-arrow-left" @click="upperLeve">上一级</el-button>
<el-button :class="lightTheme?'light-button':'dark-button'" size="small" icon="el-icon-refresh" @click="refresh">刷新</el-button>
<el-button :class="lightTheme?'light-button':'dark-button'" size="small" icon="el-icon-plus">新建</el-button>
<el-button :class="lightTheme?'light-button':'dark-button'" size="small" icon="el-icon-plus" @click="newFileAndFolder" v-contextmenu:clickContextmenu>新建</el-button>
<el-button :class="lightTheme?'light-button':'dark-button'" size="small" icon="el-icon-search">搜索</el-button>
</el-button-group>
</div>
@ -19,7 +26,6 @@
</div>
</template>
<script>
import $ from 'jquery'
import 'v-contextmenu/dist/index.css'
import { iconClass,suffix } from '@/utils/file-type'
import 'jquery.fancytree/dist/skin-win8/ui.fancytree.less';
@ -40,17 +46,18 @@
default: 500
},
directoryTreeData: {
type: Array,
default: () => []
type: Object,
default: () => {}
},
lightTheme: {
type: Boolean,
default: true,
}
},
},
data(){
return {
tree:{},
currentPath: '/',
contextData: {},
rightClicking: false,
menus: [],
@ -80,6 +87,48 @@
computed: {
},
methods: {
initTree(treeData){
const that = this
this.tree = fancytree.createTree('#dir-tree', {
extensions: ["edit"],
icon: (event,data)=> this.icon(event,data),
source: this.convert(treeData),
lazyLoad: (event,data)=> this.lazyLoad(event,data),
click: (event,data)=> this.click(event,data),
dblclick: (event,data)=> this.dblclick(event,data),
activate: (event,data)=> this.activate(event,data),
createNode: (event,data)=> this.createNode(event,data),
edit: {
triggerStart: ["f2", "mac+enter"],
beforeEdit: function(event, data){
},
edit: function(event, data){
let intput = data.node.span.querySelector('.fancytree-title .fancytree-edit-input')
let width = intput.style.width.substring(0,intput.style.width.length-2)
intput.style.width = parseInt(width)+50+'px'
},
beforeClose: function(event, data){
if( data.originalEvent.type === "mousedown" ) {
data.originalEvent.preventDefault();
return false;
}
},
save: function(event, data){
if(data.node.data.path){
that.nodeRename(data.input.val(), data.node.data, data.node)
}else{
that.createFile(data.input.val(), data.node.data, data.node)
}
return true;
},
close: function(event, data){
if( data.save ) {
}
return false
}
}
});
},
contextmenu(vnode){
this.rightClicking = true
const that = this
@ -94,27 +143,45 @@
if(this.contextData.isFolder){
this.menus = this.folderMenus
}else{
this.menus = this.fileMenus
if(this.contextData.isFolder === undefined){
}else{
this.menus = this.fileMenus
}
}
},
// 菜单点击事件
contextmenuClick(operation){
let node = this.tree.getActiveNode()
switch (operation) {
case 'openFolder':
console.log('openFolder',this.contextData)
this.enterDir(this.contextData)
break
case 'remove':
console.log('remove',this.contextData)
this.delFile(this.contextData)
break
case 'rename':
console.log('rename',this.contextData)
this.tree.getActiveNode().editStart()
break
default:
console.log('default',this.contextData)
case 'addFile':
this.editCreateNode(node,false)
break
case 'addFolder':
this.editCreateNode(node,true)
break
default:
break
}
},
editCreateNode(node,isFolder){
if(node === null || node === undefined){
this.tree.getRootNode().editCreateNode("child",{title:'',folder:isFolder})
return
}
if(node.folder){
node.editCreateNode("child",{title:'',folder:isFolder})
}else{
node.editCreateNode("after",{title:'',folder:isFolder})
}
},
// 删除文件/文件夹
@ -135,16 +202,13 @@
})
})
},
// 上级目录
upperLeve(){
let path = this.tree.rootNode.children[0].key
let title = this.tree.rootNode.children[0].title
if(path.endsWith(title)){
path = path.substring(0,path.length-title.length)
}
if(path.length === 0){
if(this.currentPath.length === 0){
return
}
let pathList = this.currentPath.split('/')
let title = pathList[pathList.length-2]
let path = this.currentPath.substring(0,this.currentPath.length-title.length)
api.upperLevelList({
username: this.$store.state.user.name,
path: path,
@ -154,13 +218,17 @@
},
// 刷新当前目录
refresh(){
api.upperLevelList({
api.listfiles({
userId: this.$store.state.user.userId,
username: this.$store.state.user.name,
path: this.tree.rootNode.children[0].key,
path: this.currentPath,
}).then(res => {
this.tree.reload(this.convert(res.data))
})
},
newFileAndFolder(){
this.menus = this.newFileMenus
},
//进入目录
enterDir(node){
let loading = this.$message({
@ -177,18 +245,25 @@
path: node.path,
}).then(res => {
loading.close()
if(res.data.length === 0){
this.currentPath = node.path+'/'
}
this.tree.reload(this.convert(res.data))
}).catch(()=>{
loading.close()
})
},
loadTreeData(treeData,reLoad) {
loadPath(data){
data.name = data.title
this.loadTreeData(data,true)
},
loadTreeData(treeData,reLoad,activeNodeData) {
api.listfiles({
userId: this.$store.state.user.userId,
username: this.$store.state.user.name,
path: treeData[0].path,
path: treeData.path,
}).then(res => {
let oldname = treeData[0].name
let oldname = treeData.name
let data = res.data.map(doc => {
if(oldname === doc.name){
doc.active = true
@ -201,47 +276,45 @@
})
if(reLoad){
this.tree.reload(this.convert(data))
if(activeNodeData){
this.$emit('treeNodeClick', activeNodeData)
}
}else{
this.initTree(this.convert(data))
}
})
},
initTree(treeData){
const that = this
this.tree = fancytree.createTree('#dir-tree', {
extensions: ["edit"],
icon: (event,data)=> this.icon(event,data),
source: this.convert(treeData),
lazyLoad: (event,data)=> this.lazyLoad(event,data),
click: (event,data)=> this.click(event,data),
dblclick: (event,data)=> this.dblclick(event,data),
activate: (event,data)=> this.activate(event,data),
createNode: (event,data)=> this.createNode(event,data),
edit: {
triggerStart: ["f2", "mac+enter"],
beforeEdit: function(event, data){
console.log(event.type, data);
},
edit: function(event, data){
let intput = data.node.span.querySelector('.fancytree-title .fancytree-edit-input')
let width = intput.style.width.substring(0,intput.style.width.length-2)
intput.style.width = parseInt(width)+50+'px'
},
beforeClose: function(event, data){
},
save: function(event, data){
that.nodeRename(data.input.val(), data.node.data, data.node)
return true;
},
close: function(event, data){
console.log(event.type, data);
if( data.save ) {
}
return false
}
// 新建文件
createFile(fileName,row,node){
let parentPath = '/'
if(node.parent.folder){
parentPath = node.parent.data.path
}else{
let parent = node.parent.children[0].data
if(parent.path){
parentPath += parent.path.substring(0,parent.path.length-parent.name.length)
}else{
parentPath += this.currentPath
}
});
console.log(this.tree.getActiveNode(),fancytree._extensions)
}
api.addFile({
fileName: fileName,
isFolder: node.folder,
username: this.$store.state.user.name,
parentPath: parentPath
}).then((res)=>{
let str = node.folder?'文件夹':'文件'
this.$message.success(`新建${str}成功`)
let activeNodeData = res.data
// if(node.parent.folder){
// activeNodeData = node.parent.data
// activeNodeData.children = res.data
// }
let path = '/'+ activeNodeData.path.substring(0,activeNodeData.path.length-activeNodeData.name.length)
this.loadTreeData({name: activeNodeData.name,path: path},true,activeNodeData)
}).catch(()=>{
node.remove()
})
},
// 重命名
nodeRename(newFileName, row, node) {
@ -259,6 +332,8 @@
if (row.suffix !== newExt) {
this.$confirm(`您确定要将扩展名“.${row.suffix}”更改为“.${newExt}”吗?`,'提示',{
type: 'warning',
showClose: false,
closeOnClickModal: false,
confirmButtonText: `保持.${row.suffix}`,
cancelButtonText: `使用.${newExt}`,
}).then(()=>{
@ -372,13 +447,20 @@
activate(event, data){
},
createNode(event, data){
if(data.node.key !== '_1'){
if(data.node.statusNodeType !== 'nodata'){
this.$refs.contextmenu.addRef({el:data.node.span,vnode: data.node.span})
}else{
data.node.span.querySelector('.fancytree-title').innerText = '无数据'
}
},
convert(data){
if(data && data.length >0){
this.currentPath = data[0].path.substring(0,data[0].path.length - data[0].name.length)
}
if(this.currentPath.length === 0){
this.currentPath = '/'
}
this.$emit('onLoadTreePath', this.currentPath)
return data.map(doc => {
doc.key = doc.path
doc.title = doc.name

View File

@ -1519,12 +1519,13 @@
})
// 使列表可拖拽
this.rowDrop()
this.fileListScrollTop = 0
// 使列表滑到顶部
if(!onLoad && !this.grid){
console.log('使列表滑到顶部',this.$refs.fileListTable)
//this.$refs.fileListTable.pagingScrollTopLeft()
if(this.fileListScrollTop > 0){
this.$refs.fileListTable.pagingScrollTopLeft()
}
}
this.fileListScrollTop = 0
},
searchFile(key,onLoad) {
if(key){

View File

@ -36,7 +36,8 @@
class="simtext-dialog"
v-dialogDrag="{ dialogWidth: dialogWidth}">
<div slot="title" class="simtext-header-title">
<span class="title-name">{{file.name}}</span>
<!--<span class="title-name">{{file.name}}</span>-->
<span><file-path-nav class="title-name" :path="filePathNav" @loadPath="loadPath"></file-path-nav></span>
<div class="title-extension">
<el-button v-if="isShowUpdateBtn" @click="saveAll" :class="lightTheme?'':'dark-button'" size="small" :loading="updating">保存所有</el-button>
<!-- <el-button @click="changePreviewMode">{{previewMode?'预览模式':'源码模式'}}</el-button> -->
@ -49,7 +50,16 @@
<div class="content">
<div class="file-contents" :style="{width: contentsWidth+'px',height: editorHieght+31+'px'}">
<div class="content-tree">
<fancy-tree ref="fancTree" :contentsWidth="contentsWidth" :contentsHieght="editorHieght" v-if="directoryTreeData.length > 0" :lightTheme="lightTheme" :directoryTreeData="directoryTreeData" @treeNodeClick="treeNodeClick"></fancy-tree>
<fancy-tree
ref="fancTree"
:contentsWidth="contentsWidth"
:contentsHieght="editorHieght"
:lightTheme="lightTheme"
:directoryTreeData="directoryTreeData"
@treeNodeClick="treeNodeClick"
@onLoadTreePath="onLoadTreePath"
>
</fancy-tree>
</div>
</div>
<div class="editor-resize" style="width: 3px;cursor: col-resize;"></div>
@ -99,11 +109,12 @@
import FileTree from"@/components/FileTree"
import FancyTree from"@/components/FancyTree"
import MessageDialog from "@/components/message/MessageDialog"
import FilePathNav from '@/components/Breadcrumb/FilePathNav'
export default {
name: "SimTextPreview",
components: {
MonacoEditor,FileTree,FancyTree,MessageDialog
MonacoEditor,FileTree,FancyTree,MessageDialog,FilePathNav
},
props: {
file: {
@ -164,9 +175,10 @@
border: '1px solid #565656!important',
color: '#ffffff!important'
},
directoryTreeData: [],
directoryTreeData: {},
editableTabsValue: '1',
editableTabs: [],
filePathNav: '',
}
},
mounted() {
@ -230,7 +242,7 @@
username: this.$store.state.user.name
}).then((res)=>{
this.loading.close()
this.directoryTreeData = [{name: res.data.name, isFolder: true, isLeaf: false, id: res.data.id, path: res.data.path}]
this.directoryTreeData = {name: res.data.name, isFolder: true, isLeaf: false, id: res.data.id, path: res.data.path}
this.textPreviewVisible = true
this.content = res.data.contentText
@ -304,6 +316,12 @@
}
}
},
loadPath(data){
this.$refs.fancTree.loadPath(data)
},
onLoadTreePath(path){
this.filePathNav = path
},
treeNodeClick(row) {
if(row.isFolder){
return
@ -514,12 +532,18 @@
if(fileContests){
fileContests.setAttribute('data-theme', 'light')
}
if(header){
header.setAttribute('data-theme', 'light')
}
}else{
header.style.background = '#292929'
header.style.color = '#fff'
if(fileContests){
fileContests.setAttribute('data-theme', 'dark')
}
if(header){
header.setAttribute('data-theme', 'dark')
}
}
},
changePreviewMode() {
@ -637,7 +661,7 @@
}
}
.title-name {
line-height: 40px;
line-height: 34px;
}
.title-extension {
float: right;
@ -667,6 +691,19 @@
}
}
&[data-theme=dark] {
.redirect{
color: #f5f5f5;
}
.no-redirect {
color: #b7b7b7;
}
.home-link {
color: #f5f5f5;
}
}
}
.el-dialog__body {
padding: 0;