forked from 77media/video-flow
137 lines
4.8 KiB
TypeScript
137 lines
4.8 KiB
TypeScript
import React from 'react';
|
|
import * as Tooltip from '@radix-ui/react-tooltip';
|
|
import { characterInfoMap, sceneInfoMap, mockCharacterOptions, mockSceneOptions } from '@/app/model/enums';
|
|
import { motion } from 'framer-motion';
|
|
|
|
interface KeywordTextProps {
|
|
text: string;
|
|
className?: string;
|
|
id?: string;
|
|
}
|
|
|
|
const KeywordText: React.FC<KeywordTextProps> = ({ text, className = '', id }) => {
|
|
// 解析文本中的关键词
|
|
const parseText = (text: string) => {
|
|
const parts: React.ReactNode[] = [];
|
|
let currentIndex = 0;
|
|
|
|
// 匹配 #角色# 和 [场景]
|
|
const regex = /#([^#]+)#|\[([^\]]+)\]/g;
|
|
let match;
|
|
|
|
while ((match = regex.exec(text)) !== null) {
|
|
// 添加普通文本
|
|
if (match.index > currentIndex) {
|
|
parts.push(text.slice(currentIndex, match.index));
|
|
}
|
|
|
|
const [fullMatch, character, scene] = match;
|
|
if (character) {
|
|
// 角色关键词
|
|
const info = mockCharacterOptions.find(option => option.characterId === id);
|
|
if (info) {
|
|
parts.push(
|
|
<Tooltip.Provider key={match.index}>
|
|
<Tooltip.Root>
|
|
<Tooltip.Trigger asChild>
|
|
<motion.span
|
|
className="text-yellow-400 font-medium cursor-help"
|
|
whileHover={{ scale: 1.05 }}
|
|
transition={{ type: "spring", stiffness: 400, damping: 10 }}
|
|
>
|
|
#{character}#
|
|
</motion.span>
|
|
</Tooltip.Trigger>
|
|
<Tooltip.Portal>
|
|
<Tooltip.Content
|
|
className="z-[9999] bg-black/60 backdrop-blur-lg p-3 rounded-lg shadow-xl border border-white/10"
|
|
sideOffset={5}
|
|
>
|
|
<div className="flex items-start gap-3">
|
|
<img
|
|
src={info.image}
|
|
alt={character}
|
|
className="w-12 h-12 rounded-full object-cover"
|
|
/>
|
|
<div>
|
|
<div className="font-medium text-white">{character}</div>
|
|
<div className="text-sm text-gray-300">
|
|
{info.gender} · {info.age}岁
|
|
</div>
|
|
<div className="text-sm text-gray-400 mt-1">
|
|
{info.description}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Tooltip.Arrow className="fill-black/90" />
|
|
</Tooltip.Content>
|
|
</Tooltip.Portal>
|
|
</Tooltip.Root>
|
|
</Tooltip.Provider>
|
|
);
|
|
} else {
|
|
parts.push(<span key={match.index} className="text-yellow-400">#{character}#</span>);
|
|
}
|
|
} else if (scene) {
|
|
// 场景关键词
|
|
const info = mockSceneOptions.find(option => option.sceneId === id);
|
|
console.log('info', info);
|
|
if (info) {
|
|
parts.push(
|
|
<Tooltip.Provider key={match.index}>
|
|
<Tooltip.Root>
|
|
<Tooltip.Trigger asChild>
|
|
<motion.span
|
|
className="text-blue-400 font-medium cursor-help"
|
|
whileHover={{ scale: 1.05 }}
|
|
transition={{ type: "spring", stiffness: 400, damping: 10 }}
|
|
>
|
|
[{scene}]
|
|
</motion.span>
|
|
</Tooltip.Trigger>
|
|
<Tooltip.Portal>
|
|
<Tooltip.Content
|
|
className="z-[9999] bg-black/60 backdrop-blur-lg p-3 rounded-lg shadow-xl border border-white/10"
|
|
sideOffset={5}
|
|
>
|
|
<div className="w-48">
|
|
<img
|
|
src={info.image}
|
|
alt={scene}
|
|
className="w-full h-24 object-cover rounded-lg mb-2"
|
|
/>
|
|
<div className="font-medium text-white">{scene}</div>
|
|
<div className="text-sm text-gray-300 mt-1">
|
|
{info.location} · {info.time}
|
|
</div>
|
|
</div>
|
|
<Tooltip.Arrow className="fill-black/90" />
|
|
</Tooltip.Content>
|
|
</Tooltip.Portal>
|
|
</Tooltip.Root>
|
|
</Tooltip.Provider>
|
|
);
|
|
} else {
|
|
parts.push(<span key={match.index} className="text-blue-400">[{scene}]</span>);
|
|
}
|
|
}
|
|
|
|
currentIndex = match.index + fullMatch.length;
|
|
}
|
|
|
|
// 添加剩余文本
|
|
if (currentIndex < text.length) {
|
|
parts.push(text.slice(currentIndex));
|
|
}
|
|
|
|
return parts;
|
|
};
|
|
|
|
return (
|
|
<span className={className}>
|
|
{parseText(text)}
|
|
</span>
|
|
);
|
|
};
|
|
|
|
export default KeywordText;
|