1
This commit is contained in:
342
cursor.md
342
cursor.md
@ -1,178 +1,204 @@
|
||||
# AI Code Generation Prompts: Startup Website Clone
|
||||
**Tech Stack:** Vite + Vue 3 (Script Setup) + Tailwind CSS + Lucide Icons
|
||||
# Web3D 场景 SDK 使用说明
|
||||
|
||||
本 SDK 用于在 Web 场景中快速完成三维展示与交互,核心能力包括:相机修改、灯光调参、环境贴图切换、按配置渲染热点、热点驱动抛出事件、模型热加载/热销毁/热替换,以及按部件切换材质。
|
||||
|
||||
---
|
||||
|
||||
## 🟢 Step 0: Project Setup (Global System Prompt)
|
||||
*在开始编写具体页面前,先发送这条指令,建立全局的设计规范和技术栈。*
|
||||
|
||||
**Prompt:**
|
||||
|
||||
> **Role:** Senior Frontend Developer specializing in Pixel-Perfect UI cloning.
|
||||
>
|
||||
> **Tech Stack Requirements:**
|
||||
> - **Framework:** Vue 3 (Composition API with `<script setup>`) + typescript.
|
||||
> - **Build Tool:** Vite.
|
||||
> - **Styling:** Tailwind CSS (Mobile-first approach).
|
||||
> - **Icons:** `lucide-vue-next` (You must use this library).
|
||||
> - **Language:** TypeScript.
|
||||
>
|
||||
> **Design System (Strict Adherence):**
|
||||
> - **Visual Style:** Minimalist, "Vercel-like" aesthetic. High contrast, clean lines.
|
||||
> - **Typography:** Inter or system-ui. Headings must use `tracking-tight` (letter-spacing: -0.025em).
|
||||
> - **Colors:**
|
||||
> - Background: `#ffffff` (White).
|
||||
> - Surface/Card: `#ffffff` (White) with `border-gray-200`.
|
||||
> - Primary Text: `#0f172a` (Slate-900).
|
||||
> - Muted Text: `#64748b` (Slate-500).
|
||||
> - Accents: Black buttons, Light gray backgrounds (`bg-gray-50`) for secondary elements.
|
||||
> - **Borders & Radius:** Delicate borders (`border`, `border-gray-100`). Card radius is `rounded-xl` or `rounded-2xl`.
|
||||
> - **Shadows:** Very subtle `shadow-sm`, hover states use `shadow-md`.
|
||||
>
|
||||
> **Instruction:** I will provide tasks to build 3 specific pages. You must replicate the layout, spacing, and Chinese copy exactly as described.
|
||||
## 功能概览
|
||||
- 相机控制:支持位置/目标点/FOV/远近裁剪的即时更新,带缓动动画。
|
||||
- 灯光调参:主光、辅光、环境光强度、颜色、方向、阴影开关与软硬度。
|
||||
- 环境贴图:HDRI 切换、曝光度、旋转(偏航/俯仰)、模糊度。
|
||||
- 热点系统:基于配置渲染热点 UI,可按 click/hover/focus 抛出事件。
|
||||
- 模型热更新:按 ID 进行加载、销毁、替换,支持队列并行与完成回调。
|
||||
- 材质切换:按部件或分组应用/还原材质,支持预设表与回滚。
|
||||
- 事件总线:统一事件接口,便于业务侧订阅热点/相机/模型状态。
|
||||
|
||||
---
|
||||
|
||||
## 🟡 Step 1: Pricing Page (定价页)
|
||||
*复制此段生成定价页面组件。*
|
||||
## 快速开始
|
||||
```bash
|
||||
npm install your-web3d-sdk
|
||||
```
|
||||
|
||||
**Prompt:**
|
||||
```ts
|
||||
import { createViewer } from 'your-web3d-sdk'
|
||||
|
||||
> **Task:** Create a **`PricingPage.vue`** component. Clone the provided "Pricing" design exactly.
|
||||
>
|
||||
> **1. Page Header:**
|
||||
> - Layout: Centered.
|
||||
> - Overline: "PRICING" (uppercase, tracking-widest, text-xs, text-gray-400).
|
||||
> - Title: "产品购买页面" (text-3xl font-bold tracking-tight).
|
||||
> - Subtitle: "按需套餐付费,尊享 Codex 与 Claude Code 两大产品线" (text-gray-500 mt-2).
|
||||
>
|
||||
> **2. Section 1: Codex Plans (2-Column Grid):**
|
||||
> - **Heading:** "Codex 套餐" (Bold, text-lg, mb-4).
|
||||
> - **Layout:** Grid `grid-cols-1 md:grid-cols-2 gap-6`.
|
||||
> - **Card Design:** `bg-white border border-gray-200 rounded-xl p-6 relative`.
|
||||
> - **Left Card (Standard):**
|
||||
> - Top: Icon + "Codex" + "codex标准套餐". Badge: "工效效率" (Top Right, gray bg, text-xs).
|
||||
> - Price: "¥79.9/月" (text-4xl font-bold). Subtext: "性价比首选 | 强大智能".
|
||||
> - Features: List with blue bullet points (daily budget $90, etc.).
|
||||
> - **Right Card (Enterprise):**
|
||||
> - Price: "¥ 999". Badge: "定制高频版".
|
||||
> - Features: "个人/企业 定制高险套餐", "高频需求首选".
|
||||
>
|
||||
> **3. Section 2: Claude Code Plans (3-Column Grid):**
|
||||
> - **Heading:** "Claude Code 套餐" (Bold, text-lg, mt-10 mb-4).
|
||||
> - **Layout:** Grid `grid-cols-1 md:grid-cols-3 gap-6`.
|
||||
> - **Cards:**
|
||||
> - Card 1: ¥228/月. Badge: "轻量性价比".
|
||||
> - Card 2: ¥398/月. Badge: "适合中等规模". (Highlight: slightly darker border or shadow).
|
||||
> - Card 3: ¥520/月. Badge: "深度运算".
|
||||
> - **Bottom Wide Card:** A full-width card for "Claude Code 定制套餐" (¥999).
|
||||
>
|
||||
> **4. CRITICAL COMPONENT - Bottom Action Button:**
|
||||
> - Inside EVERY card, pinned to the bottom.
|
||||
> - Container: `bg-gray-50` (light gray), `rounded-lg`, `p-4`, `mt-6`.
|
||||
> - Layout: Flex row, `justify-between`, `items-center`.
|
||||
> - Text Left: "联系顾问" (font-medium text-sm).
|
||||
> - Text Right: Small gray caption (e.g., "codex标准套餐 · ¥79.9/月").
|
||||
> - Icon: Small `ChevronDown` or `ChevronRight` on the right side.
|
||||
>
|
||||
> **5. Copy:** Use the exact Chinese text provided in the description.
|
||||
const sdk = await createViewer({
|
||||
container: '#viewer',
|
||||
assetsBase: '/assets',
|
||||
background: '#0f172a',
|
||||
})
|
||||
|
||||
// 加载初始场景或模型
|
||||
await sdk.model.load({ id: 'scene', url: '/models/showroom.glb' })
|
||||
```
|
||||
|
||||
### 基础生命周期
|
||||
1. 初始化 viewer:`createViewer({ container })`
|
||||
2. 加载模型或场景:`sdk.model.load(...)`
|
||||
3. 设置环境贴图/灯光:`sdk.environment.setHDRI(...)`、`sdk.lights.update(...)`
|
||||
4. 渲染热点:`sdk.hotspot.render(config.hotspots)`
|
||||
5. 订阅事件:`sdk.on('hotspot:click', handler)`
|
||||
6. 在业务侧按需调用相机/材质/模型热更新 API
|
||||
|
||||
---
|
||||
|
||||
## 🔵 Step 2: Documentation Page (使用文档页)
|
||||
*复制此段生成文档页面组件。*
|
||||
## 相机控制
|
||||
```ts
|
||||
// 直接设置视角
|
||||
sdk.camera.set({
|
||||
position: [2.5, 1.6, 3.2],
|
||||
target: [0, 0.8, 0],
|
||||
fov: 45,
|
||||
near: 0.1,
|
||||
far: 200,
|
||||
})
|
||||
|
||||
**Prompt:**
|
||||
|
||||
> **Task:** Create a **`DocsPage.vue`** component. Clone the "Usage Guide" design exactly.
|
||||
>
|
||||
> **1. Header & Controls:**
|
||||
> - Title: "使用说明". Subtitle: "新产品与插件安装的快速指南".
|
||||
> - **Tabs (Model):** A pill-shaped toggle container. Items: [Codex | Claude Code | GLM].
|
||||
> - Active State: Black background, White text.
|
||||
> - Inactive State: Transparent background, Gray text.
|
||||
> - **Tabs (OS):** Underline style. [Windows | macOS | Linux]. Active item has a black underline.
|
||||
>
|
||||
> **2. Vertical Stepper Layout (The Core Visual):**
|
||||
> - **Container:** Max-width 800px, centered.
|
||||
> - **Structure:** A vertical list of steps.
|
||||
> - **Left Column:** A Step Indicator.
|
||||
> - A black circle (`w-6 h-6 bg-black text-white rounded-full flex items-center justify-center text-xs font-bold`) with the number (1, 2, 3).
|
||||
> - A thin gray vertical line (`w-px bg-gray-200`) connecting the numbers.
|
||||
> - **Right Column:** The content.
|
||||
>
|
||||
> **3. Step Content Details:**
|
||||
> - **Step 1 (Install Node.js):**
|
||||
> - Title: "安装 Node.js 18+ (通用)".
|
||||
> - Link: "官网下载 (推荐)" in blue.
|
||||
> - Warning Box: A `bg-gray-50` rounded box containing bullet points about permissions.
|
||||
> - Version Check: A dark bar `node --version`.
|
||||
> - **Step 2 (Install CLI):**
|
||||
> - Title: "安装 Codex CLI".
|
||||
> - **Code Block:** A dark container (`bg-[#0f172a]`), `rounded-lg`, `p-4`, `shadow-inner`.
|
||||
> - Text: `npm install -g @openai/codex` (White text, font-mono).
|
||||
> - **Step 3 (Config):**
|
||||
> - Title: "配置环境 (CodeX)".
|
||||
> - **File Editor UI:** Create a mock code editor.
|
||||
> - Filename: "config.toml文件" (gray text above block).
|
||||
> - Code Content: Multi-line TOML config. Use specific colors for syntax highlighting (e.g., Strings in green, Keys in purple/white).
|
||||
>
|
||||
> **4. Component Abstraction:**
|
||||
> - Please create a separate `<CodeBlock :code="code" :lang="lang" />` component for reusability.
|
||||
// 平滑过渡到指定视角
|
||||
sdk.camera.animateTo(
|
||||
{ position: [1.2, 1, 2.4], target: [0, 0.8, 0], fov: 50 },
|
||||
{ duration: 0.8, easing: 'easeOutCubic' }
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔴 Step 3: Features Page (功能特性页)
|
||||
*复制此段生成功能着陆页组件。*
|
||||
## 灯光调参
|
||||
```ts
|
||||
// 更新主方向光
|
||||
sdk.lights.update('key', {
|
||||
intensity: 1.5,
|
||||
color: '#ffffff',
|
||||
direction: [0.3, -1, 0.25],
|
||||
castShadow: true,
|
||||
})
|
||||
|
||||
**Prompt:**
|
||||
|
||||
> **Task:** Create a **`FeaturesPage.vue`** component. Clone the "Features" design exactly.
|
||||
>
|
||||
> **1. Hero Section:**
|
||||
> - Alignment: Center.
|
||||
> - Headline: "更快、更智能地构建软件" (text-5xl font-bold tracking-tight mb-6).
|
||||
> - Subheadline: "聚合多模型,深度理解你的代码库..." (text-gray-500 max-w-2xl mx-auto).
|
||||
> - Action Buttons:
|
||||
> - Primary: "开始使用" (Black bg, white text, rounded-full, px-8 py-2.5).
|
||||
> - Secondary: "查看文档" (White bg, border border-gray-200, black text, rounded-full, px-8 py-2.5).
|
||||
>
|
||||
> **2. The Bento Grid (Critical Visual):**
|
||||
> - Section Title: "覆盖从编码到交付的全流程".
|
||||
> - **Grid System:** `grid-cols-1 md:grid-cols-3 gap-6`.
|
||||
> - **Card Anatomy:**
|
||||
> - `bg-white border border-gray-200 rounded-xl p-6 flex flex-col h-full overflow-hidden hover:shadow-md transition-all`.
|
||||
> - **Icon:** Top-left. Small Lucide icon inside a square `bg-gray-50` rounded container.
|
||||
> - **Text:** Title (font-bold mt-4) + Description (text-sm text-gray-500 mt-2).
|
||||
> - **The Mockup Area (Must Have):** At the bottom of each card, create a visual placeholder.
|
||||
> - *Implementation:* A `div` with `bg-gray-50/50 mt-6 h-32 w-full rounded border border-dashed border-gray-200`. Put a small "skeleton" UI inside (e.g., a few gray bars representing code lines) to mimic the screenshots.
|
||||
>
|
||||
> **3. Stats Bar:**
|
||||
> - Layout: Flex row, wide container.
|
||||
> - Items:
|
||||
> - "2x" (交付效率提升)
|
||||
> - "40%" (Bug 修复加速)
|
||||
> - "60%" (重复劳动减少)
|
||||
> - Style: Numbers are huge (`text-5xl font-bold tracking-tighter`). Labels are small and gray.
|
||||
>
|
||||
> **4. Footer CTA:**
|
||||
> - Text: "准备好升级研发效率了吗?"
|
||||
> - Button: "免费开始" (White button, border, rounded-full).
|
||||
// 环境光/辅光
|
||||
sdk.lights.update('fill', { intensity: 0.6, color: '#dfe8ff' })
|
||||
sdk.lights.update('ambient', { intensity: 0.25 })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Developer Tips (For You)
|
||||
## 环境贴图
|
||||
```ts
|
||||
await sdk.environment.setHDRI('/envs/showroom_2k.hdr', {
|
||||
exposure: 1.1,
|
||||
rotation: [0, 45, 0], // yaw/pitch/roll
|
||||
blur: 0.2,
|
||||
})
|
||||
```
|
||||
|
||||
1. **Global Font Fix:** To get the crisp look from the screenshots, add this to your `index.css` or `style.css`:
|
||||
```css
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
```
|
||||
2. **Icon Installation:** Ensure you have the icons installed:
|
||||
```bash
|
||||
npm install lucide-vue-next
|
||||
```
|
||||
3. **Refining:** If the AI output looks "too loose", tell it: *"Reduce the border-radius to 12px and make the text colors sharper (slate-900 instead of gray-800)."*
|
||||
---
|
||||
|
||||
## 热点渲染与事件
|
||||
```ts
|
||||
const hotspots = [
|
||||
{
|
||||
id: 'engine',
|
||||
position: [0.8, 0.6, -0.2],
|
||||
icon: 'info',
|
||||
label: '发动机舱',
|
||||
event: 'show-engine',
|
||||
payload: { part: 'engine' },
|
||||
},
|
||||
{
|
||||
id: 'door',
|
||||
position: [1.1, 0.9, 0.4],
|
||||
icon: 'action',
|
||||
label: '开启车门',
|
||||
event: 'toggle-door',
|
||||
payload: { side: 'left' },
|
||||
},
|
||||
]
|
||||
|
||||
sdk.hotspot.render(hotspots)
|
||||
|
||||
sdk.hotspot.on('click', ({ id, payload }) => {
|
||||
// 业务侧根据 id/payload 触发操作
|
||||
})
|
||||
|
||||
sdk.hotspot.on('hover', ({ id, hovering }) => {
|
||||
// hover 高亮或提示
|
||||
})
|
||||
```
|
||||
|
||||
事件命名建议(可按需扩展):
|
||||
- `hotspot:click`、`hotspot:hover`、`hotspot:focus`
|
||||
- `hotspot:rendered`(所有热点就绪)
|
||||
|
||||
---
|
||||
|
||||
## 模型热加载 / 热销毁 / 热替换
|
||||
```ts
|
||||
// 加载
|
||||
await sdk.model.load({ id: 'car', url: '/models/car.glb', draco: true })
|
||||
|
||||
// 销毁
|
||||
await sdk.model.destroy('car')
|
||||
|
||||
// 替换(内部自动销毁旧实例并加载新实例)
|
||||
await sdk.model.replace('car', { url: '/models/car-white.glb' })
|
||||
```
|
||||
|
||||
可以通过 `sdk.model.on('loaded' | 'replaced' | 'destroyed', handler)` 监听模型状态。
|
||||
|
||||
---
|
||||
|
||||
## 材质切换
|
||||
```ts
|
||||
// 应用材质预设
|
||||
sdk.material.apply({
|
||||
target: 'body', // 部件或分组名称
|
||||
material: 'paint/blue', // 预设 Key
|
||||
})
|
||||
|
||||
// 批量切换
|
||||
sdk.material.batch([
|
||||
{ target: 'wheel', material: 'rim/chrome' },
|
||||
{ target: 'glass', material: 'glass/clear' },
|
||||
])
|
||||
|
||||
// 还原默认材质
|
||||
sdk.material.reset('body')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 推荐的配置结构
|
||||
```json
|
||||
{
|
||||
"camera": { "position": [2.5, 1.6, 3.2], "target": [0, 0.8, 0], "fov": 45 },
|
||||
"environment": { "hdr": "/envs/showroom_2k.hdr", "exposure": 1.1, "rotation": [0, 45, 0] },
|
||||
"lights": {
|
||||
"key": { "intensity": 1.5, "color": "#ffffff", "direction": [0.3, -1, 0.25] },
|
||||
"fill": { "intensity": 0.6, "color": "#dfe8ff" },
|
||||
"ambient": { "intensity": 0.25 }
|
||||
},
|
||||
"hotspots": [
|
||||
{ "id": "engine", "position": [0.8, 0.6, -0.2], "icon": "info", "label": "发动机舱", "event": "show-engine" },
|
||||
{ "id": "door", "position": [1.1, 0.9, 0.4], "icon": "action", "label": "开启车门", "event": "toggle-door" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 事件清单(示例)
|
||||
- 相机:`camera:changed`
|
||||
- 热点:`hotspot:click`、`hotspot:hover`、`hotspot:rendered`
|
||||
- 模型:`model:loaded`、`model:replaced`、`model:destroyed`
|
||||
- 材质:`material:applied`、`material:reset`
|
||||
|
||||
订阅方式:
|
||||
```ts
|
||||
sdk.on('hotspot:click', (evt) => console.log(evt))
|
||||
sdk.off('hotspot:click', handler) // 移除监听
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 调试建议
|
||||
- 启用 `sdk.debug(true)` 查看加载、事件、帧率等日志。
|
||||
- 逐步应用配置:先确认相机与灯光,再添加环境贴图与热点,最后再做材质/模型热更新。
|
||||
- 热更新前后记录 ID/实例,避免重复销毁或遗漏解绑事件。
|
||||
|
||||
43
public/docs/camera-light.md
Normal file
43
public/docs/camera-light.md
Normal file
@ -0,0 +1,43 @@
|
||||
# 相机与灯光/环境
|
||||
|
||||
## 相机控制
|
||||
```ts
|
||||
// 直接设置视角
|
||||
sdk.camera.set({
|
||||
position: [2.5, 1.6, 3.2],
|
||||
target: [0, 0.8, 0],
|
||||
fov: 45,
|
||||
near: 0.1,
|
||||
far: 200,
|
||||
})
|
||||
|
||||
// 平滑过渡到指定视角
|
||||
sdk.camera.animateTo(
|
||||
{ position: [1.2, 1, 2.4], target: [0, 0.8, 0], fov: 50 },
|
||||
{ duration: 0.8, easing: 'easeOutCubic' }
|
||||
)
|
||||
```
|
||||
|
||||
## 灯光调参
|
||||
```ts
|
||||
// 更新主方向光
|
||||
sdk.lights.update('key', {
|
||||
intensity: 1.5,
|
||||
color: '#ffffff',
|
||||
direction: [0.3, -1, 0.25],
|
||||
castShadow: true,
|
||||
})
|
||||
|
||||
// 环境光/辅光
|
||||
sdk.lights.update('fill', { intensity: 0.6, color: '#dfe8ff' })
|
||||
sdk.lights.update('ambient', { intensity: 0.25 })
|
||||
```
|
||||
|
||||
## 环境贴图
|
||||
```ts
|
||||
await sdk.environment.setHDRI('/envs/showroom_2k.hdr', {
|
||||
exposure: 1.1,
|
||||
rotation: [0, 45, 0], // yaw/pitch/roll
|
||||
blur: 0.2,
|
||||
})
|
||||
```
|
||||
30
public/docs/config-template.md
Normal file
30
public/docs/config-template.md
Normal file
@ -0,0 +1,30 @@
|
||||
# 配置示例与事件清单
|
||||
|
||||
## 推荐的配置结构
|
||||
```json
|
||||
{
|
||||
"camera": { "position": [2.5, 1.6, 3.2], "target": [0, 0.8, 0], "fov": 45 },
|
||||
"environment": { "hdr": "/envs/showroom_2k.hdr", "exposure": 1.1, "rotation": [0, 45, 0] },
|
||||
"lights": {
|
||||
"key": { "intensity": 1.5, "color": "#ffffff", "direction": [0.3, -1, 0.25] },
|
||||
"fill": { "intensity": 0.6, "color": "#dfe8ff" },
|
||||
"ambient": { "intensity": 0.25 }
|
||||
},
|
||||
"hotspots": [
|
||||
{ "id": "engine", "position": [0.8, 0.6, -0.2], "icon": "info", "label": "发动机舱", "event": "show-engine" },
|
||||
{ "id": "door", "position": [1.1, 0.9, 0.4], "icon": "action", "label": "开启车门", "event": "toggle-door" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 事件清单(示例)
|
||||
- 相机:`camera:changed`
|
||||
- 热点:`hotspot:click`、`hotspot:hover`、`hotspot:rendered`
|
||||
- 模型:`model:loaded`、`model:replaced`、`model:destroyed`
|
||||
- 材质:`material:applied`、`material:reset`
|
||||
|
||||
订阅方式:
|
||||
```ts
|
||||
sdk.on('hotspot:click', (evt) => console.log(evt))
|
||||
sdk.off('hotspot:click', handler) // 移除监听
|
||||
```
|
||||
40
public/docs/hotspot-events.md
Normal file
40
public/docs/hotspot-events.md
Normal file
@ -0,0 +1,40 @@
|
||||
# 热点与事件
|
||||
|
||||
## 渲染热点
|
||||
```ts
|
||||
const hotspots = [
|
||||
{
|
||||
id: 'engine',
|
||||
position: [0.8, 0.6, -0.2],
|
||||
icon: 'info',
|
||||
label: '发动机舱',
|
||||
event: 'show-engine',
|
||||
payload: { part: 'engine' },
|
||||
},
|
||||
{
|
||||
id: 'door',
|
||||
position: [1.1, 0.9, 0.4],
|
||||
icon: 'action',
|
||||
label: '开启车门',
|
||||
event: 'toggle-door',
|
||||
payload: { side: 'left' },
|
||||
},
|
||||
]
|
||||
|
||||
sdk.hotspot.render(hotspots)
|
||||
```
|
||||
|
||||
## 订阅事件
|
||||
```ts
|
||||
sdk.hotspot.on('click', ({ id, payload }) => {
|
||||
// 根据 id/payload 触发业务逻辑
|
||||
})
|
||||
|
||||
sdk.hotspot.on('hover', ({ id, hovering }) => {
|
||||
// hover 高亮或提示
|
||||
})
|
||||
```
|
||||
|
||||
## 事件命名建议
|
||||
- `hotspot:click`、`hotspot:hover`、`hotspot:focus`
|
||||
- `hotspot:rendered`(所有热点就绪)
|
||||
46
public/docs/import-guide.md
Normal file
46
public/docs/import-guide.md
Normal file
@ -0,0 +1,46 @@
|
||||
# 引入方式与事件监听
|
||||
|
||||
介绍两种集成方式(全局挂载 / 模块化 ESM),以及统一的事件监听方法。
|
||||
|
||||
## 全局挂载(非模块化)
|
||||
1. 引入全局构建产物(替换为你的实际部署地址):
|
||||
```html
|
||||
<script src="https://sdk.zguiy.com/zt/assets/index.js"></script>
|
||||
```
|
||||
2. 获取实例并初始化:
|
||||
```js
|
||||
const kernel = window.faceSDK?.kernel
|
||||
if (!kernel) {
|
||||
console.error('SDK 未加载')
|
||||
} else {
|
||||
kernel.init({
|
||||
container: 'renderDom', // 容器 ID(canvas)
|
||||
modelUrlList: ['./model.glb'], // 模型列表
|
||||
env: { hdrPath: '/hdr/my.env' }, // 环境贴图等参数
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 模块化(ESM)
|
||||
1. 在 `<script type="module">` 中导入:
|
||||
```js
|
||||
import { kernel } from 'https://sdk.zguiy.com/zt/assets/index.js' // 构建后路径自行替换
|
||||
```
|
||||
2. 初始化(与全局方式一致):
|
||||
```js
|
||||
kernel.init({
|
||||
container: 'renderDom',
|
||||
modelUrlList: ['./model.glb'],
|
||||
env: { envPath: '/hdr/my.env' },
|
||||
})
|
||||
```
|
||||
|
||||
## 事件监听(两种方式通用)
|
||||
```js
|
||||
kernel.on('model:load:progress', (data) => console.log('模型加载进度', data))
|
||||
kernel.on('model:loaded', (data) => console.log('模型加载完成', data))
|
||||
kernel.on('model:click', (data) => console.log('模型点击', data))
|
||||
|
||||
// 需要时可移除
|
||||
// kernel.off('model:loaded', handler)
|
||||
```
|
||||
38
public/docs/model-material.md
Normal file
38
public/docs/model-material.md
Normal file
@ -0,0 +1,38 @@
|
||||
# 模型热更新与材质切换
|
||||
|
||||
## 模型热加载 / 销毁 / 替换
|
||||
```ts
|
||||
// 加载
|
||||
await sdk.model.load({ id: 'car', url: '/models/car.glb', draco: true })
|
||||
|
||||
// 销毁
|
||||
await sdk.model.destroy('car')
|
||||
|
||||
// 替换(内部自动销毁旧实例并加载新实例)
|
||||
await sdk.model.replace('car', { url: '/models/car-white.glb' })
|
||||
```
|
||||
|
||||
监听状态:
|
||||
```ts
|
||||
sdk.model.on('loaded', handler)
|
||||
sdk.model.on('replaced', handler)
|
||||
sdk.model.on('destroyed', handler)
|
||||
```
|
||||
|
||||
## 材质切换
|
||||
```ts
|
||||
// 应用材质预设
|
||||
sdk.material.apply({
|
||||
target: 'body', // 部件或分组名称
|
||||
material: 'paint/blue', // 预设 Key
|
||||
})
|
||||
|
||||
// 批量切换
|
||||
sdk.material.batch([
|
||||
{ target: 'wheel', material: 'rim/chrome' },
|
||||
{ target: 'glass', material: 'glass/clear' },
|
||||
])
|
||||
|
||||
// 还原默认材质
|
||||
sdk.material.reset('body')
|
||||
```
|
||||
35
public/docs/quickstart.md
Normal file
35
public/docs/quickstart.md
Normal file
@ -0,0 +1,35 @@
|
||||
# 快速开始
|
||||
|
||||
面向 Web 场景的三维展示与互动 SDK,提供相机、灯光、环境贴图、热点、模型热更新与材质切换等能力。
|
||||
|
||||
## 安装
|
||||
```bash
|
||||
npm install your-web3d-sdk
|
||||
```
|
||||
|
||||
## 初始化 Viewer
|
||||
```ts
|
||||
import { createViewer } from 'your-web3d-sdk'
|
||||
|
||||
const sdk = await createViewer({
|
||||
container: '#viewer',
|
||||
assetsBase: '/assets',
|
||||
background: '#0f172a',
|
||||
})
|
||||
|
||||
// 加载初始场景或模型
|
||||
await sdk.model.load({ id: 'scene', url: '/models/showroom.glb' })
|
||||
```
|
||||
|
||||
## 基础生命周期
|
||||
1. 初始化 viewer:`createViewer({ container })`
|
||||
2. 加载模型或场景:`sdk.model.load(...)`
|
||||
3. 设置环境贴图/灯光:`sdk.environment.setHDRI(...)`、`sdk.lights.update(...)`
|
||||
4. 渲染热点:`sdk.hotspot.render(config.hotspots)`
|
||||
5. 订阅事件:`sdk.on('hotspot:click', handler)`
|
||||
6. 按需调用相机/材质/模型热更新 API
|
||||
|
||||
## 调试建议
|
||||
- 启用 `sdk.debug(true)` 查看加载、事件、帧率等日志。
|
||||
- 逐步应用配置:先确认相机与灯光,再添加环境贴图与热点,最后再做材质/模型热更新。
|
||||
- 热更新前后记录 ID/实例,避免重复销毁或遗漏解绑事件。
|
||||
58
src/App.vue
58
src/App.vue
@ -22,7 +22,6 @@
|
||||
<div class="h-5 w-5 rotate-45 rounded-[4px] border-2 border-slate-900 shadow-[0_2px_6px_rgba(0,0,0,0.08)]" />
|
||||
<div class="grid leading-tight">
|
||||
<span class="text-sm font-semibold">{{ site.brand }}</span>
|
||||
<!-- <span class="text-[11px] text-gray-500">{{ site.subtitle }}</span> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -41,13 +40,12 @@
|
||||
</nav>
|
||||
|
||||
<div class="flex items-center gap-3 max-[640px]:w-full max-[640px]:justify-between">
|
||||
<a
|
||||
<button
|
||||
class="rounded-full border border-slate-900 bg-slate-900 px-4 py-2.5 text-sm font-semibold text-white shadow-[0_10px_24px_rgba(0,0,0,0.12)] transition-[transform,box-shadow,background,color] duration-200 hover:shadow-[0_12px_28px_rgba(0,0,0,0.16)] active:translate-y-px"
|
||||
:href="`mailto:${site.contact.email}`"
|
||||
@click="handleNav('/docs')"
|
||||
>
|
||||
咨询合作
|
||||
</a>
|
||||
|
||||
查看文档
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -58,7 +56,7 @@
|
||||
</main>
|
||||
|
||||
<footer class="border-t border-gray-200 bg-white">
|
||||
<div class="mx-auto grid w-full max-w-[1200px] grid-cols-5 gap-6 px-6 pt-10 pb-6 max-[900px]:grid-cols-2 max-[640px]:grid-cols-1">
|
||||
<div class="mx-auto grid w-full max-w-[1200px] grid-cols-4 gap-6 px-6 pt-10 pb-6 max-[900px]:grid-cols-2 max-[640px]:grid-cols-1">
|
||||
<div class="col-span-2 grid gap-3 max-[900px]:col-span-2 max-[640px]:col-span-1">
|
||||
<div class="inline-flex items-center gap-2.5">
|
||||
<div class="h-5 w-5 rotate-45 rounded-[4px] border-2 border-slate-900 shadow-[0_2px_6px_rgba(0,0,0,0.08)]" />
|
||||
@ -73,37 +71,26 @@
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<div class="font-bold text-gray-900">服务</div>
|
||||
<div class="font-bold text-gray-900">产品能力</div>
|
||||
<ul class="m-0 grid list-none gap-1.5 p-0 text-gray-600">
|
||||
<li>企业官网 / 落地页</li>
|
||||
<li>后台管理系统</li>
|
||||
<li>全栈业务开发</li>
|
||||
<li>Web3D 互动展示</li>
|
||||
<li>3D建模与渲染(PBR)</li>
|
||||
<li>相机控制与动画</li>
|
||||
<li>灯光与环境贴图</li>
|
||||
<li>热点渲染与事件</li>
|
||||
<li>模型热加载/替换</li>
|
||||
<li>材质切换与预设</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<div class="font-bold text-gray-900">技术栈</div>
|
||||
<div class="font-bold text-gray-900">资源</div>
|
||||
<ul class="m-0 grid list-none gap-1.5 p-0 text-gray-600">
|
||||
<li>Vue / React / TypeScript</li>
|
||||
<li>Node.js / NestJS / Express</li>
|
||||
<li>MySQL / PostgreSQL / Redis</li>
|
||||
<li>Three.js / Babylon.js</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<div class="font-bold text-gray-900">合作</div>
|
||||
<ul class="m-0 grid list-none gap-1.5 p-0 text-gray-600">
|
||||
<li>按需求评估报价</li>
|
||||
<li>按里程碑验收交付</li>
|
||||
<li>支持远程协作</li>
|
||||
<li>可提供维护服务</li>
|
||||
<li><button class="text-left text-gray-600 hover:text-black" @click="handleNav('/')">首页</button></li>
|
||||
<li><button class="text-left text-gray-600 hover:text-black" @click="handleNav('/docs')">SDK 文档</button></li>
|
||||
<li><a class="text-gray-600 hover:text-black" :href="`mailto:${site.contact.email}`">技术支持</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-t border-gray-200 py-3 text-center text-xs text-gray-400">合作条款 · 交付说明</div>
|
||||
<div class="border-t border-gray-200 py-3 text-center text-xs text-gray-400">Web3D 场景 SDK · 开箱即用的互动体验</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
@ -118,17 +105,14 @@ const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const tabs = [
|
||||
{ id: 'features', label: '能力与服务', path: '/' },
|
||||
// { id: 'pricing', label: '报价', path: '/' },
|
||||
{ id: 'cooperation', label: '合作流程', path: '/cooperation' },
|
||||
{ id: 'markdown', label: '案例/文章', path: '/markdown' },
|
||||
{ id: 'home', label: '首页', path: '/' },
|
||||
{ id: 'docs', label: 'SDK 文档', path: '/docs' },
|
||||
] as const
|
||||
|
||||
const activeId = computed(() => {
|
||||
if (route.name === 'features') return 'features'
|
||||
if (route.name === 'cooperation') return 'cooperation'
|
||||
if (route.name === 'markdown') return 'markdown'
|
||||
return 'pricing'
|
||||
if (route.name === 'home') return 'home'
|
||||
if (route.name === 'docs') return 'docs'
|
||||
return 'home'
|
||||
})
|
||||
|
||||
const handleNav = (path: string) => {
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
export const site = {
|
||||
brand: '子归云工作室',
|
||||
// subtitle: '前端 · 全栈 · Web3D · 3D建模/渲染 · 官网/后台/小程序',
|
||||
brand: 'Web3D 场景 SDK',
|
||||
tagline:
|
||||
'承接企业官网、后台管理、全栈业务系统、Web3D 互动展示(Three.js / Babylon.js)与 3D 建模渲染(PBR 流程)。支持按需求评估、按里程碑交付、长期维护。',
|
||||
'一站式 Web3D 互动 SDK:相机控制、灯光/环境贴图、热点事件、模型热更新与材质切换,快速搭建可配置的三维体验。',
|
||||
contact: {
|
||||
wechat: 'TyTinSx',
|
||||
email: '1415466602@qq.com',
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import PricingPage from '@/views/PricingPage.vue'
|
||||
import FeaturesPage from '@/views/FeaturesPage.vue'
|
||||
import MarkdownPage from '@/views/MarkdownPage.vue'
|
||||
import CooperationPage from '@/views/CooperationPage.vue'
|
||||
|
||||
const routes = [
|
||||
{ path: '/', name: 'features', component: FeaturesPage },
|
||||
{ path: '/markdown', name: 'markdown', component: MarkdownPage },
|
||||
{ path: '/cooperation', name: 'cooperation', component: CooperationPage },
|
||||
{ path: '/', name: 'home', component: FeaturesPage },
|
||||
{ path: '/docs', name: 'docs', component: MarkdownPage },
|
||||
]
|
||||
|
||||
export const router = createRouter({
|
||||
|
||||
@ -1,116 +0,0 @@
|
||||
<template>
|
||||
<section class="grid gap-6 pt-3 pb-10">
|
||||
<header class="grid gap-2.5">
|
||||
<h1 class="m-0 text-[28px] font-extrabold tracking-[-0.01em]">合作流程</h1>
|
||||
<p class="m-0 text-gray-600">灵活的合作模式,满足不同项目需求</p>
|
||||
</header>
|
||||
|
||||
<div class="grid gap-4">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
v-for="file in mdFiles"
|
||||
:key="file"
|
||||
:class="[
|
||||
'rounded-full px-4 py-2 text-sm font-semibold transition-colors',
|
||||
currentFile === file ? 'bg-slate-900 text-white' : 'border border-gray-200 bg-white text-gray-600 hover:bg-gray-50',
|
||||
]"
|
||||
@click="loadFile(file)"
|
||||
>
|
||||
{{ file.replace('.md', '') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="htmlContent"
|
||||
ref="contentRef"
|
||||
class="prose prose-slate max-w-none rounded-2xl border border-gray-200 bg-white p-6 shadow-[0_14px_26px_rgba(0,0,0,0.06)]"
|
||||
v-html="htmlContent"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { marked } from 'marked'
|
||||
import { nextTick, onMounted, ref, watch } from 'vue'
|
||||
|
||||
const BASE_URL = import.meta.env.BASE_URL
|
||||
|
||||
// 合作流程文件列表
|
||||
const cooperationFiles = ['按项目.md', '按天.md']
|
||||
|
||||
const mdFiles = ref<string[]>([])
|
||||
const currentFile = ref('')
|
||||
const htmlContent = ref('')
|
||||
const contentRef = ref<HTMLElement>()
|
||||
|
||||
onMounted(async () => {
|
||||
mdFiles.value = cooperationFiles
|
||||
if (mdFiles.value.length > 0) {
|
||||
await loadFile(mdFiles.value[0])
|
||||
}
|
||||
})
|
||||
|
||||
const addCopyButtons = () => {
|
||||
if (!contentRef.value) return
|
||||
contentRef.value.querySelectorAll('pre').forEach((pre) => {
|
||||
if (pre.querySelector('.copy-btn')) return
|
||||
const button = document.createElement('button')
|
||||
button.className = 'copy-btn'
|
||||
button.textContent = '复制'
|
||||
button.onclick = async () => {
|
||||
const code = pre.querySelector('code')
|
||||
const text = code?.textContent || ''
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
} catch {
|
||||
const textarea = document.createElement('textarea')
|
||||
textarea.value = text
|
||||
textarea.style.cssText = 'position:fixed;opacity:0'
|
||||
document.body.appendChild(textarea)
|
||||
textarea.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(textarea)
|
||||
}
|
||||
button.textContent = '已复制'
|
||||
setTimeout(() => (button.textContent = '复制'), 1500)
|
||||
}
|
||||
pre.style.position = 'relative'
|
||||
pre.appendChild(button)
|
||||
})
|
||||
}
|
||||
|
||||
watch(htmlContent, () => nextTick(addCopyButtons))
|
||||
|
||||
const loadFile = async (filename: string) => {
|
||||
currentFile.value = filename
|
||||
try {
|
||||
const res = await fetch(`${BASE_URL}docs/${filename}`)
|
||||
if (!res.ok) return
|
||||
const content = await res.text()
|
||||
htmlContent.value = marked(content) as string
|
||||
} catch (e) {
|
||||
console.error('加载文档失败:', e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.prose h1 { font-size: 1.875rem; font-weight: 800; margin-bottom: 1rem; }
|
||||
.prose h2 { font-size: 1.5rem; font-weight: 700; margin-top: 1.5rem; margin-bottom: 0.75rem; }
|
||||
.prose h3 { font-size: 1.25rem; font-weight: 600; margin-top: 1.25rem; margin-bottom: 0.5rem; }
|
||||
.prose p { margin-bottom: 1rem; line-height: 1.7; color: #4b5563; }
|
||||
.prose ul, .prose ol { margin-bottom: 1rem; padding-left: 1.5rem; }
|
||||
.prose li { margin-bottom: 0.25rem; color: #4b5563; }
|
||||
.prose code { background: #f1f5f9; padding: 0.125rem 0.375rem; border-radius: 0.25rem; font-size: 0.875rem; }
|
||||
.prose pre { background: #1e293b; color: #e2e8f0; padding: 1rem; padding-top: 2.5rem; border-radius: 0.75rem; overflow-x: auto; margin-bottom: 1rem; position: relative; }
|
||||
.prose pre::before { content: ''; position: absolute; top: 12px; left: 12px; width: 12px; height: 12px; border-radius: 50%; background: #ff5f56; box-shadow: 20px 0 0 #ffbd2e, 40px 0 0 #27c93f; }
|
||||
.prose pre code { background: transparent; padding: 0; }
|
||||
.prose a { color: #2563eb; text-decoration: underline; }
|
||||
.prose blockquote { border-left: 4px solid #e5e7eb; padding-left: 1rem; color: #6b7280; font-style: italic; }
|
||||
.prose table { width: 100%; border-collapse: collapse; margin-bottom: 1rem; }
|
||||
.prose th, .prose td { border: 1px solid #e5e7eb; padding: 0.5rem 1rem; text-align: left; }
|
||||
.prose th { background: #f9fafb; font-weight: 600; }
|
||||
.copy-btn { position: absolute; top: 0.5rem; right: 0.5rem; padding: 0.25rem 0.75rem; font-size: 0.75rem; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); border-radius: 9999px; color: #e2e8f0; cursor: pointer; transition: background 0.2s; }
|
||||
.copy-btn:hover { background: rgba(255,255,255,0.2); }
|
||||
</style>
|
||||
@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<section class="grid gap-10 pt-3 pb-12">
|
||||
<header class="grid gap-3 text-center">
|
||||
<div class="text-[13px] text-gray-500">能力与服务 / 交付型合作 / 可长期维护</div>
|
||||
<h1 class="m-0 text-[clamp(28px,4vw,44px)] font-extrabold tracking-[-0.02em]">把需求做成可交付的产品</h1>
|
||||
<p class="m-0 mx-auto max-w-[680px] leading-[1.7] text-gray-600">
|
||||
我能做前端、全栈、Web3D(Babylon.js / Three.js)、3D 建模/渲染(PBR)、官网与后台管理、小程序开发等。你给目标和素材,我负责方案、实现、联调、上线。
|
||||
<div class="text-[13px] text-gray-500">Web3D 场景 SDK · 相机/灯光/环境贴图/热点/模型/材质</div>
|
||||
<h1 class="m-0 text-[clamp(28px,4vw,44px)] font-extrabold tracking-[-0.02em]">让三维展示可配置、可热更新</h1>
|
||||
<p class="m-0 mx-auto max-w-[760px] leading-[1.7] text-gray-600">
|
||||
开箱即用的 Web3D SDK,内置相机控制、灯光与 HDRI、配置化热点、事件总线、模型热加载/销毁/替换与材质切换。适用于在线展厅、产品配置器、互动热点讲解等场景。
|
||||
</p>
|
||||
<div class="inline-flex justify-center gap-3 max-[640px]:w-full max-[640px]:justify-center">
|
||||
<Button @click="router.push('/cooperation')">查看报价</Button>
|
||||
<Button variant="secondary" @click="router.push('/cooperation')">合作流程</Button>
|
||||
<Button @click="router.push('/docs')">查看 SDK 文档</Button>
|
||||
<Button variant="secondary" @click="router.push('/docs')">示例与 API</Button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="grid gap-[18px]">
|
||||
<div class="mt-2 text-center text-[22px] font-bold">覆盖从设计对接到上线交付的全流程</div>
|
||||
<div class="mt-2 text-center text-[22px] font-bold">核心能力</div>
|
||||
<div class="grid grid-cols-3 gap-5 max-[1024px]:grid-cols-2 max-[640px]:grid-cols-1">
|
||||
<Card v-for="(feature, index) in features" :key="feature.title" hoverable class="grid content-start gap-3">
|
||||
<div
|
||||
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
|
||||
<div class="grid gap-[18px]">
|
||||
<div class="text-center font-semibold text-gray-700">适合的项目类型</div>
|
||||
<div class="text-center font-semibold text-gray-700">适用场景</div>
|
||||
<div class="grid grid-cols-2 gap-5 max-[1024px]:grid-cols-1">
|
||||
<Card v-for="item in scenarios" :key="item" hoverable class="grid grid-cols-[32px_1fr] items-center gap-3">
|
||||
<div
|
||||
@ -57,9 +57,9 @@
|
||||
</div>
|
||||
|
||||
<div class="grid gap-[18px]">
|
||||
<div class="text-center font-semibold text-gray-700">合作优势</div>
|
||||
<div class="text-center font-semibold text-gray-700">产品指标</div>
|
||||
<div class="grid grid-cols-3 gap-5 max-[1024px]:grid-cols-2 max-[640px]:grid-cols-1">
|
||||
<Card v-for="stat in stats" :key="stat.value" class="text-center">
|
||||
<Card v-for="stat in stats" :key="stat.label" class="text-center">
|
||||
<div class="text-[38px] font-extrabold tracking-[-0.02em]">{{ stat.value }}</div>
|
||||
<div class="mt-1.5 text-sm text-gray-600">{{ stat.label }}</div>
|
||||
</Card>
|
||||
@ -67,11 +67,16 @@
|
||||
</div>
|
||||
|
||||
<Card hoverable class="text-center">
|
||||
<div class="text-xl font-bold">想把需求尽快落地?</div>
|
||||
<p class="m-0 text-gray-500">把你的目标、参考链接、时间节点发我,我会给出方案与排期建议。</p>
|
||||
<div class="text-xl font-bold">准备好把 Web3D 能力接入产品了吗?</div>
|
||||
<p class="m-0 text-gray-500">从相机/灯光到热点事件,只需按照文档配置即可上线。遇到问题可随时邮件联系支持。</p>
|
||||
<div class="mt-4 inline-flex gap-3 max-[640px]:w-full max-[640px]:justify-center">
|
||||
<Button @click="router.push('/cooperation')">获取报价</Button>
|
||||
<Button variant="secondary" @click="router.push('/markdown')">查看案例</Button>
|
||||
<Button @click="router.push('/docs')">立即查看文档</Button>
|
||||
<a
|
||||
class="rounded-full border border-gray-200 bg-white px-4 py-2.5 text-sm font-semibold text-slate-900 shadow-[0_8px_18px_rgba(0,0,0,0.06)] transition hover:border-slate-900"
|
||||
:href="`mailto:${site.contact.email}`"
|
||||
>
|
||||
邮件支持
|
||||
</a>
|
||||
</div>
|
||||
</Card>
|
||||
</section>
|
||||
@ -80,19 +85,18 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
Boxes,
|
||||
Cloud,
|
||||
Code2,
|
||||
Cpu,
|
||||
Cuboid,
|
||||
LayoutTemplate,
|
||||
Camera,
|
||||
Layers,
|
||||
Sparkles,
|
||||
MousePointer2,
|
||||
Palette,
|
||||
ShieldCheck,
|
||||
Sparkles,
|
||||
SunMedium,
|
||||
} from 'lucide-vue-next'
|
||||
import Button from '@/components/Button/index.vue'
|
||||
import Card from '@/components/Card/index.vue'
|
||||
import TypeItTerminal, { type TypeItTerminalSegment } from '@/components/TypeItTerminal.vue'
|
||||
import { site } from '@/config/site'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@ -103,239 +107,139 @@ type Feature = {
|
||||
}
|
||||
|
||||
const features: Feature[] = [
|
||||
{ title: '前端工程化', description: 'Vue/React + TypeScript + 构建与规范,让项目可维护、可迭代。', icon: Code2 },
|
||||
{ title: '后台管理', description: '权限、表格、图表、导入导出与业务流程,按习惯做出“顺手”的后台。', icon: LayoutTemplate },
|
||||
{ title: '全栈开发', description: '接口设计、鉴权、数据库建模与联调,打通前后端的交付链路。', icon: Layers },
|
||||
{ title: 'Web3D', description: 'Three.js/Babylon.js 场景搭建、交互、性能优化与业务 UI 联动。', icon: Cuboid },
|
||||
{ title: '官网/落地页', description: '品牌展示 + 动效 + SEO 基础,支持多端适配与上线部署。', icon: Sparkles },
|
||||
{ title: '小程序开发', description: '微信小程序/uni-app(按需),从页面到接口联调与发布。', icon: Boxes },
|
||||
{ title: '性能优化', description: '首屏、图片、缓存、包体与渲染优化,提升体验与转化。', icon: Cpu },
|
||||
{ title: '部署上线', description: 'Docker/服务器部署(按需),支持环境隔离与灰度发布方案。', icon: Cloud },
|
||||
{ title: '质量与安全', description: '边界处理、日志与监控建议,交付稳定可靠的线上版本。', icon: ShieldCheck },
|
||||
{
|
||||
title: '相机控制与动画',
|
||||
description: 'set/animateTo 支持位置、目标、FOV、近远裁剪,带缓动过渡与回调。',
|
||||
icon: Camera,
|
||||
},
|
||||
{
|
||||
title: '灯光与环境贴图',
|
||||
description: '主光/辅光/环境光参数化配置;HDRI 切换、曝光、旋转、模糊一键生效。',
|
||||
icon: SunMedium,
|
||||
},
|
||||
{
|
||||
title: '热点渲染与事件',
|
||||
description: '配置驱动热点 UI,click/hover/focus 抛出事件;可绑定业务 Payload。',
|
||||
icon: MousePointer2,
|
||||
},
|
||||
{
|
||||
title: '模型热加载 / 替换',
|
||||
description: '按 ID 管理模型,支持加载、销毁、替换与并行队列,状态事件可订阅。',
|
||||
icon: Layers,
|
||||
},
|
||||
{
|
||||
title: '材质切换与预设',
|
||||
description: '按部件/分组应用材质预设,支持批量切换与回滚默认材质。',
|
||||
icon: Palette,
|
||||
},
|
||||
{
|
||||
title: '事件总线与调试',
|
||||
description: '统一事件接口;sdk.debug 输出加载、事件、性能日志,便于排障。',
|
||||
icon: ShieldCheck,
|
||||
},
|
||||
]
|
||||
|
||||
const featureTerminalScripts: TypeItTerminalSegment[][] = [
|
||||
[
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500">$</span> pnpm create <span class="text-emerald-600">vite</span> my-app</div>',
|
||||
'<div><span class="text-gray-500">$</span> pnpm i <span class="text-amber-600">eslint</span> <span class="text-amber-600">prettier</span></div>',
|
||||
'<div><span class="text-gray-500">✓</span> <span class="text-emerald-600">tsconfig</span> + <span class="text-emerald-600">lint</span> ready</div>',
|
||||
].join(''),
|
||||
speed: 28,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 20,
|
||||
},
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-blue-600">export</span> <span class="text-blue-600">type</span> Use r = { id: <span class="text-blue-600">string</span>; name: <span class="text-blue-600">string</span> }</div>',
|
||||
'<div><span class="text-blue-600">const</span> state = reactive<{ user?: User }>({})</div>',
|
||||
].join(''),
|
||||
speed: 18,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500">roles</span> → <span class="text-gray-500">menus</span> → <span class="text-gray-500">actions</span></div>',
|
||||
'<div><span class="text-gray-500">✓</span> route guard + permission check</div>',
|
||||
].join(''),
|
||||
speed: 22,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 18,
|
||||
},
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-slate-900">columns</span> = [<span class="text-amber-600">"ID"</span>, <span class="text-amber-600">"Name"</span>, <span class="text-amber-600">"Status"</span>]</div>',
|
||||
'<div><span class="text-gray-500">✓</span> filter · export · batch action</div>',
|
||||
].join(''),
|
||||
html: '<div><span class="text-gray-500">sdk.camera.set</span>({ fov: 45, position: [2,1.2,3] })</div>',
|
||||
speed: 26,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-blue-600">@Get</span>(<span class="text-amber-600">"/users"</span>)</div>',
|
||||
'<div><span class="text-blue-600">async</span> list(@Query() q) { <span class="text-blue-600">return</span> svc.list(q) }</div>',
|
||||
].join(''),
|
||||
speed: 18,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 16,
|
||||
},
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500">POST /auth/login</span> → <span class="text-emerald-600">JWT</span></div>',
|
||||
'<div><span class="text-gray-500">DB</span>: users · roles · sessions</div>',
|
||||
].join(''),
|
||||
speed: 22,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-blue-600">import</span> { Engine, Scene } <span class="text-blue-600">from</span> <span class="text-amber-600">"@babylonjs/core"</span></div>',
|
||||
'<div><span class="text-blue-600">const</span> engine = <span class="text-blue-600">new</span> Engine(canvas)</div>',
|
||||
'<div><span class="text-blue-600">const</span> scene = <span class="text-blue-600">new</span> Scene(engine)</div>',
|
||||
].join(''),
|
||||
speed: 20,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 18,
|
||||
},
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500">glTF</span> + <span class="text-gray-500">DRACO</span> + <span class="text-gray-500">LOD</span></div>',
|
||||
'<div><span class="text-gray-500">✓</span> interaction · hotspot · UI sync</div>',
|
||||
].join(''),
|
||||
html: '<div><span class="text-gray-500">sdk.camera.animateTo</span>({ target: [0,0.8,0] }, { duration: 0.8 })</div>',
|
||||
speed: 24,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500"><title></span>品牌官网<span class="text-gray-500"></title></span></div>',
|
||||
'<div><span class="text-gray-500"><meta</span> name=<span class="text-amber-600">"description"</span> ... <span class="text-gray-500">/></span></div>',
|
||||
].join(''),
|
||||
speed: 26,
|
||||
html: '<div><span class="text-gray-500">sdk.lights.update</span>(<span class="text-amber-600">"key"</span>, { intensity: 1.4, castShadow: true })</div>',
|
||||
speed: 24,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 20,
|
||||
deleteSpeed: 18,
|
||||
},
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500">hero</span> · <span class="text-gray-500">features</span> · <span class="text-gray-500">pricing</span> · <span class="text-gray-500">contact</span></div>',
|
||||
'<div><span class="text-gray-500">✓</span> responsive · animation · deploy</div>',
|
||||
].join(''),
|
||||
speed: 18,
|
||||
html: '<div><span class="text-gray-500">sdk.environment.setHDRI</span>("showroom.hdr", { exposure: 1.1, rotation: [0,45,0] })</div>',
|
||||
speed: 22,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-blue-600">Page</span>({ data })</div>',
|
||||
'<div>onLoad(() => fetchList())</div>',
|
||||
'<div><span class="text-gray-500">✓</span> tab · list · detail</div>',
|
||||
].join(''),
|
||||
html: '<div><span class="text-gray-500">sdk.hotspot.render</span>([{ id: "engine", event: "show-engine" }])</div>',
|
||||
speed: 24,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 18,
|
||||
},
|
||||
{
|
||||
html: '<div><span class="text-gray-500">sdk.on</span>(<span class="text-emerald-600">"hotspot:click"</span>, ({ id }) => highlight(id))</div>',
|
||||
speed: 22,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
html: '<div><span class="text-gray-500">sdk.model.load</span>({ id: "car", url: "/car.glb" })</div>',
|
||||
speed: 24,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 18,
|
||||
},
|
||||
{
|
||||
html: '<div><span class="text-gray-500">sdk.model.replace</span>("car", { url: "/car-white.glb" })</div>',
|
||||
speed: 24,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
html: '<div><span class="text-gray-500">sdk.material.apply</span>({ target: "body", material: "paint/blue" })</div>',
|
||||
speed: 24,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 18,
|
||||
},
|
||||
{
|
||||
html: '<div><span class="text-gray-500">sdk.material.batch</span>([{ target: "glass", material: "glass/clear" }])</div>',
|
||||
speed: 22,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
html: '<div><span class="text-gray-500">sdk.on</span>(<span class="text-emerald-600">"model:loaded"</span>, log)</div>',
|
||||
speed: 22,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 18,
|
||||
},
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500">subpackages</span> · <span class="text-gray-500">request</span> · <span class="text-gray-500">upload</span></div>',
|
||||
'<div><span class="text-gray-500">✓</span> publish checklist</div>',
|
||||
].join(''),
|
||||
html: '<div><span class="text-gray-500">sdk.debug</span>(<span class="text-amber-600">true</span>)</div>',
|
||||
speed: 18,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500">Lighthouse</span>: <span class="text-emerald-600">95+</span></div>',
|
||||
'<div><span class="text-gray-500">-</span> image optimize (webp/avif)</div>',
|
||||
'<div><span class="text-gray-500">-</span> code split + cache</div>',
|
||||
].join(''),
|
||||
speed: 16,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 14,
|
||||
},
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500">TTFB</span> ↓ · <span class="text-gray-500">LCP</span> ↓ · <span class="text-gray-500">CLS</span> ↓</div>',
|
||||
'<div><span class="text-gray-500">✓</span> perf budget applied</div>',
|
||||
].join(''),
|
||||
speed: 20,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-purple-600">services</span>:</div>',
|
||||
'<div> <span class="text-slate-900">web</span>: <span class="text-amber-600">nginx</span></div>',
|
||||
'<div> <span class="text-slate-900">api</span>: <span class="text-amber-600">node</span></div>',
|
||||
'<div> <span class="text-slate-900">db</span>: <span class="text-amber-600">postgres</span></div>',
|
||||
].join(''),
|
||||
speed: 18,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 16,
|
||||
},
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500">env</span>: dev · staging · prod</div>',
|
||||
'<div><span class="text-gray-500">✓</span> deploy + rollback plan</div>',
|
||||
].join(''),
|
||||
speed: 22,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500">fix</span>: edge case for empty list</div>',
|
||||
'<div><span class="text-gray-500">test</span>: add regression</div>',
|
||||
'<div><span class="text-gray-500">✓</span> release notes ready</div>',
|
||||
].join(''),
|
||||
speed: 20,
|
||||
pauseAfter: 900,
|
||||
deleteAfter: true,
|
||||
deleteSpeed: 16,
|
||||
},
|
||||
{
|
||||
html: [
|
||||
'<div><span class="text-gray-500">v1.2.0</span> · new feature</div>',
|
||||
'<div><span class="text-gray-500">v1.2.1</span> · hotfix</div>',
|
||||
].join(''),
|
||||
speed: 16,
|
||||
pauseAfter: 1200,
|
||||
},
|
||||
],
|
||||
]
|
||||
|
||||
const featureTerminalAlign: Array<'top' | 'center' | 'bottom'> = [
|
||||
'bottom',
|
||||
'top',
|
||||
'bottom',
|
||||
'top',
|
||||
'bottom',
|
||||
'top',
|
||||
'bottom',
|
||||
'top',
|
||||
'center',
|
||||
]
|
||||
const featureTerminalAlign: Array<'top' | 'center' | 'bottom'> = ['bottom', 'top', 'bottom', 'top', 'bottom', 'center']
|
||||
|
||||
const featureTerminalMinLines = [4, 3, 3, 4, 3, 3, 4, 4, 3]
|
||||
const featureTerminalMinLines = [3, 3, 3, 3, 3, 2]
|
||||
|
||||
const scenarios = [
|
||||
'品牌官网/产品官网:高还原 + 动效 + SEO 基础',
|
||||
'运营活动页:周期紧、改动频繁、注重转化',
|
||||
'后台管理:权限/表格/导入导出/图表看板',
|
||||
'全栈业务:接口 + 数据库 + 管理后台一体化交付',
|
||||
'Web3D 展示:模型加载、交互热点、镜头与动效',
|
||||
'3D 建模/渲染:建模、材质、灯光与出片(PBR 流程)',
|
||||
'小程序:列表/详情/下单/登录/授权等常见场景',
|
||||
'存量项目:重构、性能优化、修 Bug、继续迭代',
|
||||
'上线交付:部署方案、环境配置、监控建议(按需)',
|
||||
'在线产品展示/VR 展厅:相机巡览 + 热点讲解 + 材质切换',
|
||||
'Web3D 配置器:颜色/部件切换、材质预设与模型替换',
|
||||
'工业/汽车/家居互动:分部件热点、事件驱动 UI 联动',
|
||||
'营销与培训页面:按配置渲染热点,引导用户操作或答题',
|
||||
]
|
||||
|
||||
const stats = [
|
||||
{ value: '快', label: '沟通明确后快速推进' },
|
||||
{ value: '稳', label: '注重边界与线上稳定' },
|
||||
{ value: '清晰', label: '按里程碑可验收交付' },
|
||||
{ value: '可维护', label: '工程化与可读结构' },
|
||||
{ value: '可扩展', label: '考虑后续迭代成本' },
|
||||
{ value: '可协作', label: '对接设计/后端/运营' },
|
||||
{ value: '<10 s', label: '完成初始化并渲染首个模型' },
|
||||
{ value: '7 APIs', label: '核心高频能力(相机/灯光/热点/模型/材质)' },
|
||||
{ value: '<3 s', label: '缓存场景下平均热替换耗时' },
|
||||
]
|
||||
</script>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<section class="grid gap-6 pt-3 pb-10">
|
||||
<header class="grid gap-2.5">
|
||||
<h1 class="m-0 text-[28px] font-extrabold tracking-[-0.01em]">案例 / 文章</h1>
|
||||
<p class="m-0 text-gray-600">用 Markdown 维护你的案例、服务说明与技术文章(支持代码复制)。</p>
|
||||
<h1 class="m-0 text-[28px] font-extrabold tracking-[-0.01em]">SDK 文档</h1>
|
||||
<p class="m-0 text-gray-600">Web3D 场景 SDK 的快速入门、核心 API 与配置示例。</p>
|
||||
</header>
|
||||
|
||||
<div class="grid gap-4">
|
||||
@ -27,7 +27,7 @@
|
||||
v-html="htmlContent"
|
||||
/>
|
||||
<div v-else class="rounded-2xl border border-gray-200 bg-gray-50 p-6 text-center text-gray-500">
|
||||
请选择一个文档查看
|
||||
请选择一个文档查看内容
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -39,8 +39,15 @@ import { nextTick, onMounted, ref, watch } from 'vue'
|
||||
|
||||
const BASE_URL = import.meta.env.BASE_URL
|
||||
|
||||
// 案例文件列表,后期可在此添加新文件
|
||||
const caseFiles = ['web3d.md', '建模与渲染.md', 'services.md', 'case-template.md', 'contact.md']
|
||||
// SDK 文档列表
|
||||
const docsFiles = [
|
||||
'quickstart.md',
|
||||
'camera-light.md',
|
||||
'hotspot-events.md',
|
||||
'model-material.md',
|
||||
'config-template.md',
|
||||
'import-guide.md',
|
||||
]
|
||||
|
||||
const mdFiles = ref<string[]>([])
|
||||
const currentFile = ref('')
|
||||
@ -48,7 +55,7 @@ const htmlContent = ref('')
|
||||
const contentRef = ref<HTMLElement>()
|
||||
|
||||
onMounted(async () => {
|
||||
mdFiles.value = caseFiles
|
||||
mdFiles.value = docsFiles
|
||||
if (mdFiles.value.length > 0) {
|
||||
await loadFile(mdFiles.value[0])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user