项目概况

VueHooks工具

2026-01-293 min read未分类
vue

Fuse搜索工具

text
1import Fuse from 'fuse.js' 2import type {IFuseOptions} from "fuse.js"; 3 4 5export default function useFuse<T>(list: T[], options: IFuseOptions<T>) { 6 7 const fuse = new Fuse<T>(list, options) 8 9 function search(keyword: string, limit: number = Infinity): T[] { 10 if (!keyword) return list 11 const results = fuse.search(keyword, {limit}) 12 return results.map(r => r.item) 13 } 14 15 /** 16 * fuseSearch - 单次模糊搜索工具函数 17 * 18 * @param list 数据源 19 * @param keyword 搜索关键词 20 * @param options Fuse.js 配置 21 * @param limit 限制返回数量,默认 Infinity 22 */ 23 function fuseSearch<T>( 24 list: T[], 25 keyword: string, 26 options: IFuseOptions<T>, 27 limit: number = Infinity 28 ): T[] { 29 if (!keyword) return list 30 31 const fuse = new Fuse<T>(list, options) 32 const results = fuse.search(keyword, {limit}) 33 return results.map(r => r.item) 34 } 35 36 return {search, fuseSearch} 37} 38
text
1import { defineEventHandler } from 'h3' 2import { db } from '~~/server/utils/db' 3 4function capitalize(str: string): string { 5 if (!str) return '' 6 return str.charAt(0).toUpperCase() + str.slice(1) 7} 8 9interface BlogItem { 10 id: number 11 title: string 12 summary: string 13 subCategory: string 14 author: string 15 category: string 16} 17 18interface SubCategoryGroup { 19 subCategory: string 20 list: BlogItem[] 21} 22 23interface CategoryGroup { 24 category: string 25 subCategories: SubCategoryGroup[] 26} 27 28export default defineWrappedResponseHandler( 29 defineEventHandler(async () => { 30 type DbRow = { 31 id: number 32 title: string 33 author: string 34 tags: string 35 summary: string 36 sub_category: string 37 category: string 38 } 39 40 const blogs: DbRow[] = await db('blog_posts as b') 41 .select( 42 'b.id', 43 'b.title', 44 'b.author', 45 'b.tags', 46 'b.summary', 47 'b.sub_category', 48 'b.category' 49 ) 50 .where('b.is_published', 1) 51 52 const temp: Record<string, Record<string, BlogItem[]>> = {} 53 54 for (const row of blogs) { 55 const category = row.category || '未分类' 56 const sub = capitalize(row.sub_category || '未分组') 57 58 if (!temp[category]) temp[category] = {} 59 if (!temp[category][sub]) temp[category][sub] = [] 60 61 temp[category][sub].push({ 62 id: row.id, 63 title: row.title, 64 summary: row.summary, 65 subCategory: sub, 66 author: row.author, 67 category: category 68 }) 69 } 70 71 // 排序 + 转换成数组结构 72 const result: CategoryGroup[] = Object.entries(temp).map( 73 ([category, subMap]) => { 74 const subCategories: SubCategoryGroup[] = Object.entries(subMap).map( 75 ([subCategory, list]) => ({ 76 subCategory, 77 list: list.sort((a, b) => 78 a.title.localeCompare(b.title, 'zh-CN', { sensitivity: 'base' }) 79 ) 80 }) 81 ) 82 83 return { 84 category, 85 subCategories 86 } 87 } 88 ) 89 90 return result 91 }) 92) 93 94 95import {defineEventHandler} from 'h3' 96import {db} from '~~/server/utils/db' 97 98function capitalize(str: string): string { 99 if (!str) return '' 100 return str.charAt(0).toUpperCase() + str.slice(1) 101} 102 103export default defineWrappedResponseHandler(defineEventHandler(async (event) => { 104 const query = getQuery(event) // 从 URL 获取参数,比如 ?category=tech 105 const category = query.category as string 106 const blogs = await db('blog_posts as b') 107 .select('b.id', 'b.title', 'b.author', 'b.tags', 'b.summary', 'b.sub_category') 108 .where('b.category', category).andWhere('b.is_published', 1) 109 const grouped: Record<string, { 110 id: number; 111 title: string, 112 summary: string, 113 sub_category: string, 114 author: string 115 }[]> = {} 116 for (const row of blogs) { 117 const tag = capitalize(row.tags) 118 if (!grouped[tag]) { 119 grouped[tag] = [] 120 } 121 grouped[tag].push({ 122 id: row.id, 123 title: row.title, 124 summary: row.summary, 125 sub_category: row.sub_category, 126 author: row.author 127 }) 128 } 129 // 对每个 tag 下的数组按 title 排序 130 for (const tag in grouped) { 131 grouped[tag].sort((a, b) => a.title.localeCompare(b.title, 'zh-CN')) 132 } 133 return grouped; 134})) 135 136 137// api/blog/star.post.ts 138// http POST http://localhost:3000/api/star id:=1 action=inc 139export default defineWrappedResponseHandler( 140 defineEventHandler(async (event) => { 141 const body = await readBody<{ id: number; action: 'inc' | 'dec' }>(event) 142 if (!body.id) { 143 throw new Error('缺少参数 id') 144 } 145 if (!['inc', 'dec'].includes(body.action)) { 146 throw new Error('action 必须是 inc 或 dec') 147 } 148 // 更新 SQL 149 const query = db('blog_posts').where('id', body.id) 150 if (body.action === 'inc') { 151 await query.increment('star', 1) 152 } else { 153 await query.decrement('star', 1) 154 } 155 // 返回最新 star 数 156 const updated = await db('blog_posts') 157 .select('id', 'title', 'star') 158 .where('id', body.id) 159 .first() 160 return updated 161 }) 162) 163