- 来源上传弹窗 A系统基因库 tab 搜索时显示 loading spinner - NetworkActor 组件初始加载时显示 loading spinner(初始状态设为 true) - StudioNotebooksPage 笔记卡片支持暗色主题样式 - StudioNotebookDetailPage / NotebookAIWritingPage 跳转时透传 themekey 参数 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
627 lines
25 KiB
TypeScript
627 lines
25 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
useEffect,
|
|
useRef,
|
|
useState,
|
|
} from "react";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
import { Loader2, Plus, Search, Trash2 } from "lucide-react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { toast } from "sonner";
|
|
import { getStoredAuth } from "@/core/auth";
|
|
import { authenticatedFetch } from "@/components/assistant-ui/document-upload-popup";
|
|
|
|
|
|
interface NetworkActorProps {
|
|
inputValue: string;
|
|
searchSpaceId: string;
|
|
onSuccess?: () => void;
|
|
}
|
|
|
|
interface platformItem {
|
|
title: string;
|
|
icon: string;
|
|
checked: boolean;
|
|
}
|
|
|
|
interface languageItem {
|
|
title: string;
|
|
checked: boolean;
|
|
}
|
|
|
|
interface PoliticsDirItem {
|
|
地区: string;
|
|
语言: string;
|
|
年龄段: string;
|
|
性别: string;
|
|
教育程度: string;
|
|
关注话题: { 话题: string; 倾向: string; 佐证: string; 风格: string }[];
|
|
类型: string;
|
|
对HZZLC: string;
|
|
对MZZLC: string;
|
|
对TZZLC: string[];
|
|
账号描述: string;
|
|
发文频率: string;
|
|
活跃时间: string;
|
|
}
|
|
|
|
interface 用户画像 {
|
|
地区: string;
|
|
教育程度: string;
|
|
关注话题: { 倾向: string; 话题: string; 佐证: string; 风格: string }[];
|
|
语言: string;
|
|
年龄段: string;
|
|
账号描述: string;
|
|
对TZZLC: string[];
|
|
对HZZLC: string;
|
|
对MZZLC: string[];
|
|
性别: string;
|
|
类型: string;
|
|
发文频率: string;
|
|
活跃时间: string;
|
|
}
|
|
|
|
interface hx_info {
|
|
语言: string;
|
|
用户画像: 用户画像;
|
|
}
|
|
|
|
interface UserDataItem {
|
|
comment_count: number;
|
|
country: string | null;
|
|
agent_task_id: string | null;
|
|
education: string | null;
|
|
groupId: string | null;
|
|
forward_count: number;
|
|
political_stand: string;
|
|
language: string;
|
|
type: string;
|
|
concern: string | null;
|
|
profile_image: string | null;
|
|
concern_topic: string | null;
|
|
screen_name: string;
|
|
school: string | null;
|
|
is_alive: number;
|
|
createdTime: string;
|
|
data_provider: string | null;
|
|
id: string;
|
|
text: string | null;
|
|
group: string | null;
|
|
read_count: number;
|
|
group_name: string;
|
|
area: string | null;
|
|
image: string | null;
|
|
updatedTime: string;
|
|
like_count: number;
|
|
m_politics_dir: string;
|
|
camp: string | null;
|
|
tags: string[];
|
|
army: string | null;
|
|
is_sync_a: number;
|
|
tweets_count: number;
|
|
groupName: string | null;
|
|
history_key: string;
|
|
user_id: string;
|
|
real_id: string | null;
|
|
h_politics_dir: string | null;
|
|
domain: string | null;
|
|
name: string | null;
|
|
position: string | null;
|
|
job: string | null;
|
|
age_round: string | null;
|
|
birthday: string | null;
|
|
is_virtual: number;
|
|
isMedia: number;
|
|
hx_info: hx_info;
|
|
gender: string | null;
|
|
agentInfo: PoliticsDirItem;
|
|
profile_url: string | null;
|
|
tagId: string[];
|
|
user_name: string;
|
|
a_update_time: string | null;
|
|
values: string | null;
|
|
follows_count: number;
|
|
title: string | null;
|
|
is_protected: number;
|
|
platform: string;
|
|
importType: string;
|
|
update_time: string;
|
|
key: string;
|
|
introduction: string | null;
|
|
brief: string | null;
|
|
creator: string;
|
|
history_title: string;
|
|
tweets_ratio: string | null;
|
|
is_verified: number;
|
|
t_politics_dir: string[];
|
|
eventMap: string | null;
|
|
fans_count: number;
|
|
a_create_time: string | null;
|
|
hobbies: string[];
|
|
location: string | null;
|
|
average_count: number;
|
|
ratio: string | null;
|
|
classification?: string | null;
|
|
groupDiscussTags?: string[];
|
|
checked?: boolean;
|
|
content?: string;
|
|
url?: string;
|
|
}
|
|
|
|
export function NetworkActor({ inputValue, searchSpaceId, onSuccess }: NetworkActorProps) {
|
|
const [platforms, setPlatforms] = useState<platformItem[]>([
|
|
{ title: 'X', icon: 'x.png', checked: false },
|
|
{ title: 'Facebook', icon: 'facebook.png', checked: false },
|
|
{ title: 'Reddit', icon: 'reddit.png', checked: false },
|
|
{ title: 'PTT', icon: 'ptt.png', checked: false },
|
|
{ title: 'YouTube', icon: 'youtube.png', checked: false },
|
|
{ title: 'Dcard', icon: 'dcard.png', checked: false },
|
|
{ title: 'Mobile01', icon: 'mobile01.png', checked: false },
|
|
{ title: 'Threads', icon: 'threads.png', checked: false },
|
|
{ title: 'Instagram', icon: 'ins.png', checked: false },
|
|
]);
|
|
const platformList = useRef<string[]>([]);
|
|
const [languages, setLanguages] = useState<languageItem[]>([
|
|
{ title: '繁体中文', checked: false },
|
|
{ title: '简体中文', checked: false },
|
|
{ title: '英文', checked: false },
|
|
{ title: '日文', checked: false },
|
|
{ title: '其他', checked: false },
|
|
]);
|
|
const languageList = useRef<string[]>([]);
|
|
const [allPlatforms, setAllPlatforms] = useState<boolean>(true);
|
|
const [allLanguage, setAllLanguage] = useState<boolean>(true);
|
|
const [sortType, setSortType] = useState<string>('fans_count');
|
|
const sortTypeValue = useRef('fans_count');
|
|
const [fansSort, setFansSort] = useState<string>('desc');
|
|
const sortValue = useRef('desc');
|
|
const [relevanceSort, setRelevanceSort] = useState<string>('desc');
|
|
const [userList, setUserList] = useState<UserDataItem[]>([]);
|
|
const [searchText, setSearchText] = useState<string>(inputValue);
|
|
const [isSearching, setIsSearching] = useState<boolean>(false);
|
|
const [searchType, setSearchType] = useState<string>('network');
|
|
const searchValue = useRef<string>('network');
|
|
const [selNetCount, setSelNetCount] = useState<number>(0);
|
|
const [actorCount, setActorCount] = useState<number>(0);
|
|
const [directionType, setDirectionType] = useState<string>('TW');
|
|
const directionValue = useRef<string>('TW');
|
|
const consumerUrl = import.meta.env.VITE_AIPI_CONSUMER_URL || 'https://ch1.b.uat.4.cn/consumer';
|
|
const backendUrl = typeof window !== 'undefined'
|
|
? (import.meta.env.VITE_BACKEND_BASE_URL || 'http://localhost:8001')
|
|
: 'http://localhost:8001';
|
|
const [selectedSearchSources, setSelectedSearchSources] = useState<UserDataItem[]>([]);
|
|
const [isImporting, setIsImporting] = useState(false);
|
|
|
|
const searchChange = async () => {
|
|
setIsSearching(true);
|
|
if (searchValue.current === 'network') {
|
|
await agentUserSearch();
|
|
} else {
|
|
await agentGroupSearch();
|
|
}
|
|
};
|
|
|
|
const setDirectionChange = async (type: string) => {
|
|
setDirectionType(type);
|
|
directionValue.current = type;
|
|
await searchChange();
|
|
};
|
|
|
|
const searchTypeChange = async (value: string) => {
|
|
setSearchType(value);
|
|
searchValue.current = value;
|
|
await searchChange();
|
|
};
|
|
|
|
const allPlatformChange = async () => {
|
|
setAllPlatforms(true);
|
|
setPlatforms((prev) => prev.map((item) => ({ ...item, checked: false })));
|
|
platformList.current = [];
|
|
await searchChange();
|
|
};
|
|
|
|
const handleTogglePlatform = async (index: number) => {
|
|
setPlatforms((prev) => {
|
|
const newPlatforms = prev.map((item, i) =>
|
|
i === index ? { ...item, checked: !item.checked } : item
|
|
);
|
|
const checkedTitles = newPlatforms.filter((item) => item.checked).map((item) => item.title);
|
|
platformList.current = checkedTitles;
|
|
setAllPlatforms(newPlatforms.every((item) => !item.checked));
|
|
return newPlatforms;
|
|
});
|
|
await searchChange();
|
|
};
|
|
|
|
const allLanguageChange = async () => {
|
|
setAllLanguage(true);
|
|
setLanguages((prev) => prev.map((item) => ({ ...item, checked: false })));
|
|
languageList.current = [];
|
|
await searchChange();
|
|
};
|
|
|
|
const handleToggleLanguage = async (index: number) => {
|
|
setLanguages((prev) => {
|
|
const newItems = prev.map((item, i) =>
|
|
i === index ? { ...item, checked: !item.checked } : item
|
|
);
|
|
const checkedTitles = newItems.filter((item) => item.checked).map((item) => item.title);
|
|
languageList.current = checkedTitles;
|
|
setAllLanguage(newItems.every((item) => !item.checked));
|
|
return newItems;
|
|
});
|
|
await searchChange();
|
|
};
|
|
|
|
const sortTypeChange = async (type: string) => {
|
|
setSortType(type);
|
|
sortTypeValue.current = type;
|
|
sortValue.current = sortValue.current === 'desc' ? 'asc' : 'desc';
|
|
if (type === 'fans_count') setFansSort(sortValue.current);
|
|
else if (type === '_score') setRelevanceSort(sortValue.current);
|
|
await searchChange();
|
|
};
|
|
|
|
const agentUserSearch = async () => {
|
|
try {
|
|
const response = await authenticatedFetch(`${consumerUrl}/portrait/agentUser`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer admin' },
|
|
body: JSON.stringify({
|
|
historyKey: [],
|
|
historyTitle: '',
|
|
keyword: searchText,
|
|
language: languageList.current,
|
|
page: 1,
|
|
limit: 12,
|
|
platform: platformList.current,
|
|
politicalStand: directionValue.current,
|
|
sort: sortTypeValue.current,
|
|
asc: sortValue.current,
|
|
typeKeyword: 9,
|
|
}),
|
|
});
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setIsSearching(false);
|
|
const result: UserDataItem[] = data.data.list;
|
|
result.forEach((item) => {
|
|
item.profile_image = item.profile_image
|
|
? `http://minio-proxy.b.sit.4.cn/facebook-proxy?url=${item.profile_image}&is_avatar=true`
|
|
: 'http://ty.b.uat.4.cn/manage/img/default.png';
|
|
item.classification = `${item.political_stand}-${item.history_key}-${item.history_title}`;
|
|
item.title = item.user_name || item.screen_name || item.user_id;
|
|
item.content = item.agentInfo['账号描述'];
|
|
item.url = `http://ty.b.uat.4.cn/fromB/toDetail?user_id=${item.user_id}&platform=${item.platform}`;
|
|
});
|
|
setUserList(result);
|
|
setSelNetCount(data.data.total);
|
|
setActorCount(data.data.zs_total);
|
|
}
|
|
} catch (error) {
|
|
console.error('Search error:', error);
|
|
}
|
|
};
|
|
|
|
const agentGroupSearch = async () => {
|
|
try {
|
|
const response = await authenticatedFetch(`${consumerUrl}/portrait/agentGroup`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer admin' },
|
|
body: JSON.stringify({
|
|
historyKey: [],
|
|
historyTitle: '',
|
|
keyword: searchText,
|
|
language: languageList.current,
|
|
page: 1,
|
|
limit: 12,
|
|
platform: platformList.current,
|
|
politicalStand: directionValue.current,
|
|
sort: sortTypeValue.current,
|
|
asc: sortValue.current,
|
|
typeKeyword: 9,
|
|
}),
|
|
});
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setIsSearching(false);
|
|
const result: UserDataItem[] = data.data.data;
|
|
result.forEach((item) => {
|
|
item.hobbies = item.groupDiscussTags || [];
|
|
item.profile_image = item.profile_image
|
|
? `http://minio-proxy.b.sit.4.cn/facebook-proxy?url=${item.profile_image}&is_avatar=true`
|
|
: 'http://ty.b.uat.4.cn/manage/img/default.png';
|
|
item.classification = `${item.political_stand}`;
|
|
item.title = item.group_name || item.groupName;
|
|
if (item.hx_info?.['用户画像']) {
|
|
item.content = item.hx_info['用户画像']['账号描述'];
|
|
}
|
|
item.url = '';
|
|
});
|
|
setUserList(result);
|
|
setActorCount(data.data.total);
|
|
}
|
|
} catch (error) {
|
|
console.error('Search error:', error);
|
|
}
|
|
};
|
|
|
|
const listDataCheckedChange = (index: number, checked: boolean) => {
|
|
setUserList((prevSources) => {
|
|
const newSources = prevSources.map((item, aIndex) =>
|
|
aIndex === index ? { ...item, checked } : item
|
|
);
|
|
setSelectedSearchSources(newSources.filter((item) => item.checked));
|
|
return newSources;
|
|
});
|
|
};
|
|
|
|
const linkToB2 = (data: UserDataItem) => {
|
|
const url = `http://ty.b.uat.4.cn/fromB/toDetail?user_id=${data.user_id}&platform=${data.platform}`;
|
|
window.open(url, '_blank');
|
|
};
|
|
|
|
const handleBatchDelete = () => {
|
|
setUserList((prev) => prev.map((item) => ({ ...item, checked: false })));
|
|
setSelectedSearchSources([]);
|
|
};
|
|
|
|
const handleImport = async () => {
|
|
if (selectedSearchSources.length === 0) {
|
|
toast.warning('没有可导入的来源');
|
|
return;
|
|
}
|
|
setIsImporting(true);
|
|
const dataList = selectedSearchSources.map((item) => ({
|
|
title: item.title,
|
|
content: item.content,
|
|
url: item.url,
|
|
}));
|
|
try {
|
|
const response = await authenticatedFetch(`${backendUrl}/api/v1/source/import`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
search_space_id: Number(searchSpaceId),
|
|
source_list: dataList,
|
|
}),
|
|
}, true);
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
toast.success(`成功导入 ${data.imported_count || dataList.length} 个来源`);
|
|
setSearchText('');
|
|
setUserList([]);
|
|
handleBatchDelete();
|
|
onSuccess?.();
|
|
} else {
|
|
const errorData = await response.json();
|
|
toast.error(errorData.detail || '导入失败,请重试');
|
|
}
|
|
} catch {
|
|
toast.error('导入失败,请重试');
|
|
} finally {
|
|
setIsImporting(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
void searchChange();
|
|
}, []);
|
|
|
|
return (
|
|
<div>
|
|
<div className="bg-white rounded-[5px] p-[10px]">
|
|
<div className="border border-[#e8e8e8] rounded-[5px] p-[10px]">
|
|
<div className="mb-[10px] flex">
|
|
<span className="text-[#3D3D3D]">所属平台:</span>
|
|
<span className="flex-1 min-w-0">
|
|
<span className="flex items-center gap-[10px] flex-wrap">
|
|
<span
|
|
className={`flex items-center cursor-pointer border border-[transparent] pl-[10px] pr-[10px] ${allPlatforms ? 'platform-active' : ''}`}
|
|
onClick={() => void allPlatformChange()}
|
|
>全部</span>
|
|
{platforms.map((item, index) => (
|
|
<span
|
|
key={index}
|
|
className={`flex items-center cursor-pointer border border-[transparent] pl-[10px] pr-[10px] ${item.checked ? 'platform-active' : ''}`}
|
|
onClick={() => void handleTogglePlatform(index)}
|
|
>
|
|
<img src={`/platform/${item.icon}`} className="w-[24px]" alt="" />
|
|
<span className="w-[2px] h-[12px] ml-[5px] mr-[5px] bg-[#dbdbdb]"></span>
|
|
<span className="ml-[5px]">{item.title}</span>
|
|
</span>
|
|
))}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
<div className="mb-[10px] flex">
|
|
<span className="text-[#3D3D3D]">常用语言:</span>
|
|
<span className="flex-1 min-w-0">
|
|
<span className="flex items-center gap-[10px] flex-wrap">
|
|
<span
|
|
className={`flex items-center cursor-pointer border border-[transparent] pl-[10px] pr-[10px] ${allLanguage ? 'platform-active' : ''}`}
|
|
onClick={() => void allLanguageChange()}
|
|
>全部</span>
|
|
{languages.map((item, index) => (
|
|
<span
|
|
key={index}
|
|
className={`flex items-center cursor-pointer border border-[transparent] pl-[10px] pr-[10px] ${item.checked ? 'platform-active' : ''}`}
|
|
onClick={() => void handleToggleLanguage(index)}
|
|
>
|
|
<span>{item.title}</span>
|
|
</span>
|
|
))}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
<div className="mb-[10px] flex">
|
|
<span className="text-[#3D3D3D]">方向:</span>
|
|
<span className="flex-1 min-w-0">
|
|
<span className="flex items-center gap-[10px] flex-wrap">
|
|
<span className={`flex items-center cursor-pointer border border-[transparent] pl-[10px] pr-[10px] ${directionType === 'TW' ? 'platform-active' : ''}`} onClick={() => void setDirectionChange('TW')}>W方向</span>
|
|
<span className={`flex items-center cursor-pointer border border-[transparent] pl-[10px] pr-[10px] ${directionType === 'US' ? 'platform-active' : ''}`} onClick={() => void setDirectionChange('US')}>U方向</span>
|
|
<span className={`flex items-center cursor-pointer border border-[transparent] pl-[10px] pr-[10px] ${directionType === '其他' ? 'platform-active' : ''}`} onClick={() => void setDirectionChange('其他')}>其他</span>
|
|
</span>
|
|
</span>
|
|
</div>
|
|
<div className="mb-[10px] flex">
|
|
<span className="text-[#3D3D3D]">类型:</span>
|
|
<span className="flex-1 min-w-0">
|
|
<span className="flex items-center gap-[10px] flex-wrap">
|
|
<span className={`flex items-center cursor-pointer border border-[transparent] pl-[10px] pr-[10px] ${searchType === 'network' ? 'platform-active' : ''}`} onClick={() => void searchTypeChange('network')}>网络XWT</span>
|
|
<span className={`flex items-center cursor-pointer border border-[transparent] pl-[10px] pr-[10px] ${searchType === 'group' ? 'platform-active' : ''}`} onClick={() => void searchTypeChange('group')}>群组XWT</span>
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="bg-white rounded-[5px] pt-[15px] mt-[10px]">
|
|
<div className="flex items-center justify-between pb-[10px] mb-[10px] border-b border-b-[#f1f3f5]">
|
|
<div className="flex items-center ml-[10px]">
|
|
<div className="title-bar mr-[8px]"></div>
|
|
<div className="text-[#454859]">用户表格管理</div>
|
|
</div>
|
|
<div className="flex items-center">
|
|
<div
|
|
className={`relative flex items-center cursor-pointer border ${sortType === 'fans_count' ? 'border-[#cf1322]' : ''} p-[7px] pl-[10px] pr-[20px] rounded-[5px]`}
|
|
onClick={() => void sortTypeChange('fans_count')}
|
|
>
|
|
<span className="leading-[16px]">粉丝数排序</span>
|
|
<div className="ml-[10px]">
|
|
<div className={`sort-up ${fansSort === 'asc' ? 'sort-up-active' : ''}`}></div>
|
|
<div className={`mt-[4px] sort-down ${fansSort === 'desc' ? 'sort-down-active' : ''}`}></div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
className={`relative flex items-center ml-[10px] cursor-pointer border ${sortType === '_score' ? 'border-[#cf1322]' : ''} p-[7px] pl-[10px] pr-[20px] rounded-[5px]`}
|
|
onClick={() => void sortTypeChange('_score')}
|
|
>
|
|
<span className="leading-[16px]">相关度排序</span>
|
|
<div className="ml-[10px]">
|
|
<div className={`sort-up ${relevanceSort === 'asc' ? 'sort-up-active' : ''}`}></div>
|
|
<div className={`mt-[4px] sort-down ${relevanceSort === 'desc' ? 'sort-down-active' : ''}`}></div>
|
|
</div>
|
|
</div>
|
|
{searchType === 'network' && (
|
|
<>
|
|
<div className="flex items-center ml-[35px]">
|
|
<img className="w-[24px] h-[24px]" src="/icon/group-yellow-logo.svg" alt="" />
|
|
<div className="ml-[5px]">
|
|
<div className="text-[#454859] leading-[16px]">当前选中网络XWT数</div>
|
|
<div className="text-[#d97b00] leading-[16px] mt-[5px]">{selNetCount}</div>
|
|
</div>
|
|
</div>
|
|
<div className="ml-[5px] mr-[5px] h-[30px] w-[2px] -skew-x-[30deg] bg-[#dbdbdb]"></div>
|
|
</>
|
|
)}
|
|
<div className={`flex items-center ${searchType === 'network' ? 'ml-[5px]' : 'ml-[20px]'} mr-[25px]`}>
|
|
<img className="w-[24px] h-[24px]" src="/icon/group-red-logo.svg" alt="" />
|
|
<div className="ml-[5px]">
|
|
<div className="text-[#454859] leading-[16px]">网络XWT总数</div>
|
|
<div className="text-[#cf1322] leading-[16px] mt-[5px]">{actorCount}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{selectedSearchSources.length > 0 && (
|
|
<div className="flex items-center justify-between px-4 py-2 bg-gray-50 border-b border-gray-100">
|
|
<span className="text-sm text-gray-600">已选中 {selectedSearchSources.length} 项</span>
|
|
<div className="flex gap-2">
|
|
<Button size="sm" variant="outline" onClick={handleBatchDelete} className="h-8 px-3 text-sm">
|
|
<Trash2 className="h-3.5 w-3.5 mr-1" />
|
|
删除选中
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
onClick={() => void handleImport()}
|
|
disabled={isImporting}
|
|
className="h-8 px-3 text-sm bg-blue-50 hover:bg-blue-100 text-blue-600 border border-blue-200"
|
|
>
|
|
{isImporting ? <Loader2 className="h-3.5 w-3.5 mr-1 animate-spin" /> : <Plus className="h-3.5 w-3.5 mr-1" />}
|
|
导入选中
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
<div className="p-[10px]">
|
|
{isSearching ? (
|
|
<div className="flex items-center justify-center py-16">
|
|
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
|
</div>
|
|
) : null}
|
|
<div className={`grid grid-cols-4 gap-[10px] user-list${isSearching ? ' hidden' : ''}`}>
|
|
{userList.map((item, index) => (
|
|
<div key={index} className="user-item p-[10px] relative">
|
|
<div className="absolute top-[5px] right-[10px]">
|
|
<input
|
|
type="checkbox"
|
|
checked={item.checked ?? false}
|
|
onChange={() => listDataCheckedChange(index, !item.checked)}
|
|
className="w-4 h-4 cursor-pointer accent-primary"
|
|
/>
|
|
</div>
|
|
<div className="flex items-center mb-[10px] cursor-pointer" onClick={() => linkToB2(item)}>
|
|
<img src={item.profile_image ?? ''} className="w-[97px] h-[97px] rounded-[5px]" alt="" />
|
|
<div className="flex-1 min-w-0 pl-[10px]">
|
|
<div className="truncate mb-[10px] text-[18px]">{item.user_name || item.screen_name || item.user_id || item.group_name || item.groupName}</div>
|
|
<div className="flex justify-between mb-[10px]">
|
|
<div className="flex items-center">
|
|
<img src="/icon/group-yellow-logo.svg" className="w-[16px] h-[16px] mr-[5px]" alt="" />
|
|
<span>粉丝数:{item.fans_count || '-'}</span>
|
|
</div>
|
|
<div className="flex items-center">
|
|
<img src={`/platform/${item.platform}.png`} className="w-[25px] mr-[5px]" alt="" />
|
|
<span>{item.platform}</span>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center">
|
|
<img src="/icon/person-red-logo.svg" className="w-[16px] mr-[5px]" alt="" />
|
|
<span>实体人:{item.name}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="cursor-pointer" onClick={() => linkToB2(item)}>
|
|
<div className="flex items-center mb-[10px]">
|
|
<div className="w-[70px] flex items-center">
|
|
<img src="/icon/classification-logo.png" className="w-[16px] mr-[5px]" alt="" />
|
|
<span>分类:</span>
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="inline-block h-[28px] pl-[8px] pr-[8px] bg-[#fef6f6] text-[#cf1322]">{item.classification}</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center mb-[10px]">
|
|
<div className="w-[70px] flex items-center">
|
|
<img src="/icon/user-green-logo.png" className="w-[16px] mr-[5px]" alt="" />
|
|
<span>标签:</span>
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex flex-wrap items-center gap-[5px]">
|
|
{(item.hobbies ?? []).map((hobbie) => (
|
|
<div key={hobbie} className="h-[28px] pl-[8px] pr-[8px] bg-[#f6fdfb] text-[#1eb07f]">{hobbie}</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-baseline mb-[10px]">
|
|
<div className="w-[70px] flex items-center">
|
|
<img src="/icon/user-green-logo.png" className="w-[16px] mr-[5px]" alt="" />
|
|
<span>简介:</span>
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="line-clamp-3 text-ellipsis overflow-hidden">
|
|
{(item.agentInfo && item.agentInfo['账号描述']) || (item.hx_info?.['用户画像']?.['账号描述']) || ''}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|