135 lines
4.4 KiB
TypeScript
135 lines
4.4 KiB
TypeScript
|
|
import { App, Button, Card, Input, Space, Typography } from "antd";
|
|||
|
|
import { useEffect, useMemo, useState } from "react";
|
|||
|
|
import { useParams, useSearchParams } from "react-router-dom";
|
|||
|
|
|
|||
|
|
import { getMeetingPreviewAccess, getPublicMeetingPreview } from "@/api/meeting";
|
|||
|
|
import LoadingScreen from "@/components/LoadingScreen";
|
|||
|
|
import MeetingPreviewView from "@/components/preview/MeetingPreviewView";
|
|||
|
|
import usePageTitle from "@/hooks/usePageTitle";
|
|||
|
|
import type { MeetingChapterVO, MeetingTranscriptVO, MeetingVO } from "@/types";
|
|||
|
|
import { buildMeetingPreviewUrl } from "@/utils/meeting";
|
|||
|
|
|
|||
|
|
const { Paragraph, Title } = Typography;
|
|||
|
|
|
|||
|
|
export default function MeetingPreviewPage() {
|
|||
|
|
const { message } = App.useApp();
|
|||
|
|
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 loadPreview = async (password?: string) => {
|
|||
|
|
const previewResp = await getPublicMeetingPreview(meetingId, password);
|
|||
|
|
setMeeting(previewResp.data.data.meeting);
|
|||
|
|
setTranscripts(previewResp.data.data.transcripts || []);
|
|||
|
|
setChapters(previewResp.data.data.chapters || []);
|
|||
|
|
setPasswordVerified(true);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
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 {
|
|||
|
|
await loadPreview(presetAccessPassword);
|
|||
|
|
setAccessPassword(presetAccessPassword);
|
|||
|
|
return;
|
|||
|
|
} catch {
|
|||
|
|
setPasswordVerified(false);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} finally {
|
|||
|
|
setLoading(false);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
void run();
|
|||
|
|
}, [meetingId, presetAccessPassword]);
|
|||
|
|
|
|||
|
|
const handleSubmitPassword = async () => {
|
|||
|
|
if (!accessPassword.trim()) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
setLoading(true);
|
|||
|
|
try {
|
|||
|
|
await loadPreview(accessPassword.trim());
|
|||
|
|
message.success("访问校验通过");
|
|||
|
|
} catch {
|
|||
|
|
message.error("访问密码错误");
|
|||
|
|
} finally {
|
|||
|
|
setLoading(false);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
if (loading && !meeting && !passwordRequired) {
|
|||
|
|
return <LoadingScreen text="正在加载会议预览..." />;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (passwordRequired && !passwordVerified) {
|
|||
|
|
return (
|
|||
|
|
<div className="preview-page">
|
|||
|
|
<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>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!meeting) {
|
|||
|
|
return <LoadingScreen text="未找到会议内容" />;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="preview-page">
|
|||
|
|
<div className="preview-page__inner">
|
|||
|
|
<div className="preview-page__header">
|
|||
|
|
<span className="login-page__badge">iMeeting 分享预览</span>
|
|||
|
|
</div>
|
|||
|
|
<MeetingPreviewView
|
|||
|
|
meeting={meeting}
|
|||
|
|
transcripts={transcripts}
|
|||
|
|
meetingChapters={chapters}
|
|||
|
|
shareUrl={buildMeetingPreviewUrl(meeting.id)}
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|