第二次修改风格

This commit is contained in:
小潘
2026-06-16 15:41:29 +08:00
parent a33d132289
commit f1bc500678
47 changed files with 758 additions and 301 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

After

Width:  |  Height:  |  Size: 133 KiB

+69 -65
View File
@@ -3,27 +3,34 @@ import { X } from 'lucide-react'
export default function Carousel({ images, interval = 4000, themeColor }) {
const [current, setCurrent] = useState(0)
const [transitioning, setTransitioning] = useState(false)
const [prevSrc, setPrevSrc] = useState(null)
const [isTransitioning, setIsTransitioning] = useState(false)
const [preview, setPreview] = useState(null)
const timerRef = useRef(null)
const busyRef = useRef(false)
const goTo = useCallback((index) => {
setTransitioning(true)
if (index === current || busyRef.current) return
busyRef.current = true
setPrevSrc(images[current]?.url)
setCurrent(index)
// rAF x2 to ensure the DOM has rendered the new image before starting transition
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setIsTransitioning(true)
})
})
// After transition completes
setTimeout(() => {
setCurrent(index)
setTimeout(() => setTransitioning(false), 50)
}, 350)
}, [])
setIsTransitioning(false)
setPrevSrc(null)
busyRef.current = false
}, 500)
}, [current, images])
const next = useCallback(() => {
if (transitioning) return
goTo((current + 1) % images.length)
}, [current, images.length, transitioning, goTo])
const prev = useCallback(() => {
if (transitioning) return
goTo((current - 1 + images.length) % images.length)
}, [current, images.length, transitioning, goTo])
}, [current, images.length, goTo])
useEffect(() => {
if (preview !== null) return
@@ -31,13 +38,6 @@ export default function Carousel({ images, interval = 4000, themeColor }) {
return () => clearInterval(timerRef.current)
}, [next, interval, preview])
const getIndex = (offset) => {
return (current + offset + images.length) % images.length
}
const prevIdx = getIndex(-1)
const nextIdx = getIndex(1)
const handleImageClick = (index) => {
setPreview(index)
}
@@ -48,59 +48,50 @@ export default function Carousel({ images, interval = 4000, themeColor }) {
return (
<>
<div className="flex items-center justify-center">
{/* Left - prev */}
<div >
{/* Center - monitor frame (static, no animation) */}
<div
className="relative z-10 w-80 h-44 md:w-96 md:h-52 bg-white rounded-2xl border border-gray-100 overflow-hidden shadow-lg -rotate-6 cursor-pointer hover:opacity-80 transition-all duration-700 ease-[cubic-bezier(0.4,0,0.2,1)]"
style={{
opacity: transitioning ? 0.4 : 0.9,
transform: transitioning ? 'rotate(-6deg) translateX(24px) scale(0.95)' : 'rotate(-6deg)',
}}
onClick={() => { handleImageClick(prevIdx); prev() }}
>
<img src={images[prevIdx]} alt="" className="w-full h-full object-contain" />
</div>
{/* Center - current */}
<div
className="relative z-30 w-96 h-52 md:w-[500px] md:h-[280px] -mx-10 md:-mx-14 bg-white rounded-2xl border border-gray-100 overflow-hidden shadow-2xl cursor-pointer transition-all duration-700 ease-[cubic-bezier(0.4,0,0.2,1)]"
style={{
transform: transitioning ? 'scale(0.92)' : 'scale(1)',
opacity: transitioning ? 0.7 : 1,
}}
className="relative z-30 cursor-pointer"
style={{ '--monitor-theme': themeColor }}
onClick={() => handleImageClick(current)}
>
<div className="absolute top-0 left-0 right-0 h-[2px]"
style={{ background: `linear-gradient(90deg, transparent, ${themeColor}, transparent)` }} />
<img src={images[current]} alt="" className="w-full h-full object-contain" />
</div>
{/* Right - next */}
<div
className="relative z-10 w-80 h-44 md:w-96 md:h-52 bg-white rounded-2xl border border-gray-100 overflow-hidden shadow-lg rotate-6 cursor-pointer hover:opacity-80 transition-all duration-700 ease-[cubic-bezier(0.4,0,0.2,1)]"
style={{
opacity: transitioning ? 0.4 : 0.9,
transform: transitioning ? 'rotate(6deg) translateX(-24px) scale(0.95)' : 'rotate(6deg)',
}}
onClick={() => { handleImageClick(nextIdx); next() }}
>
<img src={images[nextIdx]} alt="" className="w-full h-full object-contain" />
{/* 显示器外框 */}
<div className="monitor-frame">
<div className="monitor-screen relative">
{/* 旧图片 — 交叉淡出 */}
{prevSrc && (
<img
src={prevSrc}
alt=""
className="absolute inset-0 w-full h-full object-contain transition-opacity duration-500"
style={{ opacity: isTransitioning ? 0 : 1 }}
/>
)}
{/* 新图片 — 交叉淡入 */}
<img
src={images[current]?.url}
alt=""
className="absolute inset-0 w-full h-full object-contain transition-opacity duration-500"
style={{ opacity: prevSrc ? (isTransitioning ? 1 : 0) : 1 }}
/>
</div>
{/* 底部下巴 */}
<div className="monitor-chin">
<span className="monitor-chin-label">{images[current]?.name || ''}</span>
</div>
</div>
{/* 支架 */}
<div className="monitor-stand-neck" />
<div className="monitor-stand-base" />
</div>
</div>
{/* Dot indicators */}
<div className="flex justify-center gap-2 mt-6">
<div className="flex justify-center gap-2 mt-2">
{images.map((_, i) => (
<button
key={i}
onClick={() => {
if (i === current || transitioning) return
setTransitioning(true)
setTimeout(() => {
setCurrent(i)
setTimeout(() => setTransitioning(false), 50)
}, 350)
}}
onClick={() => i !== current && goTo(i)}
className="rounded-full transition-all duration-500"
style={{
width: i === current ? '24px' : '8px',
@@ -111,6 +102,19 @@ export default function Carousel({ images, interval = 4000, themeColor }) {
))}
</div>
{/* Image name — lines on both sides */}
<div className="text-center mt-5">
<div className="flex items-center justify-center gap-5 md:gap-7">
<span className="flex-1 h-[3px] max-w-[80px] md:max-w-[140px] rounded-full" style={{ background: `linear-gradient(90deg, transparent, ${themeColor}30)` }} />
<span className="text-lg md:text-2xl font-bold tracking-[0.08em]"
style={{ color: `${themeColor}aa` }}
>
{images[current]?.name || ''}
</span>
<span className="flex-1 h-[3px] max-w-[80px] md:max-w-[140px] rounded-full" style={{ background: `linear-gradient(90deg, ${themeColor}30, transparent)` }} />
</div>
</div>
{/* Preview Modal */}
{preview !== null && (
<div
@@ -124,9 +128,9 @@ export default function Carousel({ images, interval = 4000, themeColor }) {
<X className="w-6 h-6" />
</button>
<img
src={images[preview]}
src={images[preview].url}
alt=""
className="max-w-full max-h-full object-contain rounded-2xl"
className="max-w-full max-h-[85vh] object-contain rounded-2xl"
onClick={(e) => e.stopPropagation()}
/>
</div>
+40
View File
@@ -0,0 +1,40 @@
import { useState, useEffect, useRef } from 'react';
/**
* 加载 SVG 文件并将指定颜色替换为主题色后内联渲染
* @param {string} url - SVG 文件地址
* @param {string} color - 替换的目标主题色 (hex)
* @param {string} className - 外层容器样式
*/
export default function ThemedSvg({ url, color, className }) {
const [content, setContent] = useState('');
const ref = useRef(null);
useEffect(() => {
let cancelled = false;
fetch(url)
.then(res => res.text())
.then(text => {
if (cancelled) return;
// 将 undraw 默认紫色 #6c63ff 替换为页面主题色
const themed = text.replace(/#6c63ff/gi, color);
setContent(themed);
})
.catch(() => {
if (!cancelled) setContent('');
});
return () => { cancelled = true; };
}, [url, color]);
if (!content) {
return <div className={className} />;
}
return (
<div
ref={ref}
className={className}
dangerouslySetInnerHTML={{ __html: content }}
/>
);
}
+81 -65
View File
@@ -1,5 +1,5 @@
import {
Building2, GraduationCap, BookOpen, ClipboardCheck
Building2, GraduationCap, BookOpen, ClipboardCheck,
} from 'lucide-react';
export const products = [
@@ -10,9 +10,9 @@ export const products = [
themeColor: '#60ce93',
heroImage: new URL('../assets/images/Carousel/xq.png', import.meta.url).href,
screenshots: [
new URL('../assets/images/xq/xq1.png', import.meta.url).href,
new URL('../assets/images/xq/xq2.png', import.meta.url).href,
new URL('../assets/images/xq/xq3.png', import.meta.url).href,
{ url: new URL('../assets/images/xq/xq1.png', import.meta.url).href, name: '专业管理' },
{ url: new URL('../assets/images/xq/xq2.png', import.meta.url).href, name: '数据大屏' },
{ url: new URL('../assets/images/xq/xq3.png', import.meta.url).href, name: '辅导员工作台' },
],
themeFrom: '#047857',
themeTo: '#34d399',
@@ -25,18 +25,25 @@ export const products = [
{ num: '100', suffix: '万+', label: '累计签到' },
],
features: [
{ emoji: '📋', title: '实习计划管理', desc: '按学期、专业、班级灵活制定实习计划,绑定不同实习规则,实现多批次差异化管理' },
{ emoji: '📍', title: 'GPS签到+电子围栏', desc: '基于地理位置的真实考勤,自动判断签到有效性,未签到学生一键导出名单' },
{ emoji: '📝', title: '问卷式日志', desc: '预设定向提问模板,引导学生深度反思,"流水账"变"成长档案"' },
{ emoji: '📊', title: '多维鉴定评价', desc: '综合考勤、日志、教师评价、企业反馈,自动生成实习鉴定分数,客观公正' },
{ emoji: '🔄', title: '一键同步教育厅平台', desc: '数据审核后直接同步至"广西职业院校学生实习管理平台",无缝对接省级管理要求' },
{ emoji: '📱', title: '微信小程序加持', desc: '学生通过小程序签到、写日志、上传附件,无需下载额外App,使用门槛极低' },
{ icon: 'CalendarCheck', title: '实习计划管理', desc: '按学期、专业、班级灵活制定实习计划,绑定不同实习规则,实现多批次差异化管理', image: new URL('../assets/Features/xq/xq-1.jpg', import.meta.url).href },
{ icon: 'SearchCheck', title: 'GPS签到+电子围栏', desc: '基于地理位置的真实考勤,自动判断签到有效性,未签到学生一键导出名单', image: new URL('../assets/Features/xq/xq-2.png', import.meta.url).href },
{ icon: 'ScrollText', title: '问卷式日志', desc: '预设定向提问模板,引导学生深度反思,"流水账"变"成长档案"', image: new URL('../assets/Features/xq/xq-3.png', import.meta.url).href, small: true },
{ icon: 'FileDigit', title: '多维鉴定评价', desc: '综合考勤、日志、教师评价、企业反馈,自动生成实习鉴定分数,客观公正', image: new URL('../assets/Features/xq/xq-4.png', import.meta.url).href },
{ icon: 'GitBranch', title: '一键同步教育厅平台', desc: '数据审核后直接同步至"广西职业院校学生实习管理平台",无缝对接省级管理要求', image: new URL('../assets/Features/xq/xq-5.png', import.meta.url).href },
{ icon: 'Layers', title: '微信小程序加持', desc: '学生通过小程序签到、写日志、上传附件,无需下载额外App,使用门槛极低',
image: new URL('../assets/Features/xq/xq-6.png', import.meta.url).href, small: true },
],
painPointsTitle: '校企益邦解决的核心问题',
painPointsTitle: '校企益邦\n解决的核心问题',
painPoints: [
{ title: '没有统一的方案执行,学校难以管控学生的实习情况', desc: '平台提供丰富的实习规则定制,涵盖签到、汇报、评价、考核等要求。定制后的实习规则可作为实习要求模板应用于多个实习计划,管理不同批次的学生,省去重复配置众多实习要求的麻烦,体现模板化。' },
{ title: '学生信息采集工作量大,授课教师苦不堪言', desc: '将学生实习信息采集工作分配给学生完成,指导老师只需对这些信息进行审核,减少老师在实习信息采集和整理环节的工作,保障信息准确性、规范性的同时,提高了信息收集效率。' },
{ title: '学生配合的积极性较低,提交的数据不达标', desc: '实习日志采用"问卷式",有指向性地采集学生实习日志,让学生即使面对需要频繁提交的日志,也有汇报方向,减少"流水账式"汇报,提升汇报效果。' },
{
url: new URL('../assets/images/Carousel/zh-points-1.svg', import.meta.url).href,
title: '没有统一的方案执行,学校难以管控学生的实习情况', desc: '平台提供丰富的实习规则定制,涵盖签到、汇报、评价、考核等要求。定制后的实习规则可作为实习要求模板应用于多个实习计划,管理不同批次的学生,省去重复配置众多实习要求的麻烦,体现模板化。', },
{
url: new URL('../assets/images/Carousel/Points-2.svg', import.meta.url).href,
title: '学生信息采集工作量大,授课教师苦不堪言', desc: '将学生实习信息采集工作分配给学生完成,指导老师只需对这些信息进行审核,减少老师在实习信息采集和整理环节的工作,保障信息准确性、规范性的同时,提高了信息收集效率。', },
{
url: new URL('../assets/images/Carousel/Points-3.svg', import.meta.url).href,
title: '学生配合的积极性较低,提交的数据不达标', desc: '实习日志采用"问卷式",有指向性地采集学生实习日志,让学生即使面对需要频繁提交的日志,也有汇报方向,减少"流水账式"汇报,提升汇报效果。', },
],
target: '职业院校教务处、实习管理科、各院系指导教师',
faq: [
@@ -53,9 +60,9 @@ export const products = [
themeColor: '#0c86cf',
heroImage: new URL('../assets/images/Carousel/ky.png', import.meta.url).href,
screenshots: [
new URL('../assets/images/ky/ky1.png', import.meta.url).href,
new URL('../assets/images/ky/ky2.jpeg', import.meta.url).href,
new URL('../assets/images/ky/ky3.jpeg', import.meta.url).href,
{ url: new URL('../assets/images/ky/ky1.png', import.meta.url).href, name: '个人科研画像' },
{ url: new URL('../assets/images/ky/ky2.jpeg', import.meta.url).href, name: '经办跟踪' },
{ url: new URL('../assets/images/ky/ky3.jpeg', import.meta.url).href, name: '消息推送' },
],
themeFrom: '#075985',
themeTo: '#38bdf8',
@@ -68,20 +75,20 @@ export const products = [
{ num: '8000', suffix: '+', label: '登记成果' },
],
features: [
{ emoji: '📝', title: '在线申报审批', desc: '项目申报、审核、立项全流程在线办理,智能校验引擎自动检查相似度和限项规则' },
{ emoji: '🔍', title: '智能查重防重复', desc: '项目名称相似度超80%自动拦截,同一主持人月度限申报1次,杜绝重复申报' },
{ emoji: '💰', title: '经费精细管控', desc: '纵向/横向分类管理,预算—报销实时关联,超预算自动预警,经费使用透明合规' },
{ emoji: '🏆', title: '成果自动赋分', desc: '论文、专利、著作按级别自动量化评分,一次登记多处复用,告别重复填报' },
{ emoji: '', title: '关键节点预警', desc: '经费超期、结题到期、审批退回等场景自动推送提醒,确保每个环节不遗漏' },
{ emoji: '📄', title: '年度报表一键导出', desc: '科研成果统计、项目经费汇总等年度报表自动生成,效率提升数十倍' },
{ icon: 'FileStack', title: '在线申报审批', desc: '项目申报、审核、立项全流程在线办理,智能校验引擎自动检查相似度和限项规则', image: new URL('../assets/Features/ky/ky-1.png', import.meta.url).href },
{ icon: 'SearchCheck', title: '智能查重防重复', desc: '项目名称相似度超80%自动拦截,同一主持人月度限申报1次,杜绝重复申报', image: new URL('../assets/Features/ky/ky-2.png', import.meta.url).href },
{ icon: 'Banknote', title: '经费精细管控', desc: '纵向/横向分类管理,预算—报销实时关联,超预算自动预警,经费使用透明合规', image: new URL('../assets/Features/ky/ky-3.png', import.meta.url).href },
{ icon: 'TrendingUp', title: '成果自动赋分', desc: '论文、专利、著作按级别自动量化评分,一次登记多处复用,告别重复填报', image: new URL('../assets/Features/ky/ky-4.png', import.meta.url).href },
{ icon: 'Bell', title: '关键节点预警', desc: '经费超期、结题到期、审批退回等场景自动推送提醒,确保每个环节不遗漏', image: new URL('../assets/Features/ky/ky-5.png', import.meta.url).href },
{ icon: 'FileDigit', title: '年度报表一键导出', desc: '科研成果统计、项目经费汇总等年度报表自动生成,效率提升数十倍', image: new URL('../assets/Features/ky/ky-6.png', import.meta.url).href },
],
painPointsTitle: '科研管理系统解决的核心问题',
painPointsTitle: '科研管理系统\n解决的核心问题',
painPoints: [
{ title: '传统科研管理流程繁琐,纸质办公效率低下', desc: '全流程数字化,实现科研项目申报、审批、结题的全程在线办理,消除纸质办公,提升工作效率。' },
{ title: '项目申报审核工作量大,人工审核成本高', desc: '智能校验引擎,内置项目查重(相似度检测)、限项检查、预算自动配套计算规则,大幅降低人工审核成本。' },
{ title: '科研经费管理复杂,预算与报销脱节', desc: '经费精细化管控,支持纵向/横向经费分类管理,实时关联预算与报销进度,确保经费使用规范透明。' },
{ title: '科研成果统计困难,年度报表制作耗时', desc: '成果分值量化,自动根据奖励办法对论文、专利、著作等成果进行赋分,支持一键导出年度统计报表。' },
{ title: '项目进度监控不及时,关键节点容易遗漏', desc: '多维度预警,集成短信通知与站内提醒,涵盖预算超期、结题预警、流程回退等关键节点。' },
{ url: new URL('../assets/images/Carousel/Points-1.svg', import.meta.url).href, title: '传统科研管理流程繁琐,纸质办公效率低下', desc: '全流程数字化,实现科研项目申报、审批、结题的全程在线办理,消除纸质办公,提升工作效率。'},
{ url: new URL('../assets/images/Carousel/ky-Points-1.svg', import.meta.url).href, title: '项目申报审核工作量大,人工审核成本高', desc: '智能校验引擎,内置项目查重(相似度检测)、限项检查、预算自动配套计算规则,大幅降低人工审核成本。' },
{ url: new URL('../assets/images/Carousel/ky-Points-3.svg', import.meta.url).href, title: '科研经费管理复杂,预算与报销脱节', desc: '经费精细化管控,支持纵向/横向经费分类管理,实时关联预算与报销进度,确保经费使用规范透明。' },
{ url: new URL('../assets/images/Carousel/Points-4.svg', import.meta.url).href, title: '科研成果统计困难,年度报表制作耗时', desc: '成果分值量化,自动根据奖励办法对论文、专利、著作等成果进行赋分,支持一键导出年度统计报表。' },
{ url: new URL('../assets/images/Carousel/Points-5.svg', import.meta.url).href, title: '项目进度监控不及时,关键节点容易遗漏', desc: '多维度预警,集成短信通知与站内提醒,涵盖预算超期、结题预警、流程回退等关键节点。' },
],
target: '高校科研处、财务处、各院系教师及科研项目负责人',
faq: [
@@ -99,9 +106,9 @@ export const products = [
heroImage: new URL('../assets/images/Carousel/zh.png', import.meta.url).href,
heroSize: 'small',
screenshots: [
new URL('../assets/images/zh/zh1.png', import.meta.url).href,
new URL('../assets/images/zh/zh2.png', import.meta.url).href,
new URL('../assets/images/zh/zh3.jpeg', import.meta.url).href,
{ url: new URL('../assets/images/zh/zh1.png', import.meta.url).href, name: '消息推送' },
{ url: new URL('../assets/images/zh/zh2.png', import.meta.url).href, name: '数据大屏' },
{ url: new URL('../assets/images/zh/zh3.jpeg', import.meta.url).href, name: '发展党员管理' },
],
themeFrom: '#7f1d1d',
themeTo: '#dc2626',
@@ -114,27 +121,34 @@ export const products = [
{ num: '200', suffix: '+', label: '考核指标' },
],
features: [
{ emoji: '📊', title: '三级考核指标', desc: '灵活构建校—院—支部三级指标树,分值自动汇总,考核部门与执行部门逐一对应' },
{ emoji: '📋', title: '三会一课数字化', desc: '通知—签到—记录全流程闭环,二维码签到5分钟刷新,杜绝"代签""补签"' },
{ emoji: '✍️', title: '万能工作记录', desc: '不受模块限制,任何业务工作均可灵活录入并关联考核指标,实现考核全覆盖' },
{ emoji: '🔔', title: '实时进度看板', desc: '领导层穿透查看各项指标完成进度,哪项落后一目了然,变"年底算账"为"过程管控"' },
{ emoji: '👥', title: '发展党员管理', desc: '内置入党全流程节点模型,支持自定义配置,关键节点到期自动提醒支部管理员' },
{ emoji: '📡', title: '党建数据中心', desc: '全校党建数据可视化大屏——组织健康度、党员活跃度、重点工作督办一览无余' },
{ icon: 'Pyramid', title: '三级考核指标', desc: '灵活构建校—院—支部三级指标树,分值自动汇总,考核部门与执行部门逐一对应', image: new URL('../assets/Features/zh/zh-1.png', import.meta.url).href },
{ icon: 'CalendarCheck', title: '三会一课数字化', desc: '通知—签到—记录全流程闭环,二维码签到5分钟刷新,杜绝"代签""补签"', image: new URL('../assets/Features/zh/zh-2.png', import.meta.url).href },
{ icon: 'ScrollText', title: '万能工作记录', desc: '不受模块限制,任何业务工作均可灵活录入并关联考核指标,实现考核全覆盖', image: new URL('../assets/Features/zh/zh-3.png', import.meta.url).href },
{ icon: 'ChartBar', title: '实时进度看板', desc: '领导层穿透查看各项指标完成进度,哪项落后一目了然,变"年底算账"为"过程管控"', image: new URL('../assets/Features/zh/zh-4.png', import.meta.url).href },
{ icon: 'Database', title: '发展党员管理', desc: '内置入党全流程节点模型,支持自定义配置,关键节点到期自动提醒支部管理员', image: new URL('../assets/Features/zh/zh-5.png', import.meta.url).href },
{ icon: 'Activity', title: '党建数据中心', desc: '全校党建数据可视化大屏——组织健康度、党员活跃度、重点工作督办一览无余', image: new URL('../assets/Features/zh/zh-6.png', import.meta.url).href },
],
painPointsTitle: '智慧党建平台解决的核心问题',
painPointsTitle: '智慧党建平台\n解决的核心问题',
painPoints: [
{ title: '党建工作与教学业务“两张皮”,考核指标繁杂多样,难以实现全口径统一管理', desc: '构建“专用模块+通用记录”的双重支撑体系。对于标准化党务,通过“三会一课”、“党员活动”等模块自动关联;对于教学科研等系统中无特定管理模块的考核任务,利用“工作记录”模块作为万能适配器进行灵活录入并关联指标。确保每一项考核指标(无论党务或业务)都有据可查,实现党建考核的100%全覆盖。' },
{
title: ' 年终考核“突击补材料”,重复填表耗时耗力,数据难追溯',
desc: '推行“过程资产化”管理,日常工作(如会议、活动)录入即并关联考核指标,年终无需盘点,数据自动生成,拒绝重复劳动。'
url: new URL('../assets/images/Carousel/zh-Points-1.svg', import.meta.url).href,
title: '党建工作与教学业务”两张皮”,考核指标繁杂多样,难以实现全口径统一管理',
desc: '构建”专用模块+通用记录”的双重支撑体系。对于标准化党务,通过”三会一课”、”党员活动”等模块自动关联;对于教学科研等系统中无特定管理模块的考核任务,利用”工作记录”模块作为万能适配器进行灵活录入并关联指标。确保每一项考核指标(无论党务或业务)都有据可查,实现党建考核的100%全覆盖。'},
{
url: new URL('../assets/images/Carousel/zh-Points-2.svg', import.meta.url).href,
title: ' 年终考核”突击补材料”,重复填表耗时耗力,数据难追溯',
desc: '推行”过程资产化”管理,日常工作(如会议、活动)录入即并关联考核指标,年终无需盘点,数据自动生成,拒绝重复劳动。',
},
{ url: new URL('../assets/images/Carousel/Points-3.svg', import.meta.url).href, title: '”三会一课”组织难、签到难、记录难,合规性监管滞后',
desc: '提供移动端全流程闭环,支持一键发布通知、现场扫码签到、会议纪要即时编辑上传,确保组织生活规范、留痕、可查。',
},
{
title: '“三会一课”组织难、签到难、记录难,合规性监管滞后',
desc: '提供移动端全流程闭环,支持一键发布通知、现场扫码签到、会议纪要即时编辑上传,确保组织生活规范、留痕、可查。'
},
{
title: '党建考核存在“层级衰减”与“管理盲区”,平时进度看不见,年底检查来不及',
desc: '构建分层级可视化考核体系。系统打破科层壁垒,领导层通过“完成情况看板”直观监测各指标的实时进度一键穿透查看明细;执行层通过“指标明细”精准对应“自评”与“佐证材料”,明确“谁来做、谁来考、通过什么材料佐证”。让考核从“年底算总账”变为“全过程透明化管控”,确保压力无损传导至基层末梢。'
url: new URL('../assets/images/Carousel/Points-4.svg', import.meta.url).href,
title: '党建考核存在”层级衰减”与”管理盲区”,平时进度看不见,年底检查来不及',
desc: '构建分层级可视化考核体系。系统打破科层壁垒,领导层通过”完成情况看板”直观监测各指标的实时进度一键穿透查看明细;执行层通过”指标明细”精准对应”自评”与”佐证材料”,明确”谁来做、谁来考、通过什么材料佐证”。让考核从”年底算总账”变为”全过程透明化管控”,确保压力无损传导至基层末梢。',
},
],
target: '高校党委组织部、各二级党委/党总支、基层党支部',
@@ -152,9 +166,9 @@ export const products = [
themeColor: '#8b50ee',
heroImage: new URL('../assets/images/Carousel/sk.png', import.meta.url).href,
screenshots: [
new URL('../assets/images/sk/sk1.png', import.meta.url).href,
new URL('../assets/images/sk/sk2.jpeg', import.meta.url).href,
new URL('../assets/images/sk/sk3.jpeg', import.meta.url).href,
{ url: new URL('../assets/images/sk/sk1.png', import.meta.url).href, name: '数据大屏' },
{ url: new URL('../assets/images/sk/sk2.jpeg', import.meta.url).href, name: '角色管理' },
{ url: new URL('../assets/images/sk/sk3.jpeg', import.meta.url).href, name: '消息推送' },
],
themeFrom: '#5b21b6',
themeTo: '#a78bfa',
@@ -167,23 +181,25 @@ export const products = [
{ num: '200', suffix: '万+', label: '累计咖豆' },
],
features: [
{ emoji: '📦', title: '三级精细管理', desc: '项目→模块→任务三级结构,权重灵活分配,实训教学内容标准化、可复用' },
{ emoji: '🫘', title: '咖豆积分引擎', desc: '成绩自动转换为虚拟积分,支持积分排行、兑换、晋级,让学习像游戏一样上瘾' },
{ emoji: '', title: '五级自动晋级', desc: '自定义等级名称、门槛积分和专属权益,达标自动晋升,成长路径一目了然' },
{ emoji: '🎯', title: '动态技能画像', desc: '基于任务完成情况自动生成个人雷达图,技能点逐一点亮,优势短板清晰可见' },
{ emoji: '📈', title: '人才培养驾驶舱', desc: '全校实训数据一屏呈现——项目热度、参与度、完成率、经费使用,决策有据可依' },
{ emoji: '🎁', title: '积分商城', desc: '咖豆可兑换实物或虚拟礼品,构建"学、练、评、换"完整激励闭环' },
{ icon: 'Layers', title: '三级精细管理', desc: '项目→模块→任务三级结构,权重灵活分配,实训教学内容标准化、可复用', image: new URL('../assets/Features/sk/sk-1.png', import.meta.url).href },
{ icon: 'Database', title: '咖豆积分引擎', desc: '成绩自动转换为虚拟积分,支持积分排行、兑换、晋级,让学习像游戏一样上瘾', image: new URL('../assets/Features/sk/sk-2.png', import.meta.url).href },
{ icon: 'TrendingUp', title: '五级自动晋级', desc: '自定义等级名称、门槛积分和专属权益,达标自动晋升,成长路径一目了然', small: true, image: new URL('../assets/Features/sk/sk-3.png', import.meta.url).href },
{ icon: 'Activity', title: '动态技能画像', desc: '基于任务完成情况自动生成个人雷达图,技能点逐一点亮,优势短板清晰可见', small: true, image: new URL('../assets/Features/sk/sk-4.png', import.meta.url).href },
{ icon: 'ChartBar', title: '人才培养驾驶舱', desc: '全校实训数据一屏呈现——项目热度、参与度、完成率、经费使用,决策有据可依', image: new URL('../assets/Features/sk/sk-5.png', import.meta.url).href },
{ icon: 'Banknote', title: '积分商城', desc: '咖豆可兑换实物或虚拟礼品,构建"学、练、评、换"完整激励闭环', small: true, image: new URL('../assets/Features/sk/sk-6.png', import.meta.url).href },
],
painPoints: [
{ title: '人才培养模式落地难,教学管理与“五级进阶”理念脱节。',
desc: '系统内置“项目-模块-任务”三级结构与积分晋级规则,将抽象的“五级进阶”理念转化为可量化、可执行、可追踪的线上流程。学生通过完成任务获得积分,自动点亮技能点,实现逐级晋升,确保培养模式的精准落地。' },
{
title: ' 实训项目数据散落,缺乏统一管理与深度分析',
desc: '建立统一的校级实训项目库,实现项目创建、审批、排课、执行到归档的全生命周期管理。同时,建设可视化数据大屏,为管理层提供各学院项目数量、学生参与度、完成情况等全景决策数据。' },
{
title: '技能成长过程不可视,学生缺乏明确的学习目标和动力。',
desc: '系统根据学生参与的实训任务、积分获取情况,自动生成动态的个人技能画像。学生可以直观看到自己已掌握的“技能点”以及距离下一等级的差距,明确优势与不足,并获得持续进阶的动力。' },
url: new URL('../assets/images/Carousel/sx-Points-2.svg', import.meta.url).href, title: '人才培养模式落地难,教学管理与”五级进阶”理念脱节。',
desc: '系统内置”项目-模块-任务”三级结构与积分晋级规则,将抽象的”五级进阶”理念转化为可量化、可执行、可追踪的线上流程。学生通过完成任务获得积分,自动点亮技能点,实现逐级晋升,确保培养模式的精准落地。', },
{ url: new URL('../assets/images/Carousel/Points-2.svg', import.meta.url).href,
title: ' 实训项目数据散落,缺乏统一管理与深度分析',
desc: '建立统一的校级实训项目库,实现项目创建、审批、排课、执行到归档的全生命周期管理。同时,建设可视化数据大屏,为管理层提供各学院项目数量、学生参与度、完成情况等全景决策数据。', },
{ url: new URL('../assets/images/Carousel/Points-3.svg', import.meta.url).href,
title: '技能成长过程不可视,学生缺乏明确的学习目标和动力。',
desc: '系统根据学生参与的实训任务、积分获取情况,自动生成动态的个人技能画像。学生可以直观看到自己已掌握的”技能点”以及距离下一等级的差距,明确优势与不足,并获得持续进阶的动力。', },
],
target: '商科专业群、实训中心、教务处',
faq: [
+106
View File
@@ -69,3 +69,109 @@ body {
text-shadow: 0 2px 40px rgba(0, 162, 234, 0.25),
0 0 80px rgba(0, 162, 234, 0.15);
}
/* ── 台式显示器外框 ── */
.monitor-frame {
position: relative;
background: #1a1a2e;
border-radius: 8px;
padding: 6px 6px 3px 6px;
box-shadow:
0 0 0 1px #2a2a3e,
0 2px 12px rgba(0, 0, 0, 0.3);
max-width: 750px;
margin: 0 auto;
}
.monitor-screen {
border-radius: 3px;
overflow: hidden;
background: #000;
height: 180px;
}
.monitor-chin {
display: flex;
align-items: center;
justify-content: center;
padding: 5px 0 2px 0;
}
.monitor-chin-label {
font-size: 9px;
font-weight: 500;
color: #666;
letter-spacing: 0.06em;
white-space: nowrap;
}
.monitor-stand-neck {
width: 24px;
height: 18px;
background: linear-gradient(180deg, #2a2a3e 0%, #222236 100%);
margin: 0 auto;
border-radius: 0 0 4px 4px;
}
.monitor-stand-base {
width: 100px;
height: 5px;
margin: 0 auto;
border-radius: 3px;
background: var(--monitor-theme, #60ce93);
box-shadow: 0 1px 4px rgba(0,0,0,0.2);
}
/* sm */
@media (min-width: 640px) {
.monitor-frame {
max-width: 700px;
border-radius: 10px;
padding: 8px 8px 4px 8px;
box-shadow:
0 0 0 1.5px #2a2a3e,
0 3px 16px rgba(0, 0, 0, 0.3);
}
.monitor-screen {
border-radius: 3px;
height: 260px;
}
.monitor-chin { padding: 6px 0 3px 0; }
.monitor-chin-label { font-size: 10px; }
.monitor-stand-neck { width: 30px; height: 24px; border-radius: 0 0 5px 5px; }
.monitor-stand-base { width: 130px; height: 7px; border-radius: 4px; }
}
/* md */
@media (min-width: 768px) {
.monitor-frame {
max-width: 780px;
border-radius: 12px;
padding: 10px 10px 6px 10px;
box-shadow:
0 0 0 2px #2a2a3e,
0 4px 24px rgba(0, 0, 0, 0.3);
}
.monitor-screen {
border-radius: 4px;
height: 360px;
}
.monitor-chin { padding: 8px 0 4px 0; }
.monitor-chin-label { font-size: 11px; }
.monitor-stand-neck { width: 36px; height: 30px; border-radius: 0 0 6px 6px; }
.monitor-stand-base { width: 150px; height: 8px; border-radius: 4px; }
}
/* lg */
@media (min-width: 1024px) {
.monitor-frame { max-width: 820px; }
.monitor-screen { height: 440px; }
}
/* xl */
@media (min-width: 1280px) {
.monitor-frame { max-width: 860px; }
.monitor-screen { height: 500px; }
.monitor-stand-neck { width: 40px; height: 36px; }
.monitor-stand-base { width: 180px; height: 10px; border-radius: 5px; }
}
+83 -60
View File
@@ -172,80 +172,103 @@ export default function Home() {
</div>
{/* Right: text */}
<div className="space-y-4">
<div className="space-y-3">
{(activeCapability !== null ? [capabilities[activeCapability]] : capabilities).map((c, i) => {
const idx = activeCapability !== null ? activeCapability : i
const color = products[idx]?.themeColor || '#043b8f'
const isActive = activeCapability === idx
return (
<div key={idx} className="relative p-8 rounded-2xl transition-all duration-500 cursor-pointer group overflow-hidden"
<div key={idx} className="relative rounded-2xl transition-all duration-500 cursor-pointer group overflow-hidden border"
style={isActive
? { backgroundColor: '#fff', borderColor: color + '33', borderWidth: 1, boxShadow: `0 8px 32px ${color}14, 0 2px 8px ${color}08` }
: {}}
onClick={() => setActiveCapability(idx)}>
? {
backgroundColor: '#fff',
borderColor: color + '20',
boxShadow: `0 12px 40px -8px ${color}18, 0 4px 12px ${color}0a, 0 0 0 1px ${color}10`,
transform: 'scale(1.02)',
zIndex: 10,
padding: '2rem',
}
: {
backgroundColor: '#fafbfc',
borderColor: 'transparent',
boxShadow: 'none',
transform: 'scale(0.97)',
zIndex: 1,
padding: '1.25rem',
opacity: 0.55,
}}
onClick={() => setActiveCapability(idx)}
onMouseEnter={(e) => { if (!isActive) { e.currentTarget.style.opacity = '0.8'; e.currentTarget.style.transform = 'scale(0.98)'; }}}
onMouseLeave={(e) => { if (!isActive) { e.currentTarget.style.opacity = '0.55'; e.currentTarget.style.transform = 'scale(0.97)'; }}}>
{/* Left accent line */}
{isActive && (
<div className="absolute top-6 left-0 bottom-6 w-[3px] rounded-r-full opacity-60"
style={{ background: `linear-gradient(180deg, transparent, ${color}, transparent)` }} />
)}
<div className="absolute top-0 left-0 bottom-0 w-1.5 rounded-l-2xl transition-all duration-500"
style={{ backgroundColor: isActive ? color : 'transparent' }} />
{/* Decorative corner blobs */}
<div className="absolute top-0 right-0 w-32 h-32 rounded-bl-full opacity-0 group-hover:opacity-100 transition-all duration-700"
style={{ backgroundColor: color + '06' }} />
<div className="absolute bottom-0 left-0 w-20 h-20 border rounded-tr-3xl opacity-0 group-hover:opacity-100 transition-all duration-700"
style={{ borderColor: color + '0d' }} />
{/* Number badge row */}
<div className="flex items-center gap-3 mb-4">
<span className="flex items-center justify-center w-8 h-8 rounded-lg text-xs font-black transition-all duration-300"
{/* Icon + label row */}
<div className="flex items-center gap-3 mb-3">
<span className="shrink-0 flex items-center justify-center rounded-xl transition-all duration-300"
style={isActive
? { backgroundColor: color + '14', color: color, boxShadow: `0 2px 8px ${color}14` }
: { backgroundColor: '#f1f5f9', color: '#94a3b8' }}>0{idx + 1}</span>
<div className="h-px flex-1 transition-all duration-300"
style={isActive
? { background: `linear-gradient(90deg, ${color}30, ${color}08, transparent)` }
: { backgroundColor: '#f1f5f9' }} />
{/* Product icon */}
{React.createElement(products[idx].icon, {
className: "w-4 h-4 transition-all duration-300",
style: { color: isActive ? color : '#cbd5e1' }
})}
? { backgroundColor: color, boxShadow: `0 4px 12px ${color}30`, width: '2.5rem', height: '2.5rem' }
: { backgroundColor: color + '08', width: '2rem', height: '2rem' }}>
{React.createElement(products[idx].icon, {
className: isActive ? "w-5 h-5" : "w-4 h-4",
style: { color: isActive ? '#fff' : color + '60' }
})}
</span>
<div className="flex-1 flex items-center gap-3">
<span className="text-[10px] font-bold tracking-[0.2em] uppercase transition-all duration-300 whitespace-nowrap"
style={{ color: isActive ? color + '80' : '#c0c7cf' }}>Core Capability</span>
<div className="h-px flex-1 transition-all duration-300"
style={{ background: isActive ? `linear-gradient(90deg, ${color}20, ${color}05, transparent)` : 'transparent' }} />
</div>
</div>
{/* Title */}
<h3 className="text-xl xs:text-2xl sm:text-3xl font-bold transition-all duration-300 tracking-tight"
style={{ color: isActive ? color : '#0f172a' }}>{c.title}</h3>
{/* Subtitle */}
<p className="text-sm xs:text-base mt-1.5 font-medium transition-all duration-300"
style={{ color: isActive ? color + '99' : '#94a3b8' }}>{c.sub}</p>
{/* Divider */}
<div className="my-4 h-px w-12 transition-all duration-500"
style={{ background: isActive ? `linear-gradient(90deg, ${color}40, transparent)` : '#f1f5f9' }} />
{/* Description */}
<p className="text-sm xs:text-base text-[#475569] leading-[1.9]">{c.desc}</p>
{/* Tags */}
{c.tip && (
<div className="mt-5 flex flex-wrap gap-2">
{c.tip.split('、').map((t, ti) => (
<span key={ti} className="px-3 py-1.5 text-xs font-medium rounded-full transition-all duration-300"
style={isActive
? { color: color, backgroundColor: color + '0f', borderColor: color + '1a', borderWidth: 1 }
: { color: '#94a3b8', backgroundColor: '#f8fafc', borderColor: '#e2e8f0', borderWidth: 1 }}>
{t}
</span>
))}
</div>
)}
{/* View more link */}
<h3 className="transition-all duration-300 tracking-tight"
style={isActive
? { fontSize: 'clamp(1.25rem, 2.5vw, 1.875rem)', fontWeight: 800, color: '#0f172a' }
: { fontSize: 'clamp(0.875rem, 1.5vw, 1rem)', fontWeight: 600, color: '#94a3b8' }}>{c.title}</h3>
{/* Subtitle - only active */}
{isActive && (
<div className="mt-6 flex justify-end">
<Link to={`/product/${c.slug}`}
onClick={(e) => e.stopPropagation()}
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-300"
style={{ color: color, backgroundColor: color + '0a' }}
onMouseEnter={(e) => { e.target.style.backgroundColor = color + '1a'; }}
onMouseLeave={(e) => { e.target.style.backgroundColor = color + '0a'; }}>
查看全部
<svg className="w-3.5 h-3.5 transition-transform duration-300 group-hover:translate-x-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7" /></svg>
</Link>
</div>
<p className="text-sm xs:text-base mt-1.5 font-semibold"
style={{ color: color + 'aa' }}>{c.sub}</p>
)}
{/* Divider */}
<div className="my-4 h-[3px] rounded-full transition-all duration-500"
style={{
width: isActive ? '2.5rem' : '1.5rem',
backgroundColor: isActive ? color : '#e2e8f0',
opacity: isActive ? 0.5 : 0.6
}} />
{/* Description - only active */}
{isActive && (
<>
<p className="text-sm xs:text-base text-[#334155] leading-[1.9]">{c.desc}</p>
{/* Tags */}
{c.tip && (
<div className="mt-5 flex flex-wrap gap-2">
{c.tip.split('、').map((t, ti) => (
<span key={ti} className="px-3 py-1.5 text-xs font-semibold rounded-full"
style={{ color: color, backgroundColor: color + '0f', border: `1px solid ${color}1a` }}>
{t}
</span>
))}
</div>
)}
{/* View more link */}
<div className="mt-6 flex justify-end">
<Link to={`/product/${c.slug}`}
onClick={(e) => e.stopPropagation()}
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-semibold transition-all duration-300 hover:shadow-sm"
style={{ color: '#fff', backgroundColor: color }}
onMouseEnter={(e) => { e.target.style.filter = 'brightness(1.1)'; }}
onMouseLeave={(e) => { e.target.style.filter = 'brightness(1)'; }}>
查看全部
<svg className="w-3.5 h-3.5 transition-transform duration-300 group-hover:translate-x-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7" /></svg>
</Link>
</div>
</>
)}
</div>
)
+368 -111
View File
@@ -1,9 +1,17 @@
import { useParams, Link } from 'react-router-dom'
import { ChevronDown, ArrowRight, Shield, Clock, Headphones, AlertCircle } from 'lucide-react'
import { getProduct } from '../data/products'
import ScrollReveal from '../components/ScrollReveal'
import StatNumber from '../components/StatNumber'
import Carousel from '../components/Carousel'
import { useParams, Link } from "react-router-dom"
import { ChevronDown, ArrowRight, Shield, Clock, Headphones, AlertCircle, GitBranch, FileStack, MessageCircleX, FileDigit, SearchCheck, Banknote, ChartBar, Bell, Layers, ScrollText, CalendarCheck, TrendingUp, Pyramid, Database, Activity, Star } from "lucide-react"
import { getProduct } from "../data/products"
import ScrollReveal from "../components/ScrollReveal"
import StatNumber from "../components/StatNumber"
import Carousel from "../components/Carousel"
import ThemedSvg from "../components/ThemedSvg"
// 图标映射表 — 所有图标均来自 Lucide React (MIT License,免费商用)
const iconMap = {
GitBranch, FileStack, MessageCircleX, FileDigit, SearchCheck,
Banknote, ChartBar, Bell, Layers, ScrollText, CalendarCheck,
TrendingUp, Pyramid, Database, Activity,
}
export default function ProductPage() {
const { slug } = useParams()
@@ -42,26 +50,26 @@ export default function ProductPage() {
>
{/* ── Hero ── */}
<div className="relative -mt-[70px]">
<section className="relative overflow-hidden" style={{ backgroundColor: product.themeColor }}>
<section className="relative overflow-hidden flex items-center justify-center " style={{ backgroundColor: product.themeColor }}>
{/* Desktop: left image + right card */}
<div className="hidden md:flex items-center justify-between mx-auto min-h-[760px]">
<div className="hidden md:flex items-center justify-between gap-12 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 min-h-[760px]">
{/* Image - left */}
<div className="w-1/2 flex items-center justify-center">
<div className="w-[55%] flex items-center justify-center">
{product.heroImage && (
<img src={product.heroImage} alt="" className={`w-full h-auto object-contain ${product.heroSize === 'small' ? 'max-h-[350px]' : 'max-h-[500px]'}`} />
<img src={product.heroImage} alt="" className={`w-full h-auto object-contain`} />
)}
</div>
{/* Content - right */}
<div className="w-1/2">
<h1 className="text-5xl md:text-7xl font-extrabold mb-5 tracking-tight leading-[1.1] text-white hero-product-title">
<div className="w-[45%] flex flex-col items-between justify-center gap-2 shrink-0">
<h1 className="text-4xl md:text-6xl font-extrabold text-center mb-5 tracking-tight leading-[1.1] text-white hero-product-title whitespace-nowrap">
{product.name}
</h1>
<p className="text-2xl md:text-3xl mb-8 leading-relaxed text-white/80 hero-product-slogan">
<p className="text-2xl md:text-3xl text-center mb-8 leading-relaxed text-white/80 hero-product-slogan whitespace-nowrap">
{product.slogan}
</p>
{product.stats && (
<div className="flex gap-4 max-w-[620px]">
<div className="flex gap-4 justify-center mt-6">
{product.stats.map((s, i) => (
<div key={i} className="flex-1 rounded-2xl p-4 text-center border border-white/20 bg-white/10">
<div className="text-2xl md:text-3xl font-extrabold tracking-tight text-white">
@@ -108,75 +116,182 @@ export default function ProductPage() {
</section>
</div>
{/* ── Brief ── */}
<section className="py-20 px-6 bg-white">
<div className="max-w-3xl mx-auto">
{/* ── Brief / 产品简介 ── */}
<section className="py-24 md:py-32 px-6 bg-[#f8fafc]">
<div className="max-w-4xl mx-auto">
<ScrollReveal>
<div className="relative bg-[#f8fafc] rounded-3xl p-10 md:p-14 overflow-hidden">
{/* Floating decorative shapes */}
<div className="absolute -top-6 -right-6 w-24 h-24 rounded-full bg-[var(--theme)]" />
<div className="absolute -bottom-8 -left-8 w-20 h-20 rounded-2xl rotate-12 bg-[var(--theme)]" />
<div className="absolute top-1/2 -right-4 w-8 h-8 rounded-full border-2 border-[var(--theme)]" />
{/* ═══ 折角卡片 ═══ */}
<div className="relative bg-white rounded-2xl shadow-[0_4px_32px_-8px_rgba(0,0,0,0.08),0_0_0_1px_rgba(0,0,0,0.04)]">
<p className="text-[#334155] text-lg md:text-xl leading-loose relative z-10">
{product.brief}
</p>
</div>
{product.tags && (
<div className="flex flex-wrap items-center justify-center gap-3 mt-10">
{product.tags.map((tag, i) => (
<span key={i}
className="relative px-6 py-2 text-sm font-bold text-white transition-all duration-200 hover:scale-105 hover:-translate-y-0.5"
style={{ backgroundColor: product.themeColor }}>
<span className="relative z-10">{tag}</span>
<svg className="absolute -bottom-2 left-0 w-full h-2 text-white/20" viewBox="0 0 100 8" preserveAspectRatio="none">
<path d="M0 0 L35 8 L65 8 L100 0" fill="currentColor" />
</svg>
<span className="absolute top-1/2 -translate-y-1/2 -left-2 w-1 h-4 rounded-full" style={{ backgroundColor: product.themeColor }} />
</span>
))}
{/* ── 右上折角 ── */}
<div className="absolute top-0 right-0 w-[100px] h-[100px] md:w-[130px] md:h-[130px] overflow-hidden rounded-tr-2xl z-20 pointer-events-none">
{/* 折角在卡片上投下的阴影 */}
<div className="absolute top-0 right-0 w-full h-full"
style={{
clipPath: 'polygon(100% 0, 100% 100%, 0 0)',
background: 'rgba(0,0,0,0.1)',
filter: 'blur(4px)',
transform: 'translate(2px, 2px)',
}}
/>
{/* 折角主体 — 品牌色渐变,模拟纸张背面 */}
<div className="absolute top-0 right-0 w-full h-full"
style={{
clipPath: 'polygon(100% 0, 100% 100%, 0 0)',
background: `linear-gradient(225deg, ${product.themeColor} 0%, rgba(${r},${g},${b},0.78) 100%)`,
}}
/>
{/* 折角内面高光 */}
<div className="absolute top-0 right-0 w-full h-full"
style={{
clipPath: 'polygon(100% 0, 100% 100%, 0 0)',
background: `linear-gradient(225deg, rgba(255,255,255,0.28) 0%, transparent 50%)`,
}}
/>
{/* 折痕线 */}
<div className="absolute top-0 right-0 w-full h-full"
style={{
background: `linear-gradient(225deg, transparent 37%, rgba(0,0,0,0.07) 39%, rgba(0,0,0,0.03) 41%, transparent 43%)`,
}}
/>
{/* 图标 */}
<Star className="absolute w-[16px] h-[16px] md:w-[20px] md:h-[20px] text-yellow-400 fill-yellow-400 drop-shadow-sm"
style={{ top: '18px', right: '14px', transform: 'rotate(45deg)' }}
strokeWidth={1.5}
/>
</div>
)}
{/* ── 内容区 ── */}
<div className="relative p-10 md:p-16 lg:p-20">
{/* 顶部装饰线 + 标签 */}
<div className="flex items-center gap-3 mb-7">
<span className="w-10 h-[2px] rounded-full bg-[var(--theme)]/40" />
<span className="text-sm md:text-base font-bold tracking-[0.22em] uppercase text-[var(--theme)]/60">
Brief
</span>
</div>
{/* 标题 */}
<h2 className="text-3xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1] mb-10">
产品简介
</h2>
{/* 正文 */}
<p className="text-[#334155] text-lg md:text-xl leading-[1.9] font-medium">
{product.brief}
</p>
{/* ═══ 底部标签 ═══ */}
{product.tags && (
<div className="flex flex-wrap items-center justify-center gap-3 mt-10 pt-10 border-t border-gray-100">
{product.tags.map((tag, i) => (
<span
key={i}
className="px-5 py-2 text-sm font-bold rounded-lg border transition-all duration-300 hover:-translate-y-1 cursor-default"
style={{
color: product.themeColor,
borderColor: `rgba(${r},${g},${b},0.25)`,
backgroundColor: `rgba(${r},${g},${b},0.04)`,
}}
>
{tag}
</span>
))}
</div>
)}
</div>
</div>
</ScrollReveal>
</div>
</section>
{/* ── Pain Points ── */}
{product.painPoints && product.painPoints.length > 0 && (
<section className="py-24 px-6 bg-[#fafbfc]">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="mb-14 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[var(--theme)]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">Pain Points</span>
</div>
<h2 className="text-2xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
{product.painPointsTitle || `${product.name}解决的核心问题`}
</h2>
<p className="text-[#475569] text-lg md:text-xl mt-6 max-w-xl leading-relaxed">深入理解管理难题提供经过实战验证的解决方案</p>
</div>
</ScrollReveal>
<div className="space-y-5">
<section className="py-24 md:py-32 px-6 bg-gradient-to-b from-white via-[#fafbfc] to-white overflow-hidden">
{/* ── 标题区 ── */}
<div className="text-center mb-16 md:mb-20">
<span className="inline-flex items-center gap-2 text-sm font-bold tracking-[0.2em] uppercase text-[var(--theme)] mb-4">
<span className="w-8 h-px bg-[var(--theme)]/40" />
Pain Points & Solutions
<span className="w-8 h-px bg-[var(--theme)]/40" />
</span>
<h2 className="text-3xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.8] mb-4 whitespace-pre-line">
{product.painPointsTitle || `${product.name}\n解决的核心问题`}
</h2>
<p className="mt-4 text-[#64748b] text-lg max-w-2xl mx-auto">
直击业务痛点提供经过实战验证的完整解决方案
</p>
</div>
<div className="max-w-7xl mx-auto">
{/* ── 响应式布局:移动端堆叠,md+ 左右对半 ── */}
<div className="flex flex-col gap-16 md:gap-24 lg:gap-32">
{product.painPoints.map((item, i) => (
<ScrollReveal key={i} delay={i * 0.08}>
<div className="relative bg-white rounded-2xl p-6 xs:p-8 md:p-10 border border-gray-100 hover:shadow-lg hover:shadow-gray-200/50 transition-all duration-300 group h-full">
<div className="absolute top-0 left-0 right-0 h-1 rounded-t-2 bg-[var(--theme)] opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
{/* Pain point */}
<div className="flex items-start gap-2 xs:gap-3 mb-4 xs:mb-5 pb-4 xs:pb-5 border-b border-dashed border-gray-200">
<div className="shrink-0 w-7 h-7 xs:w-9 xs:h-9 rounded-full bg-red-50 flex items-center justify-center">
<AlertCircle className="w-4 h-4 xs:w-5 xs:h-5 text-red-500" />
</div>
<h3 className="text-base xs:text-lg font-bold text-[#0f172a] leading-relaxed">{item.title}</h3>
</div>
{/* Solution */}
<div className="ml-0 sm:ml-6 md:ml-11 pl-3 xs:pl-5 border-l-2 border-[var(--theme)] bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.03)] rounded-r-lg py-3 xs:py-4 pr-3 xs:pr-4">
<span className="inline-block text-xs xs:text-sm font-semibold text-white bg-[var(--theme)] rounded px-2 xs:px-3 py-1 xs:py-1.5 mb-2 xs:mb-3">解决方案</span>
<p className="text-[#334155] text-sm xs:text-base md:text-lg xl:text-xl leading-[1.7] xs:leading-[1.9]">{item.desc}</p>
<div key={i} className={`flex flex-col md:flex-row items-center gap-8 md:gap-12 lg:gap-20 ${i % 2 === 1 ? 'md:flex-row-reverse' : ''}`}>
{/* ═══ 配图 ═══ */}
<div className="w-full md:w-1/2 flex justify-center">
<div className="relative w-full max-w-[320px] sm:max-w-[360px] md:w-4/5 lg:w-3/5">
{item.url ? (
item.url.endsWith('.svg') ? (
<ThemedSvg
url={item.url}
color={product.themeColor}
className="relative z-10 w-full mx-auto [&>svg]:w-full [&>svg]:h-auto"
/>
) : (
<img
src={item.url}
alt={item.title}
className="relative z-10 w-full mx-auto"
/>
)
) : (
<div className="relative z-10 w-full mx-auto aspect-[4/3] rounded-2xl bg-gray-100 flex items-center justify-center">
<span className="text-gray-400 text-sm">暂无配图</span>
</div>
)}
</div>
</div>
</ScrollReveal>
{/* ═══ 文字 ═══ */}
<div className="w-full md:w-1/2">
<div className="space-y-6 md:space-y-8 lg:space-y-10">
{/* ── 痛点书签卡 ── */}
<div className="relative">
<div className="relative bg-white rounded-2xl shadow-[0_6px_28px_-8px_rgba(239,68,68,0.15)] border border-red-200 overflow-visible">
<div className="absolute -top-5 md:-top-6 left-6 md:left-8 px-4 md:px-5 py-1.5 md:py-2 rounded-t-lg md:rounded-t-xl bg-red-400 shadow-[0_-2px_8px_rgba(239,68,68,0.2)]">
<span className="text-sm md:text-base font-black tracking-[0.1em] uppercase text-white">痛点</span>
</div>
<div className="absolute left-0 top-0 bottom-0 w-1.5 bg-red-400 rounded-l-2xl" />
<div className="pl-8 md:pl-10 pr-6 md:pr-8 py-6 md:py-8 pt-10 md:pt-12">
<p className="text-[#1e293b] text-base md:text-lg leading-[1.9] font-bold">
{item.title}
</p>
</div>
</div>
</div>
{/* ── 解决方案书签卡 ── */}
<div className="relative">
<div className="relative bg-white rounded-2xl shadow-[0_6px_28px_-8px_rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.15)] border overflow-visible"
style={{ borderColor: `${product.themeColor}30` }}>
<div className="absolute -top-5 md:-top-6 left-6 md:left-8 px-4 md:px-5 py-1.5 md:py-2 rounded-t-lg md:rounded-t-xl shadow-[0_-2px_8px_rgba(0,0,0,0.1)]"
style={{ backgroundColor: product.themeColor }}>
<span className="text-sm md:text-base font-black tracking-[0.1em] uppercase text-white">解决方案</span>
</div>
<div className="absolute left-0 top-0 bottom-0 w-1.5 rounded-l-2xl"
style={{ backgroundColor: product.themeColor }} />
<div className="pl-8 md:pl-10 pr-6 md:pr-8 py-6 md:py-8 pt-10 md:pt-12">
<p className="text-[#1e293b] text-base md:text-lg leading-[1.9] font-bold">
{item.desc}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
))}
</div>
</div>
@@ -184,23 +299,24 @@ export default function ProductPage() {
)}
{/* ── Core Features ── */}
<section className="py-24 px-6 bg-[#fafbfc]">
<div className="max-w-5xl mx-auto">
<section className="py-24 px-6 bg-white">
<div className="max-w-7xl mx-auto">
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[var(--theme)]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">Core Features</span>
<span className="w-8 h-[2px] rounded-full bg-[var(--theme)]/40" />
<span className="text-[11px] font-bold tracking-[0.22em] uppercase text-[var(--theme)]/60">Core Features</span>
<span className="w-8 h-[2px] rounded-full bg-[var(--theme)]/40" />
</div>
<h2 className="text-3xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
<h2 className="text-3xl md:text-[2.75rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
核心功能
</h2>
{product.target && (
<div className="mt-5 flex flex-col items-center gap-3">
<span className="text-lg text-[#0f172a] font-bold">适用对象</span>
<div className="mt-6 flex flex-col items-center gap-4">
<span className="text-xl font-extrabold text-[#0f172a]">适用对象</span>
<div className="flex flex-wrap justify-center gap-3">
{product.target.split('、').map((t, i) => (
<span key={i} className="px-4 py-1.5 rounded-lg text-sm font-bold text-[var(--theme)] border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.3)]">{t.trim()}</span>
<span key={i} className="px-5 py-2 rounded-lg text-base font-bold text-[var(--theme)] border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.3)] bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.03)]">{t.trim()}</span>
))}
</div>
</div>
@@ -208,23 +324,139 @@ export default function ProductPage() {
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* ── 桌面端:每个功能一行卡片(左文字 + 右截图) ── */}
<div className="hidden lg:flex flex-col gap-6">
{product.features.map((f, i) => (
<ScrollReveal key={i} delay={i * 0.05}>
<div className="relative bg-white rounded-2xl p-8 md:p-10 h-full border border-gray-100 hover:shadow-xl hover:shadow-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.05)] transition-all duration-500 group overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-0 group-hover:opacity-100 transition-opacity duration-500"
style={{ background: `linear-gradient(90deg, transparent, ${product.themeColor}, transparent)` }} />
<div className="absolute top-0 right-0 w-32 h-32 bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.02)] rounded-bl-full" />
<div className="absolute bottom-0 left-0 w-20 h-20 border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] rounded-tr-3xl" />
<div className={`group relative flex rounded-2xl bg-white border border-gray-100 overflow-hidden transition-all duration-500 hover:border-[var(--theme)]/20 hover:shadow-[0_8px_32px_-8px_rgba(0,0,0,0.10)] shadow-[0_1px_8px_-2px_rgba(0,0,0,0.05)] ${i % 2 === 1 ? 'flex-row-reverse' : ''}`}>
{/* hover 边框光 */}
<div className="absolute inset-0 rounded-2xl ring-1 ring-inset ring-transparent group-hover:ring-[var(--theme)]/10 transition-all duration-700 pointer-events-none z-10" />
<div className="relative z-10">
<div className="flex items-center gap-3 mb-4">
<span className="flex items-center justify-center w-8 h-8 rounded-md bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] text-xs font-black text-[var(--theme)]">0{i + 1}</span>
<div className="h-px flex-1 bg-gradient-to-r from-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] to-transparent" />
{/* 文字区 */}
<div className={`relative w-1/2 flex flex-col justify-center p-12 xl:p-14 ${i % 2 === 1 ? 'pl-10 xl:pl-12' : 'pr-10 xl:pr-12'}`}>
{/* 序号 + 标签 */}
<div className="flex items-center gap-3 mb-5">
<span className="shrink-0 flex items-center justify-center w-10 h-10 rounded-xl text-white"
style={{ backgroundColor: product.themeColor, boxShadow: `0 4px 12px ${product.themeColor}30` }}>
{f.icon && iconMap[f.icon] ? (() => { const Icon = iconMap[f.icon]; return <Icon className="w-5 h-5" strokeWidth={2} /> })() : <span className="text-sm font-black">{String(i + 1).padStart(2, '0')}</span>}
</span>
<div>
<span className="text-[10px] font-bold tracking-[0.2em] uppercase text-[var(--theme)]/50">Core Feature</span>
</div>
</div>
<h3 className="text-xl font-bold text-[#0f172a] mb-3">{f.title}</h3>
<p className="text-[#64748b] text-lg leading-relaxed">{f.desc}</p>
{/* 标题 */}
<h3 className="text-2xl lg:text-[1.65rem] font-extrabold text-[#0f172a] leading-[1.2] mb-4 tracking-tight">
{f.title}
</h3>
{/* 分隔线 */}
<div className="w-10 h-[3px] rounded-full mb-4" style={{ backgroundColor: product.themeColor, opacity: 0.25 }} />
{/* 描述 */}
<p className="text-[#475569] text-base leading-[1.8] max-w-md">
{f.desc}
</p>
</div>
{/* 截图区 */}
<div className="relative w-1/2 flex items-center justify-center p-6 lg:p-8 bg-[#f8fafc]">
{f.image ? (
<div className="relative w-full">
{f.small ? (
/* 手机模型外壳 */
<div className="relative mx-auto w-[140px]">
<div className="relative bg-[#1a1a2e] rounded-[18px] p-[3px] shadow-[0_12px_36px_-10px_rgba(0,0,0,0.25)] transition-all duration-500 group-hover:shadow-[0_18px_44px_-12px_rgba(0,0,0,0.30)] group-hover:-translate-y-1">
<div className="flex justify-center pt-0.5">
<div className="flex items-center gap-0.5">
<span className="w-1 h-1 rounded-full bg-[#0f0f1a]" />
<span className="w-4 h-0.5 bg-[#0f0f1a] rounded-full" />
</div>
</div>
<div className="mt-0.5 rounded-[6px] overflow-hidden bg-white">
<img src={f.image} alt={f.title} className="w-full h-auto object-contain max-h-[280px]" />
</div>
<div className="flex justify-center pt-1.5 pb-0.5">
<div className="w-5 h-[2px] bg-white/15 rounded-full" />
</div>
</div>
</div>
) : (
/* 浏览器窗口效果 */
<div className="rounded-lg overflow-hidden shadow-[0_6px_24px_-6px_rgba(0,0,0,0.10)] ring-1 ring-black/5 transition-all duration-500 group-hover:shadow-[0_10px_32px_-10px_rgba(0,0,0,0.15)] group-hover:-translate-y-1 w-4/5 mx-auto">
<div className="flex items-center gap-1 px-2.5 py-1.5 bg-[#1e293b]">
<span className="w-1.5 h-1.5 rounded-full bg-red-400/80" />
<span className="w-1.5 h-1.5 rounded-full bg-amber-400/80" />
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400/80" />
<span className="ml-2 text-[8px] text-white/30 font-mono truncate">{f.title}</span>
</div>
<img src={f.image} alt={f.title} className="w-full h-auto" />
</div>
)}
</div>
) : (
<div className="rounded-lg overflow-hidden ring-1 ring-gray-200 w-4/5 mx-auto">
<div className="flex items-center gap-0.5 px-2 py-1.5 bg-[#1e293b]">
<span className="w-1 h-1 rounded-full bg-red-400/60" />
<span className="w-1 h-1 rounded-full bg-amber-400/60" />
<span className="w-1 h-1 rounded-full bg-emerald-400/60" />
</div>
<div className="aspect-[4/3] bg-white border border-dashed border-gray-200 flex items-center justify-center">
<span className="text-gray-300 text-sm">暂无截图</span>
</div>
</div>
)}
</div>
</div>
</ScrollReveal>
))}
</div>
{/* ── 移动端:紧凑卡片 ── */}
<div className="lg:hidden flex flex-col gap-4">
{product.features.map((f, i) => (
<ScrollReveal key={i} delay={i * 0.05}>
<div className="bg-white rounded-2xl border border-gray-100 overflow-hidden shadow-[0_1px_8px_-2px_rgba(0,0,0,0.05)]">
<div className="p-5">
<div className="flex items-center gap-3 mb-3">
<span className="shrink-0 flex items-center justify-center w-8 h-8 rounded-lg text-white"
style={{ backgroundColor: product.themeColor }}>
{f.icon && iconMap[f.icon] ? (() => { const Icon = iconMap[f.icon]; return <Icon className="w-4 h-4" strokeWidth={2} /> })() : <span className="text-xs font-black">{String(i + 1).padStart(2, '0')}</span>}
</span>
<span className="text-[10px] font-bold tracking-[0.15em] uppercase text-[var(--theme)]/50">核心功能</span>
</div>
<h3 className="text-lg font-extrabold text-[#0f172a] mb-2 leading-snug">{f.title}</h3>
<p className="text-[#475569] text-sm leading-relaxed">{f.desc}</p>
</div>
{f.image && (
<div className="mx-3 mb-3 bg-[#f8fafc] rounded-xl p-3">
{f.small ? (
<div className="relative mx-auto w-[130px]">
<div className="relative bg-[#1a1a2e] rounded-[16px] p-[3px] shadow-[0_12px_36px_-10px_rgba(0,0,0,0.25)]">
<div className="flex justify-center pt-1">
<div className="flex items-center gap-1">
<span className="w-1 h-1 rounded-full bg-[#0f0f1a]" />
<span className="w-5 h-0.5 bg-[#0f0f1a] rounded-full" />
</div>
</div>
<div className="mt-0.5 rounded-[8px] overflow-hidden bg-white">
<img src={f.image} alt={f.title} className="w-full h-auto object-contain max-h-[240px]" />
</div>
<div className="flex justify-center pt-1 pb-0.5">
<div className="w-4 h-[2px] bg-white/15 rounded-full" />
</div>
</div>
</div>
) : (
<div className="rounded-lg overflow-hidden shadow-[0_4px_16px_-4px_rgba(0,0,0,0.08)] ring-1 ring-black/5 w-4/5 mx-auto">
<div className="flex items-center gap-0.5 px-2 py-1.5 bg-[#1e293b]">
<span className="w-1.5 h-1.5 rounded-full bg-red-400/80" />
<span className="w-1.5 h-1.5 rounded-full bg-amber-400/80" />
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400/80" />
<span className="ml-1.5 text-[7px] text-white/30 font-mono truncate">{f.title}</span>
</div>
<img src={f.image} alt={f.title} className="w-full h-auto" />
</div>
)}
</div>
)}
</div>
</ScrollReveal>
))}
@@ -239,7 +471,7 @@ export default function ProductPage() {
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[var(--theme)]" />
<span className="w-5 h-[2px] rounded-lg bg-[var(--theme)]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">Product Showcase</span>
</div>
<h2 className="text-3xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
@@ -254,15 +486,16 @@ export default function ProductPage() {
)}
{/* ── Advantages ── */}
<section className="py-24 px-6 bg-white">
<section className="py-24 md:py-32 px-6 bg-[#f8fafc]">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[var(--theme)]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">Why Choose Us</span>
<span className="w-8 h-[2px] rounded-full bg-[var(--theme)]/40" />
<span className="text-[11px] font-bold tracking-[0.22em] uppercase text-[var(--theme)]/60">Why Choose Us</span>
<span className="w-8 h-[2px] rounded-full bg-[var(--theme)]/40" />
</div>
<h2 className="text-4xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
<h2 className="text-3xl md:text-[2.75rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
为什么选择我们
</h2>
</div>
@@ -275,21 +508,45 @@ export default function ProductPage() {
{ icon: Headphones, title: '专属服务', desc: '7×24 小时响应,专属客户经理全程陪伴' },
].map((item, i) => (
<ScrollReveal key={i} delay={i * 0.08}>
<div className="relative bg-[#fafbfc] rounded-2xl p-8 md:p-10 h-full border border-gray-100 hover:shadow-xl hover:shadow-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.05)] transition-all duration-500 group overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-0 group-hover:opacity-100 transition-opacity duration-500"
style={{ background: `linear-gradient(90deg, transparent, ${product.themeColor}, transparent)` }} />
<div className="absolute top-0 right-0 w-28 h-28 bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.02)] rounded-bl-full" />
<div className="absolute bottom-0 left-0 w-16 h-16 border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] rounded-tr-3xl" />
<div className="group relative bg-white rounded-2xl p-10 md:p-12 h-full border border-gray-100 shadow-[0_2px_16px_-6px_rgba(0,0,0,0.06)] hover:shadow-[0_12px_40px_-12px_rgba(0,0,0,0.12)] transition-all duration-500 overflow-hidden">
{/* 顶部品牌色条 — hover 时滑入 */}
<div className="absolute top-0 left-0 right-0 h-1 transition-all duration-500"
style={{ background: `linear-gradient(90deg, transparent, ${product.themeColor}, transparent)`, opacity: 0 }}
/>
{/* 底部品牌色条 — 始终可见 */}
<div className="absolute bottom-0 left-0 right-0 h-[3px] transition-all duration-500"
style={{
background: `linear-gradient(90deg, transparent, ${product.themeColor}60, transparent)`,
opacity: 0.5,
}}
/>
<div className="relative z-10">
<div className="flex items-center gap-3 mb-5">
<div className="w-11 h-11 rounded-xl flex items-center justify-center bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)]">
<item.icon className="w-5 h-5" style={{ color: product.themeColor }} />
<div className="relative z-10 flex flex-col items-center text-center">
{/* 图标 — 大圆形容器 */}
<div className="mb-7">
<div className="w-[88px] h-[88px] rounded-2xl flex items-center justify-center transition-all duration-500 group-hover:scale-110 group-hover:shadow-lg"
style={{
background: `linear-gradient(135deg, ${product.themeColor}12, ${product.themeColor}08)`,
border: `1px solid ${product.themeColor}20`,
boxShadow: `0 4px 20px ${product.themeColor}15`,
}}
>
<item.icon className="w-10 h-10 transition-all duration-500 group-hover:scale-110" strokeWidth={1.5} style={{ color: product.themeColor }} />
</div>
<div className="h-px flex-1 bg-gradient-to-r from-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] to-transparent" />
</div>
<h3 className="text-xl font-bold text-[#0f172a] mb-3">{item.title}</h3>
<p className="text-[#64748b] text-lg leading-relaxed">{item.desc}</p>
{/* 标题 */}
<h3 className="text-2xl font-extrabold text-[#0f172a] mb-3 tracking-tight">
{item.title}
</h3>
{/* 分隔线 */}
<div className="w-10 h-[3px] rounded-full mb-4" style={{ backgroundColor: product.themeColor, opacity: 0.3 }} />
{/* 描述 */}
<p className="text-[#64748b] text-lg leading-relaxed">
{item.desc}
</p>
</div>
</div>
</ScrollReveal>
@@ -305,7 +562,7 @@ export default function ProductPage() {
<ScrollReveal className="mb-14 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[var(--theme)]" />
<span className="w-5 h-[2px] rounded-lg bg-[var(--theme)]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">FAQ</span>
</div>
<h2 className="text-4xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
@@ -325,7 +582,7 @@ export default function ProductPage() {
<span className="flex items-center justify-center w-8 h-8 rounded-lg bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] text-xs font-black text-[var(--theme)] shrink-0">Q{i + 1}</span>
<h3 className="text-lg font-bold text-[#0f172a]">{item.q}</h3>
</div>
<div className="w-8 h-8 rounded-full bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] flex items-center justify-center shrink-0 group-open:rotate-180 transition-transform duration-300">
<div className="w-8 h-8 rounded-lg bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] flex items-center justify-center shrink-0 group-open:rotate-180 transition-transform duration-300">
<ChevronDown className="w-4 h-4 text-[var(--theme)]" />
</div>
</summary>