【操盘手】 内容库 - 操作优化
This commit is contained in:
@@ -31,6 +31,7 @@ interface ContentLibraryDetail {
|
|||||||
selectedFriends?: WechatFriend[]
|
selectedFriends?: WechatFriend[]
|
||||||
selectedGroups?: WechatGroup[]
|
selectedGroups?: WechatGroup[]
|
||||||
selectedGroupMembers?: WechatGroupMember[]
|
selectedGroupMembers?: WechatGroupMember[]
|
||||||
|
groupMembers?: Record<string, number[]>
|
||||||
keywordInclude: string[]
|
keywordInclude: string[]
|
||||||
keywordExclude: string[]
|
keywordExclude: string[]
|
||||||
isEnabled: number
|
isEnabled: number
|
||||||
@@ -88,7 +89,37 @@ export default function EditContentLibraryPage({ params }: { params: Promise<{ i
|
|||||||
// 直接使用API返回的好友和群组数据
|
// 直接使用API返回的好友和群组数据
|
||||||
const friends = data.selectedFriends || [];
|
const friends = data.selectedFriends || [];
|
||||||
const groups = data.selectedGroups || [];
|
const groups = data.selectedGroups || [];
|
||||||
const groupMembers = data.selectedGroupMembers || [];
|
|
||||||
|
// 根据接口返回的groupMembers字段构建selectedGroupMembers数组
|
||||||
|
let groupMembers: WechatGroupMember[] = [];
|
||||||
|
|
||||||
|
// 检查是否有groupMembers数据
|
||||||
|
if (data.groupMembers) {
|
||||||
|
// 将 {"813941": [262439]} 格式转换为 WechatGroupMember[] 数组
|
||||||
|
Object.entries(data.groupMembers).forEach(([groupId, memberIds]) => {
|
||||||
|
// 确保memberIds是数组
|
||||||
|
if (Array.isArray(memberIds)) {
|
||||||
|
memberIds.forEach(memberId => {
|
||||||
|
// 在groups中查找对应的group信息
|
||||||
|
const group = groups.find(g => g.id === groupId);
|
||||||
|
|
||||||
|
if (group) {
|
||||||
|
// 将group信息与member ID组合成 WechatGroupMember 对象
|
||||||
|
groupMembers.push({
|
||||||
|
id: String(memberId),
|
||||||
|
groupId: groupId,
|
||||||
|
nickname: "", // 默认为空,实际使用时会由WechatGroupMemberSelector填充
|
||||||
|
avatar: "", // 默认为空,实际使用时会由WechatGroupMemberSelector填充
|
||||||
|
wechatId: "" // 添加必要的wechatId字段
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (data.selectedGroupMembers) {
|
||||||
|
// 如果有selectedGroupMembers字段,则直接使用
|
||||||
|
groupMembers = data.selectedGroupMembers;
|
||||||
|
}
|
||||||
|
|
||||||
setFormData({
|
setFormData({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
@@ -160,13 +191,25 @@ export default function EditContentLibraryPage({ params }: { params: Promise<{ i
|
|||||||
const loadingToast = showToast("正在更新内容库...", "loading", true);
|
const loadingToast = showToast("正在更新内容库...", "loading", true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 将群成员数据转换为{"813941": [262439]}格式
|
||||||
|
const groupMembersMap: Record<string, string[]> = {}
|
||||||
|
|
||||||
|
formData.selectedGroupMembers.forEach(member => {
|
||||||
|
if (member.groupId) {
|
||||||
|
if (!groupMembersMap[member.groupId]) {
|
||||||
|
groupMembersMap[member.groupId] = []
|
||||||
|
}
|
||||||
|
groupMembersMap[member.groupId].push(member.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
id: resolvedParams.id,
|
id: resolvedParams.id,
|
||||||
name: formData.name,
|
name: formData.name,
|
||||||
sourceType: formData.sourceType === "friends" ? 1 : 2,
|
sourceType: formData.sourceType === "friends" ? 1 : 2,
|
||||||
friends: formData.selectedFriends.map(f => f.id),
|
friends: formData.selectedFriends.map(f => f.id),
|
||||||
groups: formData.selectedGroups.map(g => g.id),
|
groups: formData.selectedGroups.map(g => g.id),
|
||||||
groupMembers: formData.selectedGroupMembers.map(m => m.id),
|
groupMembers: groupMembersMap, // 使用JSON字符串传递群成员数据
|
||||||
keywordInclude: formData.keywordsInclude.split(",").map(k => k.trim()).filter(Boolean),
|
keywordInclude: formData.keywordsInclude.split(",").map(k => k.trim()).filter(Boolean),
|
||||||
keywordExclude: formData.keywordsExclude.split(",").map(k => k.trim()).filter(Boolean),
|
keywordExclude: formData.keywordsExclude.split(",").map(k => k.trim()).filter(Boolean),
|
||||||
aiPrompt: formData.useAI ? formData.aiPrompt : "",
|
aiPrompt: formData.useAI ? formData.aiPrompt : "",
|
||||||
@@ -283,16 +326,21 @@ export default function EditContentLibraryPage({ params }: { params: Promise<{ i
|
|||||||
alt={group.name}
|
alt={group.name}
|
||||||
className="w-8 h-8 rounded-full"
|
className="w-8 h-8 rounded-full"
|
||||||
/>
|
/>
|
||||||
<span>{group.name}</span>
|
<span className="truncate max-w-[220px]">{group.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => handleSelectGroupMembers(group.id)}
|
onClick={() => handleSelectGroupMembers(group.id)}
|
||||||
className="mr-1"
|
className={`mr-1 ${formData.selectedGroupMembers.some(m => m.groupId === group.id) ? 'text-blue-500' : ''}`}
|
||||||
>
|
>
|
||||||
<Users className="h-4 w-4" />
|
<Users className="h-4 w-4" />
|
||||||
|
{formData.selectedGroupMembers.some(m => m.groupId === group.id) && (
|
||||||
|
<span className="ml-1 text-xs">
|
||||||
|
+{formData.selectedGroupMembers.filter(m => m.groupId === group.id).length}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" onClick={() => removeGroup(group.id)}>
|
<Button variant="ghost" size="sm" onClick={() => removeGroup(group.id)}>
|
||||||
<X className="h-4 w-4" />
|
<X className="h-4 w-4" />
|
||||||
@@ -302,43 +350,6 @@ export default function EditContentLibraryPage({ params }: { params: Promise<{ i
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{formData.selectedGroupMembers.length > 0 && (
|
|
||||||
<div className="mt-4">
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<Label className="text-sm font-medium">已选择群成员 ({formData.selectedGroupMembers.length})</Label>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => setFormData({...formData, selectedGroupMembers: []})}
|
|
||||||
>
|
|
||||||
清空
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-wrap gap-2 p-2 bg-gray-50 rounded-md max-h-[150px] overflow-y-auto">
|
|
||||||
{formData.selectedGroupMembers.map((member) => (
|
|
||||||
<div key={member.id} className="flex items-center bg-white px-2 py-1 rounded border">
|
|
||||||
<Avatar className="h-6 w-6 mr-2">
|
|
||||||
<AvatarImage src={member.avatar} />
|
|
||||||
<AvatarFallback>{member.nickname?.[0] || '?'}</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<span className="text-sm">{member.nickname}</span>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="h-6 w-6 p-0 ml-1"
|
|
||||||
onClick={() => setFormData({
|
|
||||||
...formData,
|
|
||||||
selectedGroupMembers: formData.selectedGroupMembers.filter(m => m.id !== member.id)
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<X className="h-3 w-3" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -88,12 +88,24 @@ export default function NewContentLibraryPage() {
|
|||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
|
// 将群成员数据转换为{"群ID":[成员ID1, 成员ID2...]}的格式
|
||||||
|
const groupMembersMap: Record<string, string[]> = {}
|
||||||
|
|
||||||
|
formData.selectedGroupMembers.forEach(member => {
|
||||||
|
if (member.groupId) {
|
||||||
|
if (!groupMembersMap[member.groupId]) {
|
||||||
|
groupMembersMap[member.groupId] = []
|
||||||
|
}
|
||||||
|
groupMembersMap[member.groupId].push(member.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
name: formData.name,
|
name: formData.name,
|
||||||
sourceType: formData.sourceType === "friends" ? 1 : 2,
|
sourceType: formData.sourceType === "friends" ? 1 : 2,
|
||||||
friends: formData.selectedFriends.map(f => f.id),
|
friends: formData.selectedFriends.map(f => f.id),
|
||||||
groups: formData.selectedGroups.map(g => g.id),
|
groups: formData.selectedGroups.map(g => g.id),
|
||||||
groupMembers: formData.selectedGroupMembers.map(m => m.id),
|
groupMembers: groupMembersMap, // 使用JSON字符串传递群成员数据
|
||||||
keywordInclude: formData.keywordsInclude.split(",").map(k => k.trim()).filter(Boolean),
|
keywordInclude: formData.keywordsInclude.split(",").map(k => k.trim()).filter(Boolean),
|
||||||
keywordExclude: formData.keywordsExclude.split(",").map(k => k.trim()).filter(Boolean),
|
keywordExclude: formData.keywordsExclude.split(",").map(k => k.trim()).filter(Boolean),
|
||||||
aiPrompt: formData.useAI ? formData.aiPrompt : "",
|
aiPrompt: formData.useAI ? formData.aiPrompt : "",
|
||||||
@@ -196,16 +208,21 @@ export default function NewContentLibraryPage() {
|
|||||||
alt={group.name}
|
alt={group.name}
|
||||||
className="w-8 h-8 rounded-full"
|
className="w-8 h-8 rounded-full"
|
||||||
/>
|
/>
|
||||||
<span>{group.name}</span>
|
<span className="truncate max-w-[220px]">{group.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => handleSelectGroupMembers(group.id)}
|
onClick={() => handleSelectGroupMembers(group.id)}
|
||||||
className="mr-1"
|
className={`mr-1 ${formData.selectedGroupMembers.some(m => m.groupId === group.id) ? 'text-blue-500' : ''}`}
|
||||||
>
|
>
|
||||||
<Users className="h-4 w-4" />
|
<Users className="h-4 w-4" />
|
||||||
|
{formData.selectedGroupMembers.some(m => m.groupId === group.id) && (
|
||||||
|
<span className="ml-1 text-xs">
|
||||||
|
+{formData.selectedGroupMembers.filter(m => m.groupId === group.id).length}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" onClick={() => removeGroup(group.id)}>
|
<Button variant="ghost" size="sm" onClick={() => removeGroup(group.id)}>
|
||||||
<X className="h-4 w-4" />
|
<X className="h-4 w-4" />
|
||||||
@@ -215,43 +232,6 @@ export default function NewContentLibraryPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{formData.selectedGroupMembers.length > 0 && (
|
|
||||||
<div className="mt-4">
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<Label className="text-sm font-medium">已选择群成员 ({formData.selectedGroupMembers.length})</Label>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => setFormData({...formData, selectedGroupMembers: []})}
|
|
||||||
>
|
|
||||||
清空
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-wrap gap-2 p-2 bg-gray-50 rounded-md max-h-[150px] overflow-y-auto">
|
|
||||||
{formData.selectedGroupMembers.map((member) => (
|
|
||||||
<div key={member.id} className="flex items-center bg-white px-2 py-1 rounded border">
|
|
||||||
<Avatar className="h-6 w-6 mr-2">
|
|
||||||
<AvatarImage src={member.avatar} />
|
|
||||||
<AvatarFallback>{member.nickname?.[0] || '?'}</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<span className="text-sm">{member.nickname}</span>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="h-6 w-6 p-0 ml-1"
|
|
||||||
onClick={() => setFormData({
|
|
||||||
...formData,
|
|
||||||
selectedGroupMembers: formData.selectedGroupMembers.filter(m => m.id !== member.id)
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<X className="h-3 w-3" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
1
Cunkebao/app/types/wechat.ts
Normal file
1
Cunkebao/app/types/wechat.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -49,7 +49,10 @@ export function WechatGroupMemberSelector({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open && groupId) {
|
if (open && groupId) {
|
||||||
fetchGroupMembers(1)
|
fetchGroupMembers(1)
|
||||||
setTempSelectedMembers([...selectedMembers])
|
|
||||||
|
// 过滤出当前群组的已选成员
|
||||||
|
const currentGroupSelectedMembers = selectedMembers.filter(member => member.groupId === groupId)
|
||||||
|
setTempSelectedMembers(currentGroupSelectedMembers)
|
||||||
}
|
}
|
||||||
}, [open, groupId, selectedMembers])
|
}, [open, groupId, selectedMembers])
|
||||||
|
|
||||||
@@ -77,16 +80,39 @@ export function WechatGroupMemberSelector({
|
|||||||
role = "member";
|
role = "member";
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const memberId = item.id || `member-${Math.random()}`;
|
||||||
id: item.id || `member-${Math.random()}`,
|
|
||||||
|
// 创建成员对象
|
||||||
|
const member = {
|
||||||
|
id: memberId,
|
||||||
nickname: item.name || item.ownerNickname || '未知成员',
|
nickname: item.name || item.ownerNickname || '未知成员',
|
||||||
wechatId: item.ownerWechatId || item.identifier || '',
|
wechatId: item.ownerWechatId || item.identifier || '',
|
||||||
avatar: item.ownerAvatar || item.avatar || '/placeholder.svg',
|
avatar: item.ownerAvatar || item.avatar || '/placeholder.svg',
|
||||||
role: role,
|
role: role,
|
||||||
joinTime: item.createTime ? new Date(item.createTime * 1000).toLocaleString() : '--'
|
joinTime: item.createTime ? new Date(item.createTime * 1000).toLocaleString() : '--',
|
||||||
|
groupId: groupId
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return member;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 处理已选择的成员
|
||||||
|
// 如果已选择的成员在新获取的成员列表中,更新其信息
|
||||||
|
const updatedTempSelected = tempSelectedMembers.map(selected => {
|
||||||
|
const foundInNewList = membersList.find(m => m.id === selected.id);
|
||||||
|
if (foundInNewList) {
|
||||||
|
return {
|
||||||
|
...selected,
|
||||||
|
nickname: foundInNewList.nickname,
|
||||||
|
avatar: foundInNewList.avatar,
|
||||||
|
wechatId: foundInNewList.wechatId,
|
||||||
|
role: foundInNewList.role
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return selected;
|
||||||
|
});
|
||||||
|
|
||||||
|
setTempSelectedMembers(updatedTempSelected);
|
||||||
setMembers(membersList)
|
setMembers(membersList)
|
||||||
setTotalItems(response.data.total)
|
setTotalItems(response.data.total)
|
||||||
setTotalPages(Math.ceil(response.data.total / pageSize))
|
setTotalPages(Math.ceil(response.data.total / pageSize))
|
||||||
@@ -118,6 +144,16 @@ export function WechatGroupMemberSelector({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSelect = (member: WechatGroupMember, checked: boolean) => {
|
||||||
|
if (checked) {
|
||||||
|
// 添加成员
|
||||||
|
setTempSelectedMembers([...tempSelectedMembers, member]);
|
||||||
|
} else {
|
||||||
|
// 移除成员
|
||||||
|
setTempSelectedMembers(tempSelectedMembers.filter(m => m.id !== member.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="max-w-md">
|
<DialogContent className="max-w-md">
|
||||||
@@ -154,13 +190,7 @@ export function WechatGroupMemberSelector({
|
|||||||
<div key={member.id} className="flex items-center space-x-3 p-2 hover:bg-gray-100 rounded-lg">
|
<div key={member.id} className="flex items-center space-x-3 p-2 hover:bg-gray-100 rounded-lg">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={tempSelectedMembers.some((m) => m.id === member.id)}
|
checked={tempSelectedMembers.some((m) => m.id === member.id)}
|
||||||
onCheckedChange={(checked) => {
|
onCheckedChange={(checked) => handleSelect(member, checked === true)}
|
||||||
if (checked) {
|
|
||||||
setTempSelectedMembers([...tempSelectedMembers, member])
|
|
||||||
} else {
|
|
||||||
setTempSelectedMembers(tempSelectedMembers.filter((m) => m.id !== member.id))
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Avatar>
|
<Avatar>
|
||||||
<AvatarImage src={member.avatar} />
|
<AvatarImage src={member.avatar} />
|
||||||
@@ -216,8 +246,10 @@ export function WechatGroupMemberSelector({
|
|||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => {
|
<Button onClick={() => {
|
||||||
onSelect(tempSelectedMembers)
|
// 从selectedMembers中移除当前群组的所有成员,然后添加新选择的成员
|
||||||
onOpenChange(false)
|
const otherGroupMembers = selectedMembers.filter(member => member.groupId !== groupId);
|
||||||
|
onSelect([...otherGroupMembers, ...tempSelectedMembers]);
|
||||||
|
onOpenChange(false);
|
||||||
}}>
|
}}>
|
||||||
确定 ({tempSelectedMembers.length})
|
确定 ({tempSelectedMembers.length})
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -31,4 +31,5 @@ export interface WechatGroupMember {
|
|||||||
gender?: "male" | "female"
|
gender?: "male" | "female"
|
||||||
role?: "owner" | "admin" | "member"
|
role?: "owner" | "admin" | "member"
|
||||||
joinTime?: string
|
joinTime?: string
|
||||||
|
groupId?: string
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user