内容库群选择功能 + 场景获客分页修复
This commit is contained in:
@@ -142,7 +142,11 @@ export default function SelectionPopup({
|
||||
// 复制一份selectedOptions到临时变量
|
||||
setTempSelectedOptions([...selectedOptions]);
|
||||
fetchGroups(1, "");
|
||||
} else {
|
||||
// 弹窗关闭时重置状态
|
||||
setTempSelectedOptions([]);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [visible]);
|
||||
|
||||
// 搜索防抖(只在弹窗打开且搜索词变化时执行)
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inputWrapper {
|
||||
position: relative;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.inputIcon {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #bdbdbd;
|
||||
font-size: 20px;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.input {
|
||||
padding-left: 38px !important;
|
||||
height: 48px;
|
||||
border-radius: 16px !important;
|
||||
border: 1px solid #e5e6eb !important;
|
||||
font-size: 16px;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.clearBtn {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #999;
|
||||
font-size: 16px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.selectedGroupsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.groupCard {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
border: 1px solid #e5e6eb;
|
||||
}
|
||||
|
||||
.groupHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.groupInfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.groupAvatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.groupDetails {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.groupName {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #222;
|
||||
margin-bottom: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.groupId {
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.deleteGroupBtn {
|
||||
color: #ff4d4f;
|
||||
font-size: 18px;
|
||||
padding: 4px;
|
||||
min-width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.membersSection {
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.membersLabel {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.membersList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.memberItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.memberAvatar {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.removeMemberBtn {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -4px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
min-width: 20px;
|
||||
padding: 0;
|
||||
background: #fff;
|
||||
border: 1px solid #e5e6eb;
|
||||
border-radius: 50%;
|
||||
color: #ff4d4f;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.memberName {
|
||||
font-size: 12px;
|
||||
color: #222;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.addMemberBtn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 50%;
|
||||
background: #fafafa;
|
||||
color: #999;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
gap: 4px;
|
||||
|
||||
span {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #f0f0f0;
|
||||
border-color: #1677ff;
|
||||
color: #1677ff;
|
||||
}
|
||||
}
|
||||
|
||||
.memberSelectionPopup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.popupHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.popupTitle {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.closeBtn {
|
||||
color: #1677ff;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.memberList {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
.memberListItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
cursor: pointer;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
.memberListItemName {
|
||||
color: #1677ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.memberListItemAvatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.memberListItemName {
|
||||
flex: 1;
|
||||
font-size: 16px;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
color: #1677ff;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.loadingBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.loadingText {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.emptyBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.emptyText {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
363
Cunkebao/src/components/GroupSelectionWithMembers/index.tsx
Normal file
363
Cunkebao/src/components/GroupSelectionWithMembers/index.tsx
Normal file
@@ -0,0 +1,363 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { SearchOutlined, DeleteOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import { Button, Input, Popup } from "antd-mobile";
|
||||
import { Avatar } from "antd-mobile";
|
||||
import style from "./index.module.scss";
|
||||
import GroupSelection from "../GroupSelection";
|
||||
import { GroupSelectionItem } from "../GroupSelection/data";
|
||||
import request from "@/api/request";
|
||||
|
||||
// 群成员接口
|
||||
export interface GroupMember {
|
||||
id: string;
|
||||
nickname: string;
|
||||
wechatId: string;
|
||||
avatar: string;
|
||||
gender?: "male" | "female";
|
||||
role?: "owner" | "admin" | "member";
|
||||
}
|
||||
|
||||
// 带成员的群选项
|
||||
export interface GroupWithMembers extends GroupSelectionItem {
|
||||
members?: GroupMember[];
|
||||
groupId?: string; // 用于关联成员和群
|
||||
}
|
||||
|
||||
interface GroupSelectionWithMembersProps {
|
||||
selectedGroups: GroupWithMembers[];
|
||||
onSelect: (groups: GroupWithMembers[]) => void;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
// 获取群成员列表
|
||||
const getGroupMembers = async (
|
||||
groupId: string,
|
||||
page: number = 1,
|
||||
limit: number = 100,
|
||||
keyword: string = "",
|
||||
): Promise<GroupMember[]> => {
|
||||
try {
|
||||
const params: any = {
|
||||
page,
|
||||
limit,
|
||||
groupId,
|
||||
};
|
||||
if (keyword.trim()) {
|
||||
params.keyword = keyword.trim();
|
||||
}
|
||||
const response = await request("/v1/kefu/wechatChatroom/members", params, "GET");
|
||||
// request 拦截器会返回 res.data.data ?? res.data
|
||||
// 对于 { code: 200, data: { list: [...] } } 的返回,拦截器会返回 { list: [...] }
|
||||
const memberList = response?.list || response?.data?.list || [];
|
||||
|
||||
// 映射接口返回的数据结构到我们的接口
|
||||
return memberList.map((item: any) => ({
|
||||
id: String(item.id),
|
||||
nickname: item.nickname || "",
|
||||
wechatId: item.wechatId || "",
|
||||
avatar: item.avatar || "",
|
||||
gender: undefined, // 接口未返回,暂时设为 undefined
|
||||
role: undefined, // 接口未返回,暂时设为 undefined
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("获取群成员失败:", error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const GroupSelectionWithMembers: React.FC<GroupSelectionWithMembersProps> = ({
|
||||
selectedGroups,
|
||||
onSelect,
|
||||
placeholder = "选择聊天群",
|
||||
className = "",
|
||||
readonly = false,
|
||||
}) => {
|
||||
const [groupSelectionVisible, setGroupSelectionVisible] = useState(false);
|
||||
const [memberSelectionVisible, setMemberSelectionVisible] = useState<{
|
||||
visible: boolean;
|
||||
groupId: string;
|
||||
}>({ visible: false, groupId: "" });
|
||||
const [allMembers, setAllMembers] = useState<Record<string, GroupMember[]>>({});
|
||||
const [selectedMembers, setSelectedMembers] = useState<Record<string, GroupMember[]>>({});
|
||||
const [loadingMembers, setLoadingMembers] = useState(false);
|
||||
|
||||
// 处理群选择
|
||||
const handleGroupSelect = (groups: GroupSelectionItem[]) => {
|
||||
const groupsWithMembers: GroupWithMembers[] = groups.map(group => {
|
||||
const existing = selectedGroups.find(g => g.id === group.id);
|
||||
return {
|
||||
...group,
|
||||
members: existing?.members || [],
|
||||
};
|
||||
});
|
||||
onSelect(groupsWithMembers);
|
||||
setGroupSelectionVisible(false);
|
||||
};
|
||||
|
||||
// 删除群
|
||||
const handleRemoveGroup = (groupId: string) => {
|
||||
if (readonly) return;
|
||||
const newGroups = selectedGroups.filter(g => g.id !== groupId);
|
||||
const newSelectedMembers = { ...selectedMembers };
|
||||
delete newSelectedMembers[groupId];
|
||||
setSelectedMembers(newSelectedMembers);
|
||||
onSelect(newGroups);
|
||||
};
|
||||
|
||||
// 打开成员选择弹窗
|
||||
const handleOpenMemberSelection = async (groupId: string) => {
|
||||
if (readonly) return;
|
||||
setMemberSelectionVisible({ visible: true, groupId });
|
||||
|
||||
// 如果还没有加载过该群的成员列表,则加载
|
||||
if (!allMembers[groupId]) {
|
||||
setLoadingMembers(true);
|
||||
try {
|
||||
const members = await getGroupMembers(groupId);
|
||||
setAllMembers(prev => ({ ...prev, [groupId]: members }));
|
||||
} catch (error) {
|
||||
console.error("加载群成员失败:", error);
|
||||
} finally {
|
||||
setLoadingMembers(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭成员选择弹窗
|
||||
const handleCloseMemberSelection = () => {
|
||||
setMemberSelectionVisible({ visible: false, groupId: "" });
|
||||
};
|
||||
|
||||
// 选择成员
|
||||
const handleSelectMember = (groupId: string, member: GroupMember) => {
|
||||
if (readonly) return;
|
||||
const currentMembers = selectedMembers[groupId] || [];
|
||||
const isSelected = currentMembers.some(m => m.id === member.id);
|
||||
|
||||
let newSelectedMembers = { ...selectedMembers };
|
||||
if (isSelected) {
|
||||
newSelectedMembers[groupId] = currentMembers.filter(m => m.id !== member.id);
|
||||
} else {
|
||||
newSelectedMembers[groupId] = [...currentMembers, member];
|
||||
}
|
||||
setSelectedMembers(newSelectedMembers);
|
||||
|
||||
// 更新群数据
|
||||
const updatedGroups = selectedGroups.map(group => {
|
||||
if (group.id === groupId) {
|
||||
return {
|
||||
...group,
|
||||
members: newSelectedMembers[groupId] || [],
|
||||
};
|
||||
}
|
||||
return group;
|
||||
});
|
||||
onSelect(updatedGroups);
|
||||
};
|
||||
|
||||
// 移除成员
|
||||
const handleRemoveMember = (groupId: string, memberId: string) => {
|
||||
if (readonly) return;
|
||||
const currentMembers = selectedMembers[groupId] || [];
|
||||
const newMembers = currentMembers.filter(m => m.id !== memberId);
|
||||
|
||||
const newSelectedMembers = { ...selectedMembers };
|
||||
newSelectedMembers[groupId] = newMembers;
|
||||
setSelectedMembers(newSelectedMembers);
|
||||
|
||||
// 更新群数据
|
||||
const updatedGroups = selectedGroups.map(group => {
|
||||
if (group.id === groupId) {
|
||||
return {
|
||||
...group,
|
||||
members: newMembers,
|
||||
};
|
||||
}
|
||||
return group;
|
||||
});
|
||||
onSelect(updatedGroups);
|
||||
};
|
||||
|
||||
// 同步 selectedGroups 到 selectedMembers
|
||||
useEffect(() => {
|
||||
const membersMap: Record<string, GroupMember[]> = {};
|
||||
selectedGroups.forEach(group => {
|
||||
if (group.members && group.members.length > 0) {
|
||||
membersMap[group.id] = group.members;
|
||||
}
|
||||
});
|
||||
setSelectedMembers(membersMap);
|
||||
}, [selectedGroups.length]);
|
||||
|
||||
// 获取显示文本
|
||||
const getDisplayText = () => {
|
||||
if (selectedGroups.length === 0) return "";
|
||||
return `已选择${selectedGroups.length}个群聊`;
|
||||
};
|
||||
|
||||
const currentGroupMembers = allMembers[memberSelectionVisible.groupId] || [];
|
||||
const currentSelectedMembers = selectedMembers[memberSelectionVisible.groupId] || [];
|
||||
|
||||
return (
|
||||
<div className={`${style.container} ${className}`}>
|
||||
{/* 输入框 */}
|
||||
<div
|
||||
className={style.inputWrapper}
|
||||
onClick={() => !readonly && setGroupSelectionVisible(true)}
|
||||
>
|
||||
<SearchOutlined className={style.inputIcon} />
|
||||
<Input
|
||||
placeholder={placeholder}
|
||||
value={getDisplayText()}
|
||||
readOnly
|
||||
className={style.input}
|
||||
/>
|
||||
{!readonly && selectedGroups.length > 0 && (
|
||||
<Button
|
||||
fill="none"
|
||||
size="small"
|
||||
className={style.clearBtn}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setSelectedMembers({});
|
||||
onSelect([]);
|
||||
}}
|
||||
>
|
||||
<DeleteOutlined />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 已选群列表 */}
|
||||
{selectedGroups.length > 0 && (
|
||||
<div className={style.selectedGroupsList}>
|
||||
{selectedGroups.map(group => (
|
||||
<div key={group.id} className={style.groupCard}>
|
||||
{/* 群信息 */}
|
||||
<div className={style.groupHeader}>
|
||||
<div className={style.groupInfo}>
|
||||
<Avatar src={group.avatar} className={style.groupAvatar} />
|
||||
<div className={style.groupDetails}>
|
||||
<div className={style.groupName}>{group.name}</div>
|
||||
<div className={style.groupId}>ID: {group.chatroomId || group.id}</div>
|
||||
</div>
|
||||
</div>
|
||||
{!readonly && (
|
||||
<Button
|
||||
fill="none"
|
||||
size="small"
|
||||
className={style.deleteGroupBtn}
|
||||
onClick={() => handleRemoveGroup(group.id)}
|
||||
>
|
||||
<DeleteOutlined />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 成员选择区域 */}
|
||||
<div className={style.membersSection}>
|
||||
<div className={style.membersLabel}>
|
||||
采集群内指定成员 ({group.members?.length || 0}人)
|
||||
</div>
|
||||
<div className={style.membersList}>
|
||||
{group.members?.map(member => (
|
||||
<div key={member.id} className={style.memberItem}>
|
||||
<Avatar src={member.avatar} className={style.memberAvatar} />
|
||||
<div className={style.memberName}>{member.nickname}</div>
|
||||
{!readonly && (
|
||||
<Button
|
||||
fill="none"
|
||||
size="small"
|
||||
className={style.removeMemberBtn}
|
||||
onClick={() => handleRemoveMember(group.id, member.id)}
|
||||
>
|
||||
<DeleteOutlined />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{!readonly && (
|
||||
<div
|
||||
className={style.addMemberBtn}
|
||||
onClick={() => handleOpenMemberSelection(group.id)}
|
||||
>
|
||||
<PlusOutlined />
|
||||
<span>添加</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 群选择弹窗 */}
|
||||
<GroupSelection
|
||||
selectedOptions={selectedGroups as GroupSelectionItem[]}
|
||||
onSelect={handleGroupSelect}
|
||||
placeholder={placeholder}
|
||||
visible={groupSelectionVisible}
|
||||
onVisibleChange={setGroupSelectionVisible}
|
||||
showInput={false}
|
||||
showSelectedList={false}
|
||||
/>
|
||||
|
||||
{/* 成员选择弹窗 */}
|
||||
<Popup
|
||||
visible={memberSelectionVisible.visible}
|
||||
onMaskClick={handleCloseMemberSelection}
|
||||
position="bottom"
|
||||
bodyStyle={{
|
||||
height: "70vh",
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
}}
|
||||
>
|
||||
<div className={style.memberSelectionPopup}>
|
||||
<div className={style.popupHeader}>
|
||||
<div className={style.popupTitle}>选择成员</div>
|
||||
<Button
|
||||
fill="none"
|
||||
size="small"
|
||||
onClick={handleCloseMemberSelection}
|
||||
className={style.closeBtn}
|
||||
>
|
||||
完成
|
||||
</Button>
|
||||
</div>
|
||||
<div className={style.memberList}>
|
||||
{loadingMembers ? (
|
||||
<div className={style.loadingBox}>
|
||||
<div className={style.loadingText}>加载中...</div>
|
||||
</div>
|
||||
) : currentGroupMembers.length > 0 ? (
|
||||
currentGroupMembers.map(member => {
|
||||
const isSelected = currentSelectedMembers.some(m => m.id === member.id);
|
||||
return (
|
||||
<div
|
||||
key={member.id}
|
||||
className={`${style.memberListItem} ${isSelected ? style.selected : ""}`}
|
||||
onClick={() => handleSelectMember(memberSelectionVisible.groupId, member)}
|
||||
>
|
||||
<Avatar src={member.avatar} className={style.memberListItemAvatar} />
|
||||
<div className={style.memberListItemName}>{member.nickname}</div>
|
||||
{isSelected && <div className={style.checkmark}>✓</div>}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div className={style.emptyBox}>
|
||||
<div className={style.emptyText}>暂无成员数据</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GroupSelectionWithMembers;
|
||||
@@ -6,11 +6,13 @@ import { DownOutlined } from "@ant-design/icons";
|
||||
import NavCommon from "@/components/NavCommon";
|
||||
import FriendSelection from "@/components/FriendSelection";
|
||||
import GroupSelection from "@/components/GroupSelection";
|
||||
import GroupSelectionWithMembers from "@/components/GroupSelectionWithMembers";
|
||||
import DeviceSelection from "@/components/DeviceSelection";
|
||||
import Layout from "@/components/Layout/Layout";
|
||||
import style from "./index.module.scss";
|
||||
import { getContentLibraryDetail, updateContentLibrary, createContentLibrary } from "./api";
|
||||
import { GroupSelectionItem } from "@/components/GroupSelection/data";
|
||||
import { GroupWithMembers } from "@/components/GroupSelectionWithMembers";
|
||||
import { FriendSelectionItem } from "@/components/FriendSelection/data";
|
||||
import { DeviceSelectionItem } from "@/components/DeviceSelection/data";
|
||||
|
||||
@@ -48,6 +50,9 @@ export default function ContentForm() {
|
||||
const [selectedGroupsOptions, setSelectedGroupsOptions] = useState<
|
||||
GroupSelectionItem[]
|
||||
>([]);
|
||||
const [selectedGroupsWithMembers, setSelectedGroupsWithMembers] = useState<
|
||||
GroupWithMembers[]
|
||||
>([]);
|
||||
const [useAI, setUseAI] = useState(false);
|
||||
const [aiPrompt, setAIPrompt] = useState("重写这条朋友圈 要求: 1、原本的字数和意思不要修改超过10% 2、出现品牌名或个人名字就去除");
|
||||
const [enabled, setEnabled] = useState(true);
|
||||
@@ -94,7 +99,30 @@ export default function ContentForm() {
|
||||
setSelectedDevices(deviceOptions || []);
|
||||
setSelectedFriends(data.sourceFriends || []);
|
||||
setSelectedGroups(data.selectedGroups || []);
|
||||
setSelectedGroupsOptions(data.selectedGroupsOptions || []);
|
||||
// 使用 wechatGroupsOptions 作为群列表数据
|
||||
setSelectedGroupsOptions(data.wechatGroupsOptions || data.selectedGroupsOptions || []);
|
||||
// 处理带成员的群数据
|
||||
// groupMembersOptions 是一个对象,key是群ID(字符串),value是成员数组
|
||||
const groupMembersMap = data.groupMembersOptions || {};
|
||||
const groupsWithMembers: GroupWithMembers[] = (data.wechatGroupsOptions || data.selectedGroupsOptions || []).map(
|
||||
(group: any) => {
|
||||
const groupIdStr = String(group.id);
|
||||
const members = groupMembersMap[groupIdStr] || [];
|
||||
// 映射成员数据结构
|
||||
return {
|
||||
...group,
|
||||
members: members.map((member: any) => ({
|
||||
id: String(member.id),
|
||||
nickname: member.nickname || "",
|
||||
wechatId: member.wechatId || "",
|
||||
avatar: member.avatar || "",
|
||||
gender: undefined,
|
||||
role: undefined,
|
||||
})),
|
||||
};
|
||||
},
|
||||
);
|
||||
setSelectedGroupsWithMembers(groupsWithMembers);
|
||||
setSelectedFriendsOptions(data.friendsGroupsOptions || []);
|
||||
setKeywordsInclude((data.keywordInclude || []).join(","));
|
||||
setKeywordsExclude((data.keywordExclude || []).join(","));
|
||||
@@ -140,7 +168,15 @@ export default function ContentForm() {
|
||||
devices: selectedDevices.map(d => d.id),
|
||||
friendsGroups: friendsGroups,
|
||||
wechatGroups: selectedGroups,
|
||||
groupMembers: {},
|
||||
groupMembers: selectedGroupsWithMembers.reduce(
|
||||
(acc, group) => {
|
||||
if (group.members && group.members.length > 0) {
|
||||
acc[group.id] = group.members.map(m => m.id);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string[]>,
|
||||
),
|
||||
keywordInclude: keywordsInclude
|
||||
.split(/,|,|\n|\s+/)
|
||||
.map(s => s.trim())
|
||||
@@ -180,6 +216,12 @@ export default function ContentForm() {
|
||||
setSelectedGroupsOptions(groups);
|
||||
};
|
||||
|
||||
const handleGroupsWithMembersChange = (groups: GroupWithMembers[]) => {
|
||||
setSelectedGroupsWithMembers(groups);
|
||||
setSelectedGroups(groups.map(g => g.id.toString()));
|
||||
setSelectedGroupsOptions(groups);
|
||||
};
|
||||
|
||||
const handleFriendsChange = (friends: FriendSelectionItem[]) => {
|
||||
setSelectedFriends(friends.map(f => f.id.toString()));
|
||||
setSelectedFriendsOptions(friends);
|
||||
@@ -335,9 +377,9 @@ export default function ContentForm() {
|
||||
/>
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab title="选择聊天群" key="groups">
|
||||
<GroupSelection
|
||||
selectedOptions={selectedGroupsOptions}
|
||||
onSelect={handleGroupsChange}
|
||||
<GroupSelectionWithMembers
|
||||
selectedGroups={selectedGroupsWithMembers}
|
||||
onSelect={handleGroupsWithMembersChange}
|
||||
placeholder="选择聊天群"
|
||||
/>
|
||||
</Tabs.Tab>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { PlanDetail, PlanListResponse, ApiResponse } from "./data";
|
||||
export function getPlanList(params: {
|
||||
sceneId: string;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
limit: number;
|
||||
}): Promise<PlanListResponse> {
|
||||
return request(`/v1/plan/list`, params, "GET");
|
||||
}
|
||||
|
||||
@@ -8,8 +8,9 @@ import {
|
||||
Popup,
|
||||
Card,
|
||||
Tag,
|
||||
InfiniteScroll,
|
||||
} from "antd-mobile";
|
||||
import { Input, Pagination } from "antd";
|
||||
import { Input } from "antd";
|
||||
import {
|
||||
PlusOutlined,
|
||||
CopyOutlined,
|
||||
@@ -80,7 +81,7 @@ const ScenarioList: React.FC = () => {
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const [loadingMore, setLoadingMore] = useState(false);
|
||||
const [total, setTotal] = useState(0);
|
||||
const pageSize = 20;
|
||||
const limit = 20;
|
||||
|
||||
// 获取计划列表数据
|
||||
const fetchPlanList = async (page: number, isLoadMore: boolean = false) => {
|
||||
@@ -96,7 +97,7 @@ const ScenarioList: React.FC = () => {
|
||||
const response = await getPlanList({
|
||||
sceneId: scenarioId,
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
limit: limit,
|
||||
});
|
||||
|
||||
if (response && response.list) {
|
||||
@@ -110,7 +111,7 @@ const ScenarioList: React.FC = () => {
|
||||
|
||||
// 更新分页信息
|
||||
setTotal(response.total || 0);
|
||||
setHasMore(response.list.length === pageSize);
|
||||
setHasMore(response.list.length === limit);
|
||||
setCurrentPage(page);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -149,10 +150,11 @@ const ScenarioList: React.FC = () => {
|
||||
fetchScenarioData();
|
||||
}, [scenarioId]);
|
||||
|
||||
// 分页改变处理
|
||||
const handlePageChange = async (page: number) => {
|
||||
setCurrentPage(page);
|
||||
await fetchPlanList(page, false);
|
||||
// 加载更多
|
||||
const handleLoadMore = async () => {
|
||||
if (!hasMore || loadingMore || loadingTasks) return;
|
||||
const nextPage = currentPage + 1;
|
||||
await fetchPlanList(nextPage, true);
|
||||
};
|
||||
|
||||
const handleCopyPlan = async (taskId: string) => {
|
||||
@@ -405,18 +407,6 @@ const ScenarioList: React.FC = () => {
|
||||
</>
|
||||
}
|
||||
loading={loading}
|
||||
footer={
|
||||
<div className="pagination-container">
|
||||
<Pagination
|
||||
total={total}
|
||||
pageSize={pageSize}
|
||||
current={currentPage}
|
||||
onChange={handlePageChange}
|
||||
showSizeChanger={false}
|
||||
showQuickJumper={false}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className={style["scenario-list-page"]}>
|
||||
{/* 计划列表 */}
|
||||
@@ -530,6 +520,33 @@ const ScenarioList: React.FC = () => {
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
{/* 上拉加载更多 */}
|
||||
<InfiniteScroll
|
||||
loadMore={handleLoadMore}
|
||||
hasMore={hasMore}
|
||||
threshold={100}
|
||||
>
|
||||
{loadingMore && (
|
||||
<div style={{ padding: "20px", textAlign: "center" }}>
|
||||
<SpinLoading color="primary" style={{ fontSize: 16 }} />
|
||||
<span style={{ marginLeft: 8, color: "#999", fontSize: 12 }}>
|
||||
加载中...
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{!hasMore && filteredTasks.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
padding: "20px",
|
||||
textAlign: "center",
|
||||
color: "#999",
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
没有更多了
|
||||
</div>
|
||||
)}
|
||||
</InfiniteScroll>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -21,6 +21,7 @@ Route::group('v1/', function () {
|
||||
Route::group('wechatChatroom/', function () {
|
||||
Route::get('list', 'app\chukebao\controller\WechatChatroomController@getList'); // 获取好友列表
|
||||
Route::get('detail', 'app\chukebao\controller\WechatChatroomController@getDetail'); // 获取群详情
|
||||
Route::get('members', 'app\chukebao\controller\WechatChatroomController@getMembers'); // 获取群成员列表
|
||||
Route::post('aiAnnouncement', 'app\chukebao\controller\WechatChatroomController@aiAnnouncement'); // AI群公告
|
||||
});
|
||||
|
||||
|
||||
@@ -151,6 +151,68 @@ class WechatChatroomController extends BaseController
|
||||
return ResponseHelper::success($detail);
|
||||
}
|
||||
|
||||
public function getMembers()
|
||||
{
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$groupId = $this->request->param('groupId', '');
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
|
||||
$accountId = $this->getUserInfo('s2_accountId');
|
||||
if (empty($accountId)) {
|
||||
return ResponseHelper::error('请先登录');
|
||||
}
|
||||
|
||||
// 验证群组ID必填
|
||||
if (empty($groupId)) {
|
||||
return ResponseHelper::error('群组ID不能为空');
|
||||
}
|
||||
|
||||
// 验证群组是否属于当前账号
|
||||
$chatroom = Db::table('s2_wechat_chatroom')
|
||||
->where(['id' => $groupId, 'isDeleted' => 0])
|
||||
->find();
|
||||
|
||||
if (!$chatroom) {
|
||||
return ResponseHelper::error('群组不存在或无权限访问');
|
||||
}
|
||||
|
||||
// 获取群组的chatroomId(微信群聊ID)
|
||||
$chatroomId = $chatroom['chatroomId'] ?? $chatroom['id'];
|
||||
|
||||
// 如果chatroomId为空,使用id作为chatroomId
|
||||
if (empty($chatroomId)) {
|
||||
$chatroomId = $chatroom['id'];
|
||||
}
|
||||
|
||||
// 构建查询
|
||||
$query = Db::table('s2_wechat_chatroom_member')
|
||||
->where('chatroomId', $chatroomId);
|
||||
|
||||
// 关键字搜索:昵称、备注、别名
|
||||
if ($keyword !== '' && $keyword !== null) {
|
||||
$query->where(function ($q) use ($keyword) {
|
||||
$like = '%' . $keyword . '%';
|
||||
$q->whereLike('nickname', $like)
|
||||
->whereOr('conRemark', 'like', $like)
|
||||
->whereOr('alias', 'like', $like);
|
||||
});
|
||||
}
|
||||
|
||||
$query->order('id desc');
|
||||
$total = $query->count();
|
||||
$list = $query->page($page, $limit)->select();
|
||||
|
||||
// 处理时间格式
|
||||
foreach ($list as $k => &$v) {
|
||||
$v['createTime'] = !empty($v['createTime']) ? date('Y-m-d H:i:s', $v['createTime']) : '';
|
||||
$v['updateTime'] = !empty($v['updateTime']) ? date('Y-m-d H:i:s', $v['updateTime']) : '';
|
||||
}
|
||||
unset($v);
|
||||
|
||||
return ResponseHelper::success(['list' => $list, 'total' => $total]);
|
||||
}
|
||||
|
||||
public function aiAnnouncement()
|
||||
{
|
||||
$userId = $this->getUserInfo('id');
|
||||
|
||||
@@ -19,7 +19,7 @@ return [
|
||||
'message:friendsList' => 'app\command\MessageFriendsListCommand', // 微信好友消息列表 √
|
||||
'message:chatroomList' => 'app\command\MessageChatroomListCommand', // 微信群聊消息列表 √
|
||||
'department:list' => 'app\command\DepartmentListCommand', // 部门列表 √
|
||||
'content:sync' => 'app\command\SyncContentCommand', // 同步内容库 √
|
||||
'content:sync' => 'app\command\SyncContentCommand', // 同步内容库 XXXXXXXX
|
||||
'groupFriends:list' => 'app\command\GroupFriendsCommand', // 微信群好友列表
|
||||
// 'allotFriends:run' => 'app\command\AllotFriendCommand', // 自动分配微信好友
|
||||
// 'allotChatroom:run' => 'app\command\AllotChatroomCommand', // 自动分配微信群聊
|
||||
|
||||
@@ -357,6 +357,7 @@ class ContentLibraryController extends Controller
|
||||
// 初始化选项数组
|
||||
$library['friendsGroupsOptions'] = [];
|
||||
$library['wechatGroupsOptions'] = [];
|
||||
$library['groupMembersOptions'] = [];
|
||||
|
||||
// 批量查询好友信息
|
||||
if (!empty($library['friendsGroups'])) {
|
||||
@@ -385,6 +386,61 @@ class ContentLibraryController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// 批量查询群成员信息
|
||||
if (!empty($library['groupMembers'])) {
|
||||
// groupMembers格式: {"826825": ["413771", "413769"], "840818": ["496300", "496302"]}
|
||||
// 键是群组ID,值是成员ID数组
|
||||
$allMemberIds = [];
|
||||
$groupMembersMap = [];
|
||||
|
||||
if (is_array($library['groupMembers'])) {
|
||||
foreach ($library['groupMembers'] as $groupId => $memberIds) {
|
||||
if (is_array($memberIds) && !empty($memberIds)) {
|
||||
$allMemberIds = array_merge($allMemberIds, $memberIds);
|
||||
// 保存群组ID和成员ID的映射关系
|
||||
$groupMembersMap[$groupId] = $memberIds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($allMemberIds)) {
|
||||
// 去重
|
||||
$allMemberIds = array_unique($allMemberIds);
|
||||
|
||||
// 查询群成员信息
|
||||
$members = Db::table('s2_wechat_chatroom_member')
|
||||
->field('id, chatroomId, wechatId, nickname, avatar, conRemark, alias, friendType, createTime, updateTime')
|
||||
->whereIn('id', $allMemberIds)
|
||||
->select();
|
||||
|
||||
// 将成员数据按ID建立索引
|
||||
$membersById = [];
|
||||
foreach ($members as $member) {
|
||||
// 格式化时间字段
|
||||
$member['createTime'] = !empty($member['createTime']) ? date('Y-m-d H:i:s', $member['createTime']) : '';
|
||||
$member['updateTime'] = !empty($member['updateTime']) ? date('Y-m-d H:i:s', $member['updateTime']) : '';
|
||||
$membersById[$member['id']] = $member;
|
||||
}
|
||||
|
||||
// 按照群组ID分组返回
|
||||
$groupMembersOptions = [];
|
||||
foreach ($groupMembersMap as $groupId => $memberIds) {
|
||||
$groupMembersOptions[$groupId] = [];
|
||||
foreach ($memberIds as $memberId) {
|
||||
if (isset($membersById[$memberId])) {
|
||||
$groupMembersOptions[$groupId][] = $membersById[$memberId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$library['groupMembersOptions'] = $groupMembersOptions;
|
||||
} else {
|
||||
$library['groupMembersOptions'] = [];
|
||||
}
|
||||
} else {
|
||||
$library['groupMembersOptions'] = [];
|
||||
}
|
||||
|
||||
//获取设备信息
|
||||
if (!empty($library['deviceGroups'])) {
|
||||
$deviceList = DeviceModel::alias('d')
|
||||
@@ -921,7 +977,7 @@ class ContentLibraryController extends Controller
|
||||
|
||||
// 如果有发送者信息,也获取发送者详情
|
||||
if (!empty($item['wechatId'])) {
|
||||
$senderInfo = Db::name('wechat_chatroom_member')
|
||||
$senderInfo = Db::table('s2_wechat_chatroom_member')
|
||||
->where([
|
||||
'chatroomId' => $groupInfo['chatroomId'],
|
||||
'wechatId' => $item['wechatId']
|
||||
@@ -1477,8 +1533,8 @@ class ContentLibraryController extends Controller
|
||||
|
||||
try {
|
||||
// 查询群组信息
|
||||
$groups = Db::name('wechat_group')->alias('g')
|
||||
->field('g.id, g.chatroomId, g.name, g.ownerWechatId')
|
||||
$groups = Db::table('s2_wechat_chatroom')->alias('g')
|
||||
->field('g.id, g.chatroomId, g.nickname as name, g.wechatAccountWechatId as ownerWechatId')
|
||||
->whereIn('g.id', $groupIds)
|
||||
->where('g.deleteTime', 0)
|
||||
->select();
|
||||
@@ -1500,12 +1556,59 @@ class ContentLibraryController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
// groupMembers格式: {"826825": ["413771", "413769"], "840818": ["496300", "496302"]}
|
||||
// 键是群组ID,值是该群组的成员ID数组
|
||||
// 需要按群组分组处理,确保每个群组只采集该群组配置的成员
|
||||
|
||||
// 建立群组ID到成员ID数组的映射
|
||||
$groupIdToMemberIds = [];
|
||||
if (is_array($groupMembers)) {
|
||||
foreach ($groupMembers as $groupId => $memberIds) {
|
||||
if (is_array($memberIds) && !empty($memberIds)) {
|
||||
$groupIdToMemberIds[$groupId] = $memberIds;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($groupIdToMemberIds)) {
|
||||
return [
|
||||
'status' => 'failed',
|
||||
'message' => '未找到有效的群成员ID'
|
||||
];
|
||||
}
|
||||
|
||||
// 为每个群组查询成员信息,建立群组ID到成员wechatId数组的映射
|
||||
$groupIdToMemberWechatIds = [];
|
||||
foreach ($groupIdToMemberIds as $groupId => $memberIds) {
|
||||
// 查询该群组的成员信息,获取wechatId
|
||||
$members = Db::table('s2_wechat_chatroom_member')
|
||||
->field('id, wechatId')
|
||||
->whereIn('id', $memberIds)
|
||||
->select();
|
||||
|
||||
$wechatIds = [];
|
||||
foreach ($members as $member) {
|
||||
if (!empty($member['wechatId'])) {
|
||||
$wechatIds[] = $member['wechatId'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($wechatIds)) {
|
||||
$groupIdToMemberWechatIds[$groupId] = array_unique($wechatIds);
|
||||
}
|
||||
}
|
||||
if (empty($groupIdToMemberWechatIds)) {
|
||||
return [
|
||||
'status' => 'failed',
|
||||
'message' => '未找到有效的群成员微信ID'
|
||||
];
|
||||
}
|
||||
|
||||
// 从群组采集内容
|
||||
$collectedData = [];
|
||||
$totalMessagesCount = 0;
|
||||
$chatroomIds = array_column($groups, 'id');
|
||||
|
||||
// 获取群消息 - 支持时间范围过滤
|
||||
// 获取群消息 - 支持时间范围过滤(先不添加群成员过滤,后面按群组分别过滤)
|
||||
$messageWhere = [
|
||||
['wechatChatroomId', 'in', $chatroomIds],
|
||||
['type', '=', 2]
|
||||
@@ -1516,7 +1619,7 @@ class ContentLibraryController extends Controller
|
||||
$messageWhere[] = ['createTime', 'between', [$library['timeStart'], $library['timeEnd']]];
|
||||
}
|
||||
|
||||
// 查询群消息
|
||||
// 查询群消息(先查询所有消息,后面按群组和成员过滤)
|
||||
$groupMessages = Db::table('s2_wechat_message')
|
||||
->where($messageWhere)
|
||||
->order('createTime', 'desc')
|
||||
@@ -1532,6 +1635,34 @@ class ContentLibraryController extends Controller
|
||||
$groupedMessages = [];
|
||||
foreach ($groupMessages as $message) {
|
||||
$chatroomId = $message['wechatChatroomId'];
|
||||
$senderWechatId = $message['senderWechatId'] ?? '';
|
||||
|
||||
// 找到对应的群组信息
|
||||
$groupInfo = null;
|
||||
foreach ($groups as $group) {
|
||||
if ($group['id'] == $chatroomId) {
|
||||
$groupInfo = $group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$groupInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查该消息的发送者是否在该群组的配置成员列表中
|
||||
$groupId = $groupInfo['id'];
|
||||
if (!isset($groupIdToMemberWechatIds[$groupId])) {
|
||||
// 该群组没有配置成员,跳过
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查发送者是否在配置的成员列表中
|
||||
if (!in_array($senderWechatId, $groupIdToMemberWechatIds[$groupId])) {
|
||||
// 发送者不在该群组的配置成员列表中,跳过
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($groupedMessages[$chatroomId])) {
|
||||
$groupedMessages[$chatroomId] = [
|
||||
'count' => 0,
|
||||
@@ -1579,27 +1710,14 @@ class ContentLibraryController extends Controller
|
||||
continue;
|
||||
}
|
||||
|
||||
// 找到对应的群组信息
|
||||
$groupInfo = null;
|
||||
foreach ($groups as $group) {
|
||||
if ($group['id'] == $chatroomId) {
|
||||
$groupInfo = $group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$groupInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 如果启用了AI处理
|
||||
if (!empty($library['aiEnabled']) && !empty($content)) {
|
||||
$contentAi = $this->aiRewrite($library, $content);
|
||||
if (!empty($content)) {
|
||||
$moment['contentAi'] = $contentAi;
|
||||
if (!empty($contentAi)) {
|
||||
$message['contentAi'] = $contentAi;
|
||||
} else {
|
||||
$moment['contentAi'] = '';
|
||||
$message['contentAi'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1968,7 +2086,38 @@ class ContentLibraryController extends Controller
|
||||
return true;
|
||||
}
|
||||
|
||||
// 提取消息内容中的链接
|
||||
$resUrls = [];
|
||||
|
||||
$content = '';
|
||||
switch ($message['msgType']) {
|
||||
case 1: // 文字
|
||||
$content = $message['content'];
|
||||
$contentType = 4;
|
||||
break;
|
||||
case 3: //图片
|
||||
$resUrls[] = $message['content'];
|
||||
$contentType = 1;
|
||||
break;
|
||||
case 47: //动态图片
|
||||
$resUrls[] = $message['content'];
|
||||
$contentType = 1;
|
||||
break;
|
||||
case 34: //语言
|
||||
return false;
|
||||
case 43: //视频
|
||||
$resUrls[] = $message['content'];
|
||||
$contentType = 3;
|
||||
break;
|
||||
case 42: //名片
|
||||
return false;
|
||||
case 49: //文件
|
||||
$links = json_decode($message['content'],true);
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
/*// 提取消息内容中的链接
|
||||
$content = $message['content'] ?? '';
|
||||
$links = [];
|
||||
$pattern = '/https?:\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]/';
|
||||
@@ -1986,6 +2135,8 @@ class ContentLibraryController extends Controller
|
||||
|
||||
// 判断内容类型 (0=未知, 1=图片, 2=链接, 3=视频, 4=文本, 5=小程序, 6=图文)
|
||||
$contentType = $this->determineContentType($content, $resUrls, $links);
|
||||
*/
|
||||
|
||||
|
||||
// 创建新的内容项目
|
||||
$item = new ContentItem();
|
||||
@@ -1993,7 +2144,7 @@ class ContentLibraryController extends Controller
|
||||
$item->type = 'group_message'; // 群消息类型
|
||||
$item->title = '来自 ' . ($group['name'] ?? '未知群组') . ' 的消息';
|
||||
$item->contentData = json_encode($message, JSON_UNESCAPED_UNICODE);
|
||||
$item->msgId = $message['msgId'] ?? ''; // 存储msgId便于后续查询
|
||||
$item->msgId = $message['msgSvrId'] ?? ''; // 存储msgSvrId便于后续查询
|
||||
$item->createTime = time();
|
||||
$item->content = $content;
|
||||
$item->contentType = $contentType; // 设置内容类型
|
||||
@@ -2011,13 +2162,17 @@ class ContentLibraryController extends Controller
|
||||
if (!empty($resUrls[0])) {
|
||||
$item->coverImage = $resUrls[0];
|
||||
}
|
||||
}else{
|
||||
$item->resUrls = json_encode([], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 处理链接
|
||||
if (!empty($links)) {
|
||||
$item->urls = json_encode($links, JSON_UNESCAPED_UNICODE);
|
||||
}else{
|
||||
$item->urls = json_encode([], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
$item->ossUrls = json_encode([], JSON_UNESCAPED_UNICODE);
|
||||
// 设置商品信息(需根据消息内容解析)
|
||||
$this->extractProductInfo($item, $content);
|
||||
|
||||
|
||||
@@ -39,10 +39,10 @@ class GetChatroomListV1Controller extends BaseController
|
||||
|
||||
$where = [];
|
||||
if ($this->getUserInfo('isAdmin') == 1) {
|
||||
$where[] = ['g.deleteTime', '=', 0];
|
||||
$where[] = ['gg.isDeleted', '=', 0];
|
||||
$where[] = ['g.ownerWechatId', 'in', $wechatIds];
|
||||
} else {
|
||||
$where[] = ['g.deleteTime', '=', 0];
|
||||
$where[] = ['gg.isDeleted', '=', 0];
|
||||
$where[] = ['g.ownerWechatId', 'in', $wechatIds];
|
||||
//$where[] = ['g.userId', '=', $this->getUserInfo('id')];
|
||||
}
|
||||
@@ -55,6 +55,7 @@ class GetChatroomListV1Controller extends BaseController
|
||||
->field(['g.id', 'g.chatroomId', 'g.name', 'g.avatar','g.ownerWechatId', 'g.identifier', 'g.createTime',
|
||||
'wa.nickname as ownerNickname','wa.avatar as ownerAvatar','wa.alias as ownerAlias'])
|
||||
->join('wechat_account wa', 'g.ownerWechatId = wa.wechatId', 'LEFT')
|
||||
->join(['s2_wechat_chatroom' => 'gg'], 'g.id = gg.id', 'LEFT')
|
||||
->where($where);
|
||||
|
||||
$total = $data->count();
|
||||
|
||||
Reference in New Issue
Block a user