返回博客列表

VueHooks工具

2026-01-29
3 min read
vue

Fuse搜索工具

Fuse搜索工具

import Fuse from 'fuse.js'
import type {IFuseOptions} from "fuse.js";


export default function useFuse<T>(list: T[], options: IFuseOptions<T>) {

    const fuse = new Fuse<T>(list, options)

    function search(keyword: string, limit: number = Infinity): T[] {
        if (!keyword) return list
        const results = fuse.search(keyword, {limit})
        return results.map(r => r.item)
    }

    /**
     * fuseSearch - 单次模糊搜索工具函数
     *
     * @param list 数据源
     * @param keyword 搜索关键词
     * @param options Fuse.js 配置
     * @param limit 限制返回数量,默认 Infinity
     */
    function fuseSearch<T>(
        list: T[],
        keyword: string,
        options: IFuseOptions<T>,
        limit: number = Infinity
    ): T[] {
        if (!keyword) return list

        const fuse = new Fuse<T>(list, options)
        const results = fuse.search(keyword, {limit})
        return results.map(r => r.item)
    }

    return {search, fuseSearch}
}

import { defineEventHandler } from 'h3'
import { db } from '~~/server/utils/db'

function capitalize(str: string): string {
    if (!str) return ''
    return str.charAt(0).toUpperCase() + str.slice(1)
}

interface BlogItem {
    id: number
    title: string
    summary: string
    subCategory: string
    author: string
    category: string
}

interface SubCategoryGroup {
    subCategory: string
    list: BlogItem[]
}

interface CategoryGroup {
    category: string
    subCategories: SubCategoryGroup[]
}

export default defineWrappedResponseHandler(
    defineEventHandler(async () => {
        type DbRow = {
            id: number
            title: string
            author: string
            tags: string
            summary: string
            sub_category: string
            category: string
        }

        const blogs: DbRow[] = await db('blog_posts as b')
            .select(
                'b.id',
                'b.title',
                'b.author',
                'b.tags',
                'b.summary',
                'b.sub_category',
                'b.category'
            )
            .where('b.is_published', 1)

        const temp: Record<string, Record<string, BlogItem[]>> = {}

        for (const row of blogs) {
            const category = row.category || '未分类'
            const sub = capitalize(row.sub_category || '未分组')

            if (!temp[category]) temp[category] = {}
            if (!temp[category][sub]) temp[category][sub] = []

            temp[category][sub].push({
                id: row.id,
                title: row.title,
                summary: row.summary,
                subCategory: sub,
                author: row.author,
                category: category
            })
        }

        // 排序 + 转换成数组结构
        const result: CategoryGroup[] = Object.entries(temp).map(
            ([category, subMap]) => {
                const subCategories: SubCategoryGroup[] = Object.entries(subMap).map(
                    ([subCategory, list]) => ({
                        subCategory,
                        list: list.sort((a, b) =>
                            a.title.localeCompare(b.title, 'zh-CN', { sensitivity: 'base' })
                        )
                    })
                )

                return {
                    category,
                    subCategories
                }
            }
        )

        return result
    })
)


import {defineEventHandler} from 'h3'
import {db} from '~~/server/utils/db'

function capitalize(str: string): string {
    if (!str) return ''
    return str.charAt(0).toUpperCase() + str.slice(1)
}

export default defineWrappedResponseHandler(defineEventHandler(async (event) => {
    const query = getQuery(event) // 从 URL 获取参数,比如 ?category=tech
    const category = query.category as string
    const blogs = await db('blog_posts as b')
        .select('b.id', 'b.title', 'b.author', 'b.tags', 'b.summary', 'b.sub_category')
        .where('b.category', category).andWhere('b.is_published', 1)
    const grouped: Record<string, {
        id: number;
        title: string,
        summary: string,
        sub_category: string,
        author: string
    }[]> = {}
    for (const row of blogs) {
        const tag = capitalize(row.tags)
        if (!grouped[tag]) {
            grouped[tag] = []
        }
        grouped[tag].push({
            id: row.id,
            title: row.title,
            summary: row.summary,
            sub_category: row.sub_category,
            author: row.author
        })
    }
    // 对每个 tag 下的数组按 title 排序
    for (const tag in grouped) {
        grouped[tag].sort((a, b) => a.title.localeCompare(b.title, 'zh-CN'))
    }
    return grouped;
}))


// api/blog/star.post.ts
//  http POST http://localhost:3000/api/star id:=1 action=inc
export default defineWrappedResponseHandler(
    defineEventHandler(async (event) => {
        const body = await readBody<{ id: number; action: 'inc' | 'dec' }>(event)
        if (!body.id) {
            throw new Error('缺少参数 id')
        }
        if (!['inc', 'dec'].includes(body.action)) {
            throw new Error('action 必须是 inc 或 dec')
        }
        // 更新 SQL
        const query = db('blog_posts').where('id', body.id)
        if (body.action === 'inc') {
            await query.increment('star', 1)
        } else {
            await query.decrement('star', 1)
        }
        // 返回最新 star 数
        const updated = await db('blog_posts')
            .select('id', 'title', 'star')
            .where('id', body.id)
            .first()
        return updated
    })
)

返回博客列表
最后更新于 2026-01-29
想法或问题?在 GitHub Issue 下方参与讨论
去评论