|
@@ -1,60 +1,47 @@
|
|
import EmptyData from "@/components/empty-data";
|
|
import EmptyData from "@/components/empty-data";
|
|
import CardListItem from "@/components/list/card-list-item/index";
|
|
import CardListItem from "@/components/list/card-list-item/index";
|
|
-import CardList from "@/components/list/card-list/index";
|
|
|
|
-import { IVoicePlayerBar } from "@/components/voice-player-bar/index";
|
|
|
|
|
|
+
|
|
import WemetaRadio from "@/components/wemeta-radio/index";
|
|
import WemetaRadio from "@/components/wemeta-radio/index";
|
|
-import { CloneVoiceStatus } from "@/consts/enum";
|
|
|
|
import IconWave from "@/images/icon-wave-20.png";
|
|
import IconWave from "@/images/icon-wave-20.png";
|
|
-import { useAppStore } from "@/store/appStore";
|
|
|
|
-import { ICharacter } from "@/types";
|
|
|
|
-import { formatDateFull } from "@/utils/index";
|
|
|
|
|
|
+
|
|
import { Image, View } from "@tarojs/components";
|
|
import { Image, View } from "@tarojs/components";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import Popup from "@/components/popup/popup";
|
|
import Popup from "@/components/popup/popup";
|
|
import PopupRecorder, { ECloneStatus } from "./components/popup-recorder/index";
|
|
import PopupRecorder, { ECloneStatus } from "./components/popup-recorder/index";
|
|
-import PopupTryout from "./components/popup-tryout/index";
|
|
|
|
import style from "./index.module.less";
|
|
import style from "./index.module.less";
|
|
import { useVoiceStore } from "@/store/voiceStore";
|
|
import { useVoiceStore } from "@/store/voiceStore";
|
|
import { EVoiceStatus, TVoiceItem } from "@/types/voice";
|
|
import { EVoiceStatus, TVoiceItem } from "@/types/voice";
|
|
import { TAgentDetail } from "@/types/agent";
|
|
import { TAgentDetail } from "@/types/agent";
|
|
|
|
|
|
|
|
+import { getVoiceStatus } from "@/service/voice";
|
|
|
|
+
|
|
interface Props {
|
|
interface Props {
|
|
- agent: TAgentDetail|null
|
|
|
|
- value?: ICharacter;
|
|
|
|
- setValue?: (value: ICharacter) => void;
|
|
|
|
|
|
+ agent: TAgentDetail | null;
|
|
onPlay?: (voice: any) => void;
|
|
onPlay?: (voice: any) => void;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+type TTask = {
|
|
|
|
+ message: string;
|
|
|
|
+ status: "processing" | "success" | "process_fail";
|
|
|
|
+ taskId: string;
|
|
|
|
+};
|
|
|
|
+
|
|
export default ({ onPlay, agent }: Props) => {
|
|
export default ({ onPlay, agent }: Props) => {
|
|
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
|
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
|
const [show, setShow] = useState(false);
|
|
const [show, setShow] = useState(false);
|
|
- const [popupType, setPopupType] = useState<"clone" | "try" | "reclone">(
|
|
|
|
- "clone",
|
|
|
|
- );
|
|
|
|
|
|
|
|
- const {getVoices} = useVoiceStore()
|
|
|
|
- const voices = useVoiceStore(state => state.voices)
|
|
|
|
- const myVoices = useVoiceStore(state => state.myVoices)
|
|
|
|
- const appConfig = useAppStore((state) => state.appConfig);
|
|
|
|
|
|
|
|
-
|
|
|
|
-
|
|
|
|
|
|
+ const { getVoices } = useVoiceStore();
|
|
|
|
+ const myVoices = useVoiceStore((state) => state.myVoices);
|
|
|
|
+
|
|
|
|
+ const [cloning, setCloning] = useState<TTask>();
|
|
|
|
|
|
const handleSelect = (item: TVoiceItem) => {
|
|
const handleSelect = (item: TVoiceItem) => {
|
|
if (item.status == EVoiceStatus.DONE) {
|
|
if (item.status == EVoiceStatus.DONE) {
|
|
if (item.voiceName) {
|
|
if (item.voiceName) {
|
|
- // setVoiceName(item.voiceName);
|
|
|
|
- onPlay &&
|
|
|
|
- onPlay(item);
|
|
|
|
|
|
+ onPlay && onPlay(item);
|
|
}
|
|
}
|
|
- // save
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
- // if (item.status == CloneVoiceStatus.CloneVoiceStatusUnconfirmed) {
|
|
|
|
- // // 未确认,弹出试听框
|
|
|
|
- // setPopupType("try");
|
|
|
|
- // setShow(true);
|
|
|
|
- // }
|
|
|
|
};
|
|
};
|
|
|
|
|
|
// 克隆按钮状态
|
|
// 克隆按钮状态
|
|
@@ -62,31 +49,36 @@ export default ({ onPlay, agent }: Props) => {
|
|
console.log(status);
|
|
console.log(status);
|
|
};
|
|
};
|
|
|
|
|
|
- const fetchVoiceList = async () => {
|
|
|
|
- await getVoices();
|
|
|
|
- const result = voices.find((item) => !item.isSystem && item.status === 'processing');
|
|
|
|
- if (result) {
|
|
|
|
- intervalRef.current = setTimeout(() => fetchVoiceList(), 3000);
|
|
|
|
- } else {
|
|
|
|
|
|
+ const fetchVoiceList = async (taskId: string) => {
|
|
|
|
+ const response = await getVoiceStatus(taskId);
|
|
|
|
+ console.log(response.data);
|
|
|
|
+ if (response.data.status === "processing") {
|
|
|
|
+ setCloning({
|
|
|
|
+ message: response.data.message,
|
|
|
|
+ status: response.data.status,
|
|
|
|
+ taskId: response.data.taskId,
|
|
|
|
+ });
|
|
|
|
+ intervalRef.current = setTimeout(() => fetchVoiceList(taskId), 3000);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ if (
|
|
|
|
+ response.data.status === "process_fail" ||
|
|
|
|
+ response.data.status === "success"
|
|
|
|
+ ) {
|
|
|
|
+ setCloning({
|
|
|
|
+ message: response.data.message,
|
|
|
|
+ status: response.data.status,
|
|
|
|
+ taskId: response.data.taskId,
|
|
|
|
+ });
|
|
stopTimer();
|
|
stopTimer();
|
|
|
|
+ getVoices();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
- // const handleSureAction = async () => {
|
|
|
|
- // setShow(false);
|
|
|
|
- // // await voiceCloneConfirm(myVoices[voiceIndex].voiceName!);
|
|
|
|
- // // fetchVoiceList();
|
|
|
|
- // };
|
|
|
|
-
|
|
|
|
- // const handleRecloneAction = () => {
|
|
|
|
- // setPopupType("reclone");
|
|
|
|
- // setShow(true);
|
|
|
|
- // };
|
|
|
|
-
|
|
|
|
// 声音录制完成
|
|
// 声音录制完成
|
|
- const onRecordEnd = (r: string) => {
|
|
|
|
- console.log("onRecordEnd:", r);
|
|
|
|
- fetchVoiceList();
|
|
|
|
|
|
+ const onRecordEnd = (taskId: string) => {
|
|
|
|
+ console.log("onRecordEnd:", taskId);
|
|
|
|
+ fetchVoiceList(taskId);
|
|
};
|
|
};
|
|
|
|
|
|
const stopTimer = () => {
|
|
const stopTimer = () => {
|
|
@@ -96,24 +88,9 @@ export default ({ onPlay, agent }: Props) => {
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
- const calcRemainCloneNum = () => {
|
|
|
|
- if (appConfig?.maxCloneNum) {
|
|
|
|
- // const clonedNum = myVoices.filter(
|
|
|
|
- // (item) =>
|
|
|
|
- // item.status === CloneVoiceStatus.CloneVoiceStatusSuccess ||
|
|
|
|
- // item.status === CloneVoiceStatus.CloneVoiceStatusUnconfirmed,
|
|
|
|
- // ).length;
|
|
|
|
- // const remainNum = appConfig.maxCloneNum - clonedNum;
|
|
|
|
- // return remainNum < 0 ? 0 : remainNum;
|
|
|
|
- }
|
|
|
|
- return myVoices.length;
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- const initPage = async () => {
|
|
|
|
-
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
-
|
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ getVoices();
|
|
|
|
+ }, []);
|
|
|
|
|
|
// 清除定时器
|
|
// 清除定时器
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
@@ -122,67 +99,88 @@ export default ({ onPlay, agent }: Props) => {
|
|
};
|
|
};
|
|
}, []);
|
|
}, []);
|
|
|
|
|
|
-
|
|
|
|
// 克隆列表右侧操作栏
|
|
// 克隆列表右侧操作栏
|
|
const renderRightColumn = (item: TVoiceItem) => {
|
|
const renderRightColumn = (item: TVoiceItem) => {
|
|
if (item.status == EVoiceStatus.DONE) {
|
|
if (item.status == EVoiceStatus.DONE) {
|
|
- return <WemetaRadio checked={item.voiceId === agent?.voiceId}></WemetaRadio>;
|
|
|
|
|
|
+ return (
|
|
|
|
+ <View className="flex items-center h-full">
|
|
|
|
+ <WemetaRadio checked={item.voiceId === agent?.voiceId}></WemetaRadio>
|
|
|
|
+ </View>
|
|
|
|
+ );
|
|
}
|
|
}
|
|
|
|
|
|
return <></>;
|
|
return <></>;
|
|
};
|
|
};
|
|
|
|
|
|
// 克隆状态栏
|
|
// 克隆状态栏
|
|
- const renderCloneStatus = (item: TVoiceItem) => {
|
|
|
|
- if (item.status === EVoiceStatus.DONE) {
|
|
|
|
|
|
+ const renderCloneStatus = (item: TTask) => {
|
|
|
|
+ if (item.status === "success") {
|
|
return (
|
|
return (
|
|
- <View className={`text-12 leading-20 text-gray-45`}>
|
|
|
|
- {item.createTime && formatDateFull(new Date(item.createTime.replace(/-/g,'/')))}
|
|
|
|
- </View>
|
|
|
|
|
|
+ <View className={`text-12 leading-20 text-green`}>{item.message}</View>
|
|
);
|
|
);
|
|
}
|
|
}
|
|
- if (item.status === EVoiceStatus.DOING) {
|
|
|
|
- return <View className={`text-12 leading-20 text-orange`}>生成中...</View>;
|
|
|
|
|
|
+ if (item.status === "processing") {
|
|
|
|
+ return (
|
|
|
|
+ <View className={`text-12 leading-20 text-primary`}>{item.message}</View>
|
|
|
|
+ );
|
|
}
|
|
}
|
|
|
|
|
|
- if (item.status == EVoiceStatus.FAILED) {
|
|
|
|
- return <View className={`text-12 leading-20 text-orange`}>生成错误</View>;
|
|
|
|
|
|
+ if (item.status === "process_fail") {
|
|
|
|
+ return <View className={`text-12 leading-20 text-orange`}>{item.message}</View>;
|
|
}
|
|
}
|
|
- return <View>...</View>;
|
|
|
|
|
|
+ console.log(item.status)
|
|
|
|
+ return <></>;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const renderItem = (item) => {
|
|
|
|
+ if (item.taskId) {
|
|
|
|
+ return <CardListItem
|
|
|
|
+ className="pb-16"
|
|
|
|
+ underline
|
|
|
|
+ leftRenderer={() => {
|
|
|
|
+ return (
|
|
|
|
+ <View className={style.listIcon}>
|
|
|
|
+ <Image src={IconWave} className={style.iconImage}></Image>
|
|
|
|
+ </View>
|
|
|
|
+ );
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ <View className="flex items-center h-full ">{renderCloneStatus(item)}</View>
|
|
|
|
+ </CardListItem>;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <CardListItem
|
|
|
|
+ className="pb-16"
|
|
|
|
+ underline
|
|
|
|
+ leftRenderer={() => {
|
|
|
|
+ return (
|
|
|
|
+ <View className={style.listIcon}>
|
|
|
|
+ <Image src={IconWave} className={style.iconImage}></Image>
|
|
|
|
+ </View>
|
|
|
|
+ );
|
|
|
|
+ }}
|
|
|
|
+ rightRenderer={() => {
|
|
|
|
+ return renderRightColumn(item);
|
|
|
|
+ }}
|
|
|
|
+ onClick={() => handleSelect(item)}
|
|
|
|
+ >
|
|
|
|
+ <View className="flex items-center h-full ">{item.voiceName}</View>
|
|
|
|
+ </CardListItem>
|
|
|
|
+ );
|
|
};
|
|
};
|
|
|
|
|
|
// 渲染克隆列表
|
|
// 渲染克隆列表
|
|
const renderCloneList = () => {
|
|
const renderCloneList = () => {
|
|
- if (!myVoices.length) {
|
|
|
|
|
|
+ //
|
|
|
|
+ const voices = cloning ? [cloning, ...myVoices] : myVoices;
|
|
|
|
+ if (!voices.length) {
|
|
return <EmptyData></EmptyData>;
|
|
return <EmptyData></EmptyData>;
|
|
}
|
|
}
|
|
return (
|
|
return (
|
|
- <View className="py-12 px-16 gap-">
|
|
|
|
- {myVoices.map((item, _index) => {
|
|
|
|
- return (
|
|
|
|
- <CardListItem
|
|
|
|
- className="py-16"
|
|
|
|
- underline
|
|
|
|
- leftRenderer={()=> {
|
|
|
|
- return <View className={style.listIcon}>
|
|
|
|
- <Image src={IconWave} className={style.iconImage}></Image>
|
|
|
|
- </View>
|
|
|
|
- }}
|
|
|
|
- rightRenderer={() => {
|
|
|
|
- return renderRightColumn(item);
|
|
|
|
- }}
|
|
|
|
- onClick={() => handleSelect(item)}
|
|
|
|
- >
|
|
|
|
- <View className="flex items-center gap-16">
|
|
|
|
- <View className="flex flex-col gap-4">
|
|
|
|
- <View className={style.ListItemText}>
|
|
|
|
- {item.voiceName}
|
|
|
|
- </View>
|
|
|
|
- {renderCloneStatus(item)}
|
|
|
|
- </View>
|
|
|
|
- </View>
|
|
|
|
- </CardListItem>
|
|
|
|
- );
|
|
|
|
|
|
+ <View className="py-12 px-16">
|
|
|
|
+ {voices.map((item, _index) => {
|
|
|
|
+ return renderItem(item);
|
|
})}
|
|
})}
|
|
</View>
|
|
</View>
|
|
);
|
|
);
|
|
@@ -190,51 +188,27 @@ export default ({ onPlay, agent }: Props) => {
|
|
|
|
|
|
return (
|
|
return (
|
|
<View className={`flex flex-col`}>
|
|
<View className={`flex flex-col`}>
|
|
- <View className="flex-1">
|
|
|
|
- {/* <VoicePlayerBar
|
|
|
|
- ref={playerRef}
|
|
|
|
- voiceName={voiceName}
|
|
|
|
- voiceNameText={createVoiceNameText(voiceName)}
|
|
|
|
- /> */}
|
|
|
|
-
|
|
|
|
- {renderCloneList()}
|
|
|
|
- </View>
|
|
|
|
-
|
|
|
|
- <Popup show={show} setShow={setShow}>
|
|
|
|
- <View>
|
|
|
|
- {(popupType == "clone" || popupType == "reclone") && (
|
|
|
|
- <PopupRecorder
|
|
|
|
- onRecordEnd={onRecordEnd}
|
|
|
|
- show={show}
|
|
|
|
- setShow={setShow}
|
|
|
|
- setCloneStatus={(status) => handleCloneStatus(status)}
|
|
|
|
- ></PopupRecorder>
|
|
|
|
- )}
|
|
|
|
- {/* {popupType == "try" && (
|
|
|
|
- <PopupTryout
|
|
|
|
- show={show}
|
|
|
|
- onSure={handleSureAction}
|
|
|
|
- onReclone={handleRecloneAction}
|
|
|
|
- showName={getCloneVoiceIdentifier(voiceIndex + 1)}
|
|
|
|
- ></PopupTryout>
|
|
|
|
- )} */}
|
|
|
|
- </View>
|
|
|
|
- </Popup>
|
|
|
|
-
|
|
|
|
|
|
+ {renderCloneList()}
|
|
|
|
+
|
|
<View
|
|
<View
|
|
className={style.addButton}
|
|
className={style.addButton}
|
|
onClick={() => {
|
|
onClick={() => {
|
|
- setPopupType("clone");
|
|
|
|
setShow(true);
|
|
setShow(true);
|
|
}}
|
|
}}
|
|
>
|
|
>
|
|
<View className="button-rounded-big font-medium">
|
|
<View className="button-rounded-big font-medium">
|
|
<View>添加克隆声音</View>
|
|
<View>添加克隆声音</View>
|
|
- <View className="font-normal text-12 leading-0">
|
|
|
|
- (剩{calcRemainCloneNum()}次)
|
|
|
|
- </View>
|
|
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
+
|
|
|
|
+ <Popup show={show} setShow={setShow}>
|
|
|
|
+ <PopupRecorder
|
|
|
|
+ onRecordEnd={onRecordEnd}
|
|
|
|
+ show={show}
|
|
|
|
+ setShow={setShow}
|
|
|
|
+ setCloneStatus={(status) => handleCloneStatus(status)}
|
|
|
|
+ ></PopupRecorder>
|
|
|
|
+ </Popup>
|
|
</View>
|
|
</View>
|
|
);
|
|
);
|
|
};
|
|
};
|