Commit 23def7bc authored by ibuler's avatar ibuler

[Update] 修改分页

parent 6d5e52db
package common
import "sync"
func NewPagination(data []interface{}, size int) *Pagination {
return &Pagination{
data: data,
pageSize: size,
currentPage: 1,
lock: new(sync.RWMutex),
}
}
type Pagination struct {
data []interface{}
currentPage int
pageSize int
totalPage int
lock *sync.RWMutex
}
func (p *Pagination) GetNextPageData() []interface{} {
if !p.HasNextPage() {
return []interface{}{}
}
p.lock.Lock()
p.currentPage++
p.lock.Unlock()
return p.GetPageData(p.currentPage)
}
func (p *Pagination) GetPrePageData() []interface{} {
if !p.HasPrePage() {
return []interface{}{}
}
p.lock.Lock()
p.currentPage--
p.lock.Unlock()
return p.GetPageData(p.currentPage)
}
func (p *Pagination) GetPageData(pageIndex int) []interface{} {
p.lock.RLock()
defer p.lock.RUnlock()
var (
endIndex int
startIndex int
)
endIndex = p.pageSize * pageIndex
startIndex = endIndex - p.pageSize
if endIndex > len(p.data) {
endIndex = len(p.data)
}
return p.data[startIndex:endIndex]
}
func (p *Pagination) CurrentPage() int {
p.lock.RLock()
defer p.lock.RUnlock()
return p.currentPage
}
func (p *Pagination) TotalCount() int {
p.lock.RLock()
defer p.lock.RUnlock()
return len(p.data)
}
func (p *Pagination) TotalPage() int {
p.lock.RLock()
defer p.lock.RUnlock()
return p.totalPage
}
func (p *Pagination) SetPageSize(size int) {
if size <= 0 {
panic("Pagination size should be larger than zero")
}
p.lock.Lock()
defer p.lock.Unlock()
if p.pageSize == size {
return
}
p.pageSize = size
if len(p.data)%size == 0 {
p.totalPage = len(p.data) / size
} else {
p.totalPage = len(p.data)/size + 1
}
p.currentPage = 1
}
func (p *Pagination) GetPageSize() int {
p.lock.RLock()
defer p.lock.RUnlock()
return p.pageSize
}
func (p *Pagination) HasNextPage() bool {
p.lock.RLock()
defer p.lock.RUnlock()
return p.currentPage < p.totalPage
}
func (p *Pagination) HasPrePage() bool {
p.lock.RLock()
defer p.lock.RUnlock()
return p.currentPage > 1
}
package common
import (
"fmt"
"strings"
"github.com/olekukonko/tablewriter"
)
const (
TruncSuffix = iota
TruncPrefix
TruncMiddle
)
type WrapperTable struct {
Fields []string
FieldsSize map[string][3]int // 列宽,列最小宽,列最大宽
Data []map[string]string
TotalSize int
TruncPolicy int
totalSize int
paddingSize int
bolderSize int
fieldsSize map[string]int // 计算后的最终宽度
cleanedData []map[string]string
Caption string
}
func (t *WrapperTable) Initial() {
// 如果设置的宽度小于title的size, 重写
t.paddingSize = 1
t.bolderSize = 1
for _, k := range t.Fields {
titleSize := len(k)
sizeDefine := t.FieldsSize[k]
if titleSize > sizeDefine[1] {
sizeDefine[1] = titleSize
t.FieldsSize[k] = sizeDefine
}
}
}
func (t *WrapperTable) CalculateColumnsSize() {
t.fieldsSize = make(map[string]int)
dataColMaxSize := make(map[string]int)
for _, row := range t.Data {
for _, colName := range t.Fields {
if colValue, ok := row[colName]; ok {
preSize, ok := dataColMaxSize[colName]
colSize := len(colValue)
if !ok || colSize > preSize {
dataColMaxSize[colName] = colSize
}
}
}
}
// 如果数据宽度大于设置最大值,则设置为准
// 如果数据最大值小彧最小值,已最小值为列宽
// 否则数据最大宽度为列宽
for k, v := range dataColMaxSize {
size, min, max := t.FieldsSize[k][0], t.FieldsSize[k][1], t.FieldsSize[k][2]
if size != 0 {
t.fieldsSize[k] = size
} else if max != 0 && v > max {
t.fieldsSize[k] = max
} else if min != 0 && v < min {
t.fieldsSize[k] = min
} else {
t.fieldsSize[k] = v
}
}
// 计算后列总长度
calSize := 0
for _, v := range t.fieldsSize {
calSize += v
}
if t.TotalSize == 0 {
t.totalSize = calSize
return
}
// 总宽度计算时应当减去 border和padding
t.totalSize = t.TotalSize - len(t.Fields)*2*t.paddingSize - (len(t.Fields)+1)*t.bolderSize
// 计算可以扩容和缩容的列
delta := t.totalSize - calSize
if delta == 0 {
return
}
var step = 1
if delta < 0 {
step = -1
}
delta = Abs(delta)
for delta > 0 {
canChangeCols := make([]string, 0)
for k, v := range t.FieldsSize {
size, min, max := v[0], v[1], v[2]
switch step {
// 扩容
case 1:
if size != 0 || (max != 0 && t.fieldsSize[k] >= max) {
continue
}
// 缩容
case -1:
if size != 0 || t.fieldsSize[k] <= min {
continue
}
}
canChangeCols = append(canChangeCols, k)
}
if len(canChangeCols) == 0 {
break
}
for _, k := range canChangeCols {
t.fieldsSize[k] += step
delta--
}
}
}
func (t *WrapperTable) convertDataToSlice() [][]string {
data := make([][]string, len(t.Data))
for i, j := range t.Data {
row := make([]string, len(t.Fields))
for m, n := range t.Fields {
row[m] = j[n]
}
data[i] = row
}
return data
}
func (t *WrapperTable) Display() string {
t.CalculateColumnsSize()
fmt.Println(t.fieldsSize)
tableString := &strings.Builder{}
table := tablewriter.NewWriter(tableString)
table.SetBorder(false)
table.SetHeader(t.Fields)
colors := make([]tablewriter.Colors, len(t.Fields))
for i := 0; i < len(t.Fields); i++ {
colors[i] = tablewriter.Colors{tablewriter.Bold, tablewriter.FgGreenColor}
}
table.SetHeaderColor(colors...)
data := t.convertDataToSlice()
table.AppendBulk(data)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
for i, j := range t.Fields {
n := t.fieldsSize[j]
table.SetColMinWidth(i, n)
}
table.SetColWidth(t.totalSize)
if t.Caption != "" {
table.SetCaption(true, t.Caption)
}
table.Render()
return tableString.String()
}
package common
import (
"fmt"
"testing"
)
func TestNewTable_CalculateColumnsSize(t *testing.T) {
table := WrapperTable{
Fields: []string{"ID", "主机名", "IP", "系统用户", "Comment"},
Data: []map[string]string{
{"ID": "1", "主机名": "asdfasdf", "IP": "192.168.1.1", "系统用户": "123", "Comment": "你好"},
{"ID": "2", "主机名": "bbb", "IP": "255.255.255.255", "系统用户": "o", "Comment": ""},
{"ID": "3", "主机名": "3", "IP": "1.1.1.1", "系统用户": "", "Comment": "aaaa"},
{"ID": "3", "主机名": "22323", "IP": "1.1.2.1", "系统用户": "", "Comment": ""},
{"ID": "2", "主机名": "22323", "IP": "192.168.1.1", "系统用户": "", "Comment": ""},
},
FieldsSize: map[string][3]int{
"ID": {0, 0, 5},
"主机名": {0, 8, 25},
"IP": {15, 0, 0},
"系统用户": {0, 12, 20},
"Comment": {0, 0, 0},
},
TotalSize: 140,
}
table.Initial()
data := table.Display()
fmt.Println(data)
fmt.Println(table.fieldsSize)
//if table.fieldsSize["comment"] != 6 {
// t.Error("comment需要为6")
//}
//
//table.TotalSize = 188
//table.CalculateColumnsSize()
//if table.fieldsSize["comment"] != 136 {
// t.Error("comment长度需要为136")
//}
//fmt.Println(table.fieldsSize)
}
......@@ -44,3 +44,18 @@ func GzipCompressFile(srcPath, dstPath string) error {
}
return nil
}
func Sum(i []int) int {
sum := 0
for _, v := range i {
sum += v
}
return sum
}
func Abs(x int) int {
if x < 0 {
return -x
}
return x
}
......@@ -6,9 +6,7 @@ import (
"io/ioutil"
"log"
"os"
"reflect"
"strings"
"sync"
)
type Config struct {
......@@ -41,8 +39,6 @@ type Config struct {
Comment string `yaml:"COMMENT"`
Language string `yaml:"LANG"`
LanguageCode string `yaml:"LANGUAGE_CODE"` // Abandon
mu sync.RWMutex
}
func (c *Config) EnsureConfigValid() {
......@@ -57,8 +53,6 @@ func (c *Config) EnsureConfigValid() {
}
func (c *Config) LoadFromYAML(body []byte) error {
c.mu.Lock()
defer c.mu.Unlock()
err := yaml.Unmarshal(body, c)
if err != nil {
log.Printf("Load yaml error: %v", err)
......@@ -75,8 +69,6 @@ func (c *Config) LoadFromYAMLPath(filepath string) error {
}
func (c *Config) LoadFromJSON(body []byte) error {
c.mu.Lock()
defer c.mu.Unlock()
err := json.Unmarshal(body, c)
if err != nil {
log.Printf("Config load yaml error")
......@@ -98,44 +90,6 @@ func (c *Config) LoadFromEnv() error {
return c.LoadFromYAML(envYAML)
}
func (c *Config) Get(key string) reflect.Value {
c.mu.Lock()
defer c.mu.Unlock()
//t := reflect.TypeOf(c)
v := reflect.ValueOf(c)
return v.FieldByName(key)
//for i := 0; i < v.NumField(); i++ {
// if v.Field(i).CanInterface() && t.Field(i).Name == key {
// return v.Field(i).Interface()
// }
//}
//return nil
}
func (c *Config) GetString(key string) string {
value := c.Get(key)
return value.String()
}
func (c *Config) GetBool(key string) bool {
value := c.Get(key)
return value.Bool()
}
func (c *Config) GetInt(key string) int {
value := c.Get(key)
return int(value.Int())
}
func (c *Config) GetMap(key string) map[string]string {
//value := c.Get(key)
//
//if val, ok := value.(map[string]string); ok {
// return val
//}
return map[string]string{}
}
func (c *Config) Load(filepath string) error {
err := c.LoadFromYAMLPath(filepath)
if err != nil {
......@@ -147,7 +101,7 @@ func (c *Config) Load(filepath string) error {
var name, _ = os.Hostname()
var rootPath, _ = os.Getwd()
var Conf = &Config{
var Conf = Config{
Name: name,
CoreHost: "http://localhost:8080",
BootstrapToken: "",
......
package handler
import (
"cocogo/pkg/common"
"cocogo/pkg/i18n"
"fmt"
"strconv"
"strings"
"sync"
"github.com/olekukonko/tablewriter"
"cocogo/pkg/config"
"cocogo/pkg/model"
"cocogo/pkg/utils"
)
const (
IDColumnMinSize = 4
HostNameColumnMaxSize = 15
IPColumnSize = 16
CommentColumnMinSize = 2
)
func NewAssetPagination(term *utils.Terminal, assets []model.Asset) *AssetPagination {
fields := []string{"ID", "Hostname", "IP", "LoginAs", "Comment"}
wtable := WrapperTable{
Fields: fields,
DataBulk: make([][]string, 0),
ColumnSize: make([]int, len(fields)),
}
var interfaceSlice = make([]interface{}, len(assets))
for i, d := range assets {
interfaceSlice[i] = d
}
page := &Pagination{
data: interfaceSlice,
lock: new(sync.RWMutex),
currentPage: 1,
}
assetPage := &AssetPagination{term: term,
tableWriter: &wtable,
page: page,
}
assetPage := &AssetPagination{term: term, assets: assets}
assetPage.Initial()
return assetPage
}
type AssetPagination struct {
term *utils.Terminal
tableWriter *WrapperTable
page *Pagination
assets []model.Asset
page *common.Pagination
currentData []model.Asset
}
func (p *AssetPagination) Initial() {
pageData := make([]interface{}, len(p.assets))
for i, v := range p.assets {
pageData[i] = v
}
pageSize := p.getPageSize()
p.page = common.NewPagination(pageData, pageSize)
firstPageData := p.page.GetPageData(1)
p.currentData = make([]model.Asset, len(firstPageData))
for i, item := range firstPageData {
p.currentData[i] = item.(model.Asset)
}
}
func (p *AssetPagination) getPageSize() int {
var (
pageSize int
)
_, height := p.term.GetSize()
switch config.Conf.AssetListPageSize {
case "auto":
pageSize = height - 7
pageSize = height - 8
case "all":
pageSize = len(p.page.data)
pageSize = len(p.assets)
default:
if value, err := strconv.Atoi(config.Conf.AssetListPageSize); err == nil {
pageSize = value
} else {
pageSize = height - 7
pageSize = height - 8
}
}
if pageSize <= 0 {
pageSize = 1
}
p.page.SetPageSize(pageSize)
tmpdata := p.page.GetPageData(1)
p.currentData = make([]model.Asset, len(tmpdata))
for i, item := range tmpdata {
p.currentData[i] = item.(model.Asset)
}
}
func (p *AssetPagination) setPageSize() {
if config.Conf.AssetListPageSize == "auto" {
var pageSize int
_, height := p.term.GetSize()
remainSize := height - 7
if remainSize > 0 {
pageSize = remainSize
} else {
pageSize = 1
}
if p.page.GetPageSize() != pageSize {
p.page.SetPageSize(pageSize)
}
}
}
func (p *AssetPagination) getColumnMaxSize() {
var (
IDSize int
HostNameSize int
systemUserSize int
CommentSize int
)
IDSize = IDColumnMinSize
CommentSize = CommentColumnMinSize
if len(strconv.Itoa(len(p.currentData))) > IDColumnMinSize {
IDSize = len(strconv.Itoa(len(p.currentData)))
}
p.tableWriter.DataBulk = p.tableWriter.DataBulk[:0]
for i, item := range p.currentData {
tmpDat := make([]string, 5)
var tmpSystemUserArray []string
result := selectHighestPrioritySystemUsers(item.SystemUsers)
tmpSystemUserArray = make([]string, len(result))
for index, sysUser := range result {
tmpSystemUserArray[index] = sysUser.Name
}
tmpSystemUserStr := fmt.Sprintf("[%s]", strings.Join(tmpSystemUserArray, ","))
if len(tmpSystemUserStr) > systemUserSize {
systemUserSize = len(tmpSystemUserStr)
}
if len(item.Hostname) >= HostNameColumnMaxSize {
HostNameSize = HostNameColumnMaxSize
tmpDat[1] = item.Hostname[:HostNameColumnMaxSize]
} else if len(item.Hostname) < HostNameColumnMaxSize && len(item.Hostname) > HostNameSize {
HostNameSize = len(item.Hostname)
tmpDat[1] = item.Hostname
} else {
tmpDat[1] = item.Hostname
}
if len(item.Comment) > CommentSize {
CommentSize = len(item.Comment)
}
tmpDat[0] = strconv.Itoa(i + 1)
tmpDat[2] = item.Ip
tmpDat[3] = tmpSystemUserStr
p.tableWriter.DataBulk = append(p.tableWriter.DataBulk, tmpDat)
}
// table writer 空白空间占用宽度 4 + (columnNum - 1) * 4
width, _ := p.term.GetSize()
remainSize := width - 16 - IDSize - HostNameSize - IPColumnSize - systemUserSize
if remainSize > 0 && CommentSize < remainSize {
CommentSize = remainSize
}
for i, item := range p.currentData {
if len(item.Comment) > CommentSize {
p.tableWriter.DataBulk[i][4] = item.Comment[:CommentSize]
} else {
p.tableWriter.DataBulk[i][4] = item.Comment
}
}
currentCapMsg := fmt.Sprintf("Page: %d, Count: %d, Total Page: %d, Total Count:%d\n",
p.page.CurrentPage(), p.page.GetPageSize(), p.page.TotalPage(), p.page.TotalCount())
msg := utils.WrapperString(currentCapMsg, utils.Green)
p.tableWriter.SetCaption(msg)
p.tableWriter.SetColumnSize(IDSize, HostNameSize, IPColumnSize, systemUserSize, CommentSize)
return pageSize
}
func (p *AssetPagination) PaginationState() []model.Asset {
func (p *AssetPagination) Start() []model.Asset {
for {
// 当前页是第一个,如果当前页数据小于page size,显示所有
if p.page.CurrentPage() == 1 && p.page.GetPageSize() > len(p.currentData) {
p.displayAssets()
p.displayPageAssets()
return []model.Asset{}
}
p.displayAssets()
p.displayPageAssets()
p.displayTipsInfo()
line, err := p.term.ReadLine()
p.setPageSize()
if err != nil {
return []model.Asset{}
}
pageSize := p.getPageSize()
p.page.SetPageSize(pageSize)
line = strings.TrimSpace(line)
switch len(line) {
case 0, 1:
switch strings.ToLower(line) {
case "p":
if !p.page.HasPrePage() {
continue
}
tmpData := p.page.GetPrePageData()
if len(p.currentData) != len(tmpData) {
p.currentData = make([]model.Asset, len(tmpData))
......@@ -193,6 +100,9 @@ func (p *AssetPagination) PaginationState() []model.Asset {
}
case "", "n":
if !p.page.HasNextPage() {
continue
}
tmpData := p.page.GetNextPageData()
if len(p.currentData) != len(tmpData) {
p.currentData = make([]model.Asset, len(tmpData))
......@@ -200,7 +110,7 @@ func (p *AssetPagination) PaginationState() []model.Asset {
for i, item := range tmpData {
p.currentData[i] = item.(model.Asset)
}
case "b":
case "b", "q":
return []model.Asset{}
default:
if indexID, err := strconv.Atoi(line); err == nil {
......@@ -216,15 +126,43 @@ func (p *AssetPagination) PaginationState() []model.Asset {
}
}
}
}
}
func (p *AssetPagination) displayAssets() {
p.getColumnMaxSize()
func (p *AssetPagination) displayPageAssets() {
fields := []string{"ID", "主机名", "IP", "系统用户", "Comment"}
data := make([]map[string]string, len(p.currentData))
for i, j := range p.currentData {
row := make(map[string]string)
row["ID"] = strconv.Itoa(i + 1)
row["主机名"] = j.Hostname
row["IP"] = j.Ip
row["系统用户"] = ""
row["Comment"] = "你好"
data[i] = row
}
w, _ := p.term.GetSize()
caption := fmt.Sprintf(i18n.T("Page: %d, Count: %d, Total Page: %d, Total Count: %d"),
p.page.CurrentPage(), p.page.GetPageSize(), p.page.TotalPage(), p.page.TotalCount(),
)
caption = utils.WrapperString(caption, utils.Green)
table := common.WrapperTable{
Fields: fields,
FieldsSize: map[string][3]int{
"ID": {0, 0, 5},
"主机名": {0, 8, 25},
"IP": {15, 0, 0},
"系统用户": {0, 12, 20},
"Comment": {0, 0, 0},
},
Data: data,
TotalSize: w,
Caption: caption,
}
table.Initial()
_, _ = p.term.Write([]byte(utils.CharClear))
_, _ = p.term.Write([]byte(p.tableWriter.Display()))
_, _ = p.term.Write([]byte(table.Display()))
}
func (p *AssetPagination) displayTipsInfo() {
......@@ -237,147 +175,3 @@ func (p *AssetPagination) displayTipsInfo() {
}
}
type WrapperTable struct {
Fields []string
DataBulk [][]string
ColumnSize []int
Caption string
}
func (w *WrapperTable) SetColumnSize(columnSizes ...int) {
if len(columnSizes) != len(w.Fields) {
panic("fields' number could not match column size")
}
for i, size := range columnSizes {
w.ColumnSize[i] = size
}
}
func (w *WrapperTable) SetCaption(cap string) {
w.Caption = cap
}
func (w *WrapperTable) Display() string {
tableString := &strings.Builder{}
table := tablewriter.NewWriter(tableString)
table.SetBorder(false)
table.SetHeader(w.Fields)
colors := make([]tablewriter.Colors, len(w.Fields))
for i := 0; i < len(w.Fields); i++ {
colors[i] = tablewriter.Colors{tablewriter.Bold, tablewriter.FgGreenColor}
}
table.SetHeaderColor(colors...)
table.AppendBulk(w.DataBulk)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
for i, value := range w.ColumnSize {
table.SetColMinWidth(i, value)
}
if w.Caption != "" {
table.SetCaption(true, w.Caption)
}
table.Render()
return tableString.String()
}
type Pagination struct {
data []interface{}
currentPage int
pageSize int
totalPage int
lock *sync.RWMutex
}
func (p *Pagination) GetNextPageData() []interface{} {
if p.HasNextPage() {
p.lock.Lock()
p.currentPage++
p.lock.Unlock()
}
return p.GetPageData(p.currentPage)
}
func (p *Pagination) GetPrePageData() []interface{} {
if p.HasPrePage() {
p.lock.Lock()
p.currentPage--
p.lock.Unlock()
}
return p.GetPageData(p.currentPage)
}
func (p *Pagination) GetPageData(pageIndex int) []interface{} {
p.lock.RLock()
defer p.lock.RUnlock()
var (
endIndex int
startIndex int
)
endIndex = p.pageSize * pageIndex
startIndex = endIndex - p.pageSize
if endIndex > len(p.data) {
endIndex = len(p.data)
}
return p.data[startIndex:endIndex]
}
func (p *Pagination) CurrentPage() int {
p.lock.RLock()
defer p.lock.RUnlock()
return p.currentPage
}
func (p *Pagination) TotalCount() int {
p.lock.RLock()
defer p.lock.RUnlock()
return len(p.data)
}
func (p *Pagination) TotalPage() int {
p.lock.RLock()
defer p.lock.RUnlock()
return p.totalPage
}
func (p *Pagination) SetPageSize(size int) {
if size <= 0 {
panic("Pagination size should be larger than zero")
}
p.lock.Lock()
defer p.lock.Unlock()
if p.pageSize == size {
return
}
p.pageSize = size
if len(p.data)%size == 0 {
p.totalPage = len(p.data) / size
} else {
p.totalPage = len(p.data)/size + 1
}
p.currentPage = 1
}
func (p *Pagination) GetPageSize() int {
p.lock.RLock()
defer p.lock.RUnlock()
return p.pageSize
}
func (p *Pagination) HasNextPage() bool {
p.lock.RLock()
defer p.lock.RUnlock()
return p.currentPage < p.totalPage
}
func (p *Pagination) HasPrePage() bool {
p.lock.RLock()
defer p.lock.RUnlock()
return p.currentPage > 1
}
......@@ -233,7 +233,7 @@ func (h *interactiveHandler) displayAssets(assets model.AssetList) {
h.term.SetPrompt(": ")
pag := NewAssetPagination(h.term, assets)
pag.Initial()
selectOneAssets := pag.PaginationState()
selectOneAssets := pag.Start()
if len(selectOneAssets) == 1 {
systemUser := h.chooseSystemUser(selectOneAssets[0].SystemUsers)
h.assetSelect = &selectOneAssets[0]
......
......@@ -80,6 +80,8 @@ func (s *SwitchSession) postBridge() {
s.cmdRecorder.End()
s.replayRecorder.End()
s.parser.Close()
_ = s.userTran.Close()
_ = s.srvTran.Close()
}
func (s *SwitchSession) Bridge() (err error) {
......
......@@ -76,10 +76,7 @@ func MustLoadServerConfigOnce() {
}
func LoadConfigFromServer() (err error) {
conf := config.Conf
conf.mu.Lock()
defer conf.mu.Unlock()
err = authClient.Get(TerminalConfigURL, conf)
err = authClient.Get(TerminalConfigURL, &config.Conf)
return err
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment