解决冲突

This commit is contained in:
Lrd
2026-06-26 12:47:27 +08:00
20 changed files with 1562 additions and 267 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+36 -36
View File
@@ -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-CUOi61z4.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-B5mkO6qz.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-eLRg4pQk.css">
</head>
<body>
<div id="root"></div>
</body>
</html>
</head>
<body>
<div id="root"></div>
</body>
</html>
+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 [contactHovered, setContactHovered] = useState(false);
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);
@@ -463,9 +495,9 @@ const AppLayout: React.FC = () => {
<div key={item.id} onClick={() => item.path && navigate(item.path)} style={{
display: 'flex', alignItems: 'center',
justifyContent: 'flex-start',
gap: 12,
padding: depth > 0 ? '8px 14px 8px 36px' : '10px 16px',
borderRadius: 12, margin: '2px 6px', cursor: 'pointer',
gap: 10,
padding: depth > 0 ? '6px 12px 6px 32px' : '6px 14px',
borderRadius: 12, margin: '1px 4px', cursor: 'pointer',
fontSize: depth > 0 ? 13 : 14, fontWeight: isActive ? 600 : 400,
color: isActive ? '#4f46e5' : '#475569',
background: isActive ? 'linear-gradient(135deg, rgba(99, 102, 241, 0.1) 0%, rgba(139, 92, 246, 0.08) 100%)' : 'transparent',
@@ -495,11 +527,11 @@ const AppLayout: React.FC = () => {
items.push(
<div key={item.id}>
<div style={{
color: '#94a3b8', fontSize: 12, fontWeight: 600,
padding: '12px 16px 6px', letterSpacing: 0.5, textTransform: 'uppercase',
}}>
{item.label}
</div>
color: '#94a3b8', fontSize: 12, fontWeight: 600,
padding: '10px 14px 4px', letterSpacing: 0.5, textTransform: 'uppercase',
}}>
{item.label}
</div>
{children.map(c => renderMenuItem(c, 1))}
</div>
);
@@ -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: '#ff0000ff', 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,142 @@ const AppLayout: React.FC = () => {
</Modal>
<NotificationPopup />
{/* Contact Button */}
<div style={{
position: 'fixed',
right: 24,
bottom: 24,
zIndex: 1000,
}}>
<div style={{
position: 'relative',
}}>
<div
style={{
position: 'absolute',
right: 64,
bottom: 8,
padding: '8px 16px',
background: '#1e293b',
color: '#ffffff',
borderRadius: 8,
fontSize: 13,
whiteSpace: 'nowrap',
opacity: contactHovered ? 1 : 0,
transition: 'opacity 0.2s ease',
pointerEvents: 'none',
}}
>
</div>
<button
onClick={() => setContactModalOpen(true)}
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)';
setContactHovered(true);
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'scale(1)';
e.currentTarget.style.boxShadow = '0 4px 20px rgba(99, 102, 241, 0.4)';
setContactHovered(false);
}}
>
<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>
);
};