mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-17 15:10:12 +01:00
295 lines
8 KiB
Go
295 lines
8 KiB
Go
// SiYuan - Refactor your thinking
|
|
// Copyright (c) 2020-present, b3log.org
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package av
|
|
|
|
import "sort"
|
|
|
|
// BaseLayout 描述了布局的基础结构。
|
|
type BaseLayout struct {
|
|
Spec int `json:"spec"` // 布局格式版本
|
|
ID string `json:"id"` // 布局 ID
|
|
Filters []*ViewFilter `json:"filters"` // 过滤规则
|
|
Sorts []*ViewSort `json:"sorts"` // 排序规则
|
|
PageSize int `json:"pageSize"` // 每页行数
|
|
}
|
|
|
|
// BaseValue 描述了字段值的基础结构。
|
|
type BaseValue struct {
|
|
ID string `json:"id"` // 字段值 ID
|
|
Value *Value `json:"value"` // 字段值
|
|
ValueType KeyType `json:"valueType"` // 字段值类型
|
|
}
|
|
|
|
// BaseInstance 描述了实例的基础结构。
|
|
type BaseInstance struct {
|
|
ID string `json:"id"` // ID
|
|
Icon string `json:"icon"` // 图标
|
|
Name string `json:"name"` // 名称
|
|
Desc string `json:"desc"` // 描述
|
|
HideAttrViewName bool `json:"hideAttrViewName"` // 是否隐藏属性视图名称
|
|
Filters []*ViewFilter `json:"filters"` // 过滤规则
|
|
Sorts []*ViewSort `json:"sorts"` // 排序规则
|
|
PageSize int `json:"pageSize"` // 每页项目
|
|
}
|
|
|
|
func (baseInstance *BaseInstance) GetSorts() []*ViewSort {
|
|
return baseInstance.Sorts
|
|
}
|
|
|
|
func (baseInstance *BaseInstance) GetFilters() []*ViewFilter {
|
|
return baseInstance.Filters
|
|
}
|
|
|
|
// BaseInstanceField 描述了实例字段的基础结构。
|
|
type BaseInstanceField struct {
|
|
ID string `json:"id"` // ID
|
|
Name string `json:"name"` // 字段名
|
|
Type KeyType `json:"type"` // 字段类型
|
|
Icon string `json:"icon"` // 字段图标
|
|
Hidden bool `json:"hidden"` // 是否隐藏
|
|
Desc string `json:"desc"` // 字段描述
|
|
|
|
// 以下是某些字段类型的特有属性
|
|
|
|
Options []*SelectOption `json:"options,omitempty"` // 选项列表
|
|
NumberFormat NumberFormat `json:"numberFormat"` // 数字字段格式化
|
|
Template string `json:"template"` // 模板字段内容
|
|
Relation *Relation `json:"relation,omitempty"` // 关联字段
|
|
Rollup *Rollup `json:"rollup,omitempty"` // 汇总字段
|
|
Date *Date `json:"date,omitempty"` // 日期设置
|
|
}
|
|
|
|
func (baseInstanceField *BaseInstanceField) GetID() string {
|
|
return baseInstanceField.ID
|
|
}
|
|
|
|
// CollectionLayout 描述了集合布局的接口。
|
|
type CollectionLayout interface {
|
|
|
|
// GetItemIDs 返回集合中所有项目的 ID。
|
|
GetItemIDs() []string
|
|
}
|
|
|
|
// Collection 描述了一个集合的接口。
|
|
// 集合可以是表格、画廊等,包含多个项目。
|
|
type Collection interface {
|
|
|
|
// GetItems 返回集合中的所有项目。
|
|
GetItems() (ret []Item)
|
|
|
|
// SetItems 设置集合中的项目。
|
|
SetItems(items []Item)
|
|
|
|
// GetFields 返回集合的所有字段。
|
|
GetFields() []Field
|
|
|
|
// GetSorts 返回集合的排序规则。
|
|
GetSorts() []*ViewSort
|
|
|
|
// GetFilters 返回集合的过滤规则。
|
|
GetFilters() []*ViewFilter
|
|
}
|
|
|
|
// Field 描述了一个字段的接口。
|
|
type Field interface {
|
|
|
|
// GetID 返回字段的 ID。
|
|
GetID() string
|
|
}
|
|
|
|
// Item 描述了一个项目的接口。
|
|
// 项目可以是表格行、画廊卡片等。
|
|
type Item interface {
|
|
|
|
// GetBlockValue 返回主键的值。
|
|
GetBlockValue() *Value
|
|
|
|
// GetValues 返回项目的所有字段值。
|
|
GetValues() []*Value
|
|
|
|
// GetValue 返回指定键 ID 的字段值。
|
|
GetValue(keyID string) (ret *Value)
|
|
|
|
// GetID 返回项目的 ID。
|
|
GetID() string
|
|
}
|
|
|
|
func sort0(collection Collection, attrView *AttributeView) {
|
|
sorts := collection.GetSorts()
|
|
if 1 > len(sorts) {
|
|
return
|
|
}
|
|
|
|
type FieldIndexSort struct {
|
|
Index int
|
|
Order SortOrder
|
|
}
|
|
|
|
var fieldIndexSorts []*FieldIndexSort
|
|
for _, s := range sorts {
|
|
for i, c := range collection.GetFields() {
|
|
if c.GetID() == s.Column {
|
|
fieldIndexSorts = append(fieldIndexSorts, &FieldIndexSort{Index: i, Order: s.Order})
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
items := collection.GetItems()
|
|
editedValItems := map[string]bool{}
|
|
for i, item := range items {
|
|
for _, fieldIndexSort := range fieldIndexSorts {
|
|
val := items[i].GetValues()[fieldIndexSort.Index]
|
|
if KeyTypeCheckbox == val.Type {
|
|
if block := item.GetBlockValue(); nil != block && block.IsEdited() {
|
|
// 如果主键编辑过,则勾选框也算作编辑过,参与排序 https://github.com/siyuan-note/siyuan/issues/11016
|
|
editedValItems[item.GetID()] = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if val.IsEdited() {
|
|
// 如果该卡片某字段的值已经编辑过,则该卡片可参与排序
|
|
editedValItems[item.GetID()] = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// 将未编辑的卡片和已编辑的卡片分开排序
|
|
var uneditedItems, editedItems []Item
|
|
for _, item := range items {
|
|
if _, ok := editedValItems[item.GetID()]; ok {
|
|
editedItems = append(editedItems, item)
|
|
} else {
|
|
uneditedItems = append(uneditedItems, item)
|
|
}
|
|
}
|
|
|
|
sort.Slice(uneditedItems, func(i, j int) bool {
|
|
val1 := uneditedItems[i].GetBlockValue()
|
|
if nil == val1 {
|
|
return true
|
|
}
|
|
val2 := uneditedItems[j].GetBlockValue()
|
|
if nil == val2 {
|
|
return false
|
|
}
|
|
return val1.CreatedAt < val2.CreatedAt
|
|
})
|
|
|
|
sort.Slice(editedItems, func(i, j int) bool {
|
|
sorted := true
|
|
for _, fieldIndexSort := range fieldIndexSorts {
|
|
val1 := editedItems[i].GetValues()[fieldIndexSort.Index]
|
|
val2 := editedItems[j].GetValues()[fieldIndexSort.Index]
|
|
if nil == val1 || val1.IsEmpty() {
|
|
if nil != val2 && !val2.IsEmpty() {
|
|
return false
|
|
}
|
|
sorted = false
|
|
continue
|
|
} else {
|
|
if nil == val2 || val2.IsEmpty() {
|
|
return true
|
|
}
|
|
}
|
|
|
|
result := val1.Compare(val2, attrView)
|
|
if 0 == result {
|
|
sorted = false
|
|
continue
|
|
}
|
|
sorted = true
|
|
|
|
if fieldIndexSort.Order == SortOrderAsc {
|
|
return 0 > result
|
|
}
|
|
return 0 < result
|
|
}
|
|
|
|
if !sorted {
|
|
key1 := editedItems[i].GetBlockValue()
|
|
if nil == key1 {
|
|
return false
|
|
}
|
|
key2 := editedItems[j].GetBlockValue()
|
|
if nil == key2 {
|
|
return false
|
|
}
|
|
return key1.CreatedAt < key2.CreatedAt
|
|
}
|
|
return false
|
|
})
|
|
|
|
// 将包含未编辑的卡片放在最后
|
|
collection.SetItems(append(editedItems, uneditedItems...))
|
|
if 1 > len(collection.GetItems()) {
|
|
collection.SetItems([]Item{})
|
|
}
|
|
}
|
|
|
|
func filter0(collection Collection, attrView *AttributeView) {
|
|
filters := collection.GetFilters()
|
|
if 1 > len(filters) {
|
|
return
|
|
}
|
|
|
|
var colIndexes []int
|
|
for _, f := range filters {
|
|
for i, c := range collection.GetFields() {
|
|
if c.GetID() == f.Column {
|
|
colIndexes = append(colIndexes, i)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
var items []Item
|
|
attrViewCache := map[string]*AttributeView{}
|
|
attrViewCache[attrView.ID] = attrView
|
|
for _, item := range collection.GetItems() {
|
|
pass := true
|
|
values := item.GetValues()
|
|
for j, index := range colIndexes {
|
|
operator := filters[j].Operator
|
|
|
|
if nil == values[index] {
|
|
if FilterOperatorIsNotEmpty == operator {
|
|
pass = false
|
|
} else if FilterOperatorIsEmpty == operator {
|
|
pass = true
|
|
break
|
|
}
|
|
|
|
if KeyTypeText != values[index].Type {
|
|
pass = false
|
|
}
|
|
break
|
|
}
|
|
|
|
if !values[index].Filter(filters[j], attrView, item.GetID(), &attrViewCache) {
|
|
pass = false
|
|
break
|
|
}
|
|
}
|
|
if pass {
|
|
items = append(items, item)
|
|
}
|
|
}
|
|
collection.SetItems(items)
|
|
}
|