Improve Recent documents (#15824)

*  Improve Recent documents
基于文档最近浏览时间进行排序

*  支持显示最近关闭文档

*  支持显示最近关闭文档

*  支持显示最近关闭文档

*  支持显示最近关闭文档

*  支持显示最近关闭文档

*  支持显示最近关闭文档

* 支持Ctrl+Shift+T打开最近关闭的文档

* 🎨 clean code

* 🔥 移除表格插入行/列的默认快捷键

*  最近文档支持显示最近修改文档

* 🎨

*  最近文档支持查看最近打开

* 🎨

* 

* Update win-build.bat
This commit is contained in:
Achuan-2 2025-10-24 11:12:14 +08:00 committed by GitHub
parent 7e1306cab9
commit d9e0c56a47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 10579 additions and 10202 deletions

View file

@ -78,6 +78,10 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/storage/getCriteria", model.CheckAuth, getCriteria)
ginServer.Handle("POST", "/api/storage/removeCriterion", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeCriterion)
ginServer.Handle("POST", "/api/storage/getRecentDocs", model.CheckAuth, getRecentDocs)
ginServer.Handle("POST", "/api/storage/updateRecentDocViewTime", model.CheckAuth, updateRecentDocViewTime)
ginServer.Handle("POST", "/api/storage/updateRecentDocCloseTime", model.CheckAuth, updateRecentDocCloseTime)
ginServer.Handle("POST", "/api/storage/updateRecentDocOpenTime", model.CheckAuth, updateRecentDocOpenTime)
ginServer.Handle("POST", "/api/storage/getOutlineStorage", model.CheckAuth, getOutlineStorage)
ginServer.Handle("POST", "/api/storage/setOutlineStorage", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setOutlineStorage)
ginServer.Handle("POST", "/api/storage/removeOutlineStorage", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeOutlineStorage)

View file

@ -29,7 +29,18 @@ func getRecentDocs(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
data, err := model.GetRecentDocs()
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
// 获取排序参数
sortBy := "viewedAt" // 默认按浏览时间排序
if arg["sortBy"] != nil {
sortBy = arg["sortBy"].(string)
}
data, err := model.GetRecentDocs(sortBy)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
@ -236,3 +247,59 @@ func removeOutlineStorage(c *gin.Context) {
return
}
}
func updateRecentDocViewTime(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
rootID := arg["rootID"].(string)
err := model.UpdateRecentDocViewTime(rootID)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
}
func updateRecentDocOpenTime(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
rootID := arg["rootID"].(string)
err := model.UpdateRecentDocOpenTime(rootID)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
}
func updateRecentDocCloseTime(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
rootID := arg["rootID"].(string)
err := model.UpdateRecentDocCloseTime(rootID)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
return
}
}

View file

@ -21,7 +21,9 @@ import (
"os"
"path"
"path/filepath"
"sort"
"sync"
"time"
"github.com/88250/gulu"
"github.com/88250/lute/parse"
@ -32,9 +34,12 @@ import (
)
type RecentDoc struct {
RootID string `json:"rootID"`
Icon string `json:"icon"`
Title string `json:"title"`
RootID string `json:"rootID"`
Icon string `json:"icon"`
Title string `json:"title"`
ViewedAt int64 `json:"viewedAt"` // 浏览时间字段
ClosedAt int64 `json:"closedAt"` // 关闭时间字段
OpenAt int64 `json:"openAt"` // 文档第一次从文档树加载到页签的时间
}
type OutlineDoc struct {
@ -70,9 +75,12 @@ func RemoveRecentDoc(ids []string) {
func setRecentDocByTree(tree *parse.Tree) {
recentDoc := &RecentDoc{
RootID: tree.Root.ID,
Icon: tree.Root.IALAttr("icon"),
Title: tree.Root.IALAttr("title"),
RootID: tree.Root.ID,
Icon: tree.Root.IALAttr("icon"),
Title: tree.Root.IALAttr("title"),
ViewedAt: time.Now().Unix(), // 使用当前时间作为浏览时间
ClosedAt: 0, // 初始化关闭时间为0表示未关闭
OpenAt: time.Now().Unix(), // 设置文档打开时间
}
recentDocLock.Lock()
@ -99,10 +107,95 @@ func setRecentDocByTree(tree *parse.Tree) {
return
}
func GetRecentDocs() (ret []*RecentDoc, err error) {
// 更新文档打开时间(只在第一次从文档树加载到页签时调用)
func UpdateRecentDocOpenTime(rootID string) error {
recentDocLock.Lock()
defer recentDocLock.Unlock()
return getRecentDocs()
recentDocs, err := getRecentDocs()
if err != nil {
return err
}
// 查找文档并更新打开时间
found := false
for _, doc := range recentDocs {
if doc.RootID == rootID {
doc.OpenAt = time.Now().Unix()
found = true
break
}
}
if found {
err = setRecentDocs(recentDocs)
}
return err
}
// 更新文档浏览时间
func UpdateRecentDocViewTime(rootID string) error {
recentDocLock.Lock()
defer recentDocLock.Unlock()
recentDocs, err := getRecentDocs()
if err != nil {
return err
}
// 查找文档并更新浏览时间
found := false
for _, doc := range recentDocs {
if doc.RootID == rootID {
doc.ViewedAt = time.Now().Unix()
found = true
break
}
}
if found {
// 按浏览时间降序排序
sort.Slice(recentDocs, func(i, j int) bool {
return recentDocs[i].ViewedAt > recentDocs[j].ViewedAt
})
err = setRecentDocs(recentDocs)
}
return err
}
// 更新文档关闭时间
func UpdateRecentDocCloseTime(rootID string) error {
recentDocLock.Lock()
defer recentDocLock.Unlock()
recentDocs, err := getRecentDocs()
if err != nil {
return err
}
// 查找文档并更新关闭时间
found := false
for _, doc := range recentDocs {
if doc.RootID == rootID {
doc.ClosedAt = time.Now().Unix()
found = true
break
}
}
if found {
err = setRecentDocs(recentDocs)
}
return err
}
func GetRecentDocs(sortBy ...string) (ret []*RecentDoc, err error) {
recentDocLock.Lock()
defer recentDocLock.Unlock()
return getRecentDocs(sortBy...)
}
func setRecentDocs(recentDocs []*RecentDoc) (err error) {
@ -127,7 +220,7 @@ func setRecentDocs(recentDocs []*RecentDoc) (err error) {
return
}
func getRecentDocs() (ret []*RecentDoc, err error) {
func getRecentDocs(sortBy ...string) (ret []*RecentDoc, err error) {
tmp := []*RecentDoc{}
dataPath := filepath.Join(util.DataDir, "storage/recent-doc.json")
if !filelock.IsExist(dataPath) {
@ -159,9 +252,77 @@ func getRecentDocs() (ret []*RecentDoc, err error) {
notExists = append(notExists, doc.RootID)
}
}
if 0 < len(notExists) {
setRecentDocs(ret)
}
// 根据排序参数进行排序
if len(sortBy) > 0 {
switch sortBy[0] {
case "closedAt":
// 按关闭时间排序
sort.Slice(ret, func(i, j int) bool {
if ret[i].ClosedAt == 0 && ret[j].ClosedAt == 0 {
// 如果都没有关闭时间,按浏览时间排序
return ret[i].ViewedAt > ret[j].ViewedAt
}
if ret[i].ClosedAt == 0 {
return false // 没有关闭时间的排在后面
}
if ret[j].ClosedAt == 0 {
return true // 有关闭时间的排在前面
}
return ret[i].ClosedAt > ret[j].ClosedAt
})
case "openAt":
// 按打开时间排序
sort.Slice(ret, func(i, j int) bool {
if ret[i].OpenAt == 0 && ret[j].OpenAt == 0 {
// 如果都没有打开时间按ID时间排序ID包含时间信息
return ret[i].RootID > ret[j].RootID
}
if ret[i].OpenAt == 0 {
return false // 没有打开时间的排在后面
}
if ret[j].OpenAt == 0 {
return true // 有打开时间的排在前面
}
return ret[i].OpenAt > ret[j].OpenAt
})
default:
// 默认按浏览时间排序
sort.Slice(ret, func(i, j int) bool {
if ret[i].ViewedAt == 0 && ret[j].ViewedAt == 0 {
// 如果都没有浏览时间按ID时间排序ID包含时间信息
return ret[i].RootID > ret[j].RootID
}
if ret[i].ViewedAt == 0 {
return false // 没有浏览时间的排在后面
}
if ret[j].ViewedAt == 0 {
return true // 有浏览时间的排在前面
}
return ret[i].ViewedAt > ret[j].ViewedAt
})
}
} else {
// 默认按浏览时间降序排序如果ViewedAt为0则使用文档创建时间
sort.Slice(ret, func(i, j int) bool {
if ret[i].ViewedAt == 0 && ret[j].ViewedAt == 0 {
// 如果都没有浏览时间按ID时间排序ID包含时间信息
return ret[i].RootID > ret[j].RootID
}
if ret[i].ViewedAt == 0 {
return false // 没有浏览时间的排在后面
}
if ret[j].ViewedAt == 0 {
return true // 有浏览时间的排在前面
}
return ret[i].ViewedAt > ret[j].ViewedAt
})
}
return
}