增加右下角联系我们整体功能

This commit is contained in:
2026-06-26 11:49:35 +08:00
parent e62956af79
commit faf31c8141
17 changed files with 1104 additions and 260 deletions
+13
View File
@@ -666,6 +666,19 @@ export async function getMaterialConsumptionFields(): Promise<any> {
return api.get('/material-consumption/fields');
}
// ── Contact ────────────────────────────────────────────────
export interface ContactRequestParams {
phone: string;
company_name: string;
industry: string;
name: string;
message?: string;
}
export async function createContactRequest(params: ContactRequestParams): Promise<any> {
return api.post('/contact/request', params);
}
// 查询上传素材列表
export interface ResourcesMaterialListParams {
advertiser_id?: string;
@@ -53,10 +53,13 @@ import {
ApiOutlined,
DatabaseOutlined,
CloudServerOutlined,
MessageOutlined,
DownOutlined,
InfoOutlined,
} from '@ant-design/icons';
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
import { useAuthStore } from '../../store/useAuthStore';
import { getMenuConfigs, getRechargePackages, getPaymentMethods, createRechargeOrder, getPaymentOrder, cancelPaymentOrder, getSiteInfo, getUnreadCount } from '../../api';
import { getMenuConfigs, getRechargePackages, getPaymentMethods, createRechargeOrder, getPaymentOrder, cancelPaymentOrder, getSiteInfo, getUnreadCount, createContactRequest } from '../../api';
import NotificationPopup from '../NotificationPopup';
interface MenuConfig {
@@ -167,6 +170,10 @@ const AppLayout: React.FC = () => {
const countdownTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
const currentOrderNoRef = useRef<string | null>(null);
const [enabledMethods, setEnabledMethods] = useState<{ alipay: boolean; wechat: boolean }>({ alipay: false, wechat: false });
const [contactModalOpen, setContactModalOpen] = useState(false);
const [contactForm] = Form.useForm();
const [contactExpanded, setContactExpanded] = useState(true);
const [submittingContact, setSubmittingContact] = useState(false);
// LocalStorage keys
const PENDING_ORDER_KEY = 'pending_payment_order';
@@ -316,6 +323,31 @@ const AppLayout: React.FC = () => {
setRechargeModalOpen(true);
};
const handleContactSubmit = async () => {
if (!user) {
message.warning('请先登录');
return;
}
try {
const values = await contactForm.validateFields();
setSubmittingContact(true);
await createContactRequest({
phone: values.phone,
company_name: values.companyName,
industry: values.industry,
name: values.name,
message: values.message,
});
message.success('提交成功,我们会尽快与您联系');
setContactModalOpen(false);
contactForm.resetFields();
} catch (err: any) {
message.error(err?.message || '提交失败');
} finally {
setSubmittingContact(false);
}
};
const stopPolling = useCallback(() => {
if (pollingTimerRef.current) {
clearInterval(pollingTimerRef.current);
@@ -674,6 +706,20 @@ const AppLayout: React.FC = () => {
<Typography.Text style={{ color: '#64748b', letterSpacing: 0 }}></Typography.Text>
<Typography.Text strong style={{ color: '#6366f1', fontSize: 20, fontWeight: 600 }}>{user?.credits ?? 0}</Typography.Text>
</Space>
<div style={{
padding: '12px 16px',
background: 'rgba(99, 102, 241, 0.06)',
borderRadius: 10,
marginBottom: 16,
display: 'flex',
alignItems: 'center',
gap: 8,
}}>
<InfoOutlined style={{ color: '#6366f1', fontSize: 14 }} />
<Typography.Text style={{ color: '#64748b', fontSize: 13 }}>
/
</Typography.Text>
</div>
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
{rechargeOptions.map((opt, idx) => {
const g = GRADIENTS[idx % GRADIENTS.length];
@@ -958,6 +1004,173 @@ const AppLayout: React.FC = () => {
</Modal>
<NotificationPopup />
{/* Contact Button */}
<div style={{
position: 'fixed',
right: 24,
bottom: contactExpanded ? 100 : 24,
zIndex: 1000,
transition: 'bottom 0.3s ease',
}}>
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end',
gap: 12,
}}>
{contactExpanded && (
<div style={{
padding: '16px 20px',
background: '#ffffff',
borderRadius: 16,
boxShadow: '0 8px 32px rgba(0,0,0,0.12)',
minWidth: 280,
animation: 'slideUp 0.3s ease',
}}>
<div style={{
color: '#1e293b',
fontSize: 16,
fontWeight: 600,
marginBottom: 12,
display: 'flex',
alignItems: 'center',
gap: 8,
}}>
<MessageOutlined style={{ color: '#6366f1' }} />
</div>
<div style={{
padding: '12px 16px',
background: 'rgba(99, 102, 241, 0.06)',
borderRadius: 12,
marginBottom: 12,
}}>
<div style={{ color: '#64748b', fontSize: 13, marginBottom: 4 }}></div>
<div style={{ color: '#1e293b', fontSize: 14, fontWeight: 600 }}>400-888-8888</div>
</div>
<Button
type="primary"
block
onClick={() => setContactModalOpen(true)}
style={{
borderRadius: 10,
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
border: 'none',
}}>
线
</Button>
</div>
)}
<button
onClick={() => setContactExpanded(!contactExpanded)}
style={{
width: 56,
height: 56,
borderRadius: '50%',
border: 'none',
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
color: '#ffffff',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 4px 20px rgba(99, 102, 241, 0.4)',
transition: 'all 0.3s ease',
}}
onMouseEnter={(e) => {
e.currentTarget.style.transform = 'scale(1.05)';
e.currentTarget.style.boxShadow = '0 6px 24px rgba(99, 102, 241, 0.5)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'scale(1)';
e.currentTarget.style.boxShadow = '0 4px 20px rgba(99, 102, 241, 0.4)';
}}
>
{contactExpanded ? (
<DownOutlined style={{ fontSize: 20 }} />
) : (
<MessageOutlined style={{ fontSize: 20 }} />
)}
</button>
</div>
</div>
{/* Contact Modal */}
<Modal
title={<Space><MessageOutlined /></Space>}
open={contactModalOpen}
onCancel={() => { setContactModalOpen(false); contactForm.resetFields(); }}
footer={null}
width={480}
>
<div style={{ marginTop: 8 }}>
<Form form={contactForm} layout="vertical">
<Form.Item
name="name"
label="姓名"
rules={[{ required: true, message: '请输入姓名' }]}
>
<Input placeholder="请输入您的姓名" size="large" />
</Form.Item>
<Form.Item
name="phone"
label="手机号"
rules={[
{ required: true, message: '请输入手机号' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' },
]}
>
<Input placeholder="请输入您的手机号" size="large" />
</Form.Item>
<Form.Item
name="companyName"
label="公司名称"
rules={[{ required: true, message: '请输入公司名称' }]}
>
<Input placeholder="请输入公司名称" size="large" />
</Form.Item>
<Form.Item
name="industry"
label="您的行业"
rules={[{ required: true, message: '请输入您的行业' }]}
>
<Input placeholder="请输入您的行业" size="large" />
</Form.Item>
<Form.Item name="message" label="留言(选填)">
<Input.TextArea
placeholder="请输入您的需求或问题"
rows={3}
style={{ borderRadius: 10 }}
/>
</Form.Item>
</Form>
<div style={{ marginTop: 16, display: 'flex', gap: 12 }}>
<Button
size="large"
onClick={() => { setContactModalOpen(false); contactForm.resetFields(); }}
style={{ borderRadius: 10, flex: 1 }}
>
</Button>
<Button
type="primary"
size="large"
onClick={handleContactSubmit}
loading={submittingContact}
style={{
borderRadius: 10,
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
border: 'none',
flex: 1,
}}
>
</Button>
</div>
</div>
</Modal>
</Layout>
);
};