mirror of https://github.com/mindoc-org/mindoc.git
实现Markdown编辑器编辑合并功能
parent
92ab952f8b
commit
d8dd092f3e
|
@ -110,3 +110,11 @@ func (c *BaseController) ExecuteViewPathTemplate(tplName string,data interface{}
|
|||
func (c *BaseController) BaseUrl() string {
|
||||
return c.Ctx.Input.Scheme() + "://" + c.Ctx.Request.Host
|
||||
}
|
||||
|
||||
//显示错误信息页面.
|
||||
func (c *BaseController) ShowErrorPage(errCode int,errMsg string) {
|
||||
c.TplName = "errors/error.tpl"
|
||||
c.Data["ErrorMessage"] = errMsg
|
||||
c.Data["ErrorCode"] = errCode
|
||||
c.StopRun()
|
||||
}
|
|
@ -1081,6 +1081,60 @@ func (c *DocumentController) RestoreHistory() {
|
|||
func (c *DocumentController) Compare() {
|
||||
c.Prepare()
|
||||
c.TplName = "document/compare.tpl"
|
||||
history_id ,_ := strconv.Atoi(c.Ctx.Input.Param(":id"))
|
||||
identify := c.Ctx.Input.Param(":key")
|
||||
|
||||
book_id := 0
|
||||
editor := "markdown"
|
||||
|
||||
//如果是超级管理员则忽略权限判断
|
||||
if c.Member.IsAdministrator() {
|
||||
book, err := models.NewBook().FindByFieldFirst("identify", identify)
|
||||
if err != nil {
|
||||
beego.Error("DocumentController.Compare => ", err)
|
||||
c.Abort("403")
|
||||
return
|
||||
}
|
||||
book_id = book.BookId
|
||||
c.Data["Model"] = book
|
||||
editor = book.Editor
|
||||
} else {
|
||||
bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
|
||||
|
||||
if err != nil || bookResult.RoleId == conf.BookObserver {
|
||||
beego.Error("FindByIdentify => ", err)
|
||||
c.Abort("403")
|
||||
return
|
||||
}
|
||||
book_id = bookResult.BookId
|
||||
c.Data["Model"] = bookResult
|
||||
editor = bookResult.Editor
|
||||
}
|
||||
|
||||
if history_id <= 0 {
|
||||
c.ShowErrorPage(60002,"参数错误")
|
||||
}
|
||||
|
||||
history,err := models.NewDocumentHistory().Find(history_id)
|
||||
if err != nil {
|
||||
beego.Error("DocumentController.Compare => ",err)
|
||||
c.ShowErrorPage(60003,err.Error())
|
||||
}
|
||||
doc,err := models.NewDocument().Find(history.DocumentId)
|
||||
|
||||
if doc.BookId != book_id {
|
||||
c.ShowErrorPage(60002,"参数错误")
|
||||
}
|
||||
c.Data["HistoryId"] = history_id
|
||||
c.Data["DocumentId"] = doc.DocumentId
|
||||
|
||||
if editor == "markdown" {
|
||||
c.Data["HistoryContent"] = history.Markdown
|
||||
c.Data["Content"] = doc.Markdown
|
||||
}else{
|
||||
c.Data["HistoryContent"] = template.HTML(history.Content)
|
||||
c.Data["Content"] = template.HTML(doc.Content)
|
||||
}
|
||||
}
|
||||
|
||||
//递归生成文档序列数组.
|
||||
|
|
|
@ -50,8 +50,11 @@ func (m *DocumentHistory) TableNameWithPrefix() string {
|
|||
func NewDocumentHistory() *DocumentHistory {
|
||||
return &DocumentHistory{}
|
||||
}
|
||||
func (m *DocumentHistory) Find() {
|
||||
func (m *DocumentHistory) Find(id int) (*DocumentHistory,error) {
|
||||
o := orm.NewOrm()
|
||||
err := o.QueryTable(m.TableNameWithPrefix()).Filter("history_id",id).One(m)
|
||||
|
||||
return m,err
|
||||
}
|
||||
//清空指定文档的历史.
|
||||
func (m *DocumentHistory) Clear(doc_id int) error {
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noto Sans'), local('NotoSans'), url(notosans/v6/C7bP6N8yXZ-PGLzbFLtQKRJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noto Sans'), local('NotoSans'), url(notosans/v6/iLJc6PpCnnbQjYc1Jq4v0xJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* devanagari */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noto Sans'), local('NotoSans'), url(notosans/v6/5pCv5Yz4eMu9gmvX8nNhfRJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+02BC, U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200B-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noto Sans'), local('NotoSans'), url(notosans/v6/gEkd0pn-sMtQ_P4HUpi6WBJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noto Sans'), local('NotoSans'), url(notosans/v6/iPF-u8L1qkTPHaKjvXERnxJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noto Sans'), local('NotoSans'), url(notosans/v6/mTzVK0-EJOCaJiOPeaz-hxJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noto Sans'), local('NotoSans'), url(notosans/v6/erE3KsIWUumgD1j_Ca-V-xJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noto Sans'), local('NotoSans'), url(notosans/v6/LeFlHvsZjXu2c3ZRgBq9nFtXRa8TVwTICgirnJhmVJw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noto Sans Bold'), local('NotoSans-Bold'), url(notosans/v6/PIbvSEyHEdL91QLOQRnZ16-j2U0lmluP9RWlSytm3ho.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noto Sans Bold'), local('NotoSans-Bold'), url(notosans/v6/PIbvSEyHEdL91QLOQRnZ15X5f-9o1vgP2EXwfjgl7AY.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* devanagari */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noto Sans Bold'), local('NotoSans-Bold'), url(notosans/v6/PIbvSEyHEdL91QLOQRnZ10Tj6bCwSDA5u__Fbjwz3f0.woff2) format('woff2');
|
||||
unicode-range: U+02BC, U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200B-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noto Sans Bold'), local('NotoSans-Bold'), url(notosans/v6/PIbvSEyHEdL91QLOQRnZ1xWV49_lSm1NYrwo-zkhivY.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noto Sans Bold'), local('NotoSans-Bold'), url(notosans/v6/PIbvSEyHEdL91QLOQRnZ16aRobkAwv3vxw3jMhVENGA.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noto Sans Bold'), local('NotoSans-Bold'), url(notosans/v6/PIbvSEyHEdL91QLOQRnZ1_8zf_FOSsgRmwsS7Aa9k2w.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noto Sans Bold'), local('NotoSans-Bold'), url(notosans/v6/PIbvSEyHEdL91QLOQRnZ1z0LW-43aMEzIO6XUTLjad8.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Noto Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noto Sans Bold'), local('NotoSans-Bold'), url(notosans/v6/PIbvSEyHEdL91QLOQRnZ1-gdm0LZdjqr5-oayXSOefg.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -232,12 +232,14 @@ function formatBytes($size) {
|
|||
function uploadImage($id,$callback) {
|
||||
/** 粘贴上传图片 **/
|
||||
document.getElementById($id).addEventListener('paste', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var clipboard = e.clipboardData;
|
||||
for (var i = 0, len = clipboard.items.length; i < len; i++) {
|
||||
if (clipboard.items[i].kind === 'file' || clipboard.items[i].type.indexOf('image') > -1) {
|
||||
|
||||
var imageFile = clipboard.items[i].getAsFile();
|
||||
|
||||
console.log(imageFile)
|
||||
var fileName = Date.parse(new Date());
|
||||
|
||||
switch (imageFile.type){
|
||||
|
@ -277,6 +279,7 @@ function uploadImage($id,$callback) {
|
|||
|
||||
}
|
||||
});
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -97,17 +97,20 @@ $(document).ready(function() {
|
|||
height: 'auto',
|
||||
cmsettings: {
|
||||
lineNumbers: true,
|
||||
readOnly: isSample
|
||||
readOnly: false
|
||||
}
|
||||
});
|
||||
if (parameters.get('lhs', null)) {
|
||||
var url = parameters.get('lhs');
|
||||
crossdomainGET(ed, 'lhs', url);
|
||||
}
|
||||
if (parameters.get('rhs', null)) {
|
||||
var url = parameters.get('rhs');
|
||||
crossdomainGET(ed, 'rhs', url);
|
||||
}
|
||||
|
||||
ed.mergely("lhs", $("#historyContent").html());
|
||||
ed.mergely("rhs", $("#documentContent").html());
|
||||
// if (parameters.get('lhs', null)) {
|
||||
// var url = parameters.get('lhs');
|
||||
// crossdomainGET(ed, 'lhs', url);
|
||||
// }
|
||||
// if (parameters.get('rhs', null)) {
|
||||
// var url = parameters.get('rhs');
|
||||
// crossdomainGET(ed, 'rhs', url);
|
||||
// }
|
||||
|
||||
// set query string options
|
||||
var urloptions = {};
|
||||
|
@ -161,7 +164,7 @@ $(document).ready(function() {
|
|||
});
|
||||
|
||||
// Load
|
||||
if (key.length == 8) {
|
||||
if (key.length === 8) {
|
||||
$.when(
|
||||
$.ajax({
|
||||
type: 'GET', async: true, dataType: 'text',
|
||||
|
@ -301,29 +304,22 @@ $(document).ready(function() {
|
|||
if (id == 'file-new') {
|
||||
window.location = '/editor';
|
||||
}
|
||||
else if (id == 'file-save') {
|
||||
// download directly from browser
|
||||
var text = ed.mergely('diff');
|
||||
if (navigator.userAgent.toLowerCase().indexOf('msie') === -1) {
|
||||
if (key == '') key = ''.random(8);
|
||||
var link = jQuery('<a />', {
|
||||
href: 'data:application/stream;base64,' + window.btoa(unescape(encodeURIComponent(text))),
|
||||
target: '_blank',
|
||||
text: 'clickme',
|
||||
id: key
|
||||
});
|
||||
link.attr('download', key + '.diff');
|
||||
jQuery('body').append(link);
|
||||
var a = $('a#' + key);
|
||||
a[0].click();
|
||||
a.remove();
|
||||
else if (id === 'file-save') {
|
||||
var rhs = ed.mergely('get', 'rhs');
|
||||
|
||||
if(window.top.hasOwnProperty("editor")){
|
||||
if(window.top.editor.hasOwnProperty("$txt")){
|
||||
window.top.editor.$txt.html(rhs);
|
||||
}else{
|
||||
|
||||
window.top.editor.clear();
|
||||
window.top.editor.insertValue(rhs);
|
||||
}
|
||||
else {
|
||||
var blob = new Blob([text]);
|
||||
window.navigator.msSaveOrOpenBlob(blob, key + '.diff');
|
||||
|
||||
window.top.layer.closeAll();
|
||||
}
|
||||
}
|
||||
else if (id == 'file-share') {
|
||||
|
||||
}else if (id == 'file-share') {
|
||||
handleShare(ed);
|
||||
}
|
||||
else if (id == 'file-import') {
|
||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 491 KiB |
Binary file not shown.
Before Width: | Height: | Size: 193 KiB |
Binary file not shown.
Before Width: | Height: | Size: 167 KiB |
Binary file not shown.
Before Width: | Height: | Size: 275 KiB |
|
@ -4,12 +4,8 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>文档比较 - Powered by MinDoc</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||
<meta name="description" content="Merge and Diff your documents with diff online and share" />
|
||||
<meta name="keywords" content="diff,merge,compare,jsdiff,comparison,difference,file,text,unix,patch,algorithm,saas,longest common subsequence,diff online" />
|
||||
<meta name="author" content="Jamie Peabody" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<link href='http://fonts.googleapis.com/css?family=Noto+Sans:400,700' rel='stylesheet' type='text/css' />
|
||||
<link href="/static/fonts/notosans.css" rel='stylesheet' type='text/css' />
|
||||
<script type="text/javascript" src="/static/jquery/1.12.4/jquery.min.js"></script>
|
||||
<link type='text/css' rel='stylesheet' href='/static/mergely/editor/lib/wicked-ui.css' />
|
||||
<script type="text/javascript" src="/static/mergely/editor/lib/wicked-ui.js"></script>
|
||||
|
@ -20,25 +16,43 @@
|
|||
<link type="text/css" rel="stylesheet" href="/static/mergely/editor/lib/farbtastic/farbtastic.css" />
|
||||
<script type="text/javascript" src="/static/mergely/lib/codemirror.min.js"></script>
|
||||
<script type="text/javascript" src="/static/mergely/lib/mergely.min.js"></script>
|
||||
<script type="text/javascript" src="/static/mergely/editor/editor.min.js"></script>
|
||||
<script type="text/javascript" src="/static/mergely/editor/editor.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="/static/mergely/lib/codemirror.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/mergely/lib/mergely.css" />
|
||||
<link type='text/css' rel='stylesheet' href='/static/mergely/editor/editor.css' />
|
||||
<script type="text/javascript" src="/static/mergely/lib/searchcursor.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var key = '';
|
||||
var isSample = key == 'usaindep';
|
||||
// var isSample = key === 'usaindep';
|
||||
</script>
|
||||
|
||||
|
||||
</head>
|
||||
<body style="visibility:hidden">
|
||||
<!-- toolbar -->
|
||||
<ul id="toolbar">
|
||||
<li id="tb-file-save" data-icon="icon-save" title="保存">保存合并</li>
|
||||
<li class="separator"></li>
|
||||
<li id="tb-view-change-prev" data-icon="icon-arrow-up" title="上一处差异">上一处差异</li>
|
||||
<li id="tb-view-change-next" data-icon="icon-arrow-down" title="下一处差异">下一处差异</li>
|
||||
<li class="separator"></li>
|
||||
<li id="tb-edit-right-merge-left" data-icon="icon-arrow-left-v" title="合并到左侧">合并到左侧</li>
|
||||
<li id="tb-edit-left-merge-right" data-icon="icon-arrow-right-v" title="合并到右侧">合并到右侧</li>
|
||||
<li id="tb-view-swap" data-icon="icon-swap" title="左右切换">左右切换</li>
|
||||
</ul>
|
||||
|
||||
<!-- find -->
|
||||
<div class="find">
|
||||
<input type="text" placeholder="请输入关键字"/>
|
||||
<button class="find-prev"><span class="icon icon-arrow-up"></span></button>
|
||||
<button class="find-next"><span class="icon icon-arrow-down"></span></button>
|
||||
<button class="find-close"><span class="icon icon-x-mark"></span></button>
|
||||
</div>
|
||||
<!-- editor -->
|
||||
<div style="position: absolute;top: 0px;bottom: 10px;left: 5px;right: 5px;overflow-y: hidden;padding-bottom: 2px;">
|
||||
<div style="position: absolute;top: 33px;bottom: 10px;left: 5px;right: 5px;overflow-y: hidden;padding-bottom: 2px;">
|
||||
<div id="mergely"></div>
|
||||
</div>
|
||||
<template id="historyContent">{{.HistoryContent}}</template>
|
||||
<template id="documentContent">{{.Content}}</template>
|
||||
<script type="text/javascript" src="/static/layer/layer.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -52,6 +52,11 @@
|
|||
<button class="btn btn-success btn-sm restore-btn" data-id="{{$item.HistoryId}}" data-loading-text="恢复中...">
|
||||
恢复
|
||||
</button>
|
||||
{{if eq $.Model.Editor "markdown"}}
|
||||
<button class="btn btn-success btn-sm compare-btn" data-id="{{$item.HistoryId}}">
|
||||
合并
|
||||
</button>
|
||||
{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
|
@ -128,6 +133,18 @@
|
|||
})
|
||||
}
|
||||
});
|
||||
$(".compare-btn").on("click",function () {
|
||||
var historyId = $(this).attr("data-id");
|
||||
|
||||
window.compareIndex = window.top.layer.open({
|
||||
type: 2,
|
||||
title: '文档比较【左侧为历史文档,右侧为当前文档,请将文档合并到右侧】',
|
||||
shade: 0.8,
|
||||
area: ['380px', '90%'],
|
||||
content: "{{urlfor "DocumentController.Compare" ":key" .Model.Identify ":id" ""}}" + historyId
|
||||
});
|
||||
window.top.layer.full(window.compareIndex);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
Loading…
Reference in New Issue