"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"; import { readRuntimeEnv } from "@/env"; 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([ { 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([]); const [languages, setLanguages] = useState([ { title: '繁体中文', checked: false }, { title: '简体中文', checked: false }, { title: '英文', checked: false }, { title: '日文', checked: false }, { title: '其他', checked: false }, ]); const languageList = useRef([]); const [allPlatforms, setAllPlatforms] = useState(true); const [allLanguage, setAllLanguage] = useState(true); const [sortType, setSortType] = useState('fans_count'); const sortTypeValue = useRef('fans_count'); const [fansSort, setFansSort] = useState('desc'); const sortValue = useRef('desc'); const [relevanceSort, setRelevanceSort] = useState('desc'); const [userList, setUserList] = useState([]); const [searchText, setSearchText] = useState(inputValue); const [isSearching, setIsSearching] = useState(false); const [searchType, setSearchType] = useState('network'); const searchValue = useRef('network'); const [selNetCount, setSelNetCount] = useState(0); const [actorCount, setActorCount] = useState(0); const [directionType, setDirectionType] = useState('TW'); const directionValue = useRef('TW'); const consumerUrl = readRuntimeEnv("VITE_AIPI_CONSUMER_URL") || 'https://ch1.b.uat.4.cn/consumer'; const backendUrl = typeof window !== 'undefined' ? (readRuntimeEnv("VITE_BACKEND_BASE_URL") || 'http://localhost:8001') : 'http://localhost:8001'; const [selectedSearchSources, setSelectedSearchSources] = useState([]); 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 (
所属平台: void allPlatformChange()} >全部 {platforms.map((item, index) => ( void handleTogglePlatform(index)} > {item.title} ))}
常用语言: void allLanguageChange()} >全部 {languages.map((item, index) => ( void handleToggleLanguage(index)} > {item.title} ))}
方向: void setDirectionChange('TW')}>W方向 void setDirectionChange('US')}>U方向 void setDirectionChange('其他')}>其他
类型: void searchTypeChange('network')}>网络XWT void searchTypeChange('group')}>群组XWT
用户表格管理
void sortTypeChange('fans_count')} > 粉丝数排序
void sortTypeChange('_score')} > 相关度排序
{searchType === 'network' && ( <>
当前选中网络XWT数
{selNetCount}
)}
网络XWT总数
{actorCount}
{selectedSearchSources.length > 0 && (
已选中 {selectedSearchSources.length} 项
)}
{isSearching ? (
) : null}
{userList.map((item, index) => (
listDataCheckedChange(index, !item.checked)} className="w-4 h-4 cursor-pointer accent-primary" />
linkToB2(item)}>
{item.user_name || item.screen_name || item.user_id || item.group_name || item.groupName}
粉丝数:{item.fans_count || '-'}
{item.platform}
实体人:{item.name}
linkToB2(item)}>
分类:
{item.classification}
标签:
{(item.hobbies ?? []).map((hobbie) => (
{hobbie}
))}
简介:
{(item.agentInfo && item.agentInfo['账号描述']) || (item.hx_info?.['用户画像']?.['账号描述']) || ''}
))}
); }