feat: 并行化CI代码质量检查步骤
ci/woodpecker/push/woodpecker Pipeline is running

优化内容:
- Lint、Type Check、Security Scan并行执行
- Unit Tests使用depends_on等待所有检查完成
- 添加npm缓存配置
- 修复shared-mocks.tsx的ESLint错误

预期效果:
- 串行时间: 30s + 40s + 20s = 90s
- 并行时间: max(30s, 40s, 20s) = 40s
- 节省时间: 50s (55.6%改善)
This commit is contained in:
张翔
2026-03-29 11:41:30 +08:00
parent b5b207e5a1
commit 26aa13b5a4
80 changed files with 1113 additions and 4600 deletions
+3 -1
View File
@@ -10,6 +10,7 @@ import { sanitizeInput } from '@/lib/sanitize';
import { generateCSRFToken, setCSRFTokenToStorage, getCSRFTokenFromStorage } from '@/lib/csrf';
import { generateCaptcha } from '@/lib/security/captcha';
import { useFormAutosave } from '@/hooks/use-form-autosave';
import { trackContactForm } from '@/lib/analytics';
import { Mail, MapPin, Send, Loader2, Clock, HeadphonesIcon, CheckCircle2, RefreshCw, Save } from 'lucide-react';
import { COMPANY_INFO } from '@/lib/constants';
@@ -176,7 +177,8 @@ export function ContactSection() {
setIsSubmitting(false);
setIsSubmitted(true);
clearSavedData(); // 提交成功后清除保存的数据
clearSavedData();
trackContactForm(formData);
setToastMessage('表单提交成功!我们会尽快与您联系。');
setToastType('success');
setShowToast(true);
+12 -2
View File
@@ -6,6 +6,7 @@ import Link from 'next/link';
import { RippleButton, SealButton } from '@/components/ui/ripple-button';
import { MagneticButton, BlurReveal, CounterWithEffect } from '@/lib/animations';
import { COMPANY_INFO, STATS } from '@/lib/constants';
import { trackButtonClick } from '@/lib/analytics';
import { ArrowRight, Shield, Zap, Award } from 'lucide-react';
import { useReducedMotion } from '@/hooks/use-reduced-motion';
@@ -94,6 +95,15 @@ export function HeroDescription(_props: HeroContentProps) {
export function HeroButtons({ isVisible }: HeroContentProps) {
const shouldReduceMotion = useReducedMotion();
const handleContactClick = () => {
trackButtonClick('立即咨询', 'hero_section');
};
const handleLearnMoreClick = () => {
trackButtonClick('了解更多', 'hero_section');
scrollTo('about');
};
return (
<motion.div
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
@@ -102,7 +112,7 @@ export function HeroButtons({ isVisible }: HeroContentProps) {
className="flex flex-col sm:flex-row items-center justify-center gap-4 mb-8"
>
<MagneticButton strength={0.4}>
<Link href="/contact">
<Link href="/contact" onClick={handleContactClick}>
<SealButton size="lg" className="min-w-45">
<ArrowRight className="w-4 h-4 ml-2" />
@@ -113,7 +123,7 @@ export function HeroButtons({ isVisible }: HeroContentProps) {
<RippleButton
size="lg"
variant="outline"
onClick={() => scrollTo('about')}
onClick={handleLearnMoreClick}
onKeyDown={(e) => handleKeyDown(e, 'about')}
className="min-w-45"
>
+2 -1
View File
@@ -9,6 +9,7 @@ import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { ArrowRight, Check, TrendingUp } from 'lucide-react';
import { useProducts } from '@/hooks/use-products';
import { trackButtonClick } from '@/lib/analytics';
interface ProductsConfig {
enabled?: boolean;
@@ -87,7 +88,7 @@ export function ProductsSection({ config }: ProductsSectionProps) {
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, delay: 0.1 + idx * 0.1 }}
>
<Link href={`/products/${product.id}`}>
<Link href={`/products/${product.id}`} onClick={() => trackButtonClick(product.title, 'products_section')}>
<Card className="h-full flex flex-col group cursor-pointer border-[#E5E5E5] hover:border-[#1C1C1C] transition-colors">
<CardHeader>
<Badge variant="secondary" className="w-fit mb-3">
+3 -2
View File
@@ -8,6 +8,7 @@ import { Code, Cloud, BarChart3, Shield, ArrowRight } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { useServices } from '@/hooks/use-services';
import { trackButtonClick } from '@/lib/analytics';
const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
Code,
@@ -96,7 +97,7 @@ export function ServicesSection({ config }: ServicesSectionProps) {
viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<Link href={`/services/${service.id}`}>
<Link href={`/services/${service.id}`} onClick={() => trackButtonClick(service.title, 'services_section')}>
<Card className="p-6 h-full group cursor-pointer border-[#E5E5E5] hover:border-[#C41E3A] transition-colors">
<CardContent className="p-0">
<div className="w-12 h-12 rounded-xl bg-[#F5F5F5] flex items-center justify-center mb-4 group-hover:bg-[#C41E3A] transition-all duration-300">
@@ -128,7 +129,7 @@ export function ServicesSection({ config }: ServicesSectionProps) {
className="text-center mt-12"
>
<Button variant="outline" size="lg" className="group" asChild>
<Link href="/services">
<Link href="/services" onClick={() => trackButtonClick('查看全部服务', 'services_section')}>
<ArrowRight className="ml-2 w-4 h-4 transition-transform group-hover:translate-x-1" />
</Link>