feat: 本次提交更新内容如下
功能暂时可以了
This commit is contained in:
@@ -37,6 +37,7 @@ import TrafficPool from './pages/traffic-pool/TrafficPool';
|
||||
import ContactImport from './pages/contact-import/ContactImport';
|
||||
import Content from './pages/content/Content';
|
||||
import TrafficPoolDetail from './pages/traffic-pool/TrafficPoolDetail';
|
||||
import NewContent from './pages/content/NewContent';
|
||||
|
||||
function App() {
|
||||
// 初始化HTTP拦截器
|
||||
@@ -89,6 +90,7 @@ function App() {
|
||||
<Route path="/traffic-pool/:id" element={<TrafficPoolDetail />} />
|
||||
<Route path="/contact-import" element={<ContactImport />} />
|
||||
<Route path="/content" element={<Content />} />
|
||||
<Route path="/content/new" element={<NewContent />} />
|
||||
{/* 你可以继续添加更多路由 */}
|
||||
</Routes>
|
||||
</LayoutWrapper>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ChevronLeft, Filter, Search, RefreshCw, Plus, Edit, Trash2, Eye, MoreVertical } from 'lucide-react';
|
||||
import { ChevronLeft, Filter, Search, RefreshCw, Plus, Edit, Trash2, Eye, MoreVertical, Copy } from 'lucide-react';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
@@ -66,6 +66,63 @@ interface ContentLibrary {
|
||||
selectedGroupMembers?: WechatGroupMember[];
|
||||
}
|
||||
|
||||
function CardMenu({ onView, onEdit, onDelete, onViewMaterials }: {
|
||||
onView: () => void;
|
||||
onEdit: () => void;
|
||||
onDelete: () => void;
|
||||
onViewMaterials: () => void;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const menuRef = React.useRef<HTMLDivElement | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
if (open) document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<div style={{ position: "relative" }}>
|
||||
<button onClick={() => setOpen((v) => !v)} style={{ background: "none", border: "none", padding: 0, margin: 0, cursor: "pointer" }}>
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</button>
|
||||
{open && (
|
||||
<div
|
||||
ref={menuRef}
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
top: 28,
|
||||
background: "#fff",
|
||||
borderRadius: 8,
|
||||
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
|
||||
zIndex: 100,
|
||||
minWidth: 120,
|
||||
padding: 4,
|
||||
}}
|
||||
>
|
||||
<div onClick={() => { onView(); setOpen(false); }} style={{ padding: 8, cursor: "pointer", display: "flex", alignItems: "center", borderRadius: 6, fontSize: 14, gap: 6, transition: "background .2s" }} onMouseOver={e => (e.currentTarget as HTMLDivElement).style.background="#f5f5f5"} onMouseOut={e => (e.currentTarget as HTMLDivElement).style.background=""}>
|
||||
<Eye className="h-4 w-4 mr-2" />查看
|
||||
</div>
|
||||
<div onClick={() => { onEdit(); setOpen(false); }} style={{ padding: 8, cursor: "pointer", display: "flex", alignItems: "center", borderRadius: 6, fontSize: 14, gap: 6, transition: "background .2s" }} onMouseOver={e => (e.currentTarget as HTMLDivElement).style.background="#f5f5f5"} onMouseOut={e => (e.currentTarget as HTMLDivElement).style.background=""}>
|
||||
<Edit className="h-4 w-4 mr-2" />编辑
|
||||
</div>
|
||||
<div onClick={() => { onDelete(); setOpen(false); }} style={{ padding: 8, cursor: "pointer", display: "flex", alignItems: "center", borderRadius: 6, fontSize: 14, gap: 6, color: "#e53e3e", transition: "background .2s" }} onMouseOver={e => (e.currentTarget as HTMLDivElement).style.background="#f5f5f5"} onMouseOut={e => (e.currentTarget as HTMLDivElement).style.background=""}>
|
||||
<Trash2 className="h-4 w-4 mr-2" />删除
|
||||
</div>
|
||||
<div onClick={() => { onViewMaterials(); setOpen(false); }} style={{ padding: 8, cursor: "pointer", display: "flex", alignItems: "center", borderRadius: 6, fontSize: 14, gap: 6, transition: "background .2s" }} onMouseOver={e => (e.currentTarget as HTMLDivElement).style.background="#f5f5f5"} onMouseOut={e => (e.currentTarget as HTMLDivElement).style.background=""}>
|
||||
<Eye className="h-4 w-4 mr-2" />查看素材
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Content() {
|
||||
const navigate = useNavigate();
|
||||
const [libraries, setLibraries] = useState<ContentLibrary[]>([]);
|
||||
@@ -192,147 +249,137 @@ export default function Content() {
|
||||
return (
|
||||
<Layout
|
||||
header={
|
||||
<>
|
||||
<UnifiedHeader title="内容库" showBack />
|
||||
<div className="flex items-center space-x-2 p-4">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-2.5 h-4 w-4 text-gray-400" />
|
||||
<Input
|
||||
placeholder="搜索内容库..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
|
||||
className="pl-9"
|
||||
/>
|
||||
<>
|
||||
<UnifiedHeader title="内容库" showBack />
|
||||
<div className="bg-white shadow-sm rounded-b-xl px-4 pt-4 pb-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-2.5 h-4 w-4 text-gray-400" />
|
||||
<Input
|
||||
placeholder="搜索内容库..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
|
||||
className="pl-9 rounded-full bg-gray-50 border-none focus:ring-2 focus:ring-blue-100"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleRefresh}
|
||||
disabled={loading}
|
||||
className="rounded-full border-gray-200"
|
||||
>
|
||||
<RefreshCw className={`h-5 w-5 ${loading ? 'animate-spin' : ''}`} />
|
||||
</Button>
|
||||
<Button onClick={handleCreateNew} className="rounded-full px-4 py-2" size="sm">
|
||||
<Plus className="h-4 w-4 mr-1" />新建
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||||
<TabsList className="grid w-full grid-cols-3 rounded-full bg-gray-100">
|
||||
<TabsTrigger value="all" className="rounded-full">全部</TabsTrigger>
|
||||
<TabsTrigger value="friends" className="rounded-full">微信好友</TabsTrigger>
|
||||
<TabsTrigger value="groups" className="rounded-full">聊天群</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={handleRefresh}
|
||||
disabled={loading}
|
||||
>
|
||||
<RefreshCw className={`h-4 w-4 ${loading ? 'animate-spin' : ''}`} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="px-4">
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||||
<TabsList className="grid w-full grid-cols-3">
|
||||
<TabsTrigger value="all">全部</TabsTrigger>
|
||||
<TabsTrigger value="friends">微信好友</TabsTrigger>
|
||||
<TabsTrigger value="groups">聊天群</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</>
|
||||
}
|
||||
footer={<BottomNav />}
|
||||
>
|
||||
<div className="space-y-4 p-4">
|
||||
<div className="space-y-3">
|
||||
{loading ? (
|
||||
<div className="flex justify-center items-center py-12">
|
||||
<RefreshCw className="h-8 w-8 text-blue-500 animate-spin" />
|
||||
</div>
|
||||
) : filteredLibraries.length === 0 ? (
|
||||
<div className="flex justify-center items-center py-12">
|
||||
<div className="text-center">
|
||||
<p className="text-gray-500 mb-4">暂无数据</p>
|
||||
<Button onClick={handleCreateNew} size="sm">
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
新建内容库
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
filteredLibraries.map((library) => (
|
||||
<Card key={library.id} className="p-4 hover:bg-gray-50">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<h3 className="font-medium text-base">{library.name}</h3>
|
||||
<Badge variant={library.isEnabled === 1 ? 'default' : 'secondary'} className="text-xs">
|
||||
{library.isEnabled === 1 ? '已启用' : '未启用'}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 space-y-1">
|
||||
<div className="flex items-center space-x-1">
|
||||
<span>来源:</span>
|
||||
{library.sourceType === 1 && library.sourceFriends?.length > 0 ? (
|
||||
<div className="space-y-3">
|
||||
{loading ? (
|
||||
<div className="flex justify-center items-center py-12">
|
||||
<RefreshCw className="h-8 w-8 text-blue-500 animate-spin" />
|
||||
</div>
|
||||
) : filteredLibraries.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-16 text-gray-400">
|
||||
<img src="/empty-state-content.svg" alt="暂无内容库" className="w-32 h-32 mb-4 opacity-80" />
|
||||
<div className="mb-2">暂无内容库,快去新建一个吧!</div>
|
||||
<Button onClick={handleCreateNew} size="sm" className="rounded-full px-6">新建内容库</Button>
|
||||
</div>
|
||||
) : (
|
||||
filteredLibraries.map((library, idx) => (
|
||||
<Card
|
||||
key={library.id}
|
||||
className={`p-4 rounded-xl shadow-sm border border-gray-100 transition hover:shadow-md bg-white ${idx !== filteredLibraries.length - 1 ? 'mb-2' : ''}`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<h3 className="font-medium text-base text-gray-900">{library.name}</h3>
|
||||
<Badge variant={library.isEnabled === 1 ? 'default' : 'secondary'} className="text-xs rounded-full px-2">
|
||||
{library.isEnabled === 1 ? '已启用' : '未启用'}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 space-y-1">
|
||||
<div className="flex items-center space-x-1">
|
||||
<span>来源:</span>
|
||||
{library.sourceType === 1 && library.sourceFriends?.length > 0 ? (
|
||||
<div className="flex -space-x-1 overflow-hidden">
|
||||
{(library.friendsData || []).slice(0, 3).map((friend) => (
|
||||
<img
|
||||
key={friend.id}
|
||||
src={friend.avatar || '/placeholder.svg'}
|
||||
alt={friend.nickname || `好友${friend.id}`}
|
||||
className="inline-block h-6 w-6 rounded-full ring-2 ring-white"
|
||||
/>
|
||||
))}
|
||||
{library.sourceFriends.length > 3 && (
|
||||
<span className="flex items-center justify-center h-6 w-6 rounded-full bg-gray-200 text-xs font-medium text-gray-800">
|
||||
+{library.sourceFriends.length - 3}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
) : library.sourceType === 2 && library.sourceGroups?.length > 0 ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="flex -space-x-1 overflow-hidden">
|
||||
{(library.friendsData || []).slice(0, 3).map((friend) => (
|
||||
{(library.groupsData || []).slice(0, 3).map((group) => (
|
||||
<img
|
||||
key={friend.id}
|
||||
src={friend.avatar || '/placeholder.svg'}
|
||||
alt={friend.nickname || `好友${friend.id}`}
|
||||
key={group.id}
|
||||
src={group.avatar || '/placeholder.svg'}
|
||||
alt={group.name || `群组${group.id}`}
|
||||
className="inline-block h-6 w-6 rounded-full ring-2 ring-white"
|
||||
/>
|
||||
))}
|
||||
{library.sourceFriends.length > 3 && (
|
||||
{library.sourceGroups.length > 3 && (
|
||||
<span className="flex items-center justify-center h-6 w-6 rounded-full bg-gray-200 text-xs font-medium text-gray-800">
|
||||
+{library.sourceFriends.length - 3}
|
||||
+{library.sourceGroups.length - 3}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
) : library.sourceType === 2 && library.sourceGroups?.length > 0 ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="flex -space-x-1 overflow-hidden">
|
||||
{(library.groupsData || []).slice(0, 3).map((group) => (
|
||||
<img
|
||||
key={group.id}
|
||||
src={group.avatar || '/placeholder.svg'}
|
||||
alt={group.name || `群组${group.id}`}
|
||||
className="inline-block h-6 w-6 rounded-full ring-2 ring-white"
|
||||
/>
|
||||
))}
|
||||
{library.sourceGroups.length > 3 && (
|
||||
<span className="flex items-center justify-center h-6 w-6 rounded-full bg-gray-200 text-xs font-medium text-gray-800">
|
||||
+{library.sourceGroups.length - 3}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-6 h-6 bg-gray-200 rounded-full"></div>
|
||||
)}
|
||||
</div>
|
||||
<div>创建人:{library.creator}</div>
|
||||
<div>内容数量:{library.itemCount}</div>
|
||||
<div>更新时间:{new Date(library.updateTime).toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-6 h-6 bg-gray-200 rounded-full"></div>
|
||||
)}
|
||||
</div>
|
||||
<div>创建人:{library.creator}</div>
|
||||
<div>内容数量:{library.itemCount}</div>
|
||||
<div>更新时间:{new Date(library.updateTime).toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})}</div>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8">
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => handleEdit(library.id)}>
|
||||
<Edit className="h-4 w-4 mr-2" />
|
||||
编辑
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => handleDelete(library.id)}>
|
||||
<Trash2 className="h-4 w-4 mr-2" />
|
||||
删除
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => handleViewMaterials(library.id)}>
|
||||
<Eye className="h-4 w-4 mr-2" />
|
||||
查看素材
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</Card>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
<CardMenu
|
||||
onView={() => navigate(`/content/${library.id}`)}
|
||||
onEdit={() => handleEdit(library.id)}
|
||||
onDelete={() => handleDelete(library.id)}
|
||||
onViewMaterials={() => handleViewMaterials(library.id)}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
202
nkebao/src/pages/content/NewContent.tsx
Normal file
202
nkebao/src/pages/content/NewContent.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import Layout from '@/components/Layout';
|
||||
import UnifiedHeader from '@/components/UnifiedHeader';
|
||||
import BottomNav from '@/components/BottomNav';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Collapse, CollapsePanel } from 'tdesign-mobile-react';
|
||||
import { toast } from '@/components/ui/toast';
|
||||
// TODO: 引入微信好友/群组选择器、日期选择器等组件
|
||||
|
||||
interface WechatFriend { id: string; nickname: string; avatar: string; }
|
||||
interface WechatGroup { id: string; name: string; avatar: string; }
|
||||
|
||||
interface ContentLibraryForm {
|
||||
name: string;
|
||||
sourceType: 'friends' | 'groups';
|
||||
keywordsInclude: string;
|
||||
keywordsExclude: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
selectedFriends: WechatFriend[];
|
||||
selectedGroups: WechatGroup[];
|
||||
useAI: boolean;
|
||||
aiPrompt: string;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export default function NewContentLibraryPage() {
|
||||
const navigate = useNavigate();
|
||||
const [form, setForm] = useState<ContentLibraryForm>({
|
||||
name: '',
|
||||
sourceType: 'friends',
|
||||
keywordsInclude: '',
|
||||
keywordsExclude: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
selectedFriends: [],
|
||||
selectedGroups: [],
|
||||
useAI: false,
|
||||
aiPrompt: '',
|
||||
enabled: true,
|
||||
});
|
||||
const [isFriendSelectorOpen, setIsFriendSelectorOpen] = useState(false);
|
||||
const [isGroupSelectorOpen, setIsGroupSelectorOpen] = useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// TODO: 选择器、日期选择器等逻辑
|
||||
|
||||
const handleSave = async () => {
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
toast({ title: '创建成功', description: '内容库已保存' });
|
||||
navigate('/content');
|
||||
} catch (error) {
|
||||
toast({ title: '创建失败', description: '保存内容库失败', variant: 'destructive' });
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout
|
||||
header={<UnifiedHeader title="新建内容库" showBack onBack={() => navigate(-1)} />}
|
||||
footer={<BottomNav />}
|
||||
>
|
||||
<div className="flex-1 bg-gray-50 min-h-screen pb-16">
|
||||
<div className="p-4 space-y-4 max-w-lg mx-auto">
|
||||
<Card className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block font-medium mb-1">内容库名称 <span className="text-red-500">*</span></label>
|
||||
<Input
|
||||
value={form.name}
|
||||
onChange={e => setForm(f => ({ ...f, name: e.target.value }))}
|
||||
placeholder="请输入内容库名称"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block font-medium mb-1">数据来源配置</label>
|
||||
<Tabs value={form.sourceType} onValueChange={val => setForm(f => ({ ...f, sourceType: val as 'friends' | 'groups' }))}>
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="friends">选择微信好友</TabsTrigger>
|
||||
<TabsTrigger value="groups">选择聊天群</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="friends">
|
||||
<Button variant="outline" className="w-full" onClick={() => setIsFriendSelectorOpen(true)}>
|
||||
选择微信好友
|
||||
</Button>
|
||||
{form.selectedFriends.length > 0 && (
|
||||
<div className="mt-2 space-y-2">
|
||||
{form.selectedFriends.map(friend => (
|
||||
<div key={friend.id} className="flex items-center justify-between bg-gray-100 p-2 rounded-md">
|
||||
<span>{friend.nickname}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="groups">
|
||||
<Button variant="outline" className="w-full" onClick={() => setIsGroupSelectorOpen(true)}>
|
||||
选择聊天群
|
||||
</Button>
|
||||
{form.selectedGroups.length > 0 && (
|
||||
<div className="mt-2 space-y-2">
|
||||
{form.selectedGroups.map(group => (
|
||||
<div key={group.id} className="flex items-center justify-between bg-gray-100 p-2 rounded-md">
|
||||
<span>{group.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
<Collapse>
|
||||
<CollapsePanel header="关键字设置" value="keywords">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block font-medium mb-1">关键字匹配</label>
|
||||
<Textarea
|
||||
value={form.keywordsInclude}
|
||||
onChange={e => setForm(f => ({ ...f, keywordsInclude: e.target.value }))}
|
||||
placeholder="如果设置了关键字,系统只会采集含有关键字的内容。多个关键字,用半角的','隔开。"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block font-medium mb-1">关键字排除</label>
|
||||
<Textarea
|
||||
value={form.keywordsExclude}
|
||||
onChange={e => setForm(f => ({ ...f, keywordsExclude: e.target.value }))}
|
||||
placeholder="排除含有这些关键字的内容。多个关键字,用半角的','隔开。"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsePanel>
|
||||
</Collapse>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<label className="block font-medium">是否启用AI</label>
|
||||
</div>
|
||||
<div className='w-10'>
|
||||
<Switch checked={form.useAI} onCheckedChange={checked => setForm(f => ({ ...f, useAI: checked }))} />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 mt-1 ">当启用AI之后,该内容库下的所有内容,都会通过AI重新生成内容。</p>
|
||||
{form.useAI && (
|
||||
<div>
|
||||
<label className="block font-medium mb-1">AI 提示词</label>
|
||||
<Textarea
|
||||
value={form.aiPrompt}
|
||||
onChange={e => setForm(f => ({ ...f, aiPrompt: e.target.value }))}
|
||||
placeholder="请输入 AI 提示词"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<label className="block font-medium mb-2">时间限制</label>
|
||||
{/* TODO: 替换为TDesign日期范围选择器 */}
|
||||
<div className='flex mb-2' style={{ justifyContent: 'space-between' }}>
|
||||
<label className='text-sm w-20 '>开始时间</label>
|
||||
<Input
|
||||
type="date"
|
||||
value={form.startDate}
|
||||
onChange={e => setForm(f => ({ ...f, startDate: e.target.value }))}
|
||||
className="inline-block w-1/2 "
|
||||
/>
|
||||
</div>
|
||||
<div className='flex '>
|
||||
<label className='text-sm w-20' >结束时间</label>
|
||||
<Input
|
||||
type="date"
|
||||
value={form.endDate}
|
||||
onChange={e => setForm(f => ({ ...f, endDate: e.target.value }))}
|
||||
className="inline-block w-1/2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block font-medium mb-1">是否启用</label>
|
||||
<Switch checked={form.enabled} onCheckedChange={checked => setForm(f => ({ ...f, enabled: checked }))} />
|
||||
</div>
|
||||
<div className="flex justify-end mt-4">
|
||||
<Button onClick={handleSave} disabled={isSubmitting || !form.name}>
|
||||
{isSubmitting ? '创建中...' : '创建内容库'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
{/* TODO: 微信好友/群组选择器弹窗、日期选择器弹窗 */}
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user