forked from 77media/video-flow
预发分支
This commit is contained in:
commit
c5065c7c68
@ -1,4 +1,5 @@
|
|||||||
import { ApiResponse } from './common';
|
import { ApiResponse } from './common';
|
||||||
|
import { BASE_URL } from './constants';
|
||||||
import { post } from './request';
|
import { post } from './request';
|
||||||
|
|
||||||
// 资源列表请求参数
|
// 资源列表请求参数
|
||||||
@ -20,5 +21,5 @@ export interface Resource {
|
|||||||
|
|
||||||
// 获取资源列表 - 按创建者筛选
|
// 获取资源列表 - 按创建者筛选
|
||||||
export const getResourcesList = async (data: ResourcesListRequest): Promise<ApiResponse<Resource[]>> => {
|
export const getResourcesList = async (data: ResourcesListRequest): Promise<ApiResponse<Resource[]>> => {
|
||||||
return post<ApiResponse<Resource[]>>('https://movieflow.api.huiying.video/resources/list', data);
|
return post<ApiResponse<Resource[]>>(BASE_URL+'/resources/list', data);
|
||||||
};
|
};
|
||||||
66
api/serversetting.ts
Normal file
66
api/serversetting.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { BASE_URL } from "./constants";
|
||||||
|
|
||||||
|
// 获取路演配置数据
|
||||||
|
export const fetchRoadshowConfigs = async () => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('开始请求接口数据...');
|
||||||
|
const response = await fetch(BASE_URL + '/serversetting/roadshow-configs', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({}),
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`接口请求失败: HTTP ${response.status} - ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
console.log('接口返回数据:', result);
|
||||||
|
|
||||||
|
// 验证返回数据格式
|
||||||
|
if (result.code !== 0) {
|
||||||
|
throw new Error(`接口错误: ${result.message || '未知错误'} (code: ${result.code})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.successful) {
|
||||||
|
throw new Error(`接口调用不成功: ${result.message || '未知原因'}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.data || !Array.isArray(result.data) || result.data.length === 0) {
|
||||||
|
throw new Error('接口返回数据格式错误或为空');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证数据结构
|
||||||
|
let validData: any[] = [];
|
||||||
|
result.data.forEach((item: any) => {
|
||||||
|
if (item) {
|
||||||
|
validData.push(JSON.parse(item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (validData.length === 0) {
|
||||||
|
throw new Error('接口返回的数据格式不正确');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('成功获取并验证接口数据:', validData);
|
||||||
|
return validData;
|
||||||
|
|
||||||
|
} catch (error: unknown) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (error instanceof Error && error.name === 'AbortError') {
|
||||||
|
throw new Error('接口请求超时,请检查网络连接');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error('接口请求失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -15,18 +15,17 @@ interface Role {
|
|||||||
|
|
||||||
interface CharacterTabContentProps {
|
interface CharacterTabContentProps {
|
||||||
taskSketch: any[];
|
taskSketch: any[];
|
||||||
currentSketchIndex: number;
|
currentRoleIndex: number;
|
||||||
onSketchSelect: (index: number) => void;
|
onSketchSelect: (index: number) => void;
|
||||||
roles: Role[];
|
roles: Role[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CharacterTabContent({
|
export function CharacterTabContent({
|
||||||
taskSketch,
|
taskSketch,
|
||||||
currentSketchIndex,
|
currentRoleIndex,
|
||||||
onSketchSelect,
|
onSketchSelect,
|
||||||
roles = []
|
roles = []
|
||||||
}: CharacterTabContentProps) {
|
}: CharacterTabContentProps) {
|
||||||
const [selectedCharacterIndex, setSelectedCharacterIndex] = useState(0);
|
|
||||||
const [isReplaceModalOpen, setIsReplaceModalOpen] = useState(false);
|
const [isReplaceModalOpen, setIsReplaceModalOpen] = useState(false);
|
||||||
const [activeReplaceMethod, setActiveReplaceMethod] = useState('upload');
|
const [activeReplaceMethod, setActiveReplaceMethod] = useState('upload');
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
@ -80,7 +79,7 @@ export function CharacterTabContent({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 获取当前选中的角色
|
// 获取当前选中的角色
|
||||||
const currentRole = roles[selectedCharacterIndex];
|
const currentRole = roles[currentRoleIndex];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
@ -98,9 +97,9 @@ export function CharacterTabContent({
|
|||||||
className={cn(
|
className={cn(
|
||||||
'relative flex-shrink-0 w-24 rounded-lg overflow-hidden cursor-pointer',
|
'relative flex-shrink-0 w-24 rounded-lg overflow-hidden cursor-pointer',
|
||||||
'aspect-[9/16]',
|
'aspect-[9/16]',
|
||||||
selectedCharacterIndex === index ? 'ring-2 ring-blue-500' : 'hover:ring-2 hover:ring-blue-500/50'
|
currentRoleIndex === index ? 'ring-2 ring-blue-500' : 'hover:ring-2 hover:ring-blue-500/50'
|
||||||
)}
|
)}
|
||||||
onClick={() => setSelectedCharacterIndex(index)}
|
onClick={() => onSketchSelect(index)}
|
||||||
whileHover={{ scale: 1.05 }}
|
whileHover={{ scale: 1.05 }}
|
||||||
whileTap={{ scale: 0.95 }}
|
whileTap={{ scale: 0.95 }}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -46,6 +46,7 @@ export function EditModal({
|
|||||||
}: EditModalProps) {
|
}: EditModalProps) {
|
||||||
const [activeTab, setActiveTab] = useState(activeEditTab);
|
const [activeTab, setActiveTab] = useState(activeEditTab);
|
||||||
const [currentIndex, setCurrentIndex] = useState(currentSketchIndex);
|
const [currentIndex, setCurrentIndex] = useState(currentSketchIndex);
|
||||||
|
const [currentRoleIndex, setCurrentRoleIndex] = useState(0);
|
||||||
|
|
||||||
// 当 activeEditTab 改变时更新 activeTab
|
// 当 activeEditTab 改变时更新 activeTab
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -61,6 +62,14 @@ export function EditModal({
|
|||||||
return parseInt(tabId) > parseInt(taskStatus);
|
return parseInt(tabId) > parseInt(taskStatus);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hanldeChangeSelect = (index: number) => {
|
||||||
|
if (activeTab === '2') {
|
||||||
|
setCurrentRoleIndex(index);
|
||||||
|
} else {
|
||||||
|
setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const renderTabContent = () => {
|
const renderTabContent = () => {
|
||||||
switch (activeTab) {
|
switch (activeTab) {
|
||||||
case '1':
|
case '1':
|
||||||
@ -68,15 +77,15 @@ export function EditModal({
|
|||||||
<ScriptTabContent
|
<ScriptTabContent
|
||||||
taskSketch={taskSketch}
|
taskSketch={taskSketch}
|
||||||
currentSketchIndex={currentIndex}
|
currentSketchIndex={currentIndex}
|
||||||
onSketchSelect={onSketchSelect}
|
onSketchSelect={hanldeChangeSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case '2':
|
case '2':
|
||||||
return (
|
return (
|
||||||
<CharacterTabContent
|
<CharacterTabContent
|
||||||
taskSketch={taskSketch}
|
taskSketch={taskSketch}
|
||||||
currentSketchIndex={currentIndex}
|
currentRoleIndex={currentRoleIndex}
|
||||||
onSketchSelect={onSketchSelect}
|
onSketchSelect={hanldeChangeSelect}
|
||||||
roles={roles}
|
roles={roles}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -85,7 +94,7 @@ export function EditModal({
|
|||||||
<VideoTabContent
|
<VideoTabContent
|
||||||
taskSketch={sketchVideo}
|
taskSketch={sketchVideo}
|
||||||
currentSketchIndex={currentIndex}
|
currentSketchIndex={currentIndex}
|
||||||
onSketchSelect={onSketchSelect}
|
onSketchSelect={hanldeChangeSelect}
|
||||||
isPlaying={false}
|
isPlaying={false}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -324,62 +324,14 @@ export function VideoTabContent({
|
|||||||
src={sketches[currentSketchIndex]?.url}
|
src={sketches[currentSketchIndex]?.url}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
loop
|
loop
|
||||||
autoPlay
|
autoPlay={false}
|
||||||
playsInline
|
playsInline
|
||||||
|
controls
|
||||||
muted={isMuted}
|
muted={isMuted}
|
||||||
onTimeUpdate={handleTimeUpdate}
|
onTimeUpdate={handleTimeUpdate}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 视频控制层 */}
|
|
||||||
<div className="absolute inset-0 bg-black/30 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
||||||
<div className="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/60 to-transparent">
|
|
||||||
{/* 进度条 */}
|
|
||||||
<div className="w-full h-1 bg-white/20 rounded-full mb-4 cursor-pointer"
|
|
||||||
onClick={(e) => {
|
|
||||||
const rect = e.currentTarget.getBoundingClientRect();
|
|
||||||
const x = e.clientX - rect.left;
|
|
||||||
const percentage = (x / rect.width) * 100;
|
|
||||||
if (videoPlayerRef.current) {
|
|
||||||
videoPlayerRef.current.currentTime = (percentage / 100) * videoPlayerRef.current.duration;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<motion.div
|
|
||||||
className="h-full bg-blue-500 rounded-full"
|
|
||||||
style={{ width: `${progress}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 控制按钮 */}
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<motion.button
|
|
||||||
className="p-2 rounded-full hover:bg-white/10"
|
|
||||||
onClick={() => setIsPlaying(!isPlaying)}
|
|
||||||
whileHover={{ scale: 1.1 }}
|
|
||||||
whileTap={{ scale: 0.9 }}
|
|
||||||
>
|
|
||||||
{isPlaying ? (
|
|
||||||
<Pause className="w-5 h-5" />
|
|
||||||
) : (
|
|
||||||
<Play className="w-5 h-5" />
|
|
||||||
)}
|
|
||||||
</motion.button>
|
|
||||||
|
|
||||||
<motion.button
|
|
||||||
className="p-2 rounded-full hover:bg-white/10"
|
|
||||||
onClick={() => setIsMuted(!isMuted)}
|
|
||||||
whileHover={{ scale: 1.1 }}
|
|
||||||
whileTap={{ scale: 0.9 }}
|
|
||||||
>
|
|
||||||
{isMuted ? (
|
|
||||||
<VolumeX className="w-5 h-5" />
|
|
||||||
) : (
|
|
||||||
<Volume2 className="w-5 h-5" />
|
|
||||||
)}
|
|
||||||
</motion.button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* 操作按钮 */}
|
{/* 操作按钮 */}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { fetchRoadshowConfigs } from "@/api/serversetting";
|
||||||
|
|
||||||
// 5组mock数据 - 保留作为fallback数据
|
// 5组mock数据 - 保留作为fallback数据
|
||||||
export const MOCK_DATA = [
|
export const MOCK_DATA = [
|
||||||
{
|
{
|
||||||
@ -49,76 +51,10 @@ export const MOCK_DATA = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// 从接口获取数据的函数
|
|
||||||
export const fetchMockDataFromAPI = async () => {
|
|
||||||
const controller = new AbortController();
|
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log('开始请求接口数据...');
|
|
||||||
const response = await fetch('https://movieflow.api.huiying.video/serversetting/roadshow-configs', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
}),
|
|
||||||
signal: controller.signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`接口请求失败: HTTP ${response.status} - ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
console.log('接口返回数据:', result);
|
|
||||||
|
|
||||||
// 验证返回数据格式
|
|
||||||
if (result.code !== 0) {
|
|
||||||
throw new Error(`接口错误: ${result.message || '未知错误'} (code: ${result.code})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.successful) {
|
|
||||||
throw new Error(`接口调用不成功: ${result.message || '未知原因'}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.data || !Array.isArray(result.data) || result.data.length === 0) {
|
|
||||||
throw new Error('接口返回数据格式错误或为空');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证数据结构
|
|
||||||
let validData: any[] = [];
|
|
||||||
result.data.forEach((item: any) => {
|
|
||||||
if (item) {
|
|
||||||
validData.push(JSON.parse(item));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (validData.length === 0) {
|
|
||||||
throw new Error('接口返回的数据格式不正确');
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('成功获取并验证接口数据:', validData);
|
|
||||||
return validData;
|
|
||||||
|
|
||||||
} catch (error: unknown) {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
|
|
||||||
if (error instanceof Error && error.name === 'AbortError') {
|
|
||||||
throw new Error('接口请求超时,请检查网络连接');
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error('接口请求失败:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 异步获取随机数据 - 从接口或fallback到本地数据
|
// 异步获取随机数据 - 从接口或fallback到本地数据
|
||||||
export const getRandomMockData = async () => {
|
export const getRandomMockData = async () => {
|
||||||
try {
|
try {
|
||||||
const apiData = await fetchMockDataFromAPI();
|
const apiData = await fetchRoadshowConfigs();
|
||||||
const randomIndex = Math.floor(Math.random() * apiData.length);
|
const randomIndex = Math.floor(Math.random() * apiData.length);
|
||||||
const selectedData = apiData[randomIndex];
|
const selectedData = apiData[randomIndex];
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
import { BASE_URL } from "@/api/constants";
|
||||||
|
|
||||||
// Mock Google OAuth configuration
|
// Mock Google OAuth configuration
|
||||||
const GOOGLE_CLIENT_ID = '1016208801816-qtvcvki2jobmcin1g4e7u4sotr0p8g3u.apps.googleusercontent.com';
|
const GOOGLE_CLIENT_ID = '1016208801816-qtvcvki2jobmcin1g4e7u4sotr0p8g3u.apps.googleusercontent.com';
|
||||||
const GOOGLE_REDIRECT_URI = typeof window !== 'undefined'
|
const GOOGLE_REDIRECT_URI = typeof window !== 'undefined'
|
||||||
? 'https://movieflow.api.huiying.video/users/oauth/callback'
|
? BASE_URL+'/users/oauth/callback'
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { BASE_URL } from './api/constants';
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
eslint: {
|
eslint: {
|
||||||
@ -39,7 +40,7 @@ const nextConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: '/api/resources/:path*',
|
source: '/api/resources/:path*',
|
||||||
destination: 'https://movieflow.api.huiying.video/:path*',
|
destination: BASE_URL+'/:path*',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user