wiki调试修改,增加自研编辑器

This commit is contained in:
暮光:城中城
2020-06-06 15:34:49 +08:00
parent 2d9a5ab6b6
commit 0965f44866
22 changed files with 1501 additions and 525 deletions

View File

@@ -6450,6 +6450,11 @@
}
}
},
"jquery": {
"version": "3.5.1",
"resolved": "https://registry.npm.taobao.org/jquery/download/jquery-3.5.1.tgz",
"integrity": "sha1-17TQjhv9uGrS8aPQOeoXMEcXq7U="
},
"js-cookie": {
"version": "2.2.1",
"resolved": "https://registry.npm.taobao.org/js-cookie/download/js-cookie-2.2.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-cookie%2Fdownload%2Fjs-cookie-2.2.1.tgz",

View File

@@ -10,15 +10,16 @@
"axios": "^0.19.0",
"core-js": "^3.3.2",
"echarts": "^4.5.0",
"element-ui": "^2.10.0",
"jquery": "^3.5.1",
"js-cookie": "^2.2.1",
"pouchdb": "^7.1.1",
"sql-formatter": "^2.3.3",
"vue": "^2.6.10",
"vue-axios": "^2.1.5",
"vue-hljs": "^1.1.2",
"vue-router": "^3.1.3",
"vuex": "^3.1.2",
"element-ui": "^2.10.0",
"sql-formatter": "^2.3.3",
"wangeditor": "^3.1.1"
},
"devDependencies": {

View File

@@ -1,461 +1,28 @@
<template>
<div id="app">
<template v-if="fullscreen">
<router-view></router-view>
</template>
<el-container v-else>
<el-aside v-show="leftCollapse">
<div style="padding: 10px;height: 100%;box-sizing: border-box;background: #fafafa;">
<div style="margin-bottom: 10px;">
<el-select v-model="choiceSpace" @change="spaceChangeEvents" filterable placeholder="选择空间" style="width: 100%;">
<el-option-group label="">
<el-option key="0" label="创建空间" value="0"></el-option>
<el-option key="-1" label="空间管理" value="-1"></el-option>
</el-option-group>
<el-option-group label="">
<el-option v-for="item in spaceOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-option-group>
</el-select>
</div>
<div align="center">
<el-button v-on:click="createWiki" icon="el-icon-plus" style="width: 100%;">创建文档</el-button>
</div>
<el-input v-model="searchKeywords" @input="searchByKeywords" @keyup.enter.native="searchByKeywords" placeholder="搜索文档" style="margin: 10px 0;">
<el-button slot="append" icon="el-icon-search" v-on:click="searchByKeywordsNewPage"></el-button>
</el-input>
<el-tree :props="defaultProps" :data="wikiPageList" @node-click="handleNodeClick"
@node-expand="handleNodeExpand" draggable @node-drop="handlePageDrop"
ref="wikiPageTree" :filter-node-method="filterPageNode" highlight-current
:expand-on-click-node="false" :default-expanded-keys="wikiPageExpandedKeys"
node-key="id"
style="background-color: #fafafa;">
</el-tree>
</div>
</el-aside>
<el-container>
<el-header>
<!--<el-switch v-model="isCollapse" ></el-switch>-->
<i class="el-icon-menu icon-collapse" @click="leftCollapse = !leftCollapse;"></i>
<!--<div class="logo" @click="aboutDialogVisible = true">zyplayer-doc-wiki</div>-->
<el-dropdown @command="userSettingDropdown" trigger="click">
<i class="el-icon-setting" style="margin-right: 15px; font-size: 16px;cursor: pointer;color: #fff;"> </i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="console">控制台</el-dropdown-item>
<el-dropdown-item command="aboutDoc" divided>关于</el-dropdown-item>
<el-dropdown-item command="myInfo">我的资料</el-dropdown-item>
<el-dropdown-item command="userSignOut">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main style="padding: 0;">
<router-view @loadPageList="loadPageList"
@changeExpandedKeys="changeWikiPageExpandedKeys"
@switchSpace="switchSpacePage">
</router-view>
</el-main>
</el-container>
</el-container>
<!--新建空间弹窗-->
<el-dialog title="创建空间" :visible.sync="newSpaceDialogVisible" width="600px" :close-on-click-modal="false">
<el-form label-width="100px" :model="newSpaceForm" :rules="newSpaceFormRules" ref="newSpaceForm">
<el-form-item label="空间名:" prop="name">
<el-input v-model="newSpaceForm.name"></el-input>
</el-form-item>
<el-form-item label="空间描述:" prop="spaceExplain">
<el-input v-model="newSpaceForm.spaceExplain"></el-input>
</el-form-item>
<el-form-item label="空间开放:">
<el-switch v-model="newSpaceForm.openDoc" inactive-text="需要登录" :inactive-value="0" active-text="开放访问" :active-value="1"></el-switch>
</el-form-item>
<el-form-item label="目录加载:">
<el-switch v-model="newSpaceForm.treeLazyLoad" inactive-text="预先加载" :inactive-value="0" active-text="延迟加载" :active-value="1"></el-switch>
</el-form-item>
<el-form-item label="空间类型:">
<el-select v-model="newSpaceForm.type" filterable placeholder="选择类型" style="width: 100%;">
<el-option :key="1" label="公共空间" :value="1">
<span style="float: left">公共空间</span>
<span style="float: right; color: #8492a6; font-size: 13px;">属于公共登录用户可访问编辑</span>
</el-option>
<el-option :key="2" label="个人空间" :value="2">
<span style="float: left">个人空间</span>
<span style="float: right; color: #8492a6; font-size: 13px;">属于个人所有登录用户可访问</span>
</el-option>
<el-option :key="3" label="隐私空间" :value="3">
<span style="float: left">隐私空间</span>
<span style="float: right; color: #8492a6; font-size: 13px;">属于个人仅创建者可访问</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" v-if="newSpaceForm.id > 0" @click="onNewSpaceSubmit('newSpaceForm')">保存修改</el-button>
<el-button type="primary" v-else @click="onNewSpaceSubmit('newSpaceForm')">立即创建</el-button>
<el-button @click="onNewSpaceCancel">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
<!--管理空间弹窗-->
<el-dialog title="管理空间" :visible.sync="manageSpaceDialogVisible" :close-on-click-modal="false" width="80%">
<el-table :data="spaceList" border style="width: 100%; margin-bottom: 5px;" max-height="500">
<el-table-column prop="id" label="ID" width="60"></el-table-column>
<el-table-column prop="name" label="名字"></el-table-column>
<el-table-column prop="spaceExplain" label="说明"></el-table-column>
<el-table-column label="开放地址">
<template slot-scope="scope">
<a target="_blank" :href="'open-wiki.html?space='+scope.row.uuid" v-if="scope.row.openDoc == 1">{{scope.row.name}}</a>
<span v-else>暂未开放</span>
</template>
</el-table-column>
<el-table-column prop="createUserName" label="创建人"></el-table-column>
<el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button size="small" type="primary" v-on:click="editSpaceInfo(scope.row)">编辑</el-button>
<el-button size="small" type="danger" v-on:click="deleteSpaceInfo(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
<!--关于弹窗-->
<el-dialog title="关于zyplayer-doc-wiki" :visible.sync="aboutDialogVisible" width="600px">
<el-form>
<el-form-item label="项目地址:">
<a target="_blank" href="https://gitee.com/zyplayer/zyplayer-doc">zyplayer-doc</a>
</el-form-item>
<el-form-item label="开发人员:">
<a target="_blank" href="http://zyplayer.com">暮光城中城</a>
</el-form-item>
<template v-if="upgradeInfo.lastVersion">
<el-form-item label="当前版本:">{{upgradeInfo.nowVersion}}</el-form-item>
<el-form-item label="最新版本:">{{upgradeInfo.lastVersion}}</el-form-item>
<el-form-item label="升级地址:">
<a target="_blank" :href="upgradeInfo.upgradeUrl">{{upgradeInfo.upgradeUrl}}</a>
</el-form-item>
<el-form-item label="升级内容:">{{upgradeInfo.upgradeContent}}</el-form-item>
</template>
<el-form-item label="">
欢迎加群讨论QQ群号466363173欢迎提交需求欢迎使用和加入开发
</el-form-item>
</el-form>
</el-dialog>
</div>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
import userApi from './common/api/user'
import pageApi from './common/api/page'
export default {
data() {
return {
leftCollapse: true,
aboutDialogVisible: false,
rightContentLoading: false,
pathIndex: [],
defaultProps: {
children: 'children',
label: 'name'
},
// 空间搜索相关
spaceOptions: [],
spaceList:[],
choiceSpace: "",
nowSpaceShow: {},
newSpaceDialogVisible: false,
manageSpaceDialogVisible: false,
newSpaceForm: {id: '', name: '', spaceExplain: '', treeLazyLoad: 0, openDoc: 0, uuid: '', type: 1},
newSpaceFormRules: {
name: [
{required: true, message: '请输入空间名', trigger: 'blur'},
{min: 2, max: 25, message: '长度在 2 到 25 个字符', trigger: 'blur'}
],
},
nowClickPath: {
id: '',
path: '',
},
// 依据目录树存储的map全局对象
treePathDataMap: new Map(),
// 搜索的输入内容
searchKeywords: "",
// 页面展示相关
wikiPageList:[],
wikiPage: {},
wikiPageExpandedKeys: [],
// 升级信息
upgradeInfo: {},
}
},
computed: {
fullscreen () {
return this.$store.state.global.fullscreen;
}
},
mounted: function () {
this.loadSpaceList();
this.checkSystemUpgrade();
},
methods: {
loadPageList(param) {
param = param || {};
this.doGetPageList(param.parentId, param.node);
},
createWiki() {
if (this.nowClickPath.spaceId > 0) {
var param = {
spaceId: this.nowClickPath.spaceId,
parentId: this.nowClickPath.parentId, path: this.nowClickPath.path
};
this.$router.push({path: '/page/edit', query: param});
} else {
this.$message.warning("请先选择或创建空间");
}
},
changeWikiPageExpandedKeys(pageId) {
this.wikiPageExpandedKeys = [pageId];
},
searchByKeywords() {
this.$refs.wikiPageTree.filter(this.searchKeywords);
},
searchByKeywordsNewPage() {
var routeUrl = this.$router.resolve({path: '/page/search', query: {keywords: this.searchKeywords}});
window.open(routeUrl.href, '_blank');
},
handleNodeClick(data) {
console.log("点击节点:", data);
this.nowClickPath = {spaceId: this.nowClickPath.spaceId, pageId: data.id, parentId: data.id, path: data.path};
this.$router.push({path: '/page/show', query: this.nowClickPath});
},
handleNodeExpand(node) {
if (node.children.length > 0 && node.children[0].needLoad) {
console.log("加载节点:", node);
this.doGetPageList(node.id, node);
}
},
handlePageDrop(draggingNode, dropNode, dropType, ev) {
console.log('tree drop: ', draggingNode.data, dropNode.data, dropType);
// 'prev'、'inner'、'next'
// before、after、inner
var param = {id: draggingNode.data.id, parentId: dropNode.data.parentId};
if (dropType == 'inner') {
param.parentId = dropNode.data.id;
} else if (dropType == 'before') {
param.beforeSeq = dropNode.data.seqNo;
} else if (dropType == 'after') {
param.afterSeq = dropNode.data.seqNo;
}
pageApi.pageChangeParent(param).then(res => {
this.doGetPageList(null);
});
},
filterPageNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
editSpaceInfo(row) {
this.newSpaceForm = {
id: row.id, name: row.name, spaceExplain: row.spaceExplain,
treeLazyLoad: row.treeLazyLoad, openDoc: row.openDoc, type: row.type
};
this.newSpaceDialogVisible = true;
},
deleteSpaceInfo(row) {
this.$confirm('确定要删除此空间及下面的所有文档吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let param = {id: row.id, delFlag: 1};
pageApi.updateSpace(param).then(() => {
this.loadSpaceList();
});
});
},
spaceChangeEvents(data) {
if (data == 0) {
this.newSpaceForm = {id: '', name: '', spaceExplain: '', treeLazyLoad: 0, openDoc: 0, uuid: '', type: 1};
this.choiceSpace = this.nowClickPath.spaceId;
this.newSpaceDialogVisible = true;
} else if (data == -1) {
// 使得选择的空间展示不变
this.choiceSpace = this.nowClickPath.spaceId;
this.manageSpaceDialogVisible = true;
} else {
// 切换空间,重新初始化当前点击项,防止创建保存到之前点击的空间下去了
this.nowClickPath = {spaceId: data};
for (var i = 0; i < this.spaceList.length; i++) {
if (this.spaceList[i].id == data) {
this.nowSpaceShow = this.spaceList[i];
break;
}
}
this.doGetPageList(null);
this.$router.push({path: '/home', query: {spaceId: data}});
}
},
loadSpaceList() {
pageApi.spaceList({}).then(json => {
this.spaceList = json.data || [];
var spaceOptions = [];
for (var i = 0; i < this.spaceList.length; i++) {
spaceOptions.push({
label: this.spaceList[i].name, value: this.spaceList[i].id
});
}
this.spaceOptions = spaceOptions;
if (this.spaceList.length > 0) {
var spaceId = this.spaceList[0].id;
this.nowSpaceShow = this.spaceList[0];
this.nowClickPath = {spaceId: spaceId};
this.choiceSpace = spaceId;
this.doGetPageList(null);
// TODO 在首页时跳转
try {
if (this.$router.app._route.path == "/home") {
this.$router.push({path: '/home', query: {spaceId: spaceId}});
}
} catch (e) {
console.log(e);
}
}
});
},
doGetPageList(parentId, node) {
var nodePath = "";
if (!!node) {
nodePath = node.nodePath || "/";
if (!nodePath.endsWith("/")) {
nodePath += "/";
}
} else {
nodePath = "/";
}
var param = {spaceId: this.nowClickPath.spaceId, parentId: parentId || 0};
if (this.nowSpaceShow.treeLazyLoad == 0) {
param.parentId = null;
}
pageApi.pageList(param).then(json => {
var result = json.data || [];
var pathIndex = [];
if (this.nowSpaceShow.treeLazyLoad == 0) {
pathIndex = result;
} else {
for (var i = 0; i < result.length; i++) {
var item = result[i];
item.parentId = item.parentId || 0;
item.children = [{label: '', needLoad: true}];// 初始化一个对象,点击展开时重新查询加载
pathIndex.push(item);
}
}
this.createNodePath(pathIndex, nodePath);
if (parentId > 0) {
node.children = pathIndex;
} else {
this.wikiPageList = pathIndex;
//this.lastClickNode = {};
}
});
},
createNodePath(node, nodePath) {
if (!nodePath.endsWith("/")) {
nodePath += "/";
}
for (var i = 0; i < node.length; i++) {
var item = node[i];
item.nodePath = nodePath + item.name;
if (!!item.children && item.children.length > 0) {
this.createNodePath(item.children, item.nodePath);
}
}
},
userSettingDropdown(command) {
console.log("command:" + command);
if (command == 'userSignOut') {
this.userSignOut();
} else if (command == 'aboutDoc') {
this.aboutDialogVisible = true;
} else if (command == 'myInfo') {
this.$router.push({path: '/user/myInfo'});
} else if (command == 'console') {
window.location = this.apilist1.HOST;
} else {
this.$message.warning("暂未开放");
}
},
userSignOut() {
userApi.userLogout().then(() => {
location.reload();
});
},
onNewSpaceSubmit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
var param = {
id: this.newSpaceForm.id,
name: this.newSpaceForm.name,
type: this.newSpaceForm.type,
openDoc: this.newSpaceForm.openDoc,
spaceExplain: this.newSpaceForm.spaceExplain,
treeLazyLoad: this.newSpaceForm.treeLazyLoad,
};
pageApi.updateSpace(param).then(json => {
if (param.id > 0) {
this.loadSpaceList();
} else {
this.spaceList.push(json.data);
this.spaceOptions.push({
label: json.data.name, value: json.data.id
});
this.nowSpaceShow = json.data;
this.nowClickPath = {spaceId: json.data.id};
this.choiceSpace = json.data.id;
this.doGetPageList(null);
}
this.newSpaceForm = {id: '', name: '', spaceExplain: '', treeLazyLoad: 0, openDoc: 0, uuid: '', type: 1};
this.newSpaceDialogVisible = false;
});
}
});
},
onNewSpaceCancel() {
this.newSpaceDialogVisible = false;
},
checkSystemUpgrade() {
userApi.systemUpgradeInfo({}).then(json => {
if (!!json.data) {
this.upgradeInfo = json.data;
console.log("zyplayer-doc发现新版本"
+ "\n升级地址" + json.data.upgradeUrl
+ "\n当前版本" + json.data.nowVersion
+ "\n最新版本" + json.data.lastVersion
+ "\n升级内容" + json.data.upgradeContent
);
}
});
},
switchSpacePage(spaceId) {
spaceId = parseInt(spaceId);
if (this.choiceSpace == spaceId) {
return;
}
this.choiceSpace = spaceId;
this.nowClickPath.spaceId = spaceId;
this.doGetPageList(null);
},
}
}
export default {
name: 'app',
components: {},
data() {
return {};
},
methods: {}
}
</script>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
}
html, body {
margin: 0;
padding: 0;
height: 100%;
}
#app, .el-container, .el-menu {
height: 100%;
}
.el-header {background-color: #409EFF; color: #333; line-height: 40px; text-align: right;height: 40px !important;}
.icon-collapse{float: left;font-size: 25px;color: #aaa;margin-top: 8px;cursor: pointer;}
.icon-collapse:hover{color: #eee;}
#app, .el-container, .el-menu {
height: 100%;
}
</style>

View File

@@ -23,7 +23,7 @@ export default {
} else if (res.data.errCode == 400) {
vue.$message('请先登录');
var href = encodeURIComponent(window.location.href);
window.location = apimix.apilist1.HOST + "#/user/login?redirect=" + href;
window.location = process.env.VUE_APP_BASE_API + "#/user/login?redirect=" + href;
} else if (res.data.errCode == 402) {
vue.$router.push("/common/noAuth");
} else if (res.data.errCode !== 200) {

View File

@@ -0,0 +1,199 @@
<!--
-- zyplayer-doc 自研编辑器集各大厂商优秀的设计拷它的css拷它的图标借鉴它的内容处理造一个现代化好用的轮子
-- 带着情怀开写路途曲折希望能完美呈现
-- 参考飞书文档语雀文档wangEditor
-- @author 暮光城中城
-- @since 2020-06-06
-->
<template>
<div class="mg-editor-box">
<div ref="mgEditor" class="mg-editor" contenteditable="true"></div>
<div class="mg-editor-toolbar" :style="getMgEditorToolbarStyle(selectionInfo)">
<span class="iconfont icon-h1" @click.stop="handleToolbarHn('h1')"></span>
<span class="iconfont icon-h2" @click.stop="handleToolbarHn('h2')"></span>
<span class="iconfont icon-h3" @click.stop="handleToolbarHn('h3')"></span>
<span class="iconfont icon-hn"></span>
<span class="iconfont icon-border"></span>
<span class="iconfont icon-delete"></span>
<span class="iconfont icon-backcolor"></span>
<span class="iconfont icon-orderedlist"></span>
<span class="iconfont icon-unorderedlist"></span>
<span class="iconfont icon-checkbox"></span>
<span class="iconfont icon-link"></span>
<span class="iconfont icon-more"></span>
</div>
</div>
</template>
<script>
import "./css/MgEditor.css";
import "./css/MgEditorIconfont.css";
import utilPast from './util/past';
import utilBase from './util/util';
import toolbarHn from "./toolbar/hn";
import toolbarCommon from './toolbar/common';
import toolbarCodeList from "./toolbar/inlineCodeList";
const $ = require("jquery");
export default {
name: "mg-editor",
data() {
return {
editor: {},
selectionInfo: {
pageX: 0,
pageY: 0,
haveSelect: false,
},
compositionEnd: true,
};
},
mounted: function () {
this.editor = this.$refs.mgEditor;
this.appendEditorLastLine();
this.editor.addEventListener('mouseup', e => {
// 不延时还能获取到选中的文字(选择文字,单击选中文字的中间)
setTimeout(() => {
let selectText = window.getSelection().getRangeAt(0).toString();
this.selectionInfo = {
pageX: e.pageX,
pageY: e.pageY,
haveSelect: !!selectText,
};
console.log("mouseup", selectText, e);
}, 100);
});
document.body.addEventListener('click', e => {
this.selectionInfo.haveSelect = false;
});
// 粘贴事件
this.editor.addEventListener('paste', e => {
e.preventDefault();
let pasteText = utilPast.getPasteText(e);
let pasteHtml = utilPast.getPasteHtml(e, true, true);
document.execCommand('insertHTML', false, pasteText);
setTimeout(() => this.handleUserInput(e), 300);
});
this.editor.addEventListener('compositionstart', () => {
this.compositionEnd = false;
});
this.editor.addEventListener('compositionend', () => {
this.compositionEnd = true;
});
this.editor.addEventListener('click', e => {
this.handleUserInput(e);
});
this.editor.addEventListener('keyup', e => {
this.handleUserInput(e);
});
this.editor.addEventListener('keydown', e => {
if (e.which == 13) {
let nowContainerOrigin = window.getSelection().getRangeAt(0).commonAncestorContainer;
let nowContainer = this.findListCodeNode(nowContainerOrigin);
// 如果是回车而且是code行修改行号
if (nowContainer != null) {
e.preventDefault();
let newDomText = '';
let innerText = nowContainer.innerText;
if (!!innerText) {
let startOffset = window.getSelection().getRangeAt(0).startOffset;
nowContainer.innerText = innerText.substring(0, startOffset);
newDomText = innerText.substring(startOffset, innerText.length);
}
let nowNum = parseInt(nowContainer.getAttribute("start")) + 1;
let codeEle = $(`<code start='${nowNum}'>${newDomText}</code>`)[0];
$(nowContainer).after(codeEle);
utilBase.moveSelectionRange(codeEle, 0, 0);
// 改后面的序号
$(codeEle).nextAll().each(function () {
$(this).attr("start", ++nowNum);
});
}
let handleClassDom = toolbarCommon.getSelectionContainer(nowContainerOrigin);
// 回车时上一行是最后一行,取消标记,防止复制了
if (this.domHaveClass(handleClassDom, "locate")) {
handleClassDom.classList.remove("locate");
setTimeout(() => this.editor.lastChild.classList.add("locate"), 100);
}
// 在特定元素里面按回车如果任由系统则会复制当前行的class等内容所以自己处理下插入一行空白内容
if (toolbarCommon.domHaveClass(handleClassDom, 'head')) {
let domInnerText = handleClassDom.innerText;
let selectRange = window.getSelection().getRangeAt(0);
// 光标在行的最后才执行此操作,否则用系统默认的
if (selectRange.startOffset == selectRange.endOffset && selectRange.endOffset == domInnerText.length) {
e.preventDefault();
let newEle = $(`<div><br/></div>`)[0];
$(handleClassDom).after(newEle);
utilBase.moveSelectionRange(newEle, 0, 0);
this.selectionInfo.haveSelect = false;
}
}
}
});
},
methods: {
handleUserInput(e) {
if (!this.compositionEnd) return;
// 判断输入内容如果是markdown语法则处理成目标样式
let nowContainer = window.getSelection().getRangeAt(0).commonAncestorContainer;
let lineText = nowContainer.data;
if (lineText == '```') {
toolbarCodeList.createCodeListBlock();
}
// 判断是否是在最后一行输入,如果是就再在最后加一行
let locate = this.findParentClassDom(nowContainer, 'locate');
if (locate != null && !!locate.innerText && !!locate.innerText.trim()) {
locate.classList.remove("locate");
this.appendEditorLastLine();
} else if (!this.domHaveClass(this.editor.lastChild, "locate")) {
this.editor.lastChild.classList.add("locate");
}
},
findListCodeNode(container) {
// 如果自己是root创建一个div放最后
if (this.isRootBox(container)) {
return null;
}
let parentNode = container.parentNode;
if (this.domHaveClass(parentNode, "list-code")) {
return container;
}
return this.findListCodeNode(parentNode);
},
findParentClassDom(container, cls) {
if (!container || this.isRootBox(container)) return null;
if (this.domHaveClass(container, cls)) return container;
return this.findParentClassDom(container.parentNode, cls);
},
appendEditorLastLine() {
// 最后再加一行,防止光标没法到最后了
$(this.editor).append("<div class='locate'><br/></div>");
},
isRootBox(container) {
return this.domHaveClass(container, "mg-editor");
},
domHaveClass(container, cls) {
return container && container.classList && container.classList.contains(cls);
},
handleToolbarHn(hn) {
toolbarHn.toolbarHn(hn);
this.selectionInfo.haveSelect = false;
},
getMgEditorToolbarStyle(selectionInfo) {
let style = {};
style.display = selectionInfo.haveSelect ? 'block' : 'none';
if (selectionInfo.haveSelect) {
let top = selectionInfo.pageY - 20 - 50;
if (top < 0) top = selectionInfo.pageY + 20;
let left = selectionInfo.pageX - 220;
if (left < 10) left = 10;
style.top = top + 'px';
style.left = left + 'px';
}
return style;
}
}
}
</script>

View File

@@ -0,0 +1,147 @@
.mg-editor {
padding: 10px;
height: 500px;
width: 100%;
font-size: 16px;
box-sizing: border-box;
}
.mg-editor:focus {
outline: none;
}
.mg-editor:empty::before {
content: attr(placeholder);
font-size: 14px;
color: #ccc;
line-height: 20px;
padding-top: 10px;
}
.mg-editor-toolbar {
position: fixed;
top: 0;
left: 0;
display: none;
z-index: 1;
padding: 5px 6px;
background-color: #fff;
border-radius: 4px;
border: 1px solid #dee0e3;
user-select: none;
box-shadow: 0 6px 24px 0 rgba(31, 35, 41, .1);
}
.mg-editor-toolbar .iconfont {
padding: 5px;
font-size: 24px;
cursor: pointer;
}
.mg-editor .list-code {
font-size: 14px !important;
line-height: 1.68 !important
}
.mg-editor .list-code code {
border: 1px solid #dee0e3;
display: block;
background-color: #f5f6f7;
line-height: 1.5;
padding: 6px 8px 3px 45px;
font-size: 14px;
word-break: break-word;
margin: 5px 0;
}
.mg-editor .list-code code .list-code-span {
padding-top: 0 !important;
padding-bottom: 0 !important;
line-height: 1.5;
}
.mg-editor .list-code code:before {
content: attr(start);
display: inline-block;
font-size: 14px;
text-align: right;
direction: rtl;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
overflow: visible;
white-space: nowrap;
width: 0;
margin-left: -11px;
padding-right: 11px;
font-weight: 400;
background-color: transparent;
line-height: 1.5;
color: #8f959e;
vertical-align: bottom;
}
.mg-editor .list-code code + code {
border-top: none;
margin-top: -9px;
}
.mg-editor .ace-editor .list-code code:before {
left: 4px;
}
/*Hn样式-S-*/
/*我很懒,样式都是抄的*/
.mg-editor .head-h1, .mg-editor .head-h2, .mg-editor .head-h3, .mg-editor .head-h4, .mg-editor .head-h5, .mg-editor .head-h6, .mg-editor .head-h7, .mg-editor .head-h8, .mg-editor .head-h9 {
font-weight: 600;
letter-spacing: .02em;
line-height: 1.65
}
.mg-editor .head-h1 {
font-size: 26px;
margin-bottom: 10px
}
.mg-editor .head-h1 .collapsable-section-handle {
font-size: 26px
}
.mg-editor .head-h2 {
font-size: 22px;
margin-bottom: 8px
}
.mg-editor .head-h2 .collapsable-section-handle {
font-size: 22px
}
.mg-editor .head-h3 {
font-size: 20px;
margin-bottom: 8px
}
.mg-editor .head-h3 .collapsable-section-handle {
font-size: 20px
}
.mg-editor .head-h4 {
font-size: 18px;
margin-bottom: 8px
}
.mg-editor .head-h4 .collapsable-section-handle {
font-size: 18px
}
.mg-editor .head-h5,.mg-editor .head-h6,.mg-editor .head-h7,.mg-editor .head-h8,.mg-editor .head-h9 {
font-size: 16px;
margin-bottom: 6px
}
.mg-editor .head-h5 .collapsable-section-handle,.mg-editor .head-h6 .collapsable-section-handle,.mg-editor .head-h7 .collapsable-section-handle,.mg-editor .head-h8 .collapsable-section-handle,.mg-editor .head-h9 .collapsable-section-handle {
font-size: 16px
}
/*Hn样式-E-*/

View File

@@ -0,0 +1,117 @@
@font-face {
font-family: "iconfont";
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAzgAAsAAAAAHHgAAAyQAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCFVgqnIJ81ATYCJANsCzgABCAFhG0HghUbihcz0s3RqnZk/9UBbxjPvImD1hEhmkgkcf1tFr58DJK1y3Mn5uYUPc7dBMPgkn3/52pKMQiOfR8PPJf7d7vBk8M4oDYlFCVwL1rTIpiigR51PM3Zz9pbiygWQxZJ0JKlmoNgUk8qGiqBmgaqyxl2dx8LUkWqocL9ijqcuBMAg3Nl8uN248ADoBoq1HREr0aSsJFLLD1i+8xqxHXyQWB4LfKg72vTFCg0zB6mUDlvT/bSbvg2n646hf5v7X919osKMHj7ISGJZqES+s763L3PZHCVikczaRoKVA6NHxErmVwJKQPx9NtywAricC1iS3hm/HmXJwCqzBJgg5slLmCQIggYYvmgrBSYnA9pSQ8wTqQ5bQywM6Aw4YesuwBO6sMHf0gNGAAhKgLRFc29ufPA+SnxqYmQ9osZhAQkOYHumUAE9IAYBEdtyzUQS95TNqfrnzQDQMqBMHjJfmoaHJQEHh/HaWvojiOQ/GceQmOwODwZOQUlFTUNLR29IMFChAoDQQqiweuAYEKvDihVWpcAlLouISgNsKEFgA4AegAYAGACgBkAFjyqYAX4YQOAHQAOADgB4AKAGwDcAeABAE8AeAHAGwA+APAFgB8A/OFoAetp6QMzgPQLIFuBKOknHGHsIFJOZpriKNQ0Gq1wWPjYurFCsOAQ4oRwQkgVTJygE4wsWx7DalkuP4nj4PELinCFThHVdJrUmMyqQjMmOy2MRoE7CNKrUsXpcowZmxcKkIW0OK4hcTxr6aC0gTXQOSjGaLjNwCp3YSy7dAq4s84s2/W/QIoL8P5mmpna/sD8FgE1Z1hmGzuCA63oANsv3uy3BQpNYTSF1bjwmHtMinlwr8e1WsbQNmJrr3dYu/sfdIS0lQ9L8/ly7QH/8M5lmoiTPcSWVSfREIC7bsRObrJlvEugo1SYU/PaKcbVD7j40+V7j3406cqGdbx2q7R9wI79YhtWTZWPIJndNS5jBCx2wHRPsaQ+CFxjh1VXP5ZvaPOf5b85/Grgv6XK5118wtkU2yzXbaZ9KdZGVUsMplyb5qdxrlXf6t7V8e5sJ7sNrToXhlb42tpUHrusSQes+uPmY85txdBiCfS6J3SUfyA52z8K/SF9kbrb9XWdRXfuOqy6xo6evI7p7fUWvuFMimm29fzas2dPHbfSwNMcgQPRKAf8849lx+9pixvarU+zqbV73CE/o9dPq7/tn8xR6HqLJrqDPA69/hqloOyha01P+a/8c2qfzf7718X/JwUKtaHn2xRub8eb9sBAVM+l526/QpFJ09tMxO3aM4Ssw6w2gCkZhIQoLgpj4fpVIOkkUliB9pMLlbpjfFisFBWGaYvT5PzbG0a0CwuF1UofVFzyjKkH0tLQmAC/tRUAo4/AV7WFHpG5FcGt655OvwsKr+SCLrdJTf86FxWK4uoNz+oprwnoX5Xnu8dzDraPlZ/c4+Ye2upVtcrwE7EaPwroWkiolda14eDkNvH42K9nT+NcF5+O+d6fHQ7tyAns+/doIH7y1sG5tcrA8b3+dfiw/yw6wNMEV9iW3HDv/8euQ7T3za5WxG+GfnOmO5GPNNNh28CPQHrm9gf0xPYAuIOt//dP+pAdIMErBYCCU3BHf1r5mvnRyXreoPzBt1vOi5BOJqD056MU9Q2rvrFSIKxElWIcaEeR7ydf8HMVyq/xkfY/dXni2yVT59+cpf9z9a19Ef4E9UbbUHv6R9HY3YzKjnNGof3OKMrwj1xuv0TVLfyOKN+sAQWu8TuodUOqk4I8YJBq+0tF+xVKbEdi0mi6yvwacT73FNz6G+cDNeLQEnEhxtzKPQg66q0PXeed4EXNOIBCryoeSM/Q2GidOLkQR2M+7WNiWHfxnkMGbMdNG+7rXusPqAZFpWIYXa1k2jNWBi0HpH1oOKqgtf02K2KpMnQ2+KlRVjKq/tC6dFqz3lOtp6g0hCzvf7dMn/pdcsySxTXL++DrgPVHVD0ualy1lDUMxoVUo4HpQyZQdnpithBB2lAZ5RDmsWrx/4NDuc7aLn8oFvKuk979YD8RQaUsJ5dlx51OPRXEUV4UmX4pUChkC+rQfbBqLC5SyUiH2389M0YvOpyJ1Krn5aYdnOCkRejktPijhyHRH2MnD06KqhT0WMAgu+lTWnr4ToHyUQpqrbOp/svf+M7cx0VJeMzDriFb5FHDXkWfAhFXt32dRgtU2cjF7pcmNfQg8NxYffKI/Er3kJS+v9VOfCiyIi+RvzZZa4k61hl+4vPlSQc2vPJMGL8XeSnrFdkCEio5JfuHIfhcuXwB7ch5NcGs4tnzfxQaxOcKVuQXpTijTKoczcy8Fen4BePHWUQalfFRpFxlYd+/8VzfI8pLJm5Ij5FS6EX6Smuzc+2Wfx5M1z3S4B8mrFF6e2m5QtmZ3GCx0eLSu5u6w1K+S+rXf534bfw7ie/o/ZrdSceFVvme2b9HeT0zq3Ti1NivEj/wgnwOFkwnUVnExD06U/Lfwed+LzKIn/z4r2ctaY8PGsiMn4b9kqEBLgBj04Tp2pmRxUnlxtVT8/q+Zs+yNu609FXGxf6LF8/KZGdLoPNiZ9jJbfZtJ6H56ZRE9Uzdv+NMr5flDobKc6WBcDtK9tc6CkcWjrLY3mgZz9TSYXS7R768Rr1DtcyEKziHuXyzyyB3bkIgaa5hqHSje2Lo0O/uLhifA+McOoKGsrPzh89NH7pXU128V5v78f30l4tjMs/bto1ZEb7SfwEmAUiQvdjP3zNbgriRad55+VPl7IxHjpe/tKIwpB3a5uFOaW+yZCfHU/MpnN+vcN0cyNaD1hm2HKX9FTK7sJN73wn1gH1ULNfXk2GcGax7N38Yz6rYhoUNrH7MeDtFoA64ZRpnHkejxEprJx9LeZ3J2v3wfW2mBnYb26APjrzqvSGrSBM9fk04scBkT0De9UFs8O+he4e+j3esuKO4vYPvHDW4OX1T43aHoVFlSBc9wqCr8mGA+VSgwl9l9EfHm1kaKC+/eNH98svLllZUBOYvpbioqzuVB9WnSbS6UFmwceqy/2+STx0l2/TSpAWho54Nz/sfXfxxCb1j2rKfl8PsF6nmpN81PpczbVnnGLNb7hQt4+QTu9wLrPGf+uUV04emxkkqtvuBpdOckz3DpuzxgEX5NOYuowQ8cAU9ATaw2TKzDQsLO94qGjMrF4vl7LhFb6Eaj9nTTr8FixVVpRgyArxvPSWHM3tqWtLUolptVnMC+LDFztTQ4ahmuZ3uUP231/0i0jWo0IEWUTtWBcQTb6ZZaplZEXwa6HRHv4p0Lb4OWq2DatEvyCnjpwVvfe6lYWzkhjcTvbYaiXdTK8kxyLOE9l3MbEAYkZvjfJg93Jno0HjpEtQfuFwL9r94Zm4zE2Lhzj2el4rKXljoKNzriuDTnWcxwz+OaEHoy+GFQZOp63B7n8jbc3MU4vGHoky88HC5v9xseU7V4S2HRVlOJI9annhor5TDm8gR5fbxup89lVRJA1VN/4zqUMn/UBssCnwqzhiCfHRItG2jY9XaEZus0SHFG4hKSbpXEF9hrOhuZE1cV3O5oSJ+yvohUQnrBxdhuLelp4sz8K83TzZW2qbcS0r5/Ggih7NuYl7e+7VhIyuElA8Nmwf3PwBA1OC9Kjo7uqphpNmo0iXj72ca8Cst4/HLZE/Cbzt60cg049cWsAhDpt7WjAS8ekH417Sdiz+po/32OhyIv+Wfmq10/Mrw/46t99Y/WbzykeDfiwEcoKv90tI8pyEnRuQ3kKO/P1X3XNUArYC64wDAxW0BBsO2Gnjv+Bx6byXjTE7rUhxoMFECGBAkMjqBUsCDSBEwkEE5iIf8M/OghZVAAEayAGCDl1oUAxUcpjgo4AIlQAX3JDL6XUpBEHxLEagwgnIwCdOeJw8xmDSNrzG0wClzUclS55OgjBdDfiEcquZNQ5X9gYfURa0Xq2KfPTqwxAmGY9iIeOWZmtq1rgy1kuqZMqwsoki/XS697gYWltpofI2hBU6ZS1YxWer8imVcev9fCIeq2TLBccU/8JB2XK0XKw9pb3deEzyW4Ydj2EggvEpHZ2pqFwjUuCepXn+oDCuLWKN7v10GM/K+9mL/jvZ6bALV6HliDeAoNAaLwxOIJDKFSqOjRoMWHXqCCCaEUMIwYBQybhKyJs+kIy/klbyRd/Ix7CeaQ+zA9+mpKgRPRttiqRJPqvYNV9Mgs0OX28+jjbDF0Pm2dYCu3DZiPB06ExudAhNdU+gsOgE/9S+gwsvYVLLl/0CC+yRVPjvWW5z2iDJOXa1tLDnMV8pevCMAAAA=') format('woff2'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-h2:before {
content: "\e503";
}
.icon-h1:before {
content: "\e504";
}
.icon-h3:before {
content: "\e505";
}
.icon-hn:before {
content: "\e506";
}
.icon-h4:before {
content: "\e507";
}
.icon-h5:before {
content: "\e508";
}
.icon-h6:before {
content: "\e509";
}
.icon-h7:before {
content: "\e50b";
}
.icon-h8:before {
content: "\e50c";
}
.icon-h9:before {
content: "\e50d";
}
.icon-border:before {
content: "\e50e";
}
.icon-delete:before {
content: "\e50f";
}
.icon-backcolor:before {
content: "\e510";
}
.icon-orderedlist:before {
content: "\e511";
}
.icon-unorderedlist:before {
content: "\e512";
}
.icon-checkbox:before {
content: "\e513";
}
.icon-link:before {
content: "\e514";
}
.icon-more:before {
content: "\e515";
}
.icon-underline:before {
content: "\e516";
}
.icon-aligncenter:before {
content: "\e517";
}
.icon-alignleft:before {
content: "\e518";
}
.icon-blockquote:before {
content: "\e519";
}
.icon-italic:before {
content: "\e51a";
}
.icon-alignright:before {
content: "\e51b";
}
.icon-inlinecode:before {
content: "\e51c";
}
.icon-inlinecodelist:before {
content: "\e51d";
}

View File

@@ -0,0 +1,19 @@
export default {
getSelectionContainer(judgeRoot) {
let container = window.getSelection().getRangeAt(0).commonAncestorContainer;
if (container.nodeType != 1) {
container = container.parentNode;
}
if (container.nodeType != 1 || (judgeRoot && this.isRootBox(container))) {
return null;
}
return container;
},
isRootBox(container) {
return this.domHaveClass(container, "mg-editor");
},
domHaveClass(container, cls) {
return container && container.classList && container.classList.contains(cls);
},
}

View File

@@ -0,0 +1,20 @@
import toolbarCommon from './common';
const $ = require("jquery");
export default {
toolbarHn(hn) {
// 找到内容生成div把每一行包住
let container = toolbarCommon.getSelectionContainer(true);
if (container == null) return;
let innerTextArr = container.innerText.split("\n");
if (innerTextArr.length >= 1) {
innerTextArr.filter(item => !!item).forEach(item => {
$(container).before(`<div class="head head-${hn}">${item}</div>`);
});
}
// 最后把当前行移出掉
container.remove();
}
}

View File

@@ -0,0 +1,36 @@
import toolbarCommon from './common';
import utilBase from '../util/util';
const $ = require("jquery");
export default {
createCodeListBlock() {
let containerNow = window.getSelection().getRangeAt(0).commonAncestorContainer;
let divEle = this.findRootDomOnNotCode(containerNow);
if (divEle == null) return;
divEle.classList.add("list-code");
let codeEle = $(`<code start='1'></code>`)[0];
$(divEle).append(codeEle);
// 光标放到代码框里
utilBase.moveSelectionRange(codeEle, 0, 0);
},
findRootDomOnNotCode(container) {
container = container.nodeType == 3 ? container.parentNode : container;
// 如果自己是root创建一个div放最后
if (toolbarCommon.isRootBox(container)) {
let lastEle = document.createElement("div");
container.appendChild(lastEle);
return lastEle;
}
// 如果父元素是root添加一个新的div再把自己移除
let parentNode = container.parentNode;
if (toolbarCommon.isRootBox(parentNode)) {
let lastEle = document.createElement("div");
parentNode.insertBefore(lastEle, container);
container.remove();
return lastEle;
}
// 在非根结点里面输入```无效
return null;
},
}

View File

@@ -0,0 +1,258 @@
<template>
<div class="mg-editor-box">
<div ref="mgEditor" class="mg-editor" contenteditable="true"></div>
<div class="mg-editor-toolbar" :style="getMgEditorToolbarStyle(selectionInfo)">
<span class="iconfont icon-h1" @click.stop="toolbarHn('h1')"></span>
<span class="iconfont icon-h2" @click.stop="toolbarHn('h2')"></span>
<span class="iconfont icon-h3" @click.stop="toolbarHn('h3')"></span>
<span class="iconfont icon-hn"></span>
<span class="iconfont icon-border"></span>
<span class="iconfont icon-delete"></span>
<span class="iconfont icon-backcolor"></span>
<span class="iconfont icon-orderedlist"></span>
<span class="iconfont icon-unorderedlist"></span>
<span class="iconfont icon-checkbox"></span>
<span class="iconfont icon-link"></span>
<span class="iconfont icon-more"></span>
</div>
</div>
</template>
<script>
import "./MgEditor.css";
import "./MgEditorIconfont.css";
const $ = require("jquery")(window);
export default {
name: "mg-editor",
data() {
return {
editor: {},
selectionInfo: {
pageX: 0,
pageY: 0,
haveSelect: false,
},
compositionEnd: true,
};
},
mounted: function () {
this.editor = this.$refs.mgEditor;
this.appendEditorLastLine();
this.editor.addEventListener('mouseup', e => {
// 不延时还能获取到选中的文字(选择文字,单击选中文字的中间)
setTimeout(() => {
let selectText = window.getSelection().getRangeAt(0).toString();
this.selectionInfo = {
pageX: e.pageX,
pageY: e.pageY,
haveSelect: !!selectText,
};
console.log("mouseup", selectText, e);
}, 100);
});
document.body.addEventListener('click', e => {
this.selectionInfo.haveSelect = false;
});
// 粘贴事件
this.editor.addEventListener('paste', e => {
let cbd = e.clipboardData;
if (!(e.clipboardData && e.clipboardData.items)) {
return;
}
debugger
for (var i = 0; i < cbd.items.length; i++) {
var item = cbd.items[i];
}
setTimeout(() => this.handleUserInput(e), 300);
});
this.editor.addEventListener('compositionstart', () => {
this.compositionEnd = false;
});
this.editor.addEventListener('compositionend', () => {
this.compositionEnd = true;
});
this.editor.addEventListener('click', e => {
this.handleUserInput(e);
});
this.editor.addEventListener('keyup', e => {
this.handleUserInput(e);
});
this.editor.addEventListener('keydown', e => {
if (e.which == 13) {
let nowContainerOrigin = window.getSelection().getRangeAt(0).commonAncestorContainer;
let nowContainer = this.findListCodeNode(nowContainerOrigin);
// 如果是回车而且是code行修改行号
if (nowContainer != null) {
e.preventDefault();
let parentNode = nowContainer.parentNode;
let nextSibling = nowContainer.nextSibling;
// 在最后加一行
let nowNum = parseInt(nowContainer.getAttribute("start")) + 1;
// let codeEle = document.createElement("code");
// codeEle.setAttribute("start", nowNum);
// if (parentNode.lastChild == nowContainer) {
// parentNode.appendChild(codeEle);
// } else {
// parentNode.insertBefore(codeEle, nextSibling);
// }
// 修改内容
let innerText = nowContainer.innerText;
if (!!innerText) {
let startOffset = window.getSelection().getRangeAt(0).startOffset;
nowContainer.innerText = innerText.substring(0, startOffset);
codeEle.innerText = innerText.substring(startOffset, innerText.length);
}
$(nowContainer).after("<code start='" + nowNum + "'></code>");
// 光标下移一行
let rangeNew = document.createRange();
rangeNew.setStart(codeEle, 0);
rangeNew.setEnd(codeEle, 0);
window.getSelection().removeAllRanges();
window.getSelection().addRange(rangeNew);
// 改后面的序号
for (; nextSibling; nextSibling = nextSibling.nextSibling) {
if (nextSibling.nodeType === 1) {
nextSibling.setAttribute("start", ++nowNum);
}
}
}
// 回车时上一行是最后一行,取消标记
if (this.domHaveClass(nowContainerOrigin, "locate")) {
nowContainerOrigin.classList.remove("locate");
setTimeout(() => this.editor.lastChild.classList.add("locate"), 100);
}
}
});
},
methods: {
handleUserInput(e) {
if (!this.compositionEnd) return;
// 判断输入内容如果是markdown语法则处理成目标样式
let nowContainer = window.getSelection().getRangeAt(0).commonAncestorContainer;
let parentNode = nowContainer.parentNode;
let lineText = nowContainer.data;
if (lineText == '```') {
let containerNow = window.getSelection().getRangeAt(0).commonAncestorContainer;
let divEle = this.findRootDomOnNotCode(containerNow);
if (divEle == null) {
return;
}
divEle.classList.add("list-code");
let codeEle = document.createElement("code");
codeEle.setAttribute("start", "1");
divEle.appendChild(codeEle);
// 光标放到代码框里
let rangeNew = document.createRange();
rangeNew.setStart(codeEle, 0);
rangeNew.setEnd(codeEle, 0);
window.getSelection().removeAllRanges();
window.getSelection().addRange(rangeNew);
}
// 判断是否是在最后一行输入,如果是就再在最后加一行
let locate = this.findParentClassDom(nowContainer, 'locate');
if (locate != null && !!locate.innerText && !!locate.innerText.trim()) {
locate.classList.remove("locate");
this.appendEditorLastLine();
} else if (!this.domHaveClass(this.editor.lastChild, "locate")) {
this.editor.lastChild.classList.add("locate");
}
},
findRootDomOnNotCode(container) {
// 如果自己是root创建一个div放最后
if (this.isRootBox(container)) {
let lastEle = document.createElement("div");
this.editor.appendChild(lastEle);
return lastEle;
}
// 如果父元素是root添加一个新的div再把自己移除
let parentNode = container.parentNode;
if (this.isRootBox(parentNode)) {
let lastEle = document.createElement("div");
parentNode.insertBefore(lastEle, container);
container.remove();
return lastEle;
}
// 在code里面输入```无效
if (this.domHaveClass(parentNode, "list-code")) {
return null;
}
return this.findRootDomOnNotCode(parentNode);
},
findListCodeNode(container) {
// 如果自己是root创建一个div放最后
if (this.isRootBox(container)) {
return null;
}
let parentNode = container.parentNode;
if (this.domHaveClass(parentNode, "list-code")) {
return container;
}
return this.findListCodeNode(parentNode);
},
findParentClassDom(container, cls) {
if (!container || this.isRootBox(container)) return null;
if (this.domHaveClass(container, cls)) return container;
return this.findParentClassDom(container.parentNode, cls);
},
appendEditorLastLine() {
// 最后再加一行,防止光标没法到最后了
let lastEle = document.createElement("div");
lastEle.classList.add("locate");
lastEle.appendChild(document.createElement("br"));
this.editor.appendChild(lastEle);
},
isRootBox(container) {
return this.domHaveClass(container, "mg-editor");
},
domHaveClass(container, cls) {
return container && container.classList && container.classList.contains(cls);
},
getSelectionContainer(judgeRoot) {
let container = window.getSelection().getRangeAt(0).commonAncestorContainer;
if (container.nodeType != 1) {
container = container.parentNode;
}
if (container.nodeType != 1 || (judgeRoot && this.isRootBox(container))) {
return null;
}
return container;
},
toolbarHn(hn) {
let container = this.getSelectionContainer();
if (container == null) return;
document.execCommand("formatBlock", false, "<" + hn + ">");
container = this.getSelectionContainer(true);
if (container == null) {
return;
}
let innerTextArr = container.innerText.split("\n");
if (innerTextArr.length > 1) {
innerTextArr.forEach(item => {
if (!!item) {
let newEle = document.createElement(hn);
newEle.innerText = item;
container.parentNode.insertBefore(newEle, container);
}
});
container.remove();
}
},
getMgEditorToolbarStyle(selectionInfo) {
let style = {};
style.display = selectionInfo.haveSelect ? 'block' : 'none';
if (selectionInfo.haveSelect) {
let top = selectionInfo.pageY - 20 - 50;
if (top < 0) top = selectionInfo.pageY + 20;
let left = selectionInfo.pageX - 220;
if (left < 10) left = 10;
style.top = top + 'px';
style.left = left + 'px';
}
return style;
}
}
}
</script>

View File

@@ -0,0 +1,87 @@
/**
* 本文件内容拷贝自https://github.com/wangfupeng1988/wangEditor
* zyplayer-doc在此基础上有稍作修改
*/
import utilBase from './util'
export default {
// 获取粘贴的纯文本
getPasteText(e) {
const clipboardData = e.clipboardData || (e.originalEvent && e.originalEvent.clipboardData);
let pasteText;
if (clipboardData == null) {
pasteText = window.clipboardData && window.clipboardData.getData('text');
} else {
pasteText = clipboardData.getData('text/plain');
}
let resultStr = '';
let htmlStr = utilBase.replaceHtmlSymbol(pasteText);
htmlStr.split('<br/>').forEach(item => {
if (resultStr) resultStr += '<br/>';
resultStr += item.trim();
});
return resultStr;
},
// 获取粘贴的html
getPasteHtml(e, filterStyle, ignoreImg) {
const clipboardData = e.clipboardData || (e.originalEvent && e.originalEvent.clipboardData);
let pasteText, pasteHtml;
if (clipboardData == null) {
pasteText = window.clipboardData && window.clipboardData.getData('text');
} else {
pasteText = clipboardData.getData('text/plain');
pasteHtml = clipboardData.getData('text/html');
}
if (!pasteHtml && pasteText) {
pasteHtml = utilBase.replaceHtmlSymbol(pasteText);
}
if (!pasteHtml) {
return;
}
// 过滤word中状态过来的无用字符
const docSplitHtml = pasteHtml.split('</html>');
if (docSplitHtml.length === 2) {
pasteHtml = docSplitHtml[0];
}
// 过滤无用标签
pasteHtml = pasteHtml.replace(/<(meta|script|link).+?>/igm, '');
// 去掉注释
pasteHtml = pasteHtml.replace(/<!--.*?-->/mg, '');
// 过滤 data-xxx 属性
pasteHtml = pasteHtml.replace(/\s?data-.+?=('|").+?('|")/igm, '');
if (ignoreImg) {
// 忽略图片
pasteHtml = pasteHtml.replace(/<img.+?>/igm, '');
}
if (filterStyle) {
// 过滤样式
pasteHtml = pasteHtml.replace(/\s?(class|style)=('|").*?('|")/igm, '');
} else {
// 保留样式
pasteHtml = pasteHtml.replace(/\s?class=('|").*?('|")/igm, '');
}
return pasteHtml
},
// 获取粘贴的图片文件
getPasteImgs(e) {
const result = [];
const txt = utilBase.getPasteText(e);
if (txt) {
// 有文字,就忽略图片
return result;
}
const clipboardData = e.clipboardData || (e.originalEvent && e.originalEvent.clipboardData) || {};
const items = clipboardData.items;
if (!items) {
return result;
}
utilBase.objForEach(items, (key, value) => {
const type = value.type;
if (/image/i.test(type)) {
result.push(value.getAsFile());
}
});
return result;
},
}

View File

@@ -0,0 +1,72 @@
/**
* 本文件内容拷贝自https://github.com/wangfupeng1988/wangEditor
* zyplayer-doc在此基础上有稍作修改
*/
// 和 UA 相关的属性
export default {
_ua: navigator.userAgent,
// 是否 webkit
isWebkit: function () {
const reg = /webkit/i;
return reg.test(this._ua);
},
// 是否 IE
isIE: function () {
return 'ActiveXObject' in window;
},
// 遍历对象
objForEach(obj, fn) {
let key, result;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
result = fn.call(obj, key, obj[key]);
if (result === false) {
break;
}
}
}
},
// 遍历类数组
arrForEach(fakeArr, fn) {
let i, item, result;
const length = fakeArr.length || 0;
for (i = 0; i < length; i++) {
item = fakeArr[i];
result = fn.call(fakeArr, item, i);
if (result === false) {
break;
}
}
},
// 获取随机数
getRandom(prefix) {
return prefix + Math.random().toString().slice(2);
},
// 替换 html 特殊字符
replaceHtmlSymbol(html) {
if (!html) return '';
return html.replace(/</gm, '&lt;')
.replace(/>/gm, '&gt;')
.replace(/"/gm, '&quot;')
.replace(/(\r\n|\r|\n)/g, '<br/>');
},
// 返回百分比的格式
percentFormat(number) {
number = (parseInt(number * 100));
return number + '%';
},
// 判断是不是 function
isFunction(fn) {
return typeof fn === 'function';
},
moveSelectionRange(dom, start, end) {
let rangeNew = document.createRange();
rangeNew.setStart(dom, start);
rangeNew.setEnd(dom, end);
window.getSelection().removeAllRanges();
window.getSelection().addRange(rangeNew);
},
}

View File

@@ -0,0 +1,461 @@
<template>
<div>
<el-container>
<el-aside v-show="leftCollapse">
<div style="padding: 10px;height: 100%;box-sizing: border-box;background: #fafafa;">
<div style="margin-bottom: 10px;">
<el-select v-model="choiceSpace" @change="spaceChangeEvents" filterable placeholder="选择空间" style="width: 100%;">
<el-option-group label="">
<el-option key="0" label="创建空间" value="0"></el-option>
<el-option key="-1" label="空间管理" value="-1"></el-option>
</el-option-group>
<el-option-group label="">
<el-option v-for="item in spaceOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-option-group>
</el-select>
</div>
<div align="center">
<el-button v-on:click="createWiki" icon="el-icon-plus" style="width: 100%;">创建文档</el-button>
</div>
<el-input v-model="searchKeywords" @input="searchByKeywords" @keyup.enter.native="searchByKeywords" placeholder="搜索文档" style="margin: 10px 0;">
<el-button slot="append" icon="el-icon-search" v-on:click="searchByKeywordsNewPage"></el-button>
</el-input>
<el-tree :props="defaultProps" :data="wikiPageList" @node-click="handleNodeClick"
@node-expand="handleNodeExpand" draggable @node-drop="handlePageDrop"
ref="wikiPageTree" :filter-node-method="filterPageNode" highlight-current
:expand-on-click-node="false" :default-expanded-keys="wikiPageExpandedKeys"
node-key="id"
style="background-color: #fafafa;">
</el-tree>
</div>
</el-aside>
<el-container>
<el-header>
<!--<el-switch v-model="isCollapse" ></el-switch>-->
<i class="el-icon-menu icon-collapse" @click="leftCollapse = !leftCollapse"></i>
<!--<div class="logo" @click="aboutDialogVisible = true">zyplayer-doc-wiki</div>-->
<el-dropdown @command="userSettingDropdown" trigger="click">
<i class="el-icon-setting" style="margin-right: 15px; font-size: 16px;cursor: pointer;color: #fff;"> </i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="console">控制台</el-dropdown-item>
<el-dropdown-item command="aboutDoc" divided>关于</el-dropdown-item>
<el-dropdown-item command="myInfo">我的资料</el-dropdown-item>
<el-dropdown-item command="userSignOut">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main style="padding: 0;">
<router-view @loadPageList="loadPageList"
@changeExpandedKeys="changeWikiPageExpandedKeys"
@switchSpace="switchSpacePage">
</router-view>
</el-main>
</el-container>
</el-container>
<!--新建空间弹窗-->
<el-dialog title="创建空间" :visible.sync="newSpaceDialogVisible" width="600px" :close-on-click-modal="false">
<el-form label-width="100px" :model="newSpaceForm" :rules="newSpaceFormRules" ref="newSpaceForm">
<el-form-item label="空间名:" prop="name">
<el-input v-model="newSpaceForm.name"></el-input>
</el-form-item>
<el-form-item label="空间描述:" prop="spaceExplain">
<el-input v-model="newSpaceForm.spaceExplain"></el-input>
</el-form-item>
<el-form-item label="空间开放:">
<el-switch v-model="newSpaceForm.openDoc" inactive-text="需要登录" :inactive-value="0" active-text="开放访问" :active-value="1"></el-switch>
</el-form-item>
<el-form-item label="目录加载:">
<el-switch v-model="newSpaceForm.treeLazyLoad" inactive-text="预先加载" :inactive-value="0" active-text="延迟加载" :active-value="1"></el-switch>
</el-form-item>
<el-form-item label="空间类型:">
<el-select v-model="newSpaceForm.type" filterable placeholder="选择类型" style="width: 100%;">
<el-option :key="1" label="公共空间" :value="1">
<span style="float: left">公共空间</span>
<span style="float: right; color: #8492a6; font-size: 13px;">属于公共登录用户可访问编辑</span>
</el-option>
<el-option :key="2" label="个人空间" :value="2">
<span style="float: left">个人空间</span>
<span style="float: right; color: #8492a6; font-size: 13px;">属于个人所有登录用户可访问</span>
</el-option>
<el-option :key="3" label="隐私空间" :value="3">
<span style="float: left">隐私空间</span>
<span style="float: right; color: #8492a6; font-size: 13px;">属于个人仅创建者可访问</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" v-if="newSpaceForm.id > 0" @click="onNewSpaceSubmit('newSpaceForm')">保存修改</el-button>
<el-button type="primary" v-else @click="onNewSpaceSubmit('newSpaceForm')">立即创建</el-button>
<el-button @click="onNewSpaceCancel">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
<!--管理空间弹窗-->
<el-dialog title="管理空间" :visible.sync="manageSpaceDialogVisible" :close-on-click-modal="false" width="80%">
<el-table :data="spaceList" border style="width: 100%; margin-bottom: 5px;" max-height="500">
<el-table-column prop="id" label="ID" width="60"></el-table-column>
<el-table-column prop="name" label="名字"></el-table-column>
<el-table-column prop="spaceExplain" label="说明"></el-table-column>
<el-table-column label="开放地址">
<template slot-scope="scope">
<a target="_blank" :href="'open-wiki.html?space='+scope.row.uuid" v-if="scope.row.openDoc == 1">{{scope.row.name}}</a>
<span v-else>暂未开放</span>
</template>
</el-table-column>
<el-table-column prop="createUserName" label="创建人"></el-table-column>
<el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button size="small" type="primary" v-on:click="editSpaceInfo(scope.row)">编辑</el-button>
<el-button size="small" type="danger" v-on:click="deleteSpaceInfo(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
<!--关于弹窗-->
<el-dialog title="关于zyplayer-doc-wiki" :visible.sync="aboutDialogVisible" width="600px">
<el-form>
<el-form-item label="项目地址:">
<a target="_blank" href="https://gitee.com/zyplayer/zyplayer-doc">zyplayer-doc</a>
</el-form-item>
<el-form-item label="开发人员:">
<a target="_blank" href="http://zyplayer.com">暮光城中城</a>
</el-form-item>
<template v-if="upgradeInfo.lastVersion">
<el-form-item label="当前版本:">{{upgradeInfo.nowVersion}}</el-form-item>
<el-form-item label="最新版本:">{{upgradeInfo.lastVersion}}</el-form-item>
<el-form-item label="升级地址:">
<a target="_blank" :href="upgradeInfo.upgradeUrl">{{upgradeInfo.upgradeUrl}}</a>
</el-form-item>
<el-form-item label="升级内容:">{{upgradeInfo.upgradeContent}}</el-form-item>
</template>
<el-form-item label="">
欢迎加群讨论QQ群号466363173欢迎提交需求欢迎使用和加入开发
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import userApi from '../../common/api/user'
import pageApi from '../../common/api/page'
export default {
data() {
return {
leftCollapse: true,
aboutDialogVisible: false,
rightContentLoading: false,
pathIndex: [],
defaultProps: {
children: 'children',
label: 'name'
},
// 空间搜索相关
spaceOptions: [],
spaceList:[],
choiceSpace: "",
nowSpaceShow: {},
newSpaceDialogVisible: false,
manageSpaceDialogVisible: false,
newSpaceForm: {id: '', name: '', spaceExplain: '', treeLazyLoad: 0, openDoc: 0, uuid: '', type: 1},
newSpaceFormRules: {
name: [
{required: true, message: '请输入空间名', trigger: 'blur'},
{min: 2, max: 25, message: '长度在 2 到 25 个字符', trigger: 'blur'}
],
},
nowClickPath: {
id: '',
path: '',
},
// 依据目录树存储的map全局对象
treePathDataMap: new Map(),
// 搜索的输入内容
searchKeywords: "",
// 页面展示相关
wikiPageList:[],
wikiPage: {},
wikiPageExpandedKeys: [],
// 升级信息
upgradeInfo: {},
}
},
computed: {
fullscreen () {
return this.$store.state.global.fullscreen;
}
},
mounted: function () {
this.loadSpaceList();
this.checkSystemUpgrade();
},
methods: {
loadPageList(param) {
param = param || {};
this.doGetPageList(param.parentId, param.node);
},
createWiki() {
if (this.nowClickPath.spaceId > 0) {
var param = {
spaceId: this.nowClickPath.spaceId,
parentId: this.nowClickPath.parentId, path: this.nowClickPath.path
};
this.$router.push({path: '/page/edit', query: param});
} else {
this.$message.warning("请先选择或创建空间");
}
},
changeWikiPageExpandedKeys(pageId) {
this.wikiPageExpandedKeys = [pageId];
},
searchByKeywords() {
this.$refs.wikiPageTree.filter(this.searchKeywords);
},
searchByKeywordsNewPage() {
var routeUrl = this.$router.resolve({path: '/page/search', query: {keywords: this.searchKeywords}});
window.open(routeUrl.href, '_blank');
},
handleNodeClick(data) {
console.log("点击节点:", data);
this.nowClickPath = {spaceId: this.nowClickPath.spaceId, pageId: data.id, parentId: data.id, path: data.path};
this.$router.push({path: '/page/show', query: this.nowClickPath});
},
handleNodeExpand(node) {
if (node.children.length > 0 && node.children[0].needLoad) {
console.log("加载节点:", node);
this.doGetPageList(node.id, node);
}
},
handlePageDrop(draggingNode, dropNode, dropType, ev) {
console.log('tree drop: ', draggingNode.data, dropNode.data, dropType);
// 'prev'、'inner'、'next'
// before、after、inner
var param = {id: draggingNode.data.id, parentId: dropNode.data.parentId};
if (dropType == 'inner') {
param.parentId = dropNode.data.id;
} else if (dropType == 'before') {
param.beforeSeq = dropNode.data.seqNo;
} else if (dropType == 'after') {
param.afterSeq = dropNode.data.seqNo;
}
pageApi.pageChangeParent(param).then(res => {
this.doGetPageList(null);
});
},
filterPageNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
editSpaceInfo(row) {
this.newSpaceForm = {
id: row.id, name: row.name, spaceExplain: row.spaceExplain,
treeLazyLoad: row.treeLazyLoad, openDoc: row.openDoc, type: row.type
};
this.newSpaceDialogVisible = true;
},
deleteSpaceInfo(row) {
this.$confirm('确定要删除此空间及下面的所有文档吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let param = {id: row.id, delFlag: 1};
pageApi.updateSpace(param).then(() => {
this.loadSpaceList();
});
});
},
spaceChangeEvents(data) {
if (data == 0) {
this.newSpaceForm = {id: '', name: '', spaceExplain: '', treeLazyLoad: 0, openDoc: 0, uuid: '', type: 1};
this.choiceSpace = this.nowClickPath.spaceId;
this.newSpaceDialogVisible = true;
} else if (data == -1) {
// 使得选择的空间展示不变
this.choiceSpace = this.nowClickPath.spaceId;
this.manageSpaceDialogVisible = true;
} else {
// 切换空间,重新初始化当前点击项,防止创建保存到之前点击的空间下去了
this.nowClickPath = {spaceId: data};
for (var i = 0; i < this.spaceList.length; i++) {
if (this.spaceList[i].id == data) {
this.nowSpaceShow = this.spaceList[i];
break;
}
}
this.doGetPageList(null);
this.$router.push({path: '/home', query: {spaceId: data}});
}
},
loadSpaceList() {
pageApi.spaceList({}).then(json => {
this.spaceList = json.data || [];
var spaceOptions = [];
for (var i = 0; i < this.spaceList.length; i++) {
spaceOptions.push({
label: this.spaceList[i].name, value: this.spaceList[i].id
});
}
this.spaceOptions = spaceOptions;
if (this.spaceList.length > 0) {
var spaceId = this.spaceList[0].id;
this.nowSpaceShow = this.spaceList[0];
this.nowClickPath = {spaceId: spaceId};
this.choiceSpace = spaceId;
this.doGetPageList(null);
// TODO 在首页时跳转
try {
if (this.$router.app._route.path == "/home") {
this.$router.push({path: '/home', query: {spaceId: spaceId}});
}
} catch (e) {
console.log(e);
}
}
});
},
doGetPageList(parentId, node) {
var nodePath = "";
if (!!node) {
nodePath = node.nodePath || "/";
if (!nodePath.endsWith("/")) {
nodePath += "/";
}
} else {
nodePath = "/";
}
var param = {spaceId: this.nowClickPath.spaceId, parentId: parentId || 0};
if (this.nowSpaceShow.treeLazyLoad == 0) {
param.parentId = null;
}
pageApi.pageList(param).then(json => {
var result = json.data || [];
var pathIndex = [];
if (this.nowSpaceShow.treeLazyLoad == 0) {
pathIndex = result;
} else {
for (var i = 0; i < result.length; i++) {
var item = result[i];
item.parentId = item.parentId || 0;
item.children = [{label: '', needLoad: true}];// 初始化一个对象,点击展开时重新查询加载
pathIndex.push(item);
}
}
this.createNodePath(pathIndex, nodePath);
if (parentId > 0) {
node.children = pathIndex;
} else {
this.wikiPageList = pathIndex;
//this.lastClickNode = {};
}
});
},
createNodePath(node, nodePath) {
if (!nodePath.endsWith("/")) {
nodePath += "/";
}
for (var i = 0; i < node.length; i++) {
var item = node[i];
item.nodePath = nodePath + item.name;
if (!!item.children && item.children.length > 0) {
this.createNodePath(item.children, item.nodePath);
}
}
},
userSettingDropdown(command) {
console.log("command:" + command);
if (command == 'userSignOut') {
this.userSignOut();
} else if (command == 'aboutDoc') {
this.aboutDialogVisible = true;
} else if (command == 'myInfo') {
this.$router.push({path: '/user/myInfo'});
} else if (command == 'console') {
window.location = process.env.VUE_APP_BASE_API;
} else {
this.$message.warning("暂未开放");
}
},
userSignOut() {
userApi.userLogout().then(() => {
location.reload();
});
},
onNewSpaceSubmit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
var param = {
id: this.newSpaceForm.id,
name: this.newSpaceForm.name,
type: this.newSpaceForm.type,
openDoc: this.newSpaceForm.openDoc,
spaceExplain: this.newSpaceForm.spaceExplain,
treeLazyLoad: this.newSpaceForm.treeLazyLoad,
};
pageApi.updateSpace(param).then(json => {
if (param.id > 0) {
this.loadSpaceList();
} else {
this.spaceList.push(json.data);
this.spaceOptions.push({
label: json.data.name, value: json.data.id
});
this.nowSpaceShow = json.data;
this.nowClickPath = {spaceId: json.data.id};
this.choiceSpace = json.data.id;
this.doGetPageList(null);
}
this.newSpaceForm = {id: '', name: '', spaceExplain: '', treeLazyLoad: 0, openDoc: 0, uuid: '', type: 1};
this.newSpaceDialogVisible = false;
});
}
});
},
onNewSpaceCancel() {
this.newSpaceDialogVisible = false;
},
checkSystemUpgrade() {
userApi.systemUpgradeInfo({}).then(json => {
if (!!json.data) {
this.upgradeInfo = json.data;
console.log("zyplayer-doc发现新版本"
+ "\n升级地址" + json.data.upgradeUrl
+ "\n当前版本" + json.data.nowVersion
+ "\n最新版本" + json.data.lastVersion
+ "\n升级内容" + json.data.upgradeContent
);
}
});
},
switchSpacePage(spaceId) {
spaceId = parseInt(spaceId);
if (this.choiceSpace == spaceId) {
return;
}
this.choiceSpace = spaceId;
this.nowClickPath.spaceId = spaceId;
this.doGetPageList(null);
},
}
}
</script>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
}
#app, .el-container, .el-menu {
height: 100%;
}
.el-header {
background-color: #1D4E89 !important;
}
.el-header {color: #333; line-height: 40px; text-align: right;height: 40px !important;}
.icon-collapse{float: left;font-size: 25px;color: #aaa;margin-top: 8px;cursor: pointer;}
.icon-collapse:hover{color: #eee;}
</style>

View File

@@ -1,31 +0,0 @@
<template>
<router-view @loadPageList="loadPageList" @changeExpandedKeys="changeExpandedKeys" @switchSpace="switchSpace"/>
</template>
<script>
export default {
name: 'PageView',
components: {},
data() {
return {}
},
created() {
},
watch: {},
methods: {
loadPageList(param) {
this.$emit('loadPageList', param);
},
changeExpandedKeys(param) {
this.$emit('changeExpandedKeys', param);
},
switchSpace(param) {
this.$emit('switchSpace', param);
},
}
}
</script>
<style scoped>
</style>

View File

@@ -19,8 +19,8 @@ Vue.prototype.$store = store;
const router = new VueRouter({routes});
// 路由跳转时判断处理
router.beforeEach((to, from, next) => {
if (to.meta.title) {
document.title = to.meta.name;
if (to.name) {
document.title = to.name;
}
store.commit('global/setFullscreen', !!to.meta.fullscreen);
next();

View File

@@ -1,37 +1,20 @@
import Home from './views/home/Home.vue'
import UserLogin from './views/user/Login.vue'
import UserMyInfo from './views/user/MyInfo.vue'
import CommonRouterView from './components/layouts/PageView'
import PageShow from './views/page/Show.vue'
import PageEdit from './views/page/Edit.vue'
import PageSearch from './views/page/Search.vue'
import CommonNoAuth from './views/common/NoAuth.vue'
import GlobalLayout from './components/layouts/GlobalLayout'
let routes = [
{path: '/', redirect: '/home'},
{path: '/user/login', name: '系统登录', component: () => import('@/views/user/Login')},
{path: '/page/search', name: '全局搜索', component: () => import('@/views/page/Search')},
{path: '/common/noAuth', name: '没有权限', component: () => import('@/views/common/NoAuth')},
{path: '/page/editorTest', name: '编辑内容', component: () => import('@/views/page/EditorTest')},
{
path: '/home',
component: Home,
name: '主页',
meta: {
requireAuth: true,
}
}, {
path: '/',
redirect: '/home'
}, {
path: '/',
name: '用户管理',
component: CommonRouterView,
name: '页面管理',
component: GlobalLayout,
children: [
{path: '/user/login', name: '系统登录', component: UserLogin, meta: {fullscreen: true}},
{path: '/user/myInfo', name: '我的信息', component: UserMyInfo},
{path: '/page/show', name: '页面内容展示', component: PageShow},
{path: '/page/edit', name: '编辑内容', component: PageEdit},
{path: '/page/search', name: '全局搜索', component: PageSearch, meta: {fullscreen: true}},
{path: '/common/noAuth', name: '没有权限', component: CommonNoAuth},
{path: '/home', name: '主页', component: () => import('@/views/home/Home')},
{path: '/user/myInfo', name: '我的信息', component: () => import('@/views/user/MyInfo')},
{path: '/page/show', name: '页面内容展示', component: () => import('@/views/page/Show')},
{path: '/page/edit', name: '编辑内容', component: () => import('@/views/page/Edit')},
]
}
];

View File

@@ -2,10 +2,10 @@
<div style="padding: 10px;" class="home-vue">
<div style="max-width: 800px;margin: 0 auto;">
<el-select v-model="searchParam.newsType" v-on:change="getSpacePageNews" placeholder="请选择查看方式" style="float: right;z-index: 1;">
<el-option :label="val" :value="index+1" v-for="(val, index) in newsTypes"></el-option>
<el-option :label="item.val" :value="item.key" :key="item.key" v-for="item in newsTypesArr"></el-option>
</el-select>
<el-tabs value="first">
<el-tab-pane :label="newsTypes[searchParam.newsType-1]" name="first">
<el-tab-pane :label="newsTypesMap[searchParam.newsType]" name="first">
<div v-if="spacePageNews.length <= 0" class="empty-news">暂无数据</div>
<div v-else class="line-box" v-for="item in spacePageNews">
<div class="line-title">
@@ -52,8 +52,13 @@
pageNum: 1,
pageSize: 20,
},
spacePageNews:[],
newsTypes:["最近更新", "最新创建", "查看最多", "点赞最多", "查看+点赞最多"],
spacePageNews: [],
// 列表类型
newsTypesArr: [
{key: 1, val: '最近更新'}, {key: 2, val: '最新创建'}, {key: 3, val: '查看最多'},
{key: 4, val: '点赞最多'}, {key: 5, val: '查看+点赞最多'}
],
newsTypesMap: {},
};
},
beforeRouteUpdate(to, from, next) {
@@ -92,6 +97,8 @@
if (!!this.searchParam.spaceId) {
this.getSpacePageNews();
}
this.newsTypesMap = {};
this.newsTypesArr.forEach(item => this.newsTypesMap[item.key] = item.val);
},
}
}

View File

@@ -0,0 +1,27 @@
<template>
<div style="padding: 10px;" class="page-editor-vue">
<mg-editor></mg-editor>
</div>
</template>
<script>
import mgEditor from '../../components/editor/MgEditor'
export default {
data() {
return {
editor: {},
};
},
components: {
'mg-editor': mgEditor,
},
mounted: function () {
},
methods: {
}
}
</script>
<style>
</style>

View File

@@ -9,7 +9,7 @@
创建{{wikiPage.createUserName}} {{wikiPage.createTime}}  
<span v-show="wikiPage.updateUserName">修改{{wikiPage.updateUserName}} {{wikiPage.updateTime}}</span>
<div style="float: right;">
<el-upload class="upload-page-file" :action="apilist1.commonUpload"
<el-upload class="upload-page-file" :action="uploadFileUrl"
:with-credentials="true"
:on-success="uploadFileSuccess" :on-error="uploadFileError"
name="files" show-file-list multiple :data="uploadFormData" :limit="999"
@@ -155,6 +155,7 @@
commentTextInput: "",
commentList: [],
recommentInfo: {},
uploadFileUrl: process.env.VUE_APP_BASE_API + '/zyplayer-doc-wiki/common/upload',
// 页面权限
pageAuthDialogVisible: false,
pageAuthUserList: [],

View File

@@ -8,5 +8,5 @@ module.exports = {
host: 'local.zyplayer.com'
},
publicPath: './',
productionSourceMap: false
productionSourceMap: false,
};