|
@@ -1,16 +1,18 @@
|
|
|
+import { Image, View, ScrollView } from "@tarojs/components";
|
|
|
import EmptyData from "@/components/EmptyData";
|
|
|
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 SliderAction from "@/components/SliderAction";
|
|
|
import style from "./index.module.less";
|
|
|
import { useVoiceStore } from "@/store/voiceStore";
|
|
|
-import { EVoiceStatus, TVoiceItem } from "@/types/voice";
|
|
|
+import { EVoiceStatus, EVoiceType, TVoiceItem } from "@/types/voice";
|
|
|
import { TAgentDetail } from "@/types/agent";
|
|
|
|
|
|
import { deleteVoice, getVoiceStatus } from "@/service/voice";
|
|
@@ -19,9 +21,16 @@ import { useModalStore } from "@/store/modalStore";
|
|
|
import { isSuccess } from "@/utils";
|
|
|
import Taro from "@tarojs/taro";
|
|
|
|
|
|
+import {
|
|
|
+ getVoices,
|
|
|
+ cloneVoice as _cloneVoice,
|
|
|
+} from "@/service/voice";
|
|
|
+import { useLoadMoreInfinite, createKey } from "@/utils/loadMoreInfinite";
|
|
|
+
|
|
|
interface Props {
|
|
|
agent: TAgentDetail | null;
|
|
|
- onPlay?: (voice: any) => void;
|
|
|
+ onPlay: (voice: any) => void;
|
|
|
+ onSelect: (voice: any) => void;
|
|
|
}
|
|
|
|
|
|
type TTask = {
|
|
@@ -30,13 +39,22 @@ type TTask = {
|
|
|
taskId: string;
|
|
|
};
|
|
|
|
|
|
-export default ({ onPlay, agent }: Props) => {
|
|
|
+export default ({ onPlay, onSelect, agent }: Props) => {
|
|
|
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
|
|
const [show, setShow] = useState(false);
|
|
|
- const {showModal} = useModalStore()
|
|
|
+ const { showModal } = useModalStore();
|
|
|
|
|
|
- const { getVoices } = useVoiceStore();
|
|
|
- const myVoices = useVoiceStore((state) => state.myVoices);
|
|
|
+ const fetcher = async ([url, {pageSize, pageIndex}, [entId]])=> {
|
|
|
+ console.log([url, {pageSize, pageIndex}], entId)
|
|
|
+ const res = await getVoices({ pageSize, pageIndex, entId, type: EVoiceType.MINE})
|
|
|
+ return res.data;
|
|
|
+ }
|
|
|
+ const { list, mutate, loadMore } = useLoadMoreInfinite<
|
|
|
+ TVoiceItem[]
|
|
|
+ >(createKey(`api/getvoices/${0}`, 10, [0]), fetcher);
|
|
|
+
|
|
|
+ // const myVoices = useVoiceStore((state) => state.myVoices);
|
|
|
+
|
|
|
const voices = useVoiceStore((state) => state.voices);
|
|
|
|
|
|
// {
|
|
@@ -44,59 +62,67 @@ export default ({ onPlay, agent }: Props) => {
|
|
|
// taskId: 'abc',
|
|
|
// status: 'processing'
|
|
|
// }
|
|
|
- const [cloning, setCloning] = useState<TTask|null>();
|
|
|
+ const [cloning, setCloning] = useState<TTask | null>();
|
|
|
|
|
|
//获取一个默认声音
|
|
|
- const getDefaultVoice = ()=> {
|
|
|
+ const getDefaultVoice = () => {
|
|
|
// 优先使用我的声音
|
|
|
- if(myVoices.length){
|
|
|
- return myVoices[0]
|
|
|
+ if (list.length) {
|
|
|
+ return list[0];
|
|
|
}
|
|
|
- // 其次使用系统声音
|
|
|
- const systemVoices = voices.filter((item)=> item.isSystem)
|
|
|
- if(systemVoices.length){
|
|
|
- return systemVoices[0]
|
|
|
+ // // 其次使用系统声音
|
|
|
+ const systemVoices = voices.filter((item) => item.isSystem);
|
|
|
+ if (systemVoices.length) {
|
|
|
+ return systemVoices[0];
|
|
|
}
|
|
|
- return null
|
|
|
- }
|
|
|
+ return null;
|
|
|
+ };
|
|
|
|
|
|
// 检查需要不要改变默认声音
|
|
|
- const syncDefaultVoice = (item: TVoiceItem)=> {
|
|
|
+ const syncDefaultVoice = (item: TVoiceItem) => {
|
|
|
// 说明删除的声音是当前使用的声音
|
|
|
- if(agent?.voiceId === item.voiceId){
|
|
|
+ if (agent?.voiceId === item.voiceId) {
|
|
|
// 需要默认找一个声音
|
|
|
- const defaultVoice = getDefaultVoice()
|
|
|
- if(defaultVoice){
|
|
|
- handleSelect(defaultVoice)
|
|
|
+ const defaultVoice = getDefaultVoice();
|
|
|
+ if (defaultVoice) {
|
|
|
+ handleSelect(defaultVoice);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
const handleSelect = (item: TVoiceItem) => {
|
|
|
+ if (item.status == EVoiceStatus.DONE) {
|
|
|
+ if (item.voiceName) {
|
|
|
+ onSelect && onSelect(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleTry = (e: any, item: TVoiceItem) => {
|
|
|
+ e.stopPropagation();
|
|
|
if (item.status == EVoiceStatus.DONE) {
|
|
|
if (item.voiceName) {
|
|
|
onPlay && onPlay(item);
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
- const handleLongPress = (item: TVoiceItem) => {
|
|
|
- if(voices.length <= 1){
|
|
|
+ const handleDeleteItem = (item: TVoiceItem) => {
|
|
|
+ if (voices.length <= 1) {
|
|
|
return;
|
|
|
}
|
|
|
- if(!item.canDel){
|
|
|
- Taro.showToast({title: '该声音无法删除'})
|
|
|
+ if (!item.canDel) {
|
|
|
+ Taro.showToast({ title: "该声音无法删除" });
|
|
|
}
|
|
|
showModal({
|
|
|
- content: '确认删除该声音?',
|
|
|
+ content: "确认删除该声音?",
|
|
|
onConfirm: async () => {
|
|
|
- const response = await deleteVoice(item.voiceId)
|
|
|
- if(isSuccess(response.status)){
|
|
|
- await getVoices();
|
|
|
+ const response = await deleteVoice(item.voiceId);
|
|
|
+ if (isSuccess(response.status)) {
|
|
|
+ await mutate();
|
|
|
syncDefaultVoice(item);
|
|
|
}
|
|
|
- }
|
|
|
- })
|
|
|
+ },
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
// 克隆按钮状态
|
|
@@ -126,7 +152,7 @@ export default ({ onPlay, agent }: Props) => {
|
|
|
taskId: response.data.taskId,
|
|
|
});
|
|
|
stopTimer();
|
|
|
- await getVoices();
|
|
|
+ await mutate();
|
|
|
setCloning(null);
|
|
|
}
|
|
|
};
|
|
@@ -144,9 +170,11 @@ export default ({ onPlay, agent }: Props) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- getVoices();
|
|
|
- }, []);
|
|
|
+ const onScrollToLower = ()=> {
|
|
|
+ loadMore()
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
|
|
|
// 清除定时器
|
|
|
useEffect(() => {
|
|
@@ -155,11 +183,29 @@ export default ({ onPlay, agent }: Props) => {
|
|
|
};
|
|
|
}, []);
|
|
|
|
|
|
+ const createSliderButtons = (item: TVoiceItem) => {
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ text: "删除",
|
|
|
+ color: "#FF8200",
|
|
|
+ onClick: () => {
|
|
|
+ handleDeleteItem(item);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ };
|
|
|
+
|
|
|
// 克隆列表右侧操作栏
|
|
|
const renderRightColumn = (item: TVoiceItem) => {
|
|
|
if (item.status == EVoiceStatus.DONE) {
|
|
|
return (
|
|
|
<View className="flex items-center h-full">
|
|
|
+ <View
|
|
|
+ className="text-primary px-12 text-12"
|
|
|
+ onClick={(e) => handleTry(e, item)}
|
|
|
+ >
|
|
|
+ 试听
|
|
|
+ </View>
|
|
|
<WemetaRadio checked={item.voiceId === agent?.voiceId}></WemetaRadio>
|
|
|
</View>
|
|
|
);
|
|
@@ -167,7 +213,7 @@ export default ({ onPlay, agent }: Props) => {
|
|
|
|
|
|
return <></>;
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
// 克隆状态栏
|
|
|
const renderCloneStatus = (item: TTask) => {
|
|
|
if (item.status === "success") {
|
|
@@ -177,66 +223,82 @@ export default ({ onPlay, agent }: Props) => {
|
|
|
}
|
|
|
if (item.status === "processing") {
|
|
|
return (
|
|
|
- <View className={`text-12 leading-20 text-primary`}>{item.message}<ThinkingAnimation /></View>
|
|
|
+ <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>;
|
|
|
+ return (
|
|
|
+ <View className={`text-12 leading-20 text-orange`}>{item.message}</View>
|
|
|
+ );
|
|
|
}
|
|
|
- console.log(item.status)
|
|
|
+ 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 py-16">{renderCloneStatus(item)}</View>
|
|
|
- </CardListItem>;
|
|
|
+ return (
|
|
|
+ <CardListItem
|
|
|
+ underline
|
|
|
+ className="px-16"
|
|
|
+ leftRenderer={() => {
|
|
|
+ return (
|
|
|
+ <View className={style.listIcon}>
|
|
|
+ <Image src={IconWave} className={style.iconImage}></Image>
|
|
|
+ </View>
|
|
|
+ );
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <View className="flex items-center h-full py-16">
|
|
|
+ {renderCloneStatus(item)}
|
|
|
+ </View>
|
|
|
+ </CardListItem>
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
return (
|
|
|
- <CardListItem
|
|
|
- underline
|
|
|
- leftRenderer={() => {
|
|
|
- return (
|
|
|
- <View className={style.listIcon}>
|
|
|
- <Image src={IconWave} className={style.iconImage}></Image>
|
|
|
+ <SliderAction actions={createSliderButtons(item)}>
|
|
|
+ <CardListItem
|
|
|
+ underline
|
|
|
+ className="px-16"
|
|
|
+ leftRenderer={() => {
|
|
|
+ return (
|
|
|
+ <View className={style.listIcon}>
|
|
|
+ <Image src={IconWave} className={style.iconImage}></Image>
|
|
|
+ </View>
|
|
|
+ );
|
|
|
+ }}
|
|
|
+ rightRenderer={() => {
|
|
|
+ return renderRightColumn(item);
|
|
|
+ }}
|
|
|
+ onClick={() => handleSelect(item)}
|
|
|
+ >
|
|
|
+ <View className="flex flex-col gap-4 py-16">
|
|
|
+ <View className="flex items-center leading-22">
|
|
|
+ {item.voiceName}
|
|
|
</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>
|
|
|
+ <View className="text-12 text-gray-45 leading-20">
|
|
|
+ {item.createTime.slice(0, 7)} 创建
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+ </CardListItem>
|
|
|
+ </SliderAction>
|
|
|
);
|
|
|
};
|
|
|
|
|
|
// 渲染克隆列表
|
|
|
const renderCloneList = () => {
|
|
|
//
|
|
|
- const voices = cloning ? [cloning, ...myVoices] : myVoices;
|
|
|
+ const voices = cloning ? [cloning, ...list] : list;
|
|
|
if (!voices.length) {
|
|
|
- return <EmptyData type={'search'}></EmptyData>;
|
|
|
+ return <EmptyData type={"search"}></EmptyData>;
|
|
|
}
|
|
|
return (
|
|
|
- <View className="px-16 flex flex-col w-full">
|
|
|
+ <View className="flex flex-col w-full">
|
|
|
{voices.map((item, _index) => {
|
|
|
return renderItem(item);
|
|
|
})}
|
|
@@ -245,9 +307,19 @@ export default ({ onPlay, agent }: Props) => {
|
|
|
};
|
|
|
|
|
|
return (
|
|
|
- <View className={`flex flex-col`}>
|
|
|
- {renderCloneList()}
|
|
|
+ <View className={`flex flex-col h-full`}>
|
|
|
+ <ScrollView
|
|
|
+ scrollY
|
|
|
+ onScrollToLower={onScrollToLower}
|
|
|
+ style={{
|
|
|
+ flex: 1,
|
|
|
+ height: "100%", // 高度自适应
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {renderCloneList()}
|
|
|
+ </ScrollView>
|
|
|
|
|
|
+
|
|
|
<View
|
|
|
className={style.addButton}
|
|
|
onClick={() => {
|