refactor(web): adopt modular Vue architecture and isolate bearer sessions

This commit is contained in:
eezd committed 2026-10-25 20:47:32 +08:00
1 parent 1b2b2b12ff
commit 3cd641a380
379 files changed
+19736 -8712

No files matched your search

+91
View File
@@ -0,0 +1,91 @@
import type { Entry, TreeIcon } from '@@/apis/vault/type'
import { computed, reactive, ref, shallowRef, watch } from 'vue'
import { basename, dirname, hiddenAssetPath } from '../../common/utils/paths'
import { rangePaths } from './selection'
import { ws } from './workspace'
export interface TreeNode {
name: string
path: string
dir: boolean
icon?: TreeIcon
children: TreeNode[]
}
const collator = new Intl.Collator('zh-CN', { numeric: true, sensitivity: 'base' })
function build(entries: Entry[]): TreeNode[] {
const root: TreeNode[] = []
const dirs = new Map<string, TreeNode>()
// 先按路径排序,保证父目录先于子项出现
for (const e of [...entries].sort((a, b) => a.path.length - b.path.length)) {
const node: TreeNode = { name: basename(e.path), path: e.path, dir: e.dir, icon: e.icon, children: [] }
if (e.dir) dirs.set(e.path, node)
;(dirs.get(dirname(e.path))?.children ?? root).push(node)
}
const sort = (nodes: TreeNode[]) => {
nodes.sort((a, b) => (a.dir !== b.dir ? (a.dir ? -1 : 1) : collator.compare(a.name, b.name)))
nodes.forEach(n => sort(n.children))
}
sort(root)
return root
}
export const tree = computed(() => build(ws.entries.filter(entry => !hiddenAssetPath(entry.path))))
export const treeIconTarget = shallowRef<{ path: string, anchor: HTMLElement } | null>(null)
// null 表示无接收目标,空字符串表示根目录;文件行指向它的直接父目录。
export const dropDirectory = ref<string | null>(null)
const EXPANDED_KEY = 'rawmark.expanded'
export const expanded = reactive(new Set<string>(JSON.parse(localStorage.getItem(EXPANDED_KEY) ?? '[]') as string[]))
watch(expanded, () => localStorage.setItem(EXPANDED_KEY, JSON.stringify([...expanded])))
// 打开或新建笔记时展开其所在的各级目录
watch(
() => [ws.active, ws.renaming, ws.image],
(paths) => {
for (const p of paths) {
for (let d = p ? dirname(p) : ''; d; d = dirname(d)) expanded.add(d)
}
},
)
export const selectedPaths = reactive(new Set<string>())
let anchor: string | null = null
export const visiblePaths = computed(() => {
const paths: string[] = []
const visit = (nodes: TreeNode[]) => {
for (const node of nodes) {
paths.push(node.path); if (expanded.has(node.path)) visit(node.children)
}
}
visit(tree.value)
return paths
})
watch(visiblePaths, (paths) => {
const visible = new Set(paths)
for (const path of selectedPaths) {
if (!visible.has(path)) selectedPaths.delete(path)
}
if (anchor && !visible.has(anchor)) anchor = null
})
export function selectPath(path: string, event?: Pick<MouseEvent, 'ctrlKey' | 'metaKey' | 'shiftKey'>) {
const additive = event?.ctrlKey || event?.metaKey
if (event?.shiftKey) {
const range = rangePaths(visiblePaths.value, anchor, path)
if (!additive) selectedPaths.clear()
range.forEach(item => selectedPaths.add(item))
return
}
if (!additive) selectedPaths.clear()
if (additive && selectedPaths.has(path)) selectedPaths.delete(path)
else selectedPaths.add(path)
anchor = path
}
export function contextSelection(path: string) {
if (!selectedPaths.has(path)) selectPath(path)
}