fix: 多次点击加入房间

master
xjs 2025-04-30 13:02:50 +08:00
parent 1270723821
commit 27edb84470
13 changed files with 74 additions and 63 deletions

View File

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>填报师</title>
<title>纬AI填报师</title>
<meta name="description" content="AIGC对话" />
<meta name="generator" content="React" />
<meta name="keywords" content="music, music-site" />

BIN
public/icons/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -8,22 +8,22 @@ import { useSearchParams } from "react-router-dom";
import { fetchUserToken } from "@/apis/user";
import { useToast } from "@/hooks/use-toast";
import { useAbortController } from "@/hooks/useAbortController";
// import { ReportContext } from "@/components/Provider/ReportResolveProvider";
import AntechamberFile from "@/components/AntechamberFile";
import AntechamberReport from "@/components/AntechamberReport";
import AntechamberWishList from "@/components/AntechamberWishList";
import { ReportContext } from "@/components/Provider/ReportResolveProvider";
export default function Antechamber() {
const { handleConnect } = useContext(RealtimeClientContext);
const { handleConnect} = useContext(RealtimeClientContext);
const { hasHandledReport } = useContext(ReportContext);
const [searchParams] = useSearchParams();
const [disable,setDisable] = useState(true);
const [isLoading,setIsLoading] = useState(false);
const token = searchParams.get("token") || '';
// const reportId = searchParams.get("reportId") || '';
// const reportType = searchParams.get("reportType") || '';
const { toast } = useToast();
const { getSignal } = useAbortController();
@ -57,54 +57,35 @@ export default function Antechamber() {
}
};
// const getReport = async () => {
// try {
// const { result, message } = await fetchReport({
// params:{Type:reportType,Id:reportId},
// options: {
// signal: getSignal(),
// headers: {
// "Authorization": `Bearer ${encodeURIComponent(token)}`
// }
// }
// });
// if (message) {
// console.log(message);
// } else {
// handleConnect({initMessage:result as string});
// setHasHandledReport(true)
// }
// } catch (error: any) {
// if (error.name !== 'AbortError') {
// console.error('获取报告失败:', error);
// }
// }
// }
useEffect(() => {
getUserToken();
}, [token]);
// useEffect(() => {
// if(reportId && reportType && !hasHandledReport){
// getReport();
// }
// }, [reportId, reportType,hasHandledReport]);
const toRoom = (params:{initMessage?:string,fileUrl?:string}) => {
if(disable){
if(!hasHandledReport && (disable || isLoading)){
return;
}
handleConnect(params);
setIsLoading(true)
handleConnect(params).then(() => {
setIsLoading(false);
});
};
return (
<div className="flex flex-col items-center h-full">
<div className="flex flex-col items-center h-full overflow-y-auto relative">
<AntechamberHeader />
<AntechamberScore />
<AntechamberWishList />
<AntechamberFile />
<AntechamberReport />
<AntechamberFile handleLoading={setIsLoading} />
<AntechamberReport handleLoading={setIsLoading} />
<InvokeButton disable={disable} onClick={() => toRoom({})} />
{
isLoading ? <div className="w-[108px] h-[108px] absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] bg-black/60 rounded-[20px] flex flex-col items-center justify-center">
<img src="/icons/loading.gif" alt="loading" className="w-[68px] h-[68px]" />
<span className="text-[14px] text-[#fff]"></span>
</div> : <></>
}
</div>
);
}

View File

@ -7,11 +7,11 @@ import {
} from "@/components/Provider/RealtimeClientProvider";
import { ReportProvider } from "@/components/Provider/ReportResolveProvider";
import { RealtimeUtils } from "@coze/realtime-api";
import { useLocation } from "react-router-dom";
function MainContent() {
const { isConnected } = useContext(RealtimeClientContext);
const { isConnected, handleDisconnect } = useContext(RealtimeClientContext);
const location = useLocation();
const handlePromise = async() => {
await RealtimeUtils.checkDevicePermission(false);
@ -21,11 +21,16 @@ function MainContent() {
handlePromise();
}, []);
useEffect(() => {
if (isConnected) {
handleDisconnect();
}
}, [location.pathname]);
return (
<ReportProvider>
{isConnected ? <Room /> : <Antechamber />}
</ReportProvider>
);
}

View File

@ -1,6 +1,8 @@
.wrapper {
width: 88px;
height: 88px;
min-height: 88px;
min-width: 88px;
background: linear-gradient(180deg, #64c7ff 0%, #0165ff 100%);
display: flex;
flex-direction: column;
@ -8,8 +10,6 @@
align-items: center;
border-radius: 50%;
position: relative;
margin-top: auto;
margin-bottom: 80px;
overflow: hidden;
}

View File

@ -15,9 +15,11 @@ export default function InvokeButton(props: IInvokeButtonProps) {
return (
<div className={`${style.wrapper} ${className}`} {...rest}>
<img className={style.call} src={CallPng} alt="call" />
<div className={style.text}>{disable ? '暂不可用':isConnecting?'连接中':'发起通话'}</div>
<div className={`${className} mt-auto mb-[80px]`} {...rest}>
<div className={`mt-[20px] ${style.wrapper}`}>
<img className={style.call} src={CallPng} alt="call" />
<div className={style.text}>{disable ? '暂不可用':isConnecting?'连接中':'发起通话'}</div>
</div>
</div>
);
}

View File

@ -6,9 +6,11 @@ import { useSearchParams } from "react-router-dom";
import { ReportContext } from "../Provider/ReportResolveProvider";
import { FileInfo, RealtimeClientContext } from "../Provider/RealtimeClientProvider";
type Props = {
handleLoading:(val:boolean) => void
}
export default function AntechamberFile() {
export default function AntechamberFile({handleLoading}:Props) {
const [searchParams] = useSearchParams();
const fileId = searchParams.get("fileId") || "";
const locationCode = searchParams.get("locationCode") || "";
@ -32,7 +34,7 @@ export default function AntechamberFile() {
});
}
let resp = result.result as FileInfo;
handleLoading(true)
handleConnect({
fileInfo: {type: resp.type,url: resp.url,tableName: resp.tableName,provinceName: resp.provinceName,subjectClaim: resp.subjectClaim},
});

View File

@ -56,7 +56,6 @@
position: relative;
z-index: 1;
backdrop-filter:blur(10px);
min-height: 213px;
box-sizing: border-box;
}

View File

@ -91,7 +91,7 @@ export default function HeaderGroup() {
key={index}
onClick={() => handleQuestion(item)}
>
<div>{item}</div>
<div className="text-[14px] text-[#000]">{item}</div>
<img
src={RightIcon}
alt="right-icon"

View File

@ -5,7 +5,11 @@ import { useSearchParams } from "react-router-dom";
import { useAbortController } from "@/hooks/useAbortController";
import { RealtimeClientContext } from "../Provider/RealtimeClientProvider";
export default function AntechamberReport() {
type Props = {
handleLoading:(val:boolean) => void
}
export default function AntechamberReport({handleLoading}:Props) {
const [searchParams] = useSearchParams();
const token = searchParams.get("token") || '';
const reportId = searchParams.get("reportId") || '';
@ -30,6 +34,7 @@ export default function AntechamberReport() {
if (message) {
console.log(message);
} else {
handleLoading(true)
handleConnect({initMessage:result as string});
setHasHandledReport(true)
}

View File

@ -5,7 +5,7 @@
}
.innerWrapper {
padding: 15px;
padding: 12px 15px;
width: 100%;
background: rgba(255, 255, 255, 0.7);
border-radius: 13px;
@ -21,14 +21,15 @@
align-items: center;
gap: 10px;
margin-top: 10px;
font-size: 16px;
font-size: 14px;
font-weight: 700;
}
.right{
color: #1580FF;
display: flex;
align-items: center;
font-size: 13px;
color: #1580FF;
display: flex;
align-items: center;
font-size: 13px;
}
.imgIcon{

View File

@ -54,7 +54,7 @@ export default function AntechamberWishList() {
return (
<>
{wishList.length > 0 ? (
<div className="w-full p-[15px]">
<div className="w-full px-[15px]">
<div className="px-[12px] pt-[14px] pb-[16px] rounded-[13px] bg-[#fff]">
<div className="flex items-center justify-between">
<div className="w-[96px] h-[16px] object-contain">
@ -95,7 +95,6 @@ export default function AntechamberWishList() {
<div className="text-[10px] px-[4px] py-[2px] rounded-[4px] text-[#636363] bg-[#fff]">
{item.type}
</div>
<div></div>
</div>
<div className="text-[#303030] text-[11px] flex items-center">
<span>
@ -118,7 +117,7 @@ export default function AntechamberWishList() {
<img
src="/icons/rightBlue.png"
alt=""
className="ml-[2px]"
className="ml-[2px] w-[10px] h-[10px]"
/>
</div>
</div>
@ -163,7 +162,7 @@ export default function AntechamberWishList() {
onClick={() => handleNavigate(wishList[0])}
>
<img src="/icons/rightBlue.png" alt="" className="ml-[2px]" />
<img src="/icons/rightBlue.png" alt="" className="ml-[2px] w-[10px] h-[10px]" />
</div>
</div>
</div>

View File

@ -91,6 +91,8 @@ export const RealtimeClientProvider = ({
const connectorId = "1024";
const clientRef = useRef<RealtimeClient | null>(null);
// 添加连接锁
const connectingLockRef = useRef<boolean>(false);
// 实时语音回复消息列表
const [messageList, setMessageList] = useState<
{ content: string; role: RoleType; event?: any }[]
@ -166,6 +168,18 @@ export const RealtimeClientProvider = ({
fileInfo?: FileInfo;
}) => {
try {
// 使用连接锁确保原子性
if (connectingLockRef.current) {
return;
}
connectingLockRef.current = true;
// 如果已经连接或正在连接中,直接返回
if (isConnected || isConnecting) {
connectingLockRef.current = false;
return;
}
if (!clientRef.current) {
await initClient({ initMessage, fileInfo });
}
@ -192,6 +206,9 @@ export const RealtimeClientProvider = ({
} else {
console.error("连接错误:" + error);
}
} finally {
// 确保在任何情况下都释放连接锁
connectingLockRef.current = false;
}
};