From 018cf590183b448fbf76528a4d7bd7772e3313ed Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Sun, 26 Jul 2026 15:45:30 +0800 Subject: [PATCH 1/5] =?UTF-8?q?refactor(title-library):=20=E4=B8=89?= =?UTF-8?q?=E9=98=B6=E6=AE=B5=E9=87=8D=E6=9E=84=20TitleLibrary=EF=BC=88736?= =?UTF-8?q?=E2=86=92137=E8=A1=8C,=20-81%=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1 - 抽类型/常量/工具函数: - types/titleLibrary.ts: TitleType, Industry, Frequency, TitleData, CategoryItem - constants/titleLibrary.ts: 筛选选项、频率阈值、常量 - utils/titleLibrary.ts: typeLabel, toTitleData, copyToClipboard Phase 2 - 抽 6 个 UI 组件: - TitleCard: 标题卡片(编辑/收藏/复制/删除) - CategorySidebar: 左侧分类列表 - FilterBar: 顶部筛选栏(搜索+类型+行业+频率) - TitleGrid: 标题网格 + 空状态 - CreateTitleModal: 新建标题弹窗 - AIGenerateModal: AI 生成标题弹窗 Phase 3 - 抽 3 个业务 Hook: - useTitleLibrary: 数据获取、分类派生、CRUD、筛选逻辑 - useTitleEdit: 编辑状态管理 + 新建标题逻辑 - useTitleAI: AI 生成标题逻辑(mock) 主文件 736→137 行,共新增 12 个文件 --- apps/web/src/pages/titles/TitleLibrary.tsx | 815 +++--------------- .../title-library/AIGenerateModal.tsx | 121 +++ .../title-library/CategorySidebar.tsx | 44 + .../title-library/CreateTitleModal.tsx | 89 ++ .../components/title-library/FilterBar.tsx | 76 ++ .../components/title-library/TitleCard.tsx | 121 +++ .../components/title-library/TitleGrid.tsx | 63 ++ .../pages/titles/constants/titleLibrary.ts | 34 + apps/web/src/pages/titles/hooks/useTitleAI.ts | 83 ++ .../src/pages/titles/hooks/useTitleEdit.ts | 86 ++ .../src/pages/titles/hooks/useTitleLibrary.ts | 174 ++++ .../src/pages/titles/types/titleLibrary.ts | 20 + .../src/pages/titles/utils/titleLibrary.ts | 49 ++ apps/web/src/test/pages/TitleLibrary.test.tsx | 12 + 14 files changed, 1080 insertions(+), 707 deletions(-) create mode 100644 apps/web/src/pages/titles/components/title-library/AIGenerateModal.tsx create mode 100644 apps/web/src/pages/titles/components/title-library/CategorySidebar.tsx create mode 100644 apps/web/src/pages/titles/components/title-library/CreateTitleModal.tsx create mode 100644 apps/web/src/pages/titles/components/title-library/FilterBar.tsx create mode 100644 apps/web/src/pages/titles/components/title-library/TitleCard.tsx create mode 100644 apps/web/src/pages/titles/components/title-library/TitleGrid.tsx create mode 100644 apps/web/src/pages/titles/constants/titleLibrary.ts create mode 100644 apps/web/src/pages/titles/hooks/useTitleAI.ts create mode 100644 apps/web/src/pages/titles/hooks/useTitleEdit.ts create mode 100644 apps/web/src/pages/titles/hooks/useTitleLibrary.ts create mode 100644 apps/web/src/pages/titles/types/titleLibrary.ts create mode 100644 apps/web/src/pages/titles/utils/titleLibrary.ts diff --git a/apps/web/src/pages/titles/TitleLibrary.tsx b/apps/web/src/pages/titles/TitleLibrary.tsx index 5becdbca6..9a102fa01 100644 --- a/apps/web/src/pages/titles/TitleLibrary.tsx +++ b/apps/web/src/pages/titles/TitleLibrary.tsx @@ -4,731 +4,132 @@ * 支持:标题卡片展示、AI 生成标题、复制/编辑/删除、收藏、分类筛选、搜索 * 对接后端真实 API(GET/POST/PUT/DELETE /titles) */ -import React, { useMemo, useState, useCallback } from "react" -import { Modal as AntModal, message, Popconfirm } from "antd" -import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query" -import { - PlusOutlined, - SearchOutlined, - DeleteOutlined, - EditOutlined, - CopyOutlined, - CheckOutlined, - RobotOutlined, - StarOutlined, - StarFilled, - FileTextOutlined, -} from "@ant-design/icons" -import { Button, Input, Select } from "@/components/ui" -import { getTitles, createTitle, updateTitle, deleteTitle, type TitleItem } from "@/api/titles" +import React from "react" +import { useTitleLibrary } from "./hooks/useTitleLibrary" +import { useTitleEdit } from "./hooks/useTitleEdit" +import { useTitleAI } from "./hooks/useTitleAI" +import { CategorySidebar } from "./components/title-library/CategorySidebar" +import { FilterBar } from "./components/title-library/FilterBar" +import { TitleGrid } from "./components/title-library/TitleGrid" +import { CreateTitleModal } from "./components/title-library/CreateTitleModal" +import { AIGenerateModal } from "./components/title-library/AIGenerateModal" import "./titles.css" -/* ============================================================ - * 类型 - * ============================================================ */ -type TitleType = "hot" | "normal" | "creative" -type Industry = "general" | "food" | "tech" | "beauty" | "education" | "travel" -type Frequency = "all" | "high" | "medium" | "low" - -interface TitleData { - id: string - content: string - type: TitleType - industry: Industry - category: string - usageCount: number - isFavorited: boolean - createdAt: string -} - -/** 后端 TitleItem → 前端 TitleData 映射 */ -const toTitleData = (item: TitleItem): TitleData => ({ - id: item.id, - content: item.content, - type: (item.category as TitleType) || "normal", - industry: "general", - category: item.category || "未分类", - usageCount: 0, - isFavorited: false, - createdAt: item.created_at?.slice(0, 10) || "", -}) - -/* ============================================================ - * 工具函数 - * ============================================================ */ -const typeLabel = (type: TitleType): string => { - switch (type) { - case "hot": - return "爆款" - case "normal": - return "常规" - case "creative": - return "创意" - } -} - -/** 复制文本到剪贴板 */ -const copyToClipboard = async (text: string): Promise => { - try { - await navigator.clipboard.writeText(text) - return true - } catch { - /* 降级方案 */ - const textarea = document.createElement("textarea") - textarea.value = text - textarea.style.position = "fixed" - textarea.style.opacity = "0" - document.body.appendChild(textarea) - textarea.select() - try { - document.execCommand("copy") - return true - } catch { - return false - } finally { - document.body.removeChild(textarea) - } - } -} - -/* ============================================================ - * TitleCard 组件 - * ============================================================ */ -const TitleCard: React.FC<{ - title: TitleData - isEditing: boolean - editText: string - onEditChange: (text: string) => void - onStartEdit: () => void - onSaveEdit: () => void - onCancelEdit: () => void - onCopy: () => void - onDelete: () => void - onToggleFavorite: () => void -}> = ({ - title, - isEditing, - editText, - onEditChange, - onStartEdit, - onSaveEdit, - onCancelEdit, - onCopy, - onDelete, - onToggleFavorite, -}) => { - return ( -
- {/* 收藏按钮 */} - - - {/* 标题文本 / 编辑区 */} - {isEditing ? ( -