🎨 文档树支持 Ctrl+Click
和 Shift+↑/↓
进行多选 https://github.com/siyuan-note/siyuan/issues/1359
This commit is contained in:
parent
c8680c9fb3
commit
e45a0694bd
4 changed files with 168 additions and 26 deletions
|
@ -267,6 +267,31 @@ func moveDoc(c *gin.Context) {
|
|||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func moveDocs(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var fromPaths []string
|
||||
fromPathsArg := arg["fromPaths"].([]interface{})
|
||||
for _, fromPath := range fromPathsArg {
|
||||
fromPaths = append(fromPaths, fromPath.(string))
|
||||
}
|
||||
toPath := arg["toPath"].(string)
|
||||
|
||||
err := model.MoveDocs(fromPaths, toPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func removeDoc(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
@ -294,6 +319,28 @@ func removeDoc(c *gin.Context) {
|
|||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func removeDocs(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
pathsArg := arg["paths"].([]interface{})
|
||||
var paths []string
|
||||
for _, path := range pathsArg {
|
||||
paths = append(paths, path.(string))
|
||||
}
|
||||
err := model.RemoveDocs(paths)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func renameDoc(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
|
|
@ -87,7 +87,9 @@ func ServeAPI(ginServer *gin.Engine) {
|
|||
ginServer.Handle("POST", "/api/filetree/createDoc", model.CheckAuth, model.CheckReadonly, createDoc)
|
||||
ginServer.Handle("POST", "/api/filetree/renameDoc", model.CheckAuth, model.CheckReadonly, renameDoc)
|
||||
ginServer.Handle("POST", "/api/filetree/removeDoc", model.CheckAuth, model.CheckReadonly, removeDoc)
|
||||
ginServer.Handle("POST", "/api/filetree/removeDocs", model.CheckAuth, model.CheckReadonly, removeDocs)
|
||||
ginServer.Handle("POST", "/api/filetree/moveDoc", model.CheckAuth, model.CheckReadonly, moveDoc)
|
||||
ginServer.Handle("POST", "/api/filetree/moveDocs", model.CheckAuth, model.CheckReadonly, moveDocs)
|
||||
ginServer.Handle("POST", "/api/filetree/duplicateDoc", model.CheckAuth, model.CheckReadonly, duplicateDoc)
|
||||
ginServer.Handle("POST", "/api/filetree/getHPathByPath", model.CheckAuth, getHPathByPath)
|
||||
ginServer.Handle("POST", "/api/filetree/getHPathByID", model.CheckAuth, getHPathByID)
|
||||
|
|
|
@ -545,3 +545,14 @@ func TryAccessFileByBlockID(id string) (ok bool) {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getBoxesByPaths(paths []string) (ret map[string]*Box) {
|
||||
for _, p := range paths {
|
||||
id := strings.TrimSuffix(path.Base(p), ".sy")
|
||||
bt := treenode.GetBlockTree(id)
|
||||
if nil != bt {
|
||||
ret[p] = Conf.Box(bt.BoxID)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -988,6 +988,8 @@ func GetFullHPathByID(id string) (hPath string, err error) {
|
|||
}
|
||||
|
||||
func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err error) {
|
||||
WaitForWritingFiles()
|
||||
|
||||
if fromBoxID == toBoxID && fromPath == toPath {
|
||||
return
|
||||
}
|
||||
|
@ -1004,13 +1006,6 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
|
|||
return
|
||||
}
|
||||
|
||||
WaitForWritingFiles()
|
||||
tree, err := LoadTree(fromBoxID, fromPath)
|
||||
if nil != err {
|
||||
err = ErrBlockNotFound
|
||||
return
|
||||
}
|
||||
|
||||
childDepth := util.GetChildDocDepth(filepath.Join(util.DataDir, fromBoxID, fromPath))
|
||||
if depth := strings.Count(toPath, "/") + childDepth; 6 < depth && !Conf.FileTree.AllowCreateDeeper {
|
||||
err = errors.New(Conf.Language(118))
|
||||
|
@ -1022,7 +1017,19 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
|
|||
err = errors.New(Conf.Language(0))
|
||||
return
|
||||
}
|
||||
isSameBox := fromBoxID == toBoxID
|
||||
|
||||
newPath, err = moveDoc(fromBox, fromPath, toBox, toPath)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
cache.ClearDocsIAL()
|
||||
IncSync()
|
||||
return
|
||||
}
|
||||
|
||||
func moveDoc(fromBox *Box, fromPath string, toBox *Box, toPath string) (newPath string, err error) {
|
||||
isSameBox := fromBox.ID == toBox.ID
|
||||
|
||||
if isSameBox {
|
||||
if !fromBox.Exist(toPath) {
|
||||
|
@ -1036,6 +1043,12 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
|
|||
}
|
||||
}
|
||||
|
||||
tree, err := LoadTree(fromBox.ID, fromPath)
|
||||
if nil != err {
|
||||
err = ErrBlockNotFound
|
||||
return
|
||||
}
|
||||
|
||||
moveToRoot := "/" == toPath
|
||||
toBlockID := tree.ID
|
||||
fromFolder := path.Join(path.Dir(fromPath), tree.ID)
|
||||
|
@ -1043,9 +1056,9 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
|
|||
if !moveToRoot {
|
||||
var toTree *parse.Tree
|
||||
if isSameBox {
|
||||
toTree, err = LoadTree(fromBoxID, toPath)
|
||||
toTree, err = LoadTree(fromBox.ID, toPath)
|
||||
} else {
|
||||
toTree, err = LoadTree(toBoxID, toPath)
|
||||
toTree, err = LoadTree(toBox.ID, toPath)
|
||||
}
|
||||
if nil != err {
|
||||
err = ErrBlockNotFound
|
||||
|
@ -1075,14 +1088,14 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
|
|||
return
|
||||
}
|
||||
} else {
|
||||
absFromPath := filepath.Join(util.DataDir, fromBoxID, fromFolder)
|
||||
absToPath := filepath.Join(util.DataDir, toBoxID, newFolder)
|
||||
absFromPath := filepath.Join(util.DataDir, fromBox.ID, fromFolder)
|
||||
absToPath := filepath.Join(util.DataDir, toBox.ID, newFolder)
|
||||
if gulu.File.IsExist(absToPath) {
|
||||
filelock.Remove(absToPath)
|
||||
}
|
||||
if err = filelock.Move(absFromPath, absToPath); nil != err {
|
||||
msg := fmt.Sprintf(Conf.Language(5), fromBox.Name, fromPath, err)
|
||||
logging.LogErrorf("move [path=%s] in box [%s] failed: %s", fromPath, fromBoxID, err)
|
||||
logging.LogErrorf("move [path=%s] in box [%s] failed: %s", fromPath, fromBox.ID, err)
|
||||
err = errors.New(msg)
|
||||
return
|
||||
}
|
||||
|
@ -1096,32 +1109,68 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
|
|||
return
|
||||
}
|
||||
|
||||
tree, err = LoadTree(fromBoxID, newPath)
|
||||
tree, err = LoadTree(fromBox.ID, newPath)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
moveTree(tree)
|
||||
} else {
|
||||
absFromPath := filepath.Join(util.DataDir, fromBoxID, fromPath)
|
||||
absToPath := filepath.Join(util.DataDir, toBoxID, newPath)
|
||||
absFromPath := filepath.Join(util.DataDir, fromBox.ID, fromPath)
|
||||
absToPath := filepath.Join(util.DataDir, toBox.ID, newPath)
|
||||
if err = filelock.Move(absFromPath, absToPath); nil != err {
|
||||
msg := fmt.Sprintf(Conf.Language(5), fromBox.Name, fromPath, err)
|
||||
logging.LogErrorf("move [path=%s] in box [%s] failed: %s", fromPath, fromBoxID, err)
|
||||
logging.LogErrorf("move [path=%s] in box [%s] failed: %s", fromPath, fromBox.ID, err)
|
||||
err = errors.New(msg)
|
||||
return
|
||||
}
|
||||
|
||||
tree, err = LoadTree(toBoxID, newPath)
|
||||
tree, err = LoadTree(toBox.ID, newPath)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
moveTree(tree)
|
||||
moveSorts(tree.ID, fromBoxID, toBoxID)
|
||||
moveSorts(tree.ID, fromBox.ID, toBox.ID)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func MoveDocs(fromPaths []string, toPath string) (err error) {
|
||||
util.PushEndlessProgress(Conf.Language(116))
|
||||
|
||||
util.PushEndlessProgress(Conf.Language(113))
|
||||
sql.WaitForWritingDatabase()
|
||||
util.ReloadUI()
|
||||
return
|
||||
}
|
||||
|
||||
func filterFromPaths(fromPaths []string, toPath string) (retFromPaths []string) {
|
||||
fromPaths = append(fromPaths, toPath)
|
||||
retFromPaths = filterSelfChildDocs(fromPaths)
|
||||
return
|
||||
}
|
||||
|
||||
func filterSelfChildDocs(paths []string) (ret []string) {
|
||||
sort.Slice(paths, func(i, j int) bool { return len(paths[i]) < len(paths[j]) })
|
||||
|
||||
dirs := map[string]string{}
|
||||
for _, fromPath := range paths {
|
||||
dir := strings.TrimSuffix(fromPath, ".sy")
|
||||
existParent := false
|
||||
for _, d := range dirs {
|
||||
if strings.HasPrefix(d, fromPath) {
|
||||
existParent = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if existParent {
|
||||
continue
|
||||
}
|
||||
dirs[dir] = fromPath
|
||||
ret = append(ret, fromPath)
|
||||
}
|
||||
cache.ClearDocsIAL()
|
||||
IncSync()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1133,7 +1182,40 @@ func RemoveDoc(boxID, p string) (err error) {
|
|||
}
|
||||
|
||||
WaitForWritingFiles()
|
||||
tree, err := LoadTree(boxID, p)
|
||||
err = removeDoc(box, p)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
IncSync()
|
||||
return
|
||||
}
|
||||
|
||||
func RemoveDocs(paths []string) (err error) {
|
||||
util.PushEndlessProgress(Conf.Language(116))
|
||||
|
||||
paths = filterSelfChildDocs(paths)
|
||||
var ids []string
|
||||
for _, p := range paths {
|
||||
ids = append(ids, strings.TrimSuffix(path.Base(p), ".sy"))
|
||||
}
|
||||
|
||||
boxes := getBoxesByPaths(ids)
|
||||
WaitForWritingFiles()
|
||||
for p, box := range boxes {
|
||||
err = removeDoc(box, p)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
util.PushEndlessProgress(Conf.Language(113))
|
||||
sql.WaitForWritingDatabase()
|
||||
util.ReloadUI()
|
||||
return
|
||||
}
|
||||
|
||||
func removeDoc(box *Box, p string) (err error) {
|
||||
tree, err := LoadTree(box.ID, p)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
@ -1144,13 +1226,13 @@ func RemoveDoc(boxID, p string) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
historyPath := filepath.Join(historyDir, boxID, p)
|
||||
absPath := filepath.Join(util.DataDir, boxID, p)
|
||||
historyPath := filepath.Join(historyDir, box.ID, p)
|
||||
absPath := filepath.Join(util.DataDir, box.ID, p)
|
||||
if err = filelock.Copy(absPath, historyPath); nil != err {
|
||||
return errors.New(fmt.Sprintf(Conf.Language(70), box.Name, absPath, err))
|
||||
}
|
||||
|
||||
copyDocAssetsToDataAssets(boxID, p)
|
||||
copyDocAssetsToDataAssets(box.ID, p)
|
||||
|
||||
rootID := tree.ID
|
||||
dir := path.Dir(p)
|
||||
|
@ -1178,7 +1260,7 @@ func RemoveDoc(boxID, p string) (err error) {
|
|||
sql.RemoveTreePathQueue(box.ID, childrenDir)
|
||||
|
||||
if "/" != dir {
|
||||
others, err := os.ReadDir(filepath.Join(util.DataDir, boxID, dir))
|
||||
others, err := os.ReadDir(filepath.Join(util.DataDir, box.ID, dir))
|
||||
if nil == err && 1 > len(others) {
|
||||
box.Remove(dir)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue