175 lines
4.9 KiB
TypeScript
175 lines
4.9 KiB
TypeScript
import { useMemo, useState, useCallback } from "react"
|
|
import { message } from "antd"
|
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
|
|
import { getTitles, createTitle, updateTitle, deleteTitle } from "@/api/titles"
|
|
import type { TitleData, CategoryItem, Frequency } from "../types/titleLibrary"
|
|
import { toTitleData, copyToClipboard } from "../utils/titleLibrary"
|
|
import { ALL_CATEGORY_ID, FREQUENCY_THRESHOLDS } from "../constants/titleLibrary"
|
|
|
|
export const useTitleLibrary = () => {
|
|
const queryClient = useQueryClient()
|
|
|
|
/* 分类 */
|
|
const [activeCatId, setActiveCatId] = useState<string>(ALL_CATEGORY_ID)
|
|
|
|
/* 数据获取 */
|
|
const { data: apiTitles = [] } = useQuery({
|
|
queryKey: ["titles"],
|
|
queryFn: getTitles,
|
|
staleTime: 30_000,
|
|
})
|
|
|
|
const titles: TitleData[] = useMemo(() => apiTitles.map(toTitleData), [apiTitles])
|
|
|
|
/* 动态派生分类 */
|
|
const categories: CategoryItem[] = useMemo(() => {
|
|
const cats = new Map<string, number>()
|
|
apiTitles.forEach((t) => {
|
|
const cat = t.category || "未分类"
|
|
cats.set(cat, (cats.get(cat) || 0) + 1)
|
|
})
|
|
return [
|
|
{ id: ALL_CATEGORY_ID, name: "全部标题", count: apiTitles.length },
|
|
...Array.from(cats.entries()).map(([name, count]) => ({
|
|
id: `cat-${name}`,
|
|
name,
|
|
count,
|
|
})),
|
|
]
|
|
}, [apiTitles])
|
|
|
|
/* CRUD mutations */
|
|
const createMutation = useMutation({
|
|
mutationFn: (content: string) => createTitle({ content }),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ["titles"] })
|
|
},
|
|
onError: () => message.error("创建标题失败"),
|
|
})
|
|
|
|
const updateMutation = useMutation({
|
|
mutationFn: ({ id, content }: { id: string; content: string }) => updateTitle(id, { content }),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ["titles"] })
|
|
},
|
|
onError: () => message.error("更新标题失败"),
|
|
})
|
|
|
|
const deleteMutation = useMutation({
|
|
mutationFn: (id: string) => deleteTitle(id),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ["titles"] })
|
|
},
|
|
onError: () => message.error("删除标题失败"),
|
|
})
|
|
|
|
/* 筛选状态 */
|
|
const [searchText, setSearchText] = useState("")
|
|
const [filterType, setFilterType] = useState<string>("all")
|
|
const [filterIndustry, setFilterIndustry] = useState<string>("all")
|
|
const [filterFrequency, setFilterFrequency] = useState<Frequency>("all")
|
|
|
|
/* 派生:筛选后的标题列表 */
|
|
const activeCategory = categories.find((c) => c.id === activeCatId)
|
|
|
|
const filteredTitles = useMemo(() => {
|
|
let list = titles
|
|
|
|
/* 按分类过滤 */
|
|
if (activeCatId !== ALL_CATEGORY_ID) {
|
|
const catName = activeCategory?.name || ""
|
|
if (catName) {
|
|
list = list.filter((t) => t.category === catName)
|
|
}
|
|
}
|
|
|
|
/* 按类型筛选 */
|
|
if (filterType !== "all") {
|
|
list = list.filter((t) => t.type === filterType)
|
|
}
|
|
|
|
/* 按行业筛选 */
|
|
if (filterIndustry !== "all") {
|
|
list = list.filter((t) => t.industry === filterIndustry)
|
|
}
|
|
|
|
/* 按使用频率筛选 */
|
|
if (filterFrequency !== "all") {
|
|
switch (filterFrequency) {
|
|
case "high":
|
|
list = list.filter((t) => t.usageCount >= FREQUENCY_THRESHOLDS.high)
|
|
break
|
|
case "medium":
|
|
list = list.filter(
|
|
(t) =>
|
|
t.usageCount >= FREQUENCY_THRESHOLDS.medium &&
|
|
t.usageCount < FREQUENCY_THRESHOLDS.high,
|
|
)
|
|
break
|
|
case "low":
|
|
list = list.filter((t) => t.usageCount < FREQUENCY_THRESHOLDS.medium)
|
|
break
|
|
}
|
|
}
|
|
|
|
/* 搜索 */
|
|
if (searchText.trim()) {
|
|
const q = searchText.trim().toLowerCase()
|
|
list = list.filter((t) => t.content.toLowerCase().includes(q))
|
|
}
|
|
|
|
return list
|
|
}, [titles, activeCatId, activeCategory, filterType, filterIndustry, filterFrequency, searchText])
|
|
|
|
/* 操作:收藏 */
|
|
const handleToggleFavorite = useCallback((_id: string) => {
|
|
message.info("收藏功能即将上线")
|
|
}, [])
|
|
|
|
/* 操作:复制 */
|
|
const handleCopy = useCallback(async (title: TitleData) => {
|
|
const ok = await copyToClipboard(title.content)
|
|
if (ok) {
|
|
message.success("已复制到剪贴板")
|
|
} else {
|
|
message.error("复制失败")
|
|
}
|
|
}, [])
|
|
|
|
/* 操作:删除 */
|
|
const handleDelete = useCallback(
|
|
(id: string) => {
|
|
deleteMutation.mutate(id)
|
|
message.success("标题已删除")
|
|
},
|
|
[deleteMutation],
|
|
)
|
|
|
|
return {
|
|
/* 状态 */
|
|
titles,
|
|
categories,
|
|
activeCatId,
|
|
activeCategory,
|
|
filteredTitles,
|
|
searchText,
|
|
filterType,
|
|
filterIndustry,
|
|
filterFrequency,
|
|
/* mutations */
|
|
createMutation,
|
|
updateMutation,
|
|
deleteMutation,
|
|
/* setters */
|
|
setActiveCatId,
|
|
setSearchText,
|
|
setFilterType,
|
|
setFilterIndustry,
|
|
setFilterFrequency,
|
|
/* handlers */
|
|
handleToggleFavorite,
|
|
handleCopy,
|
|
handleDelete,
|
|
}
|
|
}
|