第二次修改风格
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 332 KiB |
|
After Width: | Height: | Size: 461 KiB |
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 597 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 197 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 253 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 252 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 133 KiB |
@@ -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>
|
||||
|
||||
@@ -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 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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: [
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
|
||||