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

143 lines
4.8 KiB
TypeScript
Raw Normal View History

2026-06-05 07:07:45 +00:00
import { App, Button, Card, Input, Space, Typography } from "antd";
import { useEffect, useMemo, useState } from "react";
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
2026-06-05 07:07:45 +00:00
import { getMeetingPreviewAccess, getPublicMeetingPreview } from "@/api/meeting";
import LoadingScreen from "@/components/LoadingScreen";
import PageHeader from "@/components/PageHeader";
2026-06-05 07:07:45 +00:00
import MeetingPreviewView from "@/components/preview/MeetingPreviewView";
import usePageTitle from "@/hooks/usePageTitle";
import type { MeetingChapterVO, MeetingTranscriptVO, MeetingVO } from "@/types";
import { hasAccessToken } from "@/utils/auth";
2026-06-05 07:07:45 +00:00
import { buildMeetingPreviewUrl } from "@/utils/meeting";
const { Paragraph, Title } = Typography;
export default function MeetingPreviewPage() {
const { message } = App.useApp();
const navigate = useNavigate();
2026-06-05 07:07:45 +00:00
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 });
};
2026-06-05 07:07:45 +00:00
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, message]);
2026-06-05 07:07:45 +00:00
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">
<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>
2026-06-05 07:07:45 +00:00
</div>
);
}
if (!meeting) {
return <LoadingScreen text="未找到会议内容" />;
}
return (
<div className="preview-page">
<div className="preview-page__inner">
<PageHeader title="分享预览" back onBack={handleBack} />
2026-06-05 07:07:45 +00:00
<MeetingPreviewView
meeting={meeting}
transcripts={transcripts}
meetingChapters={chapters}
shareUrl={buildMeetingPreviewUrl(meeting.id)}
/>
</div>
</div>
);
}