mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-17 07:00:12 +01:00
🎨 Database gallery view https://github.com/siyuan-note/siyuan/issues/10414
This commit is contained in:
parent
5521034036
commit
2e40a0a480
6 changed files with 153 additions and 118 deletions
|
|
@ -63,13 +63,34 @@ type BaseInstanceField struct {
|
||||||
Date *Date `json:"date,omitempty"` // 日期设置
|
Date *Date `json:"date,omitempty"` // 日期设置
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CollectionLayout 描述了集合布局的接口。
|
||||||
|
type CollectionLayout interface {
|
||||||
|
|
||||||
|
// GetItemIDs 返回集合中所有项目的 ID。
|
||||||
|
GetItemIDs() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collection 描述了一个集合的接口。
|
||||||
|
// 集合可以是表格、画廊等,包含多个项目。
|
||||||
|
type Collection interface {
|
||||||
|
|
||||||
|
// GetItems 返回集合中的所有项目。
|
||||||
|
GetItems() (ret []Item)
|
||||||
|
|
||||||
|
// SetItems 设置集合中的项目。
|
||||||
|
SetItems(items []Item)
|
||||||
|
}
|
||||||
|
|
||||||
// Item 描述了一个项目的接口。
|
// Item 描述了一个项目的接口。
|
||||||
// 项目可以表格行、画廊卡片或其他视图类型的实体。
|
// 项目可以是表格行、画廊卡片等。
|
||||||
type Item interface {
|
type Item interface {
|
||||||
|
|
||||||
// GetBlockValue 返回主键的值。
|
// GetBlockValue 返回主键的值。
|
||||||
GetBlockValue() *Value
|
GetBlockValue() *Value
|
||||||
|
|
||||||
|
// GetValues 返回项目的所有字段值。
|
||||||
|
GetValues() []*Value
|
||||||
|
|
||||||
// GetID 返回项目的 ID。
|
// GetID 返回项目的 ID。
|
||||||
GetID() string
|
GetID() string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ type LayoutGallery struct {
|
||||||
CardIDs []string `json:"cardIds"` // 卡片 ID,用于自定义排序
|
CardIDs []string `json:"cardIds"` // 卡片 ID,用于自定义排序
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (layoutGallery *LayoutGallery) GetItemIDs() (ret []string) {
|
||||||
|
return layoutGallery.CardIDs
|
||||||
|
}
|
||||||
|
|
||||||
func NewLayoutGallery() *LayoutGallery {
|
func NewLayoutGallery() *LayoutGallery {
|
||||||
return &LayoutGallery{
|
return &LayoutGallery{
|
||||||
BaseLayout: &BaseLayout{
|
BaseLayout: &BaseLayout{
|
||||||
|
|
@ -126,6 +130,29 @@ func (card *GalleryCard) GetBlockValue() (ret *Value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (card *GalleryCard) GetValues() (ret []*Value) {
|
||||||
|
ret = []*Value{}
|
||||||
|
for _, v := range card.Values {
|
||||||
|
ret = append(ret, v.Value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gallery *Gallery) GetItems() (ret []Item) {
|
||||||
|
ret = []Item{}
|
||||||
|
for _, card := range gallery.Cards {
|
||||||
|
ret = append(ret, card)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gallery *Gallery) SetItems(items []Item) {
|
||||||
|
gallery.Cards = []*GalleryCard{}
|
||||||
|
for _, item := range items {
|
||||||
|
gallery.Cards = append(gallery.Cards, item.(*GalleryCard))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (gallery *Gallery) GetType() LayoutType {
|
func (gallery *Gallery) GetType() LayoutType {
|
||||||
return LayoutTypeGallery
|
return LayoutTypeGallery
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@ type LayoutTable struct {
|
||||||
RowIDs []string `json:"rowIds"` // 行 ID,用于自定义排序
|
RowIDs []string `json:"rowIds"` // 行 ID,用于自定义排序
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (layoutTable *LayoutTable) GetItemIDs() (ret []string) {
|
||||||
|
return layoutTable.RowIDs
|
||||||
|
}
|
||||||
|
|
||||||
func NewLayoutTable() *LayoutTable {
|
func NewLayoutTable() *LayoutTable {
|
||||||
return &LayoutTable{
|
return &LayoutTable{
|
||||||
BaseLayout: &BaseLayout{
|
BaseLayout: &BaseLayout{
|
||||||
|
|
@ -87,20 +91,6 @@ type TableCell struct {
|
||||||
BgColor string `json:"bgColor"` // 单元格背景颜色
|
BgColor string `json:"bgColor"` // 单元格背景颜色
|
||||||
}
|
}
|
||||||
|
|
||||||
func (row *TableRow) GetID() string {
|
|
||||||
return row.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (row *TableRow) GetBlockValue() (ret *Value) {
|
|
||||||
for _, cell := range row.Cells {
|
|
||||||
if KeyTypeBlock == cell.ValueType {
|
|
||||||
ret = cell.Value
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (row *TableRow) GetValue(keyID string) (ret *Value) {
|
func (row *TableRow) GetValue(keyID string) (ret *Value) {
|
||||||
for _, cell := range row.Cells {
|
for _, cell := range row.Cells {
|
||||||
if nil != cell.Value && keyID == cell.Value.KeyID {
|
if nil != cell.Value && keyID == cell.Value.KeyID {
|
||||||
|
|
@ -120,6 +110,47 @@ func (table *Table) GetColumn(id string) *TableColumn {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (row *TableRow) GetID() string {
|
||||||
|
return row.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (row *TableRow) GetBlockValue() (ret *Value) {
|
||||||
|
for _, cell := range row.Cells {
|
||||||
|
if KeyTypeBlock == cell.ValueType {
|
||||||
|
ret = cell.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (row *TableRow) GetValues() (ret []*Value) {
|
||||||
|
ret = []*Value{}
|
||||||
|
for _, cell := range row.Cells {
|
||||||
|
if nil != cell.Value {
|
||||||
|
ret = append(ret, cell.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (table *Table) GetItems() (ret []Item) {
|
||||||
|
ret = []Item{}
|
||||||
|
for _, row := range table.Rows {
|
||||||
|
if nil != row {
|
||||||
|
ret = append(ret, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (table *Table) SetItems(items []Item) {
|
||||||
|
table.Rows = []*TableRow{}
|
||||||
|
for _, item := range items {
|
||||||
|
table.Rows = append(table.Rows, item.(*TableRow))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (*Table) GetType() LayoutType {
|
func (*Table) GetType() LayoutType {
|
||||||
return LayoutTypeTable
|
return LayoutTypeTable
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package sql
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -559,3 +560,57 @@ func removeMissingField(attrView *av.AttributeView, view *av.View, missingKeyID
|
||||||
av.SaveAttributeView(attrView)
|
av.SaveAttributeView(attrView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filterByQuery 根据搜索条件过滤
|
||||||
|
func filterByQuery(query string, collection av.Collection) {
|
||||||
|
query = strings.TrimSpace(query)
|
||||||
|
if "" != query {
|
||||||
|
query = strings.Join(strings.Fields(query), " ") // 将连续空格转换为一个空格
|
||||||
|
keywords := strings.Split(query, " ") // 按空格分割关键字
|
||||||
|
|
||||||
|
// 使用 AND 逻辑 https://github.com/siyuan-note/siyuan/issues/11535
|
||||||
|
var hitItems []av.Item
|
||||||
|
for _, item := range collection.GetItems() {
|
||||||
|
hit := false
|
||||||
|
for _, cell := range item.GetValues() {
|
||||||
|
allKeywordsHit := true
|
||||||
|
for _, keyword := range keywords {
|
||||||
|
if !strings.Contains(strings.ToLower(cell.String(true)), strings.ToLower(keyword)) {
|
||||||
|
allKeywordsHit = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if allKeywordsHit {
|
||||||
|
hit = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hit {
|
||||||
|
hitItems = append(hitItems, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collection.SetItems(hitItems)
|
||||||
|
if 1 > len(collection.GetItems()) {
|
||||||
|
collection.SetItems([]av.Item{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// manualSort 处理用户手动排序。
|
||||||
|
func manualSort(collectionLayout av.CollectionLayout, collection av.Collection) {
|
||||||
|
sortRowIDs := map[string]int{}
|
||||||
|
for i, itemID := range collectionLayout.GetItemIDs() {
|
||||||
|
sortRowIDs[itemID] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
items := collection.GetItems()
|
||||||
|
sort.Slice(items, func(i, j int) bool {
|
||||||
|
iv := sortRowIDs[items[i].GetID()]
|
||||||
|
jv := sortRowIDs[items[j].GetID()]
|
||||||
|
if iv == jv {
|
||||||
|
return items[i].GetID() < items[j].GetID()
|
||||||
|
}
|
||||||
|
return iv < jv
|
||||||
|
})
|
||||||
|
collection.SetItems(items)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package sql
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/88250/lute"
|
"github.com/88250/lute"
|
||||||
|
|
@ -134,56 +133,8 @@ func RenderAttributeViewGallery(attrView *av.AttributeView, view *av.View, query
|
||||||
util.PushErrMsg(fmt.Sprintf(util.Langs[util.Lang][44], util.EscapeHTML(renderTemplateErr.Error())), 30000)
|
util.PushErrMsg(fmt.Sprintf(util.Langs[util.Lang][44], util.EscapeHTML(renderTemplateErr.Error())), 30000)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据搜索条件过滤
|
filterByQuery(query, ret)
|
||||||
query = strings.TrimSpace(query)
|
manualSort(view.Gallery, ret)
|
||||||
if "" != query {
|
|
||||||
// 将连续空格转换为一个空格
|
|
||||||
query = strings.Join(strings.Fields(query), " ")
|
|
||||||
// 按空格分割关键字
|
|
||||||
keywords := strings.Split(query, " ")
|
|
||||||
// 使用 AND 逻辑 https://github.com/siyuan-note/siyuan/issues/11535
|
|
||||||
var hitCards []*av.GalleryCard
|
|
||||||
for _, card := range ret.Cards {
|
|
||||||
hit := false
|
|
||||||
for _, value := range card.Values {
|
|
||||||
allKeywordsHit := true
|
|
||||||
for _, keyword := range keywords {
|
|
||||||
if !strings.Contains(strings.ToLower(value.Value.String(true)), strings.ToLower(keyword)) {
|
|
||||||
allKeywordsHit = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if allKeywordsHit {
|
|
||||||
hit = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hit {
|
|
||||||
hitCards = append(hitCards, card)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret.Cards = hitCards
|
|
||||||
if 1 > len(ret.Cards) {
|
|
||||||
ret.Cards = []*av.GalleryCard{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 自定义排序
|
|
||||||
sortCardIDs := map[string]int{}
|
|
||||||
if 0 < len(view.Gallery.CardIDs) {
|
|
||||||
for i, cardID := range view.Gallery.CardIDs {
|
|
||||||
sortCardIDs[cardID] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(ret.Cards, func(i, j int) bool {
|
|
||||||
iv := sortCardIDs[ret.Cards[i].ID]
|
|
||||||
jv := sortCardIDs[ret.Cards[j].ID]
|
|
||||||
if iv == jv {
|
|
||||||
return ret.Cards[i].ID < ret.Cards[j].ID
|
|
||||||
}
|
|
||||||
return iv < jv
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,6 @@ package sql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/88250/lute/ast"
|
"github.com/88250/lute/ast"
|
||||||
"github.com/siyuan-note/siyuan/kernel/av"
|
"github.com/siyuan-note/siyuan/kernel/av"
|
||||||
|
|
@ -140,55 +138,7 @@ func RenderAttributeViewTable(attrView *av.AttributeView, view *av.View, query s
|
||||||
util.PushErrMsg(fmt.Sprintf(util.Langs[util.Lang][44], util.EscapeHTML(renderTemplateErr.Error())), 30000)
|
util.PushErrMsg(fmt.Sprintf(util.Langs[util.Lang][44], util.EscapeHTML(renderTemplateErr.Error())), 30000)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据搜索条件过滤
|
filterByQuery(query, ret)
|
||||||
query = strings.TrimSpace(query)
|
manualSort(view.Table, ret)
|
||||||
if "" != query {
|
|
||||||
// 将连续空格转换为一个空格
|
|
||||||
query = strings.Join(strings.Fields(query), " ")
|
|
||||||
// 按空格分割关键字
|
|
||||||
keywords := strings.Split(query, " ")
|
|
||||||
// 使用 AND 逻辑 https://github.com/siyuan-note/siyuan/issues/11535
|
|
||||||
var hitRows []*av.TableRow
|
|
||||||
for _, row := range ret.Rows {
|
|
||||||
hit := false
|
|
||||||
for _, cell := range row.Cells {
|
|
||||||
allKeywordsHit := true
|
|
||||||
for _, keyword := range keywords {
|
|
||||||
if !strings.Contains(strings.ToLower(cell.Value.String(true)), strings.ToLower(keyword)) {
|
|
||||||
allKeywordsHit = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if allKeywordsHit {
|
|
||||||
hit = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hit {
|
|
||||||
hitRows = append(hitRows, row)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret.Rows = hitRows
|
|
||||||
if 1 > len(ret.Rows) {
|
|
||||||
ret.Rows = []*av.TableRow{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 自定义排序
|
|
||||||
sortRowIDs := map[string]int{}
|
|
||||||
if 0 < len(view.Table.RowIDs) {
|
|
||||||
for i, rowID := range view.Table.RowIDs {
|
|
||||||
sortRowIDs[rowID] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(ret.Rows, func(i, j int) bool {
|
|
||||||
iv := sortRowIDs[ret.Rows[i].ID]
|
|
||||||
jv := sortRowIDs[ret.Rows[j].ID]
|
|
||||||
if iv == jv {
|
|
||||||
return ret.Rows[i].ID < ret.Rows[j].ID
|
|
||||||
}
|
|
||||||
return iv < jv
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue