| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- import { useEffect, useState } from 'react'
- import { getAllConfigCodes } from './recall'
- /**
- * 召回维度配置 (configCode -> 中文标签)
- *
- * 数据源: video-vector 后端 GET /videoSearch/getAllConfigCodes
- * 启动时拉一次, 进程内缓存, 全部消费方共享.
- *
- * 在请求未返回前先吐出 SAFE_FALLBACK 避免 UI 闪烁; 拉到后切换到真实数据.
- * 命名约定:
- * VIDEO_* 视频解构维度 (选题/灵感点/关键点/目的点)
- * RESULT_LOG_* AI 识别维度 (选题/主题/关键词/口播)
- */
- const SAFE_FALLBACK: Record<string, string> = {
- VIDEO_TOPIC: '选题',
- VIDEO_INSPIRATION: '灵感点',
- }
- let cache: Record<string, string> | null = null
- let pending: Promise<Record<string, string>> | null = null
- function fetchOnce(): Promise<Record<string, string>> {
- if (cache) return Promise.resolve(cache)
- if (!pending) {
- pending = getAllConfigCodes()
- .then((d) => {
- cache = d
- return d
- })
- .catch((e) => {
- pending = null
- throw e
- })
- }
- return pending
- }
- export function useConfigCodes(): Record<string, string> {
- const [data, setData] = useState<Record<string, string>>(cache ?? SAFE_FALLBACK)
- useEffect(() => {
- if (cache) {
- setData(cache)
- return
- }
- fetchOnce().then(setData).catch(() => {
- // 失败保持 fallback 不阻塞 UI
- })
- }, [])
- return data
- }
- /**
- * 真实字典是否已经从后端拉到 (而非 SAFE_FALLBACK).
- * 用于"URL 自动召回"等场景需要等真实全量字典再触发, 避免只跑 fallback 的 2 个维度.
- */
- export function useConfigCodesReady(): boolean {
- const [ready, setReady] = useState<boolean>(cache != null)
- useEffect(() => {
- if (cache != null) {
- setReady(true)
- return
- }
- fetchOnce()
- .then(() => setReady(true))
- .catch(() => {
- // 失败保持 false, 由调用方决定是否兜底
- })
- }, [])
- return ready
- }
- /** "全部" 维度的特殊 value, 提交时拆开成所有 configCode 并发调用 */
- export const ALL_CONFIG_CODE = '__ALL__'
- /**
- * "内容理解-旧" 组(原 AI识别维度)中文标签覆写
- * 后端字典里这组的标签是 "选题"/"主题",前端要展示成 "内容选题"/"视频主题"
- * 其他标签(关键词/口播等)直通
- */
- const RESULT_LOG_LABEL_OVERRIDE: Record<string, string> = {
- 选题: '内容选题',
- 主题: '视频主题',
- }
- /**
- * 给定 configCode + 字典, 返回前端实际展示的中文标签
- * dropdown / 表格 / Tag 全部走这里, 保持一致
- */
- export function getConfigDisplayLabel(
- code: string,
- codes: Record<string, string>,
- ): string {
- const raw = codes[code] ?? code
- if (code.startsWith('RESULT_LOG_')) return RESULT_LOG_LABEL_OVERRIDE[raw] ?? raw
- return raw
- }
- /** Tab2 文本召回 dropdown: 按前缀分组, 顶部加"全部"快捷项 */
- export function buildGroupedConfigOptions(codes: Record<string, string>) {
- const video: { label: string; value: string }[] = []
- const result: { label: string; value: string }[] = []
- const other: { label: string; value: string }[] = []
- for (const [code, label] of Object.entries(codes)) {
- if (code.startsWith('VIDEO_')) {
- video.push({ label, value: code })
- } else if (code.startsWith('RESULT_LOG_')) {
- result.push({ label: RESULT_LOG_LABEL_OVERRIDE[label] ?? label, value: code })
- } else {
- other.push({ label, value: code })
- }
- }
- type Item = { label: string; value: string }
- type Group = { label: string; options: Item[] }
- const items: (Item | Group)[] = []
- // 顶部独立"全部"项 — 无 group
- items.push({ label: '全部', value: ALL_CONFIG_CODE })
- if (video.length) items.push({ label: '视频解构维度', options: video })
- if (result.length) items.push({ label: '内容理解-旧', options: result })
- if (other.length) items.push({ label: '其他', options: other })
- return items
- }
- /**
- * 给定字典,返回所有"可召回"的 configCode 列表(排除 ALL)
- * 用于"全部"模式下并发调用
- */
- export function listAllConfigCodes(codes: Record<string, string>): string[] {
- return Object.keys(codes)
- }
- /**
- * DeconstructTree 节点中文类型 → configCode 映射
- * 用于判断"以此召回"按钮该传哪个 configCode, 以及该按钮是否生效
- * (生效条件: 映射到的 configCode 在后端字典里存在)
- */
- export const TREE_NODE_TYPE_TO_CONFIG_CODE: Record<string, string> = {
- 选题: 'VIDEO_TOPIC',
- 灵感点: 'VIDEO_INSPIRATION',
- 关键点: 'VIDEO_KEYPOINT',
- 目的点: 'VIDEO_PURPOSE',
- }
|