mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-01-03 15:28:49 +01:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
2657275643
18 changed files with 340 additions and 162 deletions
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "قام الموقع المستهدف بتمكين حماية الروابط المباشرة، لذلك لا يمكن تنزيل [%d] من الموارد",
|
||||
"256": "المسار المحدد [%s] يحتوي على مسار مساحة عمل رئيسي [%s]",
|
||||
"257": "تذكرني لمدة 30 يومًا",
|
||||
"258": "فشلت العملية، يرجى المحاولة مرة أخرى لاحقًا"
|
||||
"258": "فشلت العملية، يرجى المحاولة مرة أخرى لاحقًا",
|
||||
"259": "آخر %d أيام",
|
||||
"260": "أمس",
|
||||
"261": "اليوم",
|
||||
"262": "غدًا",
|
||||
"263": "التالي %d أيام",
|
||||
"264": "الحقل [%s] فارغ"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "Die Zielseite hat den Hotlink-Schutz aktiviert, daher ist es nicht möglich, [%d] Ressourcen herunterzuladen",
|
||||
"256": "Der angegebene Pfad [%s] hat einen übergeordneten Arbeitsbereichspfad [%s]",
|
||||
"257": "Erinnere dich 30 Tage an mich",
|
||||
"258": "Vorgang fehlgeschlagen, bitte versuchen Sie es später erneut"
|
||||
"258": "Vorgang fehlgeschlagen, bitte versuchen Sie es später erneut",
|
||||
"259": "Letzte %d Tage",
|
||||
"260": "Gestern",
|
||||
"261": "Heute",
|
||||
"262": "Morgen",
|
||||
"263": "Nächste %d Tage",
|
||||
"264": "Das Feld [%s] ist leer"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "The target site has enabled hotlink protection, so it is not possible to download [%d] resources",
|
||||
"256": "The specified path [%s] has a parent workspace path [%s]",
|
||||
"257": "Remember me for 30 days",
|
||||
"258": "Operation failed, please try again later"
|
||||
"258": "Operation failed, please try again later",
|
||||
"259": "Last %d days",
|
||||
"260": "Yesterday",
|
||||
"261": "Today",
|
||||
"262": "Tomorrow",
|
||||
"263": "Next %d days",
|
||||
"264": "Field [%s] is empty"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "El sitio de destino ha activado la protección contra hotlinking, por lo que no es posible descargar [%d] recursos",
|
||||
"256": "La ruta especificada [%s] tiene una ruta de espacio de trabajo padre [%s]",
|
||||
"257": "Recuérdame durante 30 días",
|
||||
"258": "La operación falló, inténtelo de nuevo más tarde"
|
||||
"258": "La operación falló, inténtelo de nuevo más tarde",
|
||||
"259": "Últimos %d días",
|
||||
"260": "Ayer",
|
||||
"261": "Hoy",
|
||||
"262": "Mañana",
|
||||
"263": "Próximos %d días",
|
||||
"264": "El campo [%s] está vacío"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "Le site cible a activé la protection contre le hotlinking, il est donc impossible de télécharger [%d] ressources",
|
||||
"256": "Le chemin spécifié [%s] contient un chemin d'espace de travail parent [%s]",
|
||||
"257": "Souviens-toi de moi pendant 30 jours",
|
||||
"258": "Échec de l'opération, veuillez réessayer plus tard"
|
||||
"258": "Échec de l'opération, veuillez réessayer plus tard",
|
||||
"259": "Les %d derniers jours",
|
||||
"260": "Hier",
|
||||
"261": "Aujourd'hui",
|
||||
"262": "Demain",
|
||||
"263": "Les %d prochains jours",
|
||||
"264": "Le champ [%s] est vide"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "האתר היעד הפעיל הגנה על קישורים חמים, ולכן לא ניתן להוריד [%d] משאבים",
|
||||
"256": "הנתיב שצוין [%s] מכיל נתיב מרחב עבודה אב [%s]",
|
||||
"257": "זכור אותי למשך 30 יום",
|
||||
"258": "הפעולה נכשלה, נסה שוב מאוחר יותר"
|
||||
"258": "הפעולה נכשלה, נסה שוב מאוחר יותר",
|
||||
"259": "ה-%d ימים האחרונים",
|
||||
"260": "אתמול",
|
||||
"261": "היום",
|
||||
"262": "מחר",
|
||||
"263": "ה-%d ימים הבאים",
|
||||
"264": "השדה [%s] ריק"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "Il sito di destinazione ha abilitato la protezione hotlink, quindi non è possibile scaricare [%d] risorse",
|
||||
"256": "Il percorso specificato [%s] ha un percorso di spazio di lavoro padre [%s]",
|
||||
"257": "Ricordati di me per 30 giorni",
|
||||
"258": "Operazione fallita, riprova più tardi"
|
||||
"258": "Operazione fallita, riprova più tardi",
|
||||
"259": "Ultimi %d giorni",
|
||||
"260": "Ieri",
|
||||
"261": "Oggi",
|
||||
"262": "Domani",
|
||||
"263": "Prossimi %d giorni",
|
||||
"264": "Il campo [%s] è vuoto"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "ターゲットサイトはホットリンク保護を有効にしているため、[%d] 個のリソースをダウンロードできません",
|
||||
"256": "指定されたパス [%s] の親にワークスペースパス [%s] が存在します",
|
||||
"257": "30日間私を覚えていてください",
|
||||
"258": "操作に失敗しました。後でもう一度お試しください"
|
||||
"258": "操作に失敗しました。後でもう一度お試しください",
|
||||
"259": "過去 %d 日間",
|
||||
"260": "昨日",
|
||||
"261": "今日",
|
||||
"262": "明日",
|
||||
"263": "次の %d 日間",
|
||||
"264": "フィールド [%s] の値が空です"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "Docelowa strona włączyła ochronę przed hotlinkowaniem, [%d] zasobów nie można pobrać",
|
||||
"256": "Określona ścieżka [%s] ma nadrzędną ścieżkę przestrzeni roboczej [%s]",
|
||||
"257": "Zapamiętaj mnie na 30 dni",
|
||||
"258": "Operacja nie powiodła się, spróbuj ponownie później"
|
||||
"258": "Operacja nie powiodła się, spróbuj ponownie później",
|
||||
"259": "Ostatnie %d dni",
|
||||
"260": "Wczoraj",
|
||||
"261": "Dzisiaj",
|
||||
"262": "Jutro",
|
||||
"263": "Następne %d dni",
|
||||
"264": "Pole [%s] jest puste"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "O site de destino ativou proteção contra hotlinking, portanto não é possível baixar [%d] recursos",
|
||||
"256": "O caminho especificado [%s] tem um caminho de espaço de trabalho pai [%s]",
|
||||
"257": "Lembre-se de mim por 30 dias",
|
||||
"258": "A operação falhou, tente novamente mais tarde"
|
||||
"258": "A operação falhou, tente novamente mais tarde",
|
||||
"259": "Últimos %d dias",
|
||||
"260": "Ontem",
|
||||
"261": "Hoje",
|
||||
"262": "Amanhã",
|
||||
"263": "Próximos %d dias",
|
||||
"264": "O campo [%s] está vazio"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "Целевой сайт включил защиту от хотлинков, [%d] ресурсов невозможно скачать",
|
||||
"256": "Указанный путь [%s] имеет родительский путь рабочей области [%s]",
|
||||
"257": "Запомнить меня на 30 дней",
|
||||
"258": "Операция не удалась, попробуйте позже"
|
||||
"258": "Операция не удалась, попробуйте позже",
|
||||
"259": "Прошедшие %d дней",
|
||||
"260": "Вчера",
|
||||
"261": "Сегодня",
|
||||
"262": "Завтра",
|
||||
"263": "Следующие %d дней",
|
||||
"264": "Поле [%s] имеет пустое значение"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "目標站點啟用了防盜鏈,[%d] 個資源無法下載",
|
||||
"256": "指定的路徑 [%s] 父級存在工作空間路徑 [%s]",
|
||||
"257": "記住我 30 天",
|
||||
"258": "操作失敗,請稍後再試"
|
||||
"258": "操作失敗,請稍後再試",
|
||||
"259": "過去 %d 天",
|
||||
"260": "昨天",
|
||||
"261": "今天",
|
||||
"262": "明天",
|
||||
"263": "未來 %d 天",
|
||||
"264": "字段 [%s] 值為空"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,12 @@
|
|||
"255": "目标站点启用了防盗链,[%d] 个资源无法下载",
|
||||
"256": "指定的路径 [%s] 父级存在工作空间路径 [%s]",
|
||||
"257": "记住我 30 天",
|
||||
"258": "操作失败,请稍后再试"
|
||||
"258": "操作失败,请稍后再试",
|
||||
"259": "过去 %d 天",
|
||||
"260": "昨天",
|
||||
"261": "今天",
|
||||
"262": "明天",
|
||||
"263": "未来 %d 天",
|
||||
"264": "字段 [%s] 值为空"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,13 +185,14 @@ type View struct {
|
|||
Desc string `json:"desc"` // 视图描述
|
||||
Filters []*ViewFilter `json:"filters,omitempty"` // 过滤规则
|
||||
Sorts []*ViewSort `json:"sorts,omitempty"` // 排序规则
|
||||
Group *ViewGroup `json:"group,omitempty"` // 分组规则
|
||||
PageSize int `json:"pageSize"` // 每页条目数
|
||||
LayoutType LayoutType `json:"type"` // 当前布局类型
|
||||
Table *LayoutTable `json:"table,omitempty"` // 表格布局
|
||||
Gallery *LayoutGallery `json:"gallery,omitempty"` // 卡片布局
|
||||
ItemIDs []string `json:"itemIds,omitempty"` // 项目 ID 列表,用于维护所有项目
|
||||
|
||||
Group *ViewGroup `json:"group,omitempty"` // 分组规则
|
||||
GroupUpdated int64 `json:"groupUpdated"` // 分组规则更新时间戳
|
||||
Groups []*View `json:"groups,omitempty"` // 分组视图列表
|
||||
GroupItemIDs []string `json:"groupItemIds,omitempty"` // 分组项目 ID 列表,用于维护分组中的所有项目
|
||||
GroupCalc *GroupCalc `json:"groupCalc,omitempty"` // 分组计算规则
|
||||
|
|
@ -199,7 +200,6 @@ type View struct {
|
|||
GroupFolded bool `json:"groupFolded,omitempty"` // 分组是否折叠
|
||||
GroupHidden bool `json:"groupHidden,omitempty"` // 分组是否隐藏
|
||||
GroupHideEmpty bool `json:"groupHideEmpty,omitempty"` // 分组是否隐藏空分组
|
||||
GroupDefault bool `json:"groupDefault,omitempty"` // 是否为默认分组
|
||||
}
|
||||
|
||||
// GroupCalc 描述了分组计算规则和结果的结构。
|
||||
|
|
@ -291,9 +291,6 @@ type Viewable interface {
|
|||
|
||||
// SetGroupHidden 设置分组是否隐藏。
|
||||
SetGroupHidden(hidden bool)
|
||||
|
||||
// SetGroupDefault 设置分组是否为默认分组。
|
||||
SetGroupDefault(defaulted bool)
|
||||
}
|
||||
|
||||
func NewAttributeView(id string) (ret *AttributeView) {
|
||||
|
|
|
|||
|
|
@ -66,12 +66,11 @@ type BaseInstance struct {
|
|||
Folded bool `json:"folded,omitempty"` // 是否折叠
|
||||
Hidden bool `json:"hidden,omitempty"` // 是否隐藏
|
||||
|
||||
Groups []Viewable `json:"groups,omitempty"` // 分组实例列表
|
||||
GroupCalc *GroupCalc `json:"groupCalc,omitempty"` // 分组计算规则和结果
|
||||
GroupName string `json:"groupName,omitempty"` // 分组名称
|
||||
GroupFolded bool `json:"groupFolded,omitempty"` // 分组是否折叠
|
||||
GroupHidden bool `json:"groupHidden,omitempty"` // 分组是否隐藏
|
||||
GroupDefault bool `json:"groupDefault,omitempty"` // 是否为默认分组
|
||||
Groups []Viewable `json:"groups,omitempty"` // 分组实例列表
|
||||
GroupCalc *GroupCalc `json:"groupCalc,omitempty"` // 分组计算规则和结果
|
||||
GroupName string `json:"groupName,omitempty"` // 分组名称
|
||||
GroupFolded bool `json:"groupFolded,omitempty"` // 分组是否折叠
|
||||
GroupHidden bool `json:"groupHidden,omitempty"` // 分组是否隐藏
|
||||
}
|
||||
|
||||
func NewViewBaseInstance(view *View) *BaseInstance {
|
||||
|
|
@ -97,7 +96,6 @@ func NewViewBaseInstance(view *View) *BaseInstance {
|
|||
GroupName: view.GroupName,
|
||||
GroupFolded: view.GroupFolded,
|
||||
GroupHidden: view.GroupHidden,
|
||||
GroupDefault: view.GroupDefault,
|
||||
ShowIcon: showIcon,
|
||||
WrapField: wrapField,
|
||||
}
|
||||
|
|
@ -135,10 +133,6 @@ func (baseInstance *BaseInstance) SetGroupHidden(hidden bool) {
|
|||
baseInstance.GroupHidden = hidden
|
||||
}
|
||||
|
||||
func (baseInstance *BaseInstance) SetGroupDefault(defaulted bool) {
|
||||
baseInstance.GroupDefault = defaulted
|
||||
}
|
||||
|
||||
func (baseInstance *BaseInstance) GetID() string {
|
||||
return baseInstance.ID
|
||||
}
|
||||
|
|
|
|||
|
|
@ -272,10 +272,10 @@ func NetAssets2LocalAssets(rootID string, onlyImg bool, originalURL string) (err
|
|||
|
||||
func SearchAssetsByName(keyword string, exts []string) (ret []*cache.Asset) {
|
||||
ret = []*cache.Asset{}
|
||||
keywords := strings.Split(keyword, " ")
|
||||
|
||||
var keywords []string
|
||||
keywords = append(keywords, keyword)
|
||||
keywords = append(keywords, strings.Split(keyword, " ")...)
|
||||
pathHitCount := map[string]int{}
|
||||
count := 0
|
||||
filterByExt := 0 < len(exts)
|
||||
for _, asset := range cache.GetAssets() {
|
||||
if filterByExt {
|
||||
|
|
@ -295,8 +295,18 @@ func SearchAssetsByName(keyword string, exts []string) (ret []*cache.Asset) {
|
|||
lowerHName := strings.ToLower(asset.HName)
|
||||
lowerPath := strings.ToLower(asset.Path)
|
||||
var hitNameCount, hitPathCount int
|
||||
for _, k := range keywords {
|
||||
for i, k := range keywords {
|
||||
lowerKeyword := strings.ToLower(k)
|
||||
if 0 == i {
|
||||
// 第一个是完全匹配,权重最高
|
||||
if strings.Contains(lowerHName, lowerKeyword) {
|
||||
hitNameCount += 64
|
||||
}
|
||||
if strings.Contains(lowerPath, lowerKeyword) {
|
||||
hitPathCount += 64
|
||||
}
|
||||
}
|
||||
|
||||
hitNameCount += strings.Count(lowerHName, lowerKeyword)
|
||||
hitPathCount += strings.Count(lowerPath, lowerKeyword)
|
||||
if 1 > hitNameCount && 1 > hitPathCount {
|
||||
|
|
@ -318,10 +328,6 @@ func SearchAssetsByName(keyword string, exts []string) (ret []*cache.Asset) {
|
|||
Path: asset.Path,
|
||||
Updated: asset.Updated,
|
||||
})
|
||||
count++
|
||||
if Conf.Search.Limit <= count {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if 0 < len(pathHitCount) {
|
||||
|
|
@ -333,6 +339,10 @@ func SearchAssetsByName(keyword string, exts []string) (ret []*cache.Asset) {
|
|||
return ret[i].Updated > ret[j].Updated
|
||||
})
|
||||
}
|
||||
|
||||
if Conf.Search.Limit <= len(ret) {
|
||||
ret = ret[:Conf.Search.Limit]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -143,123 +143,7 @@ func SetAttributeViewGroup(avID, blockID string, group *av.ViewGroup) (err error
|
|||
}
|
||||
|
||||
view.Group = group
|
||||
view.Groups = nil
|
||||
|
||||
// 生成分组数据
|
||||
const (
|
||||
defaultGroupName = "_@default@_"
|
||||
notInRange = "_@notInRange@_"
|
||||
)
|
||||
var groupName string
|
||||
viewable := sql.RenderView(attrView, view, "")
|
||||
|
||||
var items []av.Item
|
||||
for _, item := range viewable.(av.Collection).GetItems() {
|
||||
items = append(items, item)
|
||||
}
|
||||
var rangeStart, rangeEnd float64
|
||||
switch group.Method {
|
||||
case av.GroupMethodValue:
|
||||
if av.GroupOrderMan != group.Order {
|
||||
sort.SliceStable(items, func(i, j int) bool {
|
||||
if av.GroupOrderAsc == group.Order {
|
||||
return items[i].GetValue(group.Field).String(false) < items[j].GetValue(group.Field).String(false)
|
||||
}
|
||||
return items[i].GetValue(group.Field).String(false) > items[j].GetValue(group.Field).String(false)
|
||||
})
|
||||
}
|
||||
case av.GroupMethodRangeNum:
|
||||
if nil == group.Range {
|
||||
logging.LogWarnf("range is nil in av [%s]", avID)
|
||||
return
|
||||
}
|
||||
|
||||
rangeStart, rangeEnd = group.Range.NumStart, group.Range.NumStart+group.Range.NumStep
|
||||
sort.SliceStable(items, func(i, j int) bool {
|
||||
if av.GroupOrderAsc == group.Order {
|
||||
return items[i].GetValue(group.Field).Number.Content < items[j].GetValue(group.Field).Number.Content
|
||||
}
|
||||
return items[i].GetValue(group.Field).Number.Content > items[j].GetValue(group.Field).Number.Content
|
||||
})
|
||||
case av.GroupMethodDateDay, av.GroupMethodDateWeek, av.GroupMethodDateMonth, av.GroupMethodDateYear, av.GroupMethodDateRelative:
|
||||
sort.SliceStable(items, func(i, j int) bool {
|
||||
if av.GroupOrderAsc == group.Order {
|
||||
return items[i].GetValue(group.Field).Date.Content < items[j].GetValue(group.Field).Date.Content
|
||||
}
|
||||
return items[i].GetValue(group.Field).Date.Content > items[j].GetValue(group.Field).Date.Content
|
||||
})
|
||||
}
|
||||
|
||||
groupItemsMap := map[string][]av.Item{}
|
||||
for _, item := range items {
|
||||
value := item.GetValue(group.Field)
|
||||
if value.IsEmpty() {
|
||||
groupName = defaultGroupName
|
||||
} else {
|
||||
switch group.Method {
|
||||
case av.GroupMethodValue:
|
||||
groupName = value.String(false)
|
||||
case av.GroupMethodRangeNum:
|
||||
if group.Range.NumStart > value.Number.Content || group.Range.NumEnd < value.Number.Content {
|
||||
groupName = notInRange
|
||||
break
|
||||
}
|
||||
|
||||
for rangeEnd <= group.Range.NumEnd && rangeEnd < value.Number.Content {
|
||||
rangeStart += group.Range.NumStep
|
||||
rangeEnd += group.Range.NumStep
|
||||
}
|
||||
|
||||
if rangeStart <= value.Number.Content && rangeEnd >= value.Number.Content {
|
||||
groupName = fmt.Sprintf("%s - %s", strconv.FormatFloat(rangeStart, 'f', -1, 64), strconv.FormatFloat(rangeEnd, 'f', -1, 64))
|
||||
}
|
||||
case av.GroupMethodDateDay, av.GroupMethodDateWeek, av.GroupMethodDateMonth, av.GroupMethodDateYear, av.GroupMethodDateRelative:
|
||||
var contentTime time.Time
|
||||
switch value.Type {
|
||||
case av.KeyTypeDate:
|
||||
contentTime = time.UnixMilli(value.Date.Content)
|
||||
case av.KeyTypeCreated:
|
||||
contentTime = time.UnixMilli(value.Created.Content)
|
||||
case av.KeyTypeUpdated:
|
||||
contentTime = time.UnixMilli(value.Updated.Content)
|
||||
}
|
||||
switch group.Method {
|
||||
case av.GroupMethodDateDay:
|
||||
groupName = contentTime.Format("2006-01-02")
|
||||
case av.GroupMethodDateWeek:
|
||||
year, week := contentTime.ISOWeek()
|
||||
groupName = fmt.Sprintf("%d-W%02d", year, week)
|
||||
case av.GroupMethodDateMonth:
|
||||
groupName = contentTime.Format("2006-01")
|
||||
case av.GroupMethodDateYear:
|
||||
groupName = contentTime.Format("2006")
|
||||
case av.GroupMethodDateRelative:
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
groupItemsMap[groupName] = append(groupItemsMap[groupName], item)
|
||||
}
|
||||
|
||||
for name, groupItems := range groupItemsMap {
|
||||
var v *av.View
|
||||
switch view.LayoutType {
|
||||
case av.LayoutTypeTable:
|
||||
v = av.NewTableView()
|
||||
v.Table = av.NewLayoutTable()
|
||||
case av.LayoutTypeGallery:
|
||||
v = av.NewGalleryView()
|
||||
v.Gallery = av.NewLayoutGallery()
|
||||
}
|
||||
for _, item := range groupItems {
|
||||
v.GroupItemIDs = append(v.GroupItemIDs, item.GetID())
|
||||
}
|
||||
v.Name = name
|
||||
view.Groups = append(view.Groups, v)
|
||||
view.GroupDefault = name == defaultGroupName
|
||||
}
|
||||
|
||||
err = av.SaveAttributeView(attrView)
|
||||
genGroup(view, attrView)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -370,13 +254,26 @@ func ChangeAttrViewLayout(blockID, avID string, layout av.LayoutType) (err error
|
|||
continue
|
||||
}
|
||||
|
||||
node.AttributeViewType = string(view.LayoutType)
|
||||
changed := false
|
||||
attrs := parse.IAL2Map(node.KramdownIAL)
|
||||
attrs[av.NodeAttrView] = view.ID
|
||||
err = setNodeAttrs(node, tree, attrs)
|
||||
if err != nil {
|
||||
logging.LogWarnf("set node [%s] attrs failed: %s", bID, err)
|
||||
return
|
||||
if blockID == bID { // 当前操作的镜像库
|
||||
attrs[av.NodeAttrView] = view.ID
|
||||
node.AttributeViewType = string(view.LayoutType)
|
||||
changed = true
|
||||
} else {
|
||||
if view.ID == attrs[av.NodeAttrView] {
|
||||
// 仅更新和当前操作的镜像库指定的视图相同的镜像库
|
||||
node.AttributeViewType = string(view.LayoutType)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if changed {
|
||||
err = setNodeAttrs(node, tree, attrs)
|
||||
if err != nil {
|
||||
logging.LogWarnf("set node [%s] attrs failed: %s", bID, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1405,6 +1302,12 @@ func RenderAttributeView(avID, viewID, query string, page, pageSize int) (viewab
|
|||
return
|
||||
}
|
||||
|
||||
const (
|
||||
groupNameLast30Days, groupNameLast7Days = "_@last30Days@_", "_@last7Days@_"
|
||||
groupNameYesterday, groupNameToday, groupNameTomorrow = "_@yesterday@_", "_@today@_", "_@tomorrow@_"
|
||||
groupNameNext7Days, groupNameNext30Days = "_@next7Days@_", "_@next30Days@_"
|
||||
)
|
||||
|
||||
func renderAttributeView(attrView *av.AttributeView, viewID, query string, page, pageSize int) (viewable av.Viewable, err error) {
|
||||
if 1 > len(attrView.Views) {
|
||||
view, _, _ := av.NewTableViewWithBlockKey(ast.NewNodeID())
|
||||
|
|
@ -1444,6 +1347,33 @@ func renderAttributeView(attrView *av.AttributeView, viewID, query string, page,
|
|||
return
|
||||
}
|
||||
|
||||
// 当前日期可能会变,所以如果是按日期分组则需要重新生成分组
|
||||
if isGroupByDate(view) {
|
||||
updatedDate := time.UnixMilli(view.GroupUpdated).Format("2006-01-02")
|
||||
if time.Now().Format("2006-01-02") != updatedDate {
|
||||
genGroup(view, attrView)
|
||||
}
|
||||
|
||||
for _, groupView := range view.Groups {
|
||||
switch groupView.Name {
|
||||
case groupNameLast30Days:
|
||||
groupView.Name = fmt.Sprintf(Conf.language(259), 30)
|
||||
case groupNameLast7Days:
|
||||
groupView.Name = fmt.Sprintf(Conf.language(259), 7)
|
||||
case groupNameYesterday:
|
||||
groupView.Name = Conf.language(260)
|
||||
case groupNameToday:
|
||||
groupView.Name = Conf.language(261)
|
||||
case groupNameTomorrow:
|
||||
groupView.Name = Conf.language(262)
|
||||
case groupNameNext7Days:
|
||||
groupView.Name = fmt.Sprintf(Conf.language(263), 7)
|
||||
case groupNameNext30Days:
|
||||
groupView.Name = fmt.Sprintf(Conf.language(263), 30)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果存在分组的话渲染分组视图视图
|
||||
var groups []av.Viewable
|
||||
for _, groupView := range view.Groups {
|
||||
|
|
@ -1465,6 +1395,171 @@ func renderAttributeView(attrView *av.AttributeView, viewID, query string, page,
|
|||
return
|
||||
}
|
||||
|
||||
func genGroup(view *av.View, attrView *av.AttributeView) {
|
||||
if nil == view.Group {
|
||||
return
|
||||
}
|
||||
|
||||
group := view.Group
|
||||
view.Groups = nil
|
||||
viewable := sql.RenderView(attrView, view, "")
|
||||
var items []av.Item
|
||||
for _, item := range viewable.(av.Collection).GetItems() {
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
groupKey, _ := attrView.GetKey(group.Field)
|
||||
if nil == groupKey {
|
||||
return
|
||||
}
|
||||
|
||||
var rangeStart, rangeEnd float64
|
||||
switch group.Method {
|
||||
case av.GroupMethodValue:
|
||||
if av.GroupOrderMan != group.Order {
|
||||
sort.SliceStable(items, func(i, j int) bool {
|
||||
if av.GroupOrderAsc == group.Order {
|
||||
return items[i].GetValue(group.Field).String(false) < items[j].GetValue(group.Field).String(false)
|
||||
}
|
||||
return items[i].GetValue(group.Field).String(false) > items[j].GetValue(group.Field).String(false)
|
||||
})
|
||||
}
|
||||
case av.GroupMethodRangeNum:
|
||||
if nil == group.Range {
|
||||
return
|
||||
}
|
||||
|
||||
rangeStart, rangeEnd = group.Range.NumStart, group.Range.NumStart+group.Range.NumStep
|
||||
sort.SliceStable(items, func(i, j int) bool {
|
||||
if av.GroupOrderAsc == group.Order {
|
||||
return items[i].GetValue(group.Field).Number.Content < items[j].GetValue(group.Field).Number.Content
|
||||
}
|
||||
return items[i].GetValue(group.Field).Number.Content > items[j].GetValue(group.Field).Number.Content
|
||||
})
|
||||
case av.GroupMethodDateDay, av.GroupMethodDateWeek, av.GroupMethodDateMonth, av.GroupMethodDateYear, av.GroupMethodDateRelative:
|
||||
sort.SliceStable(items, func(i, j int) bool {
|
||||
if av.GroupOrderAsc == group.Order {
|
||||
return items[i].GetValue(group.Field).Date.Content < items[j].GetValue(group.Field).Date.Content
|
||||
}
|
||||
return items[i].GetValue(group.Field).Date.Content > items[j].GetValue(group.Field).Date.Content
|
||||
})
|
||||
}
|
||||
|
||||
const defaultGroupName, notInRange = "_@default@_", "_@notInRange@_"
|
||||
var groupName string
|
||||
groupItemsMap := map[string][]av.Item{}
|
||||
for _, item := range items {
|
||||
value := item.GetValue(group.Field)
|
||||
if value.IsEmpty() {
|
||||
groupName = defaultGroupName
|
||||
groupItemsMap[groupName] = append(groupItemsMap[groupName], item)
|
||||
continue
|
||||
}
|
||||
|
||||
switch group.Method {
|
||||
case av.GroupMethodValue:
|
||||
groupName = value.String(false)
|
||||
case av.GroupMethodRangeNum:
|
||||
if group.Range.NumStart > value.Number.Content || group.Range.NumEnd < value.Number.Content {
|
||||
groupName = notInRange
|
||||
break
|
||||
}
|
||||
|
||||
for rangeEnd <= group.Range.NumEnd && rangeEnd < value.Number.Content {
|
||||
rangeStart += group.Range.NumStep
|
||||
rangeEnd += group.Range.NumStep
|
||||
}
|
||||
|
||||
if rangeStart <= value.Number.Content && rangeEnd >= value.Number.Content {
|
||||
groupName = fmt.Sprintf("%s - %s", strconv.FormatFloat(rangeStart, 'f', -1, 64), strconv.FormatFloat(rangeEnd, 'f', -1, 64))
|
||||
}
|
||||
case av.GroupMethodDateDay, av.GroupMethodDateWeek, av.GroupMethodDateMonth, av.GroupMethodDateYear, av.GroupMethodDateRelative:
|
||||
var contentTime time.Time
|
||||
switch value.Type {
|
||||
case av.KeyTypeDate:
|
||||
contentTime = time.UnixMilli(value.Date.Content)
|
||||
case av.KeyTypeCreated:
|
||||
contentTime = time.UnixMilli(value.Created.Content)
|
||||
case av.KeyTypeUpdated:
|
||||
contentTime = time.UnixMilli(value.Updated.Content)
|
||||
}
|
||||
switch group.Method {
|
||||
case av.GroupMethodDateDay:
|
||||
groupName = contentTime.Format("2006-01-02")
|
||||
case av.GroupMethodDateWeek:
|
||||
year, week := contentTime.ISOWeek()
|
||||
groupName = fmt.Sprintf("%d-W%02d", year, week)
|
||||
case av.GroupMethodDateMonth:
|
||||
groupName = contentTime.Format("2006-01")
|
||||
case av.GroupMethodDateYear:
|
||||
groupName = contentTime.Format("2006")
|
||||
case av.GroupMethodDateRelative:
|
||||
// 过去 30 天之前的按月分组
|
||||
// 过去 30 天、过去 7 天、昨天、今天、明天、未来 7 天、未来 30 天
|
||||
// 未来 30 天之后的按月分组
|
||||
now := time.Now()
|
||||
if contentTime.Before(now.AddDate(0, 0, -30)) {
|
||||
groupName = contentTime.Format("2006-01")
|
||||
} else if contentTime.Before(now.AddDate(0, 0, -7)) {
|
||||
groupName = groupNameLast30Days
|
||||
} else if contentTime.Before(now.AddDate(0, 0, -1)) {
|
||||
groupName = groupNameLast7Days
|
||||
} else if contentTime.Equal(now.AddDate(0, 0, -1)) {
|
||||
groupName = groupNameYesterday
|
||||
} else if contentTime.Equal(now) {
|
||||
groupName = groupNameToday
|
||||
} else if contentTime.Equal(now.AddDate(0, 0, 1)) {
|
||||
groupName = groupNameTomorrow
|
||||
} else if contentTime.Before(now.AddDate(0, 0, 7)) {
|
||||
groupName = groupNameNext7Days
|
||||
} else if contentTime.Before(now.AddDate(0, 0, 30)) {
|
||||
groupName = groupNameNext30Days
|
||||
} else {
|
||||
groupName = contentTime.Format("2006-01")
|
||||
}
|
||||
}
|
||||
}
|
||||
groupItemsMap[groupName] = append(groupItemsMap[groupName], item)
|
||||
}
|
||||
|
||||
for name, groupItems := range groupItemsMap {
|
||||
var v *av.View
|
||||
switch view.LayoutType {
|
||||
case av.LayoutTypeTable:
|
||||
v = av.NewTableView()
|
||||
v.Table = av.NewLayoutTable()
|
||||
case av.LayoutTypeGallery:
|
||||
v = av.NewGalleryView()
|
||||
v.Gallery = av.NewLayoutGallery()
|
||||
default:
|
||||
logging.LogWarnf("unknown layout type [%s] for group view", view.LayoutType)
|
||||
return
|
||||
}
|
||||
for _, item := range groupItems {
|
||||
v.GroupItemIDs = append(v.GroupItemIDs, item.GetID())
|
||||
}
|
||||
|
||||
if defaultGroupName == name {
|
||||
name = fmt.Sprintf(Conf.language(264), groupKey.Name)
|
||||
}
|
||||
v.Name = name
|
||||
view.Groups = append(view.Groups, v)
|
||||
}
|
||||
|
||||
if isGroupByDate(view) {
|
||||
view.GroupUpdated = time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
av.SaveAttributeView(attrView)
|
||||
}
|
||||
|
||||
func isGroupByDate(view *av.View) bool {
|
||||
if nil == view.Group {
|
||||
return false
|
||||
}
|
||||
return av.GroupMethodDateDay == view.Group.Method || av.GroupMethodDateWeek == view.Group.Method || av.GroupMethodDateMonth == view.Group.Method || av.GroupMethodDateYear == view.Group.Method || av.GroupMethodDateRelative == view.Group.Method
|
||||
}
|
||||
|
||||
func renderViewableInstance(viewable av.Viewable, view *av.View, attrView *av.AttributeView, page, pageSize int) (err error) {
|
||||
if nil == viewable {
|
||||
err = av.ErrViewNotFound
|
||||
|
|
|
|||
|
|
@ -252,12 +252,16 @@ func getVirtualRefKeywords(root *ast.Node) (ret []string) {
|
|||
if 0 < len(regexps) {
|
||||
tmp = nil
|
||||
for _, str := range ret {
|
||||
matchExclude := false
|
||||
for _, re := range regexps {
|
||||
if ok, regErr := regexp.MatchString(re, str); !ok && nil == regErr {
|
||||
tmp = append(tmp, str)
|
||||
if ok, _ := regexp.MatchString(re, str); ok {
|
||||
matchExclude = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matchExclude {
|
||||
tmp = append(tmp, str)
|
||||
}
|
||||
}
|
||||
ret = tmp
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue