update at 2026-02-07 15:51:08
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user