forked from 77media/video-flow
- [x] Chatbox-快捷操作按钮
This commit is contained in:
parent
1e4be3b1a0
commit
684f29056e
@ -3,6 +3,7 @@ import { Image as ImageIcon, Send, Trash2, ArrowUp } from "lucide-react";
|
||||
import { MessageBlock } from "./types";
|
||||
import { useUploadFile } from "@/app/service/domain/service";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { QuickActionTags, QuickAction } from "./QuickActionTags";
|
||||
|
||||
// 防抖函数
|
||||
function debounce<T extends (...args: any[]) => void>(func: T, wait: number) {
|
||||
@ -233,6 +234,19 @@ export function InputBar({ onSend, setVideoPreview, initialVideoUrl, initialVide
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 快捷操作标签组 */}
|
||||
<QuickActionTags
|
||||
onTagClick={(action: QuickAction) => {
|
||||
// 将标签文本添加到输入框
|
||||
setText(action.label);
|
||||
// 聚焦输入框并触发高度调整
|
||||
if (textareaRef.current) {
|
||||
textareaRef.current.focus();
|
||||
adjustHeight();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<motion.div
|
||||
layout
|
||||
className="px-3 m-3 border border-gray-700 rounded-[2rem]"
|
||||
|
||||
123
components/SmartChatBox/QuickActionTags.tsx
Normal file
123
components/SmartChatBox/QuickActionTags.tsx
Normal file
@ -0,0 +1,123 @@
|
||||
import React, { useRef, useCallback } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
|
||||
/** 快捷操作标签的数据结构 */
|
||||
export interface QuickAction {
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
/** 预设的快捷操作标签 */
|
||||
export const DEFAULT_QUICK_ACTIONS: QuickAction[] = [
|
||||
{ id: 'weather', label: 'Change video scene weather' },
|
||||
{ id: 'character', label: 'Change a character in the video' },
|
||||
{ id: 'costume', label: 'Change the clothing of a character in the video' },
|
||||
{ id: 'scene', label: 'Change video scene background' },
|
||||
{ id: 'action', label: 'Change character action' }
|
||||
];
|
||||
|
||||
interface QuickActionTagsProps {
|
||||
/** 自定义标签列表,如果不提供则使用默认标签 */
|
||||
actions?: QuickAction[];
|
||||
/** 点击标签时的回调函数 */
|
||||
onTagClick: (action: QuickAction) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷操作标签组组件
|
||||
* @param props 组件属性
|
||||
* @returns JSX.Element
|
||||
*/
|
||||
export function QuickActionTags({ actions = DEFAULT_QUICK_ACTIONS, onTagClick }: QuickActionTagsProps) {
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const scroll = useCallback((direction: 'left' | 'right') => {
|
||||
const container = scrollContainerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const scrollAmount = 200; // 每次滚动的距离
|
||||
const targetScroll = container.scrollLeft + (direction === 'left' ? -scrollAmount : scrollAmount);
|
||||
|
||||
container.scrollTo({
|
||||
left: targetScroll,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-alt="quick-action-tags"
|
||||
className="relative flex items-center px-3 py-2 group"
|
||||
>
|
||||
{/* 左侧渐变遮罩 */}
|
||||
<div className="absolute left-0 top-0 bottom-0 w-12 bg-gradient-to-r from-black/20 to-transparent pointer-events-none z-10" />
|
||||
|
||||
{/* 左滚动按钮 */}
|
||||
<motion.button
|
||||
onClick={() => scroll('left')}
|
||||
className="absolute left-1 z-20 p-1 rounded-full bg-black/30 text-white/80
|
||||
backdrop-blur-sm opacity-0 group-hover:opacity-100 transition-opacity
|
||||
hover:bg-black/40"
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
data-alt="scroll-left"
|
||||
>
|
||||
<ChevronLeft size={16} />
|
||||
</motion.button>
|
||||
|
||||
{/* 标签滚动容器 */}
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
className="flex overflow-x-auto gap-2 no-scrollbar scroll-smooth"
|
||||
style={{
|
||||
msOverflowStyle: 'none', /* IE and Edge */
|
||||
scrollbarWidth: 'none', /* Firefox */
|
||||
}}
|
||||
>
|
||||
{actions.map((action) => (
|
||||
<motion.button
|
||||
key={action.id}
|
||||
onClick={() => onTagClick(action)}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
className="flex-none px-[8px] py-[3px] rounded-full text-[10px] text-white/80
|
||||
backdrop-blur-md bg-white/10 border border-white/20
|
||||
hover:bg-white/20 hover:text-white
|
||||
transition-colors duration-200
|
||||
shadow-[0_4px_6px_-1px_rgba(0,0,0,0.1),0_2px_4px_-1px_rgba(0,0,0,0.06)]"
|
||||
data-alt={`quick-action-${action.id}`}
|
||||
>
|
||||
{action.label}
|
||||
</motion.button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 右侧渐变遮罩 */}
|
||||
<div className="absolute right-0 top-0 bottom-0 w-12 bg-gradient-to-l from-black/20 to-transparent pointer-events-none z-10" />
|
||||
|
||||
{/* 右滚动按钮 */}
|
||||
<motion.button
|
||||
onClick={() => scroll('right')}
|
||||
className="absolute right-1 z-20 p-1 rounded-full bg-black/30 text-white/80
|
||||
backdrop-blur-sm opacity-0 group-hover:opacity-100 transition-opacity
|
||||
hover:bg-black/40"
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
data-alt="scroll-right"
|
||||
>
|
||||
<ChevronRight size={16} />
|
||||
</motion.button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 添加全局样式来隐藏滚动条
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
@ -168,7 +168,7 @@ export default function SmartChatBox({
|
||||
</div>
|
||||
|
||||
{/* Loading indicator */}
|
||||
{isLoading && !hasMore && (
|
||||
{isLoading && (
|
||||
<div className="flex justify-start space-x-1 p-2">
|
||||
<span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce [animation-delay:-0.3s]"></span>
|
||||
<span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce [animation-delay:-0.15s]"></span>
|
||||
|
||||
@ -17,6 +17,7 @@ export function useWorkflowData() {
|
||||
const episodeId = searchParams.get('episodeId') || '';
|
||||
const from = searchParams.get('from') || '';
|
||||
const token = localStorage.getItem('token') || '';
|
||||
const useid = JSON.parse(localStorage.getItem("currentUser") || '{}').id || NaN;
|
||||
|
||||
let tempTaskObject = useRef<TaskObject>({
|
||||
title: '',
|
||||
@ -114,7 +115,7 @@ export function useWorkflowData() {
|
||||
|
||||
const generateEditPlan = useCallback(async () => {
|
||||
await getGenerateEditPlan({ project_id: episodeId });
|
||||
window.open(`https://smartcut.huiying.video/ai-editor/${episodeId}?token=${token}`, '_self');
|
||||
window.open(`https://smartcut.huiying.video/ai-editor/${episodeId}?token=${token}&userid=${useid}`, '_self');
|
||||
}, [episodeId]);
|
||||
|
||||
// useEffect(() => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user