实现文档历史功能

pull/51/head
Minho 2017-05-25 15:19:17 +08:00
parent 2b1ba118c4
commit 1fdb2c5df6
16 changed files with 491 additions and 190 deletions

View File

@ -8,6 +8,7 @@ import (
"github.com/astaxie/beego/orm" "github.com/astaxie/beego/orm"
"github.com/lifei6671/godoc/conf" "github.com/lifei6671/godoc/conf"
"github.com/lifei6671/godoc/models" "github.com/lifei6671/godoc/models"
"io/ioutil"
) )
//系统安装. //系统安装.
@ -32,26 +33,15 @@ func initialization() {
o := orm.NewOrm() o := orm.NewOrm()
_, err := o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '是否启用注册','ENABLED_REGISTER','false' WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLED_REGISTER');`).Exec() b,err := ioutil.ReadFile("./data/data.sql")
if err != nil { if err != nil {
panic("ENABLED_REGISTER => " + err.Error()) panic(err.Error())
os.Exit(1) os.Exit(1)
} }
_, err = o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '是否启用验证码','ENABLED_CAPTCHA','false' WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLED_CAPTCHA');`).Exec() sql := string(b)
if err != nil {
panic("ENABLED_CAPTCHA => " + err.Error())
os.Exit(1)
}
_, err = o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '启用匿名访问','ENABLE_ANONYMOUS','true' WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLE_ANONYMOUS');`).Exec()
if err != nil {
panic("ENABLE_ANONYMOUS => " + err.Error())
os.Exit(1)
}
_, err = o.Raw(`INSERT INTO md_options (option_title, option_name, option_value) SELECT '站点名称','SITE_NAME','MinDoc' WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'SITE_NAME');`).Exec()
_,err = o.Raw(sql).Exec()
if err != nil { if err != nil {
panic("SITE_NAME => " + err.Error()) panic("SITE_NAME => " + err.Error())
os.Exit(1) os.Exit(1)

View File

@ -9,12 +9,20 @@ import (
"github.com/astaxie/beego" "github.com/astaxie/beego"
"github.com/lifei6671/godoc/conf" "github.com/lifei6671/godoc/conf"
"github.com/astaxie/beego/orm"
) )
//系统升级. //系统升级.
func Update() { func Update() {
if len(os.Args) >= 2 && os.Args[1] == "update" { if len(os.Args) >= 2 && os.Args[1] == "update" {
adapter := beego.AppConfig.String("db_adapter")
if adapter == "mysql" {
mysqlUpdate()
}else if adapter == "sqlite3" {
sqliteUpdate()
}
fmt.Println("update successed.") fmt.Println("update successed.")
os.Exit(0) os.Exit(0)
@ -55,3 +63,41 @@ func CheckUpdate() {
os.Exit(0) os.Exit(0)
} }
} }
//MySQL 数据库更新表结构.
func mysqlUpdate() {
sql := `
IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=CurrentDatabase AND table_name = 'md_members' AND column_name = 'auth_method') THEN
ALTER TABLE md_members ADD auth_method VARCHAR(50) DEFAULT 'local' NULL;
END IF; `
o := orm.NewOrm()
_,err := o.Raw(sql).Exec()
if err != nil {
panic(fmt.Sprintf("error : 6001 => %s",err.Error()))
os.Exit(1)
}
}
//sqlite 数据库更新表结构.
func sqliteUpdate() {
o := orm.NewOrm()
var sqlite_master struct{
Name string
}
err := o.Raw("select * from sqlite_master where name='md_members' and sql like '%auth_method%' limit 1").QueryRow(&sqlite_master)
//查询是否已经存在 auth_method 列
if err == nil && sqlite_master.Name == ""{
_,err = o.Raw("ALTER TABLE md_members ADD auth_method VARCHAR(50) DEFAULT 'local' NULL;").Exec()
if err != nil {
panic(fmt.Sprintf("error : 6001 => %s",err.Error()))
os.Exit(1)
}
}
os.Exit(1)
}

View File

@ -891,11 +891,83 @@ func (c *DocumentController) Search() {
//文档历史列表. //文档历史列表.
func (c *DocumentController) History() { func (c *DocumentController) History() {
c.Prepare() c.Prepare()
c.TplName = "document/history.tpl"
identify := c.GetString("identify") identify := c.GetString("identify")
doc_id, err := c.GetInt("doc_id", 0) doc_id, err := c.GetInt("doc_id", 0)
pageIndex, _ := c.GetInt("page", 1) pageIndex, _ := c.GetInt("page", 1)
book_id := 0
//如果是超级管理员则忽略权限判断
if c.Member.Role == conf.MemberSuperRole {
book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil {
beego.Error("FindByIdentify => ", err)
c.Data["ErrorMessage"] = "项目不存在或权限不足"
return
}
book_id = book.BookId
c.Data["Model"] = book
} else {
bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
if err != nil || bookResult.RoleId == conf.BookObserver {
beego.Error("FindByIdentify => ", err)
c.Data["ErrorMessage"] = "项目不存在或权限不足"
return
}
book_id = bookResult.BookId
c.Data["Model"] = bookResult
}
if doc_id <= 0 {
c.Data["ErrorMessage"] = "参数错误"
return
}
doc, err := models.NewDocument().Find(doc_id)
if err != nil {
beego.Error("Delete => ", err)
c.Data["ErrorMessage"] = "获取历史失败"
return
}
//如果文档所属项目错误
if doc.BookId != book_id {
c.Data["ErrorMessage"] = "参数错误"
return
}
historis,totalCount,err := models.NewDocumentHistory().FindToPager(doc_id,pageIndex,conf.PageSize)
if err != nil {
beego.Error("FindToPager => ",err)
c.Data["ErrorMessage"] = "获取历史失败"
return
}
c.Data["List"] = historis
c.Data["PageHtml"] = ""
c.Data["Document"] = doc
if totalCount > 0 {
html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, conf.PageSize, totalCount)
c.Data["PageHtml"] =html
}
}
func (c *DocumentController) DeleteHistory() {
c.Prepare()
c.TplName = "document/history.tpl"
identify := c.GetString("identify")
doc_id, err := c.GetInt("doc_id", 0)
history_id,_ := c.GetInt("history_id",0)
if history_id <= 0 {
c.JsonResult(6001,"参数错误")
}
book_id := 0 book_id := 0
//如果是超级管理员则忽略权限判断 //如果是超级管理员则忽略权限判断
if c.Member.Role == conf.MemberSuperRole { if c.Member.Role == conf.MemberSuperRole {
@ -923,32 +995,70 @@ func (c *DocumentController) History() {
if err != nil { if err != nil {
beego.Error("Delete => ", err) beego.Error("Delete => ", err)
c.JsonResult(6003, "获取历史失败") c.JsonResult(6001,"获取历史失败")
} }
//如果文档所属项目错误 //如果文档所属项目错误
if doc.BookId != book_id { if doc.BookId != book_id {
c.JsonResult(6004, "参数错误") c.JsonResult(6001,"参数错误")
}
err = models.NewDocumentHistory().Delete(history_id,doc_id)
if err != nil {
beego.Error(err)
c.JsonResult(6002,"删除失败")
}
c.JsonResult(0,"ok")
} }
historis,totalCount,err := models.NewDocumentHistory().FindToPager(doc_id,pageIndex,conf.PageSize) func (c *DocumentController) RestoreHistory() {
c.Prepare()
c.TplName = "document/history.tpl"
identify := c.GetString("identify")
doc_id, err := c.GetInt("doc_id", 0)
history_id,_ := c.GetInt("history_id",0)
if history_id <= 0 {
c.JsonResult(6001,"参数错误")
}
book_id := 0
//如果是超级管理员则忽略权限判断
if c.Member.Role == conf.MemberSuperRole {
book, err := models.NewBook().FindByFieldFirst("identify", identify)
if err != nil {
beego.Error("FindByIdentify => ", err)
c.JsonResult(6002, "项目不存在或权限不足")
}
book_id = book.BookId
} else {
bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
if err != nil || bookResult.RoleId == conf.BookObserver {
beego.Error("FindByIdentify => ", err)
c.JsonResult(6002, "项目不存在或权限不足")
}
book_id = bookResult.BookId
}
if doc_id <= 0 {
c.JsonResult(6001,"参数错误")
}
doc, err := models.NewDocument().Find(doc_id)
if err != nil { if err != nil {
c.JsonResult(6005,"获取历史失败") beego.Error("Delete => ", err)
c.JsonResult(6001,"获取历史失败")
} }
var data struct { //如果文档所属项目错误
PageHtml string `json:"page_html"` if doc.BookId != book_id {
List []*models.DocumentHistorySimpleResult `json:"lists"` c.JsonResult(6001,"参数错误")
} }
data.List = historis err = models.NewDocumentHistory().Restore(history_id,doc_id,c.Member.MemberId)
if totalCount > 0 { if err != nil {
html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, conf.PageSize, totalCount) beego.Error(err)
c.JsonResult(6002,"删除失败")
data.PageHtml = string(html)
}else {
data.PageHtml = ""
} }
c.JsonResult(0,"ok",doc)
c.JsonResult(0,"ok",data)
} }
//递归生成文档序列数组. //递归生成文档序列数组.

17
data/data.sql 100644
View File

@ -0,0 +1,17 @@
INSERT INTO md_options (option_title, option_name, option_value) SELECT '是否启用注册','ENABLED_REGISTER','false' WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLED_REGISTER');
INSERT INTO md_options (option_title, option_name, option_value) SELECT '是否启用文档历史','ENABLE_DOCUMENT_HISTORY','true' WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLE_DOCUMENT_HISTORY');
INSERT INTO md_options (option_title, option_name, option_value) SELECT '是否启用验证码','ENABLED_CAPTCHA','false' WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLED_CAPTCHA');
INSERT INTO md_options (option_title, option_name, option_value) SELECT '启用匿名访问','ENABLE_ANONYMOUS','true' WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'ENABLE_ANONYMOUS');
INSERT INTO md_options (option_title, option_name, option_value) SELECT '站点名称','SITE_NAME','MinDoc' WHERE NOT exists(SELECT * FROM md_options WHERE option_name = 'SITE_NAME');

View File

@ -176,7 +176,6 @@ func (m *Book) FindToPager(pageIndex, pageSize ,memberId int) (books []*BookResu
Limit(pageSize). Limit(pageSize).
Offset(offset) Offset(offset)
logs.Info("",qb2.String())
_,err = o.Raw(qb2.String(),memberId).QueryRows(&books) _,err = o.Raw(qb2.String(),memberId).QueryRows(&books)
if err != nil { if err != nil {
logs.Error("分页查询项目列表 => ",err) logs.Error("分页查询项目列表 => ",err)

View File

@ -49,6 +49,9 @@ func (m *DocumentHistory) TableNameWithPrefix() string {
func NewDocumentHistory() *DocumentHistory { func NewDocumentHistory() *DocumentHistory {
return &DocumentHistory{} return &DocumentHistory{}
}
func (m *DocumentHistory) Find() {
} }
//清空指定文档的历史. //清空指定文档的历史.
func (m *DocumentHistory) Clear(doc_id int) error { func (m *DocumentHistory) Clear(doc_id int) error {
@ -60,18 +63,19 @@ func (m *DocumentHistory) Clear(doc_id int) error {
} }
//删除历史. //删除历史.
func (m *DocumentHistory) Delete(history_id int) error { func (m *DocumentHistory) Delete(history_id,doc_id int) error {
o := orm.NewOrm() o := orm.NewOrm()
_, err := o.Raw("DELETE md_document_history WHERE history_id = ?", history_id).Exec() _, err := o.QueryTable(m.TableNameWithPrefix()).Filter("history_id",history_id).Filter("document_id",doc_id).Delete()
return err return err
} }
//恢复指定历史的文档. //恢复指定历史的文档.
func (m *DocumentHistory) Restore(history_id int) error { func (m *DocumentHistory) Restore(history_id,doc_id,uid int) error {
o := orm.NewOrm() o := orm.NewOrm()
err := o.QueryTable(m.TableNameWithPrefix()).Filter("history_id", history_id).One(m) err := o.QueryTable(m.TableNameWithPrefix()).Filter("history_id", history_id).Filter("document_id",doc_id).One(m)
if err != nil { if err != nil {
return err return err
@ -81,10 +85,25 @@ func (m *DocumentHistory) Restore(history_id int) error {
if err != nil { if err != nil {
return err return err
} }
history := NewDocumentHistory()
history.DocumentId = doc_id
history.Content = doc.Content
history.Markdown = doc.Markdown
history.DocumentName = doc.DocumentName
history.ModifyAt = uid
history.MemberId = doc.MemberId
history.ParentId = doc.ParentId
history.Version = time.Now().Unix()
history.Action = "restore"
history.ActionName = "恢复文档"
history.InsertOrUpdate()
doc.DocumentName = m.DocumentName doc.DocumentName = m.DocumentName
doc.Content = m.Content doc.Content = m.Content
doc.Markdown = m.Markdown doc.Markdown = m.Markdown
doc.Release = m.Content doc.Release = m.Content
doc.Version = time.Now().Unix()
_, err = o.Update(doc) _, err = o.Update(doc)
@ -111,10 +130,10 @@ func (m *DocumentHistory) FindToPager(doc_id, page_index, page_size int) (docs [
totalCount = 0 totalCount = 0
sql := `SELECT history.*,m1.account,m2.account as ModifyName sql := `SELECT history.*,m1.account,m2.account as modify_name
FROM md_document_history AS history FROM md_document_history AS history
LEFT JOIN md_members AS m1 ON history.member_id = m1.member_id LEFT JOIN md_members AS m1 ON history.member_id = m1.member_id
LEFT JOIN md_members AS m2 ON history.member_id = m2.member_id LEFT JOIN md_members AS m2 ON history.modify_at = m2.member_id
WHERE history.document_id = ? ORDER BY history.history_id DESC LIMIT ?,?;` WHERE history.document_id = ? ORDER BY history.history_id DESC LIMIT ?,?;`
_, err = o.Raw(sql,doc_id,offset,page_size).QueryRows(&docs) _, err = o.Raw(sql,doc_id,offset,page_size).QueryRows(&docs)

View File

@ -22,7 +22,7 @@ type Member struct {
Account string `orm:"size(100);unique;column(account)" json:"account"` Account string `orm:"size(100);unique;column(account)" json:"account"`
Password string `orm:"size(1000);column(password)" json:"-"` Password string `orm:"size(1000);column(password)" json:"-"`
//认证方式: local 本地数据库 /ldap LDAP //认证方式: local 本地数据库 /ldap LDAP
AuthMethod string `orm:"size(10);column(auth_method);default(local)" json:"auth_method)"` AuthMethod string `orm:"column(auth_method);default(local);size(50);" json:"auth_method)"`
Description string `orm:"column(description);size(2000)" json:"description"` Description string `orm:"column(description);size(2000)" json:"description"`
Email string `orm:"size(100);column(email);unique" json:"email"` Email string `orm:"size(100);column(email);unique" json:"email"`
Phone string `orm:"size(255);column(phone);null;default(null)" json:"phone"` Phone string `orm:"size(255);column(phone);null;default(null)" json:"phone"`
@ -128,6 +128,8 @@ func (m *Member) ldapLogin(account string, password string) (*Member, error) {
m.AuthMethod = "ldap" m.AuthMethod = "ldap"
m.Avatar = "/static/images/headimgurl.jpg" m.Avatar = "/static/images/headimgurl.jpg"
m.Role = beego.AppConfig.DefaultInt("ldap_user_role", 2) m.Role = beego.AppConfig.DefaultInt("ldap_user_role", 2)
m.CreateTime = time.Now()
err = m.Add() err = m.Add()
if err != nil { if err != nil {
logs.Error("自动注册LDAP用户错误", err) logs.Error("自动注册LDAP用户错误", err)

View File

@ -57,8 +57,10 @@ func init() {
beego.Router("/api/:key/create",&controllers.DocumentController{},"post:Create") beego.Router("/api/:key/create",&controllers.DocumentController{},"post:Create")
beego.Router("/api/:key/delete", &controllers.DocumentController{},"post:Delete") beego.Router("/api/:key/delete", &controllers.DocumentController{},"post:Delete")
beego.Router("/api/:key/content/?:id",&controllers.DocumentController{},"*:Content") beego.Router("/api/:key/content/?:id",&controllers.DocumentController{},"*:Content")
beego.Router("/api/history", &controllers.DocumentController{},"get:History")
beego.Router("/history/get", &controllers.DocumentController{},"get:History")
beego.Router("/history/delete", &controllers.DocumentController{},"*:DeleteHistory")
beego.Router("/history/restore", &controllers.DocumentController{},"*:RestoreHistory")
beego.Router("/docs/:key", &controllers.DocumentController{},"*:Index") beego.Router("/docs/:key", &controllers.DocumentController{},"*:Index")
beego.Router("/docs/:key/:id", &controllers.DocumentController{},"*:Read") beego.Router("/docs/:key/:id", &controllers.DocumentController{},"*:Read")

View File

@ -200,30 +200,68 @@ function showSuccess($msg,$id) {
return true; return true;
} }
window.documentHistory = function() {
layer.open({
type: 2,
title: '历史版本',
shadeClose: true,
shade: 0.8,
area: ['700px','80%'],
content: window.historyURL + "?identify=" + window.book.identify + "&doc_id=" + window.selectNode.id,
end : function () {
if(window.SelectedId){
var selected = {node:{
id : window.SelectedId
}};
window.loadDocument(selected);
window.SelectedId = null;
}
}
});
};
$(function () { $(function () {
$("#documentHistoryModal").on("shown.bs.modal",function () { window.vueApp = new Vue({
var historyVue = new Vue({ el : "#attachList",
el : "#documentHistoryModal",
data : { data : {
lists : [] lists : []
}, },
delimiters : ['${','}'], delimiters : ['${','}'],
methods : { methods : {
removeAttach : function ($attach_id) {
} var $this = this;
var item = $this.lists.filter(function ($item) {
return $item.attachment_id == $attach_id;
}); });
if(item && item[0].hasOwnProperty("state")){
$this.lists = $this.lists.filter(function ($item) {
return $item.attachment_id != $attach_id;
});
return;
}
$.ajax({ $.ajax({
url : window.historyURL, url : window.removeAttachURL,
data : { "identify" : window.book.identify,"doc_id" : window.selectNode.id }, type : "post",
dataType :"json", data : { "attach_id" : $attach_id},
success : function (res) { success : function (res) {
console.log(res);
if(res.errcode === 0){ if(res.errcode === 0){
historyVue.lists = res.data.lists; $this.lists = $this.lists.filter(function ($item) {
return $item.attachment_id != $attach_id;
});
}else{ }else{
alert(res.message); layer.msg(res.message);
} }
} }
}); });
}
},
watch : {
lists : function ($lists) {
$("#attachInfo").text(" " + $lists.length + " 个附件")
}
}
}); });
}); });

View File

@ -9,24 +9,18 @@ $(function () {
"editor" : "wangEditor" "editor" : "wangEditor"
}; };
wangEditor.config.menus.splice(0,0,"|"); wangEditor.config.menus.splice(0,0,"|");
wangEditor.config.menus.splice(0,0,"history");
wangEditor.config.menus.splice(0,0,"save"); wangEditor.config.menus.splice(0,0,"save");
wangEditor.config.menus.splice(0,0,"release"); wangEditor.config.menus.splice(0,0,"release");
wangEditor.config.menus.splice(29,0,"attach") wangEditor.config.menus.splice(29,0,"attach")
//移除地图、背景色 //移除地图、背景色
editor.config.menus = $.map(wangEditor.config.menus, function(item, key) { editor.config.menus = $.map(wangEditor.config.menus, function(item, key) {
if (item === 'bgcolor') {
return null;
}
if (item === 'fullscreen') { if (item === 'fullscreen') {
return null; return null;
} }
if (item === "undo"){
return null;
}
if (item === "redo"){
return null;
}
return item; return item;
}); });

View File

@ -61,7 +61,7 @@ $(function () {
if(name === "attachment"){ if(name === "attachment"){
$("#uploadAttachModal").modal("show"); $("#uploadAttachModal").modal("show");
}else if(name === "history"){ }else if(name === "history"){
$("#documentHistoryModal").modal("show"); window.documentHistory();
}else if(name === "save"){ }else if(name === "save"){
saveDocument(false); saveDocument(false);
@ -126,7 +126,7 @@ $(function () {
* 加载指定的文档到编辑器中 * 加载指定的文档到编辑器中
* @param $node * @param $node
*/ */
function loadDocument($node) { window.loadDocument = function($node) {
var index = layer.load(1, { var index = layer.load(1, {
shade: [0.1,'#fff'] //0.1透明度的白色背景 shade: [0.1,'#fff'] //0.1透明度的白色背景
}); });
@ -175,7 +175,6 @@ $(function () {
if(item.id === doc_id){ if(item.id === doc_id){
version = item.version; version = item.version;
console.log(item)
break; break;
} }
} }

View File

@ -0,0 +1,47 @@
(function () {
// 获取 wangEditor 构造函数和 jquery
var E = window.wangEditor;
var $ = window.jQuery;
// 用 createMenu 方法创建菜单
E.createMenu(function (check) {
// 定义菜单id不要和其他菜单id重复。编辑器自带的所有菜单id可通过『参数配置-自定义菜单』一节查看
var menuId = 'history';
// check将检查菜单配置『参数配置-自定义菜单』一节描述中是否该菜单id如果没有则忽略下面的代码。
if (!check(menuId)) {
return;
}
// this 指向 editor 对象自身
var editor = this;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor, // 编辑器对象
id: menuId, // 菜单id
title: '历史', // 菜单标题
// 正常状态和选中状态下的dom对象样式需要自定义
$domNormal: $('<a href="#" tabindex="-1"><i class="fa fa-history" aria-hidden="true" name="history"></i></a>'),
$domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="fa fa-history" aria-hidden="true" name="history"></i></a>')
});
// 菜单正常状态下,点击将触发该事件
menu.clickEvent = function (e) {
window.documentHistory();
};
// 菜单选中状态下,点击将触发该事件
menu.clickEventSelected = function (e) {
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
})();

View File

@ -0,0 +1,134 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="/favicon.ico">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="SmartWiki" />
<title>历史版本 - Powered by MinDoc</title>
<!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="/static/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="/static/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="{{cdnjs "/static/jquery/1.12.4/jquery.min.js"}}"></script>
<style type="text/css">
.container{margin: 5px auto;}
</style>
</head>
<body>
<div class="container">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<td>#</td>
<td class="col-sm-6">修改时间</td>
<td class="col-sm-2">修改人</td>
<td class="col-sm=2">版本</td>
<td class="col-sm-2">操作</td>
</tr>
</thead>
<tbody>
{{range $index,$item := .List}}
<tr>
<td>{{$item.HistoryId}}</td>
<td>{{date $item.ModifyTime "Y-m-d H:i:s"}}</td>
<td>{{$item.ModifyName}}</td>
<td>{{$item.Version}}</td>
<td>
<button class="btn btn-danger btn-sm delete-btn" data-id="{{$item.HistoryId}}" data-loading-text="删除中...">
删除
</button>
<button class="btn btn-success btn-sm restore-btn" data-id="{{$item.HistoryId}}" data-loading-text="恢复中...">
恢复
</button>
</td>
</tr>
{{else}}
<tr>
<td colspan="6" class="text-center">暂无数据</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<nav>
{{.PageHtml}}
</nav>
</div>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}"></script>
<script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript" ></script>
<script type="text/javascript">
$(function () {
$(".delete-btn").on("click",function () {
var id = $(this).attr('data-id');
var $btn = $(this).button('loading');
var $then = $(this);
if(!id){
layer.msg('参数错误');
}else{
$.ajax({
url : "{{urlfor "DocumentController.DeleteHistory"}}",
type : "post",
dataType : "json",
data : { "identify" : "{{.Model.Identify}}","doc_id" : "{{.Document.DocumentId}}" ,"history_id" : id },
success :function (res) {
if(res.errcode === 0){
$then.parents('tr').remove().empty();
}else{
layer.msg(res.message);
}
},
error : function () {
$btn.button('reset');
}
})
}
});
$(".restore-btn").on("click",function () {
var id = $(this).attr('data-id');
var $btn = $(this).button('loading');
var $then = $(this);
var index = parent.layer.getFrameIndex(window.name);
if(!id){
layer.msg('参数错误');
}else{
$.ajax({
url : "{{urlfor "DocumentController.RestoreHistory"}}",
type : "post",
dataType : "json",
data : { "identify" : "{{.Model.Identify}}","doc_id" : "{{.Document.DocumentId}}" ,"history_id" : id },
success :function (res) {
if(res.errcode === 0){
var $node = { "node" : { "id" : res.data.doc_id}};
parent.loadDocument($node);
parent.layer.close(index);
}else{
layer.msg(res.message);
}
},
error : function () {
$btn.button('reset');
}
})
}
});
});
</script>
</body>
</html>

View File

@ -19,7 +19,8 @@
window.releaseURL = "{{urlfor "BookController.Release" ":key" .Model.Identify}}"; window.releaseURL = "{{urlfor "BookController.Release" ":key" .Model.Identify}}";
window.sortURL = "{{urlfor "BookController.SaveSort" ":key" .Model.Identify}}"; window.sortURL = "{{urlfor "BookController.SaveSort" ":key" .Model.Identify}}";
window.baiduMapKey = "{{.BaiDuMapKey}}"; window.baiduMapKey = "{{.BaiDuMapKey}}";
window.historyURL = "{{urlfor "DocumentController.History"}}";
window.removeAttachURL = "{{urlfor "DocumentController.RemoveAttachment"}}";
window.vueApp = null; window.vueApp = null;
</script> </script>
<!-- Bootstrap --> <!-- Bootstrap -->
@ -190,54 +191,10 @@
<script src="{{cdnjs "/static/wangEditor/plugins/save-menu.js"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/wangEditor/plugins/save-menu.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/wangEditor/plugins/release-menu.js"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/wangEditor/plugins/release-menu.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/wangEditor/plugins/attach-menu.js"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/wangEditor/plugins/attach-menu.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/wangEditor/plugins/history-menu.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript" ></script> <script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript" ></script>
<script src="{{cdnjs "/static/to-markdown/dist/to-markdown.js"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/to-markdown/dist/to-markdown.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/js/jquery.form.js"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/js/jquery.form.js"}}" type="text/javascript"></script>
<script type="text/javascript">
window.vueApp = new Vue({
el : "#attachList",
data : {
lists : []
},
delimiters : ['${','}'],
methods : {
removeAttach : function ($attach_id) {
var $this = this;
var item = $this.lists.filter(function ($item) {
return $item.attachment_id == $attach_id;
});
if(item && item[0].hasOwnProperty("state")){
$this.lists = $this.lists.filter(function ($item) {
return $item.attachment_id != $attach_id;
});
return;
}
$.ajax({
url : "{{urlfor "DocumentController.RemoveAttachment"}}",
type : "post",
data : { "attach_id" : $attach_id},
success : function (res) {
console.log(res);
if(res.errcode === 0){
$this.lists = $this.lists.filter(function ($item) {
return $item.attachment_id != $attach_id;
});
}else{
layer.msg(res.message);
}
}
});
}
},
watch : {
lists : function ($lists) {
$("#attachInfo").text(" " + $lists.length + " 个附件")
}
}
});
</script>
<script src="/static/js/editor.js" type="text/javascript"></script> <script src="/static/js/editor.js" type="text/javascript"></script>
<script src="/static/js/html-editor.js" type="text/javascript"></script> <script src="/static/js/html-editor.js" type="text/javascript"></script>
<script type="text/javascript"> <script type="text/javascript">

View File

@ -18,6 +18,7 @@
window.releaseURL = "{{urlfor "BookController.Release" ":key" .Model.Identify}}"; window.releaseURL = "{{urlfor "BookController.Release" ":key" .Model.Identify}}";
window.sortURL = "{{urlfor "BookController.SaveSort" ":key" .Model.Identify}}"; window.sortURL = "{{urlfor "BookController.SaveSort" ":key" .Model.Identify}}";
window.historyURL = "{{urlfor "DocumentController.History"}}"; window.historyURL = "{{urlfor "DocumentController.History"}}";
window.removeAttachURL = "{{urlfor "DocumentController.RemoveAttachment"}}";
</script> </script>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
@ -214,31 +215,8 @@
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">文档历史记录</h4> <h4 class="modal-title">文档历史记录</h4>
</div> </div>
<div class="modal-body text-center"> <div class="modal-body text-center" id="historyList">
<template v-if="lists.length <= 0">
暂无数据
</template>
<template v-else>
<table class="table">
<thead>
<tr>
<th>#</th><th>名称</th><th>修改时间</th><th>修改人</th><th>版本</th><th>操作</th>
</tr>
</thead>
<tbody>
<template v-for="item in lists">
<tr>
<td>${item.history_id}</td>
<td>${item.action_name}</td>
<td>${item.modify_time}</td>
<td>${item.modify_name}</td>
<td>${item.version}</td>
<td></td>
</tr>
</template>
</tbody>
</table>
</template>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
@ -256,48 +234,7 @@
<script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript" ></script> <script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript" ></script>
<script src="{{cdnjs "/static/js/jquery.form.js"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/js/jquery.form.js"}}" type="text/javascript"></script>
<script type="text/javascript"> <script type="text/javascript">
window.vueApp = new Vue({
el : "#attachList",
data : {
lists : []
},
delimiters : ['${','}'],
methods : {
removeAttach : function ($attach_id) {
var $this = this;
var item = $this.lists.filter(function ($item) {
return $item.attachment_id == $attach_id;
});
if(item && item[0].hasOwnProperty("state")){
$this.lists = $this.lists.filter(function ($item) {
return $item.attachment_id != $attach_id;
});
return;
}
$.ajax({
url : "{{urlfor "DocumentController.RemoveAttachment"}}",
type : "post",
data : { "attach_id" : $attach_id},
success : function (res) {
console.log(res);
if(res.errcode === 0){
$this.lists = $this.lists.filter(function ($item) {
return $item.attachment_id != $attach_id;
});
}else{
layer.msg(res.message);
}
}
});
}
},
watch : {
lists : function ($lists) {
$("#attachInfo").text(" " + $lists.length + " 个附件")
}
}
});
</script> </script>
<script src="/static/js/editor.js" type="text/javascript"></script> <script src="/static/js/editor.js" type="text/javascript"></script>

View File

@ -77,7 +77,17 @@
</label> </label>
</div> </div>
</div> </div>
<div class="form-group">
<label>启用文档历史</label>
<div class="radio">
<label class="radio-inline">
<input type="radio" {{if eq .ENABLE_DOCUMENT_HISTORY.OptionValue "true"}}checked{{end}} name="ENABLE_DOCUMENT_HISTORY" value="true">开启<span class="text"></span>
</label>
<label class="radio-inline">
<input type="radio" {{if eq .ENABLE_DOCUMENT_HISTORY.OptionValue "false"}}checked{{end}} name="ENABLE_DOCUMENT_HISTORY" value="false">关闭<span class="text"></span>
</label>
</div>
</div>
<div class="form-group"> <div class="form-group">
<button type="submit" id="btnSaveBookInfo" class="btn btn-success" data-loading-text="保存中...">保存修改</button> <button type="submit" id="btnSaveBookInfo" class="btn btn-success" data-loading-text="保存中...">保存修改</button>
<span id="form-error-message" class="error-message"></span> <span id="form-error-message" class="error-message"></span>