私域操盘手 - 修复流量池页面不会自动加载状态列表选择器的问题

This commit is contained in:
柳清爽
2025-05-13 14:43:21 +08:00
parent 957d197245
commit 55ea2bf7e3
4 changed files with 66 additions and 72 deletions

View File

@@ -37,7 +37,7 @@ interface TrafficUser {
phone: string phone: string
region: string region: string
note: string note: string
status: "pending" | "added" | "failed" status: number
addTime: string addTime: string
source: string source: string
assignedTo: string assignedTo: string
@@ -48,6 +48,7 @@ interface TrafficUser {
interface StatusType { interface StatusType {
id: number id: number
name: string name: string
code: string
} }
interface ApiResponse<T> { interface ApiResponse<T> {
@@ -144,37 +145,43 @@ export default function TrafficPoolPage() {
const params = new URLSearchParams({ const params = new URLSearchParams({
page: page.toString(), page: page.toString(),
limit: "30", // 设置每页显示30条 limit: "30",
search: debouncedSearchQuery, search: debouncedSearchQuery,
category: activeCategory, category: activeCategory,
source: sourceFilter !== "all" ? sourceFilter : "", source: sourceFilter !== "all" ? sourceFilter : "",
status: statusFilter === "all" ? "" : statusFilter, status: statusFilter === "all" ? "" : statusFilter,
}) })
// 检查是否有来源参数
const sourceParam = searchParams?.get("source") const sourceParam = searchParams?.get("source")
if (sourceParam) { if (sourceParam) {
params.append("wechatSource", sourceParam) params.append("wechatSource", sourceParam)
} }
const response = await api.get<ApiResponse<TrafficPoolResponse>>(`/v1/traffic/pool?${params.toString()}`) const response = await api.get<ApiResponse<TrafficPoolResponse>>(`/v1/traffic/pool?${params.toString()}`, {
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`
}
} as any)
if (response.code === 200) { if (response.code === 200) {
const { list, pagination, statistics } = response.data const { list, pagination, statistics } = response.data
// 转换数据格式
const transformedUsers = list.map(user => ({ const transformedUsers = list.map(user => ({
...user,
id: user.id.toString(), id: user.id.toString(),
status: getStatusFromCode(user.status), avatar: user.avatar,
tags: user.tags || [], nickname: user.name || user.nickname || '未知用户',
category: user.category || "potential", wechatId: user.wechatId,
phone: user.phone,
region: user.region,
note: user.note,
status: user.status,
addTime: formatDateTime(user.createTime), addTime: formatDateTime(user.createTime),
source: user.fromd || '未知来源', source: user.fromd || '未知来源',
nickname: user.name || user.nickname || '未知用户' assignedTo: user.assignedTo,
category: user.category || "potential",
tags: user.tags || []
})) }))
// 更新用户列表
setUsers(prev => isNewSearch ? transformedUsers : [...prev, ...transformedUsers]) setUsers(prev => isNewSearch ? transformedUsers : [...prev, ...transformedUsers])
setCurrentPage(page) setCurrentPage(page)
setHasMore(list.length > 0 && page < pagination.totalPages) setHasMore(list.length > 0 && page < pagination.totalPages)
@@ -207,7 +214,11 @@ export default function TrafficPoolPage() {
const fetchStatusTypes = useCallback(async () => { const fetchStatusTypes = useCallback(async () => {
try { try {
const response = await api.get<ApiResponse<StatusType[]>>('/v1/traffic/pool/types') const response = await api.get<ApiResponse<StatusType[]>>('/v1/traffic/pool/types', {
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`
}
} as any)
if (response.code === 200) { if (response.code === 200) {
setStatusTypes(response.data) setStatusTypes(response.data)
@@ -236,9 +247,9 @@ export default function TrafficPoolPage() {
fetchUsers(1, true) fetchUsers(1, true)
}, [fetchUsers]) }, [fetchUsers])
// 设置 IntersectionObserver // 初始化 IntersectionObserver
useEffect(() => { useEffect(() => {
observerRef.current = new IntersectionObserver( const observer = new IntersectionObserver(
(entries) => { (entries) => {
if (entries[0].isIntersecting && hasMore && !isFetching) { if (entries[0].isIntersecting && hasMore && !isFetching) {
fetchUsers(currentPage + 1) fetchUsers(currentPage + 1)
@@ -247,35 +258,30 @@ export default function TrafficPoolPage() {
{ threshold: 0.5 } { threshold: 0.5 }
) )
return () => { if (loadingRef.current) {
if (observerRef.current) { observer.observe(loadingRef.current)
observerRef.current.disconnect()
}
}
}, [fetchUsers, currentPage, hasMore, isFetching])
// 观察加载指示器
useEffect(() => {
if (loadingRef.current && observerRef.current) {
observerRef.current.observe(loadingRef.current)
} }
return () => { return () => {
if (loadingRef.current && observerRef.current) { if (loadingRef.current) {
observerRef.current.unobserve(loadingRef.current) observer.unobserve(loadingRef.current)
} }
} }
}, [loadingRef.current, observerRef.current]) }, [hasMore, isFetching, currentPage, fetchUsers])
// 初始加载 // 初始化数据
useEffect(() => { useEffect(() => {
fetchStatusTypes()
fetchUsers(1, true) fetchUsers(1, true)
return () => { }, [])
if (abortControllerRef.current) {
abortControllerRef.current.abort() // 监听筛选条件变化
} useEffect(() => {
} setUsers([])
}, [fetchUsers]) setCurrentPage(1)
setHasMore(true)
fetchUsers(1, true)
}, [activeCategory, sourceFilter, statusFilter, debouncedSearchQuery])
const handleUserClick = (user: TrafficUser) => { const handleUserClick = (user: TrafficUser) => {
setSelectedUser(user) setSelectedUser(user)
@@ -283,16 +289,8 @@ export default function TrafficPoolPage() {
} }
// 添加状态码转换函数 // 添加状态码转换函数
const getStatusFromCode = (statusCode: number): "pending" | "added" | "failed" => { const getStatusFromCode = (statusCode: number): number => {
const statusMap: Record<number, "pending" | "added" | "failed"> = { return statusCode;
1: "pending", // 待处理
2: "pending", // 处理中
3: "added", // 已添加
4: "failed", // 已拒绝
5: "failed", // 已过期
6: "failed", // 已取消
}
return statusMap[statusCode] || "pending"
} }
return ( return (
@@ -431,14 +429,14 @@ export default function TrafficPoolPage() {
<div className="font-medium truncate">{user.nickname}</div> <div className="font-medium truncate">{user.nickname}</div>
<div <div
className={`text-xs px-2 py-1 rounded-full ${ className={`text-xs px-2 py-1 rounded-full ${
user.status === "added" user.status === 2
? "bg-green-100 text-green-800" ? "bg-green-100 text-green-800"
: user.status === "pending" : user.status === 1
? "bg-yellow-100 text-yellow-800" ? "bg-yellow-100 text-yellow-800"
: "bg-red-100 text-red-800" : "bg-red-100 text-red-800"
}`} }`}
> >
{user.status === "added" ? "已添加" : user.status === "pending" ? "待处理" : "已失败"} {user.status === 2 ? "已添加" : user.status === 1 ? "待处理" : "已失败"}
</div> </div>
</div> </div>
<div className="text-sm text-gray-500">: {user.wechatId}</div> <div className="text-sm text-gray-500">: {user.wechatId}</div>
@@ -463,19 +461,19 @@ export default function TrafficPoolPage() {
</Card> </Card>
))} ))}
{/* 加载更多指示器 */} {/* 加载状态显示 */}
{hasMore && ( <div ref={loadingRef} className="flex justify-center py-4">
<div ref={loadingRef} className="py-4 flex justify-center"> {isFetching && (
{isFetching && <RefreshCw className="h-6 w-6 animate-spin text-blue-500" />} <div className="flex items-center space-x-2">
</div> <div className="w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin"></div>
)} <span className="text-gray-600">...</span>
</div>
{/* 显示加载状态和总数 */} )}
<div className="text-sm text-gray-500 text-center"> {!hasMore && users.length > 0 && (
{stats.total > 0 && ( <span className="text-gray-500"></span>
<span> )}
{users.length} / {stats.total} {!isFetching && users.length === 0 && (
</span> <span className="text-gray-500"></span>
)} )}
</div> </div>
</> </>
@@ -502,18 +500,14 @@ export default function TrafficPoolPage() {
<div className="text-sm text-gray-500">{selectedUser.wechatId}</div> <div className="text-sm text-gray-500">{selectedUser.wechatId}</div>
<Badge <Badge
className={`mt-1 ${ className={`mt-1 ${
selectedUser.status === "added" selectedUser.status === 2
? "bg-green-100 text-green-800" ? "bg-green-100 text-green-800"
: selectedUser.status === "pending" : selectedUser.status === 1
? "bg-yellow-100 text-yellow-800" ? "bg-yellow-100 text-yellow-800"
: "bg-red-100 text-red-800" : "bg-red-100 text-red-800"
}`} }`}
> >
{selectedUser.status === "added" {selectedUser.status === 2 ? "已添加" : selectedUser.status === 1 ? "待处理" : "已失败"}
? "已添加"
: selectedUser.status === "pending"
? "待处理"
: "已失败"}
</Badge> </Badge>
</div> </div>
</div> </div>

View File

@@ -41,8 +41,8 @@ Route::group('v1/', function () {
// 流量池相关 // 流量池相关
Route::group('traffic/pool', function () { Route::group('traffic/pool', function () {
Route::get('', 'app\cunkebao\controller\traffic\GetDissociateListWithInCompanyV1Controller@index'); Route::get('', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@index');
Route::get('types', 'app\cunkebao\controller\traffic\GetDissociateTypeListV1Controller@index'); Route::get('types', 'app\cunkebao\controller\traffic\GetPotentialTypeListV1Controller@index');
}); });
// 工作台相关 // 工作台相关

View File

@@ -10,7 +10,7 @@ use library\ResponseHelper;
/** /**
* 流量池控制器 * 流量池控制器
*/ */
class GetDissociateListWithInCompanyV1Controller extends BaseController class GetPotentialListWithInCompanyV1Controller extends BaseController
{ {
/** /**
* 构建查询条件 * 构建查询条件

View File

@@ -10,7 +10,7 @@ use library\ResponseHelper;
/** /**
* 流量池控制器 * 流量池控制器
*/ */
class GetDissociateTypeListV1Controller extends BaseController class GetPotentialTypeListV1Controller extends BaseController
{ {
protected function getTypeList(): array protected function getTypeList(): array
{ {