Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
d1009f2333
10 changed files with 842 additions and 798 deletions
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -243,7 +243,6 @@ func initWorkspaceDir(workspaceArg string) {
|
|||
} else {
|
||||
workspacePaths, _ = ReadWorkspacePaths()
|
||||
if 0 < len(workspacePaths) {
|
||||
// 取最后一个(也就是最近打开的)工作空间
|
||||
WorkspaceDir = workspacePaths[len(workspacePaths)-1]
|
||||
} else {
|
||||
WorkspaceDir = defaultWorkspaceDir
|
||||
|
|
Loading…
Add table
Reference in a new issue