update at 2026-02-07 15:51:08

This commit is contained in:
douboer
2026-02-07 15:51:08 +08:00
parent 0b2595b0e0
commit 91fcd79203
3 changed files with 57 additions and 2411 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -9,26 +9,45 @@ const searchKeyword = ref('')
const fontTree = computed(() => fontStore.fontTree) const fontTree = computed(() => fontStore.fontTree)
const normalizedSearchKeyword = computed(() => searchKeyword.value.trim().toLowerCase()) const normalizedSearchKeyword = computed(() => searchKeyword.value.trim().toLowerCase())
const isSelectedOnlyMode = computed(() => {
const keyword = normalizedSearchKeyword.value
return keyword.includes('选中') || keyword.includes('选择') || keyword.includes('已选')
})
const normalizedNameSearchKeyword = computed(() => {
return isSelectedOnlyMode.value ? '' : normalizedSearchKeyword.value
})
const hasSearchKeyword = computed(() => normalizedSearchKeyword.value.length > 0) const hasSearchKeyword = computed(() => normalizedSearchKeyword.value.length > 0)
function nodeHasMatch(node: (typeof fontTree.value)[number]): boolean {
if (node.type !== 'category') {
return false
}
const fontChildren = (node.children ?? []).filter((child) => {
return child.type === 'font' && !!child.fontInfo
})
const selectedFilteredChildren = isSelectedOnlyMode.value
? fontChildren.filter(child => !!child.fontInfo && fontStore.previewFontIds.has(child.fontInfo.id))
: fontChildren
if (normalizedNameSearchKeyword.value.length === 0) {
return selectedFilteredChildren.length > 0
}
const keyword = normalizedNameSearchKeyword.value
if (node.name.toLowerCase().includes(keyword)) {
return selectedFilteredChildren.length > 0
}
return selectedFilteredChildren.some(child => child.name.toLowerCase().includes(keyword))
}
const hasMatchedFonts = computed(() => { const hasMatchedFonts = computed(() => {
if (!hasSearchKeyword.value) { if (!hasSearchKeyword.value) {
return fontTree.value.length > 0 return fontTree.value.length > 0
} }
const keyword = normalizedSearchKeyword.value return fontTree.value.some(node => nodeHasMatch(node))
return fontTree.value.some((node) => {
if (node.type !== 'category') {
return false
}
if (node.name.toLowerCase().includes(keyword)) {
return true
}
return (node.children ?? []).some(child => {
return child.type === 'font' && child.name.toLowerCase().includes(keyword)
})
})
}) })
</script> </script>
@@ -66,7 +85,8 @@ const hasMatchedFonts = computed(() => {
<FontTree <FontTree
v-else v-else
:nodes="fontTree" :nodes="fontTree"
:search-keyword="normalizedSearchKeyword" :search-keyword="normalizedNameSearchKeyword"
:selected-only="isSelectedOnlyMode"
/> />
</div> </div>
</template> </template>

View File

@@ -8,11 +8,13 @@ import { useFontStore } from '../stores/fontStore'
const props = defineProps<{ const props = defineProps<{
nodes: FontTreeNode[] nodes: FontTreeNode[]
searchKeyword?: string searchKeyword?: string
selectedOnly?: boolean
}>() }>()
const fontStore = useFontStore() const fontStore = useFontStore()
const normalizedSearchKeyword = computed(() => (props.searchKeyword ?? '').trim().toLowerCase()) const normalizedSearchKeyword = computed(() => (props.searchKeyword ?? '').trim().toLowerCase())
const isSearchMode = computed(() => normalizedSearchKeyword.value.length > 0) const isSearchMode = computed(() => normalizedSearchKeyword.value.length > 0)
const isFilterMode = computed(() => isSearchMode.value || props.selectedOnly === true)
type FontLeafNode = FontTreeNode & { fontInfo: NonNullable<FontTreeNode['fontInfo']> } type FontLeafNode = FontTreeNode & { fontInfo: NonNullable<FontTreeNode['fontInfo']> }
@@ -24,16 +26,20 @@ function getVisibleChildren(node: FontTreeNode): FontLeafNode[] {
const fontChildren = node.children.filter( const fontChildren = node.children.filter(
(child): child is FontLeafNode => child.type === 'font' && !!child.fontInfo, (child): child is FontLeafNode => child.type === 'font' && !!child.fontInfo,
) )
const selectedFilteredChildren = props.selectedOnly
? fontChildren.filter(child => fontStore.previewFontIds.has(child.fontInfo.id))
: fontChildren
if (!isSearchMode.value) { if (!isSearchMode.value) {
return fontChildren return selectedFilteredChildren
} }
const keyword = normalizedSearchKeyword.value const keyword = normalizedSearchKeyword.value
if (node.name.toLowerCase().includes(keyword)) { if (node.name.toLowerCase().includes(keyword)) {
return fontChildren return selectedFilteredChildren
} }
return fontChildren.filter(child => child.name.toLowerCase().includes(keyword)) return selectedFilteredChildren.filter(child => child.name.toLowerCase().includes(keyword))
} }
function shouldRenderCategory(node: FontTreeNode): boolean { function shouldRenderCategory(node: FontTreeNode): boolean {
@@ -41,11 +47,11 @@ function shouldRenderCategory(node: FontTreeNode): boolean {
} }
function isCategoryExpanded(node: FontTreeNode): boolean { function isCategoryExpanded(node: FontTreeNode): boolean {
return isSearchMode.value ? true : !!node.expanded return isFilterMode.value ? true : !!node.expanded
} }
function toggleExpand(node: FontTreeNode) { function toggleExpand(node: FontTreeNode) {
if (isSearchMode.value) { if (isFilterMode.value) {
return return
} }
const next = !node.expanded const next = !node.expanded
@@ -115,14 +121,14 @@ function handleCategorySelectAll(node: FontTreeNode, event: Event) {
<button <button
@click="toggleExpand(node)" @click="toggleExpand(node)"
class="tree-toggle" class="tree-toggle"
:disabled="isSearchMode" :disabled="isFilterMode"
> >
<img <img
v-if="isCategoryExpanded(node)" v-if="isCategoryExpanded(node)"
src="../assets/icons/zhedie.svg" src="../assets/icons/zhedie.svg"
alt="收起" alt="收起"
class="w-[15px] h-[15px]" class="w-[15px] h-[15px]"
:class="{ 'opacity-70': isSearchMode }" :class="{ 'opacity-70': isFilterMode }"
/> />
<img <img
v-else v-else
@@ -137,7 +143,7 @@ function handleCategorySelectAll(node: FontTreeNode, event: Event) {
<div <div
@click="toggleExpand(node)" @click="toggleExpand(node)"
class="text-base font-medium text-black flex-1 ml-2" class="text-base font-medium text-black flex-1 ml-2"
:class="isSearchMode ? 'cursor-default' : 'cursor-pointer'" :class="isFilterMode ? 'cursor-default' : 'cursor-pointer'"
> >
{{ node.name }}{{ getCategoryFontCount(node) }}字体 {{ node.name }}{{ getCategoryFontCount(node) }}字体
</div> </div>