Files
soul-yongping/soul-admin/src/pages/find-partner/tabs/ResourceDockingTab.tsx

140 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from 'react'
import { Card, CardContent } from '@/components/ui/card'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { RefreshCw, Send } from 'lucide-react'
import { Pagination } from '@/components/ui/Pagination'
import { get, post } from '@/api/client'
interface MatchRecord {
id: string; userId: string; matchedUserId: string; matchType: string
phone?: string; wechatId?: string; userNickname?: string; matchedNickname?: string
createdAt: string; ckbStatus?: string
}
const typeLabels: Record<string, string> = {
investor: '资源对接', mentor: '导师顾问', team: '团队招募',
}
export function ResourceDockingTab() {
const [records, setRecords] = useState<MatchRecord[]>([])
const [total, setTotal] = useState(0)
const [page, setPage] = useState(1)
const [pageSize, setPageSize] = useState(10)
const [isLoading, setIsLoading] = useState(true)
const [typeFilter, setTypeFilter] = useState('investor')
const [pushingId, setPushingId] = useState<string | null>(null)
async function load() {
setIsLoading(true)
try {
const params = new URLSearchParams({ page: String(page), pageSize: String(pageSize), matchType: typeFilter })
const data = await get<{ success?: boolean; records?: MatchRecord[]; total?: number }>(`/api/db/match-records?${params}`)
if (data?.success) { setRecords(data.records || []); setTotal(data.total ?? 0) }
} catch (e) { console.error(e) }
finally { setIsLoading(false) }
}
useEffect(() => { load() }, [page, typeFilter])
const pushToCKB = async (record: MatchRecord) => {
if (!record.phone && !record.wechatId) {
alert('该记录无联系方式,无法推送到存客宝')
return
}
setPushingId(record.id)
try {
const res = await post<{ success?: boolean; message?: string }>('/api/ckb/join', {
type: record.matchType || 'investor',
phone: record.phone || '',
wechat: record.wechatId || '',
userId: record.userId,
name: record.userNickname || '',
})
alert(res?.message || (res?.success ? '推送成功' : '推送失败'))
} catch (e) {
alert('推送失败: ' + (e instanceof Error ? e.message : '网络错误'))
} finally {
setPushingId(null)
}
}
const totalPages = Math.ceil(total / pageSize) || 1
const hasContact = (r: MatchRecord) => !!(r.phone || r.wechatId)
return (
<div>
<div className="flex justify-between items-center mb-4">
<div>
<p className="text-gray-400">/</p>
<p className="text-gray-500 text-xs mt-1"> {total} </p>
</div>
<div className="flex items-center gap-2">
<select value={typeFilter} onChange={e => { setTypeFilter(e.target.value); setPage(1) }}
className="bg-[#0f2137] border border-gray-700 text-white rounded-lg px-3 py-2 text-sm">
{Object.entries(typeLabels).map(([k, v]) => <option key={k} value={k}>{v}</option>)}
</select>
<Button onClick={load} disabled={isLoading} variant="outline" className="border-gray-600 text-gray-300 hover:bg-gray-700/50 bg-transparent">
<RefreshCw className={`w-4 h-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} />
</Button>
</div>
</div>
<Card className="bg-[#0f2137] border-gray-700/50 shadow-xl">
<CardContent className="p-0">
{isLoading ? (
<div className="flex justify-center py-12"><RefreshCw className="w-6 h-6 text-[#38bdac] animate-spin" /><span className="ml-2 text-gray-400">...</span></div>
) : (
<>
<Table>
<TableHeader>
<TableRow className="bg-[#0a1628] hover:bg-[#0a1628] border-gray-700">
<TableHead className="text-gray-400"></TableHead>
<TableHead className="text-gray-400"></TableHead>
<TableHead className="text-gray-400"></TableHead>
<TableHead className="text-gray-400"></TableHead>
<TableHead className="text-gray-400"></TableHead>
<TableHead className="text-gray-400 text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{records.map(r => (
<TableRow key={r.id} className={`border-gray-700/50 ${hasContact(r) ? 'hover:bg-[#0a1628]' : 'opacity-60'}`}>
<TableCell className="text-white">{r.userNickname || r.userId?.slice(0, 12)}</TableCell>
<TableCell className="text-white">{r.matchedNickname || r.matchedUserId?.slice(0, 12)}</TableCell>
<TableCell>
<Badge className="bg-[#38bdac]/20 text-[#38bdac] border-0">{typeLabels[r.matchType] || r.matchType}</Badge>
</TableCell>
<TableCell className="text-sm">
{r.phone && <div className="text-green-400">📱 {r.phone}</div>}
{r.wechatId && <div className="text-blue-400">💬 {r.wechatId}</div>}
{!r.phone && !r.wechatId && <span className="text-gray-600"></span>}
</TableCell>
<TableCell className="text-gray-400 text-sm">{r.createdAt ? new Date(r.createdAt).toLocaleString() : '-'}</TableCell>
<TableCell className="text-right">
{hasContact(r) ? (
<Button size="sm" onClick={() => pushToCKB(r)} disabled={pushingId === r.id}
className="bg-[#38bdac] hover:bg-[#2da396] text-white text-xs h-7 px-3">
<Send className="w-3 h-3 mr-1" />
{pushingId === r.id ? '推送中...' : '推送CKB'}
</Button>
) : (
<span className="text-gray-600 text-xs"></span>
)}
</TableCell>
</TableRow>
))}
{records.length === 0 && <TableRow><TableCell colSpan={6} className="text-center py-12 text-gray-500"></TableCell></TableRow>}
</TableBody>
</Table>
<Pagination page={page} totalPages={totalPages} total={total} pageSize={pageSize} onPageChange={setPage} onPageSizeChange={n => { setPageSize(n); setPage(1) }} />
</>
)}
</CardContent>
</Card>
</div>
)
}