完成接口

This commit is contained in:
海龙 2025-08-03 15:07:32 +08:00
parent 7960573a12
commit e983a10037
9 changed files with 250 additions and 204 deletions

View File

@ -2,8 +2,8 @@ import { post } from './request';
import { ProjectTypeEnum } from '@/app/model/enums'; import { ProjectTypeEnum } from '@/app/model/enums';
import { ApiResponse } from '@/api/common'; import { ApiResponse } from '@/api/common';
import { BASE_URL } from './constants' import { BASE_URL } from './constants'
import { AITextEntity, RoleEntity, SceneEntity, ShotEntity, TagEntity, ContentItem } from '@/app/service/domain/Entities'; import { AITextEntity, RoleEntity, SceneEntity, ShotEntity, TagEntity } from '@/app/service/domain/Entities';
import { ScriptSlice } from "@/app/service/domain/valueObject"; import { ContentItem, LensType, ScriptSlice } from "@/app/service/domain/valueObject";
// API 响应类型 // API 响应类型
interface BaseApiResponse<T> { interface BaseApiResponse<T> {
@ -471,7 +471,7 @@ export const regenerateShot = async (request: {
/** 分镜ID */ /** 分镜ID */
shotId?: string; shotId?: string;
/** 镜头描述 */ /** 镜头描述 */
shotPrompt?: string; shotPrompt?: LensType[];
/** 对话内容 */ /** 对话内容 */
dialogueContent?: ContentItem[]; dialogueContent?: ContentItem[];
/** 角色ID替换参数格式为{oldId:string,newId:string}[] */ /** 角色ID替换参数格式为{oldId:string,newId:string}[] */
@ -575,7 +575,7 @@ export const getShotVideoScript = async (request: {
*/ */
export const generateScriptStream = async (request: { export const generateScriptStream = async (request: {
/** 剧本提示词 */ /** 剧本提示词 */
prompt: string; text: string;
}) => { }) => {
return post<ApiResponse<any>>('/text_to_script/generate_script_stream', request,{ return post<ApiResponse<any>>('/text_to_script/generate_script_stream', request,{
responseType: 'stream', responseType: 'stream',
@ -588,8 +588,10 @@ export const generateScriptStream = async (request: {
* @returns Promise<ApiResponse<应用结果>> * @returns Promise<ApiResponse<应用结果>>
*/ */
export const applyScriptToShot = async (request: { export const applyScriptToShot = async (request: {
/** 项目ID */
projectId: string;
/** 剧本*/ /** 剧本*/
script: string; scriptText: string;
}): Promise<ApiResponse<any>> => { }): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>('/movie/apply_script_to_shot', request); return post<ApiResponse<any>>('/movie/apply_script_to_shot', request);
}; };
@ -612,3 +614,36 @@ export const getProjectScript = async (request: {
scriptText: string; scriptText: string;
}>>('/movie/get_project_script', request); }>>('/movie/get_project_script', request);
}; };
/**
*
* @param request
* @returns Promise<ApiResponse<保存结果>>
*/
export const saveScript = async (request: {
/** 项目ID */
projectId: string;
/** 剧本文本 */
scriptText: string;
}): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>('/movie/save_script', request);
};
/**
*
* @param request
* @returns Promise<ApiResponse<{ projectId: string }>>
*/
export const createProject = async (request: {
/** 用户提示词 */
userPrompt: string;
/** 剧本内容 */
scriptContent: string;
}): Promise<ApiResponse<{
/** 项目ID */
projectId: string;
}>> => {
return post<ApiResponse<{
projectId: string;
}>>('/movie/create_project', request);
};

View File

@ -1,7 +1,8 @@
import { useState, useCallback, useMemo } from "react"; import { useState, useCallback, useMemo } from "react";
import { ScriptSlice } from "../domain/valueObject"; import { ScriptSlice } from "../domain/valueObject";
import { ScriptEditUseCase } from "../usecase/ScriptEditUseCase"; import { ScriptEditUseCase } from "../usecase/ScriptEditUseCase";
import { getProjectScript } from "../../../api/video_flow"; import { getProjectScript, saveScript as saveScriptAPI, createProject as createProjectAPI } from "../../../api/video_flow";
import { throttle } from "@/utils/tools";
/** /**
* Hook接口 * Hook接口
@ -13,113 +14,83 @@ export interface UseScriptService {
scriptText: string; scriptText: string;
/** 剧本片段列表 */ /** 剧本片段列表 */
scriptSlices: ScriptSlice[]; scriptSlices: ScriptSlice[];
/** 当前聚焦的剧本片段ID */
focusedSliceId: string;
/** 当前聚焦的剧本片段 */
focusedSlice: ScriptSlice | null;
/** 当前聚焦的剧本片段文本 */
scriptSliceText: string;
/** 用户提示词(可编辑) */ /** 用户提示词(可编辑) */
userPrompt: string; userPrompt: string;
/** 加载状态 */ /** 加载状态 */
loading: boolean; loading: boolean;
/** 错误信息 */ /** 项目ID */
error: string | null; projectId: string;
// 操作方法 // 操作方法
/** 获取剧本数据(用户提示词) */ /** 获取根据用户想法调用接口AI生成剧本(用户提示词) */
fetchScriptData: (prompt: string) => Promise<void>; fetchScriptData: (prompt: string) => Promise<void>;
/** 根据项目ID获取已存在的剧本数据 */ /** 根据项目ID获取已存在的剧本数据 */
fetchProjectScript: (projectId: string) => Promise<void>; fetchProjectScript: (projectId: string) => Promise<void>;
/** 设置当前聚焦的剧本片段 */
setFocusedSlice: (sliceId: string) => void;
/** 清除聚焦状态 */
clearFocusedSlice: () => void;
/** 快速更新当前聚焦的剧本片段文本(无防抖) */
updateScriptSliceText: (text: string, metaData?: any) => void;
/** 更新用户提示词 */ /** 更新用户提示词 */
updateUserPrompt: (prompt: string) => void; updateUserPrompt: (prompt: string) => void;
/** 重置剧本内容到初始状态 */ /** 重置剧本内容到初始状态 */
resetScript: () => void; resetScript: () => void;
/** AI生成剧本 */
generateScript: (prompt: string) => Promise<void>;
/** 应用剧本 */ /** 应用剧本 */
applyScript: () => Promise<void>; applyScript: () => Promise<void>;
/** 更新聚焦剧本片段 */ /** 中断剧本生成 */
UpdateFocusedSlice: (text: string) => void; abortGenerateScript: () => void;
/** 保存剧本 */
saveScript: () => Promise<void>;
/** 创建项目 */
createProject: () => Promise<void>;
} }
/** /**
* Hook * Hook
* *
* *
*/ */
export const useScriptService = (): UseScriptService => { export const useScriptService = (): UseScriptService => {
// 响应式状态 // 响应式状态
const [scriptText, setScriptText] = useState<string>(""); const [scriptText, setScriptText] = useState<string>("");
const [scriptSlices, setScriptSlices] = useState<ScriptSlice[]>([]); const [scriptSlices, setScriptSlices] = useState<ScriptSlice[]>([]);
const [focusedSliceId, setFocusedSliceId] = useState<string>("");
const [scriptSliceText, setScriptSliceText] = useState<string>("");
const [userPrompt, setUserPrompt] = useState<string>(""); const [userPrompt, setUserPrompt] = useState<string>("");
const [initialScriptText, setInitialScriptText] = useState<string>(""); const [initialScriptText, setInitialScriptText] = useState<string>("");
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null); const [projectId, setProjectId] = useState<string>("");
// UseCase实例 // UseCase实例
const [scriptEditUseCase, setScriptEditUseCase] = useState<ScriptEditUseCase | null>(null); const [scriptEditUseCase, setScriptEditUseCase] = useState<ScriptEditUseCase | null>(null);
// 防抖定时器
const [debounceTimer, setDebounceTimer] = useState<NodeJS.Timeout | null>(null);
const DEBOUNCE_DELAY = 300;
/** /**
* * ai生成剧本
*/
const focusedSlice = useMemo(() => {
return scriptSlices.find(slice => slice.id === focusedSliceId) || null;
}, [scriptSlices, focusedSliceId]);
/**
*
* @param prompt * @param prompt
*/ */
const fetchScriptData = useCallback(async (prompt: string): Promise<void> => { const fetchScriptData = useCallback(async (prompt: string): Promise<void> => {
try { try {
setLoading(true); setLoading(true);
setError(null);
// 清空当前状态 // 清空当前状态
setScriptText(""); setScriptText("");
setScriptSlices([]); setScriptSlices([]);
setFocusedSliceId("");
setScriptSliceText("");
// 更新用户提示词状态 // 更新用户提示词状态
setUserPrompt(prompt); setUserPrompt(prompt);
// 保存初始提示词(只在第一次获取时保存)
if (!initialScriptText) {
setInitialScriptText(prompt);
}
// 创建新的剧本编辑用例 // 创建新的剧本编辑用例
const newScriptEditUseCase = new ScriptEditUseCase(''); const newScriptEditUseCase = new ScriptEditUseCase('');
setScriptEditUseCase(newScriptEditUseCase); setScriptEditUseCase(newScriptEditUseCase);
// 调用AI生成剧本 // 调用AI生成剧本
await newScriptEditUseCase.generateScript(prompt); await newScriptEditUseCase.generateScript(prompt,throttle((newContent)=>{
// 获取生成的剧本文本
const generatedScriptText = newScriptEditUseCase.toString();
setScriptText(generatedScriptText);
// 获取剧本片段列表
const slices = newScriptEditUseCase.getScriptSlices();
setScriptSlices(slices);
// 获取生成的剧本文本 // 保存初始剧本文本(只在第一次获取时保存)
const generatedScriptText = newScriptEditUseCase.toString(); if (!initialScriptText) {
setScriptText(generatedScriptText); setInitialScriptText(generatedScriptText);
}
// 获取剧本片段列表 }));
const slices = newScriptEditUseCase.getScriptSlices();
setScriptSlices(slices);
} catch (error) { } catch (error) {
console.error('获取剧本数据失败:', error); console.error('获取剧本数据失败:', error);
setError(error instanceof Error ? error.message : '获取剧本数据失败');
throw error; throw error;
} finally { } finally {
setLoading(false); setLoading(false);
@ -133,13 +104,12 @@ export const useScriptService = (): UseScriptService => {
const fetchProjectScript = useCallback(async (projectId: string): Promise<void> => { const fetchProjectScript = useCallback(async (projectId: string): Promise<void> => {
try { try {
setLoading(true); setLoading(true);
setError(null);
// 清空当前状态 // 清空当前状态
setScriptText(""); setScriptText("");
setScriptSlices([]); setScriptSlices([]);
setFocusedSliceId("");
setScriptSliceText(""); // 设置项目ID
setProjectId(projectId);
// 调用API获取项目剧本数据 // 调用API获取项目剧本数据
const response = await getProjectScript({ projectId }); const response = await getProjectScript({ projectId });
@ -153,9 +123,9 @@ export const useScriptService = (): UseScriptService => {
// 更新用户提示词状态 // 更新用户提示词状态
setUserPrompt(prompt); setUserPrompt(prompt);
// 保存初始提示词(只在第一次获取时保存) // 保存初始剧本文本(只在第一次获取时保存)
if (!initialScriptText) { if (!initialScriptText) {
setInitialScriptText(prompt); setInitialScriptText(scriptText);
} }
// 创建新的剧本编辑用例并初始化数据 // 创建新的剧本编辑用例并初始化数据
@ -171,84 +141,12 @@ export const useScriptService = (): UseScriptService => {
} catch (error) { } catch (error) {
console.error('获取项目剧本数据失败:', error); console.error('获取项目剧本数据失败:', error);
setError(error instanceof Error ? error.message : '获取项目剧本数据失败');
throw error; throw error;
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [initialScriptText]); }, [initialScriptText]);
/**
*
* @param sliceId ID
*/
const setFocusedSlice = useCallback((sliceId: string): void => {
setFocusedSliceId(sliceId);
// 同步输入框文本为当前聚焦片段的文本
const focusedSlice = scriptSlices.find(slice => slice.id === sliceId);
if (focusedSlice) {
setScriptSliceText(focusedSlice.text);
} else {
setScriptSliceText("");
}
}, [scriptSlices]);
/**
*
*/
const clearFocusedSlice = useCallback((): void => {
setFocusedSliceId("");
setScriptSliceText("");
}, []);
/**
*
* @param text
* @param metaData
*/
const updateScriptSliceText = useCallback((text: string, metaData?: any): void => {
setScriptSliceText(text);
// 自动触发防抖更新值对象
// 清除之前的定时器
if (debounceTimer) {
clearTimeout(debounceTimer);
}
// 设置新的防抖定时器
const timer = setTimeout(() => {
UpdateFocusedSlice(text, metaData);
}, DEBOUNCE_DELAY);
setDebounceTimer(timer);
}, [debounceTimer]);
/**
*
* @param text
* @param metaData
*/
const UpdateFocusedSlice = useCallback((text: string, metaData?: any): void => {
if (!focusedSliceId || !scriptEditUseCase) {
return;
}
const success = scriptEditUseCase.updateScriptSlice(
focusedSliceId,
text,
metaData
);
if (success) {
// 更新本地片段列表
const slices = scriptEditUseCase.getScriptSlices();
setScriptSlices(slices);
}
}, [focusedSliceId, scriptEditUseCase]);
/** /**
* *
* @param prompt * @param prompt
@ -261,41 +159,16 @@ export const useScriptService = (): UseScriptService => {
* *
*/ */
const resetScript = useCallback((): void => { const resetScript = useCallback((): void => {
if (initialScriptText) { if (initialScriptText && scriptEditUseCase) {
// 重新调用AI生成剧本fetchScriptData会自动清空状态 // 重置剧本文本到初始状态
fetchScriptData(initialScriptText); setScriptText(initialScriptText);
// 更新现有剧本编辑用例的数据
scriptEditUseCase.updateScript(initialScriptText);
// 从UseCase获取解析后的剧本片段
const scriptSlices = scriptEditUseCase.getScriptSlices();
setScriptSlices(scriptSlices);
} }
}, [initialScriptText, fetchScriptData]); }, [initialScriptText, scriptEditUseCase]);
/**
* AI生成剧本
* @param prompt
*/
const generateScript = useCallback(async (prompt: string): Promise<void> => {
try {
setLoading(true);
setError(null);
if (!scriptEditUseCase) {
throw new Error("剧本编辑用例未初始化");
}
await scriptEditUseCase.generateScript(prompt);
// 更新片段列表(这里需要根据实际的流式数据处理逻辑来调整)
const slices = scriptEditUseCase.getScriptSlices();
setScriptSlices(slices);
} catch (error) {
console.error("AI生成剧本失败:", error);
setError(error instanceof Error ? error.message : "AI生成剧本失败");
throw error;
} finally {
setLoading(false);
}
}, [scriptEditUseCase]);
/** /**
* *
@ -303,44 +176,114 @@ export const useScriptService = (): UseScriptService => {
const applyScript = useCallback(async (): Promise<void> => { const applyScript = useCallback(async (): Promise<void> => {
try { try {
setLoading(true); setLoading(true);
setError(null);
if (!scriptEditUseCase) { if (!scriptEditUseCase) {
throw new Error("剧本编辑用例未初始化"); throw new Error("剧本编辑用例未初始化");
} }
await scriptEditUseCase.applyScript(); await scriptEditUseCase.applyScript(projectId);
} catch (error) { } catch (error) {
console.error("应用剧本失败:", error); console.error("应用剧本失败:", error);
setError(error instanceof Error ? error.message : "应用剧本失败");
throw error; throw error;
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [scriptEditUseCase, projectId]);
/**
*
*/
const abortGenerateScript = useCallback((): void => {
if (scriptEditUseCase) {
scriptEditUseCase.abortGenerateScript();
setLoading(false);
}
}, [scriptEditUseCase]); }, [scriptEditUseCase]);
/**
*
*/
const saveScript = useCallback(async (): Promise<void> => {
try {
setLoading(true);
if (!projectId) {
throw new Error("项目ID未设置");
}
if (!scriptEditUseCase) {
throw new Error("剧本编辑用例未初始化");
}
// 调用保存剧本接口
const scriptText = scriptEditUseCase.toString();
const response = await saveScriptAPI({ projectId, scriptText });
if (!response.successful) {
throw new Error(response.message || '保存剧本失败');
}
console.log("剧本保存成功");
} catch (error) {
console.error("保存剧本失败:", error);
throw error;
} finally {
setLoading(false);
}
}, [projectId, scriptEditUseCase]);
/**
*
* @throws {Error} -
*/
const createProject = useCallback(async (): Promise<void> => {
try {
setLoading(true);
// 直接使用当前state中的userPrompt和scriptText
const currentUserPrompt = userPrompt;
const currentScriptContent = scriptText;
const response = await createProjectAPI({
userPrompt: currentUserPrompt,
scriptContent: currentScriptContent
});
if (!response.successful) {
throw new Error(response.message || '创建项目失败');
}
const { projectId: newProjectId } = response.data;
setProjectId(newProjectId);
console.log("项目创建成功");
} catch (error) {
console.error("创建项目失败:", error);
throw error;
} finally {
setLoading(false);
}
}, [userPrompt, scriptText]);
return { return {
// 响应式状态 // 响应式状态
scriptText, scriptText,
scriptSlices, scriptSlices,
focusedSliceId,
focusedSlice,
scriptSliceText,
userPrompt, userPrompt,
loading, loading,
error, projectId,
// 操作方法 // 操作方法
fetchScriptData, fetchScriptData,
fetchProjectScript, fetchProjectScript,
setFocusedSlice,
clearFocusedSlice,
updateScriptSliceText,
updateUserPrompt, updateUserPrompt,
resetScript, resetScript,
generateScript,
applyScript, applyScript,
UpdateFocusedSlice abortGenerateScript,
saveScript,
createProject,
}; };
}; };

View File

@ -3,9 +3,8 @@ import {
ShotEntity, ShotEntity,
RoleEntity, RoleEntity,
SceneEntity, SceneEntity,
ContentItem,
} from "../domain/Entities"; } from "../domain/Entities";
import { ScriptSlice, ScriptValueObject } from "../domain/valueObject"; import { ContentItem, ScriptSlice, ScriptValueObject } from "../domain/valueObject";
import { ShotItem } from "../domain/Item"; import { ShotItem } from "../domain/Item";
import { ShotEditUseCase } from "../usecase/ShotEditUsecase"; import { ShotEditUseCase } from "../usecase/ShotEditUsecase";
import { import {

View File

@ -3,6 +3,8 @@
* *
*/ */
import { ContentItem, LensType } from "./valueObject";
/** /**
* *
*/ */
@ -66,13 +68,7 @@ export interface SceneEntity extends BaseEntity {
generateTextId: string; generateTextId: string;
} }
/**对话内容项 */
export interface ContentItem {
/** 角色ID */
roleId: string;
/** 对话内容 */
content: string;
}
/**分镜进度 */ /**分镜进度 */
export enum ShotStatus { export enum ShotStatus {
/** 草稿加载中 */ /** 草稿加载中 */
@ -82,6 +78,8 @@ export enum ShotStatus {
/** 完成 */ /** 完成 */
finished = 2, finished = 2,
} }
/** /**
* *
*/ */
@ -101,7 +99,7 @@ export interface ShotEntity extends BaseEntity {
/**对话内容 */ /**对话内容 */
content: ContentItem[]; content: ContentItem[];
/**镜头项 */ /**镜头项 */
shot: string[]; lens: LensType[];
/**分镜剧本Id */ /**分镜剧本Id */
scriptId: string; scriptId: string;
} }

View File

@ -21,7 +21,22 @@ export interface ScriptSlice {
/** 元数据 */ /** 元数据 */
metaData: any; metaData: any;
} }
/**对话内容项 */
export interface ContentItem {
/** 角色ID */
roleId: string;
/** 对话内容 */
content: string;
}
/**镜头值对象 */
export interface LensType {
/** 镜头名称 */
name: string;
/** 镜头描述 */
content: string;
/**运镜描述 */
movement: string;
}
/** /**
* @description: * @description:
* @return {*} * @return {*}

View File

@ -7,7 +7,7 @@ describe('ScriptService 业务逻辑测试', () => {
* generateScriptStream * generateScriptStream
*/ */
const stream = await generateScriptStream({ const stream = await generateScriptStream({
prompt: '一个年轻人在咖啡店里等待他的约会对象,心情紧张地摆弄着手机。' text: '一个年轻人在咖啡店里等待他的约会对象,心情紧张地摆弄着手机。'
}); });
let allData = ''; let allData = '';

View File

@ -4,6 +4,7 @@ import { generateScriptStream, applyScriptToShot } from "@/api/video_flow";
export class ScriptEditUseCase { export class ScriptEditUseCase {
loading: boolean = false; loading: boolean = false;
private scriptValueObject: ScriptValueObject; private scriptValueObject: ScriptValueObject;
private abortController: AbortController | null = null;
constructor(script: string) { constructor(script: string) {
this.scriptValueObject = new ScriptValueObject(script); this.scriptValueObject = new ScriptValueObject(script);
@ -12,15 +13,19 @@ export class ScriptEditUseCase {
/** /**
* @description: AI生成剧本方法 * @description: AI生成剧本方法
* @param prompt * @param prompt
* @param stream_callback
* @returns Promise<void> * @returns Promise<void>
*/ */
async generateScript(prompt: string): Promise<void> { async generateScript(prompt: string, stream_callback?: (data: any) => void): Promise<void> {
try { try {
this.loading = true; this.loading = true;
// 创建新的中断控制器
this.abortController = new AbortController();
// 使用API接口生成剧本 // 使用API接口生成剧本
const response = await generateScriptStream({ const response = await generateScriptStream({
prompt, text: prompt,
}); });
if (!response.successful) { if (!response.successful) {
@ -29,15 +34,37 @@ export class ScriptEditUseCase {
// 使用for await处理流式数据 // 使用for await处理流式数据
for await (const data of response.data) { for await (const data of response.data) {
// 检查是否被中断
if (this.abortController.signal.aborted) {
console.log("剧本生成被中断");
break;
}
// TODO: 根据流式数据更新剧本片段 // TODO: 根据流式数据更新剧本片段
// 这里需要根据实际的流式数据格式来处理 // 这里需要根据实际的流式数据格式来处理
// 可能需要将流式数据转换为ScriptSlice并添加到scriptValueObject中 // 可能需要将流式数据转换为ScriptSlice并添加到scriptValueObject中
stream_callback?.(data);
} }
} catch (error) { } catch (error) {
if (this.abortController?.signal.aborted) {
console.log("剧本生成被中断");
return;
}
console.error("AI生成剧本出错:", error); console.error("AI生成剧本出错:", error);
throw error; throw error;
} finally { } finally {
this.loading = false; this.loading = false;
this.abortController = null;
}
}
/**
* @description:
*/
abortGenerateScript(): void {
if (this.abortController) {
this.abortController.abort();
this.loading = false;
} }
} }
@ -45,13 +72,14 @@ export class ScriptEditUseCase {
* @description: * @description:
* @returns Promise<void> * @returns Promise<void>
*/ */
async applyScript(): Promise<void> { async applyScript(projectId: string): Promise<void> {
try { try {
this.loading = true; this.loading = true;
// 调用应用剧本接口 // 调用应用剧本接口
const response = await applyScriptToShot({ const response = await applyScriptToShot({
script: this.scriptValueObject.toString(), projectId,
scriptText: this.scriptValueObject.toString(),
}); });
if (!response.successful) { if (!response.successful) {

View File

@ -20,7 +20,8 @@
], ],
"paths": { "paths": {
"@/*": ["./*"] "@/*": ["./*"]
} },
"maxNodeModuleJsDepth":0
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View File

@ -2,10 +2,37 @@ import { ScriptSlice, ScriptSliceType } from "@/app/service/domain/valueObject";
export function parseScriptEntity(text: string):ScriptSlice { export function parseScriptEntity(text: string):ScriptSlice {
const scriptSlice:ScriptSlice={ const scriptSlice:ScriptSlice={
type:ScriptSliceType.text, // 生成唯一ID单次使用即可
text:text, id: `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`,
metaData:{} type: ScriptSliceType.text,
text: text,
metaData: {}
} }
return scriptSlice; return scriptSlice;
} }
/**
* @description
* @param {Function} func -
* @param {number} delay -
* @returns {Function} -
* @throws {Error} -
* @example
* const throttledFn = throttle(() => { console.log('触发'); }, 1000);
* window.addEventListener('resize', throttledFn);
*/
export function throttle<T extends (...args: any[]) => any>(func: T, delay: number=100): (...args: Parameters<T>) => void {
if (typeof delay !== 'number' || delay < 0) {
throw new Error('throttle: 第二个参数必须是非负数');
}
let lastCall = 0;
return (...args: Parameters<T>) => {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
func(...args);
}
};
}