123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- import EmptyData from "@/components/empty-data";
- import CardListItem from "@/components/list/card-list-item/index";
- import WemetaRadio from "@/components/WemetaRadio/index";
- import IconWave from "@/images/icon-wave-20.png";
- import { Image, View } from "@tarojs/components";
- import { useEffect, useRef, useState } from "react";
- import Popup from "@/components/popup/popup";
- import PopupRecorder, { ECloneStatus } from "./components/popup-recorder/index";
- import style from "./index.module.less";
- import { useVoiceStore } from "@/store/voiceStore";
- import { EVoiceStatus, TVoiceItem } from "@/types/voice";
- import { TAgentDetail } from "@/types/agent";
- import { deleteVoice, getVoiceStatus } from "@/service/voice";
- import ThinkingAnimation from "@/components/think-animation";
- import { useModalStore } from "@/store/modalStore";
- import { isSuccess } from "@/utils";
- interface Props {
- agent: TAgentDetail | null;
- onPlay?: (voice: any) => void;
- }
- type TTask = {
- message: string;
- status: "processing" | "success" | "process_fail";
- taskId: string;
- };
- export default ({ onPlay, agent }: Props) => {
- const intervalRef = useRef<NodeJS.Timeout | null>(null);
- const [show, setShow] = useState(false);
- const {showModal} = useModalStore()
- const { getVoices } = useVoiceStore();
- const myVoices = useVoiceStore((state) => state.myVoices);
- const voices = useVoiceStore((state) => state.voices);
- // {
- // message: '生成中',
- // taskId: 'abc',
- // status: 'processing'
- // }
- const [cloning, setCloning] = useState<TTask>();
- //获取一个默认声音
- const getDefaultVoice = ()=> {
- // 优先使用我的声音
- if(myVoices.length){
- return myVoices[0]
- }
- // 其次使用系统声音
- const systemVoices = voices.filter((item)=> item.isSystem)
- if(systemVoices.length){
- return systemVoices[0]
- }
- return null
- }
- // 检查需要不要改变默认声音
- const syncDefaultVoice = (item: TVoiceItem)=> {
- // 说明删除的声音是当前使用的声音
- if(agent?.voiceId === item.voiceId){
- // 需要默认找一个声音
- const defaultVoice = getDefaultVoice()
- if(defaultVoice){
- handleSelect(defaultVoice)
- }
- }
-
- }
- const handleSelect = (item: TVoiceItem) => {
- if (item.status == EVoiceStatus.DONE) {
- if (item.voiceName) {
- onPlay && onPlay(item);
- }
- }
- };
- const handleLongPress = (item: TVoiceItem) => {
- if(voices.length <= 1){
- return;
- }
- showModal({
- content: '确认删除该声音?',
- onConfirm: async () => {
- const response = await deleteVoice(item.voiceId)
- if(isSuccess(response.status)){
- await getVoices();
- syncDefaultVoice(item);
- }
- }
- })
- };
- // 克隆按钮状态
- const handleCloneStatus = (status: ECloneStatus) => {
- console.log(status);
- };
- 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();
- getVoices();
- }
- };
- // 声音录制完成
- const onRecordEnd = (taskId: string) => {
- console.log("onRecordEnd:", taskId);
- fetchVoiceList(taskId);
- };
- const stopTimer = () => {
- if (intervalRef.current !== null) {
- clearTimeout(intervalRef.current);
- intervalRef.current = null;
- }
- };
- useEffect(() => {
- getVoices();
- }, []);
- // 清除定时器
- useEffect(() => {
- return () => {
- stopTimer();
- };
- }, []);
- // 克隆列表右侧操作栏
- const renderRightColumn = (item: TVoiceItem) => {
- if (item.status == EVoiceStatus.DONE) {
- return (
- <View className="flex items-center h-full">
- <WemetaRadio checked={item.voiceId === agent?.voiceId}></WemetaRadio>
- </View>
- );
- }
- return <></>;
- };
-
- // 克隆状态栏
- const renderCloneStatus = (item: TTask) => {
- if (item.status === "success") {
- return (
- <View className={`text-12 leading-20 text-green`}>{item.message}</View>
- );
- }
- if (item.status === "processing") {
- return (
- <View className={`text-12 leading-20 text-primary`}>{item.message}<ThinkingAnimation /></View>
- );
- }
- if (item.status === "process_fail") {
- return <View className={`text-12 leading-20 text-orange`}>{item.message}</View>;
- }
- console.log(item.status)
- return <></>;
- };
- const renderItem = (item) => {
- if (item.taskId) {
- return <CardListItem
- 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
- underline
- leftRenderer={() => {
- return (
- <View className={style.listIcon}>
- <Image src={IconWave} className={style.iconImage}></Image>
- </View>
- );
- }}
- rightRenderer={() => {
- return renderRightColumn(item);
- }}
- onLongPress={()=> handleLongPress(item)}
- onClick={() => handleSelect(item)}
- >
- <View className="flex flex-col gap-4 py-16">
- <View className="flex items-center leading-22">{item.voiceName}</View>
- <View className="text-12 text-gray-45 leading-20">{item.createTime.slice(0,7)}创建</View>
- </View>
- </CardListItem>
- );
- };
- // 渲染克隆列表
- const renderCloneList = () => {
- //
- const voices = cloning ? [cloning, ...myVoices] : myVoices;
- if (!voices.length) {
- return <EmptyData></EmptyData>;
- }
- return (
- <View className="px-16 flex flex-col w-full">
- {voices.map((item, _index) => {
- return renderItem(item);
- })}
- </View>
- );
- };
- return (
- <View className={`flex flex-col`}>
- {renderCloneList()}
-
- <View
- className={style.addButton}
- onClick={() => {
- setShow(true);
- }}
- >
- <View className="button-rounded-big font-medium">
- <View>添加克隆声音</View>
- </View>
- </View>
- <Popup show={show} setShow={setShow}>
- <PopupRecorder
- onRecordEnd={onRecordEnd}
- show={show}
- setShow={setShow}
- setCloneStatus={(status) => handleCloneStatus(status)}
- ></PopupRecorder>
- </Popup>
- </View>
- );
- };
|