mirror of https://github.com/mindoc-org/mindoc.git
实现文档历史功能
parent
2b1ba118c4
commit
1fdb2c5df6
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/astaxie/beego/orm"
|
||||
"github.com/lifei6671/godoc/conf"
|
||||
"github.com/lifei6671/godoc/models"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
//系统安装.
|
||||
|
@ -32,26 +33,15 @@ func initialization() {
|
|||
|
||||
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 {
|
||||
panic("ENABLED_REGISTER => " + err.Error())
|
||||
panic(err.Error())
|
||||
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()
|
||||
|
||||
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()
|
||||
sql := string(b)
|
||||
|
||||
_,err = o.Raw(sql).Exec()
|
||||
if err != nil {
|
||||
panic("SITE_NAME => " + err.Error())
|
||||
os.Exit(1)
|
||||
|
|
|
@ -9,12 +9,20 @@ import (
|
|||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/lifei6671/godoc/conf"
|
||||
"github.com/astaxie/beego/orm"
|
||||
)
|
||||
|
||||
//系统升级.
|
||||
func 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.")
|
||||
|
||||
os.Exit(0)
|
||||
|
@ -55,3 +63,41 @@ func CheckUpdate() {
|
|||
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)
|
||||
}
|
|
@ -891,11 +891,83 @@ func (c *DocumentController) Search() {
|
|||
//文档历史列表.
|
||||
func (c *DocumentController) History() {
|
||||
c.Prepare()
|
||||
c.TplName = "document/history.tpl"
|
||||
|
||||
identify := c.GetString("identify")
|
||||
doc_id, err := c.GetInt("doc_id", 0)
|
||||
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
|
||||
//如果是超级管理员则忽略权限判断
|
||||
if c.Member.Role == conf.MemberSuperRole {
|
||||
|
@ -916,39 +988,77 @@ func (c *DocumentController) History() {
|
|||
}
|
||||
|
||||
if doc_id <= 0 {
|
||||
c.JsonResult(6001, "参数错误")
|
||||
c.JsonResult(6001,"参数错误")
|
||||
}
|
||||
|
||||
doc, err := models.NewDocument().Find(doc_id)
|
||||
|
||||
if err != nil {
|
||||
beego.Error("Delete => ", err)
|
||||
c.JsonResult(6003, "获取历史失败")
|
||||
c.JsonResult(6001,"获取历史失败")
|
||||
}
|
||||
//如果文档所属项目错误
|
||||
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")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
historis,totalCount,err := models.NewDocumentHistory().FindToPager(doc_id,pageIndex,conf.PageSize)
|
||||
if doc_id <= 0 {
|
||||
c.JsonResult(6001,"参数错误")
|
||||
}
|
||||
|
||||
doc, err := models.NewDocument().Find(doc_id)
|
||||
|
||||
if err != nil {
|
||||
c.JsonResult(6005,"获取历史失败")
|
||||
beego.Error("Delete => ", err)
|
||||
c.JsonResult(6001,"获取历史失败")
|
||||
}
|
||||
var data struct {
|
||||
PageHtml string `json:"page_html"`
|
||||
List []*models.DocumentHistorySimpleResult `json:"lists"`
|
||||
//如果文档所属项目错误
|
||||
if doc.BookId != book_id {
|
||||
c.JsonResult(6001,"参数错误")
|
||||
}
|
||||
data.List = historis
|
||||
if totalCount > 0 {
|
||||
html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, conf.PageSize, totalCount)
|
||||
|
||||
data.PageHtml = string(html)
|
||||
}else {
|
||||
data.PageHtml = ""
|
||||
err = models.NewDocumentHistory().Restore(history_id,doc_id,c.Member.MemberId)
|
||||
if err != nil {
|
||||
beego.Error(err)
|
||||
c.JsonResult(6002,"删除失败")
|
||||
}
|
||||
|
||||
c.JsonResult(0,"ok",data)
|
||||
c.JsonResult(0,"ok",doc)
|
||||
}
|
||||
|
||||
//递归生成文档序列数组.
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -176,7 +176,6 @@ func (m *Book) FindToPager(pageIndex, pageSize ,memberId int) (books []*BookResu
|
|||
Limit(pageSize).
|
||||
Offset(offset)
|
||||
|
||||
logs.Info("",qb2.String())
|
||||
_,err = o.Raw(qb2.String(),memberId).QueryRows(&books)
|
||||
if err != nil {
|
||||
logs.Error("分页查询项目列表 => ",err)
|
||||
|
|
|
@ -49,6 +49,9 @@ func (m *DocumentHistory) TableNameWithPrefix() string {
|
|||
|
||||
func NewDocumentHistory() *DocumentHistory {
|
||||
return &DocumentHistory{}
|
||||
}
|
||||
func (m *DocumentHistory) Find() {
|
||||
|
||||
}
|
||||
//清空指定文档的历史.
|
||||
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()
|
||||
|
||||
_, 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
|
||||
}
|
||||
|
||||
//恢复指定历史的文档.
|
||||
func (m *DocumentHistory) Restore(history_id int) error {
|
||||
func (m *DocumentHistory) Restore(history_id,doc_id,uid int) error {
|
||||
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 {
|
||||
return err
|
||||
|
@ -81,10 +85,25 @@ func (m *DocumentHistory) Restore(history_id int) error {
|
|||
if err != nil {
|
||||
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.Content = m.Content
|
||||
doc.Markdown = m.Markdown
|
||||
doc.Release = m.Content
|
||||
doc.Version = time.Now().Unix()
|
||||
|
||||
_, err = o.Update(doc)
|
||||
|
||||
|
@ -111,10 +130,10 @@ func (m *DocumentHistory) FindToPager(doc_id, page_index, page_size int) (docs [
|
|||
|
||||
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
|
||||
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 ?,?;`
|
||||
|
||||
_, err = o.Raw(sql,doc_id,offset,page_size).QueryRows(&docs)
|
||||
|
|
|
@ -22,7 +22,7 @@ type Member struct {
|
|||
Account string `orm:"size(100);unique;column(account)" json:"account"`
|
||||
Password string `orm:"size(1000);column(password)" json:"-"`
|
||||
//认证方式: 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"`
|
||||
Email string `orm:"size(100);column(email);unique" json:"email"`
|
||||
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.Avatar = "/static/images/headimgurl.jpg"
|
||||
m.Role = beego.AppConfig.DefaultInt("ldap_user_role", 2)
|
||||
m.CreateTime = time.Now()
|
||||
|
||||
err = m.Add()
|
||||
if err != nil {
|
||||
logs.Error("自动注册LDAP用户错误", err)
|
||||
|
|
|
@ -57,8 +57,10 @@ func init() {
|
|||
beego.Router("/api/:key/create",&controllers.DocumentController{},"post:Create")
|
||||
beego.Router("/api/:key/delete", &controllers.DocumentController{},"post:Delete")
|
||||
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/:id", &controllers.DocumentController{},"*:Read")
|
||||
|
|
|
@ -200,30 +200,68 @@ function showSuccess($msg,$id) {
|
|||
return true;
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$("#documentHistoryModal").on("shown.bs.modal",function () {
|
||||
var historyVue = new Vue({
|
||||
el : "#documentHistoryModal",
|
||||
data : {
|
||||
lists : []
|
||||
},
|
||||
delimiters : ['${','}'],
|
||||
methods : {
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url : window.historyURL,
|
||||
data : { "identify" : window.book.identify,"doc_id" : window.selectNode.id },
|
||||
dataType :"json",
|
||||
success : function (res) {
|
||||
if(res.errcode === 0){
|
||||
historyVue.lists = res.data.lists;
|
||||
}else{
|
||||
alert(res.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function () {
|
||||
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 : window.removeAttachURL,
|
||||
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 + " 个附件")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
|
@ -9,24 +9,18 @@ $(function () {
|
|||
"editor" : "wangEditor"
|
||||
};
|
||||
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,"release");
|
||||
wangEditor.config.menus.splice(29,0,"attach")
|
||||
|
||||
//移除地图、背景色
|
||||
editor.config.menus = $.map(wangEditor.config.menus, function(item, key) {
|
||||
if (item === 'bgcolor') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (item === 'fullscreen') {
|
||||
return null;
|
||||
}
|
||||
if (item === "undo"){
|
||||
return null;
|
||||
}
|
||||
if (item === "redo"){
|
||||
return null;
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ $(function () {
|
|||
if(name === "attachment"){
|
||||
$("#uploadAttachModal").modal("show");
|
||||
}else if(name === "history"){
|
||||
$("#documentHistoryModal").modal("show");
|
||||
window.documentHistory();
|
||||
}else if(name === "save"){
|
||||
saveDocument(false);
|
||||
|
||||
|
@ -126,7 +126,7 @@ $(function () {
|
|||
* 加载指定的文档到编辑器中
|
||||
* @param $node
|
||||
*/
|
||||
function loadDocument($node) {
|
||||
window.loadDocument = function($node) {
|
||||
var index = layer.load(1, {
|
||||
shade: [0.1,'#fff'] //0.1透明度的白色背景
|
||||
});
|
||||
|
@ -175,7 +175,6 @@ $(function () {
|
|||
|
||||
if(item.id === doc_id){
|
||||
version = item.version;
|
||||
console.log(item)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
||||
})();
|
|
@ -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>
|
|
@ -19,7 +19,8 @@
|
|||
window.releaseURL = "{{urlfor "BookController.Release" ":key" .Model.Identify}}";
|
||||
window.sortURL = "{{urlfor "BookController.SaveSort" ":key" .Model.Identify}}";
|
||||
window.baiduMapKey = "{{.BaiDuMapKey}}";
|
||||
|
||||
window.historyURL = "{{urlfor "DocumentController.History"}}";
|
||||
window.removeAttachURL = "{{urlfor "DocumentController.RemoveAttachment"}}";
|
||||
window.vueApp = null;
|
||||
</script>
|
||||
<!-- Bootstrap -->
|
||||
|
@ -190,54 +191,10 @@
|
|||
<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/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/to-markdown/dist/to-markdown.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/html-editor.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
window.releaseURL = "{{urlfor "BookController.Release" ":key" .Model.Identify}}";
|
||||
window.sortURL = "{{urlfor "BookController.SaveSort" ":key" .Model.Identify}}";
|
||||
window.historyURL = "{{urlfor "DocumentController.History"}}";
|
||||
window.removeAttachURL = "{{urlfor "DocumentController.RemoveAttachment"}}";
|
||||
</script>
|
||||
<!-- Bootstrap -->
|
||||
<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">×</span></button>
|
||||
<h4 class="modal-title">文档历史记录</h4>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<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 class="modal-body text-center" id="historyList">
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<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/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>
|
||||
|
|
|
@ -77,7 +77,17 @@
|
|||
</label>
|
||||
</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">
|
||||
<button type="submit" id="btnSaveBookInfo" class="btn btn-success" data-loading-text="保存中...">保存修改</button>
|
||||
<span id="form-error-message" class="error-message"></span>
|
||||
|
|
Loading…
Reference in New Issue