Преглед на файлове

feat:实现团队功能

lifei6671 преди 7 години
родител
ревизия
d152455c50

+ 59 - 5
controllers/BookController.go

@@ -171,7 +171,6 @@ func (c *BookController) SaveBook() {
 	book.HistoryCount = historyCount
 	book.IsDownload = 0
 
-
 	if autoRelease {
 		book.AutoRelease = 1
 	} else {
@@ -194,7 +193,7 @@ func (c *BookController) SaveBook() {
 	}
 	if autoSave {
 		book.AutoSave = 1
-	}else{
+	} else {
 		book.AutoSave = 0
 	}
 	if err := book.Update(); err != nil {
@@ -329,7 +328,7 @@ func (c *BookController) UploadCover() {
 	fileName := "cover_" + strconv.FormatInt(time.Now().UnixNano(), 16)
 
 	//附件路径按照项目组织
-	filePath := filepath.Join("uploads", book.Identify,"images", fileName+ext)
+	filePath := filepath.Join("uploads", book.Identify, "images", fileName+ext)
 
 	path := filepath.Dir(filePath)
 
@@ -394,7 +393,7 @@ func (c *BookController) Users() {
 	pageIndex, _ := c.GetInt("page", 1)
 
 	if key == "" {
-		c.Abort("404")
+		c.ShowErrorPage(404, "项目不存在或已删除")
 	}
 
 	book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId)
@@ -407,7 +406,7 @@ func (c *BookController) Users() {
 
 	c.Data["Model"] = *book
 
-	members, totalCount, err := models.NewMemberRelationshipResult().FindForUsersByBookId(book.BookId, pageIndex, 15)
+	members, totalCount, err := models.NewMemberRelationshipResult().FindForUsersByBookId(book.BookId, pageIndex, conf.PageSize)
 
 	if totalCount > 0 {
 		pager := pagination.NewPagination(c.Ctx.Request, totalCount, conf.PageSize, c.BaseUrl())
@@ -819,6 +818,61 @@ func (c *BookController) SaveSort() {
 	c.JsonResult(0, "ok")
 }
 
+func (c *BookController) Team() {
+	c.Prepare()
+	c.TplName = "book/team.tpl"
+
+	key := c.Ctx.Input.Param(":key")
+	pageIndex, _ := c.GetInt("page", 1)
+
+	if key == "" {
+		c.ShowErrorPage(404, "项目不存在或已删除")
+	}
+
+	book, err := models.NewBookResult().FindByIdentify(key, c.Member.MemberId)
+	if err != nil {
+		if err == models.ErrPermissionDenied {
+			c.ShowErrorPage(403, "权限不足")
+		}
+		c.ShowErrorPage(500, "系统错误")
+	}
+
+	c.Data["Model"] = book
+
+	members, totalCount, err := models.NewTeamRelationship().FindByBookToPager(book.BookId, pageIndex, conf.PageSize)
+
+	if totalCount > 0 {
+		pager := pagination.NewPagination(c.Ctx.Request, totalCount, conf.PageSize, c.BaseUrl())
+		c.Data["PageHtml"] = pager.HtmlPages()
+	} else {
+		c.Data["PageHtml"] = ""
+	}
+	b, err := json.Marshal(members)
+
+	if err != nil {
+		c.Data["Result"] = template.JS("[]")
+	} else {
+		c.Data["Result"] = template.JS(string(b))
+	}
+}
+
+func (c *BookController) TeamAdd() {
+	c.Prepare()
+
+	c.JsonResult(0, "OK")
+}
+
+func (c *BookController) TeamDelete() {
+	c.Prepare()
+
+	c.JsonResult(0, "OK")
+}
+
+func (c *BookController) TeamSearch() {
+	c.Prepare()
+
+}
+
 func (c *BookController) IsPermission() (*models.BookResult, error) {
 	identify := c.GetString("identify")
 

+ 94 - 0
controllers/ManagerController.go

@@ -982,15 +982,109 @@ func (c *ManagerController) TeamChangeMemberRole() {
 
 }
 
+//团队项目列表.
 func (c *ManagerController) TeamBookList() {
 	c.Prepare()
 	c.TplName = "manager/team_book_list.tpl"
 
+	teamId, _ := strconv.Atoi(c.Ctx.Input.Param(":id"))
+	pageIndex, _ := c.GetInt("page", 0)
+
+	if teamId <= 0 {
+		c.JsonResult(5002, "团队标识不能为空")
+	}
+
+	team, err := models.NewTeam().First(teamId)
+
+	if err == orm.ErrNoRows {
+		c.ShowErrorPage(404, "团队不存在")
+	}
+	c.CheckErrorResult(500, err)
+	c.Data["Model"] = team
+
+	teams, totalCount, err := models.NewTeamRelationship().FindToPager(teamId, pageIndex, conf.PageSize)
+
+	if err != nil && err != orm.ErrNoRows {
+		c.ShowErrorPage(500, err.Error())
+	}
+	if err == orm.ErrNoRows || len(teams) <= 0 {
+		c.Data["Result"] = template.JS("[]")
+		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"] = ""
+	}
+
+	b, err := json.Marshal(teams)
+
+	if err != nil {
+		beego.Error("编码 JSON 结果失败 ->", err)
+		c.Data["Result"] = template.JS("[]")
+	} else {
+		c.Data["Result"] = template.JS(string(b))
+	}
 }
+
+//给团队增加项目.
 func (c *ManagerController) TeamBookAdd() {
 	c.Prepare()
+
+	teamId, _ := c.GetInt("teamId")
+	bookId, _ := c.GetInt("bookId")
+
+	if teamId <= 0 || bookId <= 0 {
+		c.JsonResult(500, "参数错误")
+	}
+	teamRel := models.NewTeamRelationship()
+	teamRel.BookId = bookId
+	teamRel.TeamId = teamId
+
+	err := teamRel.Save()
+
+	if err != nil {
+		c.JsonResult(5001, err.Error())
+	} else {
+		teamRel.Include()
+		c.JsonResult(0, "OK", teamRel)
+	}
 }
 
+//搜索未参与的项目.
+func (c *ManagerController) TeamSearchBook() {
+	c.Prepare()
+
+	teamId, _ := c.GetInt("teamId")
+	keyword := strings.TrimSpace(c.GetString("q"))
+
+	if teamId <= 0 {
+		c.JsonResult(500, "参数错误")
+	}
+
+	searchResult, err := models.NewTeamRelationship().FindNotJoinBookByName(teamId, keyword, 10)
+
+	if err != nil {
+		c.JsonResult(500, err.Error())
+	}
+	c.JsonResult(0, "OK", searchResult)
+
+}
 func (c *ManagerController) TeamBookDelete() {
 	c.Prepare()
+	teamRelationshipId, _ := c.GetInt("teamRelId")
+
+	if teamRelationshipId <= 0 {
+		c.JsonResult(500, "参数错误")
+	}
+
+	err := models.NewTeamRelationship().Delete(teamRelationshipId)
+
+	if err != nil {
+		c.JsonResult(5001, "删除失败")
+	}
+	c.JsonResult(0, "OK")
 }

+ 1 - 1
models/Team.go

@@ -95,7 +95,7 @@ func (t *Team) FindToPager(pageIndex, pageSize int) (list []*Team, totalCount in
 
 	offset := (pageIndex - 1) * pageSize
 
-	_, err = o.QueryTable(t.TableNameWithPrefix()).Offset(offset).Limit(pageSize).All(&list)
+	_, err = o.QueryTable(t.TableNameWithPrefix()).OrderBy("-team_id").Offset(offset).Limit(pageSize).All(&list)
 
 	if err != nil {
 		return

+ 12 - 2
models/TeamMember.go

@@ -63,7 +63,7 @@ func (m *TeamMember) ChangeRoleId(teamId int, memberId int, roleId conf.BookRole
 	}
 	o := orm.NewOrm()
 
-	err = o.QueryTable(m.TableNameWithPrefix()).Filter("team_id", teamId).Filter("member_id", memberId).One(m)
+	err = o.QueryTable(m.TableNameWithPrefix()).Filter("team_id", teamId).Filter("member_id", memberId).OrderBy("-team_member_id").One(m)
 
 	if err != nil {
 		beego.Error("查询团队用户时失败 ->", err)
@@ -79,6 +79,7 @@ func (m *TeamMember) ChangeRoleId(teamId int, memberId int, roleId conf.BookRole
 	return m, err
 }
 
+//查询团队中指定的用户.
 func (m *TeamMember) FindFirst(teamId, memberId int) (*TeamMember, error) {
 	if teamId <= 0 || memberId <= 0 {
 		return nil, ErrInvalidParameter
@@ -93,6 +94,7 @@ func (m *TeamMember) FindFirst(teamId, memberId int) (*TeamMember, error) {
 	return m.Include(),nil
 }
 
+//更新或插入团队用户.
 func (m *TeamMember) Save(cols ...string) (err error) {
 
 	if m.TeamId <= 0 {
@@ -110,7 +112,11 @@ func (m *TeamMember) Save(cols ...string) (err error) {
 	if !o.QueryTable(NewMember()).Filter("member_id", m.MemberId).Filter("status", 0).Exist() {
 		return errors.New("用户不存在或已禁用")
 	}
+
 	if m.TeamMemberId <= 0 {
+		if o.QueryTable(m.TableNameWithPrefix()).Filter("team_id",m.TeamId).Filter("member_id",m.MemberId).Exist() {
+			return errors.New("团队中已存在该用户")
+		}
 		_, err = o.Insert(m)
 	} else {
 		_, err = o.Update(m, cols...)
@@ -121,6 +127,7 @@ func (m *TeamMember) Save(cols ...string) (err error) {
 	return
 }
 
+//删除一个团队用户.
 func (m *TeamMember) Delete(id int) (err error) {
 
 	if id <= 0 {
@@ -134,6 +141,7 @@ func (m *TeamMember) Delete(id int) (err error) {
 	return
 }
 
+//分页查询团队用户.
 func (m *TeamMember) FindToPager(teamId, pageIndex, pageSize int) (list []*TeamMember, totalCount int, err error) {
 	if teamId <= 0 {
 		err = ErrInvalidParameter
@@ -165,6 +173,7 @@ func (m *TeamMember) FindToPager(teamId, pageIndex, pageSize int) (list []*TeamM
 	return
 }
 
+//查询关联数据.
 func (m *TeamMember) Include() *TeamMember {
 
 	if member, err := NewMember().Find(m.MemberId, "account", "real_name", "avatar"); err == nil {
@@ -184,6 +193,7 @@ func (m *TeamMember) Include() *TeamMember {
 	return m
 }
 
+//查询未加入团队的用户。
 func (m *TeamMember) FindNotJoinMemberByAccount(teamId int, account string, limit int) (*SelectMemberResult, error) {
 	if teamId <= 0 {
 		return nil, ErrInvalidParameter
@@ -193,7 +203,7 @@ func (m *TeamMember) FindNotJoinMemberByAccount(teamId int, account string, limi
 	sql := `select member.member_id,member.account
 from md_members as member 
   left join md_team_member as team on team.team_id = ? and member.member_id != team.member_id
-  where member.account like ?
+  where member.account like ? and team.member_id isnull 
   order by member.member_id desc 
 limit ?;`
 

+ 190 - 1
models/TeamRelationship.go

@@ -3,6 +3,9 @@ package models
 import (
 	"github.com/lifei6671/mindoc/conf"
 	"time"
+	"github.com/astaxie/beego/orm"
+	"github.com/astaxie/beego"
+	"errors"
 )
 
 type TeamRelationship struct {
@@ -10,6 +13,9 @@ type TeamRelationship struct {
 	BookId             int       `orm:"column(book_id)"`
 	TeamId             int       `orm:"column(team_id)"`
 	CreateTime         time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
+	BookMemberId       int       `orm:"-" json:"book_member_id"`
+	BookMemberName     string    `orm:"-" json:"book_member_name"`
+	BookName           string    `orm:"-" json:"book_name"`
 }
 
 // TableName 获取对应数据库表名.
@@ -27,9 +33,192 @@ func (m *TeamRelationship) TableEngine() string {
 
 // 联合唯一键
 func (m *TeamRelationship) TableUnique() [][]string {
-	return [][]string{{"team_id", "team_id"}}
+	return [][]string{{"team_id", "book_id"}}
+}
+func (m *TeamRelationship) QueryTable() orm.QuerySeter {
+	return orm.NewOrm().QueryTable(m.TableNameWithPrefix())
 }
 
 func NewTeamRelationship() *TeamRelationship {
 	return &TeamRelationship{}
 }
+
+//保存团队项目.
+func (m *TeamRelationship) Save(cols ...string) (err error) {
+	if m.TeamId <= 0 || m.BookId <= 0 {
+		return ErrInvalidParameter
+	}
+	if (m.TeamRelationshipId > 0 && m.QueryTable().Filter("book_id", m.BookId).Filter("team_id", m.TeamId).Filter("team_relationship_id__ne", m.TeamRelationshipId).Exist()) ||
+		m.TeamRelationshipId <= 0 && m.QueryTable().Filter("book_id", m.BookId).Filter("team_id", m.TeamId).Exist() {
+		return errors.New("当前团队已加入该项目")
+	}
+	if m.TeamRelationshipId > 0 {
+		_, err = orm.NewOrm().Update(m)
+	} else {
+		_, err = orm.NewOrm().Insert(m)
+	}
+	if err != nil {
+		beego.Error("保存团队项目时出错 ->", err)
+	}
+	return
+}
+
+func (m *TeamRelationship) Delete(teamRelId int) (err error) {
+	if teamRelId <= 0 {
+		return ErrInvalidParameter
+	}
+	_, err = m.QueryTable().Filter("team_relationship_id", teamRelId).Delete()
+
+	if err != nil {
+		beego.Error("删除团队项目失败 ->", err)
+	}
+	return
+}
+
+//分页查询团队项目.
+func (m *TeamRelationship) FindToPager(teamId, pageIndex, pageSize int) (list []*TeamRelationship, totalCount int, err error) {
+	if teamId <= 0 {
+		err = ErrInvalidParameter
+		return
+	}
+	offset := (pageIndex - 1) * pageSize
+
+	o := orm.NewOrm()
+
+	_, err = o.QueryTable(m.TableNameWithPrefix()).Filter("team_id", teamId).OrderBy("-team_relationship_id").Offset(offset).Limit(pageSize).All(&list)
+
+	if err != nil {
+		beego.Error("查询团队项目时出错 ->", err)
+		return
+	}
+	count, err := m.QueryTable().Filter("team_id", teamId).Count()
+
+	if err != nil {
+		beego.Error("查询团队项目时出错 ->", err)
+		return
+	}
+	totalCount = int(count)
+	for _, item := range list {
+		item.Include()
+	}
+	return
+}
+
+//加载附加数据.
+func (m *TeamRelationship) Include() (*TeamRelationship, error) {
+	if m.BookId > 0 {
+		b, err := NewBook().Find(m.BookId, "book_name", "identify", "member_id")
+		if err != nil {
+			return m, err
+		}
+		m.BookName = b.BookName
+		m.BookMemberId = b.MemberId
+		if b.MemberId > 0 {
+			member, err := NewMember().Find(b.MemberId, "account", "real_name")
+			if err != nil {
+				return m, err
+			}
+			if member.RealName == "" {
+				m.BookMemberName = member.Account
+			} else {
+				m.BookMemberName = member.RealName
+			}
+		}
+	}
+	return m, nil
+}
+
+//查询未加入团队的用户。
+func (m *TeamRelationship) FindNotJoinBookByName(teamId int, bookName string, limit int) (*SelectMemberResult, error) {
+	if teamId <= 0 {
+		return nil, ErrInvalidParameter
+	}
+	o := orm.NewOrm()
+
+	sql := `select book.book_id,book.book_name
+from  md_books as book
+where book.book_id not in (select team.book_id from md_team_relationship as team where team_id=?)
+and book.book_name like ? order by book_id desc limit ?;`
+
+	books := make([]*Book, 0)
+
+	_, err := o.Raw(sql, teamId, "%"+bookName+"%", limit).QueryRows(&books)
+
+	if err != nil {
+		beego.Error("查询团队项目时出错 ->", err)
+		return nil, err
+	}
+
+	result := SelectMemberResult{}
+	items := make([]KeyValueItem, 0)
+
+	for _, book := range books {
+		item := KeyValueItem{}
+		item.Id = book.BookId
+		item.Text = book.BookName
+		items = append(items, item)
+	}
+	result.Result = items
+
+	return &result, err
+}
+
+//查询指定项目的团队.
+func (m *TeamRelationship) FindByBookToPager(bookId, pageIndex,pageSize int) (list []*TeamRelationship,totalCount int,err error) {
+
+	if bookId <= 0{
+		err = ErrInvalidParameter
+		return
+	}
+
+	offset := (pageIndex - 1) * pageSize
+
+	o := orm.NewOrm()
+
+	_, err = o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", bookId).OrderBy("-team_relationship_id").Offset(offset).Limit(pageSize).All(&list)
+
+	if err != nil {
+		beego.Error("查询团队项目时出错 ->", err)
+		return
+	}
+	count, err := m.QueryTable().Filter("book_id", bookId).Count()
+
+	if err != nil {
+		beego.Error("查询团队项目时出错 ->", err)
+		return
+	}
+	totalCount = int(count)
+	for _, item := range list {
+		item.Include()
+	}
+	return
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 8 - 0
routers/router.go

@@ -55,6 +55,7 @@ func init() {
 	beego.Router("/manager/team/book/list/:id", &controllers.ManagerController{}, "*:TeamBookList")
 	beego.Router("/manager/team/book/add", &controllers.ManagerController{}, "POST:TeamBookAdd")
 	beego.Router("/manager/team/book/delete", &controllers.ManagerController{}, "POST:TeamBookDelete")
+	beego.Router("/manager/team/book/search", &controllers.ManagerController{}, "*:TeamSearchBook")
 
 
 	beego.Router("/setting", &controllers.SettingController{}, "*:Index")
@@ -67,6 +68,8 @@ func init() {
 	beego.Router("/book/:key/users", &controllers.BookController{}, "*:Users")
 	beego.Router("/book/:key/release", &controllers.BookController{}, "post:Release")
 	beego.Router("/book/:key/sort", &controllers.BookController{}, "post:SaveSort")
+	beego.Router("/book/:key/teams", &controllers.BookController{}, "*:Team")
+
 
 	beego.Router("/book/create", &controllers.BookController{}, "*:Create")
 	beego.Router("/book/users/create", &controllers.BookMemberController{}, "post:AddMember")
@@ -82,6 +85,11 @@ func init() {
 	beego.Router("/book/setting/token", &controllers.BookController{}, "post:CreateToken")
 	beego.Router("/book/setting/delete", &controllers.BookController{}, "post:Delete")
 
+	beego.Router("/book/team/add", &controllers.BookController{}, "POST:TeamAdd")
+	beego.Router("/book/team/delete", &controllers.BookController{}, "POST:TeamDelete")
+	beego.Router("/book/team/search", &controllers.BookController{}, "POST:TeamSearch")
+
+
 	//管理文章的路由
 	beego.Router("/manage/blogs", &controllers.BlogController{},"*:ManageList")
 	beego.Router("/manage/blogs/setting/?:id", &controllers.BlogController{}, "*:ManageSetting")

+ 2 - 1
views/book/dashboard.tpl

@@ -27,7 +27,8 @@
             <div class="page-left">
                 <ul class="menu">
                     <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.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-user" aria-hidden="true"></i> 成员</a> </li>
+                    <li><a href="{{urlfor "BookController.Team" ":key" .Model.Identify}}" class="item"><i class="fa fa-group" aria-hidden="true"></i> 团队</a> </li>
                     {{if eq .Model.RoleId 0 1}}
                     <li><a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a> </li>
                     {{end}}

+ 3 - 7
views/book/setting.tpl

@@ -15,12 +15,7 @@
     <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/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:// -->
-    <!--[if lt IE 9]>
-    <script src="/static/html5shiv/3.7.3/html5shiv.min.js"></script>
-    <script src="/static/respond.js/1.4.2/respond.min.js"></script>
-    <![endif]-->
+
 </head>
 <body>
 <div class="manual-reader">
@@ -30,7 +25,8 @@
             <div class="page-left">
                 <ul class="menu">
                     <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><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-user" aria-hidden="true"></i> 成员</a> </li>
+                    <li><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-group" 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>
 

+ 301 - 0
views/book/team.tpl

@@ -0,0 +1,301 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+    <title>团队 - {{.Model.BookName}} - Powered by MinDoc</title>
+
+    <!-- Bootstrap -->
+    <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
+    <link href="{{cdncss "/static/font-awesome/css/font-awesome.min.css"}}" rel="stylesheet">
+    <link href="{{cdncss "/static/select2/4.0.5/css/select2.min.css"}}" rel="stylesheet">
+
+    <link href="{{cdncss "/static/css/main.css" "version"}}" rel="stylesheet">
+
+    <style type="text/css">
+        .table > tbody > tr > td {
+            vertical-align: middle;
+        }
+    </style>
+</head>
+<body>
+<div class="manual-reader">
+{{template "widgets/header.tpl" .}}
+    <div class="container manual-body">
+        <div class="row">
+            <div class="page-left">
+                <ul class="menu">
+                    <li><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-user" aria-hidden="true"></i> 成员</a> </li>
+                    <li class="active"><a href="{{urlfor "BookController.Team" ":key" .Model.Identify}}" class="item"><i class="fa fa-group" aria-hidden="true"></i> 团队</a> </li>
+                {{if eq .Model.RoleId 0 1}}
+                    <li><a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a> </li>
+                {{end}}
+                </ul>
+
+            </div>
+            <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="#addTeamDialogModal"><i class="fa fa-user-plus" aria-hidden="true"></i>
+                            添加团队
+                        </button>
+                    {{end}}
+                    </div>
+                </div>
+                <div class="box-body">
+                    <div class="users-list" id="teamList">
+                        <template v-if="lists.length <= 0">
+                            <div class="text-center">暂无数据</div>
+                        </template>
+                        <template v-else>
+                            <table class="table">
+                                <thead>
+                                <tr>
+                                    <th>团队名称</th>
+                                    <th width="150px">成员数量</th>
+                                    <th width="150px">项目数量</th>
+                                    <th align="center" width="220px">操作</th>
+                                </tr>
+                                </thead>
+                                <tbody>
+                                <tr v-for="item in lists">
+                                    <td>${item.team_name}</td>
+                                    <td>${item.member_count}</td>
+                                    <td>${item.book_count}</td>
+                                    <td>
+                                        <a :href="'{{urlfor "ManagerController.TeamBookList" ":id" ""}}' + item.team_id" class="btn btn-primary btn-sm">项目</a>
+                                        <a :href="'{{urlfor "ManagerController.TeamMemberList" ":id" ""}}' + item.team_id" type="button" class="btn btn-success btn-sm">成员</a>
+                                        <button type="button" class="btn btn-sm btn-default" @click="editTeam(item.team_id)">编辑</button>
+                                        <button type="button" class="btn btn-danger btn-sm" @click="deleteTeam(item.team_id,$event)" data-loading-text="删除中">删除</button>
+                                    </td>
+                                </tr>
+                                </tbody>
+                            </table>
+                        </template>
+                        <nav class="pagination-container">
+                        {{.PageHtml}}
+                        </nav>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+{{template "widgets/footer.tpl" .}}
+</div>
+<!-- Modal -->
+<div class="modal fade" id="addTeamDialogModal" tabindex="-1" role="dialog" aria-labelledby="addTeamDialogModalLabel">
+    <div class="modal-dialog" role="document">
+        <form method="post" autocomplete="off" class="form-horizontal"
+              action="{{urlfor "ManagerController.TeamCreate"}}" id="addTeamDialogForm">
+            <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="account">团队名称<span
+                                class="error-message">*</span></label>
+                        <div class="col-sm-10">
+                            <input type="text" name="teamName" class="form-control" placeholder="团队名称" id="teamName" maxlength="50">
+                        </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" data-loading-text="保存中..." id="btnAddTeam">保存
+                    </button>
+                </div>
+            </div>
+        </form>
+    </div>
+</div><!--END Modal-->
+<div class="modal fade" id="editTeamDialogModal" tabindex="-1" role="dialog" aria-labelledby="editTeamDialogModalLabel">
+    <div class="modal-dialog" role="document">
+        <form method="post" autocomplete="off" class="form-horizontal" action="{{urlfor "ManagerController.TeamEdit"}}" id="editTeamDialogForm">
+            <input type="hidden" name="teamId" 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="account">团队名称<span class="error-message">*</span></label>
+                        <div class="col-sm-10">
+                            <input type="text" name="teamName" class="form-control" placeholder="团队名称" maxlength="50">
+                        </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" data-loading-text="保存中..." id="btnEditTeam">保存
+                    </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/vuejs/vue.min.js"}}"></script>
+<script src="{{cdnjs "/static/js/jquery.form.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 () {
+        var editTeamDialogModal = $("#editTeamDialogModal");
+
+        $("#addTeamDialogModal").on("show.bs.modal", function () {
+            window.addTeamDialogModalHtml = $(this).find("form").html();
+            $('.js-data-example-ajax').select2({
+                language: "zh-CN",
+                minimumInputLength : 1,
+                minimumResultsForSearch: Infinity,
+                maximumSelectionLength:1,
+                width : "100%",
+                ajax: {
+                    url: '{{urlfor "BookController.TeamSearch" "identity" .Model.Identify}}',
+                    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("form").html(window.addTeamDialogModalHtml);
+        });
+
+
+        $("#addTeamDialogForm").ajaxForm({
+            beforeSubmit: function () {
+                var account = $.trim($("#addTeamDialogForm #teamName").val());
+                if (account === "") {
+                    return showError("团队名称不能为空");
+                }
+                $("#btnAddTeam").button("loading");
+                return true;
+            },
+            success: function ($res) {
+                if ($res.errcode === 0) {
+                    app.lists.splice(0, 0, $res.data);
+                    $("#addTeamDialogModal").modal("hide");
+                } else {
+                    showError($res.message);
+                }
+            },
+            error: function () {
+                showError("服务器异常");
+            },
+            complete: function () {
+                $("#btnAddTeam").button("reset");
+            }
+        });
+
+        editTeamDialogModal.on("shown.bs.modal",function () {
+            $(this).find("input[name='teamName']").focus();
+        });
+        $("#editTeamDialogForm").ajaxForm({
+            beforeSubmit: function () {
+                var account = $.trim(editTeamDialogModal.find("input[name='teamName']").val());
+                if (account === "") {
+                    return showError("团队名称不能为空");
+                }
+                $("#btnEditTeam").button("loading");
+                return true;
+            },success :function ($res) {
+                if ($res.errcode === 0) {
+                    for (var index in app.lists) {
+                        var item = app.lists[index];
+                        if (item.team_id == $res.data.team_id) {
+                            app.lists.splice(index, 1, $res.data);
+                            break;
+                        }
+                    }
+                    editTeamDialogModal.modal("hide");
+                }else {
+                    showError($res.message);
+                }
+            },
+            error: function () {
+                showError("服务器异常");
+            },
+            complete: function () {
+                $("#btnEditTeam").button("reset");
+            }
+        });
+
+        var app = new Vue({
+            el: "#teamList",
+            data: {
+                lists: {{.Result}}
+            },
+            delimiters: ['${', '}'],
+            methods: {
+                deleteTeam: function (id, e) {
+                    var $this = this;
+                    $.ajax({
+                        url: "{{urlfor "ManagerController.TeamDelete"}}",
+                        type: "post",
+                        data: {"id": id},
+                        dataType: "json",
+                        success: function (res) {
+                            if (res.errcode === 0) {
+
+                                for (var index in $this.lists) {
+                                    var item = $this.lists[index];
+                                    if (item.team_id == id) {
+                                        $this.lists.splice(index, 1);
+                                        break;
+                                    }
+                                }
+                            } else {
+                                alert("操作失败:" + res.message);
+                            }
+                        }
+                    });
+                },
+                editTeam : function (id, e) {
+                    var $this = this;
+                    for (var index in $this.lists) {
+                        var item = $this.lists[index];
+                        if (item.team_id == id) {
+                            editTeamDialogModal.find("input[name='teamName']").val(item.team_name);
+                            editTeamDialogModal.find("input[name='teamId']").val(item.team_id);
+                            editTeamDialogModal.modal("show");
+                            break;
+                        }
+                    }
+                }
+            }
+        });
+        Vue.nextTick(function () {
+            $("[data-toggle='tooltip']").tooltip();
+        });
+    });
+</script>
+</body>
+</html>

+ 3 - 2
views/book/users.tpl

@@ -22,8 +22,9 @@
             <div class="page-left">
                 <ul class="menu">
                     <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>
-                    {{if eq .Model.RoleId 0 1}}
+                    <li class="active"><a href="{{urlfor "BookController.Users" ":key" .Model.Identify}}" class="item"><i class="fa fa-user" aria-hidden="true"></i> 成员</a> </li>
+                    <li><a href="{{urlfor "BookController.Team" ":key" .Model.Identify}}" class="item"><i class="fa fa-group" aria-hidden="true"></i> 团队</a> </li>
+                {{if eq .Model.RoleId 0 1}}
                     <li><a href="{{urlfor "BookController.Setting" ":key" .Model.Identify}}" class="item"><i class="fa fa-gear" aria-hidden="true"></i> 设置</a> </li>
                     {{end}}
                 </ul>

+ 191 - 0
views/manager/team_book_list.tpl

@@ -0,0 +1,191 @@
+<!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/select2/4.0.5/css/select2.min.css"}}" rel="stylesheet">
+
+    <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" "team"}}
+            <div class="page-right">
+                <div class="m-box">
+                    <div class="box-head">
+                        <strong class="box-title">{{.Model.TeamName}} - 团队项目</strong>
+                        <button type="button"  class="btn btn-success btn-sm pull-right" data-toggle="modal" data-target="#addTeamBookDialogModal"><i class="fa fa-book" aria-hidden="true"></i> 添加项目</button>
+                    </div>
+                </div>
+                <div class="box-body">
+                    <div class="attach-list" id="teamBookList">
+                        <table class="table">
+                            <thead>
+                            <tr>
+                                <th>项目名称</th>
+                                <th>项目作者</th>
+                                <th>加入时间</th>
+                                <th>操作</th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <template v-if="lists.length <= 0">
+                                <tr class="text-center"><td colspan="4">暂无数据</td></tr>
+                            </template>
+                            <template v-else>
+                            <tr v-for="item in lists">
+                                <td>${item.book_name}</td>
+                                <td>${item.book_member_name}</td>
+                                <td>${(new Date(item.create_time)).format("yyyy-MM-dd hh:mm:ss")}</td>
+                                <td>
+                                    <button type="button" data-method="delete" class="btn btn-danger btn-sm" @click="deleteTeamBook(item.team_relationship_id)" data-loading-text="删除中...">删除</button>
+                                </td>
+                            </tr>
+                            </template>
+                            </tbody>
+                        </table>
+
+                        <nav class="pagination-container">
+                        {{.PageHtml}}
+                        </nav>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+{{template "widgets/footer.tpl" .}}
+</div>
+<!-- Modal -->
+<div class="modal fade" id="addTeamBookDialogModal" tabindex="-1" role="dialog" aria-labelledby="addTeamBookDialogModalLabel">
+    <div class="modal-dialog modal-sm" role="document" style="width: 450px;">
+        <form method="post" autocomplete="off" class="form-horizontal" action="{{urlfor "ManagerController.TeamBookAdd"}}" id="addTeamBookDialogForm">
+            <input type="hidden" name="teamId" value="{{.Model.TeamId}}">
+            <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">项目名称</label>
+                        <div class="col-sm-9">
+                            <select class="js-data-example-ajax form-control" multiple="multiple" name="bookId" id="bookId"></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" data-loading-text="保存中..." id="btnAddBook">保存</button>
+                </div>
+            </div>
+        </form>
+    </div>
+</div><!--END Modal-->
+<script src="{{cdnjs "/static/jquery/1.12.4/jquery.min.js"}}"></script>
+<script src="{{cdnjs "/static/vuejs/vue.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/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 () {
+        var modalCache = $("#addTeamBookDialogModal form").html();
+
+        $("#addTeamBookDialogForm").ajaxForm({
+            beforeSubmit : function () {
+                var memberId = $.trim($("#bookId").val());
+                if(memberId === ""){
+                    return showError("项目不能为空");
+                }
+                $("#btnAddBook").button("loading");
+            },
+            success : function (res) {
+                if(res.errcode === 0){
+                    app.lists.splice(0,0,res.data);
+                    $("#addTeamBookDialogModal").modal("hide");
+                }else{
+                    showError(res.message);
+                }
+                $("#btnAddBook").button("reset");
+            }
+        });
+
+        $("#addTeamBookDialogModal").on("hidden.bs.modal",function () {
+            $(this).find("form").html(modalCache);
+        }).on("show.bs.modal",function () {
+            $('.js-data-example-ajax').select2({
+                language: "zh-CN",
+                minimumInputLength : 1,
+                minimumResultsForSearch: Infinity,
+                maximumSelectionLength:1,
+                width : "100%",
+                ajax: {
+                    url: '{{urlfor "ManagerController.TeamSearchBook" "teamId" .Model.TeamId}}',
+                    dataType: 'json',
+                    data: function (params) {
+                        return {
+                            q: params.term, // search term
+                            page: params.page
+                        };
+                    },
+                    processResults: function (data, params) {
+                        return {
+                            results : data.data.results
+                        }
+                    }
+                }
+            });
+        });
+
+        var app = new Vue({
+            el: "#teamBookList",
+            data: {
+                lists: {{.Result}}
+            },
+            delimiters: ['${', '}'],
+            methods: {
+                deleteTeamBook: function ($id, $e) {
+                    var $this = this;
+                    $.ajax({
+                        url : "{{urlfor "ManagerController.TeamBookDelete"}}",
+                        data : { "teamRelId" : $id },
+                        type : "post",
+                        dataType : "json",
+                        success : function ($res) {
+                            if($res.errcode === 0){
+                                for (var index in $this.lists) {
+                                    var item = $this.lists[index];
+                                    if (item.team_relationship_id == $id) {
+                                        $this.lists.splice(index,1);
+                                        break;
+                                    }
+                                }
+                            }else {
+                                layer.msg(res.message);
+                            }
+                        },
+                        error : function () {
+                            layer.msg("服务器异常");
+                        }
+                    });
+                }
+            }
+        });
+    });
+</script>
+</body>
+</html>

+ 0 - 3
views/manager/team_member_list.tpl

@@ -26,9 +26,7 @@
                 <div class="m-box">
                     <div class="box-head">
                         <strong class="box-title">{{.Model.TeamName}} - 成员管理</strong>
-                    {{if eq .Member.Role 0}}
                         <button type="button"  class="btn btn-success btn-sm pull-right" data-toggle="modal" data-target="#addTeamMemberDialogModal"><i class="fa fa-user-plus" aria-hidden="true"></i> 添加成员</button>
-                    {{end}}
                     </div>
                 </div>
                 <div class="box-body">
@@ -224,7 +222,6 @@
                                 for (var index in $this.lists) {
                                     var item = $this.lists[index];
                                     if (item.member_id == id) {
-                                        console.log(item);
                                         $this.lists.splice(index,1);
                                         break;
                                     }