Files
qihuaiflow/frontend/src/pages/front/PricingPage.tsx
T

663 lines
29 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 React, { useState, useEffect, useCallback } from 'react';
import { paymentAPI } from '@/api';
import { useAuth } from '@/contexts/AuthContext';
import './PricingPage.css';
// ---- SVG Icons ----
const IconCheck = () => (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<polyline points="20 6 9 17 4 12" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
const IconService = () => (
<svg width="18" height="18" viewBox="0 0 24 24" fill="none">
<rect x="3" y="3" width="18" height="18" rx="3" stroke="currentColor" strokeWidth="1.8" />
<line x1="9" y1="9" x2="15" y2="9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
<line x1="9" y1="13" x2="13" y2="13" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
<line x1="9" y1="17" x2="11" y2="17" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
</svg>
);
const IconTag = () => (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<path d="M12 2L2 7l10 5 10-5-10-5z" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round" />
<path d="M2 17l10 5 10-5" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round" />
<path d="M2 12l10 5 10-5" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round" />
</svg>
);
const IconCoupon = () => (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<rect x="2" y="5" width="20" height="14" rx="2" stroke="currentColor" strokeWidth="1.6" />
<line x1="12" y1="5" x2="12" y2="19" stroke="currentColor" strokeWidth="1.6" strokeDasharray="2 2" />
<circle cx="6" cy="12" r="3" stroke="currentColor" strokeWidth="1.2" />
<text x="18" y="13.5" textAnchor="middle" fontSize="5" fontWeight="800" fill="currentColor">OFF</text>
</svg>
);
const IconClock = () => (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="1.6" />
<polyline points="12 6 12 12 16 14" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
const IconSparkles = () => (
<svg width="18" height="18" viewBox="0 0 24 24" fill="none">
<path d="M12 2l1.5 5h5l-4 3 1.5 5-4-3-4 3 1.5-5-4-3h5L12 2z" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" />
</svg>
);
const IconAppCustom = () => (
<svg width="32" height="32" viewBox="0 0 24 24" fill="none">
<rect x="3" y="3" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1.6" />
<rect x="14" y="3" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1.6" />
<rect x="3" y="14" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1.6" />
<rect x="14" y="14" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1.6" />
</svg>
);
const IconPlatform = () => (
<svg width="32" height="32" viewBox="0 0 24 24" fill="none">
<rect x="2" y="9" width="20" height="13" rx="2" stroke="currentColor" strokeWidth="1.6" />
<path d="M6 9V7a6 6 0 0112 0v2" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" />
<circle cx="12" cy="16" r="2" stroke="currentColor" strokeWidth="1.4" />
</svg>
);
const IconDataCustom = () => (
<svg width="32" height="32" viewBox="0 0 24 24" fill="none">
<ellipse cx="12" cy="5" rx="9" ry="3" stroke="currentColor" strokeWidth="1.6" />
<path d="M3 5v14c0 1.66 4.03 3 9 3s9-1.34 9-3V5" stroke="currentColor" strokeWidth="1.6" />
<path d="M3 12c0 1.66 4.03 3 9 3s9-1.34 9-3" stroke="currentColor" strokeWidth="1.2" strokeDasharray="2 2" />
</svg>
);
const IconPrivateProject = () => (
<svg width="32" height="32" viewBox="0 0 24 24" fill="none">
<path d="M3 21h18" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" />
<path d="M5 21V7l7-4 7 4v14" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" />
<path d="M9 14v7" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
<path d="M15 11v10" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
</svg>
);
const IconWechat = () => (
<svg width="28" height="28" viewBox="0 0 24 24" fill="none">
<path d="M8 12a1.5 1.5 0 100-3 1.5 1.5 0 000 3z" fill="currentColor" />
<path d="M15 10.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z" fill="currentColor" />
<path d="M21 13.5c0 3.6-3.7 6.5-8.3 6.5-.8 0-1.5-.1-2.2-.2L7 21l1.3-3.2C6.5 16.5 5 15.1 5 13.5 5 9.9 8.7 7 13.3 7S21 9.9 21 13.5z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
</svg>
);
const IconAlipay = () => (
<svg width="28" height="28" viewBox="0 0 24 24" fill="none">
<rect x="2" y="4" width="20" height="16" rx="3" stroke="currentColor" strokeWidth="1.8" />
<path d="M8 14.5c1.5-2.5 3.5-4 5.5-4.5" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" />
<path d="M7 12l4-3.5" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" />
<circle cx="14" cy="14" r="1" fill="currentColor" />
<text x="12" y="17" textAnchor="middle" fontSize="5" fontWeight="700" fill="currentColor">Alipay</text>
</svg>
);
function formatYuan(n: number): string {
return n.toLocaleString('en-US');
}
interface ServiceItem {
id: string;
name: string;
description: string;
type: string;
price: number;
features?: string[];
assigned_agent_id?: string;
}
interface CouponItem {
id: string;
code: string;
type: string;
value: number;
min_amount: number;
expire_at: string;
status: string;
}
const SERVICE_TYPE_CONFIG: Record<string, { label: string; icon: React.ReactNode; color: string; bg: string }> = {
app_custom: { label: '应用定制', icon: <IconAppCustom />, color: '#4F46E5', bg: '#EEF2FF' },
platform_proxy: { label: '平台代理', icon: <IconPlatform />, color: '#219B5E', bg: '#ECFDF5' },
data_custom: { label: '数据定制', icon: <IconDataCustom />, color: '#D97706', bg: '#FFFBEB' },
private_project: { label: '私有化项目', icon: <IconPrivateProject />, color: '#7C3AED', bg: '#F5F3FF' },
};
const PricingPage: React.FC = () => {
const { user } = useAuth();
const [billingCycle, setBillingCycle] = useState<'month' | 'quarter' | 'year'>('month');
const [plans, setPlans] = useState<any[]>([]);
const [services, setServices] = useState<ServiceItem[]>([]);
const [coupons, setCoupons] = useState<CouponItem[]>([]);
const [loading, setLoading] = useState(true);
const [subscribeMsg, setSubscribeMsg] = useState('');
// Modal states
const [showModal, setShowModal] = useState<string | null>(null); // service type or null
const [appCustomType, setAppCustomType] = useState<string>('skill'); // skill/agent/mcp
const [platformForm, setPlatformForm] = useState({ companyName: '', contactName: '', usage: '' });
const [dataRequestForm, setDataRequestForm] = useState({ title: '', description: '', requirements: '' });
const [privateProjectForm, setPrivateProjectForm] = useState({ title: '', description: '', requirements: '' });
const [submitLoading, setSubmitLoading] = useState(false);
const currentPlanLevel = user?.membership_level ?? 0;
const loadData = useCallback(async () => {
try {
setLoading(true);
const [plansRes, servicesRes, couponsRes] = await Promise.all([
paymentAPI.getPlans(),
paymentAPI.getServices(),
paymentAPI.getCoupons(),
]);
if (plansRes.code === 0) setPlans(plansRes.data);
if (servicesRes.code === 0) setServices(servicesRes.data);
if (couponsRes.code === 0) setCoupons(couponsRes.data);
} catch (e) {
console.error('加载付费数据失败', e);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
loadData();
}, [loadData]);
const getPrice = (plan: any, cycle: typeof billingCycle) => {
const base = Number(plan.price) || 0;
switch (cycle) {
case 'quarter': return Math.round(base * 2.7);
case 'year': return Math.round(base * 10);
default: return base;
}
};
const getCycleLabel = (cycle: typeof billingCycle) => {
switch (cycle) { case 'month': return '/月'; case 'quarter': return '/季'; case 'year': return '/年'; }
};
const handleSubscribe = async (plan: any) => {
try {
setSubscribeMsg('');
const res = await paymentAPI.createOrder({
item_id: plan.id,
order_type: 'membership',
});
if (res.code === 0) {
const payRes = await paymentAPI.payOrder(res.data.id);
if (payRes.code === 0) {
setSubscribeMsg('成功订阅 ' + plan.name + '!');
await loadData();
}
}
} catch (e: any) {
setSubscribeMsg('订阅失败:' + (e.message || '未知错误'));
}
};
// 应用定制服务 - 提交
const handleAppCustomSubmit = async (serviceId: string) => {
try {
setSubmitLoading(true);
const res = await paymentAPI.createCustomOrder({
service_id: serviceId,
description: '应用定制服务 - ' + (appCustomType === 'skill' ? 'Skills' : appCustomType === 'agent' ? 'Agent' : 'MCP') + ' 类型',
budget: 0,
contact_info: '',
});
if (res.code === 0) {
setSubscribeMsg('应用定制服务已提交!即将跳转到定制对话窗口...');
setShowModal(null);
// 跳转到聊天页面,带定制服务ID
setTimeout(() => {
window.location.href = '/chat?service_id=' + res.data.id;
}, 1500);
}
} catch (e: any) {
setSubscribeMsg('提交失败:' + (e.message || '未知错误'));
} finally {
setSubmitLoading(false);
}
};
// 平台代理申请 - 提交
const handlePlatformProxySubmit = async (serviceId: string) => {
if (!platformForm.companyName.trim()) {
setSubscribeMsg('请填写公司名称');
return;
}
try {
setSubmitLoading(true);
const res = await paymentAPI.createServiceRequest({
service_id: serviceId,
title: '平台代理申请 - ' + platformForm.companyName,
description: '公司名称: ' + platformForm.companyName + '\n联系人: ' + platformForm.contactName + '\n用途说明: ' + platformForm.usage,
requirements: {
company_name: platformForm.companyName,
contact_name: platformForm.contactName,
usage: platformForm.usage,
},
});
if (res.code === 0) {
setSubscribeMsg('平台代理申请已成功提交!工单号: ' + res.data.id);
setShowModal(null);
setPlatformForm({ companyName: '', contactName: '', usage: '' });
await loadData();
}
} catch (e: any) {
setSubscribeMsg('提交失败:' + (e.message || '未知错误'));
} finally {
setSubmitLoading(false);
}
};
// 数据定制服务 - 提交
const handleDataCustomSubmit = async (serviceId: string) => {
if (!dataRequestForm.title.trim()) {
setSubscribeMsg('请填写需求标题');
return;
}
try {
setSubmitLoading(true);
const res = await paymentAPI.createServiceRequest({
service_id: serviceId,
title: dataRequestForm.title,
description: dataRequestForm.description,
requirements: { detail: dataRequestForm.requirements },
});
if (res.code === 0) {
setSubscribeMsg('数据定制需求已成功提交!工单号: ' + res.data.id);
setShowModal(null);
setDataRequestForm({ title: '', description: '', requirements: '' });
await loadData();
}
} catch (e: any) {
setSubscribeMsg('提交失败:' + (e.message || '未知错误'));
} finally {
setSubmitLoading(false);
}
};
// 私有化项目服务 - 提交
const handlePrivateProjectSubmit = async (serviceId: string) => {
if (!privateProjectForm.title.trim()) {
setSubscribeMsg('请填写咨询标题');
return;
}
try {
setSubmitLoading(true);
const res = await paymentAPI.createServiceRequest({
service_id: serviceId,
title: privateProjectForm.title,
description: privateProjectForm.description,
requirements: { detail: privateProjectForm.requirements },
});
if (res.code === 0) {
setSubscribeMsg('私有化项目咨询已成功提交!工单号: ' + res.data.id);
setShowModal(null);
setPrivateProjectForm({ title: '', description: '', requirements: '' });
await loadData();
}
} catch (e: any) {
setSubscribeMsg('提交失败:' + (e.message || '未知错误'));
} finally {
setSubmitLoading(false);
}
};
const getServiceAction = (svc: ServiceItem) => {
switch (svc.type) {
case 'app_custom':
return { label: '前往定制', action: () => setShowModal('app_custom_' + svc.id) };
case 'platform_proxy':
return { label: '立即申请', action: () => setShowModal('platform_proxy_' + svc.id) };
case 'data_custom':
return { label: '提交需求', action: () => setShowModal('data_custom_' + svc.id) };
case 'private_project':
return { label: '咨询方案', action: () => setShowModal('private_project_' + svc.id) };
default:
return { label: '了解更多', action: () => {} };
}
};
if (loading) return <div className="pricing-page"><p style={{textAlign:'center',marginTop:60}}>加载中...</p></div>;
return (
<div className="pricing-page">
<div className="pricing-header">
<h1 className="pricing-title">会员订阅</h1>
<p className="pricing-subtitle">选择最适合你的方案,解锁 AI 无限可能</p>
</div>
<div className="pricing-cycle-tabs">
{(['month', 'quarter', 'year'] as const).map((cycle) => (
<button
key={cycle}
className={`pricing-cycle-tab ${billingCycle === cycle ? 'active' : ''}`}
onClick={() => setBillingCycle(cycle)}
>
{cycle === 'month' ? '月付' : cycle === 'quarter' ? '季付' : '年付'}
{cycle === 'year' && <span className="pricing-savings-badge">84折</span>}
</button>
))}
</div>
{subscribeMsg && <div className="pricing-subscribe-msg">{subscribeMsg}</div>}
<div className="pricing-plans-grid">
{plans.map((plan: any, idx: number) => {
const isCurrent = currentPlanLevel === plan.level;
const price = getPrice(plan, billingCycle);
const isPopular = idx === 1;
return (
<div
key={plan.id}
className={`pricing-plan-card ${isCurrent ? 'pricing-plan--current' : ''} ${isPopular ? 'pricing-plan--popular' : ''}`}
>
{isPopular && (
<div className="pricing-popular-ribbon"><IconSparkles /><span>最受欢迎</span></div>
)}
{isCurrent && <div className="pricing-current-badge">当前方案</div>}
<div className="pricing-plan-head">
<span className="pricing-plan-name">{plan.name}</span>
<div className="pricing-plan-price-row">
<span className="pricing-plan-price">
<span className="pricing-currency">$</span>{price === 0 ? '0' : formatYuan(price)}
</span>
<span className="pricing-plan-cycle">{getCycleLabel(billingCycle)}</span>
</div>
<p className="pricing-plan-tokens">日赠 {formatYuan(plan.daily_free_tokens)} Tokens</p>
</div>
<div className="pricing-plan-features">
{(plan.features || []).map((feat: string, i: number) => (
<div key={i} className="pricing-feature-item">
<span className="pricing-feature-check"><IconCheck /></span>
<span>{feat}</span>
</div>
))}
</div>
<button
className={`pricing-subscribe-btn ${isCurrent ? 'pricing-btn--current' : ''}`}
onClick={() => !isCurrent && handleSubscribe(plan)}
>
{isCurrent ? '当前方案' : '立即订阅'}
</button>
</div>
);
})}
</div>
{/* 增值服务 - 四大类 */}
<div className="pricing-section">
<div className="pricing-section-header">
<IconService />
<h2 className="pricing-section-title">增值服务</h2>
</div>
<div className="pricing-services-grid-v2">
{services.map((svc) => {
const config = SERVICE_TYPE_CONFIG[svc.type] || SERVICE_TYPE_CONFIG.app_custom;
const actionInfo = getServiceAction(svc);
return (
<div key={svc.id} className="pricing-service-card-v2">
<div className="pricing-service-card-v2-icon" style={{ background: config.bg, color: config.color }}>
{config.icon}
</div>
<div className="pricing-service-card-v2-body">
<span className="pricing-service-card-v2-tag" style={{ color: config.color, background: config.bg }}>
{config.label}
</span>
<h4 className="pricing-service-card-v2-name">{svc.name}</h4>
<p className="pricing-service-card-v2-desc">{svc.description}</p>
{(svc.features && svc.features.length > 0) && (
<div className="pricing-service-card-v2-features">
{svc.features.map((feat: string, i: number) => (
<span key={i} className="pricing-service-card-v2-feature-tag">{feat}</span>
))}
</div>
)}
</div>
<div className="pricing-service-card-v2-action">
{svc.price > 0 ? (
<span className="pricing-service-card-v2-price">{formatYuan(svc.price)} 元起</span>
) : (
<span className="pricing-service-card-v2-price">免费咨询</span>
)}
<button
className="pricing-service-card-v2-btn"
onClick={actionInfo.action}
>
{actionInfo.label}
</button>
</div>
</div>
);
})}
{services.length === 0 && <p className="pricing-empty">暂无增值服务</p>}
</div>
</div>
{/* 优惠券 */}
<div className="pricing-section">
<div className="pricing-section-header">
<IconCoupon />
<h2 className="pricing-section-title">优惠券</h2>
</div>
<div className="pricing-coupons-row">
{coupons.map((cp) => {
const isPercent = cp.type === 'percent';
return (
<div key={cp.id} className="pricing-coupon-card">
<div className="pricing-coupon-left">
<span className="pricing-coupon-value">{isPercent ? `${cp.value}%` : `$${cp.value}`}</span>
<span className="pricing-coupon-type">{isPercent ? '折扣券' : '立减券'}</span>
</div>
<div className="pricing-coupon-right">
<div className="pricing-coupon-code">{cp.code}</div>
<div className="pricing-coupon-desc">满 {formatYuan(cp.min_amount || 0)} 元可用</div>
<div className="pricing-coupon-valid"><IconClock /><span>有效期至 {cp.expire_at || '--'}</span></div>
<span className="pricing-coupon-status">{cp.status === 'active' ? '可使用' : cp.status}</span>
</div>
</div>
);
})}
{coupons.length === 0 && <p className="pricing-empty">暂无可用优惠券</p>}
</div>
</div>
{/* 支付方式 */}
<div className="pricing-section">
<div className="pricing-section-header">
<IconTag />
<h2 className="pricing-section-title">支付方式</h2>
</div>
<div className="pricing-payment-row">
<div className="pricing-payment-method"><IconWechat /><span>微信支付</span></div>
<div className="pricing-payment-method"><IconAlipay /><span>支付宝</span></div>
</div>
</div>
{/* ===== Modal - 应用定制服务 ===== */}
{showModal && showModal.startsWith('app_custom_') && (
<div className="pricing-modal-overlay" onClick={() => setShowModal(null)}>
<div className="pricing-modal" onClick={e => e.stopPropagation()}>
<h3 className="pricing-modal-title">应用定制服务</h3>
<p className="pricing-modal-subtitle">请选择您需要的定制类型</p>
<div className="pricing-modal-app-type-grid">
{[
{ type: 'skill', label: 'Skills', desc: '自定义技能开发,扩展AI能力边界' },
{ type: 'agent', label: 'Agent', desc: '智能体定制,构建专属AI助手' },
{ type: 'mcp', label: 'MCP', desc: 'MCP协议开发,连接外部数据源' },
].map((item) => (
<div
key={item.type}
className={`pricing-modal-app-type-card ${appCustomType === item.type ? 'active' : ''}`}
onClick={() => setAppCustomType(item.type)}
>
<div className="pricing-modal-app-type-label">{item.label}</div>
<div className="pricing-modal-app-type-desc">{item.desc}</div>
</div>
))}
</div>
<div className="pricing-modal-actions">
<button
className="pricing-modal-btn-cancel"
onClick={() => setShowModal(null)}
>取消</button>
<button
className="pricing-modal-btn-confirm"
disabled={submitLoading}
onClick={() => handleAppCustomSubmit(showModal.replace('app_custom_', ''))}
>
{submitLoading ? '提交中...' : '确认并前往定制'}
</button>
</div>
</div>
</div>
)}
{/* ===== Modal - 平台代理申请 ===== */}
{showModal && showModal.startsWith('platform_proxy_') && (
<div className="pricing-modal-overlay" onClick={() => setShowModal(null)}>
<div className="pricing-modal" onClick={e => e.stopPropagation()}>
<h3 className="pricing-modal-title">平台代理申请</h3>
<p className="pricing-modal-subtitle">填写以下信息,提交代理申请</p>
<div className="pricing-modal-form">
<div className="pricing-modal-form-group">
<label>公司名称 <span className="required">*</span></label>
<input
type="text"
placeholder="请输入公司名称"
value={platformForm.companyName}
onChange={e => setPlatformForm({ ...platformForm, companyName: e.target.value })}
/>
</div>
<div className="pricing-modal-form-group">
<label>联系人</label>
<input
type="text"
placeholder="请输入联系人姓名"
value={platformForm.contactName}
onChange={e => setPlatformForm({ ...platformForm, contactName: e.target.value })}
/>
</div>
<div className="pricing-modal-form-group">
<label>用途说明</label>
<textarea
placeholder="请简要说明代理用途"
rows={3}
value={platformForm.usage}
onChange={e => setPlatformForm({ ...platformForm, usage: e.target.value })}
/>
</div>
</div>
<div className="pricing-modal-actions">
<button className="pricing-modal-btn-cancel" onClick={() => setShowModal(null)}>取消</button>
<button
className="pricing-modal-btn-confirm"
disabled={submitLoading}
onClick={() => handlePlatformProxySubmit(showModal.replace('platform_proxy_', ''))}
>{submitLoading ? '提交中...' : '提交申请'}</button>
</div>
</div>
</div>
)}
{/* ===== Modal - 数据定制服务 ===== */}
{showModal && showModal.startsWith('data_custom_') && (
<div className="pricing-modal-overlay" onClick={() => setShowModal(null)}>
<div className="pricing-modal" onClick={e => e.stopPropagation()}>
<h3 className="pricing-modal-title">数据定制服务</h3>
<p className="pricing-modal-subtitle">请描述您的数据定制需求</p>
<div className="pricing-modal-form">
<div className="pricing-modal-form-group">
<label>需求标题 <span className="required">*</span></label>
<input
type="text"
placeholder="请输入需求标题"
value={dataRequestForm.title}
onChange={e => setDataRequestForm({ ...dataRequestForm, title: e.target.value })}
/>
</div>
<div className="pricing-modal-form-group">
<label>需求描述</label>
<textarea
placeholder="请描述您的数据定制需求详情"
rows={3}
value={dataRequestForm.description}
onChange={e => setDataRequestForm({ ...dataRequestForm, description: e.target.value })}
/>
</div>
<div className="pricing-modal-form-group">
<label>具体要求</label>
<textarea
placeholder="请说明数据格式、数量、交付时间等具体要求"
rows={2}
value={dataRequestForm.requirements}
onChange={e => setDataRequestForm({ ...dataRequestForm, requirements: e.target.value })}
/>
</div>
</div>
<div className="pricing-modal-actions">
<button className="pricing-modal-btn-cancel" onClick={() => setShowModal(null)}>取消</button>
<button
className="pricing-modal-btn-confirm"
disabled={submitLoading}
onClick={() => handleDataCustomSubmit(showModal.replace('data_custom_', ''))}
>{submitLoading ? '提交中...' : '提交需求'}</button>
</div>
</div>
</div>
)}
{/* ===== Modal - 私有化项目服务 ===== */}
{showModal && showModal.startsWith('private_project_') && (
<div className="pricing-modal-overlay" onClick={() => setShowModal(null)}>
<div className="pricing-modal" onClick={e => e.stopPropagation()}>
<h3 className="pricing-modal-title">私有化项目服务</h3>
<p className="pricing-modal-subtitle">请描述您的私有化部署需求,我们将为您提供专业方案</p>
<div className="pricing-modal-form">
<div className="pricing-modal-form-group">
<label>项目标题 <span className="required">*</span></label>
<input
type="text"
placeholder="请输入项目标题"
value={privateProjectForm.title}
onChange={e => setPrivateProjectForm({ ...privateProjectForm, title: e.target.value })}
/>
</div>
<div className="pricing-modal-form-group">
<label>项目描述</label>
<textarea
placeholder="请描述您的私有化部署需求"
rows={3}
value={privateProjectForm.description}
onChange={e => setPrivateProjectForm({ ...privateProjectForm, description: e.target.value })}
/>
</div>
<div className="pricing-modal-form-group">
<label>具体要求</label>
<textarea
placeholder="请说明部署规模、安全要求、时间计划等"
rows={2}
value={privateProjectForm.requirements}
onChange={e => setPrivateProjectForm({ ...privateProjectForm, requirements: e.target.value })}
/>
</div>
</div>
<div className="pricing-modal-actions">
<button className="pricing-modal-btn-cancel" onClick={() => setShowModal(null)}>取消</button>
<button
className="pricing-modal-btn-confirm"
disabled={submitLoading}
onClick={() => handlePrivateProjectSubmit(showModal.replace('private_project_', ''))}
>{submitLoading ? '提交中...' : '提交咨询'}</button>
</div>
</div>
</div>
)}
</div>
);
};
export default PricingPage;