wiki优化
This commit is contained in:
@@ -37,6 +37,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* es抽象类
|
* es抽象类
|
||||||
|
*
|
||||||
* @author 暮光:城中城
|
* @author 暮光:城中城
|
||||||
* @since 2019-07-07
|
* @since 2019-07-07
|
||||||
*/
|
*/
|
||||||
@@ -101,6 +102,7 @@ public abstract class EsAbstractService<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 多条件 模糊查询,查询前100条
|
* 多条件 模糊查询,查询前100条
|
||||||
|
*
|
||||||
* @param condition 查询条件
|
* @param condition 查询条件
|
||||||
*/
|
*/
|
||||||
public List<T> getDataByCondition(List<EsQueryColumn> condition) {
|
public List<T> getDataByCondition(List<EsQueryColumn> condition) {
|
||||||
@@ -109,9 +111,10 @@ public abstract class EsAbstractService<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 多条件 模糊查询
|
* 多条件 模糊查询
|
||||||
* @param condition 查询条件
|
*
|
||||||
|
* @param condition 查询条件
|
||||||
* @param startIndex 开始行
|
* @param startIndex 开始行
|
||||||
* @param pageSize 每页数量
|
* @param pageSize 每页数量
|
||||||
*/
|
*/
|
||||||
public EsPage<T> getDataByCondition(List<EsQueryColumn> condition, String[] fields, Integer startIndex, Integer pageSize) {
|
public EsPage<T> getDataByCondition(List<EsQueryColumn> condition, String[] fields, Integer startIndex, Integer pageSize) {
|
||||||
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
|
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
|
||||||
@@ -147,11 +150,13 @@ public abstract class EsAbstractService<T> {
|
|||||||
});
|
});
|
||||||
return this.getDataByQuery(boolQueryBuilder, fields, startIndex, pageSize);
|
return this.getDataByQuery(boolQueryBuilder, fields, startIndex, pageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多条件 模糊查询
|
* 多条件 模糊查询
|
||||||
|
*
|
||||||
* @param queryBuilders 查询条件
|
* @param queryBuilders 查询条件
|
||||||
* @param startIndex 开始行
|
* @param startIndex 开始行
|
||||||
* @param pageSize 每页数量
|
* @param pageSize 每页数量
|
||||||
*/
|
*/
|
||||||
public EsPage<T> getDataByQuery(QueryBuilder queryBuilders, String[] fields, Integer startIndex, Integer pageSize) {
|
public EsPage<T> getDataByQuery(QueryBuilder queryBuilders, String[] fields, Integer startIndex, Integer pageSize) {
|
||||||
// 设置高亮标签
|
// 设置高亮标签
|
||||||
@@ -208,5 +213,5 @@ public abstract class EsAbstractService<T> {
|
|||||||
esPage.setData(tableList);
|
esPage.setData(tableList);
|
||||||
return esPage;
|
return esPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,10 +52,11 @@ public class WikiPageServiceImpl extends ServiceImpl<WikiPageMapper, WikiPage> i
|
|||||||
}
|
}
|
||||||
this.updateById(wikiPage);
|
this.updateById(wikiPage);
|
||||||
// 给相关人发送消息
|
// 给相关人发送消息
|
||||||
|
WikiPage wikiPageSel = this.getById(wikiPage.getId());
|
||||||
DocUserDetails currentUser = DocUserUtil.getCurrentUser();
|
DocUserDetails currentUser = DocUserUtil.getCurrentUser();
|
||||||
UserMessage userMessage = userMessageService.createUserMessage(currentUser, wikiPage.getId(), wikiPage.getName(), UserMsgSysType.WIKI, UserMsgType.WIKI_PAGE_PARENT);
|
UserMessage userMessage = userMessageService.createUserMessage(currentUser, wikiPageSel.getId(), wikiPageSel.getName(), UserMsgSysType.WIKI, UserMsgType.WIKI_PAGE_PARENT);
|
||||||
userMessage.setAffectUserId(wikiPage.getCreateUserId());
|
userMessage.setAffectUserId(wikiPageSel.getCreateUserId());
|
||||||
userMessage.setAffectUserName(wikiPage.getCreateUserName());
|
userMessage.setAffectUserName(wikiPageSel.getCreateUserName());
|
||||||
userMessageService.addWikiMessage(userMessage);
|
userMessageService.addWikiMessage(userMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -426,4 +426,22 @@ CREATE TABLE `db_transfer_task` (
|
|||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `user_message`;
|
||||||
|
CREATE TABLE `user_message` (
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键自增ID',
|
||||||
|
`sys_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '系统类型 1=manage 2=wiki 3=db',
|
||||||
|
`msg_type` int NOT NULL DEFAULT 1 COMMENT '消息类型 1=普通文本消息 2=wiki文档创建 3=wiki文档删除 4=wiki文档编辑 5=wiki文档权限修改 6=wiki文档评论 7=wiki文档删除评论 8=wiki文档上传附件',
|
||||||
|
`data_id` bigint(20) NULL DEFAULT NULL COMMENT '消息关联的数据ID',
|
||||||
|
`data_desc` varchar(100) NULL DEFAULT NULL COMMENT '消息关联的数据说明',
|
||||||
|
`msg_content` varchar(255) NULL DEFAULT NULL COMMENT '消息内容',
|
||||||
|
`msg_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '消息状态 0=未读 1=已读 2=已删除',
|
||||||
|
`operator_user_id` bigint(20) NULL DEFAULT NULL COMMENT '操作人用户ID',
|
||||||
|
`operator_user_name` varchar(20) NULL DEFAULT NULL COMMENT '操作人用户名',
|
||||||
|
`affect_user_id` bigint(20) NULL DEFAULT NULL COMMENT '影响人用户ID',
|
||||||
|
`affect_user_name` varchar(20) NULL DEFAULT NULL COMMENT '影响人用户名',
|
||||||
|
`accept_user_id` bigint(20) NULL DEFAULT NULL COMMENT '接收人用户ID',
|
||||||
|
`creation_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
|
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户消息表' ROW_FORMAT = Compact;
|
||||||
|
|
||||||
SET FOREIGN_KEY_CHECKS = 1;
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
ENV = 'development'
|
ENV = 'development'
|
||||||
|
|
||||||
# base api
|
# base api
|
||||||
VUE_APP_BASE_API = 'http://local.zyplayer.com:8083/zyplayer-doc-manage'
|
# VUE_APP_BASE_API = 'http://local.zyplayer.com:8083/zyplayer-doc-manage'
|
||||||
# VUE_APP_BASE_API = 'http://doc.zyplayer.com/zyplayer-doc-manage'
|
VUE_APP_BASE_API = 'http://doc.zyplayer.com/zyplayer-doc-manage'
|
||||||
|
|
||||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||||
|
|
||||||
|
|||||||
@@ -82,10 +82,11 @@
|
|||||||
this.userInput = this.$refs.userInput;
|
this.userInput = this.$refs.userInput;
|
||||||
this.editorDom.push(new Dom('text', 'head head-h1'));
|
this.editorDom.push(new Dom('text', 'head head-h1'));
|
||||||
document.body.addEventListener('click', e => {
|
document.body.addEventListener('click', e => {
|
||||||
for (let i = 0; i < this.editorDom.length; i++) {
|
let endIndex = Math.min(this.editorDom.length, this.editorRange.endDomIndex);
|
||||||
|
for (let i = this.editorRange.startDomIndex; i < endIndex; i++) {
|
||||||
this.editorDom[i].clearRange();
|
this.editorDom[i].clearRange();
|
||||||
}
|
}
|
||||||
this.editorToolbarStyle.display = 'none';
|
this.hideToolbar();
|
||||||
});
|
});
|
||||||
// 监听输入框的特殊按键
|
// 监听输入框的特殊按键
|
||||||
this.userInput.addEventListener('keydown', e => {
|
this.userInput.addEventListener('keydown', e => {
|
||||||
@@ -262,25 +263,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleToolbarBold() {
|
handleToolbarBold() {
|
||||||
for (let i = this.editorRange.startDomIndex; i < this.editorRange.endDomIndex; i++) {
|
this.handleToolbarCommon(dom => dom.addSelectionTextStyle('bold', 1));
|
||||||
this.editorDom[i].addSelectionTextStyle('bold', 1);
|
|
||||||
}
|
|
||||||
this.editorToolbarStyle.display = 'none';
|
|
||||||
window.getSelection().removeAllRanges();
|
|
||||||
},
|
},
|
||||||
handleToolbarStrikeThrough() {
|
handleToolbarStrikeThrough() {
|
||||||
for (let i = this.editorRange.startDomIndex; i < this.editorRange.endDomIndex; i++) {
|
this.handleToolbarCommon(dom => dom.addSelectionTextStyle('strikethrough', 1));
|
||||||
this.editorDom[i].addSelectionTextStyle('strikethrough', 1);
|
|
||||||
}
|
|
||||||
this.editorToolbarStyle.display = 'none';
|
|
||||||
window.getSelection().removeAllRanges();
|
|
||||||
},
|
},
|
||||||
handleToolbarHn(hn) {
|
handleToolbarHn(hn) {
|
||||||
|
this.handleToolbarCommon(dom => dom.addSelectionTextHead(hn));
|
||||||
|
},
|
||||||
|
handleToolbarCommon(callback) {
|
||||||
|
let undoDomBefore = [], undoDomAfter = [];
|
||||||
for (let i = this.editorRange.startDomIndex; i < this.editorRange.endDomIndex; i++) {
|
for (let i = this.editorRange.startDomIndex; i < this.editorRange.endDomIndex; i++) {
|
||||||
this.editorDom[i].addSelectionTextHead(hn);
|
undoDomBefore.push(JSON.parse(JSON.stringify(this.editorDom[i])));
|
||||||
|
callback(this.editorDom[i]);
|
||||||
|
undoDomAfter.push(this.editorDom[i]);
|
||||||
}
|
}
|
||||||
this.editorToolbarStyle.display = 'none';
|
this.undoRedo.execute(1, this.editorRange.startDomIndex, undoDomBefore, undoDomAfter);
|
||||||
|
this.hideToolbar();
|
||||||
window.getSelection().removeAllRanges();
|
window.getSelection().removeAllRanges();
|
||||||
|
setTimeout(() => this.userInput.focus(), 50);
|
||||||
},
|
},
|
||||||
hideToolbar() {
|
hideToolbar() {
|
||||||
this.editorRange.startDomIndex = -1;
|
this.editorRange.startDomIndex = -1;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ function Dom(type = 'text', cls = '', text = '', styleRange = []) {
|
|||||||
// 一个范围的样式,例:{start: 1, end: 2, class: 'xx xxx'}
|
// 一个范围的样式,例:{start: 1, end: 2, class: 'xx xxx'}
|
||||||
this.styleRange = [];
|
this.styleRange = [];
|
||||||
styleRange.forEach(item => {
|
styleRange.forEach(item => {
|
||||||
this.styleRange.push(new StyleRange(item.start, item.end, this.styleRange.cls));
|
this.styleRange.push(new StyleRange(item.start, item.end, item.cls));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ UndoRedo.prototype = {
|
|||||||
let undoIndex = undoInfo.index;
|
let undoIndex = undoInfo.index;
|
||||||
changeContent.forEach(item => {
|
changeContent.forEach(item => {
|
||||||
this.undoObjDomToEditor(undoInfo, undoIndex, item);
|
this.undoObjDomToEditor(undoInfo, undoIndex, item);
|
||||||
undoIndex--;
|
undoIndex++;
|
||||||
});
|
});
|
||||||
this.undoRedoIndex = Math.max(this.undoRedoIndex - 1, -1);
|
this.undoRedoIndex = Math.max(this.undoRedoIndex - 1, -1);
|
||||||
},
|
},
|
||||||
@@ -62,17 +62,18 @@ UndoRedo.prototype = {
|
|||||||
this.undoRedoIndex--;
|
this.undoRedoIndex--;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let undoInfo = this.undoRedoList[this.undoRedoIndex];
|
let redoInfo = this.undoRedoList[this.undoRedoIndex];
|
||||||
let actionText = (undoInfo.type == 1) ? undoInfo.after : undoInfo.before;
|
let actionText = (redoInfo.type == 1) ? redoInfo.after : redoInfo.before;
|
||||||
let changeContent = JSON.parse(actionText);
|
let changeContent = JSON.parse(actionText);
|
||||||
let undoIndex = undoInfo.index;
|
let redoIndex = redoInfo.index;
|
||||||
changeContent.forEach(item => {
|
changeContent.forEach(item => {
|
||||||
this.redoObjDomToEditor(undoInfo, item);
|
this.redoObjDomToEditor(redoInfo, redoIndex, item);
|
||||||
undoIndex++;
|
redoIndex++;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
redoObjDomToEditor(undoInfo, undoIndex, domObj) {
|
redoObjDomToEditor(undoInfo, undoIndex, domObj) {
|
||||||
let dom = new Dom(domObj.type, domObj.cls, domObj.text, domObj.styleRange);
|
let dom = new Dom(domObj.type, domObj.cls, domObj.text, domObj.styleRange);
|
||||||
|
dom.computerStyleRangeToDom();
|
||||||
if (undoInfo.type == 1) {
|
if (undoInfo.type == 1) {
|
||||||
// 1=修改 2=添加 3=删除
|
// 1=修改 2=添加 3=删除
|
||||||
if (this.editorDom.length > undoIndex) {
|
if (this.editorDom.length > undoIndex) {
|
||||||
@@ -94,6 +95,7 @@ UndoRedo.prototype = {
|
|||||||
},
|
},
|
||||||
undoObjDomToEditor(undoInfo, undoIndex, domObj) {
|
undoObjDomToEditor(undoInfo, undoIndex, domObj) {
|
||||||
let dom = new Dom(domObj.type, domObj.cls, domObj.text, domObj.styleRange);
|
let dom = new Dom(domObj.type, domObj.cls, domObj.text, domObj.styleRange);
|
||||||
|
dom.computerStyleRangeToDom();
|
||||||
if (undoInfo.type == 1) {
|
if (undoInfo.type == 1) {
|
||||||
// 1=修改 2=添加 3=删除
|
// 1=修改 2=添加 3=删除
|
||||||
if (this.editorDom.length > undoIndex) {
|
if (this.editorDom.length > undoIndex) {
|
||||||
|
|||||||
342
zyplayer-doc-ui/wiki-ui/src/components/editor3/MgEditor.vue
Normal file
342
zyplayer-doc-ui/wiki-ui/src/components/editor3/MgEditor.vue
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
<!--
|
||||||
|
-- zyplayer-doc 自研编辑器,集各大厂商优秀的设计(拷它的css!拷它的图标!借鉴它的内容处理),造一个现代化、好用的轮子。
|
||||||
|
-- 带着情怀开写,路途曲折,希望能完美呈现。
|
||||||
|
-- 参考:飞书文档、语雀文档、wangEditor
|
||||||
|
-- @author 暮光:城中城
|
||||||
|
-- @since 2020-06-06
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div class="mg-editor-box">
|
||||||
|
<div ref="mgEditor" class="mg-editor" contenteditable="true" @click.stop="editorClick($event)">
|
||||||
|
<template v-for="(item, index) in editorDom">
|
||||||
|
<div v-if="item.type=='text'" :class="item.cls" :index="index" @click.stop="domClick(item, $event)">
|
||||||
|
<template v-if="item.dom && item.dom.length > 0">
|
||||||
|
<span v-for="(span, subIndex) in item.dom" :class="span.cls" :index="index" :subindex="subIndex">{{span.text}}</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="!!item.text">{{item.text}}</template>
|
||||||
|
<br v-else/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="item.type=='locate'" :class="item.cls" :index="index" @click.stop="domClick(item, $event)">
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="mg-editor-toolbar" :style="editorToolbarStyle">
|
||||||
|
<span class="iconfont icon-h1" @click="handleToolbarHn('h1')"></span>
|
||||||
|
<span class="iconfont icon-h2" @click="handleToolbarHn('h2')"></span>
|
||||||
|
<span class="iconfont icon-h3" @click="handleToolbarHn('h3')"></span>
|
||||||
|
<span class="iconfont icon-hn"></span>
|
||||||
|
<span class="iconfont icon-bold" @click="handleToolbarBold()"></span>
|
||||||
|
<span class="iconfont icon-delete" @click="handleToolbarStrikeThrough()"></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>
|
||||||
|
<!-- <textarea ref="userInput" @input="userInputDataChange()" v-model="userInputData" class="user-input" :style="userInputStyle"></textarea>-->
|
||||||
|
<!-- <div class="mg-editor-cursor" :style="editorCursorStyle"></div>-->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import "./css/MgEditor.css";
|
||||||
|
import "./css/MgEditorIconfont.css";
|
||||||
|
import Dom from './util/dom';
|
||||||
|
import UndoRedo from './util/undoRedo';
|
||||||
|
import toolbarCommon from './toolbar/common';
|
||||||
|
|
||||||
|
const $ = require("jquery");
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "mg-editor",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
editor: {},
|
||||||
|
editorRange: {
|
||||||
|
startOffset: 0,
|
||||||
|
endOffset: 0,
|
||||||
|
startDomIndex: -1,
|
||||||
|
endDomIndex: -1,
|
||||||
|
},
|
||||||
|
compositionStart: false,
|
||||||
|
userInput: {},
|
||||||
|
userInputStyle: {
|
||||||
|
top: 0, left: 0, display: 'none'
|
||||||
|
},
|
||||||
|
editorCursorStyle: {
|
||||||
|
top: 0, left: 0, display: 'none'
|
||||||
|
},
|
||||||
|
editorToolbarStyle: {
|
||||||
|
top: 0, left: 0, display: 'none'
|
||||||
|
},
|
||||||
|
editorDom: [],
|
||||||
|
editDom: {},
|
||||||
|
userInputData: '',
|
||||||
|
undoRedo: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
this.undoRedo = new UndoRedo(this.editorDom);
|
||||||
|
this.editor = this.$refs.mgEditor;
|
||||||
|
this.userInput = this.$refs.userInput;
|
||||||
|
this.editorDom.push(new Dom('text', 'head head-h1'));
|
||||||
|
document.body.addEventListener('click', e => {
|
||||||
|
for (let i = 0; i < this.editorDom.length; i++) {
|
||||||
|
this.editorDom[i].clearRange();
|
||||||
|
}
|
||||||
|
this.editorToolbarStyle.display = 'none';
|
||||||
|
});
|
||||||
|
// 监听输入框的输入事件
|
||||||
|
this.editor.addEventListener('textInput', e => {
|
||||||
|
// if (this.compositionStart) return;
|
||||||
|
this.userInputData = e.data;
|
||||||
|
this.userInputDataChange();
|
||||||
|
console.log("textInput " + e.data);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
this.editor.addEventListener('input', e => {
|
||||||
|
if (!e.data) return;
|
||||||
|
this.userInputData = e.data;
|
||||||
|
this.userInputDataChange();
|
||||||
|
console.log("input " + e.data);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
this.editor.addEventListener('compositionstart', e => {
|
||||||
|
this.compositionStart = true;
|
||||||
|
console.log("compositionstart")
|
||||||
|
});
|
||||||
|
this.editor.addEventListener('compositionend', e => {
|
||||||
|
document.execCommand('delete', false, '');
|
||||||
|
this.compositionStart = false;
|
||||||
|
console.log("compositionend")
|
||||||
|
});
|
||||||
|
// this.stopPropagation("beforeinput input textInput change keydown keyup keypress");
|
||||||
|
// this.stopPropagation("keypress textInput");
|
||||||
|
// 监听输入框的特殊按键
|
||||||
|
this.editor.addEventListener('keydown', e => {
|
||||||
|
if (e.which == 13) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.editDom = this.editDom.keyEnter(this.editorDom, this.editorRange, this.undoRedo);
|
||||||
|
// 修改光标位置为下一行的开始
|
||||||
|
this.editorRange.startDomIndex = 0;
|
||||||
|
this.editorRange.endDomIndex = 0;
|
||||||
|
this.$forceUpdate();
|
||||||
|
} else if (e.keyCode == 90 && e.ctrlKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.undoRedo.undo();
|
||||||
|
this.$forceUpdate();
|
||||||
|
} else if (e.keyCode == 89 && e.ctrlKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.undoRedo.redo();
|
||||||
|
this.$forceUpdate();
|
||||||
|
}
|
||||||
|
// console.log(e)
|
||||||
|
});
|
||||||
|
// 鼠标选中事件
|
||||||
|
this.editor.addEventListener('mouseup', e => {
|
||||||
|
// 不延时还能获取到选中的文字(选择文字,单击选中文字的中间)
|
||||||
|
let selectionRange = this.getSelectionRange();
|
||||||
|
if (selectionRange == null) {
|
||||||
|
this.hideToolbar();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let selectText = selectionRange.toString();
|
||||||
|
if (!!selectText) {
|
||||||
|
let startNode = toolbarCommon.getRootDom(selectionRange.startContainer);
|
||||||
|
let endNode = toolbarCommon.getRootDom(selectionRange.endContainer);
|
||||||
|
let startIndex = parseInt(startNode.getAttribute("index"));
|
||||||
|
let endIndex = parseInt(endNode.getAttribute("index"));
|
||||||
|
let isOneDom = startIndex == endIndex;
|
||||||
|
if (startIndex >= 0) {
|
||||||
|
let startOffset = selectionRange.startOffset;
|
||||||
|
let startRealElem = toolbarCommon.getRealElem(selectionRange.startContainer);
|
||||||
|
if (!toolbarCommon.isRootBox(startRealElem.parentNode)) {
|
||||||
|
let previousSibling = startRealElem.previousSibling;
|
||||||
|
for (; previousSibling;) {
|
||||||
|
startOffset += previousSibling.innerText.length;
|
||||||
|
previousSibling = previousSibling.previousSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let domTemp = this.editorDom[startIndex];
|
||||||
|
let endOffset = isOneDom ? selectionRange.endOffset : domTemp.text.length;
|
||||||
|
let endRealElem = toolbarCommon.getRealElem(selectionRange.endContainer);
|
||||||
|
if (!toolbarCommon.isRootBox(endRealElem.parentNode)) {
|
||||||
|
let endPreviousSibling = endRealElem.previousSibling;
|
||||||
|
for (; endPreviousSibling;) {
|
||||||
|
endOffset += endPreviousSibling.innerText.length;
|
||||||
|
endPreviousSibling = endPreviousSibling.previousSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
domTemp.setOffset(startOffset, endOffset);
|
||||||
|
}
|
||||||
|
if (!isOneDom) {
|
||||||
|
for (let i = startIndex + 1; i < endIndex; i++) {
|
||||||
|
this.editorDom[i].setOffsetAll();
|
||||||
|
}
|
||||||
|
let endOffset = selectionRange.endOffset;
|
||||||
|
let previousSibling = toolbarCommon.getRealElem(selectionRange.endContainer).previousSibling;
|
||||||
|
for (; previousSibling;) {
|
||||||
|
endOffset += previousSibling.innerText.length;
|
||||||
|
previousSibling = previousSibling.previousSibling;
|
||||||
|
}
|
||||||
|
if (endIndex >= 0) {
|
||||||
|
let domTemp = this.editorDom[endIndex];
|
||||||
|
domTemp.setOffset(0, endOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.editorRange.startDomIndex = startIndex;
|
||||||
|
this.editorRange.endDomIndex = endIndex + 1;
|
||||||
|
this.editorToolbarStyle.display = 'block';
|
||||||
|
} else {
|
||||||
|
this.hideToolbar();
|
||||||
|
}
|
||||||
|
// console.log("mouseup", selectText, e);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
editorClick(event) {
|
||||||
|
let lastDom = this.editorDom[this.editorDom.length - 1];
|
||||||
|
if (lastDom.type != 'locate') {
|
||||||
|
lastDom = new Dom('locate', 'locate');
|
||||||
|
this.editorDom.push(lastDom);
|
||||||
|
this.undoRedo.execute(2, this.editorDom.length - 1, lastDom, '');
|
||||||
|
}
|
||||||
|
setTimeout(() => event.target.lastChild.click(), 100);
|
||||||
|
},
|
||||||
|
stopPropagation(events) {
|
||||||
|
events.split(" ").forEach(event => {
|
||||||
|
this.editor.addEventListener(event, e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
domClick(dom, event) {
|
||||||
|
setTimeout(() => this.domClickTimer(dom, event), 50);
|
||||||
|
},
|
||||||
|
domClickTimer(dom, event) {
|
||||||
|
this.editDom = dom;
|
||||||
|
this.editDom.target = event.target;
|
||||||
|
// 设置接收用户输入的输入框绝对位置
|
||||||
|
this.userInputStyle.top = event.pageY + 'px';
|
||||||
|
this.userInputStyle.left = event.pageX + 'px';
|
||||||
|
this.userInputStyle.display = 'block';
|
||||||
|
// 设置光标绝对位置
|
||||||
|
let computedStyle = window.getComputedStyle(event.target);
|
||||||
|
let fontSize = parseInt(computedStyle.fontSize);
|
||||||
|
let offsetTop = event.target.offsetTop + (fontSize / 2 / 2);
|
||||||
|
let offsetLeft = event.target.offsetLeft;
|
||||||
|
this.editorCursorStyle.top = offsetTop + 'px';
|
||||||
|
this.editorCursorStyle.left = offsetLeft + 'px';
|
||||||
|
this.editorCursorStyle.height = computedStyle.fontSize;
|
||||||
|
this.editorCursorStyle.display = 'block';
|
||||||
|
// 设置光标所在对象的位置
|
||||||
|
let selectionRange = this.getSelectionRange();
|
||||||
|
if (selectionRange == null) {
|
||||||
|
this.hideToolbar();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let startNode = toolbarCommon.getRootDom(selectionRange.startContainer);
|
||||||
|
let endNode = toolbarCommon.getRootDom(selectionRange.endContainer);
|
||||||
|
let startIndex = startNode.getAttribute("index");
|
||||||
|
let endIndex = endNode.getAttribute("index");
|
||||||
|
if (startIndex != endIndex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 光标开始位置计算
|
||||||
|
let startOffset = selectionRange.startOffset;
|
||||||
|
let previousSibling = toolbarCommon.getRealElem(selectionRange.startContainer).previousSibling;
|
||||||
|
for (; previousSibling; previousSibling = previousSibling.previousSibling) {
|
||||||
|
startOffset += previousSibling.innerText.length;
|
||||||
|
}
|
||||||
|
// 光标结束位置计算
|
||||||
|
let endOffset = selectionRange.endOffset;
|
||||||
|
let endPreviousSibling = toolbarCommon.getRealElem(selectionRange.endContainer).previousSibling;
|
||||||
|
for (; endPreviousSibling; endPreviousSibling = endPreviousSibling.previousSibling) {
|
||||||
|
endOffset += endPreviousSibling.innerText.length;
|
||||||
|
}
|
||||||
|
this.editorRange.startOffset = startOffset;
|
||||||
|
this.editorRange.endOffset = endOffset;
|
||||||
|
// console.log(startOffset, endOffset);
|
||||||
|
// 如果没有选中内容,隐藏工具栏,输入框获取焦点
|
||||||
|
if (startOffset == endOffset) {
|
||||||
|
this.hideToolbar();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userInputDataChange() {
|
||||||
|
if (!this.userInputData) return;
|
||||||
|
// 如果在最后一个div里面输入,则改为非最后一个,然后在最后再加一行
|
||||||
|
let domNew;
|
||||||
|
if (this.editDom.type == 'locate') {
|
||||||
|
this.editDom.type = 'text';
|
||||||
|
this.editDom.removeClass('locate');
|
||||||
|
domNew = new Dom('locate', 'locate');
|
||||||
|
this.editorDom.push(domNew);
|
||||||
|
}
|
||||||
|
let beforeDom = this.editDom.clone();
|
||||||
|
let oldText = this.editDom.text || '';
|
||||||
|
// 如果文字的中间位置点击,则把内容放到指定位置
|
||||||
|
let startOffset = this.editorRange.startOffset;
|
||||||
|
this.editDom.addText(startOffset, this.userInputData);
|
||||||
|
if (startOffset < oldText.length) {
|
||||||
|
this.editorRange.startOffset = this.editorRange.endOffset = (startOffset + this.userInputData.length);
|
||||||
|
} else {
|
||||||
|
// 否则放到最后
|
||||||
|
this.editorRange.startOffset = this.editorRange.endOffset = this.editDom.text.length;
|
||||||
|
}
|
||||||
|
// let newLength = this.userInputData.replace(/[\u0391-\uFFE5]/g, "aa").length / 2;
|
||||||
|
// let letterSpacing = this.userInputData.length * 0.52;
|
||||||
|
// this.editorCursorStyle.left = (parseInt(this.editorCursorStyle.left) + (parseInt(fontSize) * newLength) + letterSpacing) + 'px';
|
||||||
|
this.userInputData = '';
|
||||||
|
// 增加撤销重做记录
|
||||||
|
let editDomNode = toolbarCommon.getRootDom(this.editDom.target);
|
||||||
|
if (editDomNode != null) {
|
||||||
|
let editIndex = parseInt(editDomNode.getAttribute("index"));
|
||||||
|
this.undoRedo.execute(1, editIndex, beforeDom, this.editDom);
|
||||||
|
}
|
||||||
|
// 如果在最后一个div里面输入,则改为非最后一个,然后在最后再加一行
|
||||||
|
if (!!domNew) {
|
||||||
|
this.undoRedo.execute(2, this.editorDom.length - 1, domNew, '');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleToolbarBold() {
|
||||||
|
for (let i = this.editorRange.startDomIndex; i < this.editorRange.endDomIndex; i++) {
|
||||||
|
this.editorDom[i].addSelectionTextStyle('bold', 1);
|
||||||
|
}
|
||||||
|
this.editorToolbarStyle.display = 'none';
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
},
|
||||||
|
handleToolbarStrikeThrough() {
|
||||||
|
for (let i = this.editorRange.startDomIndex; i < this.editorRange.endDomIndex; i++) {
|
||||||
|
this.editorDom[i].addSelectionTextStyle('strikethrough', 1);
|
||||||
|
}
|
||||||
|
this.editorToolbarStyle.display = 'none';
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
},
|
||||||
|
handleToolbarHn(hn) {
|
||||||
|
for (let i = this.editorRange.startDomIndex; i < this.editorRange.endDomIndex; i++) {
|
||||||
|
this.editorDom[i].addSelectionTextHead(hn);
|
||||||
|
}
|
||||||
|
this.editorToolbarStyle.display = 'none';
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
},
|
||||||
|
hideToolbar() {
|
||||||
|
this.editorRange.startDomIndex = -1;
|
||||||
|
this.editorRange.endDomIndex = -1;
|
||||||
|
this.editorToolbarStyle.display = 'none';
|
||||||
|
},
|
||||||
|
getSelectionRange() {
|
||||||
|
let selection = window.getSelection();
|
||||||
|
if (selection.rangeCount > 0) {
|
||||||
|
return selection.getRangeAt(0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
223
zyplayer-doc-ui/wiki-ui/src/components/editor3/css/MgEditor.css
Normal file
223
zyplayer-doc-ui/wiki-ui/src/components/editor3/css/MgEditor.css
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
|
||||||
|
.mg-editor-box {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.mg-editor-box .user-input {
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
font-size: 1px;
|
||||||
|
background: transparent;
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
display: none;
|
||||||
|
z-index: 1;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
border: none;
|
||||||
|
resize: none;
|
||||||
|
outline: none;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 1px;
|
||||||
|
margin: 0 -1px;
|
||||||
|
contain: strict;
|
||||||
|
-ms-user-select: text;
|
||||||
|
-moz-user-select: text;
|
||||||
|
-webkit-user-select: text;
|
||||||
|
user-select: text;
|
||||||
|
white-space: pre !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mg-editor-box .mg-editor-cursor {
|
||||||
|
display: block;
|
||||||
|
top: 42px;
|
||||||
|
left: 202px;
|
||||||
|
width: 7px;
|
||||||
|
height: 14px;
|
||||||
|
color: #F8F8F0;
|
||||||
|
opacity: 0.2;
|
||||||
|
z-index: 4;
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-left: 2px solid;
|
||||||
|
animation-duration: 1000ms;
|
||||||
|
|
||||||
|
animation-timing-function: step-end;
|
||||||
|
animation-name: blink-ace-animate;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
|
||||||
|
transform: translatez(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink-ace-animate {
|
||||||
|
from, to {opacity: 1;}
|
||||||
|
60% {opacity: 0;}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink-ace-animate-smooth {
|
||||||
|
from, to {opacity: 1;}
|
||||||
|
45% {opacity: 1;}
|
||||||
|
60% {opacity: 0;}
|
||||||
|
85% {opacity: 0;}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.mg-editor {
|
||||||
|
padding: 10px;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: text;
|
||||||
|
background: #272822;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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-*/
|
||||||
|
|
||||||
|
.mg-editor .bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mg-editor .strikethrough {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "iconfont";
|
||||||
|
src: url('data:application/x-font-woff2;charset=utf-8;base64,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') 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-bold: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";
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
getRootDom(nowDom) {
|
||||||
|
let newDom = this.getRealElem(nowDom);
|
||||||
|
if (!newDom || this.isRootBox(nowDom)) return null;
|
||||||
|
if (this.isRootBox(newDom.parentNode)) return newDom;
|
||||||
|
return this.getRootDom(newDom.parentNode);
|
||||||
|
},
|
||||||
|
getRealElem: function (elem) {
|
||||||
|
return !elem || elem.nodeType === 1 ? elem : elem.parentNode;
|
||||||
|
},
|
||||||
|
isRootBox(container) {
|
||||||
|
return this.domHaveClass(container, "mg-editor");
|
||||||
|
},
|
||||||
|
domHaveClass(container, cls) {
|
||||||
|
return container && container.classList && container.classList.contains(cls);
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
156
zyplayer-doc-ui/wiki-ui/src/components/editor3/util/dom.js
Normal file
156
zyplayer-doc-ui/wiki-ui/src/components/editor3/util/dom.js
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
// 构造函数
|
||||||
|
import StyleRange from "./styleRange";
|
||||||
|
import toolbarCommon from "../../editor/toolbar/common";
|
||||||
|
|
||||||
|
function Dom(type = 'text', cls = '', text = '', styleRange = []) {
|
||||||
|
this.type = type;
|
||||||
|
this.text = text;
|
||||||
|
this.target = '';
|
||||||
|
this.cls = cls;
|
||||||
|
this.clsSet = new Set(cls.split(" "));
|
||||||
|
this.startOffset = -1;
|
||||||
|
this.endOffset = -1;
|
||||||
|
this.dom = [];
|
||||||
|
// 一个范围的样式,例:{start: 1, end: 2, class: 'xx xxx'}
|
||||||
|
this.styleRange = [];
|
||||||
|
styleRange.forEach(item => {
|
||||||
|
this.styleRange.push(new StyleRange(item.start, item.end, this.styleRange.cls));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原型
|
||||||
|
Dom.prototype = {
|
||||||
|
constructor: Dom,
|
||||||
|
// 删除class
|
||||||
|
removeClass(cls) {
|
||||||
|
if (!this.hasClass(cls)) return;
|
||||||
|
this.clsSet.delete(cls);
|
||||||
|
this.cls = Array.from(this.clsSet).join(" ");
|
||||||
|
},
|
||||||
|
hasClass(cls) {
|
||||||
|
return this.clsSet.has(cls);
|
||||||
|
},
|
||||||
|
addClass(cls) {
|
||||||
|
if (this.hasClass(cls)) return this;
|
||||||
|
this.clsSet.add(cls);
|
||||||
|
this.cls = Array.from(this.clsSet).join(" ");
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
setOffset(start, end) {
|
||||||
|
this.startOffset = start;
|
||||||
|
this.endOffset = end;
|
||||||
|
},
|
||||||
|
setOffsetAll() {
|
||||||
|
this.startOffset = 0;
|
||||||
|
this.endOffset = this.text.length;
|
||||||
|
},
|
||||||
|
clearRange() {
|
||||||
|
this.startOffset = this.endOffset = -1;
|
||||||
|
},
|
||||||
|
addText(startOffset, data) {
|
||||||
|
if (!data) return;
|
||||||
|
// todo 删除选中的内容
|
||||||
|
// 如果在某个样式范围内,则加入
|
||||||
|
for (let i = 0; i < this.styleRange.length; i++) {
|
||||||
|
let item = this.styleRange[i];
|
||||||
|
if (startOffset >= item.start && startOffset < item.end) {
|
||||||
|
item.end += data.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (startOffset < this.text.length) {
|
||||||
|
this.text = this.text.substring(0, startOffset) + data + this.text.substring(startOffset, this.text.length);
|
||||||
|
} else {
|
||||||
|
this.text = this.text + data;
|
||||||
|
}
|
||||||
|
this.computerStyleRangeToDom();
|
||||||
|
},
|
||||||
|
addSelectionTextHead(hn) {
|
||||||
|
if (this.startOffset < 0 || this.endOffset < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.addClass('head').addClass('head-' + hn);
|
||||||
|
this.clearRange();
|
||||||
|
},
|
||||||
|
addSelectionTextStyle(cls) {
|
||||||
|
if (this.startOffset < 0 || this.endOffset < 0 || this.startOffset == this.endOffset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 添加新的分区
|
||||||
|
let styleRangeNew = [].concat(this.styleRange);
|
||||||
|
styleRangeNew.push(new StyleRange(this.startOffset, this.endOffset, cls));
|
||||||
|
let textStyleArr = [];
|
||||||
|
let textLen = this.text.length;
|
||||||
|
for (let i = 0; i < textLen; i++) {
|
||||||
|
textStyleArr[i] = '';
|
||||||
|
}
|
||||||
|
// 所有范围值拆分到每个字
|
||||||
|
let rangeLen = styleRangeNew.length;
|
||||||
|
for (let i = 0; i < rangeLen; i++) {
|
||||||
|
let item = styleRangeNew[i];
|
||||||
|
for (let j = item.start; j < item.end && j < textLen; j++) {
|
||||||
|
textStyleArr[j] += ' ' + item.cls;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 去重再排序,获取排序后的字符串
|
||||||
|
for (let i = 0; i < textLen; i++) {
|
||||||
|
if (!!textStyleArr[i]) {
|
||||||
|
let clsSet = new Set(textStyleArr[i].split(" "));
|
||||||
|
textStyleArr[i] = Array.from(clsSet).sort((val1, val2) => val1 - val2).join(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 合并同一个范围内样式表重叠的
|
||||||
|
let styleRangeMerged = [];
|
||||||
|
for (let i = 0; i < textLen; i++) {
|
||||||
|
if (!!textStyleArr[i]) {
|
||||||
|
let start = i;
|
||||||
|
for (let j = i + 1; j < textLen; j++, i++) {
|
||||||
|
if (textStyleArr[i] !== textStyleArr[j]) break;
|
||||||
|
}
|
||||||
|
styleRangeMerged.push(new StyleRange(start, i + 1, textStyleArr[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.styleRange = styleRangeMerged;
|
||||||
|
this.clearRange();
|
||||||
|
this.computerStyleRangeToDom();
|
||||||
|
},
|
||||||
|
computerStyleRangeToDom() {
|
||||||
|
// 把样式表渲染为dom列表
|
||||||
|
this.dom = [];
|
||||||
|
let lastStart = 0;
|
||||||
|
for (let i = 0; i < this.styleRange.length; i++) {
|
||||||
|
let item = this.styleRange[i];
|
||||||
|
if (lastStart < item.start) {
|
||||||
|
this.dom.push(new Dom('', '', this.text.substring(lastStart, item.start)));
|
||||||
|
}
|
||||||
|
let text = this.text.substring(item.start, item.end);
|
||||||
|
this.dom.push(new Dom('', item.cls, text));
|
||||||
|
lastStart = item.end;
|
||||||
|
}
|
||||||
|
if (lastStart < this.text.length) {
|
||||||
|
this.dom.push(new Dom('', '', this.text.substring(lastStart, this.text.length)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clone() {
|
||||||
|
return new Dom(this.type, this.cls, this.text, this.styleRange);
|
||||||
|
},
|
||||||
|
// 回车事件处理
|
||||||
|
keyEnter(editorDom, editorRange, undoRedo) {
|
||||||
|
let nextText = '';
|
||||||
|
let oldText = this.text || '';
|
||||||
|
// 如果文字的中间位置点击,则把内容分割到两行
|
||||||
|
if (editorRange.startOffset < oldText.length) {
|
||||||
|
let beforeDom = this.clone();
|
||||||
|
this.text = oldText.substring(0, editorRange.startOffset);
|
||||||
|
undoRedo.execute(1, editIndex, beforeDom, this);
|
||||||
|
nextText = oldText.substring(editorRange.startOffset, oldText.length);
|
||||||
|
}
|
||||||
|
let editDomNode = toolbarCommon.getRootDom(this.target);
|
||||||
|
let editIndex = parseInt(editDomNode.getAttribute("index"));
|
||||||
|
let domNew = new Dom('text', this.cls, nextText);
|
||||||
|
editorDom.splice(editIndex + 1, 0, domNew);
|
||||||
|
undoRedo.execute(2, editIndex + 1, domNew, '');
|
||||||
|
return domNew;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Dom;
|
||||||
242
zyplayer-doc-ui/wiki-ui/src/components/editor3/util/dom.js.old
Normal file
242
zyplayer-doc-ui/wiki-ui/src/components/editor3/util/dom.js.old
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
// 构造函数
|
||||||
|
import TextStyle from "./textStyle";
|
||||||
|
|
||||||
|
function Dom(type = 'text', cls = '', text = '') {
|
||||||
|
this.type = type;
|
||||||
|
this.text = text;
|
||||||
|
this.target = '';
|
||||||
|
this.cls = cls;
|
||||||
|
this.clsSet = new Set(cls.split(" "));
|
||||||
|
this.startOffset = -1;
|
||||||
|
this.endOffset = -1;
|
||||||
|
this.dom = [];
|
||||||
|
this.textStyle = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原型
|
||||||
|
Dom.prototype = {
|
||||||
|
constructor: Dom,
|
||||||
|
// 删除class
|
||||||
|
removeClass(cls) {
|
||||||
|
if (!this.hasClass(cls)) return;
|
||||||
|
this.clsSet.delete(cls);
|
||||||
|
this.cls = Array.from(this.clsSet).join(" ");
|
||||||
|
},
|
||||||
|
hasClass(cls) {
|
||||||
|
return this.clsSet.has(cls);
|
||||||
|
},
|
||||||
|
addClass(cls) {
|
||||||
|
if (this.hasClass(cls)) return;
|
||||||
|
this.clsSet.add(cls);
|
||||||
|
this.cls = Array.from(this.clsSet).join(" ");
|
||||||
|
},
|
||||||
|
setOffset(start, end) {
|
||||||
|
this.startOffset = start;
|
||||||
|
this.endOffset = end;
|
||||||
|
},
|
||||||
|
setOffsetAll() {
|
||||||
|
this.startOffset = 0;
|
||||||
|
this.endOffset = this.text.length;
|
||||||
|
},
|
||||||
|
addText(startOffset, data) {
|
||||||
|
if (!data) return;
|
||||||
|
// todo 删除选中的内容
|
||||||
|
let textStyleNew = [];
|
||||||
|
let len = this.text.length;
|
||||||
|
if (len > 0) {
|
||||||
|
if (startOffset < this.text.length) {
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
if (i == startOffset) {
|
||||||
|
let styleCopy = this.textStyle[i];
|
||||||
|
for (let j = 0; j < data.length; j++) {
|
||||||
|
textStyleNew.push(styleCopy.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textStyleNew.push(this.textStyle[i]);
|
||||||
|
}
|
||||||
|
this.text = this.text.substring(0, startOffset) + data + this.text.substring(startOffset, this.text.length);
|
||||||
|
} else {
|
||||||
|
textStyleNew = textStyleNew.concat(this.textStyle);
|
||||||
|
let styleCopyNew = this.textStyle[this.textStyle.length - 1];
|
||||||
|
for (let j = 0; j < data.length; j++) {
|
||||||
|
textStyleNew.push(styleCopyNew.clone());
|
||||||
|
}
|
||||||
|
this.text = this.text + data;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let j = 0; j < data.length; j++) {
|
||||||
|
textStyleNew.push(new TextStyle());
|
||||||
|
}
|
||||||
|
this.text = data;
|
||||||
|
}
|
||||||
|
this.textStyle = textStyleNew;
|
||||||
|
this.computerTextStyleToDom();
|
||||||
|
},
|
||||||
|
addSelectionTextStyle(type, value) {
|
||||||
|
if (this.startOffset < 0 || this.endOffset < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i = this.startOffset; i < this.endOffset; i++) {
|
||||||
|
this.textStyle[i][type] = value;
|
||||||
|
}
|
||||||
|
this.startOffset = this.endOffset = -1;
|
||||||
|
this.computerTextStyleToDom();
|
||||||
|
},
|
||||||
|
computerTextStyleToDom() {
|
||||||
|
this.dom = [];
|
||||||
|
let len = this.text.length;
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
let startIndex = i;
|
||||||
|
for (let j = i + 1; j < len; j++, i++) {
|
||||||
|
if (this.textStyle[i].notEq(this.textStyle[j])) break;
|
||||||
|
}
|
||||||
|
let text = this.text.substring(startIndex, i + 1);
|
||||||
|
this.dom.push(new Dom('', this.textStyle[i].getCls(), text));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
borderSelection1() {
|
||||||
|
this.removeClass('border');
|
||||||
|
if (this.startOffset == 0 && this.endOffset > 0) {
|
||||||
|
if (this.endOffset == this.text.length) {
|
||||||
|
// 全选
|
||||||
|
this.addClass('border');
|
||||||
|
} else {
|
||||||
|
// 选中前面的部分 xx-xxxxxx-xx
|
||||||
|
if (this.dom.length > 0) {
|
||||||
|
// 有元素的时候
|
||||||
|
let len = 0;
|
||||||
|
let newDomArr = [];
|
||||||
|
this.dom.forEach(item => {
|
||||||
|
let domSub = false;
|
||||||
|
let textLen = item.text.length;
|
||||||
|
if (len < this.endOffset) {
|
||||||
|
if (!this.hasClass('border') && !item.hasClass('border')) {
|
||||||
|
if (len + textLen <= this.endOffset) {
|
||||||
|
item.addClass('border');
|
||||||
|
} else {
|
||||||
|
// 再次拆分
|
||||||
|
domSub = true;
|
||||||
|
let textEnd = this.endOffset - len;
|
||||||
|
newDomArr.push(new Dom('', item.cls + ' border', item.text.substring(0, textEnd)));
|
||||||
|
newDomArr.push(new Dom('', item.cls, item.text.substring(textEnd, textLen)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!domSub) {
|
||||||
|
newDomArr.push(item);
|
||||||
|
}
|
||||||
|
len += textLen;
|
||||||
|
});
|
||||||
|
this.dom = newDomArr;
|
||||||
|
} else {
|
||||||
|
let newDom = new Dom('', 'border', this.text.substring(0, this.endOffset));
|
||||||
|
let lastDom = new Dom('', '', this.text.substring(this.endOffset, this.text.length));
|
||||||
|
this.dom = [newDom, lastDom];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this.startOffset > 0) {
|
||||||
|
if (this.dom.length > 0) {
|
||||||
|
// 有元素的时候
|
||||||
|
if (this.endOffset == this.text.length) {
|
||||||
|
// 选中后面的部分 xx-xxxxxx-xx
|
||||||
|
let len = 0;
|
||||||
|
let newDomArr = [];
|
||||||
|
this.dom.forEach(item => {
|
||||||
|
let domSub = false;
|
||||||
|
let textLen = item.text.length;
|
||||||
|
if (len + textLen > this.startOffset) {
|
||||||
|
if (!this.hasClass('border') && !item.hasClass('border')) {
|
||||||
|
if (len == this.startOffset) {
|
||||||
|
item.addClass('border');
|
||||||
|
} else {
|
||||||
|
// 再次拆分
|
||||||
|
domSub = true;
|
||||||
|
let textStart = this.startOffset - len;
|
||||||
|
newDomArr.push(new Dom('', item.cls, item.text.substring(0, textStart)));
|
||||||
|
newDomArr.push(new Dom('', item.cls + ' border', item.text.substring(textStart, textLen)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!domSub) {
|
||||||
|
newDomArr.push(item);
|
||||||
|
}
|
||||||
|
len += textLen;
|
||||||
|
});
|
||||||
|
this.dom = newDomArr;
|
||||||
|
} else {
|
||||||
|
// 选中中间一部分 xx xx-xxxxxx-xx
|
||||||
|
let len = 0;
|
||||||
|
let newDomArr = [];
|
||||||
|
this.dom.forEach(item => {
|
||||||
|
let domSub = false;
|
||||||
|
let textLen = item.text.length;
|
||||||
|
len += textLen;
|
||||||
|
if (len > this.startOffset && len <= this.endOffset) {
|
||||||
|
if (!this.hasClass('border') && !item.hasClass('border')) {
|
||||||
|
let textEnd = textLen - (len - this.endOffset);
|
||||||
|
if (len - textLen <= this.startOffset) {
|
||||||
|
if (this.endOffset >= len) {
|
||||||
|
// 全选
|
||||||
|
item.addClass('border');
|
||||||
|
} else {
|
||||||
|
// 选择前面
|
||||||
|
domSub = true;
|
||||||
|
newDomArr.push(new Dom('', item.cls + ' border', item.text.substring(0, textEnd)));
|
||||||
|
newDomArr.push(new Dom('', item.cls, item.text.substring(textEnd, textLen)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
domSub = true;
|
||||||
|
let textStart = len - this.startOffset;
|
||||||
|
newDomArr.push(new Dom('', item.cls, item.text.substring(0, textStart)));
|
||||||
|
if (this.endOffset >= len) {
|
||||||
|
// 选择后面
|
||||||
|
newDomArr.push(new Dom('', item.cls + ' border', item.text.substring(textStart, textLen)));
|
||||||
|
} else {
|
||||||
|
// 选择中间
|
||||||
|
newDomArr.push(new Dom('', 'border', item.text.substring(textStart, textEnd)));
|
||||||
|
newDomArr.push(new Dom('', '', item.text.substring(textEnd, textLen)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!domSub) {
|
||||||
|
newDomArr.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.dom = newDomArr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 空的时候
|
||||||
|
this.dom.push(new Dom('', '', this.text.substring(0, this.startOffset)));
|
||||||
|
if (this.endOffset == this.text.length) {
|
||||||
|
// 选中后面的部分
|
||||||
|
this.dom.push(new Dom('', 'border', this.text.substring(this.startOffset, this.text.length)));
|
||||||
|
} else {
|
||||||
|
// 选中中间一部分
|
||||||
|
this.dom.push(new Dom('', 'border', this.text.substring(this.startOffset, this.endOffset)));
|
||||||
|
this.dom.push(new Dom('', '', this.text.substring(this.endOffset, this.text.length)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.startOffset = this.endOffset = -1;
|
||||||
|
},
|
||||||
|
// 回车事件处理
|
||||||
|
keyEnter(editorDom, editorRange) {
|
||||||
|
let nextText = '';
|
||||||
|
let oldText = this.text || '';
|
||||||
|
// 如果文字的中间位置点击,则把内容分割到两行
|
||||||
|
if (editorRange.startOffset < oldText.length) {
|
||||||
|
this.text = oldText.substring(0, editorRange.startOffset);
|
||||||
|
nextText = oldText.substring(editorRange.startOffset, oldText.length);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < editorDom.length; i++) {
|
||||||
|
if (this == editorDom[i]) {
|
||||||
|
editorDom.splice(i + 1, 0, new Dom('text', this.cls, nextText));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Dom;
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// 构造函数
|
||||||
|
function StyleRange(start, end, cls = '') {
|
||||||
|
cls = cls.trim();
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
this.cls = cls;
|
||||||
|
this.clsSet = new Set(cls.split(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原型
|
||||||
|
StyleRange.prototype = {
|
||||||
|
constructor: StyleRange,
|
||||||
|
// 删除class
|
||||||
|
removeClass(cls = '') {
|
||||||
|
cls = cls.trim();
|
||||||
|
if (!this.hasClass(cls)) return;
|
||||||
|
this.clsSet.delete(cls);
|
||||||
|
this.cls = Array.from(this.clsSet).join(" ");
|
||||||
|
},
|
||||||
|
hasClass(cls = '') {
|
||||||
|
cls = cls.trim();
|
||||||
|
return this.clsSet.has(cls);
|
||||||
|
},
|
||||||
|
addClass(cls = '') {
|
||||||
|
cls = cls.trim();
|
||||||
|
if (this.hasClass(cls)) return;
|
||||||
|
this.clsSet.add(cls);
|
||||||
|
this.cls = Array.from(this.clsSet).join(" ");
|
||||||
|
},
|
||||||
|
getSortClass() {
|
||||||
|
return Array.from(this.clsSet).sort((val1, val2) => val1 - val2).join(" ");
|
||||||
|
},
|
||||||
|
classSameAll(compare) {
|
||||||
|
if (compare.clsSet.size != this.clsSet.size) return false;
|
||||||
|
let values = Array.from(compare.clsSet);
|
||||||
|
for (let i = 0; i < values.length; i++) {
|
||||||
|
if (!this.clsSet.has(values[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StyleRange;
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// 构造函数
|
||||||
|
|
||||||
|
function TextStyle() {
|
||||||
|
this.hn = '';// hn 标题,1~9
|
||||||
|
this.strikeThrough = 0;// 删除线 1=有
|
||||||
|
this.backColor = '';// 背景色,颜色值
|
||||||
|
this.underline = 0;// 下划线 1=有
|
||||||
|
this.align = 0;// 对齐 1=左 2=中 3=右
|
||||||
|
this.bold = 0;// 加粗 1=有
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原型
|
||||||
|
TextStyle.prototype = {
|
||||||
|
constructor: TextStyle,
|
||||||
|
notEq(compare) {
|
||||||
|
return !this.eq(compare);
|
||||||
|
},
|
||||||
|
eq(compare) {
|
||||||
|
if (!compare) return false;
|
||||||
|
if (this == compare) return true;
|
||||||
|
return this.hn == compare.hn
|
||||||
|
&& this.strikeThrough == compare.strikeThrough
|
||||||
|
&& this.backColor == compare.backColor
|
||||||
|
&& this.underline == compare.underline
|
||||||
|
&& this.align == compare.align
|
||||||
|
&& this.bold == compare.bold;
|
||||||
|
},
|
||||||
|
getCls() {
|
||||||
|
let clsSet = new Set();
|
||||||
|
if (this.hn) clsSet.add('head').add('head-' + this.hn);
|
||||||
|
if (this.strikeThrough) clsSet.add('strikethrough');
|
||||||
|
if (this.backColor) clsSet.add('backcolor-' + this.backColor);
|
||||||
|
if (this.underline) clsSet.add('underline');
|
||||||
|
if (this.align) clsSet.add('align-' + this.align);
|
||||||
|
if (this.bold) clsSet.add('bold');
|
||||||
|
return Array.from(clsSet).join(" ");
|
||||||
|
},
|
||||||
|
clone() {
|
||||||
|
let result = new TextStyle();
|
||||||
|
result.hn = this.hn;// hn 标题,1~9
|
||||||
|
result.strikeThrough = this.strikeThrough;// 删除线 1=有
|
||||||
|
result.backColor = this.backColor;// 背景色,颜色值
|
||||||
|
result.underline = this.underline;// 下划线 1=有
|
||||||
|
result.align = this.align;// 对齐 1=左 2=中 3=右
|
||||||
|
result.bold = this.bold;// 加粗 1=有
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TextStyle;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// 构造函数
|
||||||
|
function UndoInfo(type = 1, index = -1, before = '', after = '') {
|
||||||
|
// 操作类型 1=修改 2=添加 3=删除
|
||||||
|
this.type = type;
|
||||||
|
// 数组下标
|
||||||
|
this.index = index;
|
||||||
|
// 修改前内容json
|
||||||
|
this.before = before;
|
||||||
|
// 修改后内容json
|
||||||
|
this.after = after;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原型
|
||||||
|
UndoInfo.prototype = {
|
||||||
|
constructor: UndoInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UndoInfo;
|
||||||
118
zyplayer-doc-ui/wiki-ui/src/components/editor3/util/undoRedo.js
Normal file
118
zyplayer-doc-ui/wiki-ui/src/components/editor3/util/undoRedo.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// 构造函数
|
||||||
|
|
||||||
|
import UndoInfo from "./undoInfo";
|
||||||
|
import Dom from "./dom";
|
||||||
|
import vue from '../../../main'
|
||||||
|
|
||||||
|
function UndoRedo(editorDom) {
|
||||||
|
this.editorDom = editorDom;
|
||||||
|
this.undoRedoList = [];
|
||||||
|
this.undoRedoIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原型
|
||||||
|
UndoRedo.prototype = {
|
||||||
|
constructor: UndoRedo,
|
||||||
|
execute(type, index, before, after) {
|
||||||
|
// 忽略后面的操作步骤
|
||||||
|
if (this.undoRedoIndex >= 0 && this.undoRedoIndex < this.undoRedoList.length - 1) {
|
||||||
|
this.undoRedoList.splice(this.undoRedoIndex, this.undoRedoList.length - this.undoRedoIndex);
|
||||||
|
}
|
||||||
|
// 最多保留50步
|
||||||
|
if (this.undoRedoList.length >= 50) {
|
||||||
|
this.undoRedoList.splice(0, 1);
|
||||||
|
}
|
||||||
|
// 处理下,只保留有用的字段
|
||||||
|
before = this.handleDomColumn(before);
|
||||||
|
after = this.handleDomColumn(after);
|
||||||
|
this.undoRedoList.push(new UndoInfo(type, index, before, after));
|
||||||
|
this.undoRedoIndex = this.undoRedoList.length - 1;
|
||||||
|
},
|
||||||
|
handleDomColumn(content) {
|
||||||
|
if (!content) return '';
|
||||||
|
let beforeObj = [];
|
||||||
|
if (content instanceof Array) {
|
||||||
|
content.forEach(item => {
|
||||||
|
beforeObj.push({type: item.type, cls: item.cls, text: item.text, styleRange: item.styleRange});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
beforeObj.push({type: content.type, cls: content.cls, text: content.text, styleRange: content.styleRange});
|
||||||
|
}
|
||||||
|
return JSON.stringify(beforeObj);
|
||||||
|
},
|
||||||
|
undo() {
|
||||||
|
if (this.undoRedoIndex >= this.undoRedoList.length) {
|
||||||
|
this.undoRedoIndex = this.undoRedoList.length - 1;
|
||||||
|
}
|
||||||
|
if (this.undoRedoIndex < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let undoInfo = this.undoRedoList[this.undoRedoIndex];
|
||||||
|
let changeContent = JSON.parse(undoInfo.before);
|
||||||
|
let undoIndex = undoInfo.index;
|
||||||
|
changeContent.forEach(item => {
|
||||||
|
this.undoObjDomToEditor(undoInfo, undoIndex, item);
|
||||||
|
undoIndex--;
|
||||||
|
});
|
||||||
|
this.undoRedoIndex = Math.max(this.undoRedoIndex - 1, -1);
|
||||||
|
},
|
||||||
|
redo() {
|
||||||
|
this.undoRedoIndex++;
|
||||||
|
if (this.undoRedoIndex < 0 || this.undoRedoIndex >= this.undoRedoList.length) {
|
||||||
|
this.undoRedoIndex--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let undoInfo = this.undoRedoList[this.undoRedoIndex];
|
||||||
|
let actionText = (undoInfo.type == 1) ? undoInfo.after : undoInfo.before;
|
||||||
|
let changeContent = JSON.parse(actionText);
|
||||||
|
let undoIndex = undoInfo.index;
|
||||||
|
changeContent.forEach(item => {
|
||||||
|
this.redoObjDomToEditor(undoInfo, undoIndex, item);
|
||||||
|
undoIndex++;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
redoObjDomToEditor(undoInfo, undoIndex, domObj) {
|
||||||
|
let dom = new Dom(domObj.type, domObj.cls, domObj.text, domObj.styleRange);
|
||||||
|
if (undoInfo.type == 1) {
|
||||||
|
// 1=修改 2=添加 3=删除
|
||||||
|
if (this.editorDom.length > undoIndex) {
|
||||||
|
vue.$set(this.editorDom, undoIndex, dom);
|
||||||
|
}
|
||||||
|
} else if (undoInfo.type == 2) {
|
||||||
|
// 1=修改 2=添加 3=删除
|
||||||
|
if (this.editorDom.length == undoIndex) {
|
||||||
|
this.editorDom.push(dom);
|
||||||
|
} else if (this.editorDom.length > undoIndex) {
|
||||||
|
this.editorDom.splice(undoIndex, 0, dom);
|
||||||
|
}
|
||||||
|
} else if (undoInfo.type == 3) {
|
||||||
|
// 1=修改 2=添加 3=删除
|
||||||
|
if (this.editorDom.length > undoIndex) {
|
||||||
|
this.editorDom.splice(undoIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
undoObjDomToEditor(undoInfo, undoIndex, domObj) {
|
||||||
|
let dom = new Dom(domObj.type, domObj.cls, domObj.text, domObj.styleRange);
|
||||||
|
if (undoInfo.type == 1) {
|
||||||
|
// 1=修改 2=添加 3=删除
|
||||||
|
if (this.editorDom.length > undoIndex) {
|
||||||
|
vue.$set(this.editorDom, undoIndex, dom);
|
||||||
|
}
|
||||||
|
} else if (undoInfo.type == 2) {
|
||||||
|
// 1=修改 2=添加 3=删除
|
||||||
|
if (this.editorDom.length > undoIndex) {
|
||||||
|
this.editorDom.splice(undoIndex, 1);
|
||||||
|
}
|
||||||
|
} else if (undoInfo.type == 3) {
|
||||||
|
// 1=修改 2=添加 3=删除
|
||||||
|
if (this.editorDom.length == undoIndex) {
|
||||||
|
this.editorDom.push(dom);
|
||||||
|
} else if (this.editorDom.length > undoIndex) {
|
||||||
|
this.editorDom.splice(undoIndex, 0, dom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UndoRedo;
|
||||||
@@ -2,6 +2,8 @@ package com.zyplayer.doc.wiki.controller;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.zyplayer.doc.core.annotation.AuthMan;
|
import com.zyplayer.doc.core.annotation.AuthMan;
|
||||||
import com.zyplayer.doc.core.json.DocResponseJson;
|
import com.zyplayer.doc.core.json.DocResponseJson;
|
||||||
import com.zyplayer.doc.core.json.ResponseJson;
|
import com.zyplayer.doc.core.json.ResponseJson;
|
||||||
@@ -32,7 +34,6 @@ import java.io.FileInputStream;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,8 +68,10 @@ public class WikiCommonController {
|
|||||||
queryWrapper.like("user_name", search).or().like("user_no", search)
|
queryWrapper.like("user_name", search).or().like("user_no", search)
|
||||||
.or().like("email", search);
|
.or().like("email", search);
|
||||||
queryWrapper.select("id", "user_name");
|
queryWrapper.select("id", "user_name");
|
||||||
List<UserInfo> userInfoList = userInfoService.list(queryWrapper);
|
// 搜索最多返回20条
|
||||||
return DocResponseJson.ok(userInfoList);
|
IPage<UserInfo> page = new Page<>(1, 20, false);
|
||||||
|
userInfoService.page(page, queryWrapper);
|
||||||
|
return DocResponseJson.ok(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/file")
|
@GetMapping("/file")
|
||||||
|
|||||||
@@ -161,8 +161,7 @@ public class WikiPageController {
|
|||||||
@PostMapping("/changeParent")
|
@PostMapping("/changeParent")
|
||||||
public ResponseJson<Object> changeParent(WikiPage wikiPage, Integer beforeSeq, Integer afterSeq) {
|
public ResponseJson<Object> changeParent(WikiPage wikiPage, Integer beforeSeq, Integer afterSeq) {
|
||||||
DocUserDetails currentUser = DocUserUtil.getCurrentUser();
|
DocUserDetails currentUser = DocUserUtil.getCurrentUser();
|
||||||
Long id = wikiPage.getId();
|
WikiPage wikiPageSel = wikiPageService.getById(wikiPage.getId());
|
||||||
WikiPage wikiPageSel = wikiPageService.getById(id);
|
|
||||||
// 编辑权限判断
|
// 编辑权限判断
|
||||||
WikiSpace wikiSpaceSel = wikiSpaceService.getById(wikiPageSel.getSpaceId());
|
WikiSpace wikiSpaceSel = wikiSpaceService.getById(wikiPageSel.getSpaceId());
|
||||||
String canEdit = wikiPageAuthService.canEdit(wikiSpaceSel, wikiPageSel.getEditType(), wikiPageSel.getId(), currentUser.getUserId());
|
String canEdit = wikiPageAuthService.canEdit(wikiSpaceSel, wikiPageSel.getEditType(), wikiPageSel.getId(), currentUser.getUserId());
|
||||||
@@ -175,7 +174,7 @@ public class WikiPageController {
|
|||||||
wikiPageUp.setUpdateTime(new Date());
|
wikiPageUp.setUpdateTime(new Date());
|
||||||
wikiPageUp.setUpdateUserId(currentUser.getUserId());
|
wikiPageUp.setUpdateUserId(currentUser.getUserId());
|
||||||
wikiPageUp.setUpdateUserName(currentUser.getUsername());
|
wikiPageUp.setUpdateUserName(currentUser.getUsername());
|
||||||
wikiPageService.changeParent(wikiPage, beforeSeq, afterSeq);
|
wikiPageService.changeParent(wikiPageUp, beforeSeq, afterSeq);
|
||||||
return DocResponseJson.ok();
|
return DocResponseJson.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user