feat:1、实现空节点功能,当文档标记为空目录时不加载内容页无法编辑内容

2、实现HTTP接口方式登录,
3、优化markdown编辑器部分功能
This commit is contained in:
lifei6671 2019-05-20 17:38:37 +08:00
parent d8e56548ea
commit b510a45908
13 changed files with 463 additions and 215 deletions

View File

@ -107,7 +107,7 @@ func (c *AccountController) Login() {
member, err := models.NewMember().Login(account, password)
if err == nil {
member.LastLoginTime = time.Now()
member.Update()
_ = member.Update("last_login_time")
c.SetMember(*member)

View File

@ -753,14 +753,7 @@ func (c *BookController) Release() {
}
bookId = book.BookId
}
go func(identify string) {
models.NewBook().ReleaseContent(bookId)
//当文档发布后,需要删除已缓存的转换项目
outputPath := filepath.Join(conf.GetExportOutputPath(), strconv.Itoa(bookId))
_ = os.RemoveAll(outputPath)
}(identify)
go models.NewBook().ReleaseContent(bookId)
c.JsonResult(0, "发布任务已推送到任务队列,稍后将在后台执行。")
}

View File

@ -203,6 +203,7 @@ func (c *DocumentController) Edit() {
beego.Error("查询项目时出错 -> ", err)
c.ShowErrorPage(500, "查询项目时出错")
}
return
}
if bookResult.RoleId == conf.BookObserver {
c.JsonResult(6002, "项目不存在或权限不足")
@ -318,6 +319,8 @@ func (c *DocumentController) Create() {
if isOpen == 1 {
document.IsOpen = 1
} else if isOpen == 2 {
document.IsOpen = 2
} else {
document.IsOpen = 0
}
@ -742,6 +745,7 @@ func (c *DocumentController) Content() {
doc.Version = time.Now().Unix()
doc.Content = content
doc.ModifyAt = c.Member.MemberId
if err := doc.InsertOrUpdate(); err != nil {
beego.Error("InsertOrUpdate => ", err)
@ -775,6 +779,7 @@ func (c *DocumentController) Content() {
doc, err := models.NewDocument().Find(docId)
if err != nil {
c.JsonResult(6003, "文档不存在")
return
}
attach, err := models.NewAttachment().FindListByDocumentId(doc.DocumentId)

View File

@ -7,7 +7,6 @@ import (
_ "github.com/astaxie/beego/session/memcache"
_ "github.com/astaxie/beego/session/mysql"
_ "github.com/astaxie/beego/session/redis"
_ "github.com/go-sql-driver/mysql"
"github.com/kardianos/service"
"github.com/lifei6671/mindoc/commands"
"github.com/lifei6671/mindoc/commands/daemon"

View File

@ -11,21 +11,25 @@ import (
"regexp"
"strconv"
"strings"
"sync"
"time"
"encoding/json"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/lifei6671/mindoc/conf"
"github.com/lifei6671/mindoc/utils"
"github.com/lifei6671/mindoc/utils/cryptil"
"github.com/lifei6671/mindoc/utils/filetil"
"github.com/lifei6671/mindoc/utils/requests"
"github.com/lifei6671/mindoc/utils/ziptil"
"gopkg.in/russross/blackfriday.v2"
"encoding/json"
"github.com/lifei6671/mindoc/utils"
)
var releaseQueue = make(chan int, 500)
var once = sync.Once{}
// Book struct .
type Book struct {
BookId int `orm:"pk;auto;unique;column(book_id)" json:"book_id"`
@ -375,7 +379,7 @@ ORDER BY book.order_index, book.book_id DESC limit ?,?`
}
sql := "SELECT m.account,doc.modify_time FROM md_documents AS doc LEFT JOIN md_members AS m ON doc.modify_at=m.member_id WHERE book_id = ? LIMIT 1 ORDER BY doc.modify_time DESC"
if err == nil && len(books) > 0 {
if len(books) > 0 {
for index, book := range books {
var text struct {
Account string
@ -580,9 +584,17 @@ WHERE (relationship_id > 0 OR book.privately_owned = 0 or team.team_member_id >
}
}
//发布文档
// ReleaseContent 批量发布文档
func (book *Book) ReleaseContent(bookId int) {
releaseQueue <- bookId
once.Do(func() {
go func() {
defer func() {
if err := recover(); err != nil {
beego.Error("协程崩溃 ->", err)
}
}()
for bookId := range releaseQueue {
o := orm.NewOrm()
var docs []*Document
@ -590,12 +602,19 @@ func (book *Book) ReleaseContent(bookId int) {
if err != nil {
beego.Error("发布失败 =>", bookId, err)
return
continue
}
for _, item := range docs {
item.BookId = bookId
_ = item.ReleaseContent()
}
//当文档发布后,需要删除已缓存的转换项目
outputPath := filepath.Join(conf.GetExportOutputPath(), strconv.Itoa(bookId))
_ = os.RemoveAll(outputPath)
}
}()
})
}
//重置文档数量

View File

@ -38,7 +38,7 @@ type Document struct {
ModifyTime time.Time `orm:"column(modify_time);type(datetime);auto_now" json:"modify_time"`
ModifyAt int `orm:"column(modify_at);type(int)" json:"-"`
Version int64 `orm:"column(version);type(bigint);" json:"version"`
//是否展开子目录0 否/1 是
//是否展开子目录0 否/1 是 /2 空间节点,单击时展开下一级
IsOpen int `orm:"column(is_open);type(int);default(0)" json:"is_open"`
AttachList []*Attachment `orm:"-" json:"attach"`
}
@ -301,7 +301,18 @@ func (item *Document) Processor() *Document {
if selector := docQuery.Find("div.wiki-bottom").First(); selector.Size() <= 0 && item.MemberId > 0 {
//处理文档结尾信息
docCreator, err := NewMember().Find(item.MemberId, "real_name", "account")
release := "<div class=\"wiki-bottom\">文档更新时间: " + item.ModifyTime.Local().Format("2006-01-02 15:04") + " &nbsp;&nbsp;作者:"
release := "<div class=\"wiki-bottom\">"
if item.ModifyAt > 0 {
docModify, err := NewMember().Find(item.ModifyAt, "real_name", "account")
if err == nil {
if docModify.RealName != "" {
release += "最后编辑: " + docModify.RealName + " &nbsp;"
} else {
release += "最后编辑: " + docModify.Account + " &nbsp;"
}
}
}
release += "文档更新时间: " + item.ModifyTime.Local().Format("2006-01-02 15:04") + " &nbsp;&nbsp;作者:"
if err == nil && docCreator != nil {
if docCreator.RealName != "" {
release += docCreator.RealName
@ -348,6 +359,7 @@ func (item *Document) Processor() *Document {
//移除危险脚本链接
if strings.HasPrefix(val, "data:text/html") ||
strings.HasPrefix(val, "vbscript:") ||
strings.HasPrefix(val, "&#106;avascript:") ||
strings.HasPrefix(val, "javascript:") {
selection.SetAttr("href", "#")
}

View File

@ -2,11 +2,11 @@ package models
import (
"bytes"
"html/template"
"math"
"fmt"
"github.com/astaxie/beego/orm"
"github.com/lifei6671/mindoc/conf"
"fmt"
"html/template"
"math"
)
type DocumentTree struct {
@ -22,6 +22,7 @@ type DocumentTree struct {
type DocumentSelected struct {
Selected bool `json:"selected"`
Opened bool `json:"opened"`
Disabled bool `json:"disabled"`
}
//获取项目的文档树状结构
@ -32,7 +33,10 @@ func (item *Document) FindDocumentTree(bookId int) ([]*DocumentTree, error) {
var docs []*Document
count, err := o.QueryTable(item).Filter("book_id", bookId).OrderBy("order_sort", "document_id").Limit(math.MaxInt32).All(&docs, "document_id", "version", "document_name", "parent_id", "identify","is_open")
count, err := o.QueryTable(item).Filter("book_id", bookId).
OrderBy("order_sort", "document_id").
Limit(math.MaxInt32).
All(&docs, "document_id", "version", "document_name", "parent_id", "identify", "is_open")
if err != nil {
return trees, err
@ -42,13 +46,19 @@ func (item *Document) FindDocumentTree(bookId int) ([]*DocumentTree, error) {
trees = make([]*DocumentTree, count)
for index, item := range docs {
tree := &DocumentTree{}
tree := &DocumentTree{
AAttrs: map[string]interface{}{"is_open": false, "opened": 0},
}
if index == 0 {
tree.State = &DocumentSelected{Selected: true, Opened: true}
tree.AAttrs = map[string]interface{}{ "is_open": true}
tree.AAttrs = map[string]interface{}{"is_open": true, "opened": 1}
} else if item.IsOpen == 1 {
tree.State = &DocumentSelected{Selected: false, Opened: true}
tree.AAttrs = map[string]interface{}{ "is_open": true}
tree.AAttrs = map[string]interface{}{"is_open": true, "opened": 1}
}
if item.IsOpen == 2 {
tree.State = &DocumentSelected{Selected: false, Opened: false, Disabled: true}
tree.AAttrs = map[string]interface{}{"disabled": true, "opened": 2}
}
tree.DocumentId = item.DocumentId
tree.Identify = item.Identify
@ -124,9 +134,11 @@ func getDocumentTree(array []*DocumentTree, parentId int, selectedId int, select
buf.WriteString(uri)
}
buf.WriteString(fmt.Sprintf("\" title=\"%s\"", template.HTMLEscapeString(item.DocumentName)))
if item.State != nil && item.State.Disabled {
buf.WriteString(" disabled=\"true\"")
}
buf.WriteString(fmt.Sprintf(" data-version=\"%d\"%s>%s</a>", item.Version, selected, template.HTMLEscapeString(item.DocumentName)))
for _, sub := range array {
if p, ok := sub.ParentId.(int); ok && p == item.DocumentId {
getDocumentTree(array, p, selectedId, selectedParentId, buf)

View File

@ -2,8 +2,12 @@
package models
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strings"
"time"
@ -67,8 +71,11 @@ func (m *Member) Login(account string, password string) (*Member, error) {
if err != nil {
if beego.AppConfig.DefaultBool("ldap_enable", false) == true {
logs.Info("转入LDAP登陆")
logs.Info("转入LDAP登陆 ->", account)
return member.ldapLogin(account, password)
} else if beego.AppConfig.String("http_login_url") != "" {
logs.Info("转入 HTTP 接口登陆 ->", account)
return member.httpLogin(account, password)
} else {
logs.Error("用户登录 ->", err)
return member, ErrMemberNoExist
@ -85,6 +92,8 @@ func (m *Member) Login(account string, password string) (*Member, error) {
}
case "ldap":
return member.ldapLogin(account, password)
case "http":
return member.httpLogin(account, password)
default:
return member, ErrMemberAuthMethodInvalid
}
@ -131,7 +140,7 @@ func (m *Member) ldapLogin(account string, password string) (*Member, error) {
beego.Error("绑定 LDAP 用户失败 ->", err)
return m, ErrorMemberPasswordError
}
if m.Account == "" {
if m.MemberId <= 0 {
m.Account = account
m.Email = searchResult.Entries[0].GetAttributeValue("mail")
m.AuthMethod = "ldap"
@ -149,6 +158,73 @@ func (m *Member) ldapLogin(account string, password string) (*Member, error) {
return m, nil
}
func (m *Member) httpLogin(account, password string) (*Member, error) {
urlStr := beego.AppConfig.String("http_login_url")
if urlStr == "" {
return nil, ErrMemberAuthMethodInvalid
}
val := url.Values{"": []string{""}}
resp, err := http.PostForm(urlStr, val)
if err != nil {
beego.Error("通过接口登录失败 -> ", urlStr, account, err)
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
beego.Error("读取接口返回值失败 -> ", urlStr, account, err)
return nil, err
}
beego.Info("HTTP 登录接口返回数据 ->", string(body))
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
beego.Error("解析接口返回值失败 -> ", urlStr, account, string(body))
return nil, errors.New("解析接口返回值失败")
}
if code, ok := result["errcode"]; !ok || code.(float64) != 200 {
if msg, ok := result["message"]; ok {
return nil, errors.New(msg.(string))
}
return nil, errors.New("接口返回值格式不正确")
}
if m.MemberId <= 0 {
member := NewMember()
if email, ok := result["email"]; !ok || email == "" {
return nil, errors.New("接口返回的数据缺少邮箱字段")
} else {
member.Email = email.(string)
}
if avatar, ok := result["avater"]; ok && avatar != "" {
member.Avatar = avatar.(string)
} else {
member.Avatar = conf.URLForWithCdnImage("/static/images/headimgurl.jpg")
}
if realName, ok := result["real_name"]; ok && realName != "" {
member.RealName = realName.(string)
}
member.Account = account
member.Password = password
member.AuthMethod = "http"
member.Role = conf.SystemRole(beego.AppConfig.DefaultInt("ldap_user_role", 2))
member.CreateTime = time.Now()
if err := member.Add(); err != nil {
beego.Error("自动注册用户错误", err)
return m, ErrorMemberPasswordError
}
member.ResolveRoleName()
*m = *member
}
return m, nil
}
// Add 添加一个用户.
func (m *Member) Add() error {
o := orm.NewOrm()
@ -219,7 +295,6 @@ func (m *Member) Find(id int, cols ...string) (*Member, error) {
return m, nil
}
func (m *Member) ResolveRoleName() {
if m.Role == conf.MemberSuperRole {
m.RoleName = "超级管理员"
@ -283,11 +358,11 @@ func (m *Member) FindToPager(pageIndex, pageSize int) ([]*Member, int, error) {
return members, int(totalCount), nil
}
func (c *Member) IsAdministrator() bool {
if c == nil || c.MemberId <= 0 {
func (m *Member) IsAdministrator() bool {
if m == nil || m.MemberId <= 0 {
return false
}
return c.Role == 0 || c.Role == 1
return m.Role == 0 || m.Role == 1
}
//根据指定字段查找用户.

View File

@ -2984,102 +2984,170 @@
h1 : function() {
var cm = this.cm;
var cursor = cm.getCursor();
var selection = cm.getSelection();
if (cursor.ch !== 0)
{
cm.setCursor(cursor.line, 0);
cm.replaceSelection("# " + selection);
cm.setCursor(cursor.line, cursor.ch + 2);
var selection = cm.getLine(cursor.line);
var patt1=new RegExp("^#{1}[ ]");//如果存在H1
var patt2=new RegExp("^#{1,6}[ ]");//如果存在H2-H6
//如果存在H1,取消H1的设置
if (patt1.test(selection)===true){
selection=selection.replace(/^#{1}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection(selection+"\n");
cm.setCursor(cursor.line, cursor.ch -2);
}
else
//如果存在H1-H6,取消H2-H6,并替换为H1
else if(patt2.test(selection)===true){
selection=selection.replace(/^#{1,6}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("# "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +2);
}else
//设置为H1
{
cm.replaceSelection("# " + selection);
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("# "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +2);
}
},
h2 : function() {
var cm = this.cm;
var cursor = cm.getCursor();
var selection = cm.getSelection();
if (cursor.ch !== 0)
{
cm.setCursor(cursor.line, 0);
cm.replaceSelection("## " + selection);
var selection = cm.getLine(cursor.line);
var patt1=new RegExp("^#{2}[ ]");//如果存在H1
var patt2=new RegExp("^#{1,6}[ ]");//如果存在H1-H6
//如果存在H2,取消H2的设置
if (patt1.test(selection)===true){
selection=selection.replace(/^#{2}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection(selection+"\n");
cm.setCursor(cursor.line, cursor.ch -3);
}else if(patt2.test(selection)===true){
selection=selection.replace(/^#{1,6}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("## "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +3);
}else{
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("## "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +3);
}
else
{
cm.replaceSelection("## " + selection);
}
},
h3 : function() {
var cm = this.cm;
var cursor = cm.getCursor();
var selection = cm.getSelection();
if (cursor.ch !== 0)
{
cm.setCursor(cursor.line, 0);
cm.replaceSelection("### " + selection);
cm.setCursor(cursor.line, cursor.ch + 4);
var selection = cm.getLine(cursor.line);
var patt1=new RegExp("^#{3}[ ]");//如果存在H3
var patt2=new RegExp("^#{1,6}[ ]");//如果存在H1-H6
//如果存在H3,取消H3的设置
if (patt1.test(selection)===true){
selection=selection.replace(/^#{3}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection(selection+"\n");
cm.setCursor(cursor.line, cursor.ch -4);
}
else
//如果存在H1-H6,取消H1-H6,并替换为H3
else if(patt2.test(selection)===true){
selection=selection.replace(/^#{1,6}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("### "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +4);
}else
//设置为H3
{
cm.replaceSelection("### " + selection);
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("### "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +4);
}
},
h4 : function() {
var cm = this.cm;
var cursor = cm.getCursor();
var selection = cm.getSelection();
if (cursor.ch !== 0)
{
cm.setCursor(cursor.line, 0);
cm.replaceSelection("#### " + selection);
cm.setCursor(cursor.line, cursor.ch + 5);
var selection = cm.getLine(cursor.line);
var patt1=new RegExp("^#{4}[ ]");//如果存在H4
var patt2=new RegExp("^#{1,6}[ ]");//如果存在H1-H6
//如果存在H4,取消H4的设置
if (patt1.test(selection)===true){
selection=selection.replace(/^#{4}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection(selection+"\n");
cm.setCursor(cursor.line, cursor.ch -5);
}
else
//如果存在H1-H6,取消H1-H6,并替换为H4
else if(patt2.test(selection)===true){
selection=selection.replace(/^#{1,6}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("#### "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +5);
}else
//设置为H4
{
cm.replaceSelection("#### " + selection);
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("#### "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +5);
}
},
h5 : function() {
var cm = this.cm;
var cursor = cm.getCursor();
var selection = cm.getSelection();
if (cursor.ch !== 0)
{
cm.setCursor(cursor.line, 0);
cm.replaceSelection("##### " + selection);
cm.setCursor(cursor.line, cursor.ch + 6);
var selection = cm.getLine(cursor.line);
var patt1=new RegExp("^#{5}[ ]");//如果存在H5
var patt2=new RegExp("^#{1,6}[ ]");//如果存在H1-H6
//如果存在H5,取消H5的设置
if (patt1.test(selection)===true){
selection=selection.replace(/^#{5}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection(selection+"\n");
cm.setCursor(cursor.line, cursor.ch -6);
}
else
//如果存在H1-H6,取消H1-H6,并替换为H5
else if(patt2.test(selection)===true){
selection=selection.replace(/^#{1,6}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("##### "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +6);
}else
//设置为H5
{
cm.replaceSelection("##### " + selection);
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("##### "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +6);
}
},
h6 : function() {
var cm = this.cm;
var cursor = cm.getCursor();
var selection = cm.getSelection();
if (cursor.ch !== 0)
{
cm.setCursor(cursor.line, 0);
cm.replaceSelection("###### " + selection);
cm.setCursor(cursor.line, cursor.ch + 7);
var selection = cm.getLine(cursor.line);
var patt1=new RegExp("^#{6}[ ]");//如果存在H6
var patt2=new RegExp("^#{1,6}[ ]");//如果存在H1-H6
//如果存在H6,取消H6的设置
if (patt1.test(selection)===true){
selection=selection.replace(/^#{6}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection(selection+"\n");
cm.setCursor(cursor.line, cursor.ch -7);
}
else
//如果存在H1-H6,取消H1-H6,并替换为H6
else if(patt2.test(selection)===true){
selection=selection.replace(/^#{1,6}[ ]/,"");
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("###### "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +7);
}else
//设置为H6
{
cm.replaceSelection("###### " + selection);
cm.setSelection({line:cursor.line, ch:0}, {line:cursor.line+1, ch:0 });
cm.replaceSelection("###### "+selection+"\n");
cm.setCursor(cursor.line, cursor.ch +7);
}
},
@ -3087,20 +3155,36 @@
var cm = this.cm;
var cursor = cm.getCursor();
var selection = cm.getSelection();
var patt1=new RegExp("^-{1}[ ]");
var cnt =0;
//如果未选择,将该行设置为无序列表
if (selection === "")
{
cm.setCursor(cursor.line, 0);
cm.replaceSelection("- " + selection);
cm.setCursor(cursor.line, cursor.ch + 2);
}
else
{
var selectionText = selection.split("\n");
//判断取中内容是否已作无序列表标记
for (var i = 0, len = selectionText.length; i < len; i++)
{
selectionText[i] = (selectionText[i] === "") ? "" : "- " + selectionText[i];
if (patt1.test(selectionText[i])===true ){cnt++;}
}
//如果全作了无序列表标记,取消标记
if(cnt===selectionText.length){
for (var i = 0, len = selectionText.length; i < len; i++){
selectionText[i] = (selectionText[i] === "") ? "" : selectionText[i].replace(/^-{1}[ ]/,"");
}
}else
//对未打上无序列表标记,打上标记
{
for (var i = 0, len = selectionText.length; i < len; i++){
selectionText[i] = (selectionText[i] === "") ? "" : "- "+selectionText[i].replace(/^-{1}[ ]/,"");
}
}
cm.replaceSelection(selectionText.join("\n"));
}
},
@ -3109,20 +3193,36 @@
var cm = this.cm;
var cursor = cm.getCursor();
var selection = cm.getSelection();
var patt1=new RegExp("^[0-9]+[\.][ ]");
var cnt =0;
//如果未选择,将该行设置为有序列表
if (selection === "")
{
cm.setCursor(cursor.line, 0);
cm.replaceSelection("1. " + selection);
cm.setCursor(cursor.line, cursor.ch + 3);
}
else
{
var selectionText = selection.split("\n");
//判断取中内容是否已作有序列表标记
for (var i = 0, len = selectionText.length; i < len; i++)
{
selectionText[i] = (selectionText[i] === "") ? "" : (i+1) + ". " + selectionText[i];
if (patt1.test(selectionText[i])===true ){cnt++;}
}
//如果全作了有序列表标记,取消标记
if(cnt===selectionText.length){
for (var i = 0, len = selectionText.length; i < len; i++){
selectionText[i] = (selectionText[i] === "") ? "" : selectionText[i].replace(/^[0-9]+[\.][ ]/,"");
}
}else
//对未打上有序列表标记,打上标记
{
for (var i = 0, len = selectionText.length; i < len; i++){
selectionText[i] = (selectionText[i] === "") ? "" : (i+1)+". "+selectionText[i].replace(/^[0-9]+[\.][ ]/,"");
}
}
cm.replaceSelection(selectionText.join("\n"));
}
},
@ -4765,7 +4865,14 @@
return datefmt;
};
/**
* 获取指定行的内容
* @param n
* @returns {*}
*/
editormd.getLine = function(n) {
return this.cm.getLine(n);
};
return editormd;
}));

View File

@ -179,11 +179,11 @@ function openEditCatalogDialog($node) {
$then.find("input[name='parent_id']").val(parentId);
$then.find("input[name='doc_name']").val(text);
if($node.a_attr && $node.a_attr.is_open){
$then.find("input[name='is_open'][value='1']").prop("checked","checked");
}else{
$then.find("input[name='is_open'][value='0']").prop("checked","checked");
}
var open = $node.a_attr && $node.a_attr.opened ? $node.a_attr.opened : 0;
console.log($node)
$then.find("input[name='is_open'][value='" + open + "']").prop("checked", "checked");
for (var index in window.documentCategory) {
var item = window.documentCategory[index];
@ -211,6 +211,7 @@ function pushDocumentCategory($node) {
}
window.documentCategory.push($node);
}
/**
* 将数据重置到Vue列表中
* @param $lists
@ -241,6 +242,7 @@ function releaseBook() {
}
});
}
//实现小提示
$("[data-toggle='tooltip']").hover(function () {
var title = $(this).attr('data-title');
@ -305,9 +307,11 @@ window.documentHistory = function() {
content: window.historyURL + "?identify=" + window.book.identify + "&doc_id=" + window.selectNode.id,
end: function () {
if (window.SelectedId) {
var selected = {node:{
var selected = {
node: {
id: window.SelectedId
}};
}
};
window.loadDocument(selected);
window.SelectedId = null;
}
@ -389,6 +393,7 @@ function initHighlighting() {
hljs.highlightBlock(block);
});
}
$(function () {
window.vueApp = new Vue({
el: "#attachList",

View File

@ -199,7 +199,12 @@ $(function () {
"multiple" : false,
'animation' : 0
}
}).on('select_node.jstree', function (node, selected, event) {
}).on('select_node.jstree', function (node, selected) {
//如果是空目录则直接出发展开下一级功能
if (selected.node.a_attr && selected.node.a_attr.disabled) {
selected.instance.toggle_node(selected.node);
return false
}
$(".m-manual").removeClass('manual-mobile-show-left');
loadDocument(selected.node.a_attr.href, selected.node.id,selected.node.a_attr['data-version']);
});

View File

@ -125,9 +125,11 @@ $(function () {
// 插入 GFM 任务列表
var cm = window.editor.cm;
var selection = cm.getSelection();
var cursor = cm.getCursor();
if (selection === "") {
cm.setCursor(cursor.line, 0);
cm.replaceSelection("- [x] " + selection);
cm.setCursor(cursor.line, cursor.ch + 6);
} else {
var selectionText = selection.split("\n");
@ -196,6 +198,10 @@ $(function () {
layer.msg("获取当前文档信息失败");
return;
}
if (node.a_attr && node.a_attr.disabled) {
layer.msg("空节点不能添加内容");
return;
}
var doc_id = parseInt(node.id);
@ -381,7 +387,7 @@ $(function () {
//如果没有选中节点则选中默认节点
// openLastSelectedNode();
}).on('select_node.jstree', function (node, selected, event) {
}).on('select_node.jstree', function (node, selected) {
if ($("#markdown-save").hasClass('change')) {
if (confirm("编辑内容未保存,需要保存吗?")) {
@ -391,6 +397,12 @@ $(function () {
return true;
}
}
//如果是空目录则直接出发展开下一级功能
if (selected.node.a_attr && selected.node.a_attr.disabled) {
selected.instance.toggle_node(selected.node);
return false
}
loadDocument(selected);
}).on("move_node.jstree", jstree_save).on("delete_node.jstree",function($node,$parent) {

View File

@ -167,17 +167,21 @@
</div>
<div class="form-group">
<div class="col-lg-6">
<div class="col-lg-4">
<label>
<input type="radio" name="is_open" value="1"> 展开<span class="text">(在阅读时会自动展开节点)</span>
</label>
</div>
<div class="col-lg-6">
<div class="col-lg-4">
<label>
<input type="radio" name="is_open" value="0" checked> 关闭<span class="text">(在阅读时会关闭节点)</span>
</label>
</div>
<div class="col-lg-4">
<label>
<input type="radio" name="is_open" value="2"> 空目录<span class="text">(单击时会展开下级节点)</span>
</label>
</div>
<div class="clearfix"></div>
</div>
</div>