Initial commit
9
audi-red-note-mini-app/.env.example
Normal file
@@ -0,0 +1,9 @@
|
||||
# GEMINI_API_KEY: Required for Gemini AI API calls.
|
||||
# AI Studio automatically injects this at runtime from user secrets.
|
||||
# Users configure this via the Secrets panel in the AI Studio UI.
|
||||
GEMINI_API_KEY="MY_GEMINI_API_KEY"
|
||||
|
||||
# APP_URL: The URL where this applet is hosted.
|
||||
# AI Studio automatically injects this at runtime with the Cloud Run service URL.
|
||||
# Used for self-referential links, OAuth callbacks, and API endpoints.
|
||||
APP_URL="MY_APP_URL"
|
||||
8
audi-red-note-mini-app/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
node_modules/
|
||||
build/
|
||||
dist/
|
||||
coverage/
|
||||
.DS_Store
|
||||
*.log
|
||||
.env*
|
||||
!.env.example
|
||||
20
audi-red-note-mini-app/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
<div align="center">
|
||||
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
|
||||
</div>
|
||||
|
||||
# Run and deploy your AI Studio app
|
||||
|
||||
This contains everything you need to run your app locally.
|
||||
|
||||
View your app in AI Studio: https://ai.studio/apps/546f4d9a-c8ab-40ae-a5f0-bdd95abfca93
|
||||
|
||||
## Run Locally
|
||||
|
||||
**Prerequisites:** Node.js
|
||||
|
||||
|
||||
1. Install dependencies:
|
||||
`npm install`
|
||||
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
|
||||
3. Run the app:
|
||||
`npm run dev`
|
||||
13
audi-red-note-mini-app/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>My Google AI Studio App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
5
audi-red-note-mini-app/metadata.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Audi Red Note Mini App",
|
||||
"description": "A high-fidelity Audi brand experience for car exploration and lead generation.",
|
||||
"requestFramePermissions": []
|
||||
}
|
||||
3992
audi-red-note-mini-app/package-lock.json
generated
Normal file
34
audi-red-note-mini-app/package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "react-example",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port=3000 --host=0.0.0.0",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"clean": "rm -rf dist",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@google/genai": "^1.29.0",
|
||||
"@tailwindcss/vite": "^4.1.14",
|
||||
"@vitejs/plugin-react": "^5.0.4",
|
||||
"lucide-react": "^0.546.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"vite": "^6.2.0",
|
||||
"express": "^4.21.2",
|
||||
"dotenv": "^17.2.3",
|
||||
"motion": "^12.23.24"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.14.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "~5.8.2",
|
||||
"vite": "^6.2.0",
|
||||
"@types/express": "^4.17.21"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 111 KiB |
463
audi-red-note-mini-app/src/App.tsx
Normal file
@@ -0,0 +1,463 @@
|
||||
/**
|
||||
* @license
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { useState, useEffect, FormEvent } from "react";
|
||||
import { motion, AnimatePresence } from "motion/react";
|
||||
import {
|
||||
ChevronRight,
|
||||
X,
|
||||
Check,
|
||||
Phone,
|
||||
User,
|
||||
MapPin,
|
||||
ShieldCheck,
|
||||
ArrowLeft,
|
||||
Loader2
|
||||
} from "lucide-react";
|
||||
|
||||
// --- Types ---
|
||||
|
||||
interface CarModel {
|
||||
id: string;
|
||||
name: string;
|
||||
price: string;
|
||||
image: string;
|
||||
tagline: string;
|
||||
}
|
||||
|
||||
const CAR_MODELS: CarModel[] = [
|
||||
{
|
||||
id: "a3",
|
||||
name: "Audi A3 Sportback",
|
||||
price: "203,100",
|
||||
image: "https://images.unsplash.com/photo-1606152421802-db97b9c7a11b?auto=format&fit=crop&q=80&w=800",
|
||||
tagline: "进取,不负期待"
|
||||
},
|
||||
{
|
||||
id: "a4l",
|
||||
name: "Audi A4L",
|
||||
price: "321,800",
|
||||
image: "https://images.unsplash.com/photo-1614162692292-7ac56d7f7f1e?auto=format&fit=crop&q=80&w=800",
|
||||
tagline: "做更强大的自己"
|
||||
},
|
||||
{
|
||||
id: "a6l",
|
||||
name: "Audi A6L",
|
||||
price: "427,900",
|
||||
image: "https://images.unsplash.com/photo-1541348263662-e0c86433610a?auto=format&fit=crop&q=80&w=800",
|
||||
tagline: "懂你,更懂未来"
|
||||
},
|
||||
{
|
||||
id: "q3",
|
||||
name: "Audi Q3",
|
||||
price: "279,800",
|
||||
image: "https://images.unsplash.com/photo-1533473359331-0135ef1b58bf?auto=format&fit=crop&q=80&w=800",
|
||||
tagline: "活出生命的辽阔"
|
||||
},
|
||||
{
|
||||
id: "q5l",
|
||||
name: "Audi Q5L",
|
||||
price: "396,800",
|
||||
image: "https://images.unsplash.com/photo-1566473065146-d215f068671b?auto=format&fit=crop&q=80&w=800",
|
||||
tagline: "自由,由我定义"
|
||||
},
|
||||
{
|
||||
id: "etron-gt",
|
||||
name: "Audi e-tron GT",
|
||||
price: "999,800",
|
||||
image: "https://images.unsplash.com/photo-1617469767053-d3b523a0b982?auto=format&fit=crop&q=80&w=800",
|
||||
tagline: "静谧,亦能澎湃"
|
||||
}
|
||||
];
|
||||
|
||||
const CITIES = ["北京", "上海", "广州", "深圳", "杭州", "成都", "南京", "武汉"];
|
||||
|
||||
// --- Components ---
|
||||
|
||||
const Header = ({ onBack, showBack }: { onBack?: () => void; showBack?: boolean }) => (
|
||||
<header className="sticky top-0 z-40 bg-white/80 backdrop-blur-md border-b border-gray-100 px-6 py-4 flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
{showBack && (
|
||||
<button onClick={onBack} className="p-1 -ml-2 hover:bg-gray-100 rounded-full transition-colors">
|
||||
<ArrowLeft size={24} />
|
||||
</button>
|
||||
)}
|
||||
<div className="flex flex-col">
|
||||
<span className="text-xl font-bold tracking-[0.2em] uppercase">Audi</span>
|
||||
<span className="text-[10px] text-gray-400 tracking-widest uppercase -mt-1">Vorsprung durch Technik</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<div className="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center">
|
||||
<User size={16} className="text-gray-600" />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
|
||||
const PIPLPopup = ({ onAccept }: { onAccept: () => void }) => (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 z-50 flex items-center justify-center p-6 bg-black/60 backdrop-blur-sm"
|
||||
>
|
||||
<motion.div
|
||||
initial={{ scale: 0.9, opacity: 0, y: 20 }}
|
||||
animate={{ scale: 1, opacity: 1, y: 0 }}
|
||||
className="bg-white w-full max-w-md p-8 shadow-2xl"
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<ShieldCheck className="text-audi-red" size={32} />
|
||||
<h2 className="text-2xl font-bold tracking-tight">个人信息保护提示</h2>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 text-gray-600 text-sm leading-relaxed mb-8">
|
||||
<p>
|
||||
欢迎访问奥迪官方预约系统。在您提交信息前,请仔细阅读并同意我们的《个人信息保护政策》(PIPL)。
|
||||
</p>
|
||||
<p>
|
||||
我们将收集您的<b>姓名、手机号码及所在城市</b>,用于为您安排试驾、发送购车优惠及提供售后咨询服务。
|
||||
</p>
|
||||
<p>
|
||||
我们承诺将严格遵守相关法律法规,保护您的个人隐私安全。未经您的许可,我们不会将您的信息提供给第三方。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={onAccept}
|
||||
className="audi-button w-full flex items-center justify-center gap-2"
|
||||
>
|
||||
同意并继续 <ChevronRight size={18} />
|
||||
</button>
|
||||
|
||||
<p className="text-center text-[10px] text-gray-400 mt-4 uppercase tracking-widest">
|
||||
Audi Privacy Compliance
|
||||
</p>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
export default function App() {
|
||||
const [currentPage, setCurrentPage] = useState<"gallery" | "form">("gallery");
|
||||
const [selectedCar, setSelectedCar] = useState<CarModel | null>(null);
|
||||
const [showPIPL, setShowPIPL] = useState(true);
|
||||
const [piplAccepted, setPiplAccepted] = useState(false);
|
||||
|
||||
// Form State
|
||||
const [formData, setFormData] = useState({
|
||||
name: "",
|
||||
phone: "",
|
||||
city: "",
|
||||
agreed: false
|
||||
});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isSuccess, setIsSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
|
||||
const handleLearnMore = (car: CarModel) => {
|
||||
setSelectedCar(car);
|
||||
setCurrentPage("form");
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
setCurrentPage("gallery");
|
||||
setIsSuccess(false);
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
if (!formData.name.trim()) newErrors.name = "请输入姓名";
|
||||
if (!/^1[3-9]\d{9}$/.test(formData.phone)) newErrors.phone = "请输入有效的手机号";
|
||||
if (!formData.city) newErrors.city = "请选择城市";
|
||||
if (!formData.agreed) newErrors.agreed = "请阅读并同意隐私政策";
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!validateForm()) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
setIsSubmitting(false);
|
||||
setIsSuccess(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white selection:bg-audi-red selection:text-white">
|
||||
<AnimatePresence>
|
||||
{showPIPL && <PIPLPopup onAccept={() => { setShowPIPL(false); setPiplAccepted(true); }} />}
|
||||
</AnimatePresence>
|
||||
|
||||
<Header
|
||||
showBack={currentPage === "form"}
|
||||
onBack={handleBack}
|
||||
/>
|
||||
|
||||
<main className="max-w-screen-xl mx-auto">
|
||||
<AnimatePresence mode="wait">
|
||||
{currentPage === "gallery" ? (
|
||||
<motion.section
|
||||
key="gallery"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
className="px-6 py-12"
|
||||
>
|
||||
<div className="mb-12">
|
||||
<h1 className="text-4xl md:text-6xl font-black tracking-tighter uppercase mb-4">
|
||||
奥迪车型中心
|
||||
</h1>
|
||||
<p className="text-gray-500 max-w-2xl text-lg">
|
||||
探索奥迪全系车型,感受前瞻科技与极致工艺的完美融合。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{CAR_MODELS.map((car) => (
|
||||
<motion.div
|
||||
key={car.id}
|
||||
layoutId={car.id}
|
||||
className="audi-card group"
|
||||
>
|
||||
<div className="relative aspect-[16/10] overflow-hidden">
|
||||
<img
|
||||
src={car.image}
|
||||
alt={car.name}
|
||||
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<h3 className="text-xl font-bold tracking-tight">{car.name}</h3>
|
||||
<span className="text-audi-red font-mono text-sm">CNY {car.price} 起</span>
|
||||
</div>
|
||||
<p className="text-gray-400 text-sm mb-6">{car.tagline}</p>
|
||||
<button
|
||||
onClick={() => handleLearnMore(car)}
|
||||
className="audi-button-outline w-full flex items-center justify-center gap-2 group/btn"
|
||||
>
|
||||
立即预约 <ChevronRight size={18} className="transition-transform group-hover/btn:translate-x-1" />
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.section>
|
||||
) : (
|
||||
<motion.section
|
||||
key="form"
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -20 }}
|
||||
className="px-6 py-12 lg:flex lg:gap-16 lg:items-start"
|
||||
>
|
||||
{/* Hero Image Section */}
|
||||
<div className="lg:w-1/2 mb-12 lg:mb-0 lg:sticky lg:top-32">
|
||||
<div className="relative rounded-lg overflow-hidden shadow-2xl">
|
||||
<img
|
||||
src={selectedCar?.image}
|
||||
alt={selectedCar?.name}
|
||||
className="w-full aspect-video object-cover"
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
<div className="absolute bottom-0 left-0 right-0 p-8 bg-gradient-to-t from-black/80 to-transparent text-white">
|
||||
<h2 className="text-3xl font-bold mb-2">{selectedCar?.name}</h2>
|
||||
<p className="text-gray-300 opacity-80">{selectedCar?.tagline}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-8 grid grid-cols-3 gap-4">
|
||||
{[1, 2, 3].map(i => (
|
||||
<div key={i} className="aspect-square bg-gray-100 rounded overflow-hidden">
|
||||
<img
|
||||
src={`https://picsum.photos/seed/audi-${selectedCar?.id}-${i}/400/400`}
|
||||
alt="Detail"
|
||||
className="w-full h-full object-cover opacity-60 hover:opacity-100 transition-opacity"
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Form Section */}
|
||||
<div className="lg:w-1/2 max-w-md">
|
||||
{isSuccess ? (
|
||||
<motion.div
|
||||
initial={{ scale: 0.9, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
className="text-center py-12"
|
||||
>
|
||||
<div className="w-20 h-20 bg-green-50 text-green-500 rounded-full flex items-center justify-center mx-auto mb-6">
|
||||
<Check size={40} />
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold mb-4">预约成功</h2>
|
||||
<p className="text-gray-500 mb-8">
|
||||
您的预约信息已收到。我们的专属顾问将在 24 小时内与您联系,为您安排试驾体验。
|
||||
</p>
|
||||
<button onClick={handleBack} className="audi-button w-full">
|
||||
返回车型中心
|
||||
</button>
|
||||
</motion.div>
|
||||
) : (
|
||||
<>
|
||||
<div className="mb-10">
|
||||
<h2 className="text-3xl font-bold tracking-tight mb-2">预约试驾</h2>
|
||||
<p className="text-gray-500">请填写以下信息,开启您的奥迪之旅。</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-8">
|
||||
<div className="space-y-1">
|
||||
<label className="text-[10px] uppercase tracking-widest text-gray-400 font-bold">姓名</label>
|
||||
<div className="relative">
|
||||
<User className="absolute left-0 top-1/2 -translate-y-1/2 text-gray-300" size={18} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="请输入您的真实姓名"
|
||||
className={`audi-input pl-8 ${errors.name ? 'border-audi-red' : ''}`}
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({...formData, name: e.target.value})}
|
||||
/>
|
||||
</div>
|
||||
{errors.name && <p className="text-audi-red text-xs mt-1">{errors.name}</p>}
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<label className="text-[10px] uppercase tracking-widest text-gray-400 font-bold">手机号码</label>
|
||||
<div className="relative">
|
||||
<Phone className="absolute left-0 top-1/2 -translate-y-1/2 text-gray-300" size={18} />
|
||||
<input
|
||||
type="tel"
|
||||
placeholder="1xx xxxx xxxx"
|
||||
className={`audi-input pl-8 ${errors.phone ? 'border-audi-red' : ''}`}
|
||||
value={formData.phone}
|
||||
onChange={(e) => setFormData({...formData, phone: e.target.value})}
|
||||
/>
|
||||
</div>
|
||||
{errors.phone && <p className="text-audi-red text-xs mt-1">{errors.phone}</p>}
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<label className="text-[10px] uppercase tracking-widest text-gray-400 font-bold">意向城市</label>
|
||||
<div className="relative">
|
||||
<MapPin className="absolute left-0 top-1/2 -translate-y-1/2 text-gray-300" size={18} />
|
||||
<select
|
||||
className={`audi-input pl-8 appearance-none ${errors.city ? 'border-audi-red' : ''}`}
|
||||
value={formData.city}
|
||||
onChange={(e) => setFormData({...formData, city: e.target.value})}
|
||||
>
|
||||
<option value="">请选择所在城市</option>
|
||||
{CITIES.map(city => <option key={city} value={city}>{city}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
{errors.city && <p className="text-audi-red text-xs mt-1">{errors.city}</p>}
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="relative flex items-center h-5">
|
||||
<input
|
||||
id="pipl"
|
||||
type="checkbox"
|
||||
className="w-4 h-4 text-audi-black border-gray-300 rounded focus:ring-audi-black"
|
||||
checked={formData.agreed}
|
||||
onChange={(e) => setFormData({...formData, agreed: e.target.checked})}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 leading-relaxed">
|
||||
<label htmlFor="pipl">
|
||||
我已阅读并同意奥迪的 <span className="text-audi-black underline cursor-pointer">《个人信息保护政策》</span> 和 <span className="text-audi-black underline cursor-pointer">《隐私条款》</span>。本人同意奥迪及其授权经销商与我联系。
|
||||
</label>
|
||||
{errors.agreed && <p className="text-audi-red text-xs mt-1">{errors.agreed}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="audi-button w-full flex items-center justify-center gap-3 h-14"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Loader2 className="animate-spin" size={20} />
|
||||
提交中...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
立即提交预约 <ChevronRight size={20} />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</motion.section>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</main>
|
||||
|
||||
<footer className="bg-audi-black text-white py-16 px-6 mt-20">
|
||||
<div className="max-w-screen-xl mx-auto flex flex-col md:flex-row justify-between items-start gap-12">
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-3xl font-bold tracking-[0.3em] uppercase">Audi</span>
|
||||
<span className="text-xs text-gray-500 tracking-[0.5em] uppercase">Vorsprung durch Technik</span>
|
||||
</div>
|
||||
<p className="text-gray-500 text-sm max-w-xs">
|
||||
突破科技,启迪未来。奥迪致力于为您提供卓越的驾驶体验与前瞻的移动出行方案。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-12">
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-xs font-bold uppercase tracking-widest">车型</h4>
|
||||
<ul className="text-gray-500 text-sm space-y-2">
|
||||
<li>轿车</li>
|
||||
<li>SUV</li>
|
||||
<li>e-tron 电动</li>
|
||||
<li>Audi Sport</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-xs font-bold uppercase tracking-widest">服务</h4>
|
||||
<ul className="text-gray-500 text-sm space-y-2">
|
||||
<li>预约试驾</li>
|
||||
<li>经销商查询</li>
|
||||
<li>售后服务</li>
|
||||
<li>金融方案</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-xs font-bold uppercase tracking-widest">关于</h4>
|
||||
<ul className="text-gray-500 text-sm space-y-2">
|
||||
<li>品牌故事</li>
|
||||
<li>新闻中心</li>
|
||||
<li>联系我们</li>
|
||||
<li>加入我们</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-screen-xl mx-auto mt-20 pt-8 border-t border-white/10 flex flex-col md:flex-row justify-between gap-6 text-[10px] text-gray-600 uppercase tracking-widest">
|
||||
<p>© 2026 一汽奥迪销售有限责任公司 版权所有</p>
|
||||
<div className="flex gap-6">
|
||||
<span className="hover:text-white cursor-pointer transition-colors">隐私政策</span>
|
||||
<span className="hover:text-white cursor-pointer transition-colors">法律声明</span>
|
||||
<span className="hover:text-white cursor-pointer transition-colors">京ICP备00000000号</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
31
audi-red-note-mini-app/src/index.css
Normal file
@@ -0,0 +1,31 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||
--color-audi-red: #BB0A30;
|
||||
--color-audi-black: #000000;
|
||||
--color-audi-silver: #F0F0F0;
|
||||
--color-audi-dark-gray: #333333;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply bg-white text-audi-black font-sans antialiased;
|
||||
}
|
||||
}
|
||||
|
||||
.audi-button {
|
||||
@apply px-6 py-3 bg-audi-black text-white font-medium uppercase tracking-wider transition-all duration-300 hover:bg-audi-dark-gray active:scale-95 disabled:opacity-50 disabled:pointer-events-none;
|
||||
}
|
||||
|
||||
.audi-button-outline {
|
||||
@apply px-6 py-3 border border-audi-black text-audi-black font-medium uppercase tracking-wider transition-all duration-300 hover:bg-audi-black hover:text-white active:scale-95;
|
||||
}
|
||||
|
||||
.audi-input {
|
||||
@apply w-full px-4 py-3 border-b border-gray-300 focus:border-audi-black outline-none transition-colors duration-300 bg-transparent;
|
||||
}
|
||||
|
||||
.audi-card {
|
||||
@apply bg-white border border-gray-100 overflow-hidden transition-all duration-500 hover:shadow-2xl hover:-translate-y-1;
|
||||
}
|
||||
10
audi-red-note-mini-app/src/main.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import {StrictMode} from 'react';
|
||||
import {createRoot} from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import './index.css';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
);
|
||||
26
audi-red-note-mini-app/tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": false,
|
||||
"module": "ESNext",
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"allowJs": true,
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
},
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
24
audi-red-note-mini-app/vite.config.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
import {defineConfig, loadEnv} from 'vite';
|
||||
|
||||
export default defineConfig(({mode}) => {
|
||||
const env = loadEnv(mode, '.', '');
|
||||
return {
|
||||
plugins: [react(), tailwindcss()],
|
||||
define: {
|
||||
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, '.'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
// HMR is disabled in AI Studio via DISABLE_HMR env var.
|
||||
// Do not modifyâfile watching is disabled to prevent flickering during agent edits.
|
||||
hmr: process.env.DISABLE_HMR !== 'true',
|
||||
},
|
||||
};
|
||||
});
|
||||