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 { MessageBlock } from "./types";
|
||||||
import { useUploadFile } from "@/app/service/domain/service";
|
import { useUploadFile } from "@/app/service/domain/service";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
|
import { QuickActionTags, QuickAction } from "./QuickActionTags";
|
||||||
|
|
||||||
// 防抖函数
|
// 防抖函数
|
||||||
function debounce<T extends (...args: any[]) => void>(func: T, wait: number) {
|
function debounce<T extends (...args: any[]) => void>(func: T, wait: number) {
|
||||||
@ -233,6 +234,19 @@ export function InputBar({ onSend, setVideoPreview, initialVideoUrl, initialVide
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 快捷操作标签组 */}
|
||||||
|
<QuickActionTags
|
||||||
|
onTagClick={(action: QuickAction) => {
|
||||||
|
// 将标签文本添加到输入框
|
||||||
|
setText(action.label);
|
||||||
|
// 聚焦输入框并触发高度调整
|
||||||
|
if (textareaRef.current) {
|
||||||
|
textareaRef.current.focus();
|
||||||
|
adjustHeight();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
layout
|
layout
|
||||||
className="px-3 m-3 border border-gray-700 rounded-[2rem]"
|
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>
|
</div>
|
||||||
|
|
||||||
{/* Loading indicator */}
|
{/* Loading indicator */}
|
||||||
{isLoading && !hasMore && (
|
{isLoading && (
|
||||||
<div className="flex justify-start space-x-1 p-2">
|
<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.3s]"></span>
|
||||||
<span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce [animation-delay:-0.15s]"></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 episodeId = searchParams.get('episodeId') || '';
|
||||||
const from = searchParams.get('from') || '';
|
const from = searchParams.get('from') || '';
|
||||||
const token = localStorage.getItem('token') || '';
|
const token = localStorage.getItem('token') || '';
|
||||||
|
const useid = JSON.parse(localStorage.getItem("currentUser") || '{}').id || NaN;
|
||||||
|
|
||||||
let tempTaskObject = useRef<TaskObject>({
|
let tempTaskObject = useRef<TaskObject>({
|
||||||
title: '',
|
title: '',
|
||||||
@ -114,7 +115,7 @@ export function useWorkflowData() {
|
|||||||
|
|
||||||
const generateEditPlan = useCallback(async () => {
|
const generateEditPlan = useCallback(async () => {
|
||||||
await getGenerateEditPlan({ project_id: episodeId });
|
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]);
|
}, [episodeId]);
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user