imeeting/imeeting-h5/src/pages/meeting-preview/index.tsx

164 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { App, Button, Card, Input, Space, Typography } from "antd";
import { useEffect, useMemo, useState } from "react";
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import {getMeetingPreviewAccess, getPublicMeetingPreview, getPublicRealtimeMeetingSessionStatus} from "@/api/meeting";
import LoadingScreen from "@/components/LoadingScreen";
import PageHeader from "@/components/PageHeader";
import MeetingPreviewView from "@/components/preview/MeetingPreviewView";
import usePageTitle from "@/hooks/usePageTitle";
import type { MeetingChapterVO, MeetingTranscriptVO, MeetingVO } from "@/types";
import { hasAccessToken } from "@/utils/auth";
import { buildMeetingPreviewUrl } from "@/utils/meeting";
const { Paragraph, Title } = Typography;
export default function MeetingPreviewPage() {
const { message } = App.useApp();
const navigate = useNavigate();
const { id } = useParams<{ id: string }>();
const [searchParams] = useSearchParams();
const [loading, setLoading] = useState(true);
const [meeting, setMeeting] = useState<MeetingVO | null>(null);
const [transcripts, setTranscripts] = useState<MeetingTranscriptVO[]>([]);
const [chapters, setChapters] = useState<MeetingChapterVO[]>([]);
const [passwordRequired, setPasswordRequired] = useState(false);
const [passwordVerified, setPasswordVerified] = useState(false);
const [accessPassword, setAccessPassword] = useState("");
const meetingId = useMemo(() => Number(id), [id]);
const presetAccessPassword = useMemo(() => (searchParams.get("accessPassword") || "").trim(), [searchParams]);
usePageTitle(meeting?.title || "会议预览");
const handleBack = () => {
navigate(hasAccessToken() ? "/meetings" : "/login", { replace: true });
};
const loadPreview = async (password?: string) => {
const previewResp = await getPublicMeetingPreview(meetingId, password);
const preview = previewResp.data.data;
if (preview.meeting.meetingType === "REALTIME") {
try {
const statusResp = await getPublicRealtimeMeetingSessionStatus(meetingId, password);
const status = statusResp.data.data.status;
if (status !== "COMPLETING" && status !== "COMPLETED") {
const query = password ? `?accessPassword=${encodeURIComponent(password)}` : "";
navigate(`/meetings/${meetingId}/realtime-preview${query}`, {replace: true});
return true;
}
} catch {
// 状态查询异常时继续进入原预览页。
}
}
setMeeting(preview.meeting);
setTranscripts(preview.transcripts || []);
setChapters(preview.chapters || []);
setPasswordVerified(true);
return false;
};
useEffect(() => {
const run = async () => {
if (!meetingId || Number.isNaN(meetingId)) {
message.error("会议编号无效");
setLoading(false);
return;
}
setLoading(true);
try {
const accessResp = await getMeetingPreviewAccess(meetingId);
const required = !!accessResp.data.data.passwordRequired;
setPasswordRequired(required);
if (!required) {
await loadPreview();
return;
}
if (presetAccessPassword) {
try {
const redirected = await loadPreview(presetAccessPassword);
if (redirected) {
return;
}
setAccessPassword(presetAccessPassword);
return;
} catch {
setPasswordVerified(false);
}
}
} finally {
setLoading(false);
}
};
void run();
}, [meetingId, presetAccessPassword, message]);
const handleSubmitPassword = async () => {
if (!accessPassword.trim()) {
return;
}
setLoading(true);
try {
const redirected = await loadPreview(accessPassword.trim());
if (redirected) {
return;
}
message.success("访问校验通过");
} catch {
message.error("访问密码错误");
} finally {
setLoading(false);
}
};
if (loading && !meeting && !passwordRequired) {
return <LoadingScreen text="正在加载会议预览..." />;
}
if (passwordRequired && !passwordVerified) {
return (
<div className="preview-page">
<div className="preview-page__inner">
<PageHeader title="分享预览" back onBack={handleBack} />
<Card className="surface-card preview-password-card">
<Title level={3}></Title>
<Paragraph type="secondary">访访</Paragraph>
<Space direction="vertical" size={16} style={{ width: "100%" }}>
<Input.Password
value={accessPassword}
placeholder="请输入访问密码"
onChange={(event) => setAccessPassword(event.target.value)}
onPressEnter={() => void handleSubmitPassword()}
/>
<Button type="primary" block loading={loading} onClick={() => void handleSubmitPassword()}>
</Button>
</Space>
</Card>
</div>
</div>
);
}
if (!meeting) {
return <LoadingScreen text="未找到会议内容" />;
}
return (
<div className="preview-page">
<div className="preview-page__inner">
<PageHeader title="分享预览" back onBack={handleBack} />
<MeetingPreviewView
meeting={meeting}
transcripts={transcripts}
meetingChapters={chapters}
shareUrl={buildMeetingPreviewUrl(meeting.id)}
/>
</div>
</div>
);
}