增加右下角联系我们整体功能
This commit is contained in:
Vendored
+90
-90
File diff suppressed because one or more lines are too long
Vendored
+36
-36
@@ -1,37 +1,37 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<title>后台管理</title>
|
||||
<script>
|
||||
(function() {
|
||||
var cached = localStorage.getItem('siteInfo');
|
||||
if (cached) {
|
||||
try {
|
||||
var info = JSON.parse(cached);
|
||||
if (info.siteName) {
|
||||
document.title = info.siteName + ' - 管理后台';
|
||||
}
|
||||
if (info.siteLogo) {
|
||||
var link = document.querySelector('link[rel="icon"]');
|
||||
if (link) {
|
||||
link.href = info.siteLogo;
|
||||
link.type = 'image/png';
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-DAaavCN6.js"></script>
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<title>后台管理</title>
|
||||
<script>
|
||||
(function() {
|
||||
var cached = localStorage.getItem('siteInfo');
|
||||
if (cached) {
|
||||
try {
|
||||
var info = JSON.parse(cached);
|
||||
if (info.siteName) {
|
||||
document.title = info.siteName + ' - 管理后台';
|
||||
}
|
||||
if (info.siteLogo) {
|
||||
var link = document.querySelector('link[rel="icon"]');
|
||||
if (link) {
|
||||
link.href = info.siteLogo;
|
||||
link.type = 'image/png';
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-C1APw6hP.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>␍
|
||||
</body>
|
||||
</html>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -31,6 +31,7 @@ import AdminShotReplications from './pages/AdminShotReplications';
|
||||
import AdminShotTaskSetDetail from './pages/AdminShotTaskSetDetail';
|
||||
import AdminReplicationProjectDetail from './pages/AdminReplicationProjectDetail';
|
||||
import AdminVideoPromptSchemaConfig from './pages/AdminVideoPromptSchemaConfig';
|
||||
import AdminContactRequests from './pages/AdminContactRequests';
|
||||
|
||||
import { useAdminStore } from './store';
|
||||
|
||||
@@ -103,6 +104,7 @@ const App = () => {
|
||||
<Route path="authoriza" element={<AdminAuthoriz />} />
|
||||
<Route path="consume" element={<AdminConsume />} />
|
||||
<Route path="platform" element={<AdminPlatform />} />
|
||||
<Route path="contact-requests" element={<AdminContactRequests />} />
|
||||
</Route>
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
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;
|
||||
@@ -1 +1 @@
|
||||
{"root":["./src/app.tsx","./src/env.d.ts","./src/main.tsx","./src/api/client.ts","./src/api/crypto.ts","./src/api/index.ts","./src/pages/adminauthoriz.tsx","./src/pages/adminconsume.tsx","./src/pages/admincreditratios.tsx","./src/pages/admincreditrecords.tsx","./src/pages/admindashboard.tsx","./src/pages/admingenerationairecords.tsx","./src/pages/admingenerationrecords.tsx","./src/pages/adminhotopeningreplicationdetail.tsx","./src/pages/adminhotopeningreplications.tsx","./src/pages/adminimageengines.tsx","./src/pages/adminindustries.tsx","./src/pages/adminlayout.tsx","./src/pages/adminloginpage.tsx","./src/pages/adminmenuconfig.tsx","./src/pages/adminmodels.tsx","./src/pages/adminnotificationmanager.tsx","./src/pages/adminoauthapplist.tsx","./src/pages/adminoperationlogs.tsx","./src/pages/adminpaymentconfig.tsx","./src/pages/adminpaymentstats.tsx","./src/pages/adminrechargepackages.tsx","./src/pages/adminreplicationprojectdetail.tsx","./src/pages/adminsettings.tsx","./src/pages/adminshotreplications.tsx","./src/pages/adminshottasksetdetail.tsx","./src/pages/adminusers.tsx","./src/pages/adminvideoengines.tsx","./src/pages/adminvideopromptschemaconfig.tsx","./src/pages/adminplatform.tsx","./src/pages/adminreplication/components/jsoncollapse.tsx","./src/pages/adminreplication/components/mediapreview.tsx","./src/pages/adminreplication/components/statustag.tsx","./src/pages/adminreplication/components/videopromptschemaviewer.tsx","./src/store/index.ts","./src/types/index.ts","./src/types/xlsx-js-style.d.ts","./src/utils/excelexport.ts","./src/utils/formatdate.ts","./src/utils/videopromptschema.ts"],"version":"6.0.3"}
|
||||
{"root":["./src/app.tsx","./src/env.d.ts","./src/main.tsx","./src/api/client.ts","./src/api/crypto.ts","./src/api/index.ts","./src/pages/adminauthoriz.tsx","./src/pages/adminconsume.tsx","./src/pages/admincontactrequests.tsx","./src/pages/admincreditratios.tsx","./src/pages/admincreditrecords.tsx","./src/pages/admindashboard.tsx","./src/pages/admingenerationairecords.tsx","./src/pages/admingenerationrecords.tsx","./src/pages/adminhotopeningreplicationdetail.tsx","./src/pages/adminhotopeningreplications.tsx","./src/pages/adminimageengines.tsx","./src/pages/adminindustries.tsx","./src/pages/adminlayout.tsx","./src/pages/adminloginpage.tsx","./src/pages/adminmenuconfig.tsx","./src/pages/adminmodels.tsx","./src/pages/adminnotificationmanager.tsx","./src/pages/adminoauthapplist.tsx","./src/pages/adminoperationlogs.tsx","./src/pages/adminpaymentconfig.tsx","./src/pages/adminpaymentstats.tsx","./src/pages/adminrechargepackages.tsx","./src/pages/adminreplicationprojectdetail.tsx","./src/pages/adminsettings.tsx","./src/pages/adminshotreplications.tsx","./src/pages/adminshottasksetdetail.tsx","./src/pages/adminusers.tsx","./src/pages/adminvideoengines.tsx","./src/pages/adminvideopromptschemaconfig.tsx","./src/pages/adminplatform.tsx","./src/pages/adminreplication/components/jsoncollapse.tsx","./src/pages/adminreplication/components/mediapreview.tsx","./src/pages/adminreplication/components/statustag.tsx","./src/pages/adminreplication/components/videopromptschemaviewer.tsx","./src/store/index.ts","./src/types/index.ts","./src/types/xlsx-js-style.d.ts","./src/utils/excelexport.ts","./src/utils/formatdate.ts","./src/utils/videopromptschema.ts"],"version":"6.0.3"}
|
||||
Reference in New Issue
Block a user