69a0f7b24c
- 侧边栏导航:仪表盘、登录、内容浏览、发布、互动、API 测试、设置 - 7 个页面所有按钮、标签、提示、错误信息改为中文 - API 端点列表分类改为中文(登录、内容、发布、互动) - 组件内文本:展开/收起、复制、点赞、收藏、评论等 - 页面标题改为 Social MCP - 管理后台
170 lines
6.8 KiB
TypeScript
170 lines
6.8 KiB
TypeScript
import { useState, useCallback } from 'react';
|
|
import { Card } from '@/components/ui/Card';
|
|
import { Button } from '@/components/ui/Button';
|
|
import { Input } from '@/components/ui/Input';
|
|
import { Textarea } from '@/components/ui/Textarea';
|
|
import { Select } from '@/components/ui/Select';
|
|
import { Tabs } from '@/components/ui/Tabs';
|
|
import { JsonViewer } from '@/components/ui/JsonViewer';
|
|
import { useToast } from '@/context/ToastContext';
|
|
import { publishImage, publishVideo } from '@/api/endpoints';
|
|
|
|
export function PublishPage() {
|
|
const { toast } = useToast();
|
|
const [tab, setTab] = useState('image');
|
|
|
|
// Image form
|
|
const [imgTitle, setImgTitle] = useState('');
|
|
const [imgContent, setImgContent] = useState('');
|
|
const [imgPaths, setImgPaths] = useState('');
|
|
const [imgTags, setImgTags] = useState('');
|
|
const [imgVisibility, setImgVisibility] = useState('public');
|
|
const [imgOriginal, setImgOriginal] = useState(false);
|
|
const [imgLoading, setImgLoading] = useState(false);
|
|
const [imgResult, setImgResult] = useState<unknown>(null);
|
|
|
|
// Video form
|
|
const [vidTitle, setVidTitle] = useState('');
|
|
const [vidContent, setVidContent] = useState('');
|
|
const [vidPath, setVidPath] = useState('');
|
|
const [vidTags, setVidTags] = useState('');
|
|
const [vidVisibility, setVidVisibility] = useState('public');
|
|
const [vidLoading, setVidLoading] = useState(false);
|
|
const [vidResult, setVidResult] = useState<unknown>(null);
|
|
|
|
const handlePublishImage = useCallback(async () => {
|
|
if (!imgTitle.trim() || !imgPaths.trim()) {
|
|
toast('warning', '标题和图片为必填项');
|
|
return;
|
|
}
|
|
setImgLoading(true);
|
|
setImgResult(null);
|
|
try {
|
|
const images = imgPaths.split('\n').map((s) => s.trim()).filter(Boolean);
|
|
const tags = imgTags.split(',').map((s) => s.trim()).filter(Boolean);
|
|
const res = await publishImage({
|
|
title: imgTitle,
|
|
content: imgContent,
|
|
images,
|
|
tags: tags.length > 0 ? tags : undefined,
|
|
is_original: imgOriginal,
|
|
visibility: imgVisibility as 'public' | 'private' | 'friends',
|
|
});
|
|
setImgResult(res);
|
|
if (res.success) {
|
|
toast('success', '图文笔记发布成功!');
|
|
} else {
|
|
toast('error', res.error?.message || '发布失败');
|
|
}
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : '发布失败';
|
|
toast('error', msg);
|
|
setImgResult({ error: msg });
|
|
} finally {
|
|
setImgLoading(false);
|
|
}
|
|
}, [imgTitle, imgContent, imgPaths, imgTags, imgVisibility, imgOriginal, toast]);
|
|
|
|
const handlePublishVideo = useCallback(async () => {
|
|
if (!vidTitle.trim() || !vidPath.trim()) {
|
|
toast('warning', '标题和视频路径为必填项');
|
|
return;
|
|
}
|
|
setVidLoading(true);
|
|
setVidResult(null);
|
|
try {
|
|
const tags = vidTags.split(',').map((s) => s.trim()).filter(Boolean);
|
|
const res = await publishVideo({
|
|
title: vidTitle,
|
|
content: vidContent,
|
|
video: vidPath,
|
|
tags: tags.length > 0 ? tags : undefined,
|
|
visibility: vidVisibility as 'public' | 'private' | 'friends',
|
|
});
|
|
setVidResult(res);
|
|
if (res.success) {
|
|
toast('success', '视频笔记发布成功!');
|
|
} else {
|
|
toast('error', res.error?.message || '发布失败');
|
|
}
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : '发布失败';
|
|
toast('error', msg);
|
|
setVidResult({ error: msg });
|
|
} finally {
|
|
setVidLoading(false);
|
|
}
|
|
}, [vidTitle, vidContent, vidPath, vidTags, vidVisibility, toast]);
|
|
|
|
return (
|
|
<div className="max-w-3xl space-y-4">
|
|
<h1 className="text-2xl font-bold">发布笔记</h1>
|
|
|
|
<Tabs
|
|
tabs={[
|
|
{ key: 'image', label: '图文笔记' },
|
|
{ key: 'video', label: '视频笔记' },
|
|
]}
|
|
active={tab}
|
|
onChange={setTab}
|
|
/>
|
|
|
|
{tab === 'image' && (
|
|
<Card>
|
|
<div className="space-y-4">
|
|
<Input label="标题" value={imgTitle} onChange={(e) => setImgTitle(e.target.value)} placeholder="笔记标题" />
|
|
<Textarea label="正文" value={imgContent} onChange={(e) => setImgContent(e.target.value)} placeholder="笔记正文" />
|
|
<Textarea label="图片路径(每行一个)" value={imgPaths} onChange={(e) => setImgPaths(e.target.value)} placeholder="/path/to/image1.jpg /path/to/image2.jpg" />
|
|
<Input label="标签(逗号分隔)" value={imgTags} onChange={(e) => setImgTags(e.target.value)} placeholder="旅行, 美食" />
|
|
<div className="flex gap-4 items-end">
|
|
<Select
|
|
label="可见性"
|
|
options={[
|
|
{ value: 'public', label: '公开' },
|
|
{ value: 'private', label: '私密' },
|
|
{ value: 'friends', label: '仅好友' },
|
|
]}
|
|
value={imgVisibility}
|
|
onChange={(e) => setImgVisibility(e.target.value)}
|
|
/>
|
|
<label className="flex items-center gap-2 pb-2 cursor-pointer">
|
|
<input type="checkbox" checked={imgOriginal} onChange={(e) => setImgOriginal(e.target.checked)} className="rounded" />
|
|
<span className="text-sm text-dark-muted">原创声明</span>
|
|
</label>
|
|
</div>
|
|
<Button onClick={() => void handlePublishImage()} loading={imgLoading}>
|
|
发布图文笔记
|
|
</Button>
|
|
{imgResult !== null && <JsonViewer data={imgResult} />}
|
|
</div>
|
|
</Card>
|
|
)}
|
|
|
|
{tab === 'video' && (
|
|
<Card>
|
|
<div className="space-y-4">
|
|
<Input label="标题" value={vidTitle} onChange={(e) => setVidTitle(e.target.value)} placeholder="笔记标题" />
|
|
<Textarea label="正文" value={vidContent} onChange={(e) => setVidContent(e.target.value)} placeholder="笔记正文" />
|
|
<Input label="视频路径" value={vidPath} onChange={(e) => setVidPath(e.target.value)} placeholder="/path/to/video.mp4" />
|
|
<Input label="标签(逗号分隔)" value={vidTags} onChange={(e) => setVidTags(e.target.value)} placeholder="旅行, vlog" />
|
|
<Select
|
|
label="可见性"
|
|
options={[
|
|
{ value: 'public', label: '公开' },
|
|
{ value: 'private', label: '私密' },
|
|
{ value: 'friends', label: '仅好友' },
|
|
]}
|
|
value={vidVisibility}
|
|
onChange={(e) => setVidVisibility(e.target.value)}
|
|
/>
|
|
<Button onClick={() => void handlePublishVideo()} loading={vidLoading}>
|
|
发布视频笔记
|
|
</Button>
|
|
{vidResult !== null && <JsonViewer data={vidResult} />}
|
|
</div>
|
|
</Card>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|