github.com/zhongdalu/gf@v1.0.0/g/util/gpage/gpage.go (about)

     1  // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/zhongdalu/gf.
     6  
     7  // Package gpage provides useful paging functionality for web pages.
     8  package gpage
     9  
    10  import (
    11  	"fmt"
    12  	"github.com/zhongdalu/gf/g/net/ghttp"
    13  	"github.com/zhongdalu/gf/g/text/gregex"
    14  	"github.com/zhongdalu/gf/g/text/gstr"
    15  	"github.com/zhongdalu/gf/g/util/gconv"
    16  	"math"
    17  	url2 "net/url"
    18  	"strings"
    19  )
    20  
    21  // 分页对象
    22  type Page struct {
    23  	Url            *url2.URL     // 当前页面的URL对象
    24  	Router         *ghttp.Router // 当前页面的路由对象(与gf框架耦合,在静态分页下有效)
    25  	UrlTemplate    string        // URL生成规则,内部可使用{.page}变量指定页码
    26  	TotalSize      int           // 总共数据条数
    27  	TotalPage      int           // 总页数
    28  	CurrentPage    int           // 当前页码
    29  	PageName       string        // 分页参数名称(GET参数)
    30  	NextPageTag    string        // 下一页标签
    31  	PrevPageTag    string        // 上一页标签
    32  	FirstPageTag   string        // 首页标签
    33  	LastPageTag    string        // 尾页标签
    34  	PrevBar        string        // 上一分页条
    35  	NextBar        string        // 下一分页条
    36  	PageBarNum     int           // 控制分页条的数量
    37  	AjaxActionName string        // AJAX方法名,当该属性有值时,表示使用AJAX分页
    38  }
    39  
    40  // 创建一个分页对象,输入参数分别为:
    41  // 总数量、每页数量、当前页码、当前的URL(URI+QUERY)、(可选)路由规则(例如: /user/list/:page、/order/list/*page、/order/list/{page}.html)
    42  func New(TotalSize, perPage int, CurrentPage interface{}, url string, router ...*ghttp.Router) *Page {
    43  	u, _ := url2.Parse(url)
    44  	page := &Page{
    45  		PageName:     "page",
    46  		PrevPageTag:  "<",
    47  		NextPageTag:  ">",
    48  		FirstPageTag: "|<",
    49  		LastPageTag:  ">|",
    50  		PrevBar:      "<<",
    51  		NextBar:      ">>",
    52  		TotalSize:    TotalSize,
    53  		TotalPage:    int(math.Ceil(float64(TotalSize) / float64(perPage))),
    54  		CurrentPage:  1,
    55  		PageBarNum:   10,
    56  		Url:          u,
    57  	}
    58  	curPage := gconv.Int(CurrentPage)
    59  	if curPage > 0 {
    60  		page.CurrentPage = curPage
    61  	}
    62  	if len(router) > 0 {
    63  		page.Router = router[0]
    64  	}
    65  	return page
    66  }
    67  
    68  // 启用AJAX分页
    69  func (page *Page) EnableAjax(actionName string) {
    70  	page.AjaxActionName = actionName
    71  }
    72  
    73  // 设置URL生成规则模板,模板中可使用{.page}变量指定页码位置
    74  func (page *Page) SetUrlTemplate(template string) {
    75  	page.UrlTemplate = template
    76  }
    77  
    78  // 获取显示"下一页"的内容.
    79  func (page *Page) NextPage(styles ...string) string {
    80  	var curStyle, style string
    81  	if len(styles) > 0 {
    82  		curStyle = styles[0]
    83  	}
    84  	if len(styles) > 1 {
    85  		style = styles[0]
    86  	}
    87  	if page.CurrentPage < page.TotalPage {
    88  		return page.GetLink(page.GetUrl(page.CurrentPage+1), page.NextPageTag, "下一页", style)
    89  	}
    90  	return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.NextPageTag)
    91  }
    92  
    93  /// 获取显示“上一页”的内容
    94  func (page *Page) PrevPage(styles ...string) string {
    95  	var curStyle, style string
    96  	if len(styles) > 0 {
    97  		curStyle = styles[0]
    98  	}
    99  	if len(styles) > 1 {
   100  		style = styles[0]
   101  	}
   102  	if page.CurrentPage > 1 {
   103  		return page.GetLink(page.GetUrl(page.CurrentPage-1), page.PrevPageTag, "上一页", style)
   104  	}
   105  	return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.PrevPageTag)
   106  }
   107  
   108  /**
   109  * 获取显示“首页”的代码
   110  *
   111  * @return string
   112   */
   113  func (page *Page) FirstPage(styles ...string) string {
   114  	var curStyle, style string
   115  	if len(styles) > 0 {
   116  		curStyle = styles[0]
   117  	}
   118  	if len(styles) > 1 {
   119  		style = styles[0]
   120  	}
   121  	if page.CurrentPage == 1 {
   122  		return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.FirstPageTag)
   123  	}
   124  	return page.GetLink(page.GetUrl(1), page.FirstPageTag, "第一页", style)
   125  }
   126  
   127  // 获取显示“尾页”的内容
   128  func (page *Page) LastPage(styles ...string) string {
   129  	var curStyle, style string
   130  	if len(styles) > 0 {
   131  		curStyle = styles[0]
   132  	}
   133  	if len(styles) > 1 {
   134  		style = styles[0]
   135  	}
   136  	if page.CurrentPage == page.TotalPage {
   137  		return fmt.Sprintf(`<span class="%s">%s</span>`, curStyle, page.LastPageTag)
   138  	}
   139  	return page.GetLink(page.GetUrl(page.TotalPage), page.LastPageTag, "最后页", style)
   140  }
   141  
   142  // 获得分页条列表内容
   143  func (page *Page) PageBar(styles ...string) string {
   144  	var curStyle, style string
   145  	if len(styles) > 0 {
   146  		curStyle = styles[0]
   147  	}
   148  	if len(styles) > 1 {
   149  		style = styles[0]
   150  	}
   151  	plus := int(math.Ceil(float64(page.PageBarNum / 2)))
   152  	if page.PageBarNum-plus+page.CurrentPage > page.TotalPage {
   153  		plus = page.PageBarNum - page.TotalPage + page.CurrentPage
   154  	}
   155  	begin := page.CurrentPage - plus + 1
   156  	if begin < 1 {
   157  		begin = 1
   158  	}
   159  	ret := ""
   160  	for i := begin; i < begin+page.PageBarNum; i++ {
   161  		if i <= page.TotalPage {
   162  			if i != page.CurrentPage {
   163  				ret += page.GetLink(page.GetUrl(i), gconv.String(i), style, "")
   164  			} else {
   165  				ret += fmt.Sprintf(`<span class="%s">%d</span>`, curStyle, i)
   166  			}
   167  		} else {
   168  			break
   169  		}
   170  	}
   171  	return ret
   172  }
   173  
   174  // 获取基于select标签的显示跳转按钮的代码
   175  func (page *Page) SelectBar() string {
   176  	ret := `<select name="gpage_select" onchange="window.location.href=this.value">`
   177  	for i := 1; i <= page.TotalPage; i++ {
   178  		if i == page.CurrentPage {
   179  			ret += fmt.Sprintf(`<option value="%s" selected>%d</option>`, page.GetUrl(i), i)
   180  		} else {
   181  			ret += fmt.Sprintf(`<option value="%s">%d</option>`, page.GetUrl(i), i)
   182  		}
   183  	}
   184  	ret += "</select>"
   185  	return ret
   186  }
   187  
   188  // 预定义的分页显示风格内容
   189  func (page *Page) GetContent(mode int) string {
   190  	switch mode {
   191  	case 1:
   192  		page.NextPageTag = "下一页"
   193  		page.PrevPageTag = "上一页"
   194  		return fmt.Sprintf(
   195  			`%s <span class="current">%d</span> %s`,
   196  			page.PrevPage(),
   197  			page.CurrentPage,
   198  			page.NextPage(),
   199  		)
   200  
   201  	case 2:
   202  		page.NextPageTag = "下一页>>"
   203  		page.PrevPageTag = "<<上一页"
   204  		page.FirstPageTag = "首页"
   205  		page.LastPageTag = "尾页"
   206  		return fmt.Sprintf(
   207  			`%s%s<span class="current">[第%d页]</span>%s%s第%s页`,
   208  			page.FirstPage(),
   209  			page.PrevPage(),
   210  			page.CurrentPage,
   211  			page.NextPage(),
   212  			page.LastPage(),
   213  			page.SelectBar(),
   214  		)
   215  
   216  	case 3:
   217  		page.NextPageTag = "下一页"
   218  		page.PrevPageTag = "上一页"
   219  		page.FirstPageTag = "首页"
   220  		page.LastPageTag = "尾页"
   221  		pageStr := page.FirstPage()
   222  		pageStr += page.PrevPage()
   223  		pageStr += page.PageBar("current")
   224  		pageStr += page.NextPage()
   225  		pageStr += page.LastPage()
   226  		pageStr += fmt.Sprintf(
   227  			`<span>当前页%d/%d</span> <span>共%d条</span>`,
   228  			page.CurrentPage,
   229  			page.TotalPage,
   230  			page.TotalSize,
   231  		)
   232  		return pageStr
   233  
   234  	case 4:
   235  		page.NextPageTag = "下一页"
   236  		page.PrevPageTag = "上一页"
   237  		page.FirstPageTag = "首页"
   238  		page.LastPageTag = "尾页"
   239  		pageStr := page.FirstPage()
   240  		pageStr += page.PrevPage()
   241  		pageStr += page.PageBar("current")
   242  		pageStr += page.NextPage()
   243  		pageStr += page.LastPage()
   244  		return pageStr
   245  	}
   246  	return ""
   247  }
   248  
   249  // 为指定的页面返回地址值
   250  func (page *Page) GetUrl(pageNo int) string {
   251  	// 复制一个URL对象
   252  	url := *page.Url
   253  	if len(page.UrlTemplate) == 0 && page.Router != nil {
   254  		page.UrlTemplate = page.makeUrlTemplate(url.Path, page.Router)
   255  	}
   256  	if len(page.UrlTemplate) > 0 {
   257  		// 指定URL生成模板
   258  		url.Path = gstr.Replace(page.UrlTemplate, "{.page}", gconv.String(pageNo))
   259  		return url.String()
   260  	}
   261  
   262  	values := page.Url.Query()
   263  	values.Set(page.PageName, gconv.String(pageNo))
   264  	url.RawQuery = values.Encode()
   265  	return url.String()
   266  }
   267  
   268  // 根据当前URL以及注册路由信息计算出对应的URL模板
   269  func (page *Page) makeUrlTemplate(url string, router *ghttp.Router) (tpl string) {
   270  	if page.Router != nil && len(router.RegNames) > 0 {
   271  		if match, err := gregex.MatchString(router.RegRule, url); err == nil && len(match) > 0 {
   272  			if len(match) > len(router.RegNames) {
   273  				tpl = router.Uri
   274  				hasPageName := false
   275  				for i, name := range router.RegNames {
   276  					rule := fmt.Sprintf(`[:\*]%s|\{%s\}`, name, name)
   277  					if !hasPageName && strings.Compare(name, page.PageName) == 0 {
   278  						hasPageName = true
   279  						tpl, _ = gregex.ReplaceString(rule, `{.page}`, tpl)
   280  					} else {
   281  						tpl, _ = gregex.ReplaceString(rule, match[i+1], tpl)
   282  					}
   283  				}
   284  				if !hasPageName {
   285  					tpl = ""
   286  				}
   287  			}
   288  		}
   289  	}
   290  	return
   291  }
   292  
   293  // 获取链接地址
   294  func (page *Page) GetLink(url, text, title, style string) string {
   295  	if len(style) > 0 {
   296  		style = fmt.Sprintf(`class="%s" `, style)
   297  	}
   298  	if len(page.AjaxActionName) > 0 {
   299  		return fmt.Sprintf(`<a %shref='#' onclick="%s('%s')">%s</a>`, style, page.AjaxActionName, url, text)
   300  	} else {
   301  		return fmt.Sprintf(`<a %shref="%s" title="%s">%s</a>`, style, url, title, text)
   302  	}
   303  }