manual save(2026-01-29 14:41)
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
%PDF-1.4
|
||||
% placeholder file
|
||||
20
public/images/placeholder-qr.svg
Normal file
20
public/images/placeholder-qr.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="32" y="32" width="448" height="448" rx="48" fill="#F8FAFC"/>
|
||||
<rect x="72" y="72" width="368" height="368" rx="24" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="8"/>
|
||||
|
||||
<rect x="112" y="112" width="96" height="96" rx="16" fill="#1E3A8A"/>
|
||||
<rect x="304" y="112" width="96" height="96" rx="16" fill="#1E3A8A"/>
|
||||
<rect x="112" y="304" width="96" height="96" rx="16" fill="#1E3A8A"/>
|
||||
|
||||
<rect x="232" y="232" width="48" height="48" rx="10" fill="#D4AF37"/>
|
||||
<rect x="304" y="256" width="24" height="24" rx="6" fill="#1E3A8A"/>
|
||||
<rect x="352" y="304" width="56" height="56" rx="14" fill="#1E3A8A"/>
|
||||
<rect x="280" y="336" width="40" height="40" rx="10" fill="#D4AF37"/>
|
||||
|
||||
<path d="M160 142H128V174H160V142Z" fill="#F8FAFC"/>
|
||||
<path d="M352 142H320V174H352V142Z" fill="#F8FAFC"/>
|
||||
<path d="M160 334H128V366H160V334Z" fill="#F8FAFC"/>
|
||||
|
||||
<path d="M120 440H392" stroke="#CBD5E1" stroke-width="6" stroke-linecap="round"/>
|
||||
<text x="256" y="470" text-anchor="middle" font-family="system-ui, -apple-system, Segoe UI, Roboto, Arial" font-size="18" fill="#64748B">QR PLACEHOLDER</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -3,6 +3,8 @@ import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { Home } from './pages/Home'
|
||||
import { About } from './pages/About'
|
||||
import { Services } from './pages/Services'
|
||||
import { HighTechEnterprise } from './pages/HighTechEnterprise'
|
||||
import { ServiceDetail } from './pages/ServiceDetail'
|
||||
import { News } from './pages/News'
|
||||
import { Contact } from './pages/Contact'
|
||||
import { Cases } from './pages/Cases'
|
||||
@@ -71,6 +73,8 @@ function App() {
|
||||
<Route path="/" element={<PageWrapper><Home /></PageWrapper>} />
|
||||
<Route path="/about" element={<PageWrapper><About /></PageWrapper>} />
|
||||
<Route path="/services" element={<PageWrapper><Services /></PageWrapper>} />
|
||||
<Route path="/services/:serviceId" element={<PageWrapper><ServiceDetail /></PageWrapper>} />
|
||||
<Route path="/services/tech-service/high-tech-enterprise" element={<PageWrapper><HighTechEnterprise /></PageWrapper>} />
|
||||
<Route path="/cases" element={<PageWrapper><Cases /></PageWrapper>} />
|
||||
<Route path="/news" element={<PageWrapper><News /></PageWrapper>} />
|
||||
<Route path="/learning" element={<PageWrapper><Learning /></PageWrapper>} />
|
||||
@@ -82,6 +86,8 @@ function App() {
|
||||
<Route path="/en" element={<PageWrapper><Home /></PageWrapper>} />
|
||||
<Route path="/en/about" element={<PageWrapper><About /></PageWrapper>} />
|
||||
<Route path="/en/services" element={<PageWrapper><Services /></PageWrapper>} />
|
||||
<Route path="/en/services/:serviceId" element={<PageWrapper><ServiceDetail /></PageWrapper>} />
|
||||
<Route path="/en/services/tech-service/high-tech-enterprise" element={<PageWrapper><HighTechEnterprise /></PageWrapper>} />
|
||||
<Route path="/en/cases" element={<PageWrapper><Cases /></PageWrapper>} />
|
||||
<Route path="/en/news" element={<PageWrapper><News /></PageWrapper>} />
|
||||
<Route path="/en/learning" element={<PageWrapper><Learning /></PageWrapper>} />
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { TrendingUp, Cpu, Building2, Briefcase, ArrowRight } from 'lucide-react';
|
||||
import { TrendingUp, Cpu, Building2, Briefcase, ArrowRight, Wrench } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { SERVICES } from '../../lib/constants';
|
||||
import { getLocaleFromPathname, withLocalePath } from '../../lib/i18n'
|
||||
|
||||
/**
|
||||
* ServicesSection 组件 - 核心业务展示区域
|
||||
@@ -12,8 +13,10 @@ const ServiceCard: React.FC<{
|
||||
service: typeof SERVICES[0];
|
||||
index: number;
|
||||
}> = ({ service, index }) => {
|
||||
const locale = getLocaleFromPathname(window.location.pathname)
|
||||
const iconMap: Record<string, React.ComponentType<{ size?: number; className?: string }>> = {
|
||||
TrendingUp,
|
||||
Wrench,
|
||||
Cpu,
|
||||
Building2,
|
||||
Briefcase,
|
||||
@@ -58,7 +61,7 @@ const ServiceCard: React.FC<{
|
||||
|
||||
{/* 了解更多链接 */}
|
||||
<Link
|
||||
to={`/services/${service.id}`}
|
||||
to={withLocalePath(locale, `/services/${service.id}`)}
|
||||
className="relative z-10 inline-flex items-center gap-2 text-primary font-medium group-hover:text-primary-light transition-colors"
|
||||
>
|
||||
了解更多
|
||||
|
||||
152
src/components/PaymentQrModal.tsx
Normal file
152
src/components/PaymentQrModal.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
import React, { useEffect, useId, useMemo, useState } from 'react'
|
||||
import { X, QrCode, ShieldCheck } from 'lucide-react'
|
||||
import { grantDownloadAccess } from '../lib/downloadGate'
|
||||
|
||||
type PaymentMethod = 'wechat' | 'alipay'
|
||||
|
||||
export type PaymentQrModalProps = {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
downloadKey: string
|
||||
title: string
|
||||
amountCny?: number
|
||||
qrImageSrc?: string
|
||||
onPaid: () => void
|
||||
}
|
||||
|
||||
function getDefaultQrImageSrc(): string {
|
||||
// Placeholder SVG kept in public/ so it can be swapped easily later.
|
||||
return '/images/placeholder-qr.svg'
|
||||
}
|
||||
|
||||
export const PaymentQrModal: React.FC<PaymentQrModalProps> = ({
|
||||
open,
|
||||
onClose,
|
||||
downloadKey,
|
||||
title,
|
||||
amountCny = 1,
|
||||
qrImageSrc,
|
||||
onPaid,
|
||||
}) => {
|
||||
const dialogTitleId = useId()
|
||||
const [method, setMethod] = useState<PaymentMethod>('wechat')
|
||||
|
||||
const qrSrc = useMemo(() => qrImageSrc ?? getDefaultQrImageSrc(), [qrImageSrc])
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') onClose()
|
||||
}
|
||||
window.addEventListener('keydown', onKeyDown)
|
||||
return () => window.removeEventListener('keydown', onKeyDown)
|
||||
}, [onClose, open])
|
||||
|
||||
if (!open) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[100]">
|
||||
<div className="absolute inset-0 bg-black/50" aria-hidden="true" onClick={onClose} />
|
||||
|
||||
<div className="absolute inset-0 flex items-center justify-center p-4">
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby={dialogTitleId}
|
||||
className="w-full max-w-lg rounded-2xl bg-white shadow-xl border border-gray-100 overflow-hidden"
|
||||
>
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-100">
|
||||
<div>
|
||||
<div id={dialogTitleId} className="text-base font-semibold text-primary-dark">
|
||||
付费下载
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 mt-1">支付完成后可下载:{title}</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="w-9 h-9 inline-flex items-center justify-center rounded-lg text-gray-600 hover:bg-gray-50"
|
||||
aria-label="关闭"
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="px-6 py-5">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
<div className="inline-flex items-center gap-2 text-sm text-gray-700">
|
||||
<QrCode size={18} className="text-primary" />
|
||||
<span>金额:<span className="font-semibold text-primary-dark">{amountCny} 元</span></span>
|
||||
</div>
|
||||
<div className="inline-flex rounded-xl bg-gray-50 p-1 border border-gray-100">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMethod('wechat')}
|
||||
className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
method === 'wechat' ? 'bg-white text-primary shadow-sm' : 'text-gray-600 hover:text-primary'
|
||||
}`}
|
||||
>
|
||||
微信
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMethod('alipay')}
|
||||
className={`px-3 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
method === 'alipay' ? 'bg-white text-primary shadow-sm' : 'text-gray-600 hover:text-primary'
|
||||
}`}
|
||||
>
|
||||
支付宝
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 grid sm:grid-cols-[1fr_180px] gap-4 items-start">
|
||||
<div className="rounded-2xl bg-background border border-gray-100 p-4">
|
||||
<div className="text-sm font-semibold text-primary-dark">扫码支付说明</div>
|
||||
<ol className="mt-2 text-sm text-gray-600 space-y-2 list-decimal pl-5">
|
||||
<li>选择微信或支付宝</li>
|
||||
<li>使用对应 App 扫描右侧二维码</li>
|
||||
<li>支付完成后点击“我已付款,继续下载”</li>
|
||||
</ol>
|
||||
|
||||
<div className="mt-4 flex items-start gap-2 text-xs text-gray-500">
|
||||
<ShieldCheck size={16} className="mt-0.5 text-accent-dark" />
|
||||
<p>当前为占位二维码。后续替换为正式收款码后即可真实收款。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl border border-gray-100 bg-white p-4">
|
||||
<div className="aspect-square rounded-xl overflow-hidden bg-gray-50 border border-gray-100">
|
||||
<img src={qrSrc} alt={`${method === 'wechat' ? '微信' : '支付宝'}收款码`} className="w-full h-full object-contain" />
|
||||
</div>
|
||||
<div className="mt-3 text-center text-xs text-gray-500">{method === 'wechat' ? '微信扫码' : '支付宝扫码'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-6 py-4 border-t border-gray-100 flex items-center justify-between gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 rounded-xl text-sm font-medium text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
grantDownloadAccess(downloadKey, method)
|
||||
onPaid()
|
||||
}}
|
||||
className="px-4 py-2 rounded-xl text-sm font-medium text-white bg-primary hover:bg-primary-light"
|
||||
>
|
||||
我已付款,继续下载
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PaymentQrModal
|
||||
@@ -34,6 +34,7 @@ export const NAVIGATION_MENU = [
|
||||
export const FOOTER_LINKS = {
|
||||
products: [
|
||||
{ label: '金融服务', path: '/services/finance' },
|
||||
{ label: '科技服务', path: '/services/tech-service' },
|
||||
{ label: '科技研发', path: '/services/tech' },
|
||||
{ label: '产业投资', path: '/services/investment' },
|
||||
{ label: '咨询服务', path: '/services/consulting' },
|
||||
@@ -85,6 +86,14 @@ export const SERVICES = [
|
||||
icon: 'TrendingUp',
|
||||
features: ['财富管理', '投资顾问', '资产配置', '风险管理'],
|
||||
},
|
||||
{
|
||||
id: 'tech-service',
|
||||
title: '科技服务',
|
||||
description:
|
||||
'围绕企业创新与资质建设,提供高新技术企业认定、研发管理体系辅导、科技项目申报等科技服务。',
|
||||
icon: 'Wrench',
|
||||
features: ['高新技术企业认定', '科技项目申报', '研发管理辅导', '资质与合规'],
|
||||
},
|
||||
{
|
||||
id: 'tech',
|
||||
title: '科技研发',
|
||||
|
||||
42
src/lib/downloadGate.ts
Normal file
42
src/lib/downloadGate.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
type DownloadGatePayload = {
|
||||
key: string
|
||||
paidAt: string
|
||||
amountCny: number
|
||||
method: 'wechat' | 'alipay'
|
||||
}
|
||||
|
||||
const STORAGE_PREFIX = 'downloadGate:'
|
||||
|
||||
export function hasDownloadAccess(key: string): boolean {
|
||||
try {
|
||||
const raw = localStorage.getItem(`${STORAGE_PREFIX}${key}`)
|
||||
if (!raw) return false
|
||||
const parsed = JSON.parse(raw) as DownloadGatePayload
|
||||
return parsed?.key === key && parsed.amountCny >= 1
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function grantDownloadAccess(key: string, method: DownloadGatePayload['method']): void {
|
||||
const payload: DownloadGatePayload = {
|
||||
key,
|
||||
paidAt: new Date().toISOString(),
|
||||
amountCny: 1,
|
||||
method,
|
||||
}
|
||||
|
||||
try {
|
||||
localStorage.setItem(`${STORAGE_PREFIX}${key}`, JSON.stringify(payload))
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
export function revokeDownloadAccess(key: string): void {
|
||||
try {
|
||||
localStorage.removeItem(`${STORAGE_PREFIX}${key}`)
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
175
src/pages/HighTechEnterprise.tsx
Normal file
175
src/pages/HighTechEnterprise.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { FileText, Download, Lock, CheckCircle2 } from 'lucide-react'
|
||||
import { Header } from '../components/Header'
|
||||
import { Footer } from '../components/Footer'
|
||||
import { usePageMeta } from '../hooks/usePageMeta'
|
||||
import { getLocaleFromPathname, withLocalePath } from '../lib/i18n'
|
||||
import { hasDownloadAccess } from '../lib/downloadGate'
|
||||
import { PaymentQrModal } from '../components/PaymentQrModal'
|
||||
|
||||
const DOWNLOAD_KEY = 'htech-application-2026'
|
||||
const FILE_NAME = '2026年高新技术企业认定申请表.pdf'
|
||||
const FILE_PATH = '/downloads/2026-high-tech-enterprise-application-form.pdf'
|
||||
|
||||
function startFileDownload(absoluteOrPublicPath: string, fileName?: string) {
|
||||
const a = document.createElement('a')
|
||||
a.href = absoluteOrPublicPath
|
||||
if (fileName) a.download = fileName
|
||||
a.rel = 'noopener'
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
a.remove()
|
||||
}
|
||||
|
||||
export const HighTechEnterprise: React.FC = () => {
|
||||
const locale = getLocaleFromPathname(window.location.pathname)
|
||||
const [payOpen, setPayOpen] = useState(false)
|
||||
const [downloadUnlocked, setDownloadUnlocked] = useState(() => hasDownloadAccess(DOWNLOAD_KEY))
|
||||
|
||||
usePageMeta({
|
||||
title: locale === 'en' ? 'High-tech enterprise certification' : '高新技术企业认定要求',
|
||||
description:
|
||||
locale === 'en'
|
||||
? 'High-tech enterprise certification requirements and download of the 2026 application form.'
|
||||
: '高新技术企业认定的核心要求说明,并提供 2026 年申请表下载入口。',
|
||||
locale,
|
||||
})
|
||||
|
||||
const alreadyPaid = downloadUnlocked
|
||||
|
||||
const handleDownloadClick = useCallback(() => {
|
||||
if (hasDownloadAccess(DOWNLOAD_KEY)) {
|
||||
setDownloadUnlocked(true)
|
||||
startFileDownload(FILE_PATH, FILE_NAME)
|
||||
return
|
||||
}
|
||||
|
||||
setPayOpen(true)
|
||||
}, [])
|
||||
|
||||
const breadcrumbHref = useMemo(() => withLocalePath(locale, '/services'), [locale])
|
||||
|
||||
return (
|
||||
<motion.div className="min-h-screen bg-background" initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
|
||||
<Header />
|
||||
|
||||
<main>
|
||||
<section className="pt-32 pb-10 bg-white border-b border-gray-100">
|
||||
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<a href={breadcrumbHref} className="text-sm text-gray-500 hover:text-primary">
|
||||
{locale === 'en' ? 'Back to Services' : '返回服务范围'}
|
||||
</a>
|
||||
<h1 className="mt-3 text-3xl md:text-4xl font-semibold text-primary-dark">
|
||||
{locale === 'en' ? 'High-tech enterprise certification requirements' : '高新技术企业认定要求'}
|
||||
</h1>
|
||||
<p className="mt-4 text-gray-600 leading-relaxed">
|
||||
{locale === 'en'
|
||||
? 'This page provides a simplified overview. Replace with official materials later.'
|
||||
: '本页为精简版概览,便于用户快速理解核心要求;后续可替换为完整政策解读与本地政策细则。'}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-12">
|
||||
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid lg:grid-cols-[1fr_360px] gap-8 items-start">
|
||||
<article className="bg-white rounded-2xl border border-gray-100 p-6 md:p-8">
|
||||
<h2 className="text-xl font-semibold text-primary-dark">{locale === 'en' ? 'Key points' : '核心要点(示例)'}</h2>
|
||||
|
||||
<div className="mt-5 space-y-4 text-sm text-gray-700 leading-relaxed">
|
||||
<div className="flex gap-3">
|
||||
<CheckCircle2 className="mt-0.5 text-accent-dark" size={18} />
|
||||
<p>
|
||||
{locale === 'en'
|
||||
? 'Intellectual property, R&D activity, and revenue structure are commonly reviewed items.'
|
||||
: '常见审核关注:知识产权布局、研发活动真实性、收入结构与高新产品(服务)比例等。'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<CheckCircle2 className="mt-0.5 text-accent-dark" size={18} />
|
||||
<p>
|
||||
{locale === 'en'
|
||||
? 'R&D expense accounting should be consistent and traceable.'
|
||||
: '研发费用归集口径需一致、可追溯,建议提前建立台账与证据链。'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<CheckCircle2 className="mt-0.5 text-accent-dark" size={18} />
|
||||
<p>
|
||||
{locale === 'en'
|
||||
? 'Policies differ by region; always follow the latest local rules.'
|
||||
: '各地细则可能存在差异,实际申报以最新的本地政策与平台要求为准。'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-8 border-gray-100" />
|
||||
|
||||
<h3 className="text-lg font-semibold text-primary-dark">{locale === 'en' ? 'Downloads' : '资料下载'}</h3>
|
||||
<p className="mt-2 text-sm text-gray-600">
|
||||
{locale === 'en'
|
||||
? 'Download requires a 1 CNY payment via WeChat/Alipay.'
|
||||
: '下载需支付 1 元(微信/支付宝均可)。'}
|
||||
</p>
|
||||
|
||||
<div className="mt-4 rounded-2xl border border-gray-100 bg-background p-4 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-white border border-gray-100 flex items-center justify-center">
|
||||
<FileText size={20} className="text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-semibold text-primary-dark">{FILE_NAME}</div>
|
||||
<div className="mt-1 text-xs text-gray-500">PDF · {locale === 'en' ? 'Form template' : '申请表模板'}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleDownloadClick}
|
||||
className="inline-flex items-center justify-center gap-2 px-5 py-3 rounded-xl text-sm font-medium text-white bg-primary hover:bg-primary-light transition-colors"
|
||||
aria-label="下载申请表"
|
||||
>
|
||||
{alreadyPaid ? <Download size={18} /> : <Lock size={18} />}
|
||||
{alreadyPaid ? (locale === 'en' ? 'Download' : '下载') : locale === 'en' ? 'Pay & download' : '付费下载'}
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<aside className="bg-white rounded-2xl border border-gray-100 p-6">
|
||||
<div className="text-sm font-semibold text-primary-dark">{locale === 'en' ? 'Service scope' : '对应服务'}</div>
|
||||
<p className="mt-2 text-sm text-gray-600 leading-relaxed">
|
||||
{locale === 'en'
|
||||
? 'Tech services: certification guidance, R&D accounting, and application material preparation.'
|
||||
: '科技服务:认定辅导、研发费用归集、申报材料梳理与答疑支持。'}
|
||||
</p>
|
||||
<div className="mt-4 rounded-xl bg-background border border-gray-100 p-4 text-xs text-gray-500 leading-relaxed">
|
||||
{locale === 'en'
|
||||
? 'Note: Payment is a front-end placeholder for now and does not verify actual payment.'
|
||||
: '提示:当前“支付”仅为前端占位演示,不具备真实收款与到账校验能力。上线真实付费需对接支付回调与服务端验签。'}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
|
||||
<PaymentQrModal
|
||||
open={payOpen}
|
||||
onClose={() => setPayOpen(false)}
|
||||
downloadKey={DOWNLOAD_KEY}
|
||||
title={FILE_NAME}
|
||||
amountCny={1}
|
||||
onPaid={() => {
|
||||
setDownloadUnlocked(true)
|
||||
setPayOpen(false)
|
||||
startFileDownload(FILE_PATH, FILE_NAME)
|
||||
}}
|
||||
/>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HighTechEnterprise
|
||||
106
src/pages/ServiceDetail.tsx
Normal file
106
src/pages/ServiceDetail.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { motion } from 'framer-motion'
|
||||
import { ArrowRight, CheckCircle2, FileText } from 'lucide-react'
|
||||
import { Header } from '../components/Header'
|
||||
import { Footer } from '../components/Footer'
|
||||
import { SERVICES } from '../lib/constants'
|
||||
import { getLocaleFromPathname, withLocalePath } from '../lib/i18n'
|
||||
import { usePageMeta } from '../hooks/usePageMeta'
|
||||
|
||||
export const ServiceDetail: React.FC = () => {
|
||||
const locale = getLocaleFromPathname(window.location.pathname)
|
||||
const { serviceId } = useParams<{ serviceId: string }>()
|
||||
|
||||
const service = useMemo(() => SERVICES.find((s) => s.id === serviceId), [serviceId])
|
||||
|
||||
usePageMeta({
|
||||
title: service ? (locale === 'en' ? 'Service' : service.title) : locale === 'en' ? 'Service' : '服务',
|
||||
description: service?.description,
|
||||
locale,
|
||||
})
|
||||
|
||||
const backHref = useMemo(() => withLocalePath(locale, '/services'), [locale])
|
||||
|
||||
if (!service) {
|
||||
return (
|
||||
<motion.div className="min-h-screen bg-background" initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
|
||||
<Header />
|
||||
<main className="pt-32 pb-16">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<a href={backHref} className="text-sm text-gray-500 hover:text-primary">
|
||||
{locale === 'en' ? 'Back to Services' : '返回服务范围'}
|
||||
</a>
|
||||
<h1 className="mt-4 text-3xl font-semibold text-primary-dark">{locale === 'en' ? 'Service not found' : '未找到该服务'}</h1>
|
||||
<p className="mt-3 text-gray-600">{locale === 'en' ? 'Please check the URL.' : '请检查访问路径是否正确。'}</p>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
const isTechService = service.id === 'tech-service'
|
||||
const highTechHref = withLocalePath(locale, '/services/tech-service/high-tech-enterprise')
|
||||
|
||||
return (
|
||||
<motion.div className="min-h-screen bg-background" initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
|
||||
<Header />
|
||||
|
||||
<main>
|
||||
<section className="pt-32 pb-10 bg-white border-b border-gray-100">
|
||||
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<a href={backHref} className="text-sm text-gray-500 hover:text-primary">
|
||||
{locale === 'en' ? 'Back to Services' : '返回服务范围'}
|
||||
</a>
|
||||
<h1 className="mt-3 text-3xl md:text-4xl font-semibold text-primary-dark">{service.title}</h1>
|
||||
<p className="mt-4 text-gray-600 leading-relaxed">{service.description}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-12">
|
||||
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="bg-white rounded-2xl border border-gray-100 p-6 md:p-8">
|
||||
<h2 className="text-xl font-semibold text-primary-dark">{locale === 'en' ? 'What we provide' : '我们提供'}</h2>
|
||||
<div className="mt-5 grid sm:grid-cols-2 gap-3">
|
||||
{service.features.map((f) => (
|
||||
<div key={f} className="flex items-start gap-3 rounded-xl bg-background border border-gray-100 p-4">
|
||||
<CheckCircle2 size={18} className="mt-0.5 text-accent-dark" />
|
||||
<div className="text-sm text-gray-700">{f}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{isTechService && (
|
||||
<div className="mt-8 rounded-2xl border border-gray-100 bg-background p-5">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-sm font-semibold text-primary-dark">
|
||||
<FileText size={18} className="text-primary" />
|
||||
高新技术企业认定
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-gray-600 leading-relaxed">
|
||||
查看认定要求,并下载 2026 年高新技术企业认定申请表(付费下载 1 元)。
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href={highTechHref}
|
||||
className="inline-flex items-center justify-center gap-2 px-4 py-2 rounded-xl text-sm font-medium text-white bg-primary hover:bg-primary-light transition-colors"
|
||||
>
|
||||
进入页面
|
||||
<ArrowRight size={18} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ServiceDetail
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Cpu,
|
||||
Building2,
|
||||
Briefcase,
|
||||
Wrench,
|
||||
CheckCircle,
|
||||
Target,
|
||||
BarChart3,
|
||||
@@ -45,6 +46,38 @@ const serviceDetails = {
|
||||
{ step: '04', title: '持续跟踪', desc: '定期调整和优化投资组合' },
|
||||
],
|
||||
},
|
||||
techService: {
|
||||
description:
|
||||
'科技服务聚焦企业创新能力建设与资质合规,提供高新技术企业认定辅导、申报材料梳理、研发费用归集与体系完善等支持,帮助企业更高效地完成申报与长期管理。',
|
||||
features: [
|
||||
{
|
||||
icon: Wrench,
|
||||
title: '认定辅导',
|
||||
desc: '对照政策要求梳理差距,形成可落地的提升与申报路径',
|
||||
},
|
||||
{
|
||||
icon: Shield,
|
||||
title: '研发体系建设',
|
||||
desc: '完善研发管理制度、知识产权与成果转化台账,提升持续合规能力',
|
||||
},
|
||||
{
|
||||
icon: BarChart3,
|
||||
title: '费用归集与核算',
|
||||
desc: '辅助研发费用归集口径与台账搭建,降低申报风险',
|
||||
},
|
||||
{
|
||||
icon: Target,
|
||||
title: '项目申报与匹配',
|
||||
desc: '结合企业业务方向,进行科技项目匹配与材料准备',
|
||||
},
|
||||
],
|
||||
process: [
|
||||
{ step: '01', title: '资质诊断', desc: '梳理现状,评估达标差距与可行路径' },
|
||||
{ step: '02', title: '材料梳理', desc: '搭建目录清单,统一口径与证据链' },
|
||||
{ step: '03', title: '申报提交', desc: '按节点推进提交与补正,提升通过率' },
|
||||
{ step: '04', title: '后续管理', desc: '建立持续合规机制与年度复盘' },
|
||||
],
|
||||
},
|
||||
tech: {
|
||||
description: '我们聚焦人工智能、大数据、云计算等前沿技术,为企业提供全方位的数字化转型解决方案。',
|
||||
features: [
|
||||
@@ -144,6 +177,7 @@ const partners = [
|
||||
const categories = [
|
||||
{ id: 'all', label: '全部' },
|
||||
{ id: 'finance', label: '金融服务' },
|
||||
{ id: 'techService', label: '科技服务' },
|
||||
{ id: 'tech', label: '科技研发' },
|
||||
{ id: 'investment', label: '产业投资' },
|
||||
{ id: 'consulting', label: '咨询服务' },
|
||||
@@ -270,6 +304,7 @@ export const Services: React.FC = () => {
|
||||
{SERVICES.map((service, index) => {
|
||||
const iconMap: Record<string, React.ComponentType<{ size?: number; className?: string }>> = {
|
||||
TrendingUp,
|
||||
Wrench,
|
||||
Cpu,
|
||||
Building2,
|
||||
Briefcase,
|
||||
@@ -433,6 +468,63 @@ export const Services: React.FC = () => {
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* 科技服务 */}
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center mb-20">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<span className="inline-block px-4 py-1 bg-accent/20 text-accent-dark text-sm font-semibold rounded-full mb-4">
|
||||
科技服务
|
||||
</span>
|
||||
<h3 className="text-2xl font-bold text-primary-dark mb-6">
|
||||
高新技术企业认定与科技项目申报
|
||||
</h3>
|
||||
<p className="text-gray-600 leading-relaxed mb-8">
|
||||
{serviceDetails.techService.description}
|
||||
</p>
|
||||
|
||||
<div className="grid sm:grid-cols-2 gap-4 mb-8">
|
||||
{serviceDetails.techService.features.map((feature, index) => (
|
||||
<div key={index} className="flex items-start gap-3 p-4 bg-background rounded-xl">
|
||||
<div className="p-2 bg-primary/10 rounded-lg">
|
||||
<feature.icon size={20} className="text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-medium text-primary-dark text-sm">{feature.title}</h4>
|
||||
<p className="text-xs text-gray-500 mt-1">{feature.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<a
|
||||
href={locale === 'en' ? '/en/services/tech-service' : '/services/tech-service'}
|
||||
className="inline-flex items-center justify-center px-6 py-3 bg-primary text-white rounded-lg hover:bg-primary-light transition-colors"
|
||||
>
|
||||
查看科技服务详情
|
||||
</a>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="relative"
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<div className="aspect-square rounded-2xl overflow-hidden shadow-xl">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1526378722430-5c37f7f116aa?w=800&h=800&fit=crop"
|
||||
alt="科技服务 - 高新技术企业认定与申报支持"
|
||||
className="w-full h-full object-cover hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user