Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Vanessa 2024-03-14 11:01:21 +08:00
commit d1009f2333
10 changed files with 842 additions and 798 deletions

View file

@ -447,7 +447,9 @@ export const hintRenderTemplate = (value: string, protyle: IProtyle, nodeElement
export const hintRenderWidget = (value: string, protyle: IProtyle) => {
focusByRange(protyle.toolbar.range);
insertHTML(protyle.lute.SpinBlockDOM(`<iframe src="/widgets/${value}" data-subtype="widget" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>`), protyle, true);
// src 地址以 / 结尾
// Use the path ending with `/` when loading the widget https://github.com/siyuan-note/siyuan/issues/10520
insertHTML(protyle.lute.SpinBlockDOM(`<iframe src="/widgets/${value}/" data-subtype="widget" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>`), protyle, true);
hideElements(["util"], protyle);
};

View file

@ -122,8 +122,12 @@ func addAttributeViewValues(c *gin.Context) {
previousID = arg["previousID"].(string)
}
isDetached := arg["isDetached"].(bool)
ignoreFillFilter := true
if nil != arg["ignoreFillFilter"] {
ignoreFillFilter = arg["ignoreFillFilter"].(bool)
}
err := model.AddAttributeViewBlock(nil, srcIDs, avID, blockID, previousID, isDetached)
err := model.AddAttributeViewBlock(nil, srcIDs, avID, blockID, previousID, isDetached, ignoreFillFilter)
if nil != err {
ret.Code = -1
ret.Msg = err.Error()

View file

@ -17,6 +17,9 @@
package av
import (
"strings"
"time"
"github.com/siyuan-note/siyuan/kernel/util"
)
@ -75,6 +78,557 @@ const (
FilterOperatorIsFalse FilterOperator = "Is false"
)
func (value *Value) Filter(filter *ViewFilter, attrView *AttributeView, rowID string) bool {
if nil == filter || (nil == filter.Value && nil == filter.RelativeDate) {
return true
}
if nil != filter.Value && value.Type != filter.Value.Type {
// 由于字段类型被用户编辑过导致和过滤器值类型不匹配,该情况下不过滤
return true
}
if nil != value.Rollup && KeyTypeRollup == value.Type && nil != filter && nil != filter.Value && KeyTypeRollup == filter.Value.Type &&
nil != filter.Value.Rollup && 0 < len(filter.Value.Rollup.Contents) {
// 单独处理汇总类型的比较
// 处理为空和不为空
switch filter.Operator {
case FilterOperatorIsEmpty:
return 0 == len(value.Rollup.Contents)
case FilterOperatorIsNotEmpty:
return 0 != len(value.Rollup.Contents)
}
// 处理值比较
key, _ := attrView.GetKey(value.KeyID)
if nil == key {
return false
}
relKey, _ := attrView.GetKey(key.Rollup.RelationKeyID)
if nil == relKey {
return false
}
relVal := attrView.GetValue(relKey.ID, rowID)
if nil == relVal || nil == relVal.Relation {
return false
}
destAv, _ := ParseAttributeView(relKey.Relation.AvID)
if nil == destAv {
return false
}
for _, blockID := range relVal.Relation.BlockIDs {
destVal := destAv.GetValue(key.Rollup.KeyID, blockID)
if nil == destVal {
continue
}
if destVal.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
return true
}
}
return false
}
if nil != value.Relation && KeyTypeRelation == value.Type && 0 < len(value.Relation.Contents) && nil != filter && nil != filter.Value && KeyTypeRelation == filter.Value.Type &&
nil != filter.Value.Relation && 0 < len(filter.Value.Relation.BlockIDs) {
// 单独处理关联类型的比较
for _, relationValue := range value.Relation.Contents {
filterValue := &Value{Type: KeyTypeBlock, Block: &ValueBlock{Content: filter.Value.Relation.BlockIDs[0]}}
if relationValue.filter(filterValue, filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
return true
}
}
return false
}
return value.filter(filter.Value, filter.RelativeDate, filter.RelativeDate2, filter.Operator)
}
func (value *Value) filter(other *Value, relativeDate, relativeDate2 *RelativeDate, operator FilterOperator) bool {
switch value.Type {
case KeyTypeBlock:
if nil != value.Block && nil != other && nil != other.Block {
switch operator {
case FilterOperatorIsEqual:
return value.Block.Content == other.Block.Content
case FilterOperatorIsNotEqual:
return value.Block.Content != other.Block.Content
case FilterOperatorContains:
return strings.Contains(value.Block.Content, other.Block.Content)
case FilterOperatorDoesNotContain:
return !strings.Contains(value.Block.Content, other.Block.Content)
case FilterOperatorStartsWith:
return strings.HasPrefix(value.Block.Content, other.Block.Content)
case FilterOperatorEndsWith:
return strings.HasSuffix(value.Block.Content, other.Block.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.Block.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.Block.Content)
}
}
case KeyTypeText:
if nil != value.Text && nil != other && nil != other.Text {
switch operator {
case FilterOperatorIsEqual:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return value.Text.Content == other.Text.Content
case FilterOperatorIsNotEqual:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return value.Text.Content != other.Text.Content
case FilterOperatorContains:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return strings.Contains(value.Text.Content, other.Text.Content)
case FilterOperatorDoesNotContain:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return !strings.Contains(value.Text.Content, other.Text.Content)
case FilterOperatorStartsWith:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return strings.HasPrefix(value.Text.Content, other.Text.Content)
case FilterOperatorEndsWith:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return strings.HasSuffix(value.Text.Content, other.Text.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.Text.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.Text.Content)
}
}
case KeyTypeNumber:
if nil != value.Number && nil != other && nil != other.Number {
switch operator {
case FilterOperatorIsEqual:
if !other.Number.IsNotEmpty {
return true
}
return value.Number.Content == other.Number.Content
case FilterOperatorIsNotEqual:
if !other.Number.IsNotEmpty {
return true
}
return value.Number.Content != other.Number.Content
case FilterOperatorIsGreater:
return value.Number.Content > other.Number.Content
case FilterOperatorIsGreaterOrEqual:
return value.Number.Content >= other.Number.Content
case FilterOperatorIsLess:
return value.Number.Content < other.Number.Content
case FilterOperatorIsLessOrEqual:
return value.Number.Content <= other.Number.Content
case FilterOperatorIsEmpty:
return !value.Number.IsNotEmpty
case FilterOperatorIsNotEmpty:
return value.Number.IsNotEmpty
}
}
case KeyTypeDate:
if nil != value.Date {
if nil != relativeDate {
// 使用相对时间比较
count := relativeDate.Count
unit := relativeDate.Unit
direction := relativeDate.Direction
relativeTimeStart, relativeTimeEnd := calcRelativeTimeRegion(count, unit, direction)
_, relativeTimeEnd2 := calcRelativeTimeRegion(relativeDate2.Count, relativeDate2.Unit, relativeDate2.Direction)
return filterTime(value.Date.Content, value.Date.IsNotEmpty, relativeTimeStart, relativeTimeEnd, relativeTimeEnd2, operator)
} else { // 使用具体时间比较
if nil == other.Date {
return true
}
otherTime := time.UnixMilli(other.Date.Content)
otherStart := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 0, 0, 0, 0, otherTime.Location())
otherEnd := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 23, 59, 59, 999999999, otherTime.Location())
return filterTime(value.Date.Content, value.Date.IsNotEmpty, otherStart, otherEnd, time.Now(), operator)
}
}
case KeyTypeCreated:
if nil != value.Created {
if nil != relativeDate {
// 使用相对时间比较
count := relativeDate.Count
unit := relativeDate.Unit
direction := relativeDate.Direction
relativeTimeStart, relativeTimeEnd := calcRelativeTimeRegion(count, unit, direction)
return filterTime(value.Created.Content, true, relativeTimeStart, relativeTimeEnd, time.Now(), operator)
} else { // 使用具体时间比较
if nil == other.Created {
return true
}
otherTime := time.UnixMilli(other.Created.Content)
otherStart := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 0, 0, 0, 0, otherTime.Location())
otherEnd := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 23, 59, 59, 999999999, otherTime.Location())
return filterTime(value.Created.Content, value.Created.IsNotEmpty, otherStart, otherEnd, time.Now(), operator)
}
}
case KeyTypeUpdated:
if nil != value.Updated {
if nil != relativeDate {
// 使用相对时间比较
count := relativeDate.Count
unit := relativeDate.Unit
direction := relativeDate.Direction
relativeTimeStart, relativeTimeEnd := calcRelativeTimeRegion(count, unit, direction)
return filterTime(value.Updated.Content, true, relativeTimeStart, relativeTimeEnd, time.Now(), operator)
} else { // 使用具体时间比较
if nil == other.Updated {
return true
}
otherTime := time.UnixMilli(other.Updated.Content)
otherStart := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 0, 0, 0, 0, otherTime.Location())
otherEnd := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 23, 59, 59, 999999999, otherTime.Location())
return filterTime(value.Updated.Content, value.Updated.IsNotEmpty, otherStart, otherEnd, time.Now(), operator)
}
}
case KeyTypeSelect, KeyTypeMSelect:
if nil != value.MSelect {
if nil != other && nil != other.MSelect {
switch operator {
case FilterOperatorIsEqual, FilterOperatorContains:
contains := false
for _, v := range value.MSelect {
for _, v2 := range other.MSelect {
if v.Content == v2.Content {
contains = true
break
}
}
}
return contains
case FilterOperatorIsNotEqual, FilterOperatorDoesNotContain:
contains := false
for _, v := range value.MSelect {
for _, v2 := range other.MSelect {
if v.Content == v2.Content {
contains = true
break
}
}
}
return !contains
case FilterOperatorIsEmpty:
return 0 == len(value.MSelect) || 1 == len(value.MSelect) && "" == value.MSelect[0].Content
case FilterOperatorIsNotEmpty:
return 0 != len(value.MSelect) && !(1 == len(value.MSelect) && "" == value.MSelect[0].Content)
}
return false
}
// 没有设置比较值
switch operator {
case FilterOperatorIsEqual, FilterOperatorIsNotEqual, FilterOperatorContains, FilterOperatorDoesNotContain:
return true
case FilterOperatorIsEmpty:
return 0 == len(value.MSelect) || 1 == len(value.MSelect) && "" == value.MSelect[0].Content
case FilterOperatorIsNotEmpty:
return 0 != len(value.MSelect) && !(1 == len(value.MSelect) && "" == value.MSelect[0].Content)
}
}
case KeyTypeURL:
if nil != value.URL && nil != other && nil != other.URL {
switch operator {
case FilterOperatorIsEqual:
return value.URL.Content == other.URL.Content
case FilterOperatorIsNotEqual:
return value.URL.Content != other.URL.Content
case FilterOperatorContains:
return strings.Contains(value.URL.Content, other.URL.Content)
case FilterOperatorDoesNotContain:
return !strings.Contains(value.URL.Content, other.URL.Content)
case FilterOperatorStartsWith:
return strings.HasPrefix(value.URL.Content, other.URL.Content)
case FilterOperatorEndsWith:
return strings.HasSuffix(value.URL.Content, other.URL.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.URL.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.URL.Content)
}
}
case KeyTypeEmail:
if nil != value.Email && nil != other && nil != other.Email {
switch operator {
case FilterOperatorIsEqual:
return value.Email.Content == other.Email.Content
case FilterOperatorIsNotEqual:
return value.Email.Content != other.Email.Content
case FilterOperatorContains:
return strings.Contains(value.Email.Content, other.Email.Content)
case FilterOperatorDoesNotContain:
return !strings.Contains(value.Email.Content, other.Email.Content)
case FilterOperatorStartsWith:
return strings.HasPrefix(value.Email.Content, other.Email.Content)
case FilterOperatorEndsWith:
return strings.HasSuffix(value.Email.Content, other.Email.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.Email.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.Email.Content)
}
}
case KeyTypePhone:
if nil != value.Phone && nil != other && nil != other.Phone {
switch operator {
case FilterOperatorIsEqual:
return value.Phone.Content == other.Phone.Content
case FilterOperatorIsNotEqual:
return value.Phone.Content != other.Phone.Content
case FilterOperatorContains:
return strings.Contains(value.Phone.Content, other.Phone.Content)
case FilterOperatorDoesNotContain:
return !strings.Contains(value.Phone.Content, other.Phone.Content)
case FilterOperatorStartsWith:
return strings.HasPrefix(value.Phone.Content, other.Phone.Content)
case FilterOperatorEndsWith:
return strings.HasSuffix(value.Phone.Content, other.Phone.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.Phone.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.Phone.Content)
}
}
case KeyTypeMAsset:
if nil != value.MAsset && nil != other && nil != other.MAsset && 0 < len(value.MAsset) && 0 < len(other.MAsset) {
switch operator {
case FilterOperatorIsEqual, FilterOperatorContains:
contains := false
for _, v := range value.MAsset {
for _, v2 := range other.MAsset {
if v.Content == v2.Content {
contains = true
break
}
}
}
return contains
case FilterOperatorIsNotEqual, FilterOperatorDoesNotContain:
contains := false
for _, v := range value.MAsset {
for _, v2 := range other.MAsset {
if v.Content == v2.Content {
contains = true
break
}
}
}
return !contains
case FilterOperatorIsEmpty:
return 0 == len(value.MAsset) || 1 == len(value.MAsset) && "" == value.MAsset[0].Content
case FilterOperatorIsNotEmpty:
return 0 != len(value.MAsset) && !(1 == len(value.MAsset) && "" == value.MAsset[0].Content)
}
}
case KeyTypeTemplate:
if nil != value.Template && nil != other && nil != other.Template {
switch operator {
case FilterOperatorIsEqual:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content == other.Template.Content
case FilterOperatorIsNotEqual:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content != other.Template.Content
case FilterOperatorIsGreater:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content > other.Template.Content
case FilterOperatorIsGreaterOrEqual:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content >= other.Template.Content
case FilterOperatorIsLess:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content < other.Template.Content
case FilterOperatorIsLessOrEqual:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content <= other.Template.Content
case FilterOperatorContains:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return strings.Contains(value.Template.Content, other.Template.Content)
case FilterOperatorDoesNotContain:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return !strings.Contains(value.Template.Content, other.Template.Content)
case FilterOperatorStartsWith:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return strings.HasPrefix(value.Template.Content, other.Template.Content)
case FilterOperatorEndsWith:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return strings.HasSuffix(value.Template.Content, other.Template.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.Template.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.Template.Content)
}
}
case KeyTypeCheckbox:
if nil != value.Checkbox {
switch operator {
case FilterOperatorIsTrue:
return value.Checkbox.Checked
case FilterOperatorIsFalse:
return !value.Checkbox.Checked
}
}
}
switch operator {
case FilterOperatorIsEmpty:
return value.IsEmpty()
case FilterOperatorIsNotEmpty:
return !value.IsEmpty()
}
return false
}
func filterTime(valueMills int64, valueIsNotEmpty bool, otherValueStart, otherValueEnd, otherValueEnd2 time.Time, operator FilterOperator) bool {
valueTime := time.UnixMilli(valueMills)
switch operator {
case FilterOperatorIsEqual:
return (valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)) && valueTime.Before(otherValueEnd)
case FilterOperatorIsNotEqual:
return valueTime.Before(otherValueStart) || valueTime.After(otherValueEnd)
case FilterOperatorIsGreater:
return valueTime.After(otherValueEnd) || valueTime.Equal(otherValueEnd)
case FilterOperatorIsGreaterOrEqual:
return valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)
case FilterOperatorIsLess:
return valueTime.Before(otherValueStart)
case FilterOperatorIsLessOrEqual:
return valueTime.Before(otherValueEnd) || valueTime.Equal(otherValueEnd)
case FilterOperatorIsBetween:
return (valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)) && (valueTime.Before(otherValueEnd2) || valueTime.Equal(otherValueEnd2))
case FilterOperatorIsEmpty:
return !valueIsNotEmpty
case FilterOperatorIsNotEmpty:
return valueIsNotEmpty
}
return false
}
// 根据 Count、Unit 和 Direction 计算相对当前时间的开始时间和结束时间
func calcRelativeTimeRegion(count int, unit RelativeDateUnit, direction RelativeDateDirection) (start, end time.Time) {
now := time.Now()
switch unit {
case RelativeDateUnitDay:
switch direction {
case RelativeDateDirectionBefore:
// 结束时间使用今天的开始时间
end = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
// 开始时间使用结束时间减去 count 天
start = end.AddDate(0, 0, -count)
case RelativeDateDirectionThis:
// 开始时间使用今天的开始时间
start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
// 结束时间使用开始时间加上 count 天
end = start.AddDate(0, 0, count)
case RelativeDateDirectionAfter:
// 开始时间使用今天的结束时间
start = time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 999999999, now.Location())
// 结束时间使用开始时间加上 count 天
end = start.AddDate(0, 0, count)
}
case RelativeDateUnitWeek:
weekday := int(now.Weekday())
if 0 == weekday {
weekday = 7
}
switch direction {
case RelativeDateDirectionBefore:
// 结束时间使用本周的开始时间
end = time.Date(now.Year(), now.Month(), now.Day()-weekday, 0, 0, 0, 0, now.Location())
// 开始时间使用结束时间减去 count*7 天
start = end.AddDate(0, 0, -count*7)
case RelativeDateDirectionThis:
// 开始时间使用本周的开始时间
start = time.Date(now.Year(), now.Month(), now.Day()-weekday, 0, 0, 0, 0, now.Location())
// 结束时间使用开始时间加上 count*7 天
end = start.AddDate(0, 0, count*7)
case RelativeDateDirectionAfter:
// 开始时间使用本周的结束时间
start = time.Date(now.Year(), now.Month(), now.Day()-weekday+7, 23, 59, 59, 999999999, now.Location())
// 结束时间使用开始时间加上 count*7 天
end = start.AddDate(0, 0, count*7)
}
case RelativeDateUnitMonth:
switch direction {
case RelativeDateDirectionBefore:
// 结束时间使用本月的开始时间
end = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
// 开始时间使用结束时间减去 count 个月
start = end.AddDate(0, -count, 0)
case RelativeDateDirectionThis:
// 开始时间使用本月的开始时间
start = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
// 结束时间使用开始时间加上 count 个月
end = start.AddDate(0, count, 0)
case RelativeDateDirectionAfter:
// 开始时间使用本月的结束时间
start = time.Date(now.Year(), now.Month()+1, 1, 0, 0, 0, 0, now.Location()).Add(-time.Nanosecond)
// 结束时间使用开始时间加上 count 个月
end = start.AddDate(0, count, 0)
}
case RelativeDateUnitYear:
switch direction {
case RelativeDateDirectionBefore:
// 结束时间使用今年的开始时间
end = time.Date(now.Year(), 1, 1, 0, 0, 0, 0, now.Location())
// 开始时间使用结束时间减去 count 年
start = end.AddDate(-count, 0, 0)
case RelativeDateDirectionThis:
// 开始时间使用今年的开始时间
start = time.Date(now.Year(), 1, 1, 0, 0, 0, 0, now.Location())
// 结束时间使用开始时间加上 count 年
end = start.AddDate(count, 0, 0)
case RelativeDateDirectionAfter:
// 开始时间使用今年的结束时间
start = time.Date(now.Year()+1, 1, 1, 0, 0, 0, 0, now.Location()).Add(-time.Nanosecond)
// 结束时间使用开始时间加上 count 年
end = start.AddDate(count, 0, 0)
}
}
return
}
func (filter *ViewFilter) GetAffectValue(key *Key, defaultVal *Value) (ret *Value) {
if nil != filter.Value {
if KeyTypeRelation == filter.Value.Type || KeyTypeTemplate == filter.Value.Type || KeyTypeRollup == filter.Value.Type || KeyTypeUpdated == filter.Value.Type || KeyTypeCreated == filter.Value.Type {

View file

@ -16,6 +16,14 @@
package av
import (
"bytes"
"strconv"
"strings"
"github.com/siyuan-note/siyuan/kernel/util"
)
type Sortable interface {
SortRows()
}
@ -31,3 +39,238 @@ const (
SortOrderAsc SortOrder = "ASC"
SortOrderDesc SortOrder = "DESC"
)
func (value *Value) Compare(other *Value) int {
switch value.Type {
case KeyTypeBlock:
if nil != value.Block && nil != other.Block {
ret := strings.Compare(value.Block.Content, other.Block.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeText:
if nil != value.Text && nil != other.Text {
ret := strings.Compare(value.Text.Content, other.Text.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeNumber:
if nil != value.Number && nil != other.Number {
if value.Number.IsNotEmpty {
if !other.Number.IsNotEmpty {
return 1
}
if value.Number.Content > other.Number.Content {
return 1
} else if value.Number.Content < other.Number.Content {
return -1
} else {
return int(value.CreatedAt - other.CreatedAt)
}
} else {
if other.Number.IsNotEmpty {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
}
case KeyTypeDate:
if nil != value.Date && nil != other.Date {
if value.Date.IsNotEmpty {
if !other.Date.IsNotEmpty {
return 1
}
if value.Date.Content > other.Date.Content {
return 1
} else if value.Date.Content < other.Date.Content {
return -1
} else {
return int(value.CreatedAt - other.CreatedAt)
}
} else {
if other.Date.IsNotEmpty {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
}
case KeyTypeCreated:
if nil != value.Created && nil != other.Created {
if value.Created.Content > other.Created.Content {
return 1
} else if value.Created.Content < other.Created.Content {
return -1
} else {
return int(value.CreatedAt - other.CreatedAt)
}
}
case KeyTypeUpdated:
if nil != value.Updated && nil != other.Updated {
if value.Updated.Content > other.Updated.Content {
return 1
} else if value.Updated.Content < other.Updated.Content {
return -1
} else {
return int(value.CreatedAt - other.CreatedAt)
}
}
case KeyTypeSelect, KeyTypeMSelect:
if nil != value.MSelect && nil != other.MSelect {
var v1 string
for _, v := range value.MSelect {
v1 += v.Content
}
var v2 string
for _, v := range other.MSelect {
v2 += v.Content
}
ret := strings.Compare(v1, v2)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeURL:
if nil != value.URL && nil != other.URL {
ret := strings.Compare(value.URL.Content, other.URL.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeEmail:
if nil != value.Email && nil != other.Email {
ret := strings.Compare(value.Email.Content, other.Email.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypePhone:
if nil != value.Phone && nil != other.Phone {
ret := strings.Compare(value.Phone.Content, other.Phone.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeMAsset:
if nil != value.MAsset && nil != other.MAsset {
var v1 string
for _, v := range value.MAsset {
v1 += v.Content
}
var v2 string
for _, v := range other.MAsset {
v2 += v.Content
}
ret := strings.Compare(v1, v2)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeTemplate:
if nil != value.Template && nil != other.Template {
vContent := strings.TrimSpace(value.Template.Content)
oContent := strings.TrimSpace(other.Template.Content)
if util.IsNumeric(vContent) && util.IsNumeric(oContent) {
v1, _ := strconv.ParseFloat(vContent, 64)
v2, _ := strconv.ParseFloat(oContent, 64)
if v1 > v2 {
return 1
}
if v1 < v2 {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
ret := strings.Compare(value.Template.Content, other.Template.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeCheckbox:
if nil != value.Checkbox && nil != other.Checkbox {
if value.Checkbox.Checked && !other.Checkbox.Checked {
return 1
}
if !value.Checkbox.Checked && other.Checkbox.Checked {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
case KeyTypeRelation:
if nil != value.Relation && nil != other.Relation {
vContentBuf := bytes.Buffer{}
for _, c := range value.Relation.Contents {
vContentBuf.WriteString(c.String())
vContentBuf.WriteByte(' ')
}
vContent := strings.TrimSpace(vContentBuf.String())
oContentBuf := bytes.Buffer{}
for _, c := range other.Relation.Contents {
oContentBuf.WriteString(c.String())
oContentBuf.WriteByte(' ')
}
oContent := strings.TrimSpace(oContentBuf.String())
if util.IsNumeric(vContent) && util.IsNumeric(oContent) {
v1, _ := strconv.ParseFloat(vContent, 64)
v2, _ := strconv.ParseFloat(oContent, 64)
if v1 > v2 {
return 1
}
if v1 < v2 {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
ret := strings.Compare(vContent, oContent)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeRollup:
if nil != value.Rollup && nil != other.Rollup {
vContentBuf := bytes.Buffer{}
for _, c := range value.Rollup.Contents {
vContentBuf.WriteString(c.String())
vContentBuf.WriteByte(' ')
}
vContent := strings.TrimSpace(vContentBuf.String())
oContentBuf := bytes.Buffer{}
for _, c := range other.Rollup.Contents {
oContentBuf.WriteString(c.String())
oContentBuf.WriteByte(' ')
}
oContent := strings.TrimSpace(oContentBuf.String())
if util.IsNumeric(vContent) && util.IsNumeric(oContent) {
v1, _ := strconv.ParseFloat(vContent, 64)
v2, _ := strconv.ParseFloat(oContent, 64)
if v1 > v2 {
return 1
}
if v1 < v2 {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
ret := strings.Compare(vContent, oContent)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
}
return int(value.CreatedAt - other.CreatedAt)
}

View file

@ -17,13 +17,9 @@
package av
import (
"bytes"
"github.com/siyuan-note/siyuan/kernel/util"
"math"
"sort"
"strconv"
"strings"
"time"
)
// LayoutTable 描述了表格布局的结构。
@ -82,775 +78,6 @@ const (
CalcOperatorPercentUnchecked CalcOperator = "Percent unchecked"
)
func (value *Value) Compare(other *Value) int {
switch value.Type {
case KeyTypeBlock:
if nil != value.Block && nil != other.Block {
ret := strings.Compare(value.Block.Content, other.Block.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeText:
if nil != value.Text && nil != other.Text {
ret := strings.Compare(value.Text.Content, other.Text.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeNumber:
if nil != value.Number && nil != other.Number {
if value.Number.IsNotEmpty {
if !other.Number.IsNotEmpty {
return 1
}
if value.Number.Content > other.Number.Content {
return 1
} else if value.Number.Content < other.Number.Content {
return -1
} else {
return int(value.CreatedAt - other.CreatedAt)
}
} else {
if other.Number.IsNotEmpty {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
}
case KeyTypeDate:
if nil != value.Date && nil != other.Date {
if value.Date.IsNotEmpty {
if !other.Date.IsNotEmpty {
return 1
}
if value.Date.Content > other.Date.Content {
return 1
} else if value.Date.Content < other.Date.Content {
return -1
} else {
return int(value.CreatedAt - other.CreatedAt)
}
} else {
if other.Date.IsNotEmpty {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
}
case KeyTypeCreated:
if nil != value.Created && nil != other.Created {
if value.Created.Content > other.Created.Content {
return 1
} else if value.Created.Content < other.Created.Content {
return -1
} else {
return int(value.CreatedAt - other.CreatedAt)
}
}
case KeyTypeUpdated:
if nil != value.Updated && nil != other.Updated {
if value.Updated.Content > other.Updated.Content {
return 1
} else if value.Updated.Content < other.Updated.Content {
return -1
} else {
return int(value.CreatedAt - other.CreatedAt)
}
}
case KeyTypeSelect, KeyTypeMSelect:
if nil != value.MSelect && nil != other.MSelect {
var v1 string
for _, v := range value.MSelect {
v1 += v.Content
}
var v2 string
for _, v := range other.MSelect {
v2 += v.Content
}
ret := strings.Compare(v1, v2)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeURL:
if nil != value.URL && nil != other.URL {
ret := strings.Compare(value.URL.Content, other.URL.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeEmail:
if nil != value.Email && nil != other.Email {
ret := strings.Compare(value.Email.Content, other.Email.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypePhone:
if nil != value.Phone && nil != other.Phone {
ret := strings.Compare(value.Phone.Content, other.Phone.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeMAsset:
if nil != value.MAsset && nil != other.MAsset {
var v1 string
for _, v := range value.MAsset {
v1 += v.Content
}
var v2 string
for _, v := range other.MAsset {
v2 += v.Content
}
ret := strings.Compare(v1, v2)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeTemplate:
if nil != value.Template && nil != other.Template {
vContent := strings.TrimSpace(value.Template.Content)
oContent := strings.TrimSpace(other.Template.Content)
if util.IsNumeric(vContent) && util.IsNumeric(oContent) {
v1, _ := strconv.ParseFloat(vContent, 64)
v2, _ := strconv.ParseFloat(oContent, 64)
if v1 > v2 {
return 1
}
if v1 < v2 {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
ret := strings.Compare(value.Template.Content, other.Template.Content)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeCheckbox:
if nil != value.Checkbox && nil != other.Checkbox {
if value.Checkbox.Checked && !other.Checkbox.Checked {
return 1
}
if !value.Checkbox.Checked && other.Checkbox.Checked {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
case KeyTypeRelation:
if nil != value.Relation && nil != other.Relation {
vContentBuf := bytes.Buffer{}
for _, c := range value.Relation.Contents {
vContentBuf.WriteString(c.String())
vContentBuf.WriteByte(' ')
}
vContent := strings.TrimSpace(vContentBuf.String())
oContentBuf := bytes.Buffer{}
for _, c := range other.Relation.Contents {
oContentBuf.WriteString(c.String())
oContentBuf.WriteByte(' ')
}
oContent := strings.TrimSpace(oContentBuf.String())
if util.IsNumeric(vContent) && util.IsNumeric(oContent) {
v1, _ := strconv.ParseFloat(vContent, 64)
v2, _ := strconv.ParseFloat(oContent, 64)
if v1 > v2 {
return 1
}
if v1 < v2 {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
ret := strings.Compare(vContent, oContent)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
case KeyTypeRollup:
if nil != value.Rollup && nil != other.Rollup {
vContentBuf := bytes.Buffer{}
for _, c := range value.Rollup.Contents {
vContentBuf.WriteString(c.String())
vContentBuf.WriteByte(' ')
}
vContent := strings.TrimSpace(vContentBuf.String())
oContentBuf := bytes.Buffer{}
for _, c := range other.Rollup.Contents {
oContentBuf.WriteString(c.String())
oContentBuf.WriteByte(' ')
}
oContent := strings.TrimSpace(oContentBuf.String())
if util.IsNumeric(vContent) && util.IsNumeric(oContent) {
v1, _ := strconv.ParseFloat(vContent, 64)
v2, _ := strconv.ParseFloat(oContent, 64)
if v1 > v2 {
return 1
}
if v1 < v2 {
return -1
}
return int(value.CreatedAt - other.CreatedAt)
}
ret := strings.Compare(vContent, oContent)
if 0 == ret {
ret = int(value.CreatedAt - other.CreatedAt)
}
return ret
}
}
return int(value.CreatedAt - other.CreatedAt)
}
func (value *Value) Filter(filter *ViewFilter, attrView *AttributeView, rowID string) bool {
if nil == filter || (nil == filter.Value && nil == filter.RelativeDate) {
return true
}
if nil != filter.Value && value.Type != filter.Value.Type {
// 由于字段类型被用户编辑过导致和过滤器值类型不匹配,该情况下不过滤
return true
}
if nil != value.Rollup && KeyTypeRollup == value.Type && nil != filter && nil != filter.Value && KeyTypeRollup == filter.Value.Type &&
nil != filter.Value.Rollup && 0 < len(filter.Value.Rollup.Contents) {
// 单独处理汇总类型的比较
key, _ := attrView.GetKey(value.KeyID)
if nil == key {
return false
}
relKey, _ := attrView.GetKey(key.Rollup.RelationKeyID)
if nil == relKey {
return false
}
relVal := attrView.GetValue(relKey.ID, rowID)
if nil == relVal || nil == relVal.Relation {
return false
}
destAv, _ := ParseAttributeView(relKey.Relation.AvID)
if nil == destAv {
return false
}
for _, blockID := range relVal.Relation.BlockIDs {
destVal := destAv.GetValue(key.Rollup.KeyID, blockID)
if nil == destVal {
continue
}
if destVal.filter(filter.Value.Rollup.Contents[0], filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
return true
}
}
return false
}
if nil != value.Relation && KeyTypeRelation == value.Type && 0 < len(value.Relation.Contents) && nil != filter && nil != filter.Value && KeyTypeRelation == filter.Value.Type &&
nil != filter.Value.Relation && 0 < len(filter.Value.Relation.BlockIDs) {
// 单独处理关联类型的比较
for _, relationValue := range value.Relation.Contents {
filterValue := &Value{Type: KeyTypeBlock, Block: &ValueBlock{Content: filter.Value.Relation.BlockIDs[0]}}
if relationValue.filter(filterValue, filter.RelativeDate, filter.RelativeDate2, filter.Operator) {
return true
}
}
return false
}
return value.filter(filter.Value, filter.RelativeDate, filter.RelativeDate2, filter.Operator)
}
func (value *Value) filter(other *Value, relativeDate, relativeDate2 *RelativeDate, operator FilterOperator) bool {
switch value.Type {
case KeyTypeBlock:
if nil != value.Block && nil != other && nil != other.Block {
switch operator {
case FilterOperatorIsEqual:
return value.Block.Content == other.Block.Content
case FilterOperatorIsNotEqual:
return value.Block.Content != other.Block.Content
case FilterOperatorContains:
return strings.Contains(value.Block.Content, other.Block.Content)
case FilterOperatorDoesNotContain:
return !strings.Contains(value.Block.Content, other.Block.Content)
case FilterOperatorStartsWith:
return strings.HasPrefix(value.Block.Content, other.Block.Content)
case FilterOperatorEndsWith:
return strings.HasSuffix(value.Block.Content, other.Block.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.Block.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.Block.Content)
}
}
case KeyTypeText:
if nil != value.Text && nil != other && nil != other.Text {
switch operator {
case FilterOperatorIsEqual:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return value.Text.Content == other.Text.Content
case FilterOperatorIsNotEqual:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return value.Text.Content != other.Text.Content
case FilterOperatorContains:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return strings.Contains(value.Text.Content, other.Text.Content)
case FilterOperatorDoesNotContain:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return !strings.Contains(value.Text.Content, other.Text.Content)
case FilterOperatorStartsWith:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return strings.HasPrefix(value.Text.Content, other.Text.Content)
case FilterOperatorEndsWith:
if "" == strings.TrimSpace(other.Text.Content) {
return true
}
return strings.HasSuffix(value.Text.Content, other.Text.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.Text.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.Text.Content)
}
}
case KeyTypeNumber:
if nil != value.Number && nil != other && nil != other.Number {
switch operator {
case FilterOperatorIsEqual:
if !other.Number.IsNotEmpty {
return true
}
return value.Number.Content == other.Number.Content
case FilterOperatorIsNotEqual:
if !other.Number.IsNotEmpty {
return true
}
return value.Number.Content != other.Number.Content
case FilterOperatorIsGreater:
return value.Number.Content > other.Number.Content
case FilterOperatorIsGreaterOrEqual:
return value.Number.Content >= other.Number.Content
case FilterOperatorIsLess:
return value.Number.Content < other.Number.Content
case FilterOperatorIsLessOrEqual:
return value.Number.Content <= other.Number.Content
case FilterOperatorIsEmpty:
return !value.Number.IsNotEmpty
case FilterOperatorIsNotEmpty:
return value.Number.IsNotEmpty
}
}
case KeyTypeDate:
if nil != value.Date {
if nil != relativeDate {
// 使用相对时间比较
count := relativeDate.Count
unit := relativeDate.Unit
direction := relativeDate.Direction
relativeTimeStart, relativeTimeEnd := calcRelativeTimeRegion(count, unit, direction)
_, relativeTimeEnd2 := calcRelativeTimeRegion(relativeDate2.Count, relativeDate2.Unit, relativeDate2.Direction)
return filterTime(value.Date.Content, value.Date.IsNotEmpty, relativeTimeStart, relativeTimeEnd, relativeTimeEnd2, operator)
} else { // 使用具体时间比较
if nil == other.Date {
return true
}
otherTime := time.UnixMilli(other.Date.Content)
otherStart := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 0, 0, 0, 0, otherTime.Location())
otherEnd := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 23, 59, 59, 999999999, otherTime.Location())
return filterTime(value.Date.Content, value.Date.IsNotEmpty, otherStart, otherEnd, time.Now(), operator)
}
}
case KeyTypeCreated:
if nil != value.Created {
if nil != relativeDate {
// 使用相对时间比较
count := relativeDate.Count
unit := relativeDate.Unit
direction := relativeDate.Direction
relativeTimeStart, relativeTimeEnd := calcRelativeTimeRegion(count, unit, direction)
return filterTime(value.Created.Content, true, relativeTimeStart, relativeTimeEnd, time.Now(), operator)
} else { // 使用具体时间比较
if nil == other.Created {
return true
}
otherTime := time.UnixMilli(other.Created.Content)
otherStart := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 0, 0, 0, 0, otherTime.Location())
otherEnd := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 23, 59, 59, 999999999, otherTime.Location())
return filterTime(value.Created.Content, value.Created.IsNotEmpty, otherStart, otherEnd, time.Now(), operator)
}
}
case KeyTypeUpdated:
if nil != value.Updated {
if nil != relativeDate {
// 使用相对时间比较
count := relativeDate.Count
unit := relativeDate.Unit
direction := relativeDate.Direction
relativeTimeStart, relativeTimeEnd := calcRelativeTimeRegion(count, unit, direction)
return filterTime(value.Updated.Content, true, relativeTimeStart, relativeTimeEnd, time.Now(), operator)
} else { // 使用具体时间比较
if nil == other.Updated {
return true
}
otherTime := time.UnixMilli(other.Updated.Content)
otherStart := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 0, 0, 0, 0, otherTime.Location())
otherEnd := time.Date(otherTime.Year(), otherTime.Month(), otherTime.Day(), 23, 59, 59, 999999999, otherTime.Location())
return filterTime(value.Updated.Content, value.Updated.IsNotEmpty, otherStart, otherEnd, time.Now(), operator)
}
}
case KeyTypeSelect, KeyTypeMSelect:
if nil != value.MSelect {
if nil != other && nil != other.MSelect {
switch operator {
case FilterOperatorIsEqual, FilterOperatorContains:
contains := false
for _, v := range value.MSelect {
for _, v2 := range other.MSelect {
if v.Content == v2.Content {
contains = true
break
}
}
}
return contains
case FilterOperatorIsNotEqual, FilterOperatorDoesNotContain:
contains := false
for _, v := range value.MSelect {
for _, v2 := range other.MSelect {
if v.Content == v2.Content {
contains = true
break
}
}
}
return !contains
case FilterOperatorIsEmpty:
return 0 == len(value.MSelect) || 1 == len(value.MSelect) && "" == value.MSelect[0].Content
case FilterOperatorIsNotEmpty:
return 0 != len(value.MSelect) && !(1 == len(value.MSelect) && "" == value.MSelect[0].Content)
}
return false
}
// 没有设置比较值
switch operator {
case FilterOperatorIsEqual, FilterOperatorIsNotEqual, FilterOperatorContains, FilterOperatorDoesNotContain:
return true
case FilterOperatorIsEmpty:
return 0 == len(value.MSelect) || 1 == len(value.MSelect) && "" == value.MSelect[0].Content
case FilterOperatorIsNotEmpty:
return 0 != len(value.MSelect) && !(1 == len(value.MSelect) && "" == value.MSelect[0].Content)
}
}
case KeyTypeURL:
if nil != value.URL && nil != other && nil != other.URL {
switch operator {
case FilterOperatorIsEqual:
return value.URL.Content == other.URL.Content
case FilterOperatorIsNotEqual:
return value.URL.Content != other.URL.Content
case FilterOperatorContains:
return strings.Contains(value.URL.Content, other.URL.Content)
case FilterOperatorDoesNotContain:
return !strings.Contains(value.URL.Content, other.URL.Content)
case FilterOperatorStartsWith:
return strings.HasPrefix(value.URL.Content, other.URL.Content)
case FilterOperatorEndsWith:
return strings.HasSuffix(value.URL.Content, other.URL.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.URL.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.URL.Content)
}
}
case KeyTypeEmail:
if nil != value.Email && nil != other && nil != other.Email {
switch operator {
case FilterOperatorIsEqual:
return value.Email.Content == other.Email.Content
case FilterOperatorIsNotEqual:
return value.Email.Content != other.Email.Content
case FilterOperatorContains:
return strings.Contains(value.Email.Content, other.Email.Content)
case FilterOperatorDoesNotContain:
return !strings.Contains(value.Email.Content, other.Email.Content)
case FilterOperatorStartsWith:
return strings.HasPrefix(value.Email.Content, other.Email.Content)
case FilterOperatorEndsWith:
return strings.HasSuffix(value.Email.Content, other.Email.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.Email.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.Email.Content)
}
}
case KeyTypePhone:
if nil != value.Phone && nil != other && nil != other.Phone {
switch operator {
case FilterOperatorIsEqual:
return value.Phone.Content == other.Phone.Content
case FilterOperatorIsNotEqual:
return value.Phone.Content != other.Phone.Content
case FilterOperatorContains:
return strings.Contains(value.Phone.Content, other.Phone.Content)
case FilterOperatorDoesNotContain:
return !strings.Contains(value.Phone.Content, other.Phone.Content)
case FilterOperatorStartsWith:
return strings.HasPrefix(value.Phone.Content, other.Phone.Content)
case FilterOperatorEndsWith:
return strings.HasSuffix(value.Phone.Content, other.Phone.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.Phone.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.Phone.Content)
}
}
case KeyTypeMAsset:
if nil != value.MAsset && nil != other && nil != other.MAsset && 0 < len(value.MAsset) && 0 < len(other.MAsset) {
switch operator {
case FilterOperatorIsEqual, FilterOperatorContains:
contains := false
for _, v := range value.MAsset {
for _, v2 := range other.MAsset {
if v.Content == v2.Content {
contains = true
break
}
}
}
return contains
case FilterOperatorIsNotEqual, FilterOperatorDoesNotContain:
contains := false
for _, v := range value.MAsset {
for _, v2 := range other.MAsset {
if v.Content == v2.Content {
contains = true
break
}
}
}
return !contains
case FilterOperatorIsEmpty:
return 0 == len(value.MAsset) || 1 == len(value.MAsset) && "" == value.MAsset[0].Content
case FilterOperatorIsNotEmpty:
return 0 != len(value.MAsset) && !(1 == len(value.MAsset) && "" == value.MAsset[0].Content)
}
}
case KeyTypeTemplate:
if nil != value.Template && nil != other && nil != other.Template {
switch operator {
case FilterOperatorIsEqual:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content == other.Template.Content
case FilterOperatorIsNotEqual:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content != other.Template.Content
case FilterOperatorIsGreater:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content > other.Template.Content
case FilterOperatorIsGreaterOrEqual:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content >= other.Template.Content
case FilterOperatorIsLess:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content < other.Template.Content
case FilterOperatorIsLessOrEqual:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return value.Template.Content <= other.Template.Content
case FilterOperatorContains:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return strings.Contains(value.Template.Content, other.Template.Content)
case FilterOperatorDoesNotContain:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return !strings.Contains(value.Template.Content, other.Template.Content)
case FilterOperatorStartsWith:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return strings.HasPrefix(value.Template.Content, other.Template.Content)
case FilterOperatorEndsWith:
if "" == strings.TrimSpace(other.Template.Content) {
return true
}
return strings.HasSuffix(value.Template.Content, other.Template.Content)
case FilterOperatorIsEmpty:
return "" == strings.TrimSpace(value.Template.Content)
case FilterOperatorIsNotEmpty:
return "" != strings.TrimSpace(value.Template.Content)
}
}
case KeyTypeCheckbox:
if nil != value.Checkbox {
switch operator {
case FilterOperatorIsTrue:
return value.Checkbox.Checked
case FilterOperatorIsFalse:
return !value.Checkbox.Checked
}
}
}
return false
}
func filterTime(valueMills int64, valueIsNotEmpty bool, otherValueStart, otherValueEnd, otherValueEnd2 time.Time, operator FilterOperator) bool {
valueTime := time.UnixMilli(valueMills)
switch operator {
case FilterOperatorIsEqual:
return (valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)) && valueTime.Before(otherValueEnd)
case FilterOperatorIsNotEqual:
return valueTime.Before(otherValueStart) || valueTime.After(otherValueEnd)
case FilterOperatorIsGreater:
return valueTime.After(otherValueEnd) || valueTime.Equal(otherValueEnd)
case FilterOperatorIsGreaterOrEqual:
return valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)
case FilterOperatorIsLess:
return valueTime.Before(otherValueStart)
case FilterOperatorIsLessOrEqual:
return valueTime.Before(otherValueEnd) || valueTime.Equal(otherValueEnd)
case FilterOperatorIsBetween:
return (valueTime.After(otherValueStart) || valueTime.Equal(otherValueStart)) && (valueTime.Before(otherValueEnd2) || valueTime.Equal(otherValueEnd2))
case FilterOperatorIsEmpty:
return !valueIsNotEmpty
case FilterOperatorIsNotEmpty:
return valueIsNotEmpty
}
return false
}
// 根据 Count、Unit 和 Direction 计算相对当前时间的开始时间和结束时间
func calcRelativeTimeRegion(count int, unit RelativeDateUnit, direction RelativeDateDirection) (start, end time.Time) {
now := time.Now()
switch unit {
case RelativeDateUnitDay:
switch direction {
case RelativeDateDirectionBefore:
// 结束时间使用今天的开始时间
end = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
// 开始时间使用结束时间减去 count 天
start = end.AddDate(0, 0, -count)
case RelativeDateDirectionThis:
// 开始时间使用今天的开始时间
start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
// 结束时间使用开始时间加上 count 天
end = start.AddDate(0, 0, count)
case RelativeDateDirectionAfter:
// 开始时间使用今天的结束时间
start = time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 999999999, now.Location())
// 结束时间使用开始时间加上 count 天
end = start.AddDate(0, 0, count)
}
case RelativeDateUnitWeek:
weekday := int(now.Weekday())
if 0 == weekday {
weekday = 7
}
switch direction {
case RelativeDateDirectionBefore:
// 结束时间使用本周的开始时间
end = time.Date(now.Year(), now.Month(), now.Day()-weekday, 0, 0, 0, 0, now.Location())
// 开始时间使用结束时间减去 count*7 天
start = end.AddDate(0, 0, -count*7)
case RelativeDateDirectionThis:
// 开始时间使用本周的开始时间
start = time.Date(now.Year(), now.Month(), now.Day()-weekday, 0, 0, 0, 0, now.Location())
// 结束时间使用开始时间加上 count*7 天
end = start.AddDate(0, 0, count*7)
case RelativeDateDirectionAfter:
// 开始时间使用本周的结束时间
start = time.Date(now.Year(), now.Month(), now.Day()-weekday+7, 23, 59, 59, 999999999, now.Location())
// 结束时间使用开始时间加上 count*7 天
end = start.AddDate(0, 0, count*7)
}
case RelativeDateUnitMonth:
switch direction {
case RelativeDateDirectionBefore:
// 结束时间使用本月的开始时间
end = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
// 开始时间使用结束时间减去 count 个月
start = end.AddDate(0, -count, 0)
case RelativeDateDirectionThis:
// 开始时间使用本月的开始时间
start = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
// 结束时间使用开始时间加上 count 个月
end = start.AddDate(0, count, 0)
case RelativeDateDirectionAfter:
// 开始时间使用本月的结束时间
start = time.Date(now.Year(), now.Month()+1, 1, 0, 0, 0, 0, now.Location()).Add(-time.Nanosecond)
// 结束时间使用开始时间加上 count 个月
end = start.AddDate(0, count, 0)
}
case RelativeDateUnitYear:
switch direction {
case RelativeDateDirectionBefore:
// 结束时间使用今年的开始时间
end = time.Date(now.Year(), 1, 1, 0, 0, 0, 0, now.Location())
// 开始时间使用结束时间减去 count 年
start = end.AddDate(-count, 0, 0)
case RelativeDateDirectionThis:
// 开始时间使用今年的开始时间
start = time.Date(now.Year(), 1, 1, 0, 0, 0, 0, now.Location())
// 结束时间使用开始时间加上 count 年
end = start.AddDate(count, 0, 0)
case RelativeDateDirectionAfter:
// 开始时间使用今年的结束时间
start = time.Date(now.Year()+1, 1, 1, 0, 0, 0, 0, now.Location()).Add(-time.Nanosecond)
// 结束时间使用开始时间加上 count 年
end = start.AddDate(count, 0, 0)
}
}
return
}
// Table 描述了表格实例的结构。
type Table struct {
ID string `json:"id"` // 表格布局 ID

View file

@ -1376,15 +1376,14 @@ func (tx *Transaction) doSortAttrViewView(operation *Operation) (ret *TxErr) {
return &TxErr{code: TxErrWriteAttributeView, id: operation.AvID, msg: err.Error()}
}
view, err := getAttrViewViewByBlockID(attrView, operation.BlockID)
view := attrView.GetView(operation.ID)
if nil == view {
logging.LogErrorf("get view failed: %s", operation.BlockID)
return &TxErr{code: TxErrWriteAttributeView, id: operation.AvID, msg: err.Error()}
}
viewID := view.ID
previewViewID := operation.PreviousID
if viewID == previewViewID {
previousViewID := operation.PreviousID
if viewID == previousViewID {
return
}
@ -1402,7 +1401,7 @@ func (tx *Transaction) doSortAttrViewView(operation *Operation) (ret *TxErr) {
attrView.Views = append(attrView.Views[:index], attrView.Views[index+1:]...)
for i, v := range attrView.Views {
if v.ID == previewViewID {
if v.ID == previousViewID {
previousIndex = i + 1
break
}
@ -1926,14 +1925,14 @@ func setAttributeViewColumnCalc(operation *Operation) (err error) {
}
func (tx *Transaction) doInsertAttrViewBlock(operation *Operation) (ret *TxErr) {
err := AddAttributeViewBlock(tx, operation.SrcIDs, operation.AvID, operation.BlockID, operation.PreviousID, operation.IsDetached)
err := AddAttributeViewBlock(tx, operation.SrcIDs, operation.AvID, operation.BlockID, operation.PreviousID, operation.IsDetached, operation.IgnoreFillFilterVal)
if nil != err {
return &TxErr{code: TxErrWriteAttributeView, id: operation.AvID, msg: err.Error()}
}
return
}
func AddAttributeViewBlock(tx *Transaction, srcIDs []string, avID, blockID, previousBlockID string, isDetached bool) (err error) {
func AddAttributeViewBlock(tx *Transaction, srcIDs []string, avID, blockID, previousBlockID string, isDetached, ignoreFillFilter bool) (err error) {
for _, id := range srcIDs {
var tree *parse.Tree
if !isDetached {
@ -1949,14 +1948,14 @@ func AddAttributeViewBlock(tx *Transaction, srcIDs []string, avID, blockID, prev
}
}
if avErr := addAttributeViewBlock(avID, blockID, previousBlockID, id, isDetached, tree, tx); nil != avErr {
if avErr := addAttributeViewBlock(avID, blockID, previousBlockID, id, isDetached, ignoreFillFilter, tree, tx); nil != avErr {
return avErr
}
}
return
}
func addAttributeViewBlock(avID, blockID, previousBlockID, addingBlockID string, isDetached bool, tree *parse.Tree, tx *Transaction) (err error) {
func addAttributeViewBlock(avID, blockID, previousBlockID, addingBlockID string, isDetached, ignoreFillFilter bool, tree *parse.Tree, tx *Transaction) (err error) {
var node *ast.Node
if !isDetached {
node = treenode.GetNodeInTree(tree, addingBlockID)
@ -2007,7 +2006,7 @@ func addAttributeViewBlock(avID, blockID, previousBlockID, addingBlockID string,
// 如果存在过滤条件,则将过滤条件应用到新添加的块上
view, _ := getAttrViewViewByBlockID(attrView, blockID)
if nil != view && 0 < len(view.Table.Filters) {
if nil != view && 0 < len(view.Table.Filters) && !ignoreFillFilter {
viewable, _ := renderAttributeViewTable(attrView, view, "")
viewable.FilterRows(attrView)
viewable.SortRows()

View file

@ -587,6 +587,18 @@ func Close(force bool, execInstallPkg int) (exitCode int) {
clearWorkspaceTemp()
clearCorruptedNotebooks()
clearPortJSON()
// 将当前工作空间放到工作空间列表的最后一个
// Open the last workspace by default https://github.com/siyuan-note/siyuan/issues/10570
workspacePaths, err := util.ReadWorkspacePaths()
if nil != err {
logging.LogErrorf("read workspace paths failed: %s", err)
} else {
workspacePaths = gulu.Str.RemoveElem(workspacePaths, util.WorkspaceDir)
workspacePaths = append(workspacePaths, util.WorkspaceDir)
util.WriteWorkspacePaths(workspacePaths)
}
util.UnlockWorkspace()
time.Sleep(500 * time.Millisecond)

View file

@ -1475,8 +1475,11 @@ func processSyncMergeResult(exit, byHand bool, mergeResult *dejavu.MergeResult,
}
util.WaitForUILoaded()
util.BroadcastByType("main", "syncMergeResult", 0, "",
map[string]interface{}{"upsertRootIDs": upsertRootIDs, "removeRootIDs": removeRootIDs})
if 0 < len(upsertRootIDs) || 0 < len(removeRootIDs) {
util.BroadcastByType("main", "syncMergeResult", 0, "",
map[string]interface{}{"upsertRootIDs": upsertRootIDs, "removeRootIDs": removeRootIDs})
}
time.Sleep(2 * time.Second)
util.PushStatusBar(fmt.Sprintf(Conf.Language(149), elapsed.Seconds()))

View file

@ -1231,16 +1231,17 @@ type Operation struct {
DeckID string `json:"deckID"` // 用于添加/删除闪卡
AvID string `json:"avID"` // 属性视图 ID
SrcIDs []string `json:"srcIDs"` // 用于将块拖拽到属性视图中
IsDetached bool `json:"isDetached"` // 用于标识是否是脱离块,仅存在于属性视图中
Name string `json:"name"` // 属性视图列名
Typ string `json:"type"` // 属性视图列类型
Format string `json:"format"` // 属性视图列格式化
KeyID string `json:"keyID"` // 属性视列 ID
RowID string `json:"rowID"` // 属性视图行 ID
IsTwoWay bool `json:"isTwoWay"` // 属性视图关联列是否是双向关系
BackRelationKeyID string `json:"backRelationKeyID"` // 属性视图关联列回链关联列的 ID
AvID string `json:"avID"` // 属性视图 ID
SrcIDs []string `json:"srcIDs"` // 用于将块拖拽到属性视图中
IsDetached bool `json:"isDetached"` // 用于标识是否未绑定块,仅存在于属性视图中
IgnoreFillFilterVal bool `json:"ignoreFillFilter"` // 用于标识是否忽略填充筛选值
Name string `json:"name"` // 属性视图列名
Typ string `json:"type"` // 属性视图列类型
Format string `json:"format"` // 属性视图列格式化
KeyID string `json:"keyID"` // 属性视列 ID
RowID string `json:"rowID"` // 属性视图行 ID
IsTwoWay bool `json:"isTwoWay"` // 属性视图关联列是否是双向关系
BackRelationKeyID string `json:"backRelationKeyID"` // 属性视图关联列回链关联列的 ID
}
type Transaction struct {

View file

@ -243,7 +243,6 @@ func initWorkspaceDir(workspaceArg string) {
} else {
workspacePaths, _ = ReadWorkspacePaths()
if 0 < len(workspacePaths) {
// 取最后一个(也就是最近打开的)工作空间
WorkspaceDir = workspacePaths[len(workspacePaths)-1]
} else {
WorkspaceDir = defaultWorkspaceDir