feat:实现项目集功能

pull/425/head
lifei6671 2018-11-20 20:36:14 +08:00
parent 4702334604
commit 91df6bb024
17 changed files with 1034 additions and 23 deletions

View File

@ -105,6 +105,7 @@ func RegisterModel() {
new(models.Team),
new(models.TeamMember),
new(models.TeamRelationship),
new(models.Itemsets),
)
gob.Register(models.Blog{})
gob.Register(models.Document{})
@ -449,6 +450,7 @@ func RegisterAutoLoadConfig() {
}
}
}
//注册错误处理方法.
func RegisterError() {
beego.ErrorHandler("404", func(writer http.ResponseWriter, request *http.Request) {

View File

@ -135,8 +135,18 @@ func initialization() {
book.Theme = "default"
if err := book.Insert(); err != nil {
panic("Book.Insert => " + err.Error())
os.Exit(0)
panic("初始化项目失败 -> " + err.Error())
os.Exit(1)
}
}
if !models.NewItemsets().Exist(1) {
item := models.NewItemsets()
item.ItemName = "默认项目"
item.MemberId = 1
if err := item.Save(); err != nil {
panic("初始化项目集失败 -> " + err.Error())
os.Exit(1)
}
}
}

View File

@ -144,6 +144,7 @@ func (c *BookController) SaveBook() {
enableShare := strings.TrimSpace(c.GetString("enable_share")) == "on"
isUseFirstDocument := strings.TrimSpace(c.GetString("is_use_first_document")) == "on"
autoSave := strings.TrimSpace(c.GetString("auto_save")) == "on"
itemId,_ := c.GetInt("itemId")
if strings.Count(description, "") > 500 {
c.JsonResult(6004, "项目描述不能大于500字")
@ -157,6 +158,9 @@ func (c *BookController) SaveBook() {
c.JsonResult(6005, "最多允许添加10个标签")
}
}
if !models.NewItemsets().Exist(itemId) {
c.JsonResult(6006,"项目集不存在")
}
if editor != "markdown" && editor != "html" {
editor = "markdown"
}
@ -170,6 +174,7 @@ func (c *BookController) SaveBook() {
book.HistoryCount = historyCount
book.IsDownload = 0
book.BookPassword = c.GetString("bPassword")
book.ItemId = itemId
if autoRelease {
book.AutoRelease = 1
@ -432,6 +437,7 @@ func (c *BookController) Create() {
description := strings.TrimSpace(c.GetString("description", ""))
privatelyOwned, _ := strconv.Atoi(c.GetString("privately_owned"))
commentStatus := c.GetString("comment_status")
itemId, _ := c.GetInt("itemId")
if bookName == "" {
c.JsonResult(6001, "项目名称不能为空")
@ -451,6 +457,9 @@ func (c *BookController) Create() {
if privatelyOwned != 0 && privatelyOwned != 1 {
privatelyOwned = 1
}
if !models.NewItemsets().Exist(itemId) {
c.JsonResult(6005, "项目集不存在")
}
if commentStatus != "open" && commentStatus != "closed" && commentStatus != "group_only" && commentStatus != "registered_only" {
commentStatus = "closed"
}
@ -503,6 +512,7 @@ func (c *BookController) Create() {
book.IsUseFirstDocument = 1
book.IsDownload = 1
book.AutoRelease = 0
book.ItemId = itemId
book.Editor = "markdown"
book.Theme = "default"
@ -563,6 +573,7 @@ func (c *BookController) Import() {
identify := strings.TrimSpace(c.GetString("identify"))
description := strings.TrimSpace(c.GetString("description", ""))
privatelyOwned, _ := strconv.Atoi(c.GetString("privately_owned"))
itemId, _ := c.GetInt("itemId")
if bookName == "" {
c.JsonResult(6001, "项目名称不能为空")
@ -576,6 +587,9 @@ func (c *BookController) Import() {
if ok, err := regexp.MatchString(`^[a-z]+[a-zA-Z0-9_\-]*$`, identify); !ok || err != nil {
c.JsonResult(6003, "项目标识只能包含小写字母、数字,以及“-”和“_”符号,并且只能小写字母开头")
}
if !models.NewItemsets().Exist(itemId) {
c.JsonResult(6007, "项目集不存在")
}
if strings.Count(identify, "") > 50 {
c.JsonResult(6004, "文档标识不能超过50字")
}
@ -612,6 +626,7 @@ func (c *BookController) Import() {
book.MemberId = c.Member.MemberId
book.CommentCount = 0
book.Version = time.Now().Unix()
book.ItemId = itemId
book.Editor = "markdown"
book.Theme = "default"
@ -915,6 +930,7 @@ func (c *BookController) TeamDelete() {
c.JsonResult(0, "OK")
}
//团队搜索.
func (c *BookController) TeamSearch() {
c.Prepare()
@ -928,7 +944,22 @@ func (c *BookController) TeamSearch() {
searchResult, err := models.NewTeamRelationship().FindNotJoinBookByBookIdentify(book.BookId, keyword, 10)
if err != nil {
c.JsonResult(500, err.Error())
c.JsonResult(500, err.Error(), searchResult)
}
c.JsonResult(0, "OK", searchResult)
}
//项目集搜索.
func (c *BookController) ItemsetsSearch() {
c.Prepare()
keyword := strings.TrimSpace(c.GetString("q"))
searchResult, err := models.NewItemsets().FindItemsetsByName(keyword, 10)
if err != nil {
c.JsonResult(500, err.Error(), searchResult)
}
c.JsonResult(0, "OK", searchResult)

View File

@ -0,0 +1,87 @@
package controllers
import (
"github.com/lifei6671/mindoc/conf"
"github.com/lifei6671/mindoc/models"
"github.com/astaxie/beego/orm"
"github.com/lifei6671/mindoc/utils/pagination"
"github.com/astaxie/beego"
)
type ItemsetsController struct {
BaseController
}
func (c *ItemsetsController) Prepare() {
c.BaseController.Prepare()
//如果没有开启你们访问则跳转到登录
if !c.EnableAnonymous && c.Member == nil {
c.Redirect(conf.URLFor("AccountController.Login"), 302)
return
}
}
func (c *ItemsetsController) Index() {
c.Prepare()
c.TplName = "items/index.tpl"
pageIndex, _ := c.GetInt("page", 0)
items, totalCount, err := models.NewItemsets().FindToPager(pageIndex, conf.PageSize)
if err != nil && err != orm.ErrNoRows {
c.ShowErrorPage(500, err.Error())
}
c.Data["TotalPages"] = pageIndex
if err == orm.ErrNoRows || len(items) <= 0 {
c.Data["Lists"] = items
c.Data["PageHtml"] = ""
return
}
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["Lists"] = items
}
func (c *ItemsetsController) List() {
c.Prepare()
c.TplName = "items/list.tpl"
itemKey := c.Ctx.Input.Param(":key")
pageIndex, _ := c.GetInt("page", 1)
if itemKey == "" {
c.Abort("404")
}
item, err := models.NewItemsets().FindFirst(itemKey)
if err != nil {
if err == orm.ErrNoRows {
c.Abort("404")
} else {
beego.Error(err)
c.Abort("500")
}
}
memberId := 0
if c.Member != nil {
memberId = c.Member.MemberId
}
searchResult, totalCount, err := models.NewItemsets().FindItemsetsByItemKey(itemKey, pageIndex, conf.PageSize, memberId)
if err != nil && err != orm.ErrNoRows {
c.ShowErrorPage(500, "查询文档列表时出错")
}
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["Lists"] = searchResult
c.Data["Model"] = item
}

View File

@ -1073,6 +1073,8 @@ func (c *ManagerController) TeamSearchBook() {
c.JsonResult(0, "OK", searchResult)
}
//删除团队项目.
func (c *ManagerController) TeamBookDelete() {
c.Prepare()
teamRelationshipId, _ := c.GetInt("teamRelId")
@ -1088,3 +1090,78 @@ func (c *ManagerController) TeamBookDelete() {
}
c.JsonResult(0, "OK")
}
//项目集列表.
func (c *ManagerController) Itemsets() {
c.Prepare()
c.TplName = "manager/itemsets.tpl"
pageIndex, _ := c.GetInt("page", 0)
items, totalCount, err := models.NewItemsets().FindToPager(pageIndex, conf.PageSize)
if err != nil && err != orm.ErrNoRows {
c.ShowErrorPage(500, err.Error())
}
if err == orm.ErrNoRows || len(items) <= 0 {
c.Data["Lists"] = items
c.Data["PageHtml"] = ""
return
}
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["Lists"] = items
}
//编辑或添加项目集.
func (c *ManagerController) ItemsetsEdit() {
c.Prepare()
itemId, _ := c.GetInt("itemId")
itemName := c.GetString("itemName")
itemKey := c.GetString("itemKey")
if itemName == "" || itemKey == "" {
c.JsonResult(5001, "参数错误")
}
var item *models.Itemsets
var err error
if itemId > 0 {
if item, err = models.NewItemsets().First(itemId); err != nil {
if err == orm.ErrNoRows {
c.JsonResult(5002, "项目集不存在")
} else {
c.JsonResult(5003, "查询项目集出错")
}
}
} else {
item = models.NewItemsets()
}
item.ItemKey = itemKey
item.ItemName = itemName
item.MemberId = c.Member.MemberId
item.ModifyAt = c.Member.MemberId
if err := item.Save(); err != nil {
c.JsonResult(5004, err.Error())
}
c.JsonResult(0, "OK")
}
//删除项目集.
func (c *ManagerController) ItemsetsDelete() {
c.Prepare()
itemId, _ := c.GetInt("itemId")
if err := models.NewItemsets().Delete(itemId); err != nil {
c.JsonResult(5001, err.Error())
}
c.JsonResult(0, "OK")
}

View File

@ -31,6 +31,8 @@ type Book struct {
BookId int `orm:"pk;auto;unique;column(book_id)" json:"book_id"`
// BookName 项目名称.
BookName string `orm:"column(book_name);size(500)" json:"book_name"`
//所属项目集
ItemId int `orm:"column(item_id);type(int);default(1)" json:"item_id"`
// Identify 项目唯一标识.
Identify string `orm:"column(identify);size(100);unique" json:"identify"`
//是否是自动发布 0 否/1 是
@ -112,6 +114,9 @@ func (book *Book) Insert() error {
o := orm.NewOrm()
// o.Begin()
book.BookName = utils.StripTags(book.BookName)
if book.ItemId <= 0 {
book.ItemId = 1
}
_, err := o.Insert(book)
if err == nil {
@ -125,7 +130,7 @@ func (book *Book) Insert() error {
relationship.MemberId = book.MemberId
err = relationship.Insert()
if err != nil {
logs.Error("插入项目与用户关联 => ", err)
logs.Error("插入项目与用户关联 -> ", err)
//o.Rollback()
return err
}

View File

@ -33,6 +33,8 @@ var (
type BookResult struct {
BookId int `json:"book_id"`
BookName string `json:"book_name"`
ItemId int `json:"item_id"`
ItemName string `json:"item_name"`
Identify string `json:"identify"`
OrderIndex int `json:"order_index"`
Description string `json:"description"`
@ -203,6 +205,7 @@ func (m *BookResult) ToBookResult(book Book) *BookResult {
m.HistoryCount = book.HistoryCount
m.IsDownload = book.IsDownload == 0
m.AutoSave = book.AutoSave == 1
m.ItemId = book.ItemId
if book.Theme == "" {
m.Theme = "default"
@ -224,6 +227,11 @@ func (m *BookResult) ToBookResult(book Book) *BookResult {
m.LastModifyText = member2.Account + " 于 " + doc.ModifyTime.Local().Format("2006-01-02 15:04:05")
}
if m.ItemId > 0 {
if item,err := NewItemsets().First(m.ItemId); err == nil {
m.ItemName = item.ItemName
}
}
return m
}

251
models/Itemsets.go 100644
View File

@ -0,0 +1,251 @@
package models
import (
"time"
"github.com/lifei6671/mindoc/conf"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego"
"errors"
"github.com/lifei6671/mindoc/utils/cryptil"
)
//项目集
type Itemsets struct {
ItemId int `orm:"column(item_id);pk;auto;unique" json:"item_id"`
ItemName string `orm:"column(item_name);size(500)" json:"item_name"`
ItemKey string `orm:"column(item_key);size(200);unique" json:"item_key"`
Description string `orm:"column(description);type(text);null" json:"description"`
MemberId int `orm:"column(member_id);size(100)" json:"member_id"`
CreateTime time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
ModifyTime time.Time `orm:"column(modify_time);type(datetime);null;auto_now" json:"modify_time"`
ModifyAt int `orm:"column(modify_at);type(int)" json:"modify_at"`
BookNumber int `orm:"-" json:"book_number"`
CreateTimeString string `orm:"-" json:"create_time_string"`
}
// TableName 获取对应数据库表名.
func (item *Itemsets) TableName() string {
return "itemsets"
}
// TableEngine 获取数据使用的引擎.
func (item *Itemsets) TableEngine() string {
return "INNODB"
}
func (item *Itemsets) TableNameWithPrefix() string {
return conf.GetDatabasePrefix() + item.TableName()
}
func (item *Itemsets) QueryTable() orm.QuerySeter {
return orm.NewOrm().QueryTable(item.TableNameWithPrefix())
}
func NewItemsets() *Itemsets {
return &Itemsets{}
}
func (item *Itemsets) First(itemId int) (*Itemsets, error) {
if itemId <= 0 {
return nil, ErrInvalidParameter
}
err := item.QueryTable().Filter("item_id", itemId).One(item)
if err != nil {
beego.Error("查询项目集失败 -> item_id=", itemId, err)
} else {
item.Include()
}
return item, err
}
func (item *Itemsets) FindFirst(itemKey string) (*Itemsets,error) {
err := item.QueryTable().Filter("item_key",itemKey).One(item)
if err != nil {
beego.Error("查询项目集失败 -> itemKey=", itemKey, err)
} else {
item.Include()
}
return item,err
}
func (item *Itemsets) Exist(itemId int) bool {
return item.QueryTable().Filter("item_id", itemId).Exist()
}
//保存
func (item *Itemsets) Save() (err error) {
if item.ItemName == "" {
return errors.New("项目集名称不能为空")
}
if item.ItemKey == "" {
item.ItemKey = cryptil.NewRandChars(16)
}
if item.QueryTable().Filter("item_id__ne", item.ItemId).Filter("item_key", item.ItemKey).Exist() {
return errors.New("项目集标识已存在")
}
if item.ItemId > 0 {
_, err = orm.NewOrm().Update(item)
} else {
_, err = orm.NewOrm().Insert(item)
}
return
}
//删除.
func (item *Itemsets) Delete(itemId int) (err error) {
if itemId <= 0 {
return ErrInvalidParameter
}
if itemId == 1 {
return errors.New("默认项目集不能删除")
}
if !item.Exist(itemId) {
return errors.New("项目集不存在")
}
o := orm.NewOrm()
if err := o.Begin(); err != nil {
beego.Error("开启事物失败 ->", err)
return err
}
_, err = o.QueryTable(item.TableNameWithPrefix()).Filter("item_id", itemId).Delete()
if err != nil {
beego.Error("删除项目集失败 -> item_id=", itemId, err)
o.Rollback()
}
_, err = o.Raw("update md_books set item_id=1 where item_id=?;", itemId).Exec()
if err != nil {
beego.Error("删除项目集失败 -> item_id=", itemId, err)
o.Rollback()
}
return o.Commit()
}
func (item *Itemsets) Include() (*Itemsets, error) {
item.CreateTimeString = item.CreateTime.Format("2006-01-02 15:04:05")
i, err := NewBook().QueryTable().Filter("item_id", item.ItemId).Count()
if err != nil && err != orm.ErrNoRows {
return item, err
}
item.BookNumber = int(i)
return item, nil
}
//分页查询.
func (item *Itemsets) FindToPager(pageIndex, pageSize int) (list []*Itemsets, totalCount int, err error) {
offset := (pageIndex - 1) * pageSize
_, err = item.QueryTable().OrderBy("-item_id").Offset(offset).Limit(pageSize).All(&list)
if err != nil {
return
}
c, err := item.QueryTable().Count()
if err != nil {
return
}
totalCount = int(c)
for _, item := range list {
item.Include()
}
return
}
//根据项目集名称查询.
func (item *Itemsets) FindItemsetsByName(name string, limit int) (*SelectMemberResult, error) {
result := SelectMemberResult{}
var itemsets []*Itemsets
var err error
if name == "" {
_, err = item.QueryTable().Limit(limit).All(&itemsets)
} else {
_, err = item.QueryTable().Filter("item_name__icontains", name).Limit(limit).All(&itemsets)
}
if err != nil {
beego.Error("查询项目集失败 ->", err)
return &result, err
}
items := make([]KeyValueItem, 0)
for _, m := range itemsets {
item := KeyValueItem{}
item.Id = m.ItemId
item.Text = m.ItemName
items = append(items, item)
}
result.Result = items
return &result, err
}
//根据项目集标识查询项目集的项目列表.
func (item *Itemsets) FindItemsetsByItemKey(key string, pageIndex, pageSize, memberId int) (books []*BookResult, totalCount int, err error){
o := orm.NewOrm()
err = item.QueryTable().Filter("item_key",key).One(item)
if err != nil {
return nil,0,err
}
offset := (pageIndex - 1) * pageSize
//如果是登录用户
if memberId > 0 {
sql1 := `SELECT COUNT(*)
FROM md_books AS book
LEFT JOIN md_relationship AS rel ON rel.book_id = book.book_id AND rel.member_id = ?
left join (select *
from (select book_id,team_member_id,role_id
from md_team_relationship as mtr
left join md_team_member as mtm on mtm.team_id=mtr.team_id and mtm.member_id=? order by role_id desc )as t group by t.book_id) as team on team.book_id = book.book_id
WHERE book.item_id = ? AND (relationship_id > 0 OR book.privately_owned = 0 or team.team_member_id > 0)`
err = o.Raw(sql1, memberId, memberId, item.ItemId).QueryRow(&totalCount)
if err != nil {
return
}
sql2 := `SELECT book.*,rel1.*,member.account AS create_name FROM md_books AS book
LEFT JOIN md_relationship AS rel ON rel.book_id = book.book_id AND rel.member_id = ?
left join (select * from (select book_id,team_member_id,role_id
from md_team_relationship as mtr
left join md_team_member as mtm on mtm.team_id=mtr.team_id and mtm.member_id=? order by role_id desc )as t group by t.book_id) as team
on team.book_id = book.book_id
LEFT JOIN md_relationship AS rel1 ON rel1.book_id = book.book_id AND rel1.role_id = 0
LEFT JOIN md_members AS member ON rel1.member_id = member.member_id
WHERE book.item_id = ? AND (rel.relationship_id > 0 OR book.privately_owned = 0 or team.team_member_id > 0)
ORDER BY order_index DESC ,book.book_id DESC LIMIT ?,?`
_, err = o.Raw(sql2, memberId, memberId, item.ItemId, offset, pageSize).QueryRows(&books)
return
} else {
count, err1 := o.QueryTable(NewBook().TableNameWithPrefix()).Filter("privately_owned", 0).Filter("item_id", item.ItemId).Count()
if err1 != nil {
err = err1
return
}
totalCount = int(count)
sql := `SELECT book.*,rel.*,member.account AS create_name FROM md_books AS book
LEFT JOIN md_relationship AS rel ON rel.book_id = book.book_id AND rel.role_id = 0
LEFT JOIN md_members AS member ON rel.member_id = member.member_id
WHERE book.item_id = ? AND book.privately_owned = 0 ORDER BY order_index DESC ,book.book_id DESC LIMIT ?,?`
_, err = o.Raw(sql, item.ItemId, offset, pageSize).QueryRows(&books)
return
}
}

View File

@ -57,6 +57,10 @@ func init() {
beego.Router("/manager/team/book/delete", &controllers.ManagerController{}, "POST:TeamBookDelete")
beego.Router("/manager/team/book/search", &controllers.ManagerController{}, "*:TeamSearchBook")
beego.Router("/manager/itemsets", &controllers.ManagerController{},"*:Itemsets")
beego.Router("/manager/itemsets/edit", &controllers.ManagerController{},"post:ItemsetsEdit")
beego.Router("/manager/itemsets/delete", &controllers.ManagerController{},"post:ItemsetsDelete")
beego.Router("/setting", &controllers.SettingController{}, "*:Index")
beego.Router("/setting/password", &controllers.SettingController{}, "*:Password")
@ -72,6 +76,8 @@ func init() {
beego.Router("/book/create", &controllers.BookController{}, "*:Create")
beego.Router("/book/itemsets/search", &controllers.BookController{}, "*:ItemsetsSearch")
beego.Router("/book/users/create", &controllers.BookMemberController{}, "post:AddMember")
beego.Router("/book/users/change", &controllers.BookMemberController{}, "post:ChangeRole")
beego.Router("/book/users/delete", &controllers.BookMemberController{}, "post:RemoveMember")
@ -139,4 +145,8 @@ func init() {
beego.Router("/tag/:key", &controllers.LabelController{}, "get:Index")
beego.Router("/tags", &controllers.LabelController{}, "get:List")
beego.Router("/items", &controllers.ItemsetsController{},"get:Index")
beego.Router("/items/:key", &controllers.ItemsetsController{},"get:List")
}

View File

@ -13,6 +13,8 @@ import (
"crypto/rand"
)
var stdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
//对称加密与解密之加密【从Beego中提取出来的】
//@param value 需要加密的字符串
//@param secret 加密密钥
@ -71,7 +73,6 @@ func Sha1Crypt(str string, salt ...interface{}) (CryptStr string) {
return fmt.Sprintf("%x", sha1.Sum([]byte(str)))
}
//生成Guid字串
func UniqueId() string {
b := make([]byte, 48)
@ -81,3 +82,34 @@ func UniqueId() string {
}
return Md5Crypt(base64.URLEncoding.EncodeToString(b))
}
//生成指定长度的字符串.
func NewRandChars(length int) string {
if length == 0 {
return ""
}
clen := len(stdChars)
if clen < 2 || clen > 256 {
panic("Wrong charset length for NewLenChars()")
}
maxrb := 255 - (256 % clen)
b := make([]byte, length)
r := make([]byte, length+(length/4)) // storage for random bytes.
i := 0
for {
if _, err := rand.Read(r); err != nil {
panic("Error reading random bytes: " + err.Error())
}
for _, rb := range r {
c := int(rb)
if c > maxrb {
continue // Skip this number to avoid modulo bias.
}
b[i] = stdChars[c%clen]
i++
if i == length {
return string(b)
}
}
}
}

View File

@ -12,6 +12,7 @@
<link href="{{cdncss "/static/font-awesome/css/font-awesome.min.css"}}" rel="stylesheet" type="text/css">
<link href="{{cdncss "/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/css/fileinput.min.css"}}" rel="stylesheet" type="text/css">
<link href="{{cdncss "/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/themes/explorer-fa/theme.css"}}" rel="stylesheet" type="text/css">
<link href="{{cdncss "/static/select2/4.0.5/css/select2.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/css/main.css" "version"}}" 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:// -->
@ -137,9 +138,20 @@
<div class="modal-body">
<div class="form-group">
<div class="pull-left" style="width: 620px">
<div class="form-group">
<div class="form-group required">
<label class="text-label col-sm-2">项目集</label>
<div class="col-sm-10">
<select class="js-data-example-ajax-add form-control" multiple="multiple" name="itemId" id="itemId"></select>
</div>
<div class="clearfix"></div>
</div>
<div class="form-group required">
<label class="text-label col-sm-2">项目标题</label>
<div class="col-sm-10">
<input type="text" class="form-control" placeholder="标题(不超过100字)" name="book_name" id="bookName">
</div>
<div class="clearfix"></div>
</div>
<div class="form-group">
<div class="pull-left" style="padding: 7px 5px 6px 0">
{{urlfor "DocumentController.Index" ":key" ""}}
@ -194,6 +206,10 @@
</div>
<div class="modal-body">
<div class="form-group">
<div class="form-group required">
<label class="text-label">项目集</label>
<select class="js-data-example-ajax-import form-control" multiple="multiple" name="itemId"></select>
</div>
<div class="form-group required">
<label class="text-label">项目标题</label>
<input type="text" class="form-control" placeholder="项目标题(不超过100字)" name="book_name" maxlength="100" value="">
@ -272,6 +288,8 @@
<script src="{{cdnjs "/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/js/fileinput.min.js"}}"></script>
<script src="{{cdnjs "/static/bootstrap/plugins/bootstrap-fileinput/4.4.7/js/locales/zh.js"}}"></script>
<script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript" ></script>
<script src="{{cdnjs "/static/select2/4.0.5/js/select2.full.min.js"}}"></script>
<script src="{{cdnjs "/static/select2/4.0.5/js/i18n/zh-CN.js"}}"></script>
<script src="{{cdnjs "/static/js/main.js"}}" type="text/javascript"></script>
<script type="text/javascript">
/**
@ -400,6 +418,28 @@
$("#addBookDialogModal").on("show.bs.modal",function () {
window.bookDialogModal = $(this).find("#addBookDialogForm").html();
drawBookCover("bookCover","默认封面");
$('.js-data-example-ajax-add').select2({
language: "zh-CN",
minimumInputLength : 1,
minimumResultsForSearch: Infinity,
maximumSelectionLength:1,
width : "100%",
ajax: {
url: '{{urlfor "BookController.ItemsetsSearch"}}',
dataType: 'json',
data: function (params) {
return {
q: params.term, // search term
page: params.page
};
},
processResults: function (data, params) {
return {
results : data.data.results
}
}
}
});
}).on("hidden.bs.modal",function () {
$(this).find("#addBookDialogForm").html(window.bookDialogModal);
});
@ -429,6 +469,28 @@
return book;
}
});
$('.js-data-example-ajax-import').select2({
language: "zh-CN",
minimumInputLength : 1,
minimumResultsForSearch: Infinity,
maximumSelectionLength:1,
width : "100%",
ajax: {
url: '{{urlfor "BookController.ItemsetsSearch"}}',
dataType: 'json',
data: function (params) {
return {
q: params.term, // search term
page: params.page
};
},
processResults: function (data, params) {
return {
results : data.data.results
}
}
}
});
}).on("hidden.bs.modal",function () {
$(this).find("#importBookDialogForm").html(window.importBookDialogModal);
});
@ -439,7 +501,10 @@
$("body").on("click","#btnSaveDocument",function () {
var $this = $(this);
var itemId = $("#itemId").val();
if (itemId <= 0) {
return showError("请选择项目集")
}
var bookName = $.trim($("#bookName").val());
if (bookName === "") {
return showError("项目标题不能为空")
@ -504,6 +569,10 @@
console.log("aa");
var $then = $(this).parents("#importBookDialogForm");
var itemId = $then.find("input[name='itemId']").val();
if (itemId <= 0) {
return showError("请选择项目集")
}
var bookName = $.trim($then.find("input[name='book_name']").val());

View File

@ -14,6 +14,7 @@
<link href="{{cdncss "/static/cropper/2.3.4/cropper.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/bootstrap/plugins/tagsinput/bootstrap-tagsinput.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/bootstrap/plugins/bootstrap-switch/css/bootstrap3//bootstrap-switch.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/select2/4.0.5/css/select2.min.css"}}" rel="stylesheet">
<link href="{{cdncss "/static/css/main.css" "version"}}" rel="stylesheet">
</head>
@ -60,6 +61,12 @@
<input type="text" class="form-control" value="{{urlfor "DocumentController.Index" ":key" .Model.Identify}}" placeholder="项目唯一标识" disabled>
<p class="text">项目标识用来标记项目的唯一性,不可修改。</p>
</div>
<div class="form-group">
<label>项目集</label>
<select class="js-data-example-ajax form-control" multiple="multiple" name="itemId">
<option value="{{.Model.ItemId}}" selected="selected">{{.Model.ItemName}}</option>
</select>
</div>
<div class="form-group">
<label>历史记录数量</label>
<input type="text" class="form-control" name="history_count" value="{{.Model.HistoryCount}}" placeholder="历史记录数量">
@ -312,6 +319,8 @@
<script src="{{cdnjs "/static/js/jquery.form.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/bootstrap/plugins/tagsinput/bootstrap-tagsinput.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/bootstrap/plugins/bootstrap-switch/js/bootstrap-switch.min.js"}}" type="text/javascript"></script>
<script src="{{cdnjs "/static/select2/4.0.5/js/select2.full.min.js"}}"></script>
<script src="{{cdnjs "/static/select2/4.0.5/js/i18n/zh-CN.js"}}"></script>
<script src="{{cdnjs "/static/js/main.js"}}" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
@ -431,7 +440,28 @@
$("#btnTransferBook").button("reset");
}
});
$('.js-data-example-ajax').select2({
language: "zh-CN",
minimumInputLength : 1,
minimumResultsForSearch: Infinity,
maximumSelectionLength:1,
width : "100%",
ajax: {
url: '{{urlfor "BookController.ItemsetsSearch"}}',
dataType: 'json',
data: function (params) {
return {
q: params.term, // search term
page: params.page
};
},
processResults: function (data, params) {
return {
results : data.data.results
}
}
}
});
try {
var uploader = WebUploader.create({
auto: false,

View File

@ -0,0 +1,74 @@
<!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>
<meta name="keywords" content="MinDoc,文档在线管理系统,WIKI,wiki,wiki在线,文档在线管理,接口文档在线管理,接口文档管理">
<meta name="description" content="MinDoc文档在线管理系统 {{.site_description}}">
<meta name="author" content="Minho" />
<meta name="site" content="https://www.iminho.me" />
<!-- 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" "version"}}" rel="stylesheet">
</head>
<body>
<div class="manual-reader manual-container manual-search-reader">
{{template "widgets/header.tpl" .}}
<div class="container manual-body">
<div class="search-head">
<strong class="search-title">项目集列表</strong>
</div>
<div class="row">
<div class="hide tag-container-outer" style="border: 0;margin-top: 0;padding: 5px 15px;min-height: 200px;">
<div class="attach-list" id="ItemsetsList">
<table class="table">
<thead>
<tr>
<th width="10%">#</th>
<th width="30%">项目集名称</th>
<th width="20%">项目集标识</th>
<th width="10%">项目数量</th>
<th width="15%">创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{{range $index,$item := .Lists}}
<tr>
<td>{{$item.ItemId}}</td>
<td>{{$item.ItemName}}</td>
<td>{{$item.ItemKey}}</td>
<td>{{$item.BookNumber}}</td>
<td>{{$item.CreateTimeString}}</td>
<td>
<a href="{{urlfor "ItemsetsController.List" ":key" $item.ItemKey}}" class="btn btn-success btn-sm" target="_blank">详情</a>
</td>
</tr>
{{else}}
<tr><td class="text-center" colspan="6">暂无数据</td></tr>
{{end}}
</tbody>
</table>
</div>
</div>
<nav class="pagination-container">
{{if gt .TotalPages 1}}
{{.PageHtml}}
{{end}}
<div class="clearfix"></div>
</nav>
</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>
{{.Scripts}}
</body>
</html>

View File

@ -0,0 +1,66 @@
<!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>项目集{{.Model.ItemName}}的项目列表 - Powered by MinDoc</title>
<meta name="keywords" content="MinDoc,文档在线管理系统,WIKI,wiki,wiki在线,文档在线管理,接口文档在线管理,接口文档管理,{{.Model.ItemName}}">
<meta name="description" content="MinDoc文档在线管理系统 {{.site_description}}">
<!-- 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" "version"}}" rel="stylesheet">
</head>
<body>
<div class="manual-reader manual-container manual-search-reader">
{{template "widgets/header.tpl" .}}
<div class="container manual-body">
<div class="search-head">
<strong class="search-title">显示项目集为"{{.Model.ItemName}}"的项目</strong>
</div>
<div class="row">
<div class="manual-list">
{{range $index,$item := .Lists}}
<div class="list-item">
<dl class="manual-item-standard">
<dt>
<a href="{{urlfor "DocumentController.Index" ":key" $item.Identify}}" title="{{$item.BookName}}-{{$item.CreateName}}" target="_blank">
<img src="{{cdnimg $item.Cover}}" class="cover" alt="{{$item.BookName}}-{{$item.CreateName}}">
</a>
</dt>
<dd>
<a href="{{urlfor "DocumentController.Index" ":key" $item.Identify}}" class="name" title="{{$item.BookName}}-{{$item.CreateName}}" target="_blank">{{$item.BookName}}</a>
</dd>
<dd>
<span class="author">
<b class="text">作者</b>
<b class="text">-</b>
<b class="text">{{if eq $item.RealName "" }}{{$item.CreateName}}{{else}}{{$item.RealName}}{{end}}</b>
</span>
</dd>
</dl>
</div>
{{else}}
<div class="search-empty">
<img src="{{cdnimg "/static/images/search_empty.png"}}" class="empty-image">
<span class="empty-text">暂无项目</span>
</div>
{{end}}
<div class="clearfix"></div>
</div>
<nav class="pagination-container">
{{.PageHtml}}
</nav>
</div>
</div>
{{template "widgets/footer.tpl" .}}
</div>
<script src="{{cdnjs "/static/jquery/1.12.4/jquery.min.js"}}"></script>
<script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}"></script>
{{.Scripts}}
</body>
</html>

View File

@ -0,0 +1,257 @@
<!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" type="text/css">
<link href="{{cdncss "/static/font-awesome/css/font-awesome.min.css"}}" rel="stylesheet" type="text/css">
<link href="{{cdncss "/static/css/main.css" "version"}}" rel="stylesheet">
</head>
<body>
<div class="manual-reader">
{{template "widgets/header.tpl" .}}
<div class="container manual-body">
<div class="row">
{{template "manager/widgets.tpl" "itemsets"}}
<div class="page-right">
<div class="m-box">
<div class="box-head">
<strong class="box-title">项目集管理</strong>
{{if eq .Member.Role 0}}
<button type="button" class="btn btn-success btn-sm pull-right" data-toggle="modal" data-target="#addItemsetsDialogModal"><i class="fa fa-plus" aria-hidden="true"></i> 创建项目集</button>
{{end}}
</div>
</div>
<div class="box-body">
<div class="attach-list" id="ItemsetsList">
<table class="table">
<thead>
<tr>
<th width="10%">#</th>
<th width="30%">项目集名称</th>
<th width="20%">项目集标识</th>
<th width="20%">项目数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{{range $index,$item := .Lists}}
<tr>
<td>{{$item.ItemId}}</td>
<td>{{$item.ItemName}}</td>
<td>{{$item.ItemKey}}</td>
<td>{{$item.BookNumber}}</td>
<td>
<button type="button" class="btn btn-sm btn-default" data-id="{{$item.ItemId}}" data-method="edit" data-name="{{$item.ItemName}}" data-key="{{$item.ItemKey}}">编辑</button>
<button type="button" data-method="delete" class="btn btn-danger btn-sm" data-id="{{$item.ItemId}}" data-loading-text="删除中...">删除</button>
<a href="{{urlfor "ItemsetsController.Index" ":key" $item.ItemKey}}" class="btn btn-success btn-sm" target="_blank">详情</a>
</td>
</tr>
{{else}}
<tr><td class="text-center" colspan="6">暂无数据</td></tr>
{{end}}
</tbody>
</table>
<nav class="pagination-container">
{{.PageHtml}}
</nav>
</div>
</div>
</div>
</div>
</div>
{{template "widgets/footer.tpl" .}}
</div>
<!-- Modal -->
<div class="modal fade" id="addItemsetsDialogModal" tabindex="-1" role="dialog" aria-labelledby="addItemsetsDialogModalLabel">
<div class="modal-dialog">
<form method="post" autocomplete="off" class="form-horizontal" action="{{urlfor "ManagerController.ItemsetsEdit"}}" id="addItemsetsDialogForm">
<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">
<label class="col-sm-3 control-label" for="account">项目集名称<span class="error-message">*</span></label>
<div class="col-sm-9">
<input type="text" name="itemName" class="form-control" placeholder="项目集名称" id="itemName" maxlength="50">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" for="itemKey">项目集标识<span class="error-message">*</span></label>
<div class="col-sm-9">
<input type="text" name="itemKey" id="itemKey" class="form-control" placeholder="项目集标识" maxlength="50">
<p class="text">项目集标识只能由字母和数字组成且在2-100字符之间</p>
</div>
</div>
<div class="clearfix"></div>
</div>
<div class="modal-footer">
<span id="create-form-error-message"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="submit" class="btn btn-success" data-loading-text="保存中..." id="btnAddItemsets">保存
</button>
</div>
</div>
</form>
</div>
</div><!--END Modal-->
<div class="modal fade" id="editItemsetsDialogModal" tabindex="-1" role="dialog" aria-labelledby="editItemsetsDialogModalLabel">
<div class="modal-dialog" role="document">
<form method="post" autocomplete="off" class="form-horizontal" action="{{urlfor "ManagerController.ItemsetsEdit"}}" id="editItemsetsDialogForm">
<input type="hidden" name="itemId" value="">
<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">
<label class="col-sm-2 control-label" for="itemName">项目集名称<span class="error-message">*</span></label>
<div class="col-sm-10">
<input type="text" name="itemName" id="itemName" class="form-control" placeholder="项目集名称" maxlength="50">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="itemKey">项目集标识<span class="error-message">*</span></label>
<div class="col-sm-10">
<input type="text" name="itemKey" id="itemKey" class="form-control" placeholder="项目集标识" maxlength="50">
</div>
</div>
<div class="clearfix"></div>
</div>
<div class="modal-footer">
<span id="edit-form-error-message"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="submit" class="btn btn-success" data-loading-text="保存中..." id="btnEditItemsets">保存
</button>
</div>
</div>
</form>
</div>
</div><!--END Modal-->
<script src="{{cdnjs "/static/jquery/1.12.4/jquery.min.js"}}"></script>
<script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}"></script>
<script src="{{cdnjs "/static/js/jquery.form.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 type="text/javascript">
$(function () {
var editItemsetsDialogModal = $("#editItemsetsDialogModal");
var addItemsetsDialogForm = $("#addItemsetsDialogForm");
var editItemsetsDialogForm = $("#editItemsetsDialogForm");
editItemsetsDialogModal.on("shown.bs.modal",function () {
editItemsetsDialogModal.find("input[name='itemName']").focus();
});
$("#addItemsetsDialogModal").on("show.bs.modal", function () {
window.addItemsetsDialogModalHtml = $(this).find("form").html();
}).on("hidden.bs.modal", function () {
$(this).find("form").html(window.addItemsetsDialogModalHtml);
});
addItemsetsDialogForm.ajaxForm({
beforeSubmit: function () {
var $itemName = addItemsetsDialogForm.find("input[name='itemName']").val();
var $itemKey = addItemsetsDialogForm.find("input[name='itemKey']").val();
if ($itemName == "") {
showError("项目集名称不能为空","#create-form-error-message");
}
if ($itemKey == "") {
showError("项目集标识不能为空","#create-form-error-message");
}
$("#btnAddItemsets").button("loading");
showError("","#create-form-error-message");
return true;
},
success: function ($res) {
if ($res.errcode === 0) {
window.location = window.document.location;
} else {
showError($res.message,"#create-form-error-message");
}
},
error: function () {
showError("服务器异常","#create-form-error-message");
},
complete: function () {
$("#btnAddItemsets").button("reset");
}
});
editItemsetsDialogForm.ajaxForm({
beforeSubmit: function () {
var $itemName = editItemsetsDialogForm.find("input[name='itemName']").val();
var $itemKey = editItemsetsDialogForm.find("input[name='itemKey']").val();
if ($itemName == "") {
showError("项目集名称不能为空","#edit-form-error-message");
}
if ($itemKey == "") {
showError("项目集标识不能为空","#edit-form-error-message");
}
$("#btnEditItemsets").button("loading");
showError("","#edit-form-error-message");
return true;
} ,
success : function ($res) {
if ($res.errcode === 0) {
window.location = window.document.location;
} else {
showError($res.message,"#edit-form-error-message");
}
},
error: function () {
showError("服务器异常","#edit-form-error-message");
},
complete: function () {
$("#btnEditItemsets").button("reset");
}
});
$("#ItemsetsList").on("click","button[data-method='delete']",function () {
var id = $(this).attr("data-id");
var $this = $(this);
$(this).button("loading");
$.ajax({
url : "{{urlfor "ManagerController.ItemsetsDelete" ":id" ""}}" + id,
type : "post",
dataType : "json",
success : function (res) {
if(res.errcode === 0){
$this.closest("tr").remove().empty();
}else {
layer.msg(res.message);
}
},
error : function () {
layer.msg("服务器异常");
},
complete : function () {
$this.button("reset");
}
});
}).on("click","button[data-method='edit']",function () {
var $itemId = $(this).attr("data-id");
var $itemName = $(this).attr("data-name");
var $itemKey = $(this).attr("data-key");
editItemsetsDialogModal.find("input[name='itemId']").val($itemId);
editItemsetsDialogModal.find("input[name='itemName']").val($itemName);
editItemsetsDialogModal.find("input[name='itemKey']").val($itemKey);
editItemsetsDialogModal.modal("show");
});
});
</script>
</body>
</html>

View File

@ -4,6 +4,8 @@
<li{{if eq "users" .}} class="active"{{end}}><a href="{{urlfor "ManagerController.Users" }}" class="item"><i class="fa fa-user" aria-hidden="true"></i> 用户管理</a> </li>
<li{{if eq "team" .}} class="active"{{end}}><a href="{{urlfor "ManagerController.Team" }}" class="item"><i class="fa fa-group" aria-hidden="true"></i> 团队管理</a> </li>
<li{{if eq "books" .}} class="active"{{end}}><a href="{{urlfor "ManagerController.Books" }}" class="item"><i class="fa fa-book" aria-hidden="true"></i> 项目管理</a> </li>
<li{{if eq "itemsets" .}} class="active"{{end}}><a href="{{urlfor "ManagerController.Itemsets" }}" class="item"><i class="fa fa-archive" aria-hidden="true"></i> 项目集管理</a> </li>
{{/*<li><a href="{{urlfor "ManagerController.Comments" }}" class="item"><i class="fa fa-comments-o" aria-hidden="true"></i> </a> </li>*/}}
<li{{if eq "setting" .}} class="active"{{end}}><a href="{{urlfor "ManagerController.Setting" }}" class="item"><i class="fa fa-cogs" aria-hidden="true"></i> 配置管理</a> </li>
{{/*<li{{if eq "config" .}} class="active"{{end}}><a href="{{urlfor "ManagerController.Config" }}" class="item"><i class="fa fa-file" aria-hidden="true"></i> </a> </li>*/}}

View File

@ -16,8 +16,8 @@
<li {{if eq .ControllerName "BlogController"}}{{if eq .ActionName "List" "Index"}}class="active"{{end}}{{end}}>
<a href="{{urlfor "BlogController.List" }}" title="文章">文章</a>
</li>
<li {{if eq .ControllerName "LabelController"}}class="active"{{end}}>
<a href="{{urlfor "LabelController.List" }}" title="标签">标签</a>
<li {{if eq .ControllerName "ItemsetsController"}}class="active"{{end}}>
<a href="{{urlfor "ItemsetsController.Index" }}" title="项目集">项目集</a>
</li>
</ul>
<div class="searchbar pull-left visible-lg-inline-block visible-md-inline-block">