mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-30 21:38:48 +01:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
b2febe82b1
9 changed files with 250 additions and 349 deletions
8
.github/CONTRIBUTING.md
vendored
8
.github/CONTRIBUTING.md
vendored
|
|
@ -7,17 +7,17 @@
|
|||
|
||||
## NPM dependencies
|
||||
|
||||
Install pnpm: `npm install -g pnpm@10.11.0`
|
||||
Install pnpm: `npm install -g pnpm@10.12.1`
|
||||
|
||||
<details>
|
||||
<summary>For China mainland</summary>
|
||||
|
||||
Set the Electron mirror environment variable and install Electron:
|
||||
|
||||
* macOS/Linux: `ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ pnpm install electron@v35.5.0 -D`
|
||||
* macOS/Linux: `ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ pnpm install electron@v36.4.0 -D`
|
||||
* Windows:
|
||||
* `SET ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/`
|
||||
* `pnpm install electron@v35.5.0 -D`
|
||||
* `pnpm install electron@v36.4.0 -D`
|
||||
|
||||
NPM mirror:
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ NPM mirror:
|
|||
|
||||
Enter the app folder and execute:
|
||||
|
||||
* `pnpm install electron@v35.5.0 -D`
|
||||
* `pnpm install electron@v36.4.0 -D`
|
||||
* `pnpm run dev`
|
||||
* `pnpm run start`
|
||||
|
||||
|
|
|
|||
8
.github/CONTRIBUTING_zh_CN.md
vendored
8
.github/CONTRIBUTING_zh_CN.md
vendored
|
|
@ -7,17 +7,17 @@
|
|||
|
||||
## NPM 依赖
|
||||
|
||||
安装 pnpm:`npm install -g pnpm@10.11.0`
|
||||
安装 pnpm:`npm install -g pnpm@10.12.1`
|
||||
|
||||
<details>
|
||||
<summary>适用于中国大陆</summary>
|
||||
|
||||
设置 Electron 镜像环境变量并安装 Electron:
|
||||
|
||||
* macOS/Linux:`ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ pnpm install electron@v35.5.0 -D`
|
||||
* macOS/Linux:`ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ pnpm install electron@v36.4.0 -D`
|
||||
* Windows:
|
||||
* `SET ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/`
|
||||
* `pnpm install electron@v35.5.0 -D`
|
||||
* `pnpm install electron@v36.4.0 -D`
|
||||
|
||||
NPM 镜像:
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ NPM 镜像:
|
|||
|
||||
进入 app 文件夹执行:
|
||||
|
||||
* `pnpm install electron@v35.5.0 -D`
|
||||
* `pnpm install electron@v36.4.0 -D`
|
||||
* `pnpm run dev`
|
||||
* `pnpm run start`
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Refactor your thinking",
|
||||
"homepage": "https://b3log.org/siyuan",
|
||||
"main": "./electron/main.js",
|
||||
"packageManager": "pnpm@10.11.0",
|
||||
"packageManager": "pnpm@10.12.1",
|
||||
"scripts": {
|
||||
"lint": "eslint . --fix --cache",
|
||||
"dev": "webpack --mode development",
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
"clean-webpack-plugin": "^4.0.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"dayjs": "^1.11.5",
|
||||
"electron": "35.5.0",
|
||||
"electron": "v36.4.0",
|
||||
"electron-builder": "26.0.12",
|
||||
"encoding": "^0.1.13",
|
||||
"esbuild-loader": "^3.0.1",
|
||||
|
|
|
|||
24
app/pnpm-lock.yaml
generated
24
app/pnpm-lock.yaml
generated
|
|
@ -10,7 +10,7 @@ importers:
|
|||
dependencies:
|
||||
'@electron/remote':
|
||||
specifier: ^2.1.2
|
||||
version: 2.1.2(electron@35.5.0)
|
||||
version: 2.1.2(electron@36.4.0)
|
||||
devDependencies:
|
||||
'@eslint/eslintrc':
|
||||
specifier: ^3.3.1
|
||||
|
|
@ -40,8 +40,8 @@ importers:
|
|||
specifier: ^1.11.5
|
||||
version: 1.11.13
|
||||
electron:
|
||||
specifier: 35.5.0
|
||||
version: 35.5.0
|
||||
specifier: v36.4.0
|
||||
version: 36.4.0
|
||||
electron-builder:
|
||||
specifier: 26.0.12
|
||||
version: 26.0.12(electron-builder-squirrel-windows@26.0.11)
|
||||
|
|
@ -131,8 +131,8 @@ packages:
|
|||
resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@electron/node-gyp@https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2':
|
||||
resolution: {tarball: https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2}
|
||||
'@electron/node-gyp@git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2':
|
||||
resolution: {commit: 06b29aafb7708acef8b3669835c8a7857ebc92d2, repo: https://github.com/electron/node-gyp.git, type: git}
|
||||
version: 10.2.0-electron.1
|
||||
engines: {node: '>=12.13.0'}
|
||||
hasBin: true
|
||||
|
|
@ -1191,8 +1191,8 @@ packages:
|
|||
resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
electron@35.5.0:
|
||||
resolution: {integrity: sha512-16ScwDuKgnuL7tSrEgBvQe1Hm4CSK0vbOusPFrDs4oIs3QOdEFtrP9i8+4yKQGXpszj4f4F0MQjKv1tu9E4Gvg==}
|
||||
electron@36.4.0:
|
||||
resolution: {integrity: sha512-LLOOZEuW5oqvnjC7HBQhIqjIIJAZCIFjQxltQGLfEC7XFsBoZgQ3u3iFj+Kzw68Xj97u1n57Jdt7P98qLvUibQ==}
|
||||
engines: {node: '>= 12.20.55'}
|
||||
hasBin: true
|
||||
|
||||
|
|
@ -2803,7 +2803,7 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@electron/node-gyp@https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2':
|
||||
'@electron/node-gyp@git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2':
|
||||
dependencies:
|
||||
env-paths: 2.2.1
|
||||
exponential-backoff: 3.1.2
|
||||
|
|
@ -2840,7 +2840,7 @@ snapshots:
|
|||
|
||||
'@electron/rebuild@3.7.0':
|
||||
dependencies:
|
||||
'@electron/node-gyp': https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2
|
||||
'@electron/node-gyp': git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2
|
||||
'@malept/cross-spawn-promise': 2.0.0
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.0
|
||||
|
|
@ -2858,9 +2858,9 @@ snapshots:
|
|||
- bluebird
|
||||
- supports-color
|
||||
|
||||
'@electron/remote@2.1.2(electron@35.5.0)':
|
||||
'@electron/remote@2.1.2(electron@36.4.0)':
|
||||
dependencies:
|
||||
electron: 35.5.0
|
||||
electron: 36.4.0
|
||||
|
||||
'@electron/universal@2.0.1':
|
||||
dependencies:
|
||||
|
|
@ -4025,7 +4025,7 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
electron@35.5.0:
|
||||
electron@36.4.0:
|
||||
dependencies:
|
||||
'@electron/get': 2.0.3
|
||||
'@types/node': 22.15.3
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package av
|
||||
|
||||
import "sort"
|
||||
|
||||
// BaseLayout 描述了布局的基础结构。
|
||||
type BaseLayout struct {
|
||||
Spec int `json:"spec"` // 布局格式版本
|
||||
|
|
@ -44,6 +46,14 @@ type BaseInstance struct {
|
|||
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
|
||||
|
|
@ -63,6 +73,10 @@ type BaseInstanceField struct {
|
|||
Date *Date `json:"date,omitempty"` // 日期设置
|
||||
}
|
||||
|
||||
func (baseInstanceField *BaseInstanceField) GetID() string {
|
||||
return baseInstanceField.ID
|
||||
}
|
||||
|
||||
// CollectionLayout 描述了集合布局的接口。
|
||||
type CollectionLayout interface {
|
||||
|
||||
|
|
@ -79,6 +93,22 @@ type Collection interface {
|
|||
|
||||
// SetItems 设置集合中的项目。
|
||||
SetItems(items []Item)
|
||||
|
||||
// GetFields 返回集合的所有字段。
|
||||
GetFields() []Field
|
||||
|
||||
// GetSorts 返回集合的排序规则。
|
||||
GetSorts() []*ViewSort
|
||||
|
||||
// GetFilters 返回集合的过滤规则。
|
||||
GetFilters() []*ViewFilter
|
||||
}
|
||||
|
||||
// Field 描述了一个字段的接口。
|
||||
type Field interface {
|
||||
|
||||
// GetID 返回字段的 ID。
|
||||
GetID() string
|
||||
}
|
||||
|
||||
// Item 描述了一个项目的接口。
|
||||
|
|
@ -97,3 +127,169 @@ type Item interface {
|
|||
// 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
package av
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/88250/lute/ast"
|
||||
)
|
||||
|
||||
|
|
@ -163,6 +161,14 @@ func (gallery *Gallery) SetItems(items []Item) {
|
|||
}
|
||||
}
|
||||
|
||||
func (gallery *Gallery) GetFields() (ret []Field) {
|
||||
ret = []Field{}
|
||||
for _, field := range gallery.Fields {
|
||||
ret = append(ret, field)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (gallery *Gallery) GetType() LayoutType {
|
||||
return LayoutTypeGallery
|
||||
}
|
||||
|
|
@ -172,163 +178,9 @@ func (gallery *Gallery) GetID() string {
|
|||
}
|
||||
|
||||
func (gallery *Gallery) Sort(attrView *AttributeView) {
|
||||
if 1 > len(gallery.Sorts) {
|
||||
return
|
||||
}
|
||||
|
||||
type FieldIndexSort struct {
|
||||
Index int
|
||||
Order SortOrder
|
||||
}
|
||||
|
||||
var fieldIndexSorts []*FieldIndexSort
|
||||
for _, s := range gallery.Sorts {
|
||||
for i, c := range gallery.Fields {
|
||||
if c.ID == s.Column {
|
||||
fieldIndexSorts = append(fieldIndexSorts, &FieldIndexSort{Index: i, Order: s.Order})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editedValCards := map[string]bool{}
|
||||
for i, card := range gallery.Cards {
|
||||
for _, fieldIndexSort := range fieldIndexSorts {
|
||||
val := gallery.Cards[i].Values[fieldIndexSort.Index].Value
|
||||
if KeyTypeCheckbox == val.Type {
|
||||
if block := card.GetBlockValue(); nil != block && block.IsEdited() {
|
||||
// 如果主键编辑过,则勾选框也算作编辑过,参与排序 https://github.com/siyuan-note/siyuan/issues/11016
|
||||
editedValCards[card.ID] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if val.IsEdited() {
|
||||
// 如果该卡片某字段的值已经编辑过,则该卡片可参与排序
|
||||
editedValCards[card.ID] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将未编辑的卡片和已编辑的卡片分开排序
|
||||
var uneditedCards, editedCards []*GalleryCard
|
||||
for _, card := range gallery.Cards {
|
||||
if _, ok := editedValCards[card.ID]; ok {
|
||||
editedCards = append(editedCards, card)
|
||||
} else {
|
||||
uneditedCards = append(uneditedCards, card)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(uneditedCards, func(i, j int) bool {
|
||||
val1 := uneditedCards[i].GetBlockValue()
|
||||
if nil == val1 {
|
||||
return true
|
||||
}
|
||||
val2 := uneditedCards[j].GetBlockValue()
|
||||
if nil == val2 {
|
||||
return false
|
||||
}
|
||||
return val1.CreatedAt < val2.CreatedAt
|
||||
})
|
||||
|
||||
sort.Slice(editedCards, func(i, j int) bool {
|
||||
sorted := true
|
||||
for _, fieldIndexSort := range fieldIndexSorts {
|
||||
val1 := editedCards[i].Values[fieldIndexSort.Index].Value
|
||||
val2 := editedCards[j].Values[fieldIndexSort.Index].Value
|
||||
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 := editedCards[i].GetBlockValue()
|
||||
if nil == key1 {
|
||||
return false
|
||||
}
|
||||
key2 := editedCards[j].GetBlockValue()
|
||||
if nil == key2 {
|
||||
return false
|
||||
}
|
||||
return key1.CreatedAt < key2.CreatedAt
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// 将包含未编辑的卡片放在最后
|
||||
gallery.Cards = append(editedCards, uneditedCards...)
|
||||
if 1 > len(gallery.Cards) {
|
||||
gallery.Cards = []*GalleryCard{}
|
||||
}
|
||||
sort0(gallery, attrView)
|
||||
}
|
||||
|
||||
func (gallery *Gallery) Filter(attrView *AttributeView) {
|
||||
if 1 > len(gallery.Filters) {
|
||||
return
|
||||
}
|
||||
|
||||
var fieldIndexes []int
|
||||
for _, f := range gallery.Filters {
|
||||
for i, c := range gallery.Fields {
|
||||
if c.ID == f.Column {
|
||||
fieldIndexes = append(fieldIndexes, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cards := []*GalleryCard{}
|
||||
attrViewCache := map[string]*AttributeView{}
|
||||
attrViewCache[attrView.ID] = attrView
|
||||
for _, card := range gallery.Cards {
|
||||
pass := true
|
||||
for j, index := range fieldIndexes {
|
||||
operator := gallery.Filters[j].Operator
|
||||
|
||||
if nil == card.Values[index].Value {
|
||||
if FilterOperatorIsNotEmpty == operator {
|
||||
pass = false
|
||||
} else if FilterOperatorIsEmpty == operator {
|
||||
pass = true
|
||||
break
|
||||
}
|
||||
|
||||
if KeyTypeText != card.Values[index].ValueType {
|
||||
pass = false
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if !card.Values[index].Value.Filter(gallery.Filters[j], attrView, card.ID, &attrViewCache) {
|
||||
pass = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if pass {
|
||||
cards = append(cards, card)
|
||||
}
|
||||
}
|
||||
gallery.Cards = cards
|
||||
filter0(gallery, attrView)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
package av
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/88250/lute/ast"
|
||||
)
|
||||
|
||||
|
|
@ -151,6 +149,14 @@ func (table *Table) SetItems(items []Item) {
|
|||
}
|
||||
}
|
||||
|
||||
func (table *Table) GetFields() (ret []Field) {
|
||||
ret = []Field{}
|
||||
for _, column := range table.Columns {
|
||||
ret = append(ret, column)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (*Table) GetType() LayoutType {
|
||||
return LayoutTypeTable
|
||||
}
|
||||
|
|
@ -160,163 +166,9 @@ func (table *Table) GetID() string {
|
|||
}
|
||||
|
||||
func (table *Table) Sort(attrView *AttributeView) {
|
||||
if 1 > len(table.Sorts) {
|
||||
return
|
||||
}
|
||||
|
||||
type ColIndexSort struct {
|
||||
Index int
|
||||
Order SortOrder
|
||||
}
|
||||
|
||||
var colIndexSorts []*ColIndexSort
|
||||
for _, s := range table.Sorts {
|
||||
for i, c := range table.Columns {
|
||||
if c.ID == s.Column {
|
||||
colIndexSorts = append(colIndexSorts, &ColIndexSort{Index: i, Order: s.Order})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editedValRows := map[string]bool{}
|
||||
for i, row := range table.Rows {
|
||||
for _, colIndexSort := range colIndexSorts {
|
||||
val := table.Rows[i].Cells[colIndexSort.Index].Value
|
||||
if KeyTypeCheckbox == val.Type {
|
||||
if block := row.GetBlockValue(); nil != block && block.IsEdited() {
|
||||
// 如果主键编辑过,则勾选框也算作编辑过,参与排序 https://github.com/siyuan-note/siyuan/issues/11016
|
||||
editedValRows[row.ID] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if val.IsEdited() {
|
||||
// 如果该行某列的值已经编辑过,则该行可参与排序
|
||||
editedValRows[row.ID] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将未编辑的行和已编辑的行分开排序
|
||||
var uneditedRows, editedRows []*TableRow
|
||||
for _, row := range table.Rows {
|
||||
if _, ok := editedValRows[row.ID]; ok {
|
||||
editedRows = append(editedRows, row)
|
||||
} else {
|
||||
uneditedRows = append(uneditedRows, row)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(uneditedRows, func(i, j int) bool {
|
||||
val1 := uneditedRows[i].GetBlockValue()
|
||||
if nil == val1 {
|
||||
return true
|
||||
}
|
||||
val2 := uneditedRows[j].GetBlockValue()
|
||||
if nil == val2 {
|
||||
return false
|
||||
}
|
||||
return val1.CreatedAt < val2.CreatedAt
|
||||
})
|
||||
|
||||
sort.Slice(editedRows, func(i, j int) bool {
|
||||
sorted := true
|
||||
for _, colIndexSort := range colIndexSorts {
|
||||
val1 := editedRows[i].Cells[colIndexSort.Index].Value
|
||||
val2 := editedRows[j].Cells[colIndexSort.Index].Value
|
||||
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 colIndexSort.Order == SortOrderAsc {
|
||||
return 0 > result
|
||||
}
|
||||
return 0 < result
|
||||
}
|
||||
|
||||
if !sorted {
|
||||
key1 := editedRows[i].GetBlockValue()
|
||||
if nil == key1 {
|
||||
return false
|
||||
}
|
||||
key2 := editedRows[j].GetBlockValue()
|
||||
if nil == key2 {
|
||||
return false
|
||||
}
|
||||
return key1.CreatedAt < key2.CreatedAt
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// 将包含未编辑的行放在最后
|
||||
table.Rows = append(editedRows, uneditedRows...)
|
||||
if 1 > len(table.Rows) {
|
||||
table.Rows = []*TableRow{}
|
||||
}
|
||||
sort0(table, attrView)
|
||||
}
|
||||
|
||||
func (table *Table) Filter(attrView *AttributeView) {
|
||||
if 1 > len(table.Filters) {
|
||||
return
|
||||
}
|
||||
|
||||
var colIndexes []int
|
||||
for _, f := range table.Filters {
|
||||
for i, c := range table.Columns {
|
||||
if c.ID == f.Column {
|
||||
colIndexes = append(colIndexes, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rows := []*TableRow{}
|
||||
attrViewCache := map[string]*AttributeView{}
|
||||
attrViewCache[attrView.ID] = attrView
|
||||
for _, row := range table.Rows {
|
||||
pass := true
|
||||
for j, index := range colIndexes {
|
||||
operator := table.Filters[j].Operator
|
||||
|
||||
if nil == row.Cells[index].Value {
|
||||
if FilterOperatorIsNotEmpty == operator {
|
||||
pass = false
|
||||
} else if FilterOperatorIsEmpty == operator {
|
||||
pass = true
|
||||
break
|
||||
}
|
||||
|
||||
if KeyTypeText != row.Cells[index].ValueType {
|
||||
pass = false
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if !row.Cells[index].Value.Filter(table.Filters[j], attrView, row.ID, &attrViewCache) {
|
||||
pass = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if pass {
|
||||
rows = append(rows, row)
|
||||
}
|
||||
}
|
||||
table.Rows = rows
|
||||
filter0(table, attrView)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1180,8 +1180,6 @@ func renderAttributeView(attrView *av.AttributeView, viewID, query string, page,
|
|||
}
|
||||
}
|
||||
view.Table.Sorts = tmpSorts
|
||||
|
||||
viewable = sql.RenderAttributeViewTable(attrView, view, query)
|
||||
case av.LayoutTypeGallery:
|
||||
// 字段删除以后需要删除设置的过滤和排序
|
||||
tmpFilters := []*av.ViewFilter{}
|
||||
|
|
@ -1199,10 +1197,9 @@ func renderAttributeView(attrView *av.AttributeView, viewID, query string, page,
|
|||
}
|
||||
}
|
||||
view.Gallery.Sorts = tmpSorts
|
||||
|
||||
viewable = sql.RenderAttributeViewGallery(attrView, view, query)
|
||||
}
|
||||
|
||||
viewable = sql.RenderView(view, attrView, query)
|
||||
if nil == viewable {
|
||||
err = av.ErrViewNotFound
|
||||
logging.LogErrorf("render attribute view [%s] failed", attrView.ID)
|
||||
|
|
@ -2424,13 +2421,7 @@ func addAttributeViewBlock(now int64, avID, blockID, previousBlockID, addingBloc
|
|||
}
|
||||
|
||||
if nil != view && 0 < len(filters) && !ignoreFillFilter {
|
||||
var viewable av.Viewable
|
||||
switch view.LayoutType {
|
||||
case av.LayoutTypeTable:
|
||||
viewable = sql.RenderAttributeViewTable(attrView, view, "")
|
||||
case av.LayoutTypeGallery:
|
||||
viewable = sql.RenderAttributeViewGallery(attrView, view, "")
|
||||
}
|
||||
viewable := sql.RenderView(view, attrView, "")
|
||||
viewable.Filter(attrView)
|
||||
viewable.Sort(attrView)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,16 @@ import (
|
|||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func RenderView(view *av.View, attrView *av.AttributeView, query string) (ret av.Viewable) {
|
||||
switch view.LayoutType {
|
||||
case av.LayoutTypeTable:
|
||||
ret = RenderAttributeViewTable(attrView, view, query)
|
||||
case av.LayoutTypeGallery:
|
||||
ret = RenderAttributeViewGallery(attrView, view, query)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func RenderTemplateField(ial map[string]string, keyValues []*av.KeyValues, tplContent string) (ret string, err error) {
|
||||
if "" == ial["id"] {
|
||||
block := getBlockValue(keyValues)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue