feat(frontend): 搭建桌面端基础界面与 Workspace

- App Shell 壳层:主导航、次导航、Title Bar、状态栏
- Vault 入口页:最近 Vault、打开/创建 Vault、AI Core 状态
- Workspace 与文件树:浏览、打开、创建笔记/文件夹
- Design Token:浅色/深色主题 CSS Variables
- Contracts / Service / Store / Router 基础架构
- 统一 ApiClient 与 SseClient,对接后端 API 契约
This commit is contained in:
2026-08-29 10:36:16 +08:00
parent 694c1b27c1
commit f9efc4f4a2
42 changed files with 8044 additions and 167 deletions
+78
View File
@@ -0,0 +1,78 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { SearchResult, SearchRequest } from '@/contracts'
import * as searchService from '@/services/searchService'
export const useSearchStore = defineStore('search', () => {
const query = ref('')
const mode = ref<'fts' | 'vector' | 'hybrid'>('hybrid')
const results = ref<SearchResult[]>([])
const total = ref(0)
const isSearching = ref(false)
const selectedIndex = ref(0)
const recentQueries = ref<string[]>(['红黑树', '死锁', 'TCP三次握手'])
const error = ref<string | null>(null)
const vectorUnavailable = ref(false)
async function doSearch(request: SearchRequest) {
query.value = request.query
mode.value = request.mode || 'hybrid'
isSearching.value = true
error.value = null
try {
const resp = await searchService.searchMock(request.query, request.mode || 'hybrid')
results.value = resp.results
total.value = resp.total
selectedIndex.value = 0
} catch (e: any) {
error.value = e.message || '搜索失败'
results.value = []
total.value = 0
} finally {
isSearching.value = false
}
if (request.query && !recentQueries.value.includes(request.query)) {
recentQueries.value.unshift(request.query)
if (recentQueries.value.length > 10) recentQueries.value.pop()
}
}
function clearResults() {
results.value = []
query.value = ''
total.value = 0
selectedIndex.value = 0
error.value = null
}
function selectNext() {
if (selectedIndex.value < results.value.length - 1) selectedIndex.value++
}
function selectPrev() {
if (selectedIndex.value > 0) selectedIndex.value--
}
function setMode(m: 'fts' | 'vector' | 'hybrid') {
mode.value = m
}
return {
query,
mode,
results,
total,
isSearching,
selectedIndex,
recentQueries,
error,
vectorUnavailable,
doSearch,
clearResults,
selectNext,
selectPrev,
setMode,
}
})