浏览代码

实现项目列表、概述功能

Minho 8 年之前
父节点
当前提交
ca0d5094c1
共有 17 个文件被更改,包括 1219 次插入105 次删除
  1. 2 1
      conf/app.conf
  2. 200 10
      controllers/book.go
  3. 20 0
      models/base.go
  4. 104 3
      models/book.go
  5. 112 0
      models/book_result.go
  6. 31 1
      models/document.go
  7. 2 0
      models/errors.go
  8. 35 1
      models/member.go
  9. 60 0
      models/member_result.go
  10. 46 1
      models/relationship.go
  11. 2 1
      routers/router.go
  12. 22 1
      static/js/main.js
  13. 332 0
      utils/pager.go
  14. 30 20
      views/book/dashboard.tpl
  15. 56 18
      views/book/index.tpl
  16. 66 7
      views/book/setting.tpl
  17. 99 41
      views/book/users.tpl

+ 2 - 1
conf/app.conf

@@ -18,4 +18,5 @@ db_database=mindoc_db
 db_username=root
 db_username=root
 db_password=123456
 db_password=123456
 
 
-queue_size=50
+#项目默认封面
+cover=/static/images/book.jpg

+ 200 - 10
controllers/book.go

@@ -1,38 +1,228 @@
 package controllers
 package controllers
 
 
-import "strings"
+import (
+	"strings"
+	"regexp"
+	"strconv"
+	"time"
+	"encoding/json"
+	"html/template"
+
+	"github.com/lifei6671/godoc/models"
+	"github.com/lifei6671/godoc/utils"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+)
 
 
 type BookController struct {
 type BookController struct {
 	BaseController
 	BaseController
 }
 }
 
 
-func (p *BookController) Index() {
-	p.TplName = "book/index.tpl"
+func (c *BookController) Index() {
+	c.Prepare()
+	c.TplName = "book/index.tpl"
+
+	pageIndex, _ := c.GetInt("page", 1)
+
+	books,totalCount,err := models.NewBook().FindToPager(pageIndex,10,c.Member.MemberId)
+
+	if err != nil {
+		c.Abort("500")
+	}
+
+	html := utils.GetPagerHtml(c.Ctx.Request.RequestURI,pageIndex,10,totalCount)
+
+	c.Data["PageHtml"] = html
+
+	b,err := json.Marshal(books)
+
+	if err != nil {
+		c.Data["Result"] = template.JS("[]")
+	}else{
+		c.Data["Result"] = template.JS(string(b))
+	}
 }
 }
 
 
 // Dashboard 项目概要 .
 // Dashboard 项目概要 .
-func (p *BookController) Dashboard() {
-	p.TplName = "book/dashboard.tpl"
+func (c *BookController) Dashboard() {
+	c.Prepare()
+	c.TplName = "book/dashboard.tpl"
+
+	key := c.Ctx.Input.Param(":key")
+
+	if key == ""{
+		c.Abort("404")
+	}
+
+	book,err := models.NewBookResult().FindByIdentify(key,c.Member.MemberId)
+	if err != nil {
+		if err == models.ErrPermissionDenied {
+			c.Abort("403")
+		}
+		c.Abort("500")
+	}
+
+	c.Data["Model"] = *book
 }
 }
 
 
 // Setting 项目设置 .
 // Setting 项目设置 .
-func (p *BookController) Setting()  {
-	p.TplName = "book/setting.tpl"
+func (c *BookController) Setting()  {
+	c.Prepare()
+	c.TplName = "book/setting.tpl"
+
+	key := c.Ctx.Input.Param(":key")
+
+	if key == ""{
+		c.Abort("404")
+	}
+
+	book,err := models.NewBookResult().FindByIdentify(key,c.Member.MemberId)
+	if err != nil {
+		if err == models.ErrPermissionDenied {
+			c.Abort("403")
+		}
+		c.Abort("500")
+	}
+
+	c.Data["Model"] = *book
+
+}
+//用户列表.
+func (c *BookController) Users() {
+	c.Prepare()
+	c.TplName = "book/users.tpl"
+
+	key := c.Ctx.Input.Param(":key")
+	pageIndex,_ := c.GetInt("page",1)
+
+	if key == ""{
+		c.Abort("404")
+	}
+
+	book,err := models.NewBookResult().FindByIdentify(key,c.Member.MemberId)
+	if err != nil {
+		if err == models.ErrPermissionDenied {
+			c.Abort("403")
+		}
+		c.Abort("500")
+	}
+
+	c.Data["Model"] = *book
+
+	members,totalCount,err := models.NewMemberRelationshipResult().FindForUsersByBookId(book.BookId,pageIndex,15)
+
+	html := utils.GetPagerHtml(c.Ctx.Request.RequestURI,pageIndex,10,totalCount)
+
+	c.Data["PageHtml"] = html
+	b,err := json.Marshal(members)
+
+	if err != nil {
+		c.Data["Result"] = template.JS("[]")
+	}else{
+		c.Data["Result"] = template.JS(string(b))
+	}
 }
 }
 
 
-func (p *BookController) Users() {
-	p.TplName = "book/users.tpl"
+func (c *BookController) AddMember()  {
+	identify := c.GetString("identify")
+	account := c.GetString("account")
+	role_id,_ := c.GetInt("role_id",3)
+
+	if identify == "" || account == ""{
+		c.JsonResult(6001,"参数错误")
+	}
+	book ,err := models.NewBookResult().FindByIdentify("identify",c.Member.MemberId)
+
+	if err != nil {
+		if err == models.ErrPermissionDenied {
+			c.JsonResult(403,"权限不足")
+		}
+		if err == orm.ErrNoRows {
+			c.JsonResult(404,"项目不能存在")
+		}
+		c.JsonResult(6002,err.Error())
+	}
+	if book.RoleId != 0 && book.RoleId != 1 {
+		c.JsonResult(403,"权限不足")
+	}
+
+	member := models.NewMember()
+
+	if err := member.FindByAccount(account) ; err != nil {
+		c.JsonResult(404,"用户不存在")
+	}
+
+	if _,err := models.NewRelationship().FindForRoleId(book.BookId,member.MemberId);err == orm.ErrNoRows {
+		c.JsonResult(6003,"用户已存在该项目中")
+	}
+
+	relationship := models.NewRelationship()
+	relationship.BookId = book.BookId
+	relationship.MemberId = member.MemberId
+	relationship.RoleId = role_id
+
+	if err := relationship.Insert(); err == nil {
+		c.JsonResult(0,"ok",member)
+	}
+	c.JsonResult(500,err.Error())
 }
 }
 
 
+
 func (c *BookController) Create() {
 func (c *BookController) Create() {
 
 
 	if c.Ctx.Input.IsPost() {
 	if c.Ctx.Input.IsPost() {
 		book_name := strings.TrimSpace(c.GetString("book_name",""))
 		book_name := strings.TrimSpace(c.GetString("book_name",""))
 		identify := strings.TrimSpace(c.GetString("identify",""))
 		identify := strings.TrimSpace(c.GetString("identify",""))
 		description := strings.TrimSpace(c.GetString("description",""))
 		description := strings.TrimSpace(c.GetString("description",""))
-		privately_owned := c.GetString("privately_owned")
+		privately_owned,_ := strconv.Atoi(c.GetString("privately_owned"))
 		comment_status := c.GetString("comment_status")
 		comment_status := c.GetString("comment_status")
 
 
+		if book_name == "" {
+			c.JsonResult(6001,"项目名称不能为空")
+		}
+		if identify == "" {
+			c.JsonResult(6002,"项目标识不能为空")
+		}
+		if ok,err := regexp.MatchString(`^[a-z]+[a-zA-Z0-9_\-]*$`,identify); !ok || err != nil {
+			c.JsonResult(6003,"文档标识只能包含小写字母、数字,以及“-”和“_”符号,并且只能小写字母开头")
+		}
+		if strings.Count(identify,"") > 50 {
+			c.JsonResult(6004,"文档标识不能超过50字")
+		}
+		if strings.Count(description,"") > 500 {
+			c.JsonResult(6004,"项目描述不能大于500字")
+		}
+		if privately_owned !=0 && privately_owned != 1 {
+			privately_owned = 1
+		}
+		if comment_status != "open" && comment_status != "closed" && comment_status != "group_only" && comment_status != "registered_only" {
+			comment_status = "closed"
+		}
+
+		book := models.NewBook()
+
+		if books,err := book.FindByField("identify",identify); err == nil || len(books) > 0 {
+			c.JsonResult(6006,"项目标识已存在")
+		}
+
+		book.BookName = book_name
+		book.Description = description
+		book.CommentCount = 0
+		book.PrivatelyOwned = privately_owned
+		book.CommentStatus = comment_status
+		book.Identify = identify
+		book.DocCount = 0
+		book.MemberId = c.Member.MemberId
+		book.CommentCount = 0
+		book.Version = time.Now().Unix()
+		book.Cover = beego.AppConfig.String("cover")
+
+		err := book.Insert()
+
+		if err != nil {
+			c.JsonResult(6005,err.Error())
+		}
+		c.JsonResult(0,"ok",book)
 	}
 	}
 	c.JsonResult(6001,"error")
 	c.JsonResult(6001,"error")
 }
 }

+ 20 - 0
models/base.go

@@ -0,0 +1,20 @@
+package models
+
+import "github.com/astaxie/beego/orm"
+
+type Model struct {
+
+}
+
+func (m *Model) Find(id int) error {
+	o := orm.NewOrm()
+
+	return o.Read(m)
+}
+
+func (m *Model) Insert() error  {
+	o := orm.NewOrm()
+	_,err := o.Insert(m)
+
+	return err
+}

+ 104 - 3
models/book.go

@@ -1,6 +1,11 @@
 package models
 package models
 
 
-import "time"
+import (
+	"time"
+
+	"github.com/astaxie/beego/orm"
+	"github.com/lifei6671/godoc/conf"
+)
 
 
 // Book struct .
 // Book struct .
 type Book struct {
 type Book struct {
@@ -9,17 +14,20 @@ type Book struct {
 	BookName string		`orm:"column(book_name);size(500)" json:"book_name"`
 	BookName string		`orm:"column(book_name);size(500)" json:"book_name"`
 	// Identify 项目唯一标识.
 	// Identify 项目唯一标识.
 	Identify string		`orm:"column(identify);size(100);unique" json:"identify"`
 	Identify string		`orm:"column(identify);size(100);unique" json:"identify"`
+	OrderIndex int		`orm:"column(order_index);type(int);default(0)" json:"order_index"`
 	// Description 项目描述.
 	// Description 项目描述.
 	Description string	`orm:"column(description);size(2000)" json:"description"`
 	Description string	`orm:"column(description);size(2000)" json:"description"`
+	Label string		`orm:"column(label);size(500)" json:"label"`
 	// PrivatelyOwned 项目私有: 0 公开/ 1 私有
 	// PrivatelyOwned 项目私有: 0 公开/ 1 私有
 	PrivatelyOwned int	`orm:"column(privately_owned);type(int);default(0)" json:"privately_owned"`
 	PrivatelyOwned int	`orm:"column(privately_owned);type(int);default(0)" json:"privately_owned"`
 	// 当项目是私有时的访问Token.
 	// 当项目是私有时的访问Token.
-	PrivateToken string 	`orm:"column(private_token);size(500)" json:"private_token"`
+	PrivateToken string 	`orm:"column(private_token);size(500);null" json:"private_token"`
 	// DocCount 包含文档数量.
 	// DocCount 包含文档数量.
 	DocCount int		`orm:"column(doc_count);type(int)" json:"doc_count"`
 	DocCount int		`orm:"column(doc_count);type(int)" json:"doc_count"`
-	// CommentStatus 评论设置的状态:open为允许所有人评论,closed为不允许评论, group_only 仅允许参与者评论 ,registered_only 仅允许注册者评论.
+	// CommentStatus 评论设置的状态:open 为允许所有人评论,closed 为不允许评论, group_only 仅允许参与者评论 ,registered_only 仅允许注册者评论.
 	CommentStatus string	`orm:"column(comment_status);size(20);default(open)" json:"comment_status"`
 	CommentStatus string	`orm:"column(comment_status);size(20);default(open)" json:"comment_status"`
 	CommentCount int	`orm:"column(comment_count);type(int)" json:"comment_count"`
 	CommentCount int	`orm:"column(comment_count);type(int)" json:"comment_count"`
+	Cover string 		`orm:"column();size(1000)" json:"cover"`
 
 
 	// CreateTime 创建时间 .
 	// CreateTime 创建时间 .
 	CreateTime time.Time	`orm:"type(datetime);column(create_time);auto_now_add"`
 	CreateTime time.Time	`orm:"type(datetime);column(create_time);auto_now_add"`
@@ -28,6 +36,7 @@ type Book struct {
 	Version int64		`orm:"type(bigint);column(version)" json:"version"`
 	Version int64		`orm:"type(bigint);column(version)" json:"version"`
 }
 }
 
 
+
 // TableName 获取对应数据库表名.
 // TableName 获取对应数据库表名.
 func (m *Book) TableName() string {
 func (m *Book) TableName() string {
 	return "books"
 	return "books"
@@ -36,4 +45,96 @@ func (m *Book) TableName() string {
 func (m *Book) TableEngine() string {
 func (m *Book) TableEngine() string {
 	return "INNODB"
 	return "INNODB"
 }
 }
+func (m *Book) TableNameWithPrefix() string {
+	return conf.GetDatabasePrefix() + m.TableName()
+}
+
+func NewBook() *Book {
+	return &Book{}
+}
+
+func (m *Book) Insert() error {
+	o := orm.NewOrm()
+	_,err := o.Insert(m)
+
+	return err
+}
+
+func (m *Book) Update(cols... string) error  {
+	o := orm.NewOrm()
+
+	_,err := o.Update(m,cols...)
+	return err
+}
+
+func (m *Book) FindByField(field string,value interface{}) ([]Book,error)  {
+	o := orm.NewOrm()
+
+	var books []Book
+	_,err := o.QueryTable(conf.GetDatabasePrefix() + m.TableName()).Filter(field,value).All(&books)
+
+	return books,err
+}
+
+func (m *Book) FindToPager(pageIndex, pageSize ,memberId int) (books []BookResult,totalCount int,err error){
+
+	relationship := NewRelationship()
+
+	o := orm.NewOrm()
+
+	qb, _ := orm.NewQueryBuilder("mysql")
+
+	qb.Select("COUNT(book.book_id) AS total_count").
+		From(m.TableNameWithPrefix() + " AS book").
+		LeftJoin(relationship.TableNameWithPrefix() + " AS rel").
+		On("book.book_id=rel.book_id").
+		Where("rel.member_id=?")
+
+	err = o.Raw(qb.String(),memberId).QueryRow(&totalCount)
+
+	if err != nil {
+		return
+	}
+
+	offset := (pageIndex - 1) * pageSize
+	qb2,_ := orm.NewQueryBuilder("mysql")
+
+	qb2.Select("book.*,rel.member_id","rel.role_id","m.account as create_name").
+		From(m.TableNameWithPrefix() + " AS book").
+		LeftJoin(relationship.TableNameWithPrefix() + " AS rel").
+		On("book.book_id=rel.book_id").
+		LeftJoin(NewMember().TableNameWithPrefix() + " AS m").On("rel.member_id=m.member_id AND rel.role_id=0").
+		Where("rel.member_id=?").
+		OrderBy("book.order_index").Desc().
+		Limit(pageSize).
+		Offset(offset)
+
+	_,err = o.Raw(qb2.String(),memberId).QueryRows(&books)
+
+	sql := "SELECT m.account,doc.modify_time FROM md_documents AS doc LEFT JOIN md_members AS m ON doc.modify_at=m.member_id WHERE book_id = ? LIMIT 1 ORDER BY doc.modify_time DESC"
+
+	if err == nil && len(books) > 0{
+		for index,book := range books  {
+			var text struct{
+				Account string
+				ModifyTime time.Time
+			}
+
 
 
+			err1 := o.Raw(sql,book.BookId).QueryRow(&text)
+			if err1 == nil {
+				books[index].LastModifyText = text.Account + " 于 " + text.ModifyTime.Format("2006-01-02 15:04:05")
+			}
+			if book.RoleId == 0{
+				book.RoleName = "创始人"
+			}else if book.RoleId == 1 {
+				book.RoleName = "管理员"
+			}else if book.RoleId == 2 {
+				book.RoleName = "编辑者"
+			}else if book.RoleId == 2 {
+				book.RoleName = "观察者"
+			}
+		}
+	}
+	return
+}

+ 112 - 0
models/book_result.go

@@ -0,0 +1,112 @@
+package models
+
+import (
+	"time"
+	"github.com/astaxie/beego/orm"
+	"strings"
+)
+
+type BookResult struct {
+	BookId int              `json:"book_id"`
+	BookName string         `json:"book_name"`
+	Identify string         `json:"identify"`
+	OrderIndex int          `json:"order_index"`
+	Description string      `json:"description"`
+	PrivatelyOwned int      `json:"privately_owned"`
+	PrivateToken string     `json:"private_token"`
+	DocCount int            `json:"doc_count"`
+	CommentStatus string    `json:"comment_status"`
+	CommentCount int        `json:"comment_count"`
+	CreateTime time.Time    `json:"create_time"`
+	CreateName string 	`json:"create_name"`
+	ModifyTime time.Time	`json:"modify_time"`
+	Cover string            `json:"cover"`
+	Label string		`json:"label"`
+	MemberId int            `json:"member_id"`
+	RoleId int        	`json:"role_id"`
+	RoleName string 	`json:"role_name"`
+
+	LastModifyText string 	`json:"last_modify_text"`
+}
+
+func NewBookResult() *BookResult {
+	return &BookResult{}
+}
+
+func (m *BookResult) FindByIdentify(identify string,member_id int) (*BookResult,error) {
+	o := orm.NewOrm()
+
+	book := NewBook()
+
+	err := o.QueryTable(book.TableNameWithPrefix()).Filter("identify", identify).One(book)
+
+	if err != nil {
+		return m, err
+	}
+
+	relationship := NewRelationship()
+
+	err = o.QueryTable(relationship.TableNameWithPrefix()).Filter("book_id",book.BookId).Filter("member_id",member_id).One(relationship)
+
+	if err != nil {
+		return m,err
+	}
+	var relationship2 Relationship
+
+	err = o.QueryTable(relationship.TableNameWithPrefix()).Filter("book_id",book.BookId).Filter("role_id",0).One(&relationship2)
+
+	if err != nil {
+		return m,ErrPermissionDenied
+	}
+
+	member := NewMember()
+
+	err = member.Find(relationship2.MemberId)
+	if err != nil {
+		return m,err
+	}
+
+	m.BookId = book.BookId
+	m.BookName = book.BookName
+	m.Identify = book.Identify
+	m.OrderIndex = book.OrderIndex
+	m.Description = strings.Replace(book.Description,"\r\n","<br/>",-1)
+	m.PrivatelyOwned = book.PrivatelyOwned
+	m.PrivateToken = book.PrivateToken
+	m.DocCount = book.DocCount
+	m.CommentStatus = book.CommentStatus
+	m.CommentCount = book.CommentCount
+	m.CreateTime = book.CreateTime
+	m.CreateName = member.Account
+	m.ModifyTime = book.ModifyTime
+	m.Cover = book.Cover
+	m.Label = book.Label
+
+	m.MemberId = relationship.MemberId
+	m.RoleId = relationship.RoleId
+
+	if m.RoleId == 0{
+		m.RoleName = "创始人"
+	}else if m.RoleId == 1 {
+		m.RoleName = "管理员"
+	}else if m.RoleId == 2 {
+		m.RoleName = "编辑者"
+	}else if m.RoleId == 2 {
+		m.RoleName = "观察者"
+	}
+
+
+	doc := NewDocument()
+
+	err = o.QueryTable(doc.TableNameWithPrefix()).Filter("book_id",book.BookId).OrderBy("modify_time").One(doc)
+
+	if err == nil {
+		member2 := NewMember()
+		member2.Find(doc.ModifyAt)
+
+		m.LastModifyText = member2.Account + " 于 " + doc.ModifyTime.Format("2006-01-02 15:04:05")
+	}
+
+	return m,nil
+
+}

+ 31 - 1
models/document.go

@@ -1,6 +1,9 @@
 package models
 package models
 
 
-import "time"
+import (
+	"time"
+	"github.com/lifei6671/godoc/conf"
+)
 
 
 // Document struct.
 // Document struct.
 type Document struct {
 type Document struct {
@@ -32,3 +35,30 @@ func (m *Document) TableName() string {
 func (m *Document) TableEngine() string {
 func (m *Document) TableEngine() string {
 	return "INNODB"
 	return "INNODB"
 }
 }
+
+func (m *Document) TableNameWithPrefix()  string {
+	return conf.GetDatabasePrefix() + m.TableName()
+}
+
+func NewDocument() *Document  {
+	return &Document{}
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 2 - 0
models/errors.go

@@ -13,4 +13,6 @@ var(
 
 
 	// ErrInvalidParameter 参数错误.
 	// ErrInvalidParameter 参数错误.
 	ErrInvalidParameter = errors.New("Invalid parameter")
 	ErrInvalidParameter = errors.New("Invalid parameter")
+
+	ErrPermissionDenied = errors.New("Permission denied")
 )
 )

+ 35 - 1
models/member.go

@@ -5,6 +5,7 @@ import (
 	"time"
 	"time"
 	"github.com/astaxie/beego/orm"
 	"github.com/astaxie/beego/orm"
 	"github.com/lifei6671/godoc/utils"
 	"github.com/lifei6671/godoc/utils"
+	"github.com/lifei6671/godoc/conf"
 )
 )
 
 
 type Member struct {
 type Member struct {
@@ -31,6 +32,10 @@ func (m *Member) TableEngine() string {
 	return "INNODB"
 	return "INNODB"
 }
 }
 
 
+func (m *Member)TableNameWithPrefix() string {
+	return conf.GetDatabasePrefix() +  m.TableName()
+}
+
 func NewMember() *Member {
 func NewMember() *Member {
 	return &Member{}
 	return &Member{}
 }
 }
@@ -94,4 +99,33 @@ func (m *Member) Find(id int) error{
 		return  err
 		return  err
 	}
 	}
 	return nil
 	return nil
-}
+}
+
+func (m *Member) FindByAccount (account string) error  {
+	o := orm.NewOrm()
+
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("account",account).One(m)
+
+	return err
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 60 - 0
models/member_result.go

@@ -0,0 +1,60 @@
+package models
+
+import (
+	"time"
+	"github.com/astaxie/beego/orm"
+)
+
+type MemberRelationshipResult struct {
+	MemberId int		`json:"member_id"`
+	Account string 		`json:"account"`
+	Description string	`json:"description"`
+	Email string 		`json:"email"`
+	Phone string 		`json:"phone"`
+	Avatar string 		`json:"avatar"`
+	Role int		`json:"role"`	//用户角色:0 管理员/ 1 普通用户
+	Status int 		`json:"status"`	//用户状态:0 正常/1 禁用
+	CreateTime time.Time	`json:"create_time"`
+	CreateAt int		`json:"create_at"`
+	RelationshipId int      `json:"relationship_id"`
+	BookId int		`json:"book_id"`
+	// RoleId 角色:0 创始人(创始人不能被移除) / 1 管理员/2 编辑者/3 观察者
+	RoleId int		`json:"role_id"`
+}
+
+func NewMemberRelationshipResult() *MemberRelationshipResult {
+	return &MemberRelationshipResult{}
+}
+
+func (m *MemberRelationshipResult) FindForUsersByBookId(book_id ,pageIndex, pageSize int) ([]MemberRelationshipResult,int,error) {
+	o := orm.NewOrm()
+
+	var members []MemberRelationshipResult
+
+	sql1 := "SELECT * FROM md_relationship AS rel LEFT JOIN md_members as member ON rel.member_id = member.member_id WHERE rel.book_id = ? ORDER BY rel.relationship_id DESC  LIMIT ?,?"
+
+	sql2 := "SELECT count(*) AS total_count FROM md_relationship AS rel LEFT JOIN md_members as member ON rel.member_id = member.member_id WHERE rel.book_id = ?"
+
+	var total_count int
+
+	err := o.Raw(sql2,book_id).QueryRow(&total_count)
+
+	if err != nil {
+		return members,0,err
+	}
+
+	offset := (pageIndex-1) * pageSize
+
+	_,err = o.Raw(sql1,book_id,offset,pageSize).QueryRows(&members)
+
+	if err != nil {
+		return members,0,err
+	}
+
+	return members,total_count,nil
+}
+
+
+
+
+

+ 46 - 1
models/relationship.go

@@ -1,5 +1,10 @@
 package models
 package models
 
 
+import (
+	"github.com/lifei6671/godoc/conf"
+	"github.com/astaxie/beego/orm"
+)
+
 type Relationship struct {
 type Relationship struct {
 	RelationshipId int	`orm:"pk;auto;unique;column(relationship_id)" json:"relationship_id"`
 	RelationshipId int	`orm:"pk;auto;unique;column(relationship_id)" json:"relationship_id"`
 	MemberId int		`orm:"column(member_id);type(int)" json:"member_id"`
 	MemberId int		`orm:"column(member_id);type(int)" json:"member_id"`
@@ -13,6 +18,10 @@ type Relationship struct {
 func (m *Relationship) TableName() string {
 func (m *Relationship) TableName() string {
 	return "relationship"
 	return "relationship"
 }
 }
+func (m *Relationship) TableNameWithPrefix() string {
+	return conf.GetDatabasePrefix() + m.TableName()
+}
+
 // TableEngine 获取数据使用的引擎.
 // TableEngine 获取数据使用的引擎.
 func (m *Relationship) TableEngine() string {
 func (m *Relationship) TableEngine() string {
 	return "INNODB"
 	return "INNODB"
@@ -23,4 +32,40 @@ func (u *Relationship) TableUnique() [][]string {
 	return [][]string{
 	return [][]string{
 		[]string{"MemberId", "BookId"},
 		[]string{"MemberId", "BookId"},
 	}
 	}
-}
+}
+
+func NewRelationship() *Relationship {
+	return &Relationship{}
+}
+
+func (m *Relationship) FindForRoleId(book_id ,member_id int) (int,error) {
+	o := orm.NewOrm()
+
+	relationship := NewRelationship()
+
+	err := o.QueryTable(m.TableNameWithPrefix()).Filter("book_id",book_id).Filter("member_id",member_id).One(relationship)
+
+	if err != nil {
+		return 0,err
+	}
+	return relationship.RoleId,nil
+}
+
+func (m *Relationship) Insert() error  {
+	o := orm.NewOrm()
+
+	_,err :=  o.Insert(m)
+
+	return err
+}
+
+func (m *Relationship) Update() error  {
+	o := orm.NewOrm()
+
+	_,err :=  o.Update(m)
+
+	return err
+}
+
+
+

+ 2 - 1
routers/router.go

@@ -24,8 +24,9 @@ func init()  {
 	beego.Router("/book/:key/dashboard", &controllers.BookController{},"*:Dashboard")
 	beego.Router("/book/:key/dashboard", &controllers.BookController{},"*:Dashboard")
 	beego.Router("/book/:key/setting", &controllers.BookController{},"*:Setting")
 	beego.Router("/book/:key/setting", &controllers.BookController{},"*:Setting")
 	beego.Router("/book/:key/users", &controllers.BookController{},"*:Users")
 	beego.Router("/book/:key/users", &controllers.BookController{},"*:Users")
-	beego.Router("/book/:key/edit/:id", &controllers.BookController{},"*:Edit")
+	beego.Router("/book/:key/edit", &controllers.BookController{},"*:Edit")
 	beego.Router("/book/create", &controllers.BookController{},"*:Create")
 	beego.Router("/book/create", &controllers.BookController{},"*:Create")
+	beego.Router("/book/member/create", &controllers.BookController{},"POST:AddMember")
 
 
 	beego.Router("/book/:key/users/create", &controllers.BookMemberController{},"*:Create")
 	beego.Router("/book/:key/users/create", &controllers.BookMemberController{},"*:Create")
 	beego.Router("/book/:key/users/change", &controllers.BookMemberController{},"*:Change")
 	beego.Router("/book/:key/users/change", &controllers.BookMemberController{},"*:Change")

+ 22 - 1
static/js/main.js

@@ -10,4 +10,25 @@ function showError($msg) {
 function showSuccess($msg) {
 function showSuccess($msg) {
     $("#form-error-message").addClass("success-message").removeClass("error-message").text($msg);
     $("#form-error-message").addClass("success-message").removeClass("error-message").text($msg);
     return true;
     return true;
-}
+}
+
+Date.prototype.format = function(fmt) {
+    var o = {
+        "M+" : this.getMonth()+1,                 //月份
+        "d+" : this.getDate(),                    //日
+        "h+" : this.getHours(),                   //小时
+        "m+" : this.getMinutes(),                 //分
+        "s+" : this.getSeconds(),                 //秒
+        "q+" : Math.floor((this.getMonth()+3)/3), //季度
+        "S"  : this.getMilliseconds()             //毫秒
+    };
+    if(/(y+)/.test(fmt)) {
+        fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
+    }
+    for(var k in o) {
+        if(new RegExp("("+ k +")").test(fmt)){
+            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
+        }
+    }
+    return fmt;
+};

+ 332 - 0
utils/pager.go

@@ -0,0 +1,332 @@
+package utils
+
+import (
+	// "fmt"
+	html "html/template"
+	con "strconv"
+	"strings"
+
+	"github.com/astaxie/beego/orm"
+	"fmt"
+)
+
+type PageOptions struct {
+	TableName           string //表名  -----------------[必填]
+	Conditions          string //条件
+	CurrentPage         int    //当前页 ,默认1 每次分页,必须在前台设置新的页数,不设置始终默认1.在控制器中使用方式:cp, _ := this.GetInt("pno")   po.CurrentPage = int(cp)
+	PageSize            int    //页面大小,默认20
+	LinkItemCount       int    //生成A标签的个数 默认10个
+	Href                string //A标签的链接地址  ---------[不需要设置]
+	ParamName           string //参数名称  默认是pno
+	FirstPageText       string //首页文字  默认"首页"
+	LastPageText        string //尾页文字  默认"尾页"
+	PrePageText         string //上一页文字 默认"上一页"
+	NextPageText        string //下一页文字 默认"下一页"
+	EnableFirstLastLink bool   //是否启用首尾连接 默认false 建议开启
+	EnablePreNexLink    bool   //是否启用上一页,下一页连接 默认false 建议开启
+}
+
+/**
+ * 分页函数,适用任何表
+ * 返回 总记录条数,总页数,以及当前请求的数据RawSeter,调用中需要"rs.QueryRows(&tblog)"就行了  --tblog是一个Tb_log对象
+ * 参数:表名,当前页数,页面大小,条件(查询条件,格式为 " and name='zhifeiya' and age=12 ")
+ */
+func GetPagesInfo(tableName string, currentpage int, pagesize int, conditions string) (int, int, orm.RawSeter) {
+	if currentpage <= 1 {
+		currentpage = 1
+	}
+	if pagesize == 0 {
+		pagesize = 20
+	}
+	var rs orm.RawSeter
+	o := orm.NewOrm()
+	var totalItem, totalpages int = 0, 0                                                          //总条数,总页数
+	o.Raw("SELECT count(*) FROM " + tableName + "  where 1 > 0 " + conditions).QueryRow(&totalItem) //获取总条数
+	if totalItem <= pagesize {
+		totalpages = 1
+	} else if totalItem > pagesize {
+		temp := totalItem / pagesize
+		if (totalItem % pagesize) != 0 {
+			temp = temp + 1
+		}
+		totalpages = temp
+	}
+	rs = o.Raw("select *  from  " + tableName + "  where 1 > 0 " + conditions + " LIMIT " + con.Itoa((currentpage-1)*pagesize) + "," + con.Itoa(pagesize))
+	return totalItem, totalpages, rs
+}
+
+/**
+ * 返回总记录条数,总页数,当前页面数据,分页html
+ * 根据分页选项,生成分页连接 下面是一个实例:
+     func (this *MainController) Test() {
+        var po util.PageOptions
+        po.EnablePreNexLink = true
+        po.EnableFirstLastLink = true
+        po.LinkItemCount = 7
+        po.TableName = "help_topic"
+        cp, _ := this.GetInt("pno")
+        po.CurrentPage = int(cp)
+        _,_,_ pager := util.GetPagerLinks(&po, this.Ctx)
+        this.Data["Email"] = html.HTML(pager)
+        this.TplName = "test.html"
+    }
+*/
+func GetPagerLinks(po *PageOptions,requestURI string) (int, int, orm.RawSeter, html.HTML) {
+	str := ""
+	totalItem, totalpages, rs := GetPagesInfo(po.TableName, po.CurrentPage, po.PageSize, po.Conditions)
+	po = setDefault(po, totalpages)
+	DealUri(po, requestURI)
+	if totalpages <= po.LinkItemCount {
+		str = fun1(po, totalpages) //显示完全  12345678910
+	} else if totalpages > po.LinkItemCount {
+		if po.CurrentPage < po.LinkItemCount {
+			str = fun2(po, totalpages) //123456789...200
+		} else {
+			if po.CurrentPage + po.LinkItemCount < totalpages {
+				str = fun3(po, totalpages)
+			} else {
+				str = fun4(po, totalpages)
+			}
+		}
+	}
+	return totalItem, totalpages, rs, html.HTML(str)
+}
+
+func GetPagerHtml(requestURI string,pageIndex, pageSize,totalCount int) (html.HTML){
+	po := &PageOptions{
+		CurrentPage: pageIndex,
+		PageSize: pageSize,
+		EnableFirstLastLink : true,
+		ParamName : "page",
+	}
+	setDefault(po,totalCount)
+	DealUri(po,requestURI)
+	str := ""
+	if totalCount <= po.LinkItemCount {
+		str = fun1(po, totalCount) //显示完全  12345678910
+	} else if totalCount > po.LinkItemCount {
+		if po.CurrentPage < po.LinkItemCount {
+			str = fun2(po, totalCount) //123456789...200
+		} else {
+			if po.CurrentPage + po.LinkItemCount < totalCount {
+				str = fun3(po, totalCount)
+			} else {
+				str = fun4(po, totalCount)
+			}
+		}
+	}
+	str = strings.Replace(str,"?&","?",-1)
+	//str = strings.Replace(str,"&&","&",-1)
+	return  html.HTML(str)
+}
+
+/**
+ * 处理url,目的是保存参数
+ */
+func DealUri(po *PageOptions, requestURI string) {
+	var rs string
+	if strings.Contains(requestURI, "?") {
+		arr := strings.Split(requestURI, "?")
+		rs = ""
+		arr2 := strings.Split(arr[1], "&")
+		for _, v := range arr2 {
+			if !strings.Contains(v, po.ParamName) {
+				if strings.HasSuffix(rs,"&") {
+					rs +=  v
+				}else{
+					rs +=  v + "&"
+				}
+				//rs += "&" + v
+			}
+		}
+		if strings.HasPrefix(rs,"&") {
+			rs = string(rs[1:])
+		}
+		if strings.HasSuffix(rs,"&"){
+			rs = string(rs[0:strings.Count(rs,"")-1])
+		}
+		rs = arr[0] + "?" + rs
+		fmt.Println(rs)
+	} else {
+		//rs = requestURI + "?" //+ po.ParamName + "time=" + con.Itoa(time.Now().Second())
+		rs = requestURI + "?"
+	}
+
+	po.Href = rs
+}
+
+/**
+ * 1...197 198 199 200
+ */
+func fun4(po *PageOptions, totalPages int) string {
+	rs := ""
+	rs += getHeader(po, totalPages)
+	rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(1) + "'>" + con.Itoa(1) + "</a><li>"
+	rs += "<li><a href=''>...</a></li>"
+	for i := totalPages - po.LinkItemCount; i <= totalPages; i++ {
+		if po.CurrentPage != i {
+			rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "'>" + con.Itoa(i) + "</a></li>"
+		} else {
+			rs += "<li class=\"active\"><a href=\"###\">" + con.Itoa(i) + "  <span class=\"sr-only\">(current)</span></a></li>"
+		}
+	}
+	rs += getFooter(po, totalPages)
+	return rs
+
+}
+
+/**
+ * 1...6 7 8 9 10 11 12  13  14 15... 200
+ */
+func fun3(po *PageOptions, totalpages int) string {
+	var rs string = ""
+	rs += getHeader(po, totalpages)
+	rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(1) + "'>" + con.Itoa(1) + "</a></li>"
+	rs += "<a href=''>...</a>"
+	for i := po.CurrentPage - po.LinkItemCount/2 + 1; i <= po.CurrentPage+po.LinkItemCount/2-1; i++ {
+		if po.CurrentPage != i {
+			rs += "<a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "'>" + con.Itoa(i) + "</a>"
+		} else {
+			rs += "<li class=\"active\"><a href=\"###\">" + con.Itoa(i) +  "  <span class=\"sr-only\">(current)</span></a></li>"
+		}
+	}
+	rs += "<a href=''>...</a>"
+	rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(totalpages) + "'>" + con.Itoa(totalpages) + "</a></li>"
+	rs += getFooter(po, totalpages)
+	return rs
+
+}
+
+/**
+ * totalpages > po.LinkItemCount   po.CurrentPage < po.LinkItemCount
+ * 123456789...200
+ */
+func fun2(po *PageOptions, totalpages int) string {
+	var rs string = ""
+	rs += getHeader(po, totalpages)
+	for i := 1; i <= po.LinkItemCount+1; i++ {
+		if i == po.LinkItemCount {
+			rs += "<li><a href=\"" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "\">...</a></li>"
+		} else if i == po.LinkItemCount+1 {
+			rs += "<li><a href=\"" + po.Href + "&" + po.ParamName + "=" + con.Itoa(totalpages) + "\">" + con.Itoa(totalpages) + "</a></li>"
+		} else {
+			if po.CurrentPage != i {
+				rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "'>" + con.Itoa(i) + "</a></li>"
+			} else {
+				rs += "<li class=\"active\"><a href=\"###\">" + con.Itoa(i) +  "  <span class=\"sr-only\">(current)</span></a></li>"
+			}
+		}
+	}
+	rs += getFooter(po, totalpages)
+	return rs
+}
+
+/**
+ * totalpages <= po.LinkItemCount
+ * 显示完全  12345678910
+ */
+func fun1(po *PageOptions, totalpages int) string {
+
+	var rs string = ""
+	rs += getHeader(po, totalpages)
+	for i := 1; i <= totalpages; i++ {
+		if po.CurrentPage != i {
+
+			rs += "<li><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(i) + "'>" + con.Itoa(i) + "</a></li>"
+		} else {
+			rs += "<li class=\"active\"><a href=\"###\">" + con.Itoa(i) +  "  <span class=\"sr-only\">(current)</span></a></li>"
+		}
+	}
+	rs += getFooter(po, totalpages)
+	return rs
+}
+
+/**
+ * 头部
+ */
+func getHeader(po *PageOptions, totalpages int) string {
+	var rs string = "<ul class=\"pagination\">"
+	if po.EnableFirstLastLink { //当首页,尾页都设定的时候,就显示
+
+		rs += "<li" + judgeDisable(po, totalpages, 0) + " ><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(1) + "'>" + po.FirstPageText + "</a></li>"
+	}
+	if po.EnablePreNexLink { // disabled=\"disabled\"
+		var a int = po.CurrentPage - 1
+		if po.CurrentPage == 1 {
+			a = 1
+		}
+		rs += "<li" + judgeDisable(po, totalpages, 0) + " ><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(a) + "'>" + po.PrePageText + "</a></li>"
+	}
+	return rs
+}
+
+/**
+ * 尾部
+ */
+func getFooter(po *PageOptions, totalpages int) string {
+	var rs string = ""
+	if po.EnablePreNexLink {
+		var a int = po.CurrentPage + 1
+		if po.CurrentPage == totalpages {
+			a = totalpages
+		}
+		rs += "<li " + judgeDisable(po, totalpages, 1) + "  ><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(a) + "'>" + po.NextPageText + "</a></li>"
+	}
+	if po.EnableFirstLastLink { //当首页,尾页都设定的时候,就显示
+		rs += "<li " + judgeDisable(po, totalpages, 1) + " ><a href='" + po.Href + "&" + po.ParamName + "=" + con.Itoa(totalpages) + "'>" + po.LastPageText + "</a></li>"
+	}
+	rs += "</ul>"
+	return rs
+}
+
+/**
+ * 设置默认值
+ */
+func setDefault(po *PageOptions, totalpages int) *PageOptions {
+	if len(po.FirstPageText) <= 0 {
+		po.FirstPageText = "首页"
+	}
+	if len(po.LastPageText) <= 0 {
+		po.LastPageText = "尾页"
+	}
+	if len(po.PrePageText) <= 0 {
+		po.PrePageText = "上一页"
+	}
+	if len(po.NextPageText) <= 0 {
+		po.NextPageText = "下一页"
+	}
+	if po.CurrentPage >= totalpages {
+		po.CurrentPage = totalpages
+	}
+	if po.CurrentPage <= 1 {
+		po.CurrentPage = 1
+	}
+	if po.LinkItemCount == 0 {
+		po.LinkItemCount = 10
+	}
+	if po.PageSize == 0 {
+		po.PageSize = 20
+	}
+	if len(po.ParamName) <= 0 {
+		po.ParamName = "pno"
+	}
+	return po
+}
+
+/**
+ *判断首页尾页  上一页下一页是否能用
+ */
+func judgeDisable(po *PageOptions, totalpages int, h_f int) string {
+	var rs string = ""
+	//判断头部
+	if h_f == 0 {
+		if po.CurrentPage == 1 {
+			rs = " "
+		}
+	} else {
+		if po.CurrentPage == totalpages {
+			rs = " "
+		}
+	}
+	return rs
+}

+ 30 - 20
views/book/dashboard.tpl

@@ -26,53 +26,63 @@
         <div class="row">
         <div class="row">
             <div class="page-left">
             <div class="page-left">
                 <ul class="menu">
                 <ul class="menu">
-                    <li class="active"><a href="{{urlfor "BookController.Dashboard" ":key" "test"}}" class="item"><i class="fa fa-dashboard" aria-hidden="true"></i> 概要</a> </li>
-                    <li><a href="{{urlfor "BookController.Users" ":key" "test"}}" class="item"><i class="fa fa-users" aria-hidden="true"></i> 成员</a> </li>
-                    <li><a href="{{urlfor "BookController.Setting" ":key" "test"}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a> </li>
+                    <li class="active"><a href="{{urlfor "BookController.Dashboard" ":key" .Model.Identify}}" class="item"><i class="fa fa-dashboard" aria-hidden="true"></i> 概要</a> </li>
+                    <li><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-users" aria-hidden="true"></i> 成员</a> </li>
+                    <li><a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a> </li>
                 </ul>
                 </ul>
 
 
             </div>
             </div>
             <div class="page-right">
             <div class="page-right">
                 <div class="m-box">
                 <div class="m-box">
                     <div class="box-head">
                     <div class="box-head">
-                        <strong class="box-title"><i class="fa fa-unlock" aria-hidden="true" title="公开项目" data-toggle="tooltip"></i> 这是一个测试项目</strong>
-                        <a href="{{urlfor "BookController.Edit" ":key" "test" ":id" "1"}}" class="btn btn-default btn-sm pull-right" target="_blank"><i class="fa fa-edit" aria-hidden="true"></i> 编辑</a>
-                        <a href="{{urlfor "DocumentController.Index" ":key" "test"}}" class="btn btn-default btn-sm pull-right" style="margin-right: 5px;" target="_blank"><i class="fa fa-eye"></i> 阅读</a>
-                        <a href="{{urlfor "DocumentController.Index" ":key" "test"}}" class="btn btn-default btn-sm pull-right" style="margin-right: 5px;" target="_blank"><i class="fa fa-upload" aria-hidden="true"></i> 发布</a>
+                        <strong class="box-title">
+                            {{if eq .Model.PrivatelyOwned 0}}
+                            <i class="fa fa-unlock" aria-hidden="true" title="公开项目" data-toggle="tooltip"></i>
+                            {{else}}
+                            <i class="fa fa-lock" aria-hidden="true" title="私有项目" data-toggle="tooltip"></i>
+                            {{end}}
+                            {{.Model.BookName}}
+                        </strong>
+                        {{if ne .Model.RoleId 3}}
+                        <a href="{{urlfor "BookController.Edit" ":key" .Model.Identify ":id" .Model.BookId}}" class="btn btn-default btn-sm pull-right" target="_blank"><i class="fa fa-edit" aria-hidden="true"></i> 编辑</a>
+                        <a href="{{urlfor "DocumentController.Index" ":key" .Model.Identify}}" class="btn btn-default btn-sm pull-right" style="margin-right: 5px;" target="_blank"><i class="fa fa-eye"></i> 阅读</a>
+                        <a href="{{urlfor "DocumentController.Index" ":key" .Model.Identify}}" class="btn btn-default btn-sm pull-right" style="margin-right: 5px;" target="_blank"><i class="fa fa-upload" aria-hidden="true"></i> 发布</a>
+                        {{end}}
                     </div>
                     </div>
                 </div>
                 </div>
                 <div class="box-body">
                 <div class="box-body">
                     <div class="dashboard">
                     <div class="dashboard">
                         <div class="pull-left" style="width: 200px;margin-bottom: 15px;">
                         <div class="pull-left" style="width: 200px;margin-bottom: 15px;">
                             <div class="book-image">
                             <div class="book-image">
-                                <img src="/static/images/5fcb811e04c23cdb2088f26923fcc287_100.jpg">
+                                <img src="{{.Model.Cover}}" onerror="this.src='/static/images/book.jpg'" width="174" height="229" style="border: 1px solid #666">
                             </div>
                             </div>
                         </div>
                         </div>
 
 
                             <div class="list">
                             <div class="list">
                                 <span class="title">创建者:</span>
                                 <span class="title">创建者:</span>
-                                <span class="body">
-                                Minho
-                            </span>
+                                <span class="body">{{.Model.CreateName}}</span>
                             </div>
                             </div>
                             <div class="list">
                             <div class="list">
                                 <span class="title">文档数量:</span>
                                 <span class="title">文档数量:</span>
-                                <span class="body">20</span>
+                                <span class="body">{{.Model.DocCount}} 篇</span>
                             </div>
                             </div>
                             <div class="list">
                             <div class="list">
                                 <span class="title">创建时间:</span>
                                 <span class="title">创建时间:</span>
-                                <span class="body"> 2017-05-25 12:25:45 </span>
+                                <span class="body"> {{date .Model.CreateTime "Y-m-d H:i:s"}} </span>
                             </div>
                             </div>
                             <div class="list">
                             <div class="list">
                                 <span class="title">修改时间:</span>
                                 <span class="title">修改时间:</span>
-                                <span class="body"> 2017-05-25 12:25:45 </span>
-                            </div>
-                            <div class="summary">《TCP/IP详解,卷1:协议》是一本完整而详细的TCP/IP协议指南。描述了属于每一层的各个协议以及它们如何在不同操作系统中运行。<br>
-                                <br>
-                                作者用Lawrence Berkeley实验室的tcpdump程序来捕获不同操作系统和TCP/IP实现之间传输的不同分组。对tcpdump输出的研究可以帮助理解不同协议如何工作。 <br>
-                                <br>
-                                本书适合作为计算机专业学生学习网络的教材和教师参考书。也适用于研究网络的技术人员。
+                                <span class="body"> {{date .Model.ModifyTime "Y-m-d H:i:s"}} </span>
                             </div>
                             </div>
+                        <div class="list">
+                            <span class="title">担任角色:</span>
+                            <span class="body">{{.Model.RoleName}}</span>
+                        </div>
+                        <div class="list">
+                            <span class="title">评论数量:</span>
+                            <span class="body">{{.Model.CommentCount}} 条</span>
+                        </div>
+                            <div class="summary">{{.Model.Description}} </div>
 
 
                     </div>
                     </div>
                 </div>
                 </div>

+ 56 - 18
views/book/index.tpl

@@ -37,35 +37,54 @@
                     </div>
                     </div>
                 </div>
                 </div>
                 <div class="box-body">
                 <div class="box-body">
-                    <div class="book-list">
-                        <div class="list-item">
+                    <div class="book-list" id="bookList">
+                        <template v-if="lists.length <= 0">
+
+                        </template>
+                        <template v-else>
+
+                        <div class="list-item" v-for="item in lists">
                             <div class="book-title">
                             <div class="book-title">
                                 <div class="pull-left">
                                 <div class="pull-left">
-                                    <a href="{{urlfor "BookController.Dashboard" ":key" "test"}}" title="项目概要" data-toggle="tooltip">
-                                        <i class="fa fa-unlock" aria-hidden="true"></i> 测试项目
+                                    <a :href="'/book/' + item.identify + '/dashboard'" title="项目概要" data-toggle="tooltip">
+                                        <i class="fa fa-unlock" aria-hidden="true"></i> ${item.book_name}
                                     </a>
                                     </a>
                                 </div>
                                 </div>
                                 <div class="pull-right">
                                 <div class="pull-right">
-                                    <a href="{{urlfor "DocumentController.Index" ":key" "test"}}" title="查看文档" data-toggle="tooltip"><i class="fa fa-eye"></i> 查看文档</a>
-                                    <a href="{{urlfor "BookController.Edit" ":key" "test" ":id" "1"}}" title="编辑文档" data-toggle="tooltip"><i class="fa fa-edit" aria-hidden="true"></i> 编辑文档</a>
+                                    <a :href="'{{urlfor "DocumentController.Index" ":key" ""}}' + item.identify" title="查看文档" data-toggle="tooltip"><i class="fa fa-eye"></i> 查看文档</a>
+                                    <a :href="'/book/' + item.identify + '/edit'" title="编辑文档" data-toggle="tooltip"><i class="fa fa-edit" aria-hidden="true"></i> 编辑文档</a>
                                 </div>
                                 </div>
                                 <div class="clearfix"></div>
                                 <div class="clearfix"></div>
                             </div>
                             </div>
                             <div class="desc-text">
                             <div class="desc-text">
-                                <a href="{{urlfor "BookController.Dashboard" ":key" "test"}}" title="项目概要" style="font-size: 12px;" target="_blank">
-                                    这是一个测试用户测试项目
-                                </a>
-
+                                    <template v-if="item.description === ''">
+                                        &nbsp;
+                                    </template>
+                                    <template v-else="">
+                                        <a :href="'/book/' + item.identify + '/dashboard'" title="项目概要" style="font-size: 12px;" target="_blank">
+                                        ${item.description}
+                                        </a>
+                                    </template>
                             </div>
                             </div>
                             <div class="info">
                             <div class="info">
-                                <span title="创建时间" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-clock-o"></i> 2016/11/27 06:09</span>
-                                <span title="创建者" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-user"></i> admin</span>
-                                <span title="文档数量" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-pie-chart"></i> 4</span>
-                                <span title="项目角色" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-user-secret"></i>  拥有者</span>
-                                <span title="最后编辑" data-toggle="tooltip" data-placement="bottom"><i class="fa fa-pencil"></i> 最后编辑: admin 于 2017-04-20 12:19</span>
+                                <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>
                     </div>
                     </div>
+                    <nav>
+                        {{.PageHtml}}
+                    </nav>
                 </div>
                 </div>
             </div>
             </div>
         </div>
         </div>
@@ -91,7 +110,7 @@
                     </div>
                     </div>
                     <input type="text" class="form-control pull-left" style="width: 220px;vertical-align: middle" placeholder="项目唯一标识(不能超过50字)" name="identify" id="identify">
                     <input type="text" class="form-control pull-left" style="width: 220px;vertical-align: middle" placeholder="项目唯一标识(不能超过50字)" name="identify" id="identify">
                     <div class="clearfix"></div>
                     <div class="clearfix"></div>
-                    <p class="text" style="font-size: 12px;color: #999;margin-top: 6px;">文档标识只能包含小写字母、数字,以及“-”和“_”符号,并且只能小写字母头</p>
+                    <p class="text" style="font-size: 12px;color: #999;margin-top: 6px;">文档标识只能包含小写字母、数字,以及“-”和“_”符号,并且只能小写字母头</p>
 
 
                 </div>
                 </div>
                 <div class="form-group">
                 <div class="form-group">
@@ -118,12 +137,12 @@
                     </div>
                     </div>
                     <div class="col-lg-3">
                     <div class="col-lg-3">
                         <label>
                         <label>
-                            <input type="radio"  name="comment_status" value="">关闭评论<span class="text"></span>
+                            <input type="radio"  name="comment_status" value="closed">关闭评论<span class="text"></span>
                         </label>
                         </label>
                     </div>
                     </div>
                     <div class="col-lg-3">
                     <div class="col-lg-3">
                         <label>
                         <label>
-                            <input type="radio" name="comment_status" value="">仅允许参与者评论<span class="text"></span>
+                            <input type="radio" name="comment_status" value="group_only">仅允许参与者评论<span class="text"></span>
                         </label>
                         </label>
                     </div>
                     </div>
                     <div class="col-lg-3">
                     <div class="col-lg-3">
@@ -147,6 +166,7 @@
 
 
 <script src="/static/jquery/1.12.4/jquery.min.js"></script>
 <script src="/static/jquery/1.12.4/jquery.min.js"></script>
 <script src="/static/bootstrap/js/bootstrap.min.js"></script>
 <script src="/static/bootstrap/js/bootstrap.min.js"></script>
+<script src="/static/vuejs/vue.min.js" type="text/javascript"></script>
 <script src="/static/js/jquery.form.js" type="text/javascript"></script>
 <script src="/static/js/jquery.form.js" type="text/javascript"></script>
 <script src="/static/js/main.js" type="text/javascript"></script>
 <script src="/static/js/main.js" type="text/javascript"></script>
 <script type="text/javascript">
 <script type="text/javascript">
@@ -176,9 +196,27 @@
                 return showSuccess("");
                 return showSuccess("");
             },
             },
             success : function (res) {
             success : function (res) {
+                console.log(res)
+                if(res.errcode === 0){
 
 
+                }else{
+                    showError(res.message);
+                }
             }
             }
         });
         });
+
+        new Vue({
+            el : "#bookList",
+            data : {
+                lists : {{.Result}}
+            },
+            delimiters : ['${','}'],
+            methods : {
+            }
+        });
+        Vue.nextTick(function () {
+            $("[data-toggle='tooltip']").tooltip();
+        });
     });
     });
 </script>
 </script>
 </body>
 </body>

+ 66 - 7
views/book/setting.tpl

@@ -5,7 +5,7 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta name="viewport" content="width=device-width, initial-scale=1">
 
 
-    <title>概要 - Powered by MinDoc</title>
+    <title>设置 - Powered by MinDoc</title>
 
 
     <!-- Bootstrap -->
     <!-- Bootstrap -->
     <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
     <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
@@ -26,9 +26,9 @@
         <div class="row">
         <div class="row">
             <div class="page-left">
             <div class="page-left">
                 <ul class="menu">
                 <ul class="menu">
-                    <li><a href="{{urlfor "BookController.Dashboard" ":key" "test"}}" class="item"><i class="fa fa-dashboard" aria-hidden="true"></i> 概要</a> </li>
-                    <li><a href="{{urlfor "BookController.Users" ":key" "test"}}" class="item"><i class="fa fa-users" aria-hidden="true"></i> 成员</a> </li>
-                    <li class="active"><a href="{{urlfor "BookController.Setting" ":key" "test"}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a> </li>
+                    <li><a href="{{urlfor "BookController.Dashboard" ":key" .Model.Identify}}" class="item"><i class="fa fa-dashboard" aria-hidden="true"></i> 概要</a> </li>
+                    <li><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-users" aria-hidden="true"></i> 成员</a> </li>
+                    <li class="active"><a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a> </li>
                 </ul>
                 </ul>
 
 
             </div>
             </div>
@@ -36,8 +36,17 @@
                 <div class="m-box">
                 <div class="m-box">
                     <div class="box-head">
                     <div class="box-head">
                         <strong class="box-title"> 项目设置</strong>
                         <strong class="box-title"> 项目设置</strong>
+                        {{if eq .Model.RoleId 0 1}}
+                        {{if eq .Model.RoleId 0}}
                         <button type="button"  class="btn btn-success btn-sm pull-right">转让项目</button>
                         <button type="button"  class="btn btn-success btn-sm pull-right">转让项目</button>
+                        {{end}}
                         <button type="button"  class="btn btn-danger btn-sm pull-right" style="margin-right: 5px;">删除项目</button>
                         <button type="button"  class="btn btn-danger btn-sm pull-right" style="margin-right: 5px;">删除项目</button>
+                        {{end}}
+                        {{if eq .Model.PrivatelyOwned 1}}
+                        <button type="button"  class="btn btn-success btn-sm pull-right" style="margin-right: 5px;">转为公有</button>
+                        {{else}}
+                        <button type="button"  class="btn btn-danger btn-sm pull-right" style="margin-right: 5px;">转为私有</button>
+                        {{end}}
                     </div>
                     </div>
                 </div>
                 </div>
                 <div class="box-body" style="padding-right: 200px;">
                 <div class="box-body" style="padding-right: 200px;">
@@ -45,18 +54,68 @@
                         <form method="post" id="bookEditForm">
                         <form method="post" id="bookEditForm">
                             <div class="form-group">
                             <div class="form-group">
                                 <label>标题</label>
                                 <label>标题</label>
-                                <input type="text" class="form-control" placeholder="项目名称">
+                                <input type="text" class="form-control" placeholder="项目名称" value="{{.Model.BookName}}">
+                            </div>
+                            <div class="form-group">
+                                <label>标识</label>
+                                <input type="text" class="form-control" value=" {{.BaseUrl}}{{urlfor "DocumentController.Index" ":key" .Model.Identify}}" disabled>
+                            </div>
+                            <div class="form-group">
+                                <label>描述</label>
+                                <textarea rows="3" class="form-control" name="description" style="height: 90px">{{.Model.Description}}</textarea>
+                                <p class="text">描述信息不超过300个字符</p>
+                            </div>
+                            <div class="form-group">
+                                <label>标签</label>
+                                <input type="text" class="form-control" placeholder="项目标签" value="{{.Model.Label}}">
+                                <p class="text">最多允许添加10个标签,多个标签请用“;”分割</p>
+                            </div>
+                            <div class="form-group">
+                                <label>开启评论</label>
+                                <div class="radio">
+                                    <label class="radio-inline">
+                                        <input type="radio" {{if eq .Model.CommentStatus "open"}}checked{{end}} name="comment_status" value="open">允许所有人评论<span class="text"></span>
+                                    </label>
+                                    <label class="radio-inline">
+                                        <input type="radio" {{if eq .Model.CommentStatus "closed"}}checked{{end}} name="comment_status" value="closed">关闭评论<span class="text"></span>
+                                    </label>
+                                    <label class="radio-inline">
+                                        <input type="radio" {{if eq .Model.CommentStatus "group_only"}}checked{{end}} name="comment_status" value="group_only">仅允许参与者评论<span class="text"></span>
+                                    </label>
+                                    <label class="radio-inline">
+                                        <input type="radio" {{if eq .Model.CommentStatus "registered_only"}}checked{{end}} name="comment_status" value="registered_only">仅允许注册者评论<span class="text"></span>
+                                    </label>
+                                </div>
+                            </div>
+                            {{if eq .Model.PrivatelyOwned 1}}
+                            <div class="form-group">
+                                <label>访问令牌</label>
+                                <div class="row">
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" placeholder="访问令牌" readonly>
+                                    </div>
+                                   <div class="col-sm-4">
+                                       <button class="btn btn-success btn-sm">重写生成</button>
+                                       <button class="btn btn-danger btn-sm">删除令牌</button>
+                                   </div>
+                                </div>
+                            </div>
+                            {{end}}
+                            <div class="form-group">
+                                <button type="submit" class="btn btn-success" data-loading-text="保存中...">保存修改</button>
+                                <span id="form-error-message" class="error-message"></span>
                             </div>
                             </div>
-
                         </form>
                         </form>
                     </div>
                     </div>
                     <div class="form-right">
                     <div class="form-right">
                         <label>
                         <label>
                             <a href="javascript:;" data-toggle="modal" data-target="#upload-logo-panel">
                             <a href="javascript:;" data-toggle="modal" data-target="#upload-logo-panel">
-                                <img src="/static/images/5fcb811e04c23cdb2088f26923fcc287_100.jpg" onerror="this.src='https://wiki.iminho.me/static/images/middle.gif'" alt="头像" style="max-width: 120px;" id="headimgurl">
+                                <img src="{{.Model.Cover}}" onerror="this.src='/static/images/book.png'" alt="封面" style="max-width: 120px;border: 1px solid #999" id="headimgurl">
                             </a>
                             </a>
                         </label>
                         </label>
                     </div>
                     </div>
+                    <div class="clearfix"></div>
+
                 </div>
                 </div>
             </div>
             </div>
         </div>
         </div>

+ 99 - 41
views/book/users.tpl

@@ -5,7 +5,7 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta name="viewport" content="width=device-width, initial-scale=1">
 
 
-    <title>概要 - Powered by MinDoc</title>
+    <title>成员 - Powered by MinDoc</title>
 
 
     <!-- Bootstrap -->
     <!-- Bootstrap -->
     <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
     <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
@@ -26,9 +26,9 @@
         <div class="row">
         <div class="row">
             <div class="page-left">
             <div class="page-left">
                 <ul class="menu">
                 <ul class="menu">
-                    <li><a href="{{urlfor "BookController.Dashboard" ":key" "test"}}" class="item"><i class="fa fa-dashboard" aria-hidden="true"></i> 概要</a> </li>
-                    <li class="active"><a href="{{urlfor "BookController.Users" ":key" "test"}}" class="item"><i class="fa fa-users" aria-hidden="true"></i> 成员</a> </li>
-                    <li><a href="{{urlfor "BookController.Setting" ":key" "test"}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a> </li>
+                    <li><a href="{{urlfor "BookController.Dashboard" ":key" .Model.Identify}}" class="item"><i class="fa fa-dashboard" aria-hidden="true"></i> 概要</a> </li>
+                    <li class="active"><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-users" aria-hidden="true"></i> 成员</a> </li>
+                    <li><a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a> </li>
                 </ul>
                 </ul>
 
 
             </div>
             </div>
@@ -36,48 +36,38 @@
                 <div class="m-box">
                 <div class="m-box">
                     <div class="box-head">
                     <div class="box-head">
                         <strong class="box-title"> 成员管理</strong>
                         <strong class="box-title"> 成员管理</strong>
-                        <a href="{{urlfor "BookController.Edit" ":key" "test" ":id" "1"}}" class="btn btn-success btn-sm pull-right" target="_blank"><i class="fa fa-edit" aria-hidden="true"></i> 添加成员</a>
+                        <button type="button"  class="btn btn-success btn-sm pull-right" data-toggle="modal" data-target="#addBookMemberDialogModal"><i class="fa fa-user-plus" aria-hidden="true"></i> 添加成员</button>
                     </div>
                     </div>
                 </div>
                 </div>
                 <div class="box-body">
                 <div class="box-body">
-                    <div class="users-list">
-                        <div class="list-item">
-                            <img src="/static/images/middle.gif" class="img-circle" width="34" height="34">
-                            <span>lifei6671</span>
-                            <div class="operate">
-                                创始人
-                            </div>
-                        </div>
-                        <div class="list-item">
-                            <img src="/static/images/middle.gif" class="img-circle" width="34" height="34">
-                            <span>lifei6671</span>
-                            <div class="operate">
-                                <div class="btn-group">
-                                    <button type="button" class="btn btn-default btn-sm"  data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">编辑者 <span class="caret"></span></button>
-                                    <ul class="dropdown-menu">
-                                        <li><a href="#">管理员</a> </li>
-                                        <li><a href="#">编辑者</a> </li>
-                                        <li><a href="#">观察者</a> </li>
-                                    </ul>
-                                </div>
-                                <a href="#" class="btn btn-danger btn-sm">移除</a>
-                            </div>
-                        </div>
-                        <div class="list-item">
-                            <img src="/static/images/middle.gif" class="img-circle" width="34" height="34">
-                            <span>lifei6671</span>
-                            <div class="operate">
-                                <div class="btn-group">
-                                    <button type="button" class="btn btn-default btn-sm"  data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">编辑者 <span class="caret"></span></button>
-                                    <ul class="dropdown-menu">
-                                        <li><a href="#">管理员</a> </li>
-                                        <li><a href="#">编辑者</a> </li>
-                                        <li><a href="#">观察者</a> </li>
-                                    </ul>
+                    <div class="users-list" id="userList">
+                        <template v-if="lists.length <= 0">
+                            <div class="text-center">暂无数据</div>
+                        </template>
+                        <template v-else>
+                            <div class="list-item" v-for="item in lists">
+                                <img :src="item.avatar" onerror="this.src='/static/images/middle.gif'" class="img-circle" width="34" height="34">
+                                <span>${item.account}</span>
+                                <div class="operate">
+                                    <template v-if="item.role_id == 0">
+                                        创始人
+                                    </template>
+                                   <template v-else-if="item.role_id == 1">
+                                       {{if eq .Member.Role 0}}
+                                       <div class="btn-group">
+                                           <button type="button" class="btn btn-default btn-sm"  data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">编辑者 <span class="caret"></span></button>
+                                           <ul class="dropdown-menu">
+                                               <li><a href="#">管理员</a> </li>
+                                               <li><a href="#">编辑者</a> </li>
+                                               <li><a href="#">观察者</a> </li>
+                                           </ul>
+                                       </div>
+                                       <a href="#" class="btn btn-danger btn-sm">移除</a>
+                                       {{end}}
+                                   </template>
                                 </div>
                                 </div>
-                                <a href="#" class="btn btn-danger btn-sm">移除</a>
                             </div>
                             </div>
-                        </div>
+                        </template>
                     </div>
                     </div>
                 </div>
                 </div>
             </div>
             </div>
@@ -85,9 +75,77 @@
     </div>
     </div>
     {{template "widgets/footer.tpl" .}}
     {{template "widgets/footer.tpl" .}}
 </div>
 </div>
+<!-- Modal -->
+<div class="modal fade" id="addBookMemberDialogModal" tabindex="-1" role="dialog" aria-labelledby="addBookMemberDialogModalLabel">
+    <div class="modal-dialog modal-sm" role="document" style="width: 400px;">
+        <form method="post" autocomplete="off" class="form-horizontal" action="{{urlfor "BookController.AddMember"}}" id="addBookMemberDialogForm">
+            <input type="hidden" name="identify" value="{{.Model.Identify}}">
+            <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">账号</label>
+                       <div class="col-sm-10">
+                           <input type="text" name="account" class="form-control" placeholder="用户账号" id="account" maxlength="50">
+                       </div>
+                    </div>
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label">角色</label>
+                        <div class="col-sm-10">
+                            <select name="role_id" class="form-control">
+                                <option value="1">管理员</option>
+                                <option value="2">编辑者</option>
+                                <option value="3">观察者</option>
+                            </select>
+                        </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="submit" class="btn btn-success">保存</button>
+                </div>
+            </div>
+        </form>
+    </div>
+</div><!--END Modal-->
 <script src="/static/jquery/1.12.4/jquery.min.js"></script>
 <script src="/static/jquery/1.12.4/jquery.min.js"></script>
 <script src="/static/bootstrap/js/bootstrap.min.js"></script>
 <script src="/static/bootstrap/js/bootstrap.min.js"></script>
+<script src="/static/vuejs/vue.min.js"></script>
+<script src="/static/js/jquery.form.js" type="text/javascript"></script>
 <script src="/static/js/main.js" type="text/javascript"></script>
 <script src="/static/js/main.js" type="text/javascript"></script>
+<script type="text/javascript">
+    $(function () {
+        $("#addBookMemberDialogForm").ajaxForm({
+            beforeSubmit : function () {
+                var account = $.trim($("#account").val());
+                if(account === ""){
+                    return showError("账号不能为空");
+                }
+            },
+            success : function (res) {
+
+            }
+        });
 
 
+        new Vue({
+            el : "#userList",
+            data : {
+                lists : {{.Result}}
+            },
+            delimiters : ['${','}'],
+            methods : {
+            }
+        });
+        Vue.nextTick(function () {
+            $("[data-toggle='tooltip']").tooltip();
+        });
+    });
+</script>
 </body>
 </body>
 </html>
 </html>