1、实现文章列表、设置、编辑功能

2、实现文章附件功能
pull/358/head
lifei6671 2018-07-17 19:13:11 +08:00
parent 96ba414dd8
commit 86637ef581
15 changed files with 1521 additions and 425 deletions

View File

@ -3,6 +3,16 @@ package controllers
import ( import (
"strings" "strings"
"github.com/lifei6671/mindoc/models" "github.com/lifei6671/mindoc/models"
"time"
"github.com/astaxie/beego"
"github.com/lifei6671/mindoc/conf"
"github.com/lifei6671/mindoc/utils/pagination"
"strconv"
"fmt"
"os"
"net/http"
"path/filepath"
"github.com/astaxie/beego/orm"
) )
type BlogController struct{ type BlogController struct{
@ -25,6 +35,24 @@ func (c *BlogController) List() {
func (c *BlogController) ManageList() { func (c *BlogController) ManageList() {
c.Prepare() c.Prepare()
c.TplName = "blog/manage_list.tpl" c.TplName = "blog/manage_list.tpl"
pageIndex, _ := c.GetInt("page", 1)
blogList,totalCount,err := models.NewBlog().FindToPager(pageIndex,conf.PageSize,c.Member.MemberId,"")
beego.Info(totalCount)
if err != nil {
c.ShowErrorPage(500,err.Error())
}
if totalCount > 0 {
pager := pagination.NewPagination(c.Ctx.Request, totalCount, conf.PageSize, c.BaseUrl())
c.Data["PageHtml"] = pager.HtmlPages()
} else {
c.Data["PageHtml"] = ""
}
c.Data["ModelList"] = blogList
} }
//文章设置 //文章设置
@ -39,10 +67,21 @@ func (c *BlogController) ManageSetting() {
orderIndex,_ := c.GetInt("order_index",0) orderIndex,_ := c.GetInt("order_index",0)
blogType,_ := c.GetInt("blog_type",0) blogType,_ := c.GetInt("blog_type",0)
documentId,_ := c.GetInt("document_id",0) documentId,_ := c.GetInt("document_id",0)
blogExcerpt := c.GetString("excerpt","")
blogStatus := c.GetString("status","publish")
blogPassword := c.GetString("password","")
if blogTitle == "" { if blogTitle == "" {
c.JsonResult(6001,"文章标题不能为空") c.JsonResult(6001,"文章标题不能为空")
} }
if strings.Count(blogExcerpt,"") > 100 {
c.JsonResult(6008,"文章摘要必须小于500字符")
}
if blogStatus != "public" && blogStatus != "password" && blogStatus != "draft" {
blogStatus = "public"
}
if blogStatus == "password" && blogPassword == "" {
c.JsonResult(6010,"加密文章请设置密码")
}
if blogType != 0 && blogType != 1 { if blogType != 0 && blogType != 1 {
c.JsonResult(6005,"未知的文章类型") c.JsonResult(6005,"未知的文章类型")
}else if documentId <= 0 && blogType == 1 { }else if documentId <= 0 && blogType == 1 {
@ -73,6 +112,8 @@ func (c *BlogController) ManageSetting() {
c.JsonResult(6004,"文章标识已存在") c.JsonResult(6004,"文章标识已存在")
} }
} }
blog.Modified = time.Now()
blog.ModifyAt = c.Member.MemberId
}else{ }else{
//如果设置了文章标识 //如果设置了文章标识
if blogIdentify != "" { if blogIdentify != "" {
@ -83,17 +124,47 @@ func (c *BlogController) ManageSetting() {
blog = models.NewBlog() blog = models.NewBlog()
blog.MemberId = c.Member.MemberId blog.MemberId = c.Member.MemberId
blog.Created = time.Now()
}
if blogIdentify == "" {
blog.BlogIdentify = fmt.Sprintf("%s-%d","post",time.Now().UnixNano())
}else{
blog.BlogIdentify = blogIdentify
} }
blog.BlogTitle = blogTitle blog.BlogTitle = blogTitle
blog.BlogIdentify = blogIdentify
blog.OrderIndex = orderIndex blog.OrderIndex = orderIndex
blog.BlogType = blogType blog.BlogType = blogType
if blogType == 1 { if blogType == 1 {
blog.DocumentId = documentId blog.DocumentId = documentId
} }
blog.BlogExcerpt = blogExcerpt
blog.BlogStatus = blogStatus
blog.Password = blogPassword
if err := blog.Save();err != nil {
beego.Error("保存文章失败 -> ",err)
c.JsonResult(6011,"保存文章失败")
}else{
c.JsonResult(0,"ok",blog)
}
}
if c.Ctx.Input.Referer() == "" {
c.Data["Referer"] = "javascript:history.back();"
}else{
c.Data["Referer"] = c.Ctx.Input.Referer()
}
blogId,err := strconv.Atoi(c.Ctx.Input.Param(":id"))
if err == nil {
blog,err := models.NewBlog().FindByIdAndMemberId(blogId,c.Member.MemberId)
if err != nil {
c.ShowErrorPage(500,err.Error())
}
c.Data["Model"] = blog
}else{
c.Data["Model"] = models.NewBlog()
} }
} }
@ -101,4 +172,316 @@ func (c *BlogController) ManageSetting() {
func (c *BlogController) ManageEdit() { func (c *BlogController) ManageEdit() {
c.Prepare() c.Prepare()
c.TplName = "blog/manage_edit.tpl" c.TplName = "blog/manage_edit.tpl"
if c.Ctx.Input.IsPost() {
blogId,_ := c.GetInt("blogId",0)
if blogId <= 0 {
c.JsonResult(6001,"文章参数错误")
}
blogContent := c.GetString("content","")
blogHtml := c.GetString("htmlContent","")
version,_ := c.GetInt64("version",0)
cover := c.GetString("cover")
var blog *models.Blog
var err error
if c.Member.IsAdministrator() {
blog,err = models.NewBlog().Find(blogId)
}else{
blog,err = models.NewBlog().FindByIdAndMemberId(blogId,c.Member.MemberId)
}
if err != nil {
beego.Error("查询文章失败 ->",err)
c.JsonResult(6002,"查询文章失败")
}
if version > 0 && blog.Version != version && cover != "yes"{
c.JsonResult(6005,"文章已被修改")
}
if blog.BlogType == 1 {
doc,err := models.NewDocument().Find(blog.DocumentId)
if err != nil {
beego.Error("查询关联项目文档时出错 ->", err)
c.JsonResult(6003,"查询关联项目文档时出错")
}
doc.Markdown = blogContent
doc.Release = blogHtml
doc.Content = blogHtml
doc.ModifyTime = time.Now()
doc.ModifyAt = c.Member.MemberId
if err := doc.InsertOrUpdate("markdown","release","content","modify_time","modify_at");err != nil {
beego.Error("保存关联文档时出错 ->",err)
c.JsonResult(6004,"保存关联文档时出错")
}
}
blog.BlogContent = blogContent
blog.BlogRelease = blogHtml
blog.ModifyAt = c.Member.MemberId
blog.Modified = time.Now()
if err := blog.Save("blog_content","blog_release","modify_at","modify_time","version");err != nil {
beego.Error("保存文章失败 -> ",err)
c.JsonResult(6011,"保存文章失败")
}else{
c.JsonResult(0,"ok",blog)
}
}
blogId,_ := strconv.Atoi(c.Ctx.Input.Param(":id"))
if blogId <= 0 {
c.ShowErrorPage(500,"参数错误")
}
var blog *models.Blog
var err error
if c.Member.IsAdministrator() {
blog,err = models.NewBlog().Find(blogId)
}else{
blog,err = models.NewBlog().FindByIdAndMemberId(blogId,c.Member.MemberId)
}
if err != nil {
c.ShowErrorPage(404,"文章不存在或已删除")
}
c.Data["Model"] = blog
}
//删除文章
func (c *BlogController) ManageDelete() {
c.Prepare()
blogId,_ := c.GetInt("blog_id",0)
if blogId <= 0 {
c.JsonResult(6001,"参数错误")
}
var blog *models.Blog
var err error
if c.Member.IsAdministrator() {
blog,err = models.NewBlog().Find(blogId)
}else{
blog,err = models.NewBlog().FindByIdAndMemberId(blogId,c.Member.MemberId)
}
if err != nil {
c.JsonResult(6002,"文章不存在或已删除")
}
if err := blog.Delete(blogId); err != nil {
c.JsonResult(6003,"删除失败")
}else{
c.JsonResult(0,"删除成功")
}
}
// 上传附件或图片
func (c *BlogController) Upload() {
blogId, _ := c.GetInt("blogId")
if blogId <= 0 {
c.JsonResult(6001, "参数错误")
}
name := "editormd-file-file"
file, moreFile, err := c.GetFile(name)
if err == http.ErrMissingFile {
name = "editormd-image-file"
file, moreFile, err = c.GetFile(name)
if err == http.ErrMissingFile {
c.JsonResult(6003, "没有发现需要上传的图片")
}
}
if err != nil {
c.JsonResult(6002, err.Error())
}
defer file.Close()
type Size interface {
Size() int64
}
if conf.GetUploadFileSize() > 0 && moreFile.Size > conf.GetUploadFileSize() {
c.JsonResult(6009, "查过文件允许的上传最大值")
}
ext := filepath.Ext(moreFile.Filename)
if ext == "" {
c.JsonResult(6003, "无法解析文件的格式")
}
//如果文件类型设置为 * 标识不限制文件类型
if beego.AppConfig.DefaultString("upload_file_ext", "") != "*" {
if !conf.IsAllowUploadFileExt(ext) {
c.JsonResult(6004, "不允许的文件类型")
}
}
// 如果是超级管理员,则不判断权限
if c.Member.IsAdministrator() {
_, err := models.NewBlog().Find(blogId)
if err != nil {
c.JsonResult(6006, "文档不存在或权限不足")
}
} else {
_, err := models.NewBlog().FindByIdAndMemberId(blogId, c.Member.MemberId)
if err != nil {
beego.Error("查询文章时出错 -> ", err)
if err == orm.ErrNoRows {
c.JsonResult(6006, "权限不足")
}
c.JsonResult(6001, err.Error())
}
}
fileName := "attach_" + strconv.FormatInt(time.Now().UnixNano(), 16)
filePath := filepath.Join(conf.WorkingDirectory, "uploads", "blog", time.Now().Format("200601"), fileName+ext)
path := filepath.Dir(filePath)
os.MkdirAll(path, os.ModePerm)
err = c.SaveToFile(name, filePath)
if err != nil {
beego.Error("SaveToFile => ", err)
c.JsonResult(6005, "保存文件失败")
}
var httpPath string
result := make(map[string]interface{})
if strings.EqualFold(ext, ".jpg") || strings.EqualFold(ext, ".jpeg") || strings.EqualFold(ext, ".png") || strings.EqualFold(ext, ".gif") {
httpPath = "/" + strings.Replace(strings.TrimPrefix(filePath, conf.WorkingDirectory), "\\", "/", -1)
if strings.HasPrefix(httpPath, "//") {
httpPath = conf.URLForWithCdnImage(string(httpPath[1:]))
}
} else {
attachment := models.NewAttachment()
attachment.BookId = 0
attachment.FileName = moreFile.Filename
attachment.CreateAt = c.Member.MemberId
attachment.FileExt = ext
attachment.FilePath = strings.TrimPrefix(filePath, conf.WorkingDirectory)
attachment.DocumentId = blogId
if fileInfo, err := os.Stat(filePath); err == nil {
attachment.FileSize = float64(fileInfo.Size())
}
attachment.HttpPath = httpPath
if err := attachment.Insert(); err != nil {
os.Remove(filePath)
beego.Error("保存文件附件失败 => ", err)
c.JsonResult(6006, "文件保存失败")
}
if attachment.HttpPath == "" {
attachment.HttpPath = conf.URLFor("BlogController.Download", ":id", blogId, ":attach_id", attachment.AttachmentId)
if err := attachment.Update(); err != nil {
beego.Error("SaveToFile => ", err)
c.JsonResult(6005, "保存文件失败")
}
}
result["attach"] = attachment
}
result["errcode"] = 0
result["success"] = 1
result["message"] = "ok"
result["url"] = httpPath
result["alt"] = fileName
c.Ctx.Output.JSON(result, true, false)
c.StopRun()
}
// 删除附件
func (c *BlogController) RemoveAttachment() {
c.Prepare()
attachId, _ := c.GetInt("attach_id")
if attachId <= 0 {
c.JsonResult(6001, "参数错误")
}
attach, err := models.NewAttachment().Find(attachId)
if err != nil {
beego.Error(err)
c.JsonResult(6002, "附件不存在")
}
if !c.Member.IsAdministrator() {
_, err := models.NewBlog().FindByIdAndMemberId(attach.DocumentId,c.Member.MemberId)
if err != nil {
beego.Error(err)
c.JsonResult(6003, "文档不存在")
}
}
if err := attach.Delete();err != nil {
beego.Error(err)
c.JsonResult(6005, "删除失败")
}
os.Remove(filepath.Join(conf.WorkingDirectory, attach.FilePath))
c.JsonResult(0, "ok", attach)
}
//下载附件
func (c *BlogController) Download() {
c.Prepare()
blogId, _ := strconv.Atoi(c.Ctx.Input.Param(":id"))
attachId, _ := strconv.Atoi(c.Ctx.Input.Param(":attach_id"))
password := c.GetString("password")
blog, err := models.NewBlog().Find(blogId)
if err != nil {
if err == orm.ErrNoRows {
c.ShowErrorPage(500, "文档不存在")
} else {
c.ShowErrorPage(500, "查询文章时异常")
}
}
if (c.Member != nil && !c.Member.IsAdministrator()) || ( blog.BlogStatus == "password" && password != blog.Password) {
c.ShowErrorPage(403, "没有下载权限")
}
// 查找附件
attachment, err := models.NewAttachment().Find(attachId)
if err != nil {
beego.Error("DownloadAttachment => ", err)
if err == orm.ErrNoRows {
c.ShowErrorPage(404,"附件不存在")
} else {
c.ShowErrorPage(500,"查询附件时出现异常")
}
}
if attachment.BookId !=0 || attachment.DocumentId != blogId {
c.ShowErrorPage(404,"附件不存在")
}
c.Ctx.Output.Download(filepath.Join(conf.WorkingDirectory, attachment.FilePath), attachment.FileName)
c.StopRun()
} }

View File

@ -579,13 +579,13 @@ func (c *DocumentController) DownloadAttachment() {
// 删除附件 // 删除附件
func (c *DocumentController) RemoveAttachment() { func (c *DocumentController) RemoveAttachment() {
c.Prepare() c.Prepare()
attach_id, _ := c.GetInt("attach_id") attachId, _ := c.GetInt("attach_id")
if attach_id <= 0 { if attachId <= 0 {
c.JsonResult(6001, "参数错误") c.JsonResult(6001, "参数错误")
} }
attach, err := models.NewAttachment().Find(attach_id) attach, err := models.NewAttachment().Find(attachId)
if err != nil { if err != nil {
beego.Error(err) beego.Error(err)

View File

@ -657,17 +657,19 @@ func (c *ManagerController) AttachDetailed() {
//删除附件. //删除附件.
func (c *ManagerController) AttachDelete() { func (c *ManagerController) AttachDelete() {
c.Prepare() c.Prepare()
attach_id, _ := c.GetInt("attach_id") attachId, _ := c.GetInt("attach_id")
if attach_id <= 0 { if attachId <= 0 {
c.Abort("404") c.Abort("404")
} }
attach, err := models.NewAttachment().Find(attach_id) attach, err := models.NewAttachment().Find(attachId)
if err != nil { if err != nil {
beego.Error("AttachDelete => ", err) beego.Error("AttachDelete => ", err)
c.JsonResult(6001, err.Error()) c.JsonResult(6001, err.Error())
} }
attach.FilePath = filepath.Join(conf.WorkingDirectory,attach.FilePath)
if err := attach.Delete(); err != nil { if err := attach.Delete(); err != nil {
beego.Error("AttachDelete => ", err) beego.Error("AttachDelete => ", err)
c.JsonResult(6002, err.Error()) c.JsonResult(6002, err.Error())

View File

@ -82,11 +82,11 @@ func (m *Attachment) Find(id int) (*Attachment, error) {
return m, err return m, err
} }
//查询指定文档的附件列表
func (m *Attachment) FindListByDocumentId(doc_id int) (attaches []*Attachment, err error) { func (m *Attachment) FindListByDocumentId(docId int) (attaches []*Attachment, err error) {
o := orm.NewOrm() o := orm.NewOrm()
_, err = o.QueryTable(m.TableNameWithPrefix()).Filter("document_id", doc_id).OrderBy("-attachment_id").All(&attaches) _, err = o.QueryTable(m.TableNameWithPrefix()).Filter("document_id", docId).Filter("book_id__gt",0).OrderBy("-attachment_id").All(&attaches)
return return
} }
@ -115,20 +115,31 @@ func (m *Attachment) FindToPager(pageIndex, pageSize int) (attachList []*Attachm
attach := &AttachmentResult{} attach := &AttachmentResult{}
attach.Attachment = *item attach.Attachment = *item
attach.FileShortSize = filetil.FormatBytes(int64(attach.FileSize)) attach.FileShortSize = filetil.FormatBytes(int64(attach.FileSize))
//当项目ID为0标识是文章的附件
if item.BookId == 0 && item.DocumentId > 0 {
blog := NewBlog()
if err := o.QueryTable(blog.TableNameWithPrefix()).Filter("blog_id",item.DocumentId).One(blog,"blog_title");err == nil {
attach.BookName = blog.BlogTitle
}else{
attach.BookName = "[文章不存在]"
}
}else {
book := NewBook()
book := NewBook() if e := o.QueryTable(book.TableNameWithPrefix()).Filter("book_id", item.BookId).One(book, "book_name"); e == nil {
attach.BookName = book.BookName
if e := o.QueryTable(book.TableNameWithPrefix()).Filter("book_id", item.BookId).One(book, "book_name"); e == nil { doc := NewDocument()
attach.BookName = book.BookName
} else {
attach.BookName = "[不存在]"
}
doc := NewDocument()
if e := o.QueryTable(doc.TableNameWithPrefix()).Filter("document_id", item.DocumentId).One(doc, "document_name"); e == nil { if e := o.QueryTable(doc.TableNameWithPrefix()).Filter("document_id", item.DocumentId).One(doc, "document_name"); e == nil {
attach.DocumentName = doc.DocumentName attach.DocumentName = doc.DocumentName
} else { } else {
attach.DocumentName = "[不存在]" attach.DocumentName = "[文档不存在]"
}
} else {
attach.BookName = "[项目不存在]"
}
} }
attach.LocalHttpPath = strings.Replace(item.FilePath, "\\", "/", -1) attach.LocalHttpPath = strings.Replace(item.FilePath, "\\", "/", -1)

View File

@ -23,12 +23,12 @@ type Blog struct {
//链接到的项目中的文档ID //链接到的项目中的文档ID
DocumentId int `orm:"column(document_id);type(int);default(0)" json:"document_id"` DocumentId int `orm:"column(document_id);type(int);default(0)" json:"document_id"`
//文章摘要 //文章摘要
BlogExcerpt string `orm:"column(blog_excerpt);size(1500);unique" json:"blog_excerpt"` BlogExcerpt string `orm:"column(blog_excerpt);size(1500)" json:"blog_excerpt"`
//文章内容 //文章内容
BlogContent string `orm:"column(blog_content);type(text);null" json:"blog_content"` BlogContent string `orm:"column(blog_content);type(text);null" json:"blog_content"`
//发布后的文章内容 //发布后的文章内容
BlogRelease string `orm:"column(blog_release);type(text);null" json:"blog_release"` BlogRelease string `orm:"column(blog_release);type(text);null" json:"blog_release"`
//文章当前的状态枚举enum(publish,draft,private,static,object)值publish为已 发表draft为草稿private为私人内容(不会被公开) static(不详)object(不详)。默认为publish。 //文章当前的状态枚举enum(publish,draft,password)值publish为已 发表draft为草稿password 为私人内容(不会被公开) 。默认为publish。
BlogStatus string `orm:"column(blog_status);size(100);default(publish)" json:"blog_status"` BlogStatus string `orm:"column(blog_status);size(100);default(publish)" json:"blog_status"`
//文章密码varchar(100)值。文章编辑才可为文章设定一个密码,凭这个密码才能对文章进行重新强加或修改。 //文章密码varchar(100)值。文章编辑才可为文章设定一个密码,凭这个密码才能对文章进行重新强加或修改。
Password string `orm:"column(password);size(100)" json:"-"` Password string `orm:"column(password);size(100)" json:"-"`
@ -36,9 +36,12 @@ type Blog struct {
Modified time.Time `orm:"column(modify_time);type(datetime);auto_now" json:"modify_time"` Modified time.Time `orm:"column(modify_time);type(datetime);auto_now" json:"modify_time"`
//修改人id //修改人id
ModifyAt int `orm:"column(modify_at);type(int)" json:"-"` ModifyAt int `orm:"column(modify_at);type(int)" json:"-"`
ModifyRealName string `orm:"-" json:"modify_real_name"`
//创建时间 //创建时间
Created time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"` Created time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
Version int64 `orm:"type(bigint);column(version)" json:"version"` CreateName string `orm:"-" json:"create_name"`
//版本号
Version int64 `orm:"type(bigint);column(version)" json:"version"`
} }
// 多字段唯一键 // 多字段唯一键
@ -64,7 +67,7 @@ func (m *Blog) TableNameWithPrefix() string {
func NewBlog() *Blog { func NewBlog() *Blog {
return &Blog{ return &Blog{
Version: time.Now().Unix(), BlogStatus: "public",
} }
} }
@ -109,7 +112,7 @@ func (b *Blog)Link() (*Blog,error) {
//如果是链接文章,则需要从链接的项目中查找文章内容 //如果是链接文章,则需要从链接的项目中查找文章内容
if b.BlogType == 1 && b.DocumentId > 0{ if b.BlogType == 1 && b.DocumentId > 0{
doc := NewDocument() doc := NewDocument()
if err := o.QueryTable(doc.TableNameWithPrefix()).Filter("document_id",b.DocumentId).One(doc,"");err != nil { if err := o.QueryTable(doc.TableNameWithPrefix()).Filter("document_id",b.DocumentId).One(doc,"release","markdown");err != nil {
beego.Error("查询文章链接对象时出错 -> ",err) beego.Error("查询文章链接对象时出错 -> ",err)
}else{ }else{
b.BlogRelease = doc.Release b.BlogRelease = doc.Release
@ -127,7 +130,79 @@ func (b *Blog) IsExist(identify string) bool {
return o.QueryTable(b.TableNameWithPrefix()).Filter("blog_identify",identify).Exist() return o.QueryTable(b.TableNameWithPrefix()).Filter("blog_identify",identify).Exist()
} }
//保存文章
func (b *Blog) Save(cols ...string) error {
o := orm.NewOrm()
func (b *Blog) FindToPager() { if b.OrderIndex == 0 {
blog := NewBlog()
if err :=o.QueryTable(b.TableNameWithPrefix()).OrderBy("-blog_id").One(blog,"blog_id");err == nil{
b.OrderIndex = b.BlogId + 1;
}else{
c,_ := o.QueryTable(b.TableNameWithPrefix()).Count()
b.OrderIndex = int(c) + 1
}
}
var err error
b.Version = time.Now().Unix()
if b.BlogId > 0 {
b.Modified = time.Now()
_,err = o.Update(b,cols...)
}else{
b.Created = time.Now()
_,err = o.Insert(b)
}
return err
} }
//分页查询文章列表
func (b *Blog) FindToPager(pageIndex, pageSize int,memberId int,status string) (blogList []*Blog, totalCount int, err error) {
o := orm.NewOrm()
offset := (pageIndex - 1) * pageSize
query := o.QueryTable(b.TableNameWithPrefix());
if memberId > 0 {
query = query.Filter("member_id",memberId)
}
if status != "" {
query = query.Filter("blog_status",status)
}
_,err = query.OrderBy("-order_index").Offset(offset).Limit(pageSize).All(&blogList)
if err != nil {
if err == orm.ErrNoRows {
return
}
beego.Error("获取文章列表时出错 ->",err)
return
}
count,err := query.Count()
if err != nil {
beego.Error("获取文章数量时出错 ->",err)
return nil,0,err
}
totalCount = int(count)
for _,blog := range blogList {
if blog.BlogType == 1 {
blog.Link()
}
}
return
}
//删除文章
func (b *Blog) Delete(blogId int) error {
o := orm.NewOrm()
_,err := o.QueryTable(b.TableNameWithPrefix()).Filter("blog_id",blogId).Delete()
if err != nil {
beego.Error("删除文章失败 ->",err)
}
return err
}

View File

@ -34,27 +34,36 @@ func (m *AttachmentResult) Find(id int) (*AttachmentResult, error) {
m.Attachment = *attach m.Attachment = *attach
book := NewBook() if attach.BookId == 0 && attach.DocumentId > 0 {
blog := NewBlog()
if e := o.QueryTable(book.TableNameWithPrefix()).Filter("book_id", attach.BookId).One(book, "book_name"); e == nil { if err := o.QueryTable(blog.TableNameWithPrefix()).Filter("blog_id",attach.DocumentId).One(blog,"blog_title");err == nil {
m.BookName = book.BookName m.BookName = blog.BlogTitle
}else{
m.BookName = "[文章不存在]"
}
} else { } else {
m.BookName = "[不存在]" book := NewBook()
}
doc := NewDocument()
if e := o.QueryTable(doc.TableNameWithPrefix()).Filter("document_id", attach.DocumentId).One(doc, "document_name"); e == nil { if e := o.QueryTable(book.TableNameWithPrefix()).Filter("book_id", attach.BookId).One(book, "book_name"); e == nil {
m.DocumentName = doc.DocumentName m.BookName = book.BookName
} else { } else {
m.DocumentName = "[不存在]" m.BookName = "[不存在]"
} }
doc := NewDocument()
if e := o.QueryTable(doc.TableNameWithPrefix()).Filter("document_id", attach.DocumentId).One(doc, "document_name"); e == nil {
m.DocumentName = doc.DocumentName
} else {
m.DocumentName = "[不存在]"
}
}
if attach.CreateAt > 0 { if attach.CreateAt > 0 {
member := NewMember() member := NewMember()
if e := o.QueryTable(member.TableNameWithPrefix()).Filter("member_id", attach.CreateAt).One(member, "account"); e == nil { if e := o.QueryTable(member.TableNameWithPrefix()).Filter("member_id", attach.CreateAt).One(member, "account"); e == nil {
m.Account = member.Account m.Account = member.Account
} }
} }
m.FileShortSize = filetil.FormatBytes(int64(attach.FileSize)) m.FileShortSize = filetil.FormatBytes(int64(attach.FileSize))
m.LocalHttpPath = strings.Replace(m.FilePath, "\\", "/", -1) m.LocalHttpPath = strings.Replace(m.FilePath, "\\", "/", -1)

View File

@ -35,6 +35,8 @@ func init() {
beego.InsertFilter("/book", beego.BeforeRouter, FilterUser) beego.InsertFilter("/book", beego.BeforeRouter, FilterUser)
beego.InsertFilter("/book/*", beego.BeforeRouter, FilterUser) beego.InsertFilter("/book/*", beego.BeforeRouter, FilterUser)
beego.InsertFilter("/api/*", beego.BeforeRouter, FilterUser) beego.InsertFilter("/api/*", beego.BeforeRouter, FilterUser)
beego.InsertFilter("/blogs", beego.BeforeRouter,FilterUser)
beego.InsertFilter("/blogs/*", beego.BeforeRouter,FilterUser)
var FinishRouter = func(ctx *context.Context) { var FinishRouter = func(ctx *context.Context) {
ctx.ResponseWriter.Header().Add("MinDoc-Version", conf.VERSION) ctx.ResponseWriter.Header().Add("MinDoc-Version", conf.VERSION)

View File

@ -65,11 +65,14 @@ func init() {
//管理文章的路由 //管理文章的路由
beego.Router("/blogs", &controllers.BlogController{},"*:ManageList") beego.Router("/blogs", &controllers.BlogController{},"*:ManageList")
beego.Router("/blogs/setting/:id", &controllers.BlogController{}, "*:ManageSetting") beego.Router("/blogs/setting/?:id", &controllers.BlogController{}, "*:ManageSetting")
beego.Router("/blogs/edit/:id",&controllers.BlogController{}, "*:ManageEdit") beego.Router("/blogs/edit/?:id",&controllers.BlogController{}, "*:ManageEdit")
beego.Router("/blogs/delete",&controllers.BlogController{}, "post:ManageDelete")
beego.Router("/blogs/upload",&controllers.BlogController{}, "post:Upload")
//读文章的路由 //读文章的路由
beego.Router("/blog", &controllers.BlogController{}, "*:List") beego.Router("/blog", &controllers.BlogController{}, "*:List")
beego.Router("/blog/attach/:id:int/:attach_id:int", &controllers.BlogController{},"get:Download")
beego.Router("/blog/:id",&controllers.BlogController{}, "*:Index") beego.Router("/blog/:id",&controllers.BlogController{}, "*:Index")
beego.Router("/api/attach/remove/", &controllers.DocumentController{}, "post:RemoveAttachment") beego.Router("/api/attach/remove/", &controllers.DocumentController{}, "post:RemoveAttachment")

View File

@ -602,6 +602,346 @@ textarea{
.pagination-container .pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover, .pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover{ .pagination-container .pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover, .pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover{
color: #272727; color: #272727;
} }
/*************文章相关样式**********/
.ui.items>.item>.content>.header {
display: inline-block;
margin: -.21425em 0 0;
font-family: 'PingFang SC','Helvetica Neue','Microsoft YaHei UI','Microsoft YaHei','Noto Sans CJK SC',Sathu,EucrosiaUPC,Arial,Helvetica,sans-serif;
font-weight: 700;
color: rgba(0,0,0,.85)
}
.ui.items>.item {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
margin: 1em 0;
width: 100%;
min-height: 0;
background: 0 0;
padding: 0;
border: none;
border-radius: 0;
-webkit-box-shadow: none;
box-shadow: none;
-webkit-transition: -webkit-box-shadow .1s ease;
transition: -webkit-box-shadow .1s ease;
transition: box-shadow .1s ease;
transition: box-shadow .1s ease,-webkit-box-shadow .1s ease;
z-index: ''
}
.ui.items>.item a {
cursor: pointer
}
.ui.items {
margin: 1.5em 0
}
.ui.items:first-child {
margin-top: 0!important
}
.ui.items:last-child {
margin-bottom: 0!important
}
.ui.items>.item:after {
display: block;
content: ' ';
height: 0;
clear: both;
overflow: hidden;
visibility: hidden
}
.ui.items>.item:first-child {
margin-top: 0
}
.ui.items>.item:last-child {
margin-bottom: 0
}
.ui.items>.item>.image {
position: relative;
-webkit-box-flex: 0;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
display: block;
float: none;
margin: 0;
padding: 0;
max-height: '';
-ms-flex-item-align: top;
align-self: top
}
.ui.items>.item>.image>img {
display: block;
width: 100%;
height: auto;
border-radius: .125rem;
border: none
}
.ui.items>.item>.image:only-child>img {
border-radius: 0
}
.ui.items>.item>.content {
display: block;
-webkit-box-flex: 1;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
background: 0 0;
margin: 0;
padding: 0;
-webkit-box-shadow: none;
box-shadow: none;
font-size: 1em;
border: none;
border-radius: 0
}
.ui.items>.item>.content:after {
display: block;
content: ' ';
height: 0;
clear: both;
overflow: hidden;
visibility: hidden
}
.ui.items>.item>.image+.content {
min-width: 0;
width: auto;
display: block;
margin-left: 0;
-ms-flex-item-align: top;
align-self: top;
padding-left: 1.5em
}
.ui.items>.item>.content>.header {
display: inline-block;
margin: -.21425em 0 0;
font-family: 'PingFang SC','Helvetica Neue','Microsoft YaHei UI','Microsoft YaHei','Noto Sans CJK SC',Sathu,EucrosiaUPC,Arial,Helvetica,sans-serif;
font-weight: 700;
color: rgba(0,0,0,.85)
}
.ui.items>.item>.content>.header:not(.ui) {
font-size: 1.14285714em
}
.ui.items>.item [class*="left floated"] {
float: left
}
.ui.items>.item [class*="right floated"] {
float: right
}
.ui.items>.item .content img {
-ms-flex-item-align: middle;
align-self: middle;
width: ''
}
.ui.items>.item .avatar img,.ui.items>.item img.avatar {
width: '';
height: '';
border-radius: 500rem
}
.ui.items>.item>.content>.description {
margin-top: .6em;
max-width: auto;
font-size: 1em;
line-height: 1.4285em;
color: rgba(0,0,0,.87);
min-height: 40px;
}
.ui.items>.item>.content p {
margin: 0 0 .5em
}
.ui.items>.item>.content p:last-child {
margin-bottom: 0
}
.ui.items>.item .meta {
margin: .5em 0 .5em;
font-size: 1em;
line-height: 1em;
color: rgba(0,0,0,.6)
}
.ui.items>.item .meta * {
margin-right: .3em
}
.ui.items>.item .meta :last-child {
margin-right: 0
}
.ui.items>.item .meta [class*="right floated"] {
margin-right: 0;
margin-left: .3em
}
.ui.items>.item>.content a:not(.ui) {
color: '';
-webkit-transition: color .1s ease;
transition: color .1s ease
}
.ui.items>.item>.content a:not(.ui):hover {
color: ''
}
.ui.items>.item>.content>a.header {
color: rgba(0,0,0,.85)
}
.ui.items>.item>.content>a.header:hover {
color: #1e70bf
}
.ui.items>.item .meta>a:not(.ui) {
color: rgba(0,0,0,.4)
}
.ui.items>.item .meta>a:not(.ui):hover {
color: rgba(0,0,0,.87)
}
.ui.items>.item>.content .favorite.icon {
cursor: pointer;
opacity: .75;
-webkit-transition: color .1s ease;
transition: color .1s ease
}
.ui.items>.item>.content .favorite.icon:hover {
opacity: 1;
color: #ffb70a
}
.ui.items>.item>.content .active.favorite.icon {
color: #ffe623
}
.ui.items>.item>.content .like.icon {
cursor: pointer;
opacity: .75;
-webkit-transition: color .1s ease;
transition: color .1s ease
}
.ui.items>.item>.content .like.icon:hover {
opacity: 1;
color: #ff2733
}
.ui.items>.item>.content .active.like.icon {
color: #ff2733
}
.ui.items>.item .extra {
display: block;
position: relative;
background: 0 0;
margin: .5rem 0 0;
width: 100%;
padding: 0 0 0;
top: 0;
left: 0;
color: rgba(0,0,0,.4);
-webkit-box-shadow: none;
box-shadow: none;
-webkit-transition: color .1s ease;
transition: color .1s ease;
border-top: none
}
.ui.items>.item .extra>* {
margin: .25rem .5rem .25rem 0
}
.ui.items>.item .extra>[class*="right floated"] {
margin: .25rem 0 .25rem .5rem
}
.ui.items>.item .extra:after {
display: block;
content: ' ';
height: 0;
clear: both;
overflow: hidden;
visibility: hidden
}
.ui.items>.item>.image:not(.ui) {
width: 175px
}
.ui.horizontal.list {
display: inline-block;
font-size: 0
}
.ui.horizontal.list>.item {
display: inline-block;
margin-left: 1em;
font-size: 1rem
}
.ui.horizontal.list:not(.celled)>.item:first-child {
margin-left: 0!important;
padding-left: 0!important
}
.ui.horizontal.list .list {
padding-left: 0;
padding-bottom: 0
}
.ui.horizontal.list a{
color: rgba(0,0,0,.4);
}
.ui.horizontal.list a:hover {
color: rgba(0,0,0,.87)
}
.ui.horizontal.list .list>.item>.content,.ui.horizontal.list .list>.item>.icon,.ui.horizontal.list .list>.item>.image,.ui.horizontal.list>.item>.content,.ui.horizontal.list>.item>.icon,.ui.horizontal.list>.item>.image {
vertical-align: middle
}
.ui.horizontal.list>.item:first-child,.ui.horizontal.list>.item:last-child {
padding-top: .21428571em;
padding-bottom: .21428571em
}
.ui.horizontal.list>.item>i.icon {
margin: 0;
padding: 0 .25em 0 0
}
.ui.horizontal.list>.item>.icon,.ui.horizontal.list>.item>.icon+.content {
float: none;
display: inline-block
}
.ui.teal.label {
background-color: #00b5ad!important;
border-color: #00b5ad!important;
color: #fff!important
}
.ui.items>.item {
border-bottom: 1px solid #efefef;
margin: 0;
padding: 1em 0
}
/**************网站底部样式*************************/ /**************网站底部样式*************************/
.footer{ .footer{

149
static/js/blog.js 100644
View File

@ -0,0 +1,149 @@
$(function () {
editormd.katexURL = {
js : window.baseUrl + "/static/katex/katex",
css : window.baseUrl + "/static/katex/katex"
};
window.editor = editormd("docEditor", {
width: "100%",
height: "100%",
path: window.baseUrl + "/static/editor.md/lib/",
toolbar: true,
placeholder: "本编辑器支持 Markdown 编辑,左边编写,右边预览。",
imageUpload: true,
imageFormats: ["jpg", "jpeg", "gif", "png", "JPG", "JPEG", "GIF", "PNG"],
imageUploadURL: window.imageUploadURL,
toolbarModes: "full",
fileUpload: true,
fileUploadURL: window.fileUploadURL,
taskList: true,
flowChart: true,
htmlDecode: "style,script,iframe,title,onmouseover,onmouseout,style",
lineNumbers: false,
sequenceDiagram: true,
tocStartLevel: 1,
tocm: true,
tex:true,
saveHTMLToTextarea: true,
onload: function() {
this.hideToolbar();
var keyMap = {
"Ctrl-S": function(cm) {
saveBlog(false);
},
"Cmd-S": function(cm){
saveBlog(false);
},
"Ctrl-A": function(cm) {
cm.execCommand("selectAll");
}
};
this.addKeyMap(keyMap);
uploadImage("docEditor", function ($state, $res) {
if ($state === "before") {
return layer.load(1, {
shade: [0.1, '#fff'] // 0.1 透明度的白色背景
});
} else if ($state === "success") {
if ($res.errcode === 0) {
var value = '![](' + $res.url + ')';
window.editor.insertValue(value);
}
}
});
},
onchange: function () {
resetEditorChanged(true);
}
});
/**
*
*/
$("#editormd-tools").on("click", "a[class!='disabled']", function () {
var name = $(this).find("i").attr("name");
if (name === "attachment") {
$("#uploadAttachModal").modal("show");
}else if (name === "save") {
saveBlog(false);
} else if (name === "template") {
$("#documentTemplateModal").modal("show");
} else if (name === "tasks") {
// 插入 GFM 任务列表
var cm = window.editor.cm;
var selection = cm.getSelection();
if (selection === "") {
cm.replaceSelection("- [x] " + selection);
} else {
var selectionText = selection.split("\n");
for (var i = 0, len = selectionText.length; i < len; i++) {
selectionText[i] = (selectionText[i] === "") ? "" : "- [x] " + selectionText[i];
}
cm.replaceSelection(selectionText.join("\n"));
}
} else {
var action = window.editor.toolbarHandlers[name];
if (action !== "undefined") {
$.proxy(action, window.editor)();
window.editor.focus();
}
}
}) ;
/**
*
* @param $is_cover
*/
function saveBlog($is_cover) {
var content = window.editor.getMarkdown();
var html = window.editor.getPreviewedHTML();
$.ajax({
beforeSend: function () {
index = layer.load(1, { shade: [0.1, '#fff'] });
},
url: window.editURL,
data: { "blogId": window.blogId,"content": content,"htmlContent": html, "cover": $is_cover ? "yes" : "no","version" : window.blogVersion},
type: "post",
timeout : 30000,
dataType: "json",
success: function ($res) {
layer.close(index);
if ($res.errcode === 0) {
resetEditorChanged(false);
window.blogVersion = $res.data.version;
console.log(window.blogVersion);
} else if($res.errcode === 6005) {
var confirmIndex = layer.confirm('', {
btn: ['', ''] // 按钮
}, function() {
layer.close(confirmIndex);
saveBlog(true);
});
} else {
layer.msg(res.message);
}
},
error : function (XMLHttpRequest, textStatus, errorThrown) {
layer.close(index);
layer.msg("服务器错误:" + errorThrown);
}
});
}
/**
*
* @param $is_change
*/
function resetEditorChanged($is_change) {
if ($is_change && !window.isLoad) {
$("#markdown-save").removeClass('disabled').addClass('change');
} else {
$("#markdown-save").removeClass('change').addClass('disabled');
}
window.isLoad = false;
}
});

View File

@ -0,0 +1,301 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>编辑文章 - Powered by MinDoc</title>
<script type="text/javascript">
window.baseUrl = "{{.BaseUrl}}";
window.editor = null;
window.editURL = "{{urlfor "BlogController.ManageEdit" "blogId" .Model.BlogId}}";
window.imageUploadURL = "{{urlfor "BlogController.Upload" "blogId" .Model.BlogId}}";
window.fileUploadURL = "";
window.blogId = {{.Model.BlogId}};
window.blogVersion = {{.Model.Version}};
window.removeAttachURL = "{{urlfor "DocumentController.RemoveAttachment"}}";
</script>
<!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/font-awesome/css/font-awesome.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/jstree/3.3.4/themes/default/style.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/editor.md/css/editormd.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/css/jstree.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/highlight/styles/vs.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/webuploader/webuploader.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/css/markdown.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/prettify/themes/prettify.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/css/markdown.preview.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]-->
</head>
<body>
<div class="m-manual manual-editor">
<div class="manual-head" id="editormd-tools" style="min-width: 1200px; position:absolute;">
<div class="editormd-group">
<a href="{{urlfor "BlogController.ManageList"}}" data-toggle="tooltip" data-title="返回"><i class="fa fa-chevron-left" aria-hidden="true"></i></a>
</div>
<div class="editormd-group">
<a href="javascript:;" id="markdown-save" data-toggle="tooltip" data-title="保存" class="disabled save"><i class="fa fa-save" aria-hidden="true" name="save"></i></a>
</div>
<div class="editormd-group">
<a href="javascript:;" data-toggle="tooltip" data-title="撤销 (Ctrl-Z)"><i class="fa fa-undo first" name="undo" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="重做 (Ctrl-Y)"><i class="fa fa-repeat last" name="redo" unselectable="on"></i></a>
</div>
<div class="editormd-group">
<a href="javascript:;" data-toggle="tooltip" data-title="粗体"><i class="fa fa-bold first" name="bold" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="斜体"><i class="fa fa-italic item" name="italic" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="删除线"><i class="fa fa-strikethrough last" name="del" unselectable="on"></i></a>
</div>
<div class="editormd-group">
<a href="javascript:;" data-toggle="tooltip" data-title="标题一"><i class="fa editormd-bold first" name="h1" unselectable="on">H1</i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="标题二"><i class="fa editormd-bold item" name="h2" unselectable="on">H2</i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="标题三"><i class="fa editormd-bold item" name="h3" unselectable="on">H3</i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="标题四"><i class="fa editormd-bold item" name="h4" unselectable="on">H4</i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="标题五"><i class="fa editormd-bold item" name="h5" unselectable="on">H5</i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="标题六"><i class="fa editormd-bold last" name="h6" unselectable="on">H6</i></a>
</div>
<div class="editormd-group">
<a href="javascript:;" data-toggle="tooltip" data-title="无序列表"><i class="fa fa-list-ul first" name="list-ul" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="有序列表"><i class="fa fa-list-ol item" name="list-ol" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="横线"><i class="fa fa-minus last" name="hr" unselectable="on"></i></a>
</div>
<div class="editormd-group">
<a href="javascript:;" data-toggle="tooltip" data-title="链接"><i class="fa fa-link first" name="link" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="引用链接"><i class="fa fa-anchor item" name="reference-link" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="添加图片"><i class="fa fa-picture-o item" name="image" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="行内代码"><i class="fa fa-code item" name="code" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="代码块" unselectable="on"><i class="fa fa-file-code-o item" name="code-block" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="添加表格"><i class="fa fa-table item" name="table" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="引用"><i class="fa fa-quote-right item" name="quote" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="GFM 任务列表"><i class="fa fa-tasks item" name="tasks" aria-hidden="true"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="附件"><i class="fa fa-paperclip item" aria-hidden="true" name="attachment"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="模板"><i class="fa fa-tachometer last" name="template"></i></a>
</div>
<div class="editormd-group pull-right">
<a href="javascript:;" data-toggle="tooltip" data-title="关闭实时预览"><i class="fa fa-eye-slash first" name="watch" unselectable="on"></i></a>
<a href="javascript:;" data-toggle="tooltip" data-title="使用帮助"><i class="fa fa-question-circle-o last" aria-hidden="true" name="help"></i></a>
</div>
<div class="editormd-group">
<a href="javascript:;" data-toggle="tooltip" data-title=""></a>
<a href="javascript:;" data-toggle="tooltip" data-title=""></a>
</div>
<div class="clearfix"></div>
</div>
<div class="manual-body">
<div class="manual-editor-container" id="manualEditorContainer" style="min-width: 920px;left: 0;">
<div class="manual-editormd">
<div id="docEditor" class="manual-editormd-active"><textarea style="display:none;">{{.Model.BlogContent}}</textarea></div>
</div>
<div class="manual-editor-status">
<div id="attachInfo" class="item">0 个附件</div>
</div>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="uploadAttachModal" tabindex="-1" role="dialog" aria-labelledby="uploadAttachModalLabel">
<div class="modal-dialog" role="document">
<form method="post" id="uploadAttachModalForm" class="form-horizontal">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">上传附件</h4>
</div>
<div class="modal-body">
<div class="attach-drop-panel">
<div class="upload-container" id="filePicker"><i class="fa fa-upload" aria-hidden="true"></i></div>
</div>
<div class="attach-list" id="attachList">
<template v-for="item in lists">
<div class="attach-item" :id="item.attachment_id">
<template v-if="item.state == 'wait'">
<div class="progress">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100">
<span class="sr-only">0% Complete (success)</span>
</div>
</div>
</template>
<template v-else-if="item.state == 'error'">
<span class="error-message">${item.message}</span>
<button type="button" class="btn btn-sm close" @click="removeAttach(item.attachment_id)">
<i class="fa fa-remove" aria-hidden="true"></i>
</button>
</template>
<template v-else>
<a :href="item.http_path" target="_blank" :title="item.file_name">${item.file_name}</a>
<span class="text">(${ formatBytes(item.file_size) })</span>
<span class="error-message">${item.message}</span>
<button type="button" class="btn btn-sm close" @click="removeAttach(item.attachment_id)">
<i class="fa fa-remove" aria-hidden="true"></i>
</button>
<div class="clearfix"></div>
</template>
</div>
</template>
</div>
</div>
<div class="modal-footer">
<span id="add-error-message" class="error-message"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="btnUploadAttachFile" data-dismiss="modal">确定</button>
</div>
</div>
</form>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="documentTemplateModal" tabindex="-1" role="dialog" aria-labelledby="请选择模板类型" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="modal-title">请选择模板类型</h4>
</div>
<div class="modal-body template-list">
<div class="container">
<div class="section">
<a data-type="normal" href="javascript:;"><i class="fa fa-file-o"></i></a>
<h3><a data-type="normal" href="javascript:;">普通文档</a></h3>
<ul>
<li>默认类型</li>
<li>简单的文本文档</li>
</ul>
</div>
<div class="section">
<a data-type="api" href="javascript:;"><i class="fa fa-file-code-o"></i></a>
<h3><a data-type="api" href="javascript:;">API文档</a></h3>
<ul>
<li>用于API文档速写</li>
<li>支持代码高亮</li>
</ul>
</div>
<div class="section">
<a data-type="code" href="javascript:;"><i class="fa fa-book"></i></a>
<h3><a data-type="code" href="javascript:;">数据字典</a></h3>
<ul>
<li>用于数据字典显示</li>
<li>表格支持</li>
</ul>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
</div>
</div>
</div>
</div>
<template id="template-normal">
{{template "document/template_normal.tpl"}}
</template>
<template id="template-api">
{{template "document/template_api.tpl"}}
</template>
<template id="template-code">
{{template "document/template_code.tpl"}}
</template>
<script src="{{cdnjs "/static/jquery/1.12.4/jquery.min.js"}}"></script>
<script src="{{cdnjs "/static/vuejs/vue.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}"></script>
<script src="{{cdnjs "/static/webuploader/webuploader.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/jstree/3.3.4/jstree.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/editor.md/editormd.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/editor.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/js/blog.js"}}" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
$("#attachInfo").on("click",function () {
$("#uploadAttachModal").modal("show");
});
window.uploader = null;
$("#uploadAttachModal").on("shown.bs.modal",function () {
if(window.uploader === null){
try {
window.uploader = WebUploader.create({
auto: true,
dnd : true,
swf: '{{.BaseUrl}}/static/webuploader/Uploader.swf',
server: '{{urlfor "BlogController.Upload"}}',
formData : { "blogId" : {{.Model.BlogId}}},
pick: "#filePicker",
fileVal : "editormd-file-file",
fileNumLimit : 1,
compress : false
}).on("beforeFileQueued",function (file) {
uploader.reset();
}).on( 'fileQueued', function( file ) {
var item = {
state : "wait",
attachment_id : file.id,
file_size : file.size,
file_name : file.name,
message : "正在上传"
};
window.vueApp.lists.splice(0,0,item);
}).on("uploadError",function (file,reason) {
for(var i in window.vueApp.lists){
var item = window.vueApp.lists[i];
if(item.attachment_id == file.id){
item.state = "error";
item.message = "上传失败";
break;
}
}
}).on("uploadSuccess",function (file, res) {
for(var index in window.vueApp.lists){
var item = window.vueApp.lists[index];
if(item.attachment_id === file.id){
if(res.errcode === 0) {
window.vueApp.lists.splice(index, 1, res.attach);
}else{
item.message = res.message;
item.state = "error";
}
break;
}
}
}).on("beforeFileQueued",function (file) {
}).on("uploadComplete",function () {
}).on("uploadProgress",function (file, percentage) {
var $li = $( '#'+file.id ),
$percent = $li.find('.progress .progress-bar');
$percent.css( 'width', percentage * 100 + '%' );
});
}catch(e){
console.log(e);
}
}
});
});
</script>
</body>
</html>

View File

@ -36,167 +36,69 @@
<div class="box-head"> <div class="box-head">
<strong class="box-title">文章列表</strong> <strong class="box-title">文章列表</strong>
&nbsp; &nbsp;
<button type="button" data-toggle="modal" data-target="#addBlogDialogModal" class="btn btn-success btn-sm pull-right">添加文章</button> <a href="{{urlfor "BlogController.ManageSetting"}}" class="btn btn-success btn-sm pull-right">添加文章</a>
</div> </div>
</div> </div>
<div class="box-body" id="bookList"> <div class="box-body" id="blogList">
<div class="book-list"> <div class="ui items">
<template v-if="lists.length <= 0"> {{range $index,$item := .ModelList}}
<div class="text-center">暂无数据</div> <div class="item blog-item">
</template> <div class="content">
<template v-else> <a class="header" href="{{urlfor "BlogController.Index" ":id" $item.BlogId}}" target="_blank">
{{if eq $item.BlogStatus "password"}}
<div class="list-item" v-for="item in lists"> <div class="ui teal label horizontal" data-tooltip="加密">密</div>
<div class="book-title"> {{end}}
<div class="pull-left"> {{$item.BlogTitle}}
<a :href="'{{.BaseUrl}}/book/' + item.identify + '/dashboard'" title="项目概要" data-toggle="tooltip"> </a>
<template v-if="item.privately_owned == 0"> <div class="description">
<i class="fa fa-unlock" aria-hidden="true"></i> <p class="line-clamp">{{$item.BlogExcerpt}}&nbsp;</p>
</template>
<template v-else-if="item.privately_owned == 1">
<i class="fa fa-lock" aria-hidden="true"></i>
</template>
${item.book_name}
</a>
</div> </div>
<div class="pull-right"> <div class="extra">
<div class="btn-group"> <div>
<a :href="'{{.BaseUrl}}/book/' + item.identify + '/dashboard'" class="btn btn-default">设置</a> <div class="ui horizontal small list">
<div class="item"><i class="fa fa-clock-o"></i> {{date $item.Modified "Y-m-d H:i:s"}}</div>
<a href="javascript:;" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <div class="item"><a href="{{urlfor "BlogController.ManageEdit" ":id" $item.BlogId}}" title="文章编辑"><i class="fa fa-edit"></i> 编辑</a></div>
<span class="caret"></span> <div class="item"><a class="delete-btn" title="删除文章" data-id="{{$item.BlogId}}"><i class="fa fa-trash"></i> 删除</a></div>
<span class="sr-only">Toggle Dropdown</span> <div class="item"><a href="{{urlfor "BlogController.ManageSetting" ":id" $item.BlogId}}" title="文章设置" class="setting-btn"><i class="fa fa-gear"></i> 设置</a></div>
</a> </div>
<ul class="dropdown-menu">
<li><a :href="'{{urlfor "DocumentController.Index" ":key" ""}}' + item.identify" target="_blank">阅读</a></li>
<template v-if="item.role_id != 3">
<li><a :href="'{{.BaseUrl}}/api/' + item.identify + '/edit'" target="_blank">编辑</a></li>
</template>
<template v-if="item.role_id == 0">
<li><a :href="'javascript:deleteBook(\''+item.identify+'\');'">删除</a></li>
<li><a :href="'javascript:copyBook(\''+item.identify+'\');'">复制</a></li>
</template>
</ul>
</div> </div>
{{/*<a :href="'{{urlfor "DocumentController.Index" ":key" ""}}' + item.identify" title="查看文档" data-toggle="tooltip" target="_blank"><i class="fa fa-eye"></i> </a>*/}}
{{/*<template v-if="item.role_id != 3">*/}}
{{/*<a :href="'/api/' + item.identify + '/edit'" title="编辑文档" data-toggle="tooltip" target="_blank"><i class="fa fa-edit" aria-hidden="true"></i> </a>*/}}
{{/*</template>*/}}
</div> </div>
<div class="clearfix"></div>
</div>
<div class="desc-text">
<template v-if="item.description === ''">
&nbsp;
</template>
<template v-else="">
<a :href="'{{.BaseUrl}}/book/' + item.identify + '/dashboard'" title="项目概要" style="font-size: 12px;">
${item.description}
</a>
</template>
</div>
<div class="info">
<span title="创建时间" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-clock-o"></i>
${(new Date(item.create_time)).format("yyyy-MM-dd hh:mm:ss")}
</span>
<span title="创建者" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-user"></i> ${item.create_name}</span>
<span title="文档数量" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-pie-chart"></i> ${item.doc_count}</span>
<span title="项目角色" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-user-secret"></i> ${item.role_name}</span>
<template v-if="item.last_modify_text !== ''">
<span title="最后编辑" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-pencil"></i> 最后编辑: ${item.last_modify_text}</span>
</template>
</div> </div>
</div> </div>
</template> {{else}}
<div class="text-center">暂无文章</div>
{{end}}
</div> </div>
<template v-if="lists.length >= 0"> <nav class="pagination-container">
<nav class="pagination-container">
{{.PageHtml}} {{.PageHtml}}
</nav> </nav>
</template>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{{template "widgets/footer.tpl" .}} {{template "widgets/footer.tpl" .}}
</div> </div>
<!-- Modal -->
<div class="modal fade" id="addBlogDialogModal" tabindex="-1" role="dialog" aria-labelledby="addBlogDialogModalLabel">
<div class="modal-dialog modal-lg" role="document" style="min-width: 900px;">
<form method="post" autocomplete="off" action="{{urlfor "BookController.Create"}}" id="addBookDialogForm" enctype="multipart/form-data">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">添加文章</h4>
</div>
<div class="modal-body">
<div class="form-group">
<div class="pull-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="标题(不超过100字)" name="book_name" id="bookName">
</div>
<div class="form-group">
<div class="pull-left" style="padding: 7px 5px 6px 0">
{{urlfor "DocumentController.Index" ":key" ""}}
</div>
<input type="text" class="form-control pull-left" style="width: 410px;vertical-align: middle" placeholder="项目唯一标识(不超过50字)" name="identify" id="identify">
<div class="clearfix"></div>
<p class="text" style="font-size: 12px;color: #999;margin-top: 6px;">文档标识只能包含小写字母、数字,以及“-”、“.”和“_”符号.</p>
</div>
<div class="form-group">
<textarea name="description" id="description" class="form-control" placeholder="描述信息不超过500个字符" style="height: 90px;"></textarea>
</div>
<div class="form-group">
<div class="col-lg-6">
<label>
<input type="radio" name="privately_owned" value="0" checked> 公开<span class="text">(任何人都可以访问)</span>
</label>
</div>
<div class="col-lg-6">
<label>
<input type="radio" name="privately_owned" value="1"> 私有<span class="text">(只要参与者或使用令牌才能访问)</span>
</label>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="clearfix"></div>
</div>
<div class="modal-footer">
<span id="form-error-message"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-success" id="btnSaveDocument" data-loading-text="保存中...">保存</button>
</div>
</div>
</form>
</div>
</div>
<!--END Modal-->
<!-- Delete Book Modal --> <!-- Delete Book Modal -->
<div class="modal fade" id="deleteBookModal" tabindex="-1" role="dialog" aria-labelledby="deleteBookModalLabel"> <div class="modal fade" id="deleteBlogModal" tabindex="-1" role="dialog" aria-labelledby="deleteBlogModalLabel">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<form method="post" id="deleteBookForm" action="{{urlfor "BookController.Delete"}}"> <form method="post" id="deleteBlogForm" action="{{urlfor "BlogController.ManageDelete"}}">
<input type="hidden" name="identify" value=""> <input type="hidden" name="blog_id" value="">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<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"> <div class="modal-body">
<span style="font-size: 14px;font-weight: 400;">确定删除项目吗?</span> <span style="font-size: 14px;font-weight: 400;">确定删除文章吗?</span>
<p></p> <p></p>
<p class="text error-message">删除项目后将无法找回。</p> <p class="text error-message">删除文章后将无法找回。</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<span id="form-error-message2" class="error-message"></span> <span id="form-error-message2" class="error-message"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button> <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="submit" id="btnDeleteBook" class="btn btn-primary" data-loading-text="删除中...">确定删除</button> <button type="submit" id="btnDeleteBlog" class="btn btn-primary" data-loading-text="删除中...">确定删除</button>
</div> </div>
</div> </div>
</form> </form>
@ -212,98 +114,13 @@
<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/main.js"}}" type="text/javascript"></script> <script src="{{cdnjs "/static/js/main.js"}}" type="text/javascript"></script>
<script type="text/javascript"> <script type="text/javascript">
/**
* 绘制项目封面
* @param $id
* @param $font
*/
function drawBookCover($id,$font) {
var draw = document.getElementById($id);
//确认浏览器是否支持<canvas>元素
if (draw.getContext) {
var context = draw.getContext('2d');
//绘制红色矩形,绿色描边
context.fillStyle = '#eee';
context.strokeStyle = '#d4d4d5';
context.strokeRect(0,0,170,230);
context.fillRect(0,0,170,230);
//设置字体样式
context.font = "bold 20px SimSun";
context.textAlign = "left";
//设置字体填充颜色
context.fillStyle = "#3E403E";
var font = $font;
var lineWidth = 0; //当前行的绘制的宽度
var lastTextIndex = 0; //已经绘制上canvas最后的一个字符的下标
var drawWidth = 155,lineHeight = 25,drawStartX = 15,drawStartY=65;
//由于改变canvas 的高度会导致绘制的纹理被清空,所以,不预先绘制,先放入到一个数组当中
var arr = [];
for(var i = 0; i<font.length; i++){
//获取当前的截取的字符串的宽度
lineWidth = context.measureText(font.substr(lastTextIndex,i-lastTextIndex)).width;
if(lineWidth > drawWidth){
//判断最后一位是否是标点符号
if(judgePunctuationMarks(font[i-1])){
arr.push(font.substr(lastTextIndex,i-lastTextIndex));
lastTextIndex = i;
}else{
arr.push(font.substr(lastTextIndex,i-lastTextIndex-1));
lastTextIndex = i-1;
}
}
//将最后多余的一部分添加到数组
if(i === font.length - 1){
arr.push(font.substr(lastTextIndex,i-lastTextIndex+1));
}
}
for(var i =0; i<arr.length; i++){
context.fillText(arr[i],drawStartX,drawStartY+i*lineHeight);
}
//判断是否是需要避开的标签符号
function judgePunctuationMarks(value) {
var arr = [".",",",";","?","!",":","\"","","。","","","","","、"];
for(var i = 0; i< arr.length; i++){
if(value === arr[i]){
return true;
}
}
return false;
}
}else{
console.log("浏览器不支持")
}
}
/**
* 将base64格式的图片转换为二进制
* @param dataURI
* @returns {Blob}
*/
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], {type: mimeString});
}
/** /**
* 删除项目 * 删除项目
*/ */
function deleteBook($id) { function deleteBlog($id) {
$("#deleteBookModal").find("input[name='identify']").val($id); $("#deleteBlogModal").find("input[name='blog_id']").val($id);
$("#deleteBookModal").modal("show"); $("#deleteBlogModal").modal("show");
} }
function copyBook($id){ function copyBook($id){
var index = layer.load() var index = layer.load()
@ -329,181 +146,30 @@
} }
$(function () { $(function () {
$("#addBookDialogModal").on("show.bs.modal",function () {
window.bookDialogModal = $(this).find("#addBookDialogForm").html();
drawBookCover("bookCover","默认封面");
}).on("hidden.bs.modal",function () {
$(this).find("#addBookDialogForm").html(window.bookDialogModal);
});
/**
* 创建项目
*/
$("body").on("click","#btnSaveDocument",function () {
var $this = $(this);
$("#blogList .delete-btn").click(function () {
var bookName = $.trim($("#bookName").val()); deleteBlog($(this).attr("data-id"));
if (bookName === "") {
return showError("项目标题不能为空")
}
if (bookName.length > 100) {
return showError("项目标题必须小于100字符");
}
var identify = $.trim($("#identify").val());
if (identify === "") {
return showError("项目标识不能为空");
}
if (identify.length > 50) {
return showError("项目标识必须小于50字符");
}
var description = $.trim($("#description").val());
if (description.length > 500) {
return showError("描述信息不超过500个字符");
}
$this.button("loading");
var draw = document.getElementById("bookCover");
var form = document.getElementById("addBookDialogForm");
var fd = new FormData(form);
if (draw.getContext) {
var dataURL = draw.toDataURL("png", 100);
var blob = dataURItoBlob(dataURL);
fd.append('image-file', blob,(new Date()).valueOf() + ".png");
}
$.ajax({
url : "{{urlfor "BookController.Create"}}",
data: fd,
type: "POST",
dataType :"json",
processData: false,
contentType: false
}).success(function (res) {
$this.button("reset");
if (res.errcode === 0) {
window.app.lists.splice(0, 0, res.data);
$("#addBookDialogModal").modal("hide");
} else {
showError(res.message);
}
$this.button("reset");
}).error(function () {
$this.button("reset");
return showError("服务器异常");
});
return false;
});
/**
* 当填写项目标题后,绘制项目封面
*/
$("#bookName").on("blur",function () {
var txt = $(this).val();
if(txt !== ""){
drawBookCover("bookCover",txt);
}
}); });
/** /**
* 删除项目 * 删除项目
*/ */
$("#deleteBookForm").ajaxForm({ $("#deleteBookForm").ajaxForm({
beforeSubmit : function () { beforeSubmit : function () {
$("#btnDeleteBook").button("loading"); $("#btnDeleteBlog").button("loading");
}, },
success : function (res) { success : function ($res) {
if(res.errcode === 0){ if($res.errcode === 0){
window.location = window.location.href; window.location = window.location.href;
}else{ }else{
showError(res.message,"#form-error-message2"); showError(res.message,"#form-error-message2");
} }
$("#btnDeleteBook").button("reset"); $("#btnDeleteBlog").button("reset");
}, },
error : function () { error : function () {
showError("服务器异常","#form-error-message2"); showError("服务器异常","#form-error-message2");
$("#btnDeleteBook").button("reset"); $("#btnDeleteBlog").button("reset");
} }
}); });
$("#btnImportBook").on("click",function () {
var $this = $(this);
var $then = $(this).parents("#importBookDialogForm");
var bookName = $.trim($then.find("input[name='book_name']").val());
if (bookName === "") {
return showError("项目标题不能为空","#import-book-form-error-message");
}
if (bookName.length > 100) {
return showError("项目标题必须小于100字符","#import-book-form-error-message");
}
var identify = $.trim($then.find("input[name='identify']").val());
if (identify === "") {
return showError("项目标识不能为空","#import-book-form-error-message");
}
var description = $.trim($then.find('textarea[name="description"]').val());
if (description.length > 500) {
return showError("描述信息不超过500个字符","#import-book-form-error-message");
}
var filesCount = $('#import-book-upload').fileinput('getFilesCount');
console.log(filesCount)
if (filesCount <= 0) {
return showError("请选择需要上传的文件","#import-book-form-error-message");
}
//$("#importBookDialogForm").submit();
$("#btnImportBook").button("loading");
$('#import-book-upload').fileinput('upload');
});
window.app = new Vue({
el : "#bookList",
data : {
lists : {{.Result}}
},
delimiters : ['${','}'],
methods : {
}
});
Vue.nextTick(function () {
$("[data-toggle='tooltip']").tooltip();
});
$("#import-book-upload").fileinput({
'uploadUrl':"{{urlfor "BookController.Import"}}",
'theme': 'fa',
'showPreview': false,
'showUpload' : false,
'required': true,
'validateInitialCount': true,
"language" : "zh",
'allowedFileExtensions': ['zip'],
'msgPlaceholder' : '请选择Zip文件',
'elErrorContainer' : "#import-book-form-error-message",
'uploadExtraData' : function () {
var book = {};
var $then = $("#importBookDialogForm");
book.book_name = $then.find("input[name='book_name']").val();
book.identify = $then.find("input[name='identify']").val();
book.description = $then.find('textarea[name="description"]').val()
return book;
}
});
$("#import-book-upload").on("fileuploaded",function (event, data, previewId, index){
if(data.response.errcode === 0 || data.response.errcode === '0'){
showSuccess(data.response.message,"#import-book-form-error-message");
}else{
showError(data.response.message,"#import-book-form-error-message");
}
$("#btnImportBook").button("reset");
return true;
});
}); });
</script> </script>
</body> </body>

View File

@ -0,0 +1,153 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>文章设置 - Powered by MinDoc</title>
<!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/font-awesome/css/font-awesome.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/css/main.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]-->
</head>
<body>
<div class="manual-reader">
{{template "widgets/header.tpl" .}}
<div class="container manual-body">
<div class="row">
<div class="page-left">
<ul class="menu">
<li {{if eq .ControllerName "BookController"}}class="active"{{end}}><a href="{{urlfor "BookController.Index"}}" class="item"><i class="fa fa-sitemap" aria-hidden="true"></i> 我的项目</a> </li>
<li {{if eq .ControllerName "BlogController"}}class="active"{{end}}><a href="{{urlfor "BlogController.ManageList"}}" class="item"><i class="fa fa-file" aria-hidden="true"></i> 我的文章</a> </li>
</ul>
</div>
<div class="page-right">
<div class="m-box">
<div class="box-head">
<strong class="box-title"> 文章设置</strong>
</div>
</div>
<div class="box-body">
<form method="post" id="gloablEditForm" action="{{urlfor "BlogController.ManageSetting"}}">
<input type="hidden" name="id" id="blogId" value="{{.Model.BlogId}}">
<input type="hidden" name="identify" value="{{.Model.BlogIdentify}}">
<div class="form-group">
<label>文章标题</label>
<input type="text" class="form-control" name="title" id="title" placeholder="文章标题" value="{{.Model.BlogTitle}}">
</div>
<div class="form-group">
<label>文章类型</label>
<div class="radio">
<label class="radio-inline">
<input type="radio" {{if eq .Model.BlogType 0}}checked{{end}} name="blog_type" value="0">普通文章<span class="text"></span>
</label>
<label class="radio-inline">
<input type="radio" {{if eq .Model.BlogType 1}}checked{{end}} name="blog_type" value="1">链接文章<span class="text"></span>
</label>
</div>
</div>
<div class="form-group" id="blogLinkDocument"{{if ne .Model.BlogType 1}} style="display: none;" {{end}}>
<label>关联文档</label>
<div class="row">
<div class="col-sm-6">
<input type="text" class="form-control" placeholder="请选择项目">
</div>
<div class="col-sm-6">
<input type="text" class="form-control" placeholder="请选择文档">
</div>
</div>
</div>
<div class="form-group">
<label>文章状态</label>
<div class="radio">
<label class="radio-inline">
<input type="radio" {{if eq .Model.BlogStatus "public"}}checked{{end}} name="status" value="public">公开<span class="text"></span>
</label>
<label class="radio-inline">
<input type="radio" {{if eq .Model.BlogStatus "password"}}checked{{end}} name="status" value="password">加密<span class="text"></span>
</label>
</div>
</div>
<div class="form-group"{{if eq .Model.BlogStatus "public"}} style="display: none;"{{end}} id="blogPassword">
<label>文章密码</label>
<input type="password" class="form-control" name="password" id="password" placeholder="文章密码" value="{{.Model.Password}}" maxlength="20">
</div>
<div class="form-group">
<label>文章摘要</label>
<textarea rows="3" class="form-control" name="excerpt" style="height: 90px" placeholder="项目描述">{{.Model.BlogExcerpt}}</textarea>
<p class="text">文章摘要不超过500个字符</p>
</div>
<div class="form-group">
<button type="submit" id="btnSaveBlogInfo" class="btn btn-success" data-loading-text="保存中...">保存</button>
<a href="{{.Referer}}" title="返回" class="btn btn-info">返回</a>
<span id="form-error-message" class="error-message"></span>
</div>
</form>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
{{template "widgets/footer.tpl" .}}
</div>
<script src="{{cdnjs "/static/jquery/1.12.4/jquery.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/js/jquery.form.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/js/main.js"}}" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
$("#gloablEditForm").ajaxForm({
beforeSubmit : function () {
var title = $.trim($("#title").val());
if (title === ""){
return showError("文章标题不能为空");
}
$("#btnSaveBlogInfo").button("loading");
},success : function ($res) {
if($res.errcode === 0) {
showSuccess("保存成功");
$("#blogId").val($res.data.blog_id);
}else{
showError($res.message);
}
$("#btnSaveBlogInfo").button("reset");
}, error : function () {
showError("服务器异常.");
$("#btnSaveBlogInfo").button("reset");
}
}).find("input[name='status']").change(function () {
var $status = $(this).val();
if($status === "password"){
$("#blogPassword").show();
}else{
$("#blogPassword").hide();
}
});
$("#gloablEditForm").find("input[name='blog_type']").change(function () {
var $link = $(this).val();
if($link == 1){
$("#blogLinkDocument").show();
}else{
$("#blogLinkDocument").hide();
}
});
});
</script>
</body>
</html>

View File

@ -55,13 +55,15 @@
{{end}} {{end}}
</div> </div>
<div class="form-group"> <div class="form-group">
<label>项目名称</label> <label>项目/文章名称</label>
<input type="text" value="{{.Model.BookName}}" class="form-control input-readonly" readonly placeholder="项目名称"> <input type="text" value="{{.Model.BookName}}" class="form-control input-readonly" readonly placeholder="项目名称">
</div> </div>
{{if ne .Model.BookId 0}}
<div class="form-group"> <div class="form-group">
<label>文档名称</label> <label>文档名称</label>
<input type="text" value="{{.Model.DocumentName}}" class="form-control input-readonly" readonly placeholder="文档名称"> <input type="text" value="{{.Model.DocumentName}}" class="form-control input-readonly" readonly placeholder="文档名称">
</div> </div>
{{end}}
<div class="form-group"> <div class="form-group">
<label>文件路径</label> <label>文件路径</label>
<input type="text" value="{{.Model.FilePath}}" class="form-control input-readonly" readonly placeholder="文件路径"> <input type="text" value="{{.Model.FilePath}}" class="form-control input-readonly" readonly placeholder="文件路径">

View File

@ -48,7 +48,7 @@
<tr> <tr>
<th>#</th> <th>#</th>
<th>附件名称</th> <th>附件名称</th>
<th>项目名称</th> <th>项目/文章名称</th>
<th>文件大小</th> <th>文件大小</th>
<th>是否存在</th> <th>是否存在</th>
<th>操作</th> <th>操作</th>