Files
video-gen/video-gen-admin/src/pages/AdminContactRequests.tsx
T

270 lines
8.0 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Table, Button, Tag, Space, Typography, message, Modal } from 'antd';
import { CheckOutlined, DeleteOutlined, EyeOutlined, FilterOutlined } from '@ant-design/icons';
import { useAdminStore } from '../store';
import { api } from '../api/client';
interface ContactRequest {
id: string;
user_id: string;
phone: string;
company_name: string;
industry: string;
name: string;
message: string | null;
is_handled: boolean;
created_at: string;
}
const AdminContactRequests: React.FC = () => {
const [data, setData] = useState<ContactRequest[]>([]);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(20);
const [isHandledFilter, setIsHandledFilter] = useState<boolean | null>(null);
const [selectedItem, setSelectedItem] = useState<ContactRequest | null>(null);
const [detailModalOpen, setDetailModalOpen] = useState(false);
const { user } = useAdminStore();
const fetchData = async () => {
if (!user?.isAdmin) return;
setLoading(true);
try {
const query = new URLSearchParams();
query.set('page', String(page));
query.set('page_size', String(pageSize));
if (isHandledFilter !== null) {
query.set('is_handled', String(isHandledFilter));
}
const res = await api.get<{ items: ContactRequest[]; total: number }>(`/contact/requests?${query.toString()}`);
setData(res.items);
setTotal(res.total);
} catch (err: any) {
message.error(err?.message || '获取失败');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, [page, pageSize, isHandledFilter]);
const handleMarkHandled = async (id: string) => {
try {
await api.put(`/contact/requests/${id}/handle`);
message.success('已标记为处理');
fetchData();
} catch (err: any) {
message.error(err?.message || '操作失败');
}
};
const handleDelete = async (id: string) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除这条联系请求吗?',
onOk: async () => {
try {
await api.delete(`/contact/requests/${id}`);
message.success('删除成功');
fetchData();
} catch (err: any) {
message.error(err?.message || '删除失败');
}
},
});
};
const handleViewDetail = (item: ContactRequest) => {
setSelectedItem(item);
setDetailModalOpen(true);
};
const columns = [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
width: 100,
},
{
title: '手机号',
dataIndex: 'phone',
key: 'phone',
width: 120,
},
{
title: '公司名称',
dataIndex: 'company_name',
key: 'company_name',
width: 150,
ellipsis: true,
},
{
title: '行业',
dataIndex: 'industry',
key: 'industry',
width: 120,
},
{
title: '状态',
dataIndex: 'is_handled',
key: 'is_handled',
width: 80,
render: (isHandled: boolean) => (
<Tag color={isHandled ? 'green' : 'orange'}>
{isHandled ? '已处理' : '待处理'}
</Tag>
),
},
{
title: '提交时间',
dataIndex: 'created_at',
key: 'created_at',
width: 160,
render: (date: string) => {
return new Date(date).toLocaleString('zh-CN');
},
},
{
title: '操作',
key: 'actions',
width: 180,
render: (_: unknown, record: ContactRequest) => (
<Space size="middle">
<Button
type="text"
icon={<EyeOutlined />}
onClick={() => handleViewDetail(record)}
>
查看
</Button>
{!record.is_handled && (
<Button
type="text"
icon={<CheckOutlined />}
onClick={() => handleMarkHandled(record.id)}
>
标记处理
</Button>
)}
<Button
type="text"
danger
icon={<DeleteOutlined />}
onClick={() => handleDelete(record.id)}
>
删除
</Button>
</Space>
),
},
];
return (
<div style={{ padding: 24 }}>
<Typography.Title level={2} style={{ marginBottom: 24 }}>
联系请求管理
</Typography.Title>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
<div style={{ display: 'flex', gap: 12 }}>
<Button
type={isHandledFilter === null ? 'primary' : 'default'}
onClick={() => setIsHandledFilter(null)}
icon={<FilterOutlined />}
>
全部
</Button>
<Button
type={isHandledFilter === false ? 'primary' : 'default'}
onClick={() => setIsHandledFilter(false)}
>
待处理
</Button>
<Button
type={isHandledFilter === true ? 'primary' : 'default'}
onClick={() => setIsHandledFilter(true)}
>
已处理
</Button>
</div>
<Typography.Text style={{ color: '#64748b' }}>
{total} 条记录
</Typography.Text>
</div>
<Table
columns={columns}
dataSource={data}
loading={loading}
pagination={{
current: page,
pageSize,
total,
onChange: (p, s) => { setPage(p); setPageSize(s); },
}}
rowKey="id"
bordered={false}
style={{ background: '#ffffff', borderRadius: 12 }}
/>
<Modal
title="联系请求详情"
open={detailModalOpen}
onCancel={() => setDetailModalOpen(false)}
footer={null}
width={500}
>
{selectedItem && (
<div style={{ padding: 8 }}>
<div style={{ marginBottom: 16 }}>
<Typography.Title level={4} style={{ marginBottom: 16 }}>
{selectedItem.name}
<Tag color={selectedItem.is_handled ? 'green' : 'orange'} style={{ marginLeft: 12 }}>
{selectedItem.is_handled ? '已处理' : '待处理'}
</Tag>
</Typography.Title>
<div style={{ display: 'grid', gridTemplateColumns: '120px 1fr', gap: 12 }}>
<Typography.Text style={{ color: '#64748b' }}>手机号:</Typography.Text>
<Typography.Text>{selectedItem.phone}</Typography.Text>
<Typography.Text style={{ color: '#64748b' }}>公司名称:</Typography.Text>
<Typography.Text>{selectedItem.company_name}</Typography.Text>
<Typography.Text style={{ color: '#64748b' }}>行业:</Typography.Text>
<Typography.Text>{selectedItem.industry}</Typography.Text>
<Typography.Text style={{ color: '#64748b' }}>提交时间:</Typography.Text>
<Typography.Text>{new Date(selectedItem.created_at).toLocaleString('zh-CN')}</Typography.Text>
{selectedItem.message && (
<>
<Typography.Text style={{ color: '#64748b' }}>留言:</Typography.Text>
<Typography.Text>{selectedItem.message}</Typography.Text>
</>
)}
</div>
</div>
<div style={{ display: 'flex', gap: 12 }}>
{!selectedItem.is_handled && (
<Button
type="primary"
onClick={() => {
handleMarkHandled(selectedItem.id);
setDetailModalOpen(false);
}}
icon={<CheckOutlined />}
>
标记为已处理
</Button>
)}
<Button onClick={() => setDetailModalOpen(false)}>关闭</Button>
</div>
</div>
)}
</Modal>
</div>
);
};
export default AdminContactRequests;