菜单添加页面模板管理页面,增加一个纯图平铺模板,优化预览手机屏效果

This commit is contained in:
2026-04-17 08:35:45 +08:00
parent 9a6418eb93
commit 052195abab
36 changed files with 1534 additions and 708 deletions

View 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-app3/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
node_modules/
build/
dist/
coverage/
.DS_Store
*.log
.env*
!.env.example

View 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/8100d767-eabc-4053-8910-92a8a5c2aba8
## 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`

View 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>

View File

@@ -0,0 +1,6 @@
{
"name": "E7X Details Page",
"description": "High-fidelity mobile landing page clone for the E7X car model.",
"requestFramePermissions": [],
"majorCapabilities": []
}

View 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"
}
}

View File

@@ -0,0 +1,146 @@
import { ChevronLeft, MoreHorizontal, X } from "lucide-react";
import { motion } from "motion/react";
export default function App() {
return (
<div className="flex flex-col min-h-screen bg-black font-sans scroll-smooth">
{/* Status Bar Mockup */}
<div className="fixed top-0 left-0 right-0 h-11 flex justify-between items-end px-8 z-50 pointer-events-none">
<span className="text-[15px] font-semibold text-white tracking-tight">9:41</span>
<div className="flex items-center gap-1.5 pb-1">
<svg width="17" height="11" viewBox="0 0 17 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 8.35L2.83 8.35L2.83 10.5H0V8.35ZM4.25 6.2H7.08V10.5H4.25V6.2ZM8.5 4.05H11.33V10.5H8.5V4.05ZM12.75 1.9H15.58V10.5H12.75V1.9Z" fill="white"/>
</svg>
<svg width="16" height="12" viewBox="0 0 16 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 12L16 3.10931C11.5768 -0.493103 4.42323 -0.493103 0 3.10931L8 12Z" fill="white"/>
</svg>
<div className="w-[24.33px] h-[11.33px] border border-white/35 rounded-[3px] p-[1px] relative">
<div className="bg-white h-full w-[18px] rounded-[1px]" />
<div className="absolute top-[3px] -right-[3px] w-[2px] h-[5px] bg-white/35 rounded-r-[1px]" />
</div>
</div>
</div>
{/* Main Header */}
<header className="fixed top-0 left-0 right-0 h-24 pt-11 flex items-center justify-between px-4 bg-black/60 backdrop-blur-md z-40">
<button className="p-2 transition-opacity active:opacity-50">
<ChevronLeft className="w-6 h-6" />
</button>
<h1 className="text-lg font-medium tracking-wide">E7X详情</h1>
<div className="flex items-center bg-black/40 border border-white/10 rounded-full h-8 px-1">
<button className="px-2 border-r border-white/10">
<MoreHorizontal className="w-4 h-4" />
</button>
<button className="px-2">
<X className="w-4 h-4" />
</button>
</div>
</header>
{/* Content */}
<main className="flex-1 mt-24 mb-24 overflow-y-auto">
{/* Section 1: Side view & Metrics */}
<section className="relative overflow-hidden">
<motion.img
initial={{ opacity: 0, scale: 1.05 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8 }}
src="https://picsum.photos/seed/e7x-side/1200/900"
alt="E7X Side Profile"
className="w-full object-cover aspect-[4/3]"
referrerPolicy="no-referrer"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black via-transparent to-transparent opacity-60" />
{/* Overlaid Metrics */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4, duration: 0.6 }}
className="absolute bottom-6 left-0 right-0 grid grid-cols-4 px-4 text-center"
>
{[
{ val: "5,049", unit: "mm", label: "车长" },
{ val: "1,997", unit: "mm", label: "车宽" },
{ val: "1,710", unit: "mm", label: "车高" },
{ val: "3,060", unit: "mm", label: "轴距" },
].map((metric, i) => (
<div key={i} className="flex flex-col gap-0.5">
<div className="flex items-baseline justify-center">
<span className="text-lg font-bold leading-none">{metric.val}</span>
<span className="text-[10px] ml-0.5 leading-none font-medium opacity-80">{metric.unit}</span>
</div>
<span className="text-[10px] text-gray-400 font-normal">{metric.label}</span>
</div>
))}
</motion.div>
</section>
{/* Section 2: Front view */}
<section className="bg-black">
<motion.img
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
src="https://picsum.photos/seed/e7x-front/1200/900"
alt="E7X Front Design"
className="w-full object-cover aspect-[4/3]"
referrerPolicy="no-referrer"
/>
</section>
{/* Section 3: Text content & Detailed view */}
<motion.section
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8 }}
className="relative pt-16 pb-24 bg-black flex flex-col items-center"
>
<h2 className="text-3xl font-medium tracking-tight mb-8"></h2>
<div className="flex flex-col items-center gap-3 text-gray-300 text-[15px] font-light text-center">
<p className="tracking-[0.15em]"></p>
<p className="tracking-[0.15em]"></p>
</div>
<img
src="https://picsum.photos/seed/e7x-detail/1200/600"
alt="Detail"
className="w-full mt-16 object-cover"
referrerPolicy="no-referrer"
/>
</motion.section>
{/* Section 4: Extra Detail Image */}
<section className="-mt-12 bg-black">
<motion.img
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
src="https://picsum.photos/seed/e7x-rear/1200/900"
alt="E7X Rear"
className="w-full object-cover"
referrerPolicy="no-referrer"
/>
</section>
</main>
{/* Fixed Footer */}
<footer className="fixed bottom-0 left-0 right-0 px-6 py-6 bg-black z-50">
<motion.button
whileHover={{ scale: 1.01 }}
whileTap={{ scale: 0.98 }}
className="w-full py-4 bg-transparent border border-white text-white text-lg font-medium transition-colors hover:bg-white/5 active:bg-white/10"
>
</motion.button>
{/* iPhone indicator bar mockup */}
<div className="flex justify-center mt-6">
<div className="w-32 h-1.5 bg-white/20 rounded-full" />
</div>
</footer>
</div>
);
}

View File

@@ -0,0 +1,12 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
@import "tailwindcss";
@theme {
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
}
@layer base {
body {
@apply bg-black text-white antialiased overflow-x-hidden;
}
}

View 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>,
);

View 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
}
}

View 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',
},
};
});