style(docs): improve typography and layout consistency

- Standardize font sizes and line heights across docs
- Add ChapterIntroduction component for consistent chapter headers
- Fix markdown formatting and whitespace issues
- Improve code block and table styling
- Add font size and line height controls to layout
This commit is contained in:
sanbuphy
2026-01-13 14:42:34 +08:00
parent 7c546e62f8
commit 1d25eb9b9b
20 changed files with 1655 additions and 945 deletions
+7
View File
@@ -79,6 +79,7 @@ stage-{N}/
``` ```
Examples: Examples:
- `stage-1/1.1-introduction-to-ai-ide/index.md` - `stage-1/1.1-introduction-to-ai-ide/index.md`
- `stage-2/backend/2.1-what-is-api/extra2/extra2-what-is-api.md` - `stage-2/backend/2.1-what-is-api/extra2/extra2-what-is-api.md`
@@ -89,18 +90,21 @@ Examples:
The project uses **VitePress 2.0.0-alpha.15** with these key features: The project uses **VitePress 2.0.0-alpha.15** with these key features:
**Configuration** (`docs/.vitepress/config.mjs`): **Configuration** (`docs/.vitepress/config.mjs`):
- **Single Sidebar**: Route-based sidebars configured per path prefix (`/stage-0/`, `/stage-1/`, etc.) - **Single Sidebar**: Route-based sidebars configured per path prefix (`/stage-0/`, `/stage-1/`, etc.)
- **Navigation**: Top nav with links to each stage and appendix - **Navigation**: Top nav with links to each stage and appendix
- **Search**: Local search via `minisearch` (no external API required) - **Search**: Local search via `minisearch` (no external API required)
- **Dark Mode**: Built-in VitePress theme with toggle - **Dark Mode**: Built-in VitePress theme with toggle
**Custom Theme** (`docs/.vitepress/theme/`): **Custom Theme** (`docs/.vitepress/theme/`):
- **Image Viewer**: Viewer.js integration for zoom/rotate/flip on all images - **Image Viewer**: Viewer.js integration for zoom/rotate/flip on all images
- **Typewriter Effect**: TypeIt.js for homepage hero tagline animation - **Typewriter Effect**: TypeIt.js for homepage hero tagline animation
- **Image Optimization**: Automatic image height classes based on aspect ratio - **Image Optimization**: Automatic image height classes based on aspect ratio
- **Custom Layout**: Extends default theme with `Layout.vue` override - **Custom Layout**: Extends default theme with `Layout.vue` override
**Key Theme Behaviors**: **Key Theme Behaviors**:
- Images with aspect ratio > 1.2 get height-limited classes (tall/very-tall/ultra-tall) - Images with aspect ratio > 1.2 get height-limited classes (tall/very-tall/ultra-tall)
- Viewer.js initialized on `.vp-doc` container on each route change - Viewer.js initialized on `.vp-doc` container on each route change
- Typewriter effect only activates on homepage when `frontmatter.hero.tagline` is an array - Typewriter effect only activates on homepage when `frontmatter.hero.tagline` is an array
@@ -116,6 +120,7 @@ The sidebar is defined in `docs/.vitepress/config.mjs`. When adding new chapters
5. Links should not include `index` - use directory path with trailing slash 5. Links should not include `index` - use directory path with trailing slash
Example pattern: Example pattern:
```javascript ```javascript
{ {
text: 'Chapter Title', text: 'Chapter Title',
@@ -134,11 +139,13 @@ Example pattern:
### Deployment ### Deployment
**Vercel** (vercel.json): **Vercel** (vercel.json):
- Build command: `npm run build` - Build command: `npm run build`
- Output directory: `docs/.vitepress/dist` - Output directory: `docs/.vitepress/dist`
- Framework: vitepress - Framework: vitepress
**Preview Production Build**: **Preview Production Build**:
```bash ```bash
npm run build npm run build
npm run preview # Preview built site locally npm run preview # Preview built site locally
+42 -42
View File
@@ -1,4 +1,3 @@
<!-- trigger vercel build --> <!-- trigger vercel build -->
<div align='center'> <div align='center'>
<img src="./assets/head.png" alt="easy-vibe" width="100%"> <img src="./assets/head.png" alt="easy-vibe" width="100%">
@@ -25,6 +24,7 @@
当你尝试用AI写代码,出错不断,时常想放弃,不清楚如何将程序真正上线 😢。 当你尝试用AI写代码,出错不断,时常想放弃,不清楚如何将程序真正上线 😢。
本教程专门设计,从0到1,带你逐步掌握 Vibe Coding 技巧: 本教程专门设计,从0到1,带你逐步掌握 Vibe Coding 技巧:
- 第零阶段:从**小游戏快速入门** 上手 vibe coding 技能 - 第零阶段:从**小游戏快速入门** 上手 vibe coding 技能
- 第一阶段:产品经理视角下的 **vibe coding 技能和业务理解**,实现 Web 应用原型 - 第一阶段:产品经理视角下的 **vibe coding 技能和业务理解**,实现 Web 应用原型
- 第二阶段:学习**前后端开发与 AI 能力**相关的 vibecoding 技巧,完成完整应用 - 第二阶段:学习**前后端开发与 AI 能力**相关的 vibecoding 技巧,完成完整应用
@@ -57,70 +57,70 @@
| 章节 | 关键内容 | 状态 | | 章节 | 关键内容 | 状态 |
| :---------------------------------------------------------------------------------- | :------------------------------------------------ | :--- | | :---------------------------------------------------------------------------------- | :------------------------------------------------ | :--- |
| [初级二:认识 AI IDE 工具](docs/stage-1/1.1-introduction-to-ai-ide/index.md) | 学会使用 IDE,在本地制作小游戏 | ✅ | | [初级二:认识 AI IDE 工具](docs/stage-1/1.1-introduction-to-ai-ide/index.md) | 学会使用 IDE,在本地制作小游戏 | ✅ |
| [初级三:动手做出原型](docs/stage-1/1.2-building-prototype/index.md) | 从需求分析、AI 生成单页面,再到生成多页面产品原型 | ✅ | | [初级三:动手做出原型](docs/stage-1/1.2-building-prototype/index.md) | 从需求分析、AI 生成单页面,再到生成多页面产品原型 | ✅ |
| [初级四:给原型加上 AI 能力](docs/stage-1/1.3-integrating-ai-capabilities/index.md) | 学会接入常见 AI 能力(文本、图片、视频) | ✅ | | [初级四:给原型加上 AI 能力](docs/stage-1/1.3-integrating-ai-capabilities/index.md) | 学会接入常见 AI 能力(文本、图片、视频) | ✅ |
| [初级五:完整项目实战](docs/stage-1/1.4-complete-project-practice/index.md) | 模拟真实场景、接受用户反馈迭代,完整化项目 | ✅ | | [初级五:完整项目实战](docs/stage-1/1.4-complete-project-practice/index.md) | 模拟真实场景、接受用户反馈迭代,完整化项目 | ✅ |
| [大作业:做一个完整的 Web 应用原型并展示](docs/stage-1/1.5-final-project/index.md) | 完整实现应用,展示应用效果 | ✅ | | [大作业:做一个完整的 Web 应用原型并展示](docs/stage-1/1.5-final-project/index.md) | 完整实现应用,展示应用效果 | ✅ |
#### 附录 #### 附录
| 章节 | 关键内容 | 状态 | | 章节 | 关键内容 | 状态 |
| :-------------------------------------------------------------------------- | :---------------------------------------- | :--- | | :-------------------------------------------------------------------------- | :--------------------------------- | :--- |
| [附录A:产品思维补充](docs/stage-1/appendix-a-product-thinking/index.md) | 从零到一做产品需要考虑的思维框架 | ✅ | | [附录A:产品思维补充](docs/stage-1/appendix-a-product-thinking/index.md) | 从零到一做产品需要考虑的思维框架 | ✅ |
| [附录B:常见报错及解决方案](docs/stage-1/appendix-b-common-errors/index.md) | vibe coding 中的常见错误及排查方法 | ✅ | | [附录B:常见报错及解决方案](docs/stage-1/appendix-b-common-errors/index.md) | vibe coding 中的常见错误及排查方法 | ✅ |
### 二、初中级开发工程师 ### 二、初中级开发工程师
#### 前端部分 #### 前端部分
| 章节 | 关键内容 | 状态 | | 章节 | 关键内容 | 状态 |
| :------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------- | :--- | | :------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :--- |
| [前端零:使用 lovart 生产素材](docs/stage-2/frontend/2.0-lovart-assets/) | 学会用 lovart 批量生成人物、场景等视觉素材,为 UI 设计和前端开发提供素材基础 | 🚧 | | [前端零:使用 lovart 生产素材](docs/stage-2/frontend/2.0-lovart-assets/) | 学会用 lovart 批量生成人物、场景等视觉素材,为 UI 设计和前端开发提供素材基础 | 🚧 |
| [前端一:Figma 与 MasterGo 入门](docs/stage-2/frontend/2.1-figma-mastergo/) | 用设计工具梳理信息架构和页面结构,为前端实现打基础 | 🚧 | | [前端一:Figma 与 MasterGo 入门](docs/stage-2/frontend/2.1-figma-mastergo/) | 用设计工具梳理信息架构和页面结构,为前端实现打基础 | 🚧 |
| [前端二:构建第一个现代应用程序-UI 设计](docs/stage-2/frontend/2.2-ui-design/) | 基于设计稿完成组件化界面,实现从设计到代码的第一条链路 | 🚧 | | [前端二:构建第一个现代应用程序-UI 设计](docs/stage-2/frontend/2.2-ui-design/) | 基于设计稿完成组件化界面,实现从设计到代码的第一条链路 | 🚧 |
| [前端三:参考 UI 设计规范与多产品 UI 设计](docs/stage-2/frontend/2.3-multi-product-ui/) | 围绕统一主视觉扩展多产品界面,练习系统化设计能力 | 🚧 | | [前端三:参考 UI 设计规范与多产品 UI 设计](docs/stage-2/frontend/2.3-multi-product-ui/) | 围绕统一主视觉扩展多产品界面,练习系统化设计能力 | 🚧 |
| [前端四:一起做霍格沃茨画像](docs/stage-2/frontend/2.4-hogwarts-portraits/chapter4-lets-build-hogwarts-portraits.md) | 从 0 到 1 做出接入 AI 能力的前端应用,串联设计与开发 | ✅ | | [前端四:一起做霍格沃茨画像](docs/stage-2/frontend/2.4-hogwarts-portraits/chapter4-lets-build-hogwarts-portraits.md) | 从 0 到 1 做出接入 AI 能力的前端应用,串联设计与开发 | ✅ |
#### 后端与全栈部分 #### 后端与全栈部分
| 章节 | 关键内容 | 状态 | | 章节 | 关键内容 | 状态 |
| :------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------ | :--- | | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------ | :--- |
| [后端一:什么是 API](docs/stage-2/backend/2.1-what-is-api/extra2/extra2-what-is-api.md) | 理解 HTTP 接口与请求响应模型,为后端集成与联调做准备 | ✅ | | [后端一:什么是 API](docs/stage-2/backend/2.1-what-is-api/extra2/extra2-what-is-api.md) | 理解 HTTP 接口与请求响应模型,为后端集成与联调做准备 | ✅ |
| [后端二:从数据库到 Supabase](docs/stage-2/backend/2.2-database-supabase/chapter5/chapter5-from-database-to-supabase.md) | 在 Supabase 上落地数据库和 API,打通数据模型与前端页面 | ✅ | | [后端二:从数据库到 Supabase](docs/stage-2/backend/2.2-database-supabase/chapter5/chapter5-from-database-to-supabase.md) | 在 Supabase 上落地数据库和 API,打通数据模型与前端页面 | ✅ |
| [后端三:大模型辅助编写接口代码与接口文档](docs/stage-2/backend/2.3-ai-interface-code/) | 用大模型协助生成接口与数据库文档及代码,实现可读可测的后端 | 🚧 | | [后端三:大模型辅助编写接口代码与接口文档](docs/stage-2/backend/2.3-ai-interface-code/) | 用大模型协助生成接口与数据库文档及代码,实现可读可测的后端 | 🚧 |
| [后端四:Git 工作流](docs/stage-2/backend/2.4-git-workflow/extra1/extra1-what-is-git-and-what-is-github.md) | 在 Git 工作流中管理代码,进行版本控制和协作 | ✅ | | [后端四:Git 工作流](docs/stage-2/backend/2.4-git-workflow/extra1/extra1-what-is-git-and-what-is-github.md) | 在 Git 工作流中管理代码,进行版本控制和协作 | ✅ |
| [后端五:Zeabur 部署](docs/stage-2/backend/2.5-zeabur-deployment/extra6/extra6-zeabur-what-is-it-and-how-to-deploy-web-applications.md) | 将应用部署到 Zeabur 上线 | ✅ | | [后端五:Zeabur 部署](docs/stage-2/backend/2.5-zeabur-deployment/extra6/extra6-zeabur-what-is-it-and-how-to-deploy-web-applications.md) | 将应用部署到 Zeabur 上线 | ✅ |
| [后端六:现代 CLI 开发工具](docs/stage-2/backend/2.6-modern-cli/extra7/extra7-cli-ai-coding-tools-and-the-principles-of-test-driven-development.md) | 使用 CLI 类 AI 编程工具加速开发与调试,形成个人工程化工作流 | ✅ | | [后端六:现代 CLI 开发工具](docs/stage-2/backend/2.6-modern-cli/extra7/extra7-cli-ai-coding-tools-and-the-principles-of-test-driven-development.md) | 使用 CLI 类 AI 编程工具加速开发与调试,形成个人工程化工作流 | ✅ |
| [后端七:如何集成 Stripe 等收费系统](docs/stage-2/backend/2.7-stripe-payment/) | 接入支付系统,完成收费链路与基础结算流程 | 🚧 | | [后端七:如何集成 Stripe 等收费系统](docs/stage-2/backend/2.7-stripe-payment/) | 接入支付系统,完成收费链路与基础结算流程 | 🚧 |
| [大作业 1:构建第一个现代应用程序-全栈应用](docs/stage-2/assignments/2.1-fullstack-app/) | 综合前端、后端与支付模块,完成可上线的全栈 Web 应用 | 🚧 | | [大作业 1:构建第一个现代应用程序-全栈应用](docs/stage-2/assignments/2.1-fullstack-app/) | 综合前端、后端与支付模块,完成可上线的全栈 Web 应用 | 🚧 |
| [大作业 2:现代前端组件库 + Trae 实战](docs/stage-2/assignments/2.2-modern-frontend-trae/) | 使用现代前端组件库与 Trae,独立完成可登录注册并支持收费的产品 | 🚧 | | [大作业 2:现代前端组件库 + Trae 实战](docs/stage-2/assignments/2.2-modern-frontend-trae/) | 使用现代前端组件库与 Trae,独立完成可登录注册并支持收费的产品 | 🚧 |
#### AI 能力附录 #### AI 能力附录
| 章节 | 关键内容 | 状态 | | 章节 | 关键内容 | 状态 |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------- | :--- | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------- | :--- |
| [AI 一:Dify 入门与知识库集成](docs/stage-2/ai-capabilities/2.1-dify-knowledge-base/chapter3/chapter3-getting-started-with-dify-and-its-knowledge-base-integration.md) | 用 Dify Workflow 与基础 RAG 搭建工具类产品,为后续应用升级打样 | ✅ | | [AI 一:Dify 入门与知识库集成](docs/stage-2/ai-capabilities/2.1-dify-knowledge-base/chapter3/chapter3-getting-started-with-dify-and-its-knowledge-base-integration.md) | 用 Dify Workflow 与基础 RAG 搭建工具类产品,为后续应用升级打样 | ✅ |
| [AI 二:学会查询 AI 词典与集成多模态 API](docs/stage-2/ai-capabilities/2.2-multimodal-api/extra3/extra3-ai-capability-starter-handbook.md) | 学会查找合适的模型与 API,并把文本、图像等多模态能力接入产品 | 🚧 | | [AI 二:学会查询 AI 词典与集成多模态 API](docs/stage-2/ai-capabilities/2.2-multimodal-api/extra3/extra3-ai-capability-starter-handbook.md) | 学会查找合适的模型与 API,并把文本、图像等多模态能力接入产品 | 🚧 |
### 三、高级开发工程师 ### 三、高级开发工程师
| 章节 | 关键内容 | 状态 | | 章节 | 关键内容 | 状态 |
| :-------------------------------------------------------------------- | :----------------------------------------------------------- | :--- | | :------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------- | :--- |
| [高级一:MCP 与 ClaudeCode Skills](docs/stage-3/core-skills/3.1-mcp-claudecode-skills/) | 通过 MCP 与 Skills 扩展 IDE 能力,把外部服务接成工具 | 🚧 | | [高级一:MCP 与 ClaudeCode Skills](docs/stage-3/core-skills/3.1-mcp-claudecode-skills/) | 通过 MCP 与 Skills 扩展 IDE 能力,把外部服务接成工具 | 🚧 |
| [高级二:如何让 Coding Tools 长时间工作](docs/stage-3/core-skills/3.2-long-running-tasks/) | 设计和配置长时间运行的任务,让 Coding Tools 更稳定可靠 | 🚧 | | [高级二:如何让 Coding Tools 长时间工作](docs/stage-3/core-skills/3.2-long-running-tasks/) | 设计和配置长时间运行的任务,让 Coding Tools 更稳定可靠 | 🚧 |
| [高级三:多平台开发:如何构建微信小程序](docs/stage-3/cross-platform/3.3-wechat-miniprogram/) | 了解微信小程序生态,从官方模板到上线完成一个前端小程序 | ✅ | | [高级三:多平台开发:如何构建微信小程序](docs/stage-3/cross-platform/3.3-wechat-miniprogram/) | 了解微信小程序生态,从官方模板到上线完成一个前端小程序 | ✅ |
| [高级四:多平台开发:如何构建微信小程序-包含后端](docs/stage-3/cross-platform/3.4-wechat-miniprogram-backend/) | 在小程序中接入数据库与后端逻辑,打通完整业务闭环 | 🚧 | | [高级四:多平台开发:如何构建微信小程序-包含后端](docs/stage-3/cross-platform/3.4-wechat-miniprogram-backend/) | 在小程序中接入数据库与后端逻辑,打通完整业务闭环 | 🚧 |
| [高级五:多平台开发:如何构建安卓程序](docs/stage-3/cross-platform/3.5-android-app/) | 使用 Expo 等工具,完成 Web/原生一体化的安卓应用开发 | 🚧 | | [高级五:多平台开发:如何构建安卓程序](docs/stage-3/cross-platform/3.5-android-app/) | 使用 Expo 等工具,完成 Web/原生一体化的安卓应用开发 | 🚧 |
| [高级六:多平台开发:如何构建 iOS 程序](docs/stage-3/cross-platform/3.6-ios-app/) | 使用 Expo 等工具,完成 Web/原生一体化的 iOS 应用开发 | 🚧 | | [高级六:多平台开发:如何构建 iOS 程序](docs/stage-3/cross-platform/3.6-ios-app/) | 使用 Expo 等工具,完成 Web/原生一体化的 iOS 应用开发 | 🚧 |
| [高级七:如何构建属于自己的个人网页与学术博客](docs/stage-3/personal-brand/3.7-personal-website-blog/) | 从选型、搭建到部署,构建展示个人项目与学术成果的长久在线主页 | 🚧 | | [高级七:如何构建属于自己的个人网页与学术博客](docs/stage-3/personal-brand/3.7-personal-website-blog/) | 从选型、搭建到部署,构建展示个人项目与学术成果的长久在线主页 | 🚧 |
#### AI 能力附录 #### AI 能力附录
| 章节 | 关键内容 | 状态 | | 章节 | 关键内容 | 状态 |
| :-------------------------------------------------------------------- | :------------------------------------------------------ | :--- | | :-------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :--- |
| [高级 AI 一:什么是 RAG 以及它如何工作](docs/stage-3/ai-advanced/3.a1-rag-introduction/extra5-what-is-rag-and-how-does-it-work-and-future.md) | 系统理解 RAG 原理与常见架构,为复杂应用提供知识检索基础 | ✅ | | [高级 AI 一:什么是 RAG 以及它如何工作](docs/stage-3/ai-advanced/3.a1-rag-introduction/extra5-what-is-rag-and-how-does-it-work-and-future.md) | 系统理解 RAG 原理与常见架构,为复杂应用提供知识检索基础 | ✅ |
| [高级 AI 二:中高级 RAG 与工作流编排:以 LangGraph 为例](docs/stage-3/ai-advanced/3.a2-langgraph-advanced-rag/) | 使用 LangGraph 等工具设计多步工作流与中高级 RAG 系统 | 🚧 | | [高级 AI 二:中高级 RAG 与工作流编排:以 LangGraph 为例](docs/stage-3/ai-advanced/3.a2-langgraph-advanced-rag/) | 使用 LangGraph 等工具设计多步工作流与中高级 RAG 系统 | 🚧 |
## 如何学习 ## 如何学习
+1 -2
View File
@@ -71,8 +71,7 @@ export default defineConfig({
{ {
text: '附录 B:常见报错及解决方案', text: '附录 B:常见报错及解决方案',
link: '/stage-1/appendix-b-common-errors/' link: '/stage-1/appendix-b-common-errors/'
} },
,
{ {
text: '附录示例:贪吃蛇游戏教程', text: '附录示例:贪吃蛇游戏教程',
link: '/stage-1/appendix-articles/example0-1/vibe-coding-tools-snake-game-tutorial' link: '/stage-1/appendix-articles/example0-1/vibe-coding-tools-snake-game-tutorial'
+238 -2
View File
@@ -2,6 +2,7 @@
import DefaultTheme from 'vitepress/theme' import DefaultTheme from 'vitepress/theme'
import { useData } from 'vitepress' import { useData } from 'vitepress'
import TextType from './components/TextType.vue' import TextType from './components/TextType.vue'
import { onMounted, ref, watch } from 'vue'
const { frontmatter } = useData() const { frontmatter } = useData()
@@ -12,16 +13,251 @@ const homeTaglineTyping = {
postDeletingDelay: 500, postDeletingDelay: 500,
deletingSpeed: 18 deletingSpeed: 18
} }
const FONT_SIZE_STORAGE_KEY = 'ev-doc-font-size'
const LINE_HEIGHT_STORAGE_KEY = 'ev-doc-line-height'
const MIN_FONT_SIZE = 12
const MAX_FONT_SIZE = 18
const DEFAULT_FONT_SIZE = 13
const MIN_LINE_HEIGHT = 1.25
const MAX_LINE_HEIGHT = 1.8
const DEFAULT_LINE_HEIGHT = 1.5
const fontSize = ref(DEFAULT_FONT_SIZE)
const lineHeight = ref(DEFAULT_LINE_HEIGHT)
const isHydrated = ref(false)
const clampFontSize = (value) => {
const numeric = Number(value)
if (!Number.isFinite(numeric)) return DEFAULT_FONT_SIZE
return Math.min(MAX_FONT_SIZE, Math.max(MIN_FONT_SIZE, numeric))
}
const clampLineHeight = (value) => {
const numeric = Number(value)
if (!Number.isFinite(numeric)) return DEFAULT_LINE_HEIGHT
return Math.min(MAX_LINE_HEIGHT, Math.max(MIN_LINE_HEIGHT, numeric))
}
const applyFontSize = (size) => {
if (typeof document === 'undefined') return
document.documentElement.style.setProperty('--ev-doc-font-size', `${size}px`)
}
const applyLineHeight = (value) => {
if (typeof document === 'undefined') return
document.documentElement.style.setProperty('--ev-doc-line-height', String(value))
}
const decreaseFontSize = () => {
fontSize.value = clampFontSize(fontSize.value - 1)
}
const increaseFontSize = () => {
fontSize.value = clampFontSize(fontSize.value + 1)
}
const resetFontSize = () => {
fontSize.value = DEFAULT_FONT_SIZE
}
const resetLineHeight = () => {
lineHeight.value = DEFAULT_LINE_HEIGHT
}
onMounted(() => {
const saved = clampFontSize(localStorage.getItem(FONT_SIZE_STORAGE_KEY))
const savedLineHeight = clampLineHeight(localStorage.getItem(LINE_HEIGHT_STORAGE_KEY))
fontSize.value = saved
lineHeight.value = savedLineHeight
applyFontSize(saved)
applyLineHeight(savedLineHeight)
isHydrated.value = true
})
watch(fontSize, (next) => {
if (!isHydrated.value) return
const normalized = clampFontSize(next)
applyFontSize(normalized)
localStorage.setItem(FONT_SIZE_STORAGE_KEY, String(normalized))
})
watch(lineHeight, (next) => {
if (!isHydrated.value) return
const normalized = clampLineHeight(next)
applyLineHeight(normalized)
localStorage.setItem(LINE_HEIGHT_STORAGE_KEY, String(normalized))
})
</script> </script>
<template> <template>
<DefaultTheme.Layout> <DefaultTheme.Layout>
<template #nav-bar-content-after>
<ClientOnly>
<el-popover placement="bottom-end" trigger="click" :width="260">
<template #reference>
<button
class="ev-fontsize-button"
type="button"
aria-label="阅读设置"
style="margin-left: 16px; padding: 0; width: 32px;"
>
<el-icon :size="16"><Setting /></el-icon>
</button>
</template>
<div class="ev-fontsize-panel">
<div class="ev-setting-group">
<div class="ev-setting-header">
<div class="ev-setting-title">字号</div>
<div class="ev-setting-value">{{ fontSize }}px</div>
</div>
<div class="ev-fontsize-actions">
<button
class="ev-fontsize-action"
type="button"
@click="decreaseFontSize"
>
A-
</button>
<button
class="ev-fontsize-action"
type="button"
@click="resetFontSize"
>
默认
</button>
<button
class="ev-fontsize-action"
type="button"
@click="increaseFontSize"
>
A+
</button>
</div>
<el-slider v-model="fontSize" :min="MIN_FONT_SIZE" :max="MAX_FONT_SIZE" :step="1" />
</div>
<div class="ev-setting-group">
<div class="ev-setting-header">
<div class="ev-setting-title">行距</div>
<div class="ev-setting-value">{{ lineHeight.toFixed(2) }}</div>
</div>
<div class="ev-fontsize-actions">
<button class="ev-fontsize-action" type="button" @click="resetLineHeight">
默认
</button>
<button
class="ev-fontsize-action"
type="button"
@click="lineHeight = clampLineHeight(lineHeight - 0.05)"
>
更紧
</button>
<button
class="ev-fontsize-action"
type="button"
@click="lineHeight = clampLineHeight(lineHeight + 0.05)"
>
更松
</button>
</div>
<el-slider
v-model="lineHeight"
:min="MIN_LINE_HEIGHT"
:max="MAX_LINE_HEIGHT"
:step="0.05"
/>
</div>
</div>
</el-popover>
</ClientOnly>
</template>
<template #home-hero-info-after> <template #home-hero-info-after>
<div v-if="frontmatter.layout === 'home' && frontmatter.hero?.tagline" class="vp-typed-tagline"> <div
v-if="frontmatter.layout === 'home' && frontmatter.hero?.tagline"
class="vp-typed-tagline"
>
<ClientOnly> <ClientOnly>
<TextType :text="frontmatter.hero.tagline" v-bind="homeTaglineTyping" :loop="true" /> <TextType
:text="frontmatter.hero.tagline"
v-bind="homeTaglineTyping"
:loop="true"
/>
</ClientOnly> </ClientOnly>
</div> </div>
</template> </template>
</DefaultTheme.Layout> </DefaultTheme.Layout>
</template> </template>
<style>
.ev-fontsize-button {
display: inline-flex;
align-items: center;
justify-content: center;
height: 32px;
min-width: 32px;
padding: 0 10px;
border: 1px solid var(--vp-c-divider);
border-radius: 999px;
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
font-size: 13px;
font-weight: 600;
line-height: 1;
cursor: pointer;
}
.ev-fontsize-button:hover {
border-color: var(--vp-c-brand);
color: var(--vp-c-brand);
}
.ev-fontsize-panel {
display: grid;
gap: 12px;
}
.ev-setting-group {
display: grid;
gap: 8px;
}
.ev-setting-header {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
}
.ev-setting-title {
font-size: 13px;
font-weight: 600;
color: var(--vp-c-text-1);
}
.ev-setting-value {
font-size: 12px;
color: var(--vp-c-text-2);
}
.ev-fontsize-actions {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.ev-fontsize-action {
height: 32px;
border: 1px solid var(--vp-c-divider);
border-radius: 10px;
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
font-size: 13px;
cursor: pointer;
}
.ev-fontsize-action:hover {
border-color: var(--vp-c-brand);
color: var(--vp-c-brand);
}
</style>
@@ -0,0 +1,266 @@
<script setup>
import { computed } from 'vue'
const props = defineProps({
duration: {
type: String,
default: ''
},
expectedOutput: {
type: String,
default: ''
},
coreOutput: {
type: String,
default: ''
},
assignment: {
type: String,
default: ''
},
tags: {
type: Array,
default: () => []
}
})
const hasMeta = computed(() => props.duration || props.expectedOutput || props.coreOutput || props.assignment)
const hasTags = computed(() => props.tags && props.tags.length > 0)
</script>
<template>
<div class="chapter-introduction">
<!-- Learning Objective -->
<div class="objective-section">
<div class="objective-label">
<span class="icon">🎯</span>
<span class="title">本章学习目标</span>
</div>
<div class="content">
<!-- If tags are provided, show tags list -->
<div v-if="hasTags" class="tags-container">
<span v-for="(tag, index) in tags" :key="index" class="objective-tag">
{{ tag }}
</span>
</div>
<!-- Slot content (full description) always rendered below tags if tags exist, or alone if not -->
<div class="description-text" :class="{ 'has-tags': hasTags }">
<slot></slot>
</div>
</div>
</div>
<!-- Metrics Grid -->
<div v-if="hasMeta" class="metrics-grid">
<!-- Duration Card -->
<div v-if="duration" class="metric-card time-card">
<div class="card-icon"></div>
<div class="card-content">
<div class="card-label">预计耗时</div>
<div class="card-value" v-html="duration"></div>
</div>
</div>
<!-- Output Card -->
<div v-if="expectedOutput || coreOutput" class="metric-card output-card">
<div class="card-icon">📦</div>
<div class="card-content">
<div class="card-label">预期产出</div>
<div class="output-container">
<div v-if="coreOutput" class="core-output">{{ coreOutput }}</div>
<div v-if="expectedOutput" class="output-desc" v-html="expectedOutput"></div>
</div>
</div>
</div>
<!-- Assignment Card -->
<div v-if="assignment" class="metric-card task-card">
<div class="card-icon">📝</div>
<div class="card-content">
<div class="card-label">课后任务</div>
<div class="card-value" v-html="assignment"></div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.chapter-introduction {
margin: 24px 0;
border-radius: 16px;
background-color: var(--vp-c-bg-soft);
border: 1px solid var(--vp-c-divider);
overflow: hidden;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04);
}
.objective-section {
padding: 24px 28px;
background: linear-gradient(to right, rgba(var(--vp-c-brand-rgb), 0.05), transparent);
border-bottom: 1px dashed var(--vp-c-divider);
}
.objective-label {
display: flex;
align-items: center;
margin-bottom: 16px;
color: var(--vp-c-brand);
}
.icon {
font-size: 20px;
margin-right: 8px;
}
.title {
font-size: 14px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.content {
font-size: 16px;
line-height: 1.7;
color: var(--vp-c-text-1);
font-weight: 500;
}
/* Tags Styling */
.tags-container {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.description-text {
font-size: 16px;
line-height: 1.7;
color: var(--vp-c-text-1);
}
.description-text.has-tags {
margin-top: 16px;
font-size: 14px;
color: var(--vp-c-text-2);
border-top: 1px solid var(--vp-c-divider);
padding-top: 12px;
}
.objective-tag {
display: inline-flex;
align-items: center;
padding: 6px 14px;
background-color: var(--vp-c-bg-alt);
border: 1px solid var(--vp-c-divider);
border-radius: 99px;
font-size: 14px;
font-weight: 600;
color: var(--vp-c-text-1);
transition: all 0.2s;
}
.objective-tag:hover {
border-color: var(--vp-c-brand);
color: var(--vp-c-brand);
background-color: var(--vp-c-bg-soft);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
/* Metrics Grid */
.metrics-grid {
display: flex;
flex-wrap: wrap;
gap: 1px;
background-color: var(--vp-c-divider);
border-top: 1px solid var(--vp-c-divider);
}
.metric-card {
flex: 1 1 240px;
background-color: var(--vp-c-bg-soft);
padding: 20px 24px;
display: flex;
align-items: flex-start;
gap: 16px;
transition: background-color 0.2s;
}
.metric-card:hover {
background-color: var(--vp-c-bg-alt);
}
.card-icon {
font-size: 24px;
line-height: 1;
padding-top: 2px;
}
.card-content {
flex: 1;
display: flex;
flex-direction: column;
}
.card-label {
font-size: 12px;
color: var(--vp-c-text-2);
margin-bottom: 8px;
font-weight: 600;
text-transform: uppercase;
}
.card-value {
font-size: 14px;
line-height: 1.5;
color: var(--vp-c-text-1);
}
.card-value :deep(strong) {
display: inline-block;
color: var(--vp-c-brand-dark);
font-weight: 800;
font-size: 16px;
margin-top: 2px;
}
/* Output Container Styling */
.output-container {
display: flex;
flex-direction: column;
gap: 4px;
}
.core-output {
font-size: 18px;
font-weight: 800;
color: var(--vp-c-brand);
line-height: 1.4;
margin-bottom: 2px;
}
.output-desc {
font-size: 13px;
color: var(--vp-c-text-2);
line-height: 1.4;
}
.output-desc :deep(strong) {
color: var(--vp-c-text-1);
font-weight: 600;
}
/* Mobile adjustments */
@media (max-width: 640px) {
.metric-card {
padding: 16px 20px;
flex-basis: 100%;
}
.objective-section {
padding: 20px;
}
}
</style>
+4 -4
View File
@@ -18,10 +18,10 @@ defineProps({
items: { items: {
type: Array, type: Array,
default: () => [ default: () => [
{ title: "困境与机会", description: "普通人的编程新可能" }, { title: '困境与机会', description: '普通人的编程新可能' },
{ title: "能力初探", description: "60秒极速开发体验" }, { title: '能力初探', description: '60秒极速开发体验' },
{ title: "原生实战", description: "打造AI原生贪吃蛇" }, { title: '原生实战', description: '打造AI原生贪吃蛇' },
{ title: "拓展创造", description: "举一反三做游戏" } { title: '拓展创造', description: '举一反三做游戏' }
] ]
} }
}) })
+55 -18
View File
@@ -1,5 +1,12 @@
<script setup> <script setup>
import { computed, onMounted, onUnmounted, ref, useAttrs, watchEffect } from 'vue' import {
computed,
onMounted,
onUnmounted,
ref,
useAttrs,
watchEffect
} from 'vue'
const props = defineProps({ const props = defineProps({
text: { text: {
@@ -91,7 +98,9 @@ const currentTextIndex = ref(0)
const isVisible = ref(!props.startOnVisible) const isVisible = ref(!props.startOnVisible)
const containerRef = ref(null) const containerRef = ref(null)
const textArray = computed(() => (Array.isArray(props.text) ? props.text : [props.text])) const textArray = computed(() =>
Array.isArray(props.text) ? props.text : [props.text]
)
const cursorStyle = computed(() => ({ const cursorStyle = computed(() => ({
animationDuration: `${props.cursorBlinkDuration}s` animationDuration: `${props.cursorBlinkDuration}s`
@@ -104,8 +113,14 @@ const currentColor = computed(() => {
const getRandomSpeed = () => { const getRandomSpeed = () => {
if (!props.variableSpeed) return props.typingSpeed if (!props.variableSpeed) return props.typingSpeed
const min = typeof props.variableSpeed.min === 'number' ? props.variableSpeed.min : props.typingSpeed const min =
const max = typeof props.variableSpeed.max === 'number' ? props.variableSpeed.max : props.typingSpeed typeof props.variableSpeed.min === 'number'
? props.variableSpeed.min
: props.typingSpeed
const max =
typeof props.variableSpeed.max === 'number'
? props.variableSpeed.max
: props.typingSpeed
if (max <= min) return min if (max <= min) return min
return Math.random() * (max - min) + min return Math.random() * (max - min) + min
} }
@@ -114,7 +129,7 @@ let observer
onMounted(() => { onMounted(() => {
if (!props.startOnVisible || !containerRef.value) return if (!props.startOnVisible || !containerRef.value) return
observer = new IntersectionObserver( observer = new IntersectionObserver(
entries => { (entries) => {
for (const entry of entries) { for (const entry of entries) {
if (entry.isIntersecting) { if (entry.isIntersecting) {
isVisible.value = true isVisible.value = true
@@ -131,7 +146,7 @@ onUnmounted(() => {
if (observer) observer.disconnect() if (observer) observer.disconnect()
}) })
watchEffect(onCleanup => { watchEffect((onCleanup) => {
if (!isVisible.value) return if (!isVisible.value) return
if (!textArray.value.length) { if (!textArray.value.length) {
@@ -140,13 +155,16 @@ watchEffect(onCleanup => {
} }
const currentText = textArray.value[currentTextIndex.value] ?? '' const currentText = textArray.value[currentTextIndex.value] ?? ''
const processedText = props.reverseMode ? String(currentText).split('').reverse().join('') : String(currentText) const processedText = props.reverseMode
? String(currentText).split('').reverse().join('')
: String(currentText)
if (!isClient) { if (!isClient) {
return return
} }
const shouldStopAtEnd = !props.loop && currentTextIndex.value === textArray.value.length - 1 const shouldStopAtEnd =
!props.loop && currentTextIndex.value === textArray.value.length - 1
let timeoutId let timeoutId
@@ -155,11 +173,15 @@ watchEffect(onCleanup => {
if (!displayedText.value) { if (!displayedText.value) {
isDeleting.value = false isDeleting.value = false
if (props.onSentenceComplete) { if (props.onSentenceComplete) {
props.onSentenceComplete(textArray.value[currentTextIndex.value], currentTextIndex.value) props.onSentenceComplete(
textArray.value[currentTextIndex.value],
currentTextIndex.value
)
} }
if (shouldStopAtEnd) return if (shouldStopAtEnd) return
timeoutId = setTimeout(() => { timeoutId = setTimeout(() => {
currentTextIndex.value = (currentTextIndex.value + 1) % textArray.value.length currentTextIndex.value =
(currentTextIndex.value + 1) % textArray.value.length
currentCharIndex.value = 0 currentCharIndex.value = 0
}, props.postDeletingDelay) }, props.postDeletingDelay)
return return
@@ -172,10 +194,13 @@ watchEffect(onCleanup => {
} }
if (currentCharIndex.value < processedText.length) { if (currentCharIndex.value < processedText.length) {
timeoutId = setTimeout(() => { timeoutId = setTimeout(
displayedText.value += processedText[currentCharIndex.value] () => {
currentCharIndex.value += 1 displayedText.value += processedText[currentCharIndex.value]
}, props.variableSpeed ? getRandomSpeed() : props.typingSpeed) currentCharIndex.value += 1
},
props.variableSpeed ? getRandomSpeed() : props.typingSpeed
)
return return
} }
@@ -185,7 +210,11 @@ watchEffect(onCleanup => {
}, props.pauseDuration) }, props.pauseDuration)
} }
if (currentCharIndex.value === 0 && !isDeleting.value && !displayedText.value) { if (
currentCharIndex.value === 0 &&
!isDeleting.value &&
!displayedText.value
) {
timeoutId = setTimeout(schedule, props.initialDelay) timeoutId = setTimeout(schedule, props.initialDelay)
} else { } else {
schedule() schedule()
@@ -197,7 +226,9 @@ watchEffect(onCleanup => {
const shouldHideCursor = computed(() => { const shouldHideCursor = computed(() => {
if (!props.hideCursorWhileTyping) return false if (!props.hideCursorWhileTyping) return false
const currentText = textArray.value[currentTextIndex.value] ?? '' const currentText = textArray.value[currentTextIndex.value] ?? ''
const processedText = props.reverseMode ? String(currentText).split('').reverse().join('') : String(currentText) const processedText = props.reverseMode
? String(currentText).split('').reverse().join('')
: String(currentText)
return currentCharIndex.value < processedText.length || isDeleting.value return currentCharIndex.value < processedText.length || isDeleting.value
}) })
</script> </script>
@@ -209,13 +240,19 @@ const shouldHideCursor = computed(() => {
:class="['text-type', className]" :class="['text-type', className]"
v-bind="attrs" v-bind="attrs"
> >
<span class="text-type__content" :style="{ color: currentColor || 'inherit' }"> <span
class="text-type__content"
:style="{ color: currentColor || 'inherit' }"
>
{{ displayedText }} {{ displayedText }}
</span> </span>
<span <span
v-if="showCursor" v-if="showCursor"
class="text-type__cursor" class="text-type__cursor"
:class="[cursorClassName, shouldHideCursor ? 'text-type__cursor--hidden' : '']" :class="[
cursorClassName,
shouldHideCursor ? 'text-type__cursor--hidden' : ''
]"
:style="cursorStyle" :style="cursorStyle"
> >
{{ cursorCharacter }} {{ cursorCharacter }}
+12 -7
View File
@@ -7,13 +7,17 @@ import TypeIt from 'typeit'
import { onMounted, watch, nextTick } from 'vue' import { onMounted, watch, nextTick } from 'vue'
import { useRoute, useData } from 'vitepress' import { useRoute, useData } from 'vitepress'
import './style.css' import './style.css'
import Layout from './Layout.vue'
import StepBar from './components/StepBar.vue' import StepBar from './components/StepBar.vue'
import ChapterIntroduction from './components/ChapterIntroduction.vue'
export default { export default {
extends: DefaultTheme, extends: DefaultTheme,
Layout,
enhanceApp({ app }) { enhanceApp({ app }) {
app.use(ElementPlus) app.use(ElementPlus)
app.component('StepBar', StepBar) app.component('StepBar', StepBar)
app.component('ChapterIntroduction', ChapterIntroduction)
}, },
setup() { setup() {
const route = useRoute() const route = useRoute()
@@ -75,7 +79,7 @@ export default {
}) })
taglineData.forEach((text) => { taglineData.forEach((text) => {
typeIt.type(text).pause(2000).delete().pause(500) typeIt.type(text).pause(2000).delete().pause(500)
}) })
typeIt.go() typeIt.go()
@@ -85,7 +89,7 @@ export default {
const optimizeImages = () => { const optimizeImages = () => {
const images = document.querySelectorAll('.vp-doc img') const images = document.querySelectorAll('.vp-doc img')
images.forEach(img => { images.forEach((img) => {
if (img.complete) { if (img.complete) {
applyImageStyle(img) applyImageStyle(img)
} else { } else {
@@ -133,11 +137,12 @@ export default {
watch( watch(
() => route.path, () => route.path,
() => nextTick(() => { () =>
initViewer() nextTick(() => {
initTypewriter() initViewer()
optimizeImages() initTypewriter()
}) optimizeImages()
})
) )
} }
} }
+64 -2
View File
@@ -2,10 +2,70 @@
/* Easy-Vibe Theme Fix v2025-01-12 */ /* Easy-Vibe Theme Fix v2025-01-12 */
/* 通过变量控制分组底部留白(默认 24px) */ /* 通过变量控制分组底部留白(默认 24px) */
--vp-sidebar-nav-section-gap: 8px; --vp-sidebar-nav-section-gap: 8px;
--ev-doc-font-size: 13px;
--ev-doc-line-height: 1.5;
} }
.vp-doc { .vp-doc {
font-size: 15px; font-size: var(--ev-doc-font-size);
line-height: var(--ev-doc-line-height);
--el-font-size-extra-large: calc(var(--ev-doc-font-size) + 6px);
--el-font-size-large: calc(var(--ev-doc-font-size) + 4px);
--el-font-size-medium: calc(var(--ev-doc-font-size) + 2px);
--el-font-size-base: var(--ev-doc-font-size);
--el-font-size-small: calc(var(--ev-doc-font-size) - 1px);
--el-font-size-extra-small: calc(var(--ev-doc-font-size) - 2px);
--el-font-line-height-primary: var(--ev-doc-line-height);
}
.vp-doc :where(p, ul, ol, table, blockquote, pre, details, figure) {
margin: 10px 0;
}
.vp-doc :where(li) {
margin: 4px 0;
}
.vp-doc :where(ul, ol) {
padding-left: 1.15em;
}
.vp-doc :where(h1, h2, h3, h4, h5, h6) {
line-height: 1.3;
}
.vp-doc :where(h1) {
margin: 22px 0 12px;
}
.vp-doc :where(h2) {
margin: 20px 0 10px;
}
.vp-doc h2 {
margin: 16px 0 8px !important;
padding-top: 10px !important;
border-top: 0 !important;
}
.vp-doc :where(h3) {
margin: 18px 0 8px;
}
.vp-doc :where(h4, h5, h6) {
margin: 16px 0 8px;
}
.vp-doc :where(hr) {
margin: 14px 0;
}
.vp-doc :where(th, td) {
padding: 6px 10px;
}
.vp-doc :where(:not(pre) > code) {
font-size: 0.95em;
} }
/* 生产环境(带 data-v-* 的 scoped 样式)会比 class 选择器更高优先级。 /* 生产环境(带 data-v-* 的 scoped 样式)会比 class 选择器更高优先级。
@@ -44,7 +104,9 @@
/* 进一步压缩分组标题与第一项之间的间距 */ /* 进一步压缩分组标题与第一项之间的间距 */
:where(html) .VPSidebarItem.level-0 + .VPSidebarItem.level-1, :where(html) .VPSidebarItem.level-0 + .VPSidebarItem.level-1,
:where(html) .VPSidebarItem.level-0[data-v-d81de50c] + .VPSidebarItem.level-1[data-v-d81de50c] { :where(html)
.VPSidebarItem.level-0[data-v-d81de50c]
+ .VPSidebarItem.level-1[data-v-d81de50c] {
margin-top: -2px !important; margin-top: -2px !important;
} }
+21 -21
View File
@@ -22,9 +22,9 @@
### 零、幼儿园 ### 零、幼儿园
| 章节 | 关键内容 | 状态 | | 章节 | 关键内容 | 状态 |
| :----------------------------------------------------------------------------- | :------------------------------------- | :--- | | :------------------------------------------------------------------------------- | :------------------------------------- | :--- |
| [新手入门:学习地图](/stage-0/0.1-learning-map/) | 整体学习路径导览 | ✅ | | [新手入门:学习地图](/stage-0/0.1-learning-map/) | 整体学习路径导览 | ✅ |
| [新手入门:AI 时代,会说话就会编程](/stage-0/0.2-ai-capabilities-through-games/) | 通过贪吃蛇等案例初步感受 AI 编程的能力 | ✅ | | [新手入门:AI 时代,会说话就会编程](/stage-0/0.2-ai-capabilities-through-games/) | 通过贪吃蛇等案例初步感受 AI 编程的能力 | ✅ |
### 一、AI 产品经理 ### 一、AI 产品经理
@@ -48,32 +48,32 @@
#### 前端部分 #### 前端部分
| 章节 | 关键内容 | 状态 | | 章节 | 关键内容 | 状态 |
| :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :--- | | :------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------- | :--- |
| 前端零:使用 lovart 生产素材 | 学会用 lovart 批量生成人物、场景等视觉素材,为 UI 设计和前端开发提供素材基础 | 🚧 | | 前端零:使用 lovart 生产素材 | 学会用 lovart 批量生成人物、场景等视觉素材,为 UI 设计和前端开发提供素材基础 | 🚧 |
| 前端一:Figma 与 MasterGo 入门 | 用设计工具梳理信息架构和页面结构,为前端实现打基础 | 🚧 | | 前端一:Figma 与 MasterGo 入门 | 用设计工具梳理信息架构和页面结构,为前端实现打基础 | 🚧 |
| 前端二:构建第一个现代应用程序-UI 设计 | 基于设计稿完成组件化界面,实现从设计到代码的第一条链路 | 🚧 | | 前端二:构建第一个现代应用程序-UI 设计 | 基于设计稿完成组件化界面,实现从设计到代码的第一条链路 | 🚧 |
| 前端三:参考 UI 设计规范与多产品 UI 设计 | 围绕统一主视觉扩展多产品界面,练习系统化设计能力 | 🚧 | | 前端三:参考 UI 设计规范与多产品 UI 设计 | 围绕统一主视觉扩展多产品界面,练习系统化设计能力 | 🚧 |
| [前端四:一起做霍格沃茨画像](/stage-2/frontend/2.4-hogwarts-portraits/chapter4-lets-build-hogwarts-portraits) | 从 0 到 1 做出接入 AI 能力的前端应用,串联设计与开发 | 🚧 | | [前端四:一起做霍格沃茨画像](/stage-2/frontend/2.4-hogwarts-portraits/chapter4-lets-build-hogwarts-portraits) | 从 0 到 1 做出接入 AI 能力的前端应用,串联设计与开发 | 🚧 |
#### 后端与全栈部分 #### 后端与全栈部分
| 章节 | 关键内容 | 状态 | | 章节 | 关键内容 | 状态 |
| :---------------------------------------------------------------------------------- | :------------------------------------------------------------ | :--- | | :---------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------ | :--- |
| 后端一:什么是 API | 理解 HTTP 接口与请求响应模型,为后端集成与联调做准备 | 🚧 | | 后端一:什么是 API | 理解 HTTP 接口与请求响应模型,为后端集成与联调做准备 | 🚧 |
| [后端二:从数据库到 Supabase](/stage-2/backend/2.2-database-supabase/chapter5/chapter5-from-database-to-supabase) | 在 Supabase 上落地数据库和 API,打通数据模型与前端页面 | 🚧 | | [后端二:从数据库到 Supabase](/stage-2/backend/2.2-database-supabase/chapter5/chapter5-from-database-to-supabase) | 在 Supabase 上落地数据库和 API,打通数据模型与前端页面 | 🚧 |
| 后端三:大模型辅助编写接口代码与接口文档 | 用大模型协助生成接口与数据库文档及代码,实现可读可测的后端 | 🚧 | | 后端三:大模型辅助编写接口代码与接口文档 | 用大模型协助生成接口与数据库文档及代码,实现可读可测的后端 | 🚧 |
| 后端四:Git 工作流与 Zeabur 部署 | 在 Git 工作流中管理代码,并将应用部署到 Zeabur 上线 | 🚧 | | 后端四:Git 工作流与 Zeabur 部署 | 在 Git 工作流中管理代码,并将应用部署到 Zeabur 上线 | 🚧 |
| 后端五:现代 CLI 开发工具 | 使用 CLI 类 AI 编程工具加速开发与调试,形成个人工程化工作流 | 🚧 | | 后端五:现代 CLI 开发工具 | 使用 CLI 类 AI 编程工具加速开发与调试,形成个人工程化工作流 | 🚧 |
| 后端六:如何集成 stripe 等收费系统 | 接入支付系统,完成收费链路与基础结算流程 | 🚧 | | 后端六:如何集成 stripe 等收费系统 | 接入支付系统,完成收费链路与基础结算流程 | 🚧 |
| 大作业 1:构建第一个现代应用程序-全栈应用 | 综合前端、后端与支付模块,完成可上线的全栈 Web 应用 | 🚧 | | 大作业 1:构建第一个现代应用程序-全栈应用 | 综合前端、后端与支付模块,完成可上线的全栈 Web 应用 | 🚧 |
| 大作业 2:现代前端组件库 + Trae 实战 | 使用现代前端组件库与 Trae,独立完成可登录注册并支持收费的产品 | 🚧 | | 大作业 2:现代前端组件库 + Trae 实战 | 使用现代前端组件库与 Trae,独立完成可登录注册并支持收费的产品 | 🚧 |
#### AI 能力附录 #### AI 能力附录
| 章节 | 关键内容 | 状态 | | 章节 | 关键内容 | 状态 |
| :---------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------- | :--- | | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------- | :--- |
| [AI 一:Dify 入门与知识库集成](/stage-2/ai-capabilities/2.1-dify-knowledge-base/chapter3/chapter3-getting-started-with-dify-and-its-knowledge-base-integration) | 用 Dify Workflow 与基础 RAG 搭建工具类产品,为后续应用升级打样 | 🚧 | | [AI 一:Dify 入门与知识库集成](/stage-2/ai-capabilities/2.1-dify-knowledge-base/chapter3/chapter3-getting-started-with-dify-and-its-knowledge-base-integration) | 用 Dify Workflow 与基础 RAG 搭建工具类产品,为后续应用升级打样 | 🚧 |
| AI 二:学会查询 AI 词典与集成多模态 API | 学会查找合适的模型与 API,并把文本、图像等多模态能力接入产品 | 🚧 | | AI 二:学会查询 AI 词典与集成多模态 API | 学会查找合适的模型与 API,并把文本、图像等多模态能力接入产品 | 🚧 |
### 三、高级开发工程师 ### 三、高级开发工程师
+61 -1
View File
@@ -1,10 +1,70 @@
:root { :root {
/* 调整侧边栏分组之间的间距 */ /* 调整侧边栏分组之间的间距 */
--vp-sidebar-nav-section-gap: 8px; --vp-sidebar-nav-section-gap: 8px;
--ev-doc-font-size: 13px;
--ev-doc-line-height: 1.5;
} }
.vp-doc { .vp-doc {
font-size: 15px; font-size: var(--ev-doc-font-size);
line-height: var(--ev-doc-line-height);
--el-font-size-extra-large: calc(var(--ev-doc-font-size) + 6px);
--el-font-size-large: calc(var(--ev-doc-font-size) + 4px);
--el-font-size-medium: calc(var(--ev-doc-font-size) + 2px);
--el-font-size-base: var(--ev-doc-font-size);
--el-font-size-small: calc(var(--ev-doc-font-size) - 1px);
--el-font-size-extra-small: calc(var(--ev-doc-font-size) - 2px);
--el-font-line-height-primary: var(--ev-doc-line-height);
}
.vp-doc :where(p, ul, ol, table, blockquote, pre, details, figure) {
margin: 10px 0;
}
.vp-doc :where(li) {
margin: 4px 0;
}
.vp-doc :where(ul, ol) {
padding-left: 1.15em;
}
.vp-doc :where(h1, h2, h3, h4, h5, h6) {
line-height: 1.3;
}
.vp-doc :where(h1) {
margin: 22px 0 12px;
}
.vp-doc :where(h2) {
margin: 20px 0 10px;
}
.vp-doc h2 {
margin: 16px 0 8px !important;
padding-top: 10px !important;
border-top: 0 !important;
}
.vp-doc :where(h3) {
margin: 18px 0 8px;
}
.vp-doc :where(h4, h5, h6) {
margin: 16px 0 8px;
}
.vp-doc :where(hr) {
margin: 14px 0;
}
.vp-doc :where(th, td) {
padding: 6px 10px;
}
.vp-doc :where(:not(pre) > code) {
font-size: 0.95em;
} }
/* 减少一级标题(如"前端开发")底部的间距 */ /* 减少一级标题(如"前端开发")底部的间距 */
+15 -7
View File
@@ -163,11 +163,13 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
::: details 第一阶段完整课程大纲 (点击收起) ::: details 第一阶段完整课程大纲 (点击收起)
**模块一:AI 时代,会说话就会编程** **模块一:AI 时代,会说话就会编程**
- **1.1** 普通人的困境与机会? - **1.1** 普通人的困境与机会?
- **1.2** AI 能帮你做到什么程度? - **1.2** AI 能帮你做到什么程度?
- **1.3** 动手:你的第一个 AI 原生应用 - **1.3** 动手:你的第一个 AI 原生应用
**模块二:认识 AI IDE 工具** **模块二:认识 AI IDE 工具**
- **2.1** 写代码需要什么环境和工具 - **2.1** 写代码需要什么环境和工具
- **2.2** 什么是 IDE,为什么需要 IDE - **2.2** 什么是 IDE,为什么需要 IDE
- **2.3** AI IDE 和普通 IDE 有什么不同 - **2.3** AI IDE 和普通 IDE 有什么不同
@@ -175,6 +177,7 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
- **2.5** 怎么跟 AI 说话才有效 - **2.5** 怎么跟 AI 说话才有效
**模块三:动手做出原型** **模块三:动手做出原型**
- **3.1** 把需求变成代码的过程 - **3.1** 把需求变成代码的过程
- **3.2** 从一个单页面开始 - **3.2** 从一个单页面开始
- **3.3** 遇到报错了怎么办 - **3.3** 遇到报错了怎么办
@@ -182,6 +185,7 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
- **3.5** 把原型做得像那么回事 - **3.5** 把原型做得像那么回事
**模块四:给原型加上 AI 能力** **模块四:给原型加上 AI 能力**
- **4.1** 什么是 AI 能力接入(API 调用) - **4.1** 什么是 AI 能力接入(API 调用)
- **4.2** 如何接入文生图能力 - **4.2** 如何接入文生图能力
- **4.3** 如何接入视频生成能力 - **4.3** 如何接入视频生成能力
@@ -189,27 +193,31 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
- **4.5** 成本控制和错误处理 - **4.5** 成本控制和错误处理
**模块五:完整项目实战** **模块五:完整项目实战**
- **5.1** 制造模拟数据让原型看起来真实 - **5.1** 制造模拟数据让原型看起来真实
- **5.2** 收集反馈并快速调整 - **5.2** 收集反馈并快速调整
- **5.3** 展示你的成果 - **5.3** 展示你的成果
**大作业** **大作业**
- 做一个完整的 Web 应用原型并展示 - 做一个完整的 Web 应用原型并展示
**附录A:产品思维补充** **附录A:产品思维补充**
- **A.1** 什么是好的产品想法 - **A.1** 什么是好的产品想法
- **A.2** 如何发现用户真正的需求 - **A.2** 如何发现用户真正的需求
- **A.3** 功能优先级怎么排 - **A.3** 功能优先级怎么排
- **A.4** MVP 思维:最小可行产品 - **A.4** MVP 思维:最小可行产品
**附录B:常见报错及解决方案** **附录B:常见报错及解决方案**
- **B.1** 页面显示空白或不加载 - **B.1** 页面显示空白或不加载
- **B.2** 数据保存不成功 - **B.2** 数据保存不成功
- **B.3** 样式显示不正常 - **B.3** 样式显示不正常
- **B.4** 点击按钮没反应 - **B.4** 点击按钮没反应
- **B.5** API 调用失败 - **B.5** API 调用失败
- **B.6** 如何把报错信息有效地反馈给 AI - **B.6** 如何把报错信息有效地反馈给 AI
::: :::
# 为什么要用项目制来训练? # 为什么要用项目制来训练?
@@ -231,7 +239,6 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
短期来看,这种训练确实比较折磨人;但从长期来看,它会极大提高你在求职和职业发展中的竞争力:你会更能扛事儿,更能在不确定环境中找到突破口,也更有能力把 AI 变成真正落地的产品,而不是停留在“玩玩 Demo”阶段。 短期来看,这种训练确实比较折磨人;但从长期来看,它会极大提高你在求职和职业发展中的竞争力:你会更能扛事儿,更能在不确定环境中找到突破口,也更有能力把 AI 变成真正落地的产品,而不是停留在“玩玩 Demo”阶段。
# 提问的艺术:AI 时代的必备技能 # 提问的艺术:AI 时代的必备技能
在 AI 时代,提问也属于一种 “基本功”。同一份代码、同一个报错,**你怎么提问,几乎决定了 AI 能给出怎样的答案**:是泛泛而谈,还是一步一步给出可落地的改法。 在 AI 时代,提问也属于一种 “基本功”。同一份代码、同一个报错,**你怎么提问,几乎决定了 AI 能给出怎样的答案**:是泛泛而谈,还是一步一步给出可落地的改法。
@@ -254,10 +261,10 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
两种方式都可以,但用途不同: 两种方式都可以,但用途不同:
| 方式 | 适用场景 | 关键要求 | | 方式 | 适用场景 | 关键要求 |
| --- | --- | --- | | ------------ | ----------------------------------------- | ----------------------------------------- |
| **复制粘贴** | 报错堆栈、日志、代码、配置、API 返回 | 尽量完整,不要只截一行关键字 | | **复制粘贴** | 报错堆栈、日志、代码、配置、API 返回 | 尽量完整,不要只截一行关键字 |
| **截图** | UI 布局问题、交互异常、工具界面找不到按钮 | 截全屏 + 标注重点区域,最好配一句文字说明 | | **截图** | UI 布局问题、交互异常、工具界面找不到按钮 | 截全屏 + 标注重点区域,最好配一句文字说明 |
::: info ⭐ 建议优先级 ::: info ⭐ 建议优先级
能截图就截图;截图沟通不了再复制粘贴沟通。 能截图就截图;截图沟通不了再复制粘贴沟通。
@@ -268,9 +275,10 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
如果你不是只要答案,而是要“学会”答案。使用类似下面指令能显著提升解释质量: 如果你不是只要答案,而是要“学会”答案。使用类似下面指令能显著提升解释质量:
::: tip 🧠 学习型提问示例 ::: tip 🧠 学习型提问示例
- “请先用 5 句话讲清楚这个概念,再给几个问题提问我验证我理解对了没。” - “请先用 5 句话讲清楚这个概念,再给几个问题提问我验证我理解对了没。”
- ”请你详细解释一下这个报错信息,我不理解为什么会报错。” - ”请你详细解释一下这个报错信息,我不理解为什么会报错。”
::: :::
# 坚持了好久还是搞不定,我想放弃了 # 坚持了好久还是搞不定,我想放弃了
@@ -1,6 +1,5 @@
# 初级一:AI 时代,会说话就会编程 # 初级一:AI 时代,会说话就会编程
这是一个**基于项目制学习**的学习教程。我们鼓励你跟随步骤一步步操作,并尝试复现结果。 这是一个**基于项目制学习**的学习教程。我们鼓励你跟随步骤一步步操作,并尝试复现结果。
不要担心犯错或修改内容,我们永远相信你可以做到,请你永远记住: 不要担心犯错或修改内容,我们永远相信你可以做到,请你永远记住:
@@ -12,16 +11,18 @@
</div> </div>
</el-card> </el-card>
## 本章导读 ## 本章导读
::: info 🎯 学习目标 <ChapterIntroduction
在这一节,你会用对话式 AI 做出第一个 AI 原生小游戏——一款会“吃单词、写诗、画画”的贪吃蛇,并借此搞清楚 AI 编程的初步效果。 duration="约 <strong>4 小时</strong>,可分多次完成"
::: :tags="['对话式 AI 编程', 'AI 原生小游戏', '贪吃蛇实战']"
coreOutput="AI 原生贪吃蛇 + 自创小游戏"
expectedOutput="1 个可运行的 AI 原生贪吃蛇 + (可选)1 个你自创的 AI 原生小游戏或 Demo"
>
- 预计时间:约 **4 小时**,可分多次完成 在这一节,你会用对话式 AI 做出第一个 AI 原生小游戏——一款会“吃单词、写诗、画画”的贪吃蛇,并借此搞清楚 AI 编程的初步效果。
- 预期产出:1 个可运行的 AI 原生贪吃蛇 + (可选)1 个你自创的 AI 原生小游戏或 Demo
- Assignment:复现贪吃蛇,并(可选)实现一种你感兴趣的 AI 原生游戏 </ChapterIntroduction>
<div style="margin: 50px 0;"> <div style="margin: 50px 0;">
<ClientOnly> <ClientOnly>
@@ -168,10 +169,11 @@ AI 出现之后,第一次给了普通人一个全新的可能:你不需要会
一个可参考的经验是: 一个可参考的经验是:
::: warning ⚠️ 适用场景指南 ::: warning ⚠️ 适用场景指南
- **原型 / Demo / 内部自用工具**:非常适合先交给 AI 打第一版,再由你迭代细节。 - **原型 / Demo / 内部自用工具**:非常适合先交给 AI 打第一版,再由你迭代细节。
- **面向真实用户的大型产品**:通常需要工程师在架构、抽象、性能和维护上长期投入。 - **面向真实用户的大型产品**:通常需要工程师在架构、抽象、性能和维护上长期投入。
- **强安全 / 强合规系统(如支付、风控、医疗等)**:在当前阶段,不宜“生成完就直接上线”,必须引入严格的审查与测试流程。 - **强安全 / 强合规系统(如支付、风控、医疗等)**:在当前阶段,不宜“生成完就直接上线”,必须引入严格的审查与测试流程。
::: :::
在当下,你可以相对安心地把 AI 视作一个高效的 Demo 与自用工具搭档: 在当下,你可以相对安心地把 AI 视作一个高效的 Demo 与自用工具搭档:
只要你愿意多测试、多迭代,多问几轮“这里不对,帮我修一下并解释原因”,在原型与内部工具这一级别,整体质量通常是足够且具备实践价值的。 只要你愿意多测试、多迭代,多问几轮“这里不对,帮我修一下并解释原因”,在原型与内部工具这一级别,整体质量通常是足够且具备实践价值的。
@@ -2,12 +2,16 @@
## 本章导读 ## 本章导读
::: info 🎯 学习目标 <ChapterIntroduction
本章将围绕本地开发环境展开讲解:你将学会从 z.ai 过渡到本地的开发环境,学会在自己的电脑上搭建完整的开发环境,理解什么是 IDE、什么是 AI IDE,以及如何在日常开发中高效地使用它们。 duration="约 <strong>1 天</strong>,可分多次完成"
::: :tags="['本地开发环境搭建', 'IDE 与 AI IDE', '高效开发技巧']"
coreOutput="1 个自创小游戏"
expectedOutput="使用 Trae 产出"
>
- 预计时间:约 **1 天**,可分多次完成 本章将围绕本地开发环境展开讲解:你将学会从 z.ai 过渡到本地的开发环境,学会在自己的电脑上搭建完整的开发环境,理解什么是 IDE、什么是 AI IDE,以及如何在日常开发中高效地使用它们。
- 预期产出:使用 Trae 产出 **1 个自创小游戏**
</ChapterIntroduction>
<div style="margin: 50px 0;"> <div style="margin: 50px 0;">
<ClientOnly> <ClientOnly>
@@ -548,23 +552,44 @@ AI 很认真地给了你一段代码,你也老老实实地复制进去了,
<div style="font-weight: bold; font-size: 16px;">🚀 挑战任务:打造你的专属游戏</div> <div style="font-weight: bold; font-size: 16px;">🚀 挑战任务:打造你的专属游戏</div>
</template> </template>
你已经用本地 AI IDE 做过一个贪吃蛇。现在请你再挑战一个更复杂一点的小游戏,完整走一遍“描述需求 → 生成项目 → 本地运行 → 调试迭代”的流程。 <p>
你已经用本地 AI IDE 做过一个贪吃蛇。现在请你再挑战一个更复杂一点的小游戏,完整走一遍“描述需求 →
生成项目 → 本地运行 → 调试迭代”的流程。
</p>
1. **选择一个比贪吃蛇更复杂的游戏** <ol>
- 可以是“俄罗斯方块”“打地鼠”“扫雷”“2048””飞机大战“之类 <li>
- 或者你自己想象的一个简单原创游戏 <strong>选择一个比贪吃蛇更复杂的游戏</strong>
2. **必须用本地 AI IDE 来完成整个过程** <ul>
- 新建一个空文件夹,用 AI IDE 打开 <li>可以是“俄罗斯方块”“打地鼠”“扫雷”“2048”“飞机大战”之类</li>
- 在侧边栏聊天里描述清楚你的游戏需求 <li>或者你自己想象的一个简单原创游戏</li>
- 让 AI 负责创建文件、搭建项目结构和实现主要逻辑 </ul>
- 在本地启动开发服务器,确保游戏可以正常运行 </li>
3. **有基本的“可玩性”和反馈** <li>
- 至少包含开始、进行中、结束三种状态 <strong>必须用本地 AI IDE 来完成整个过程</strong>
- 玩家有明确的操作方式(键盘或鼠标) <ul>
- 屏幕上有清晰的得分或进度反馈 <li>新建一个空文件夹,用 AI IDE 打开</li>
4. **至少进行 2 轮以上的迭代** <li>在侧边栏聊天里描述清楚你的游戏需求</li>
- 第一轮让 AI 做出“能玩”的版本 <li>让 AI 负责创建文件、搭建项目结构和实现主要逻辑</li>
- 第二轮以后,逐步提出具体改进(样式、难度、交互优化等) <li>在本地启动开发服务器,确保游戏可以正常运行</li>
</ul>
</li>
<li>
<strong>有基本的“可玩性”和反馈</strong>
<ul>
<li>至少包含开始、进行中、结束三种状态</li>
<li>玩家有明确的操作方式(键盘或鼠标)</li>
<li>屏幕上有清晰的得分或进度反馈</li>
</ul>
</li>
<li>
<strong>至少进行 2 轮以上的迭代</strong>
<ul>
<li>第一轮让 AI 做出“能玩”的版本</li>
<li>第二轮以后,逐步提出具体改进(样式、难度、交互优化等)</li>
</ul>
</li>
</ol>
</el-card> </el-card>
## 附录 ## 附录
@@ -120,7 +120,6 @@ AI Agent 是一种软件系统,它能够感知环境、做出决策,并自
> - 蛇吃掉的不是食物,而是英文单词。 > - 蛇吃掉的不是食物,而是英文单词。
> - 页面侧边栏展示已收集单词及其数量。 > - 页面侧边栏展示已收集单词及其数量。
> - 游戏结束后,已收集的单词仍然保留,并在新一局中延续。 > - 游戏结束后,已收集的单词仍然保留,并在新一局中延续。
>
> - 页面 2:写诗页面(Make Poem > - 页面 2:写诗页面(Make Poem
> - 展示与游戏页面相同的单词列表(数据一致)。 > - 展示与游戏页面相同的单词列表(数据一致)。
> - 提供一个按钮,将当前收集的单词发送给后端生成一首诗。 > - 提供一个按钮,将当前收集的单词发送给后端生成一首诗。
@@ -6,37 +6,37 @@ app:
name: Log in name: Log in
use_icon_as_answer_icon: false use_icon_as_answer_icon: false
dependencies: dependencies:
- current_identifier: null - current_identifier: null
type: marketplace type: marketplace
value: value:
marketplace_plugin_unique_identifier: langgenius/gitee_ai:0.1.4@f621ace33bb3c140f5a1e3533fcb518f558c7b945d63523c0f85810a4b4a8b93 marketplace_plugin_unique_identifier: langgenius/gitee_ai:0.1.4@f621ace33bb3c140f5a1e3533fcb518f558c7b945d63523c0f85810a4b4a8b93
kind: app kind: app
version: 0.3.0 version: 0.3.0
workflow: workflow:
conversation_variables: conversation_variables:
- description: '' - description: ''
id: f8cc215e-ef91-437a-a823-7e80a8d345a3 id: f8cc215e-ef91-437a-a823-7e80a8d345a3
name: LOGIN name: LOGIN
selector: selector:
- conversation - conversation
- LOGIN - LOGIN
value: none value: none
value_type: string value_type: string
environment_variables: [] environment_variables: []
features: features:
file_upload: file_upload:
allowed_file_extensions: allowed_file_extensions:
- .JPG - .JPG
- .JPEG - .JPEG
- .PNG - .PNG
- .GIF - .GIF
- .WEBP - .WEBP
- .SVG - .SVG
allowed_file_types: allowed_file_types:
- image - image
allowed_file_upload_methods: allowed_file_upload_methods:
- local_file - local_file
- remote_url - remote_url
enabled: false enabled: false
fileUploadConfig: fileUploadConfig:
audio_file_size_limit: 50 audio_file_size_limit: 50
@@ -49,8 +49,8 @@ workflow:
enabled: false enabled: false
number_limits: 3 number_limits: 3
transfer_methods: transfer_methods:
- local_file - local_file
- remote_url - remote_url
number_limits: 3 number_limits: 3
opening_statement: 'Please log in with passwords:' opening_statement: 'Please log in with passwords:'
retriever_resource: retriever_resource:
@@ -68,436 +68,438 @@ workflow:
voice: '' voice: ''
graph: graph:
edges: edges:
- data: - data:
sourceType: llm sourceType: llm
targetType: answer targetType: answer
id: llm-answer id: llm-answer
source: llm source: llm
sourceHandle: source sourceHandle: source
target: answer target: answer
targetHandle: target targetHandle: target
type: custom type: custom
- data: - data:
isInIteration: false isInIteration: false
isInLoop: false isInLoop: false
sourceType: start sourceType: start
targetType: if-else targetType: if-else
id: 1758767725822-source-1758767750205-target id: 1758767725822-source-1758767750205-target
source: '1758767725822' source: '1758767725822'
sourceHandle: source sourceHandle: source
target: '1758767750205' target: '1758767750205'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
- data: - data:
isInIteration: false isInIteration: false
isInLoop: false isInLoop: false
sourceType: if-else sourceType: if-else
targetType: assigner targetType: assigner
id: 1758767920912-true-1758768026915-target id: 1758767920912-true-1758768026915-target
source: '1758767920912' source: '1758767920912'
sourceHandle: 'true' sourceHandle: 'true'
target: '1758768026915' target: '1758768026915'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
- data: - data:
isInIteration: false isInIteration: false
isInLoop: false isInLoop: false
sourceType: if-else sourceType: if-else
targetType: assigner targetType: assigner
id: 1758767920912-false-1758768059939-target id: 1758767920912-false-1758768059939-target
source: '1758767920912' source: '1758767920912'
sourceHandle: 'false' sourceHandle: 'false'
target: '1758768059939' target: '1758768059939'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
- data: - data:
isInLoop: false isInLoop: false
sourceType: if-else sourceType: if-else
targetType: llm targetType: llm
id: 1758767750205-f599486b-e4d3-4cdd-9425-31257bf28e82-llm-target id: 1758767750205-f599486b-e4d3-4cdd-9425-31257bf28e82-llm-target
source: '1758767750205' source: '1758767750205'
sourceHandle: f599486b-e4d3-4cdd-9425-31257bf28e82 sourceHandle: f599486b-e4d3-4cdd-9425-31257bf28e82
target: llm target: llm
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
- data: - data:
isInIteration: false isInIteration: false
isInLoop: false isInLoop: false
sourceType: if-else sourceType: if-else
targetType: llm targetType: llm
id: 1758767750205-5f242bc3-7f06-4a88-82e5-235a859d92bf-1758768238460-target id: 1758767750205-5f242bc3-7f06-4a88-82e5-235a859d92bf-1758768238460-target
source: '1758767750205' source: '1758767750205'
sourceHandle: 5f242bc3-7f06-4a88-82e5-235a859d92bf sourceHandle: 5f242bc3-7f06-4a88-82e5-235a859d92bf
target: '1758768238460' target: '1758768238460'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
- data: - data:
isInIteration: false isInIteration: false
isInLoop: false isInLoop: false
sourceType: llm sourceType: llm
targetType: answer targetType: answer
id: 1758768238460-source-1758768309599-target id: 1758768238460-source-1758768309599-target
source: '1758768238460' source: '1758768238460'
sourceHandle: source sourceHandle: source
target: '1758768309599' target: '1758768309599'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
- data: - data:
isInLoop: false isInLoop: false
sourceType: if-else sourceType: if-else
targetType: if-else targetType: if-else
id: 1758767750205-true-1758767920912-target id: 1758767750205-true-1758767920912-target
source: '1758767750205' source: '1758767750205'
sourceHandle: 'true' sourceHandle: 'true'
target: '1758767920912' target: '1758767920912'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
- data: - data:
isInIteration: false isInIteration: false
isInLoop: false isInLoop: false
sourceType: assigner sourceType: assigner
targetType: answer targetType: answer
id: 1758768026915-source-1758768400561-target id: 1758768026915-source-1758768400561-target
source: '1758768026915' source: '1758768026915'
sourceHandle: source sourceHandle: source
target: '1758768400561' target: '1758768400561'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
- data: - data:
isInIteration: false isInIteration: false
isInLoop: false isInLoop: false
sourceType: assigner sourceType: assigner
targetType: answer targetType: answer
id: 1758768059939-source-1758768418040-target id: 1758768059939-source-1758768418040-target
source: '1758768059939' source: '1758768059939'
sourceHandle: source sourceHandle: source
target: '1758768418040' target: '1758768418040'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
nodes: nodes:
- data: - data:
desc: '' desc: ''
selected: false
title: Start
type: start
variables: []
height: 53
id: '1758767725822'
position:
x: -227.17718464443863
y: 459.4548203041172
positionAbsolute:
x: -227.17718464443863
y: 459.4548203041172
selected: false selected: false
title: Start sourcePosition: right
type: start targetPosition: left
variables: [] type: custom
height: 53 width: 243
id: '1758767725822' - data:
position: context:
x: -227.17718464443863
y: 459.4548203041172
positionAbsolute:
x: -227.17718464443863
y: 459.4548203041172
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
context:
enabled: false
variable_selector: []
desc: ''
memory:
query_prompt_template: '{{#sys.query#}}'
role_prefix:
assistant: ''
user: ''
window:
enabled: false enabled: false
size: 10 variable_selector: []
model: desc: ''
completion_params: memory:
temperature: 0.7 query_prompt_template: '{{#sys.query#}}'
mode: chat role_prefix:
name: Qwen3-235B-A22B-Instruct-2507 assistant: ''
provider: langgenius/gitee_ai/gitee_ai user: ''
prompt_template: window:
- id: 2686a731-f250-46f0-97ec-033e929160a5 enabled: false
role: system size: 10
text: 'You are the computer that answers the user''s question. Always start model:
with "hello guest" ' completion_params:
temperature: 0.7
mode: chat
name: Qwen3-235B-A22B-Instruct-2507
provider: langgenius/gitee_ai/gitee_ai
prompt_template:
- id: 2686a731-f250-46f0-97ec-033e929160a5
role: system
text:
'You are the computer that answers the user''s question. Always start
with "hello guest" '
selected: false
title: Guest LLM
type: llm
variables: []
vision:
enabled: false
height: 89
id: llm
position:
x: 389.3839309072489
y: 663.6856819774588
positionAbsolute:
x: 389.3839309072489
y: 663.6856819774588
selected: false selected: false
title: Guest LLM sourcePosition: right
type: llm targetPosition: left
variables: [] type: custom
vision: width: 243
enabled: false - data:
height: 89 answer: '{{#llm.text#}}'
id: llm desc: ''
position: selected: true
x: 389.3839309072489 title: Answer
y: 663.6856819774588 type: answer
positionAbsolute: variables: []
x: 389.3839309072489 height: 104
y: 663.6856819774588 id: answer
selected: false position:
sourcePosition: right x: 707.3520684153225
targetPosition: left y: 673.6318886739399
type: custom positionAbsolute:
width: 243 x: 707.3520684153225
- data: y: 673.6318886739399
answer: '{{#llm.text#}}'
desc: ''
selected: true selected: true
title: Answer sourcePosition: right
type: answer targetPosition: left
variables: [] type: custom
height: 104 width: 243
id: answer - data:
position: cases:
x: 707.3520684153225 - case_id: 'true'
y: 673.6318886739399 conditions:
positionAbsolute: - comparison_operator: is
x: 707.3520684153225 id: cf0932c4-e5f4-478e-9437-1db63af30ffd
y: 673.6318886739399 value: none
selected: true varType: string
sourcePosition: right variable_selector:
targetPosition: left - conversation
type: custom - LOGIN
width: 243 id: 'true'
- data: logical_operator: and
cases: - case_id: f599486b-e4d3-4cdd-9425-31257bf28e82
- case_id: 'true' conditions:
conditions: - comparison_operator: is
- comparison_operator: is id: 18c716ea-aac5-4af7-9a50-1fd5dc40d18e
id: cf0932c4-e5f4-478e-9437-1db63af30ffd value: guest
value: none varType: string
varType: string variable_selector:
variable_selector: - conversation
- conversation - LOGIN
- LOGIN id: f599486b-e4d3-4cdd-9425-31257bf28e82
id: 'true' logical_operator: and
logical_operator: and - case_id: 5f242bc3-7f06-4a88-82e5-235a859d92bf
- case_id: f599486b-e4d3-4cdd-9425-31257bf28e82 conditions:
conditions: - comparison_operator: is
- comparison_operator: is id: 490f9251-1012-4f85-8bb7-29f355ca6b5c
id: 18c716ea-aac5-4af7-9a50-1fd5dc40d18e value: admin
value: guest varType: string
varType: string variable_selector:
variable_selector: - conversation
- conversation - LOGIN
- LOGIN id: 5f242bc3-7f06-4a88-82e5-235a859d92bf
id: f599486b-e4d3-4cdd-9425-31257bf28e82 logical_operator: and
logical_operator: and desc: ''
- case_id: 5f242bc3-7f06-4a88-82e5-235a859d92bf selected: false
conditions: title: IF/ELSE
- comparison_operator: is type: if-else
id: 490f9251-1012-4f85-8bb7-29f355ca6b5c height: 221
value: admin id: '1758767750205'
varType: string position:
variable_selector: x: 78.91702348010949
- conversation y: 535.9787937998216
- LOGIN positionAbsolute:
id: 5f242bc3-7f06-4a88-82e5-235a859d92bf x: 78.91702348010949
logical_operator: and y: 535.9787937998216
desc: ''
selected: false selected: false
title: IF/ELSE sourcePosition: right
type: if-else targetPosition: left
height: 221 type: custom
id: '1758767750205' width: 243
position: - data:
x: 78.91702348010949 cases:
y: 535.9787937998216 - case_id: 'true'
positionAbsolute: conditions:
x: 78.91702348010949 - comparison_operator: is
y: 535.9787937998216 id: ab112997-31e2-453c-8511-40adc3006e76
selected: false value: AIID
sourcePosition: right varType: string
targetPosition: left variable_selector:
type: custom - sys
width: 243 - query
- data: id: 'true'
cases: logical_operator: and
- case_id: 'true' desc: ''
conditions: selected: false
- comparison_operator: is title: IF/ELSE 2
id: ab112997-31e2-453c-8511-40adc3006e76 type: if-else
value: AIID height: 125
varType: string id: '1758767920912'
variable_selector: position:
- sys x: 659.9247584894487
- query y: 459.4548203041172
id: 'true' positionAbsolute:
logical_operator: and x: 659.9247584894487
desc: '' y: 459.4548203041172
selected: false selected: false
title: IF/ELSE 2 sourcePosition: right
type: if-else targetPosition: left
height: 125 type: custom
id: '1758767920912' width: 243
position: - data:
x: 659.9247584894487 desc: ''
y: 459.4548203041172 items:
positionAbsolute: - input_type: constant
x: 659.9247584894487 operation: set
y: 459.4548203041172 value: admin
selected: false variable_selector:
sourcePosition: right - conversation
targetPosition: left - LOGIN
type: custom write_mode: over-write
width: 243 selected: false
- data: title: Variable Assigner
desc: '' type: assigner
items: version: '2'
- input_type: constant height: 87
operation: set id: '1758768026915'
value: admin position:
variable_selector: x: 997.3839309072489
- conversation y: 426.91916104676926
- LOGIN positionAbsolute:
write_mode: over-write x: 997.3839309072489
y: 426.91916104676926
selected: false selected: false
title: Variable Assigner sourcePosition: right
type: assigner targetPosition: left
version: '2' type: custom
height: 87 width: 243
id: '1758768026915' - data:
position: desc: ''
x: 997.3839309072489 items:
y: 426.91916104676926 - input_type: constant
positionAbsolute: operation: set
x: 997.3839309072489 value: guest
y: 426.91916104676926 variable_selector:
selected: false - conversation
sourcePosition: right - LOGIN
targetPosition: left write_mode: over-write
type: custom selected: false
width: 243 title: Variable Assigner 2
- data: type: assigner
desc: '' version: '2'
items: height: 87
- input_type: constant id: '1758768059939'
operation: set position:
value: guest x: 1010.8775065168395
variable_selector: y: 553.9191610467692
- conversation positionAbsolute:
- LOGIN x: 1010.8775065168395
write_mode: over-write y: 553.9191610467692
selected: false selected: false
title: Variable Assigner 2 sourcePosition: right
type: assigner targetPosition: left
version: '2' type: custom
height: 87 width: 243
id: '1758768059939' - data:
position: context:
x: 1010.8775065168395 enabled: false
y: 553.9191610467692 variable_selector: []
positionAbsolute: desc: ''
x: 1010.8775065168395 model:
y: 553.9191610467692 completion_params:
selected: false temperature: 0.7
sourcePosition: right mode: chat
targetPosition: left name: Qwen3-235B-A22B-Instruct-2507
type: custom provider: langgenius/gitee_ai/gitee_ai
width: 243 prompt_template:
- data: - id: 1129723a-50cb-4350-a118-3b9ac6dac523
context: role: system
enabled: false text:
variable_selector: [] 'You are the computer that answers the admin''s question. Always start
desc: '' with "hello admin" . You know AIID(AI innovative Design) is a master program
model: in open FIESTA in Shenzhen. '
completion_params: selected: false
temperature: 0.7 title: Admin LLM
mode: chat type: llm
name: Qwen3-235B-A22B-Instruct-2507 variables: []
provider: langgenius/gitee_ai/gitee_ai vision:
prompt_template: enabled: false
- id: 1129723a-50cb-4350-a118-3b9ac6dac523 height: 89
role: system id: '1758768238460'
text: 'You are the computer that answers the admin''s question. Always start position:
with "hello admin" . You know AIID(AI innovative Design) is a master program x: 389.3839309072489
in open FIESTA in Shenzhen. ' y: 792.6856819774588
positionAbsolute:
x: 389.3839309072489
y: 792.6856819774588
selected: false selected: false
title: Admin LLM sourcePosition: right
type: llm targetPosition: left
variables: [] type: custom
vision: width: 243
enabled: false - data:
height: 89 answer: '{{#1758768238460.text#}}'
id: '1758768238460' desc: ''
position: selected: false
x: 389.3839309072489 title: Answer 3
y: 792.6856819774588 type: answer
positionAbsolute: variables: []
x: 389.3839309072489 height: 104
y: 792.6856819774588 id: '1758768309599'
selected: false position:
sourcePosition: right x: 707.3520684153225
targetPosition: left y: 832.4705087633828
type: custom positionAbsolute:
width: 243 x: 707.3520684153225
- data: y: 832.4705087633828
answer: '{{#1758768238460.text#}}'
desc: ''
selected: false selected: false
title: Answer 3 sourcePosition: right
type: answer targetPosition: left
variables: [] type: custom
height: 104 width: 243
id: '1758768309599' - data:
position: answer: Password Correct! You are now log in as ADMIN
x: 707.3520684153225 desc: ''
y: 832.4705087633828 selected: false
positionAbsolute: title: Answer 3
x: 707.3520684153225 type: answer
y: 832.4705087633828 variables: []
selected: false height: 117
sourcePosition: right id: '1758768400561'
targetPosition: left position:
type: custom x: 1301.383930907249
width: 243 y: 426.91916104676926
- data: positionAbsolute:
answer: Password Correct! You are now log in as ADMIN x: 1301.383930907249
desc: '' y: 426.91916104676926
selected: false selected: false
title: Answer 3 sourcePosition: right
type: answer targetPosition: left
variables: [] type: custom
height: 117 width: 243
id: '1758768400561' - data:
position: answer: Password Incorrect! You are now log in as GUEST
x: 1301.383930907249 desc: ''
y: 426.91916104676926 selected: false
positionAbsolute: title: Answer 4
x: 1301.383930907249 type: answer
y: 426.91916104676926 variables: []
selected: false height: 117
sourcePosition: right id: '1758768418040'
targetPosition: left position:
type: custom x: 1498.8808102839819
width: 243 y: 553.9191610467692
- data: positionAbsolute:
answer: Password Incorrect! You are now log in as GUEST x: 1498.8808102839819
desc: '' y: 553.9191610467692
selected: false selected: false
title: Answer 4 sourcePosition: right
type: answer targetPosition: left
variables: [] type: custom
height: 117 width: 243
id: '1758768418040'
position:
x: 1498.8808102839819
y: 553.9191610467692
positionAbsolute:
x: 1498.8808102839819
y: 553.9191610467692
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 243
viewport: viewport:
x: 273.4245102591761 x: 273.4245102591761
y: -8.654295375493462 y: -8.654295375493462
@@ -6,45 +6,45 @@ app:
name: Love Loop name: Love Loop
use_icon_as_answer_icon: false use_icon_as_answer_icon: false
dependencies: dependencies:
- current_identifier: null - current_identifier: null
type: marketplace type: marketplace
value: value:
marketplace_plugin_unique_identifier: langgenius/gitee_ai:0.1.4@f621ace33bb3c140f5a1e3533fcb518f558c7b945d63523c0f85810a4b4a8b93 marketplace_plugin_unique_identifier: langgenius/gitee_ai:0.1.4@f621ace33bb3c140f5a1e3533fcb518f558c7b945d63523c0f85810a4b4a8b93
kind: app kind: app
version: 0.3.0 version: 0.3.0
workflow: workflow:
conversation_variables: conversation_variables:
- description: '' - description: ''
id: 411d934a-94cb-4899-a892-31300f69228e id: 411d934a-94cb-4899-a892-31300f69228e
name: words name: words
selector: selector:
- conversation - conversation
- words - words
value: love value: love
value_type: string value_type: string
- description: '' - description: ''
id: 7aca1d78-2dbc-4ccf-8428-0f0eed7b203c id: 7aca1d78-2dbc-4ccf-8428-0f0eed7b203c
name: WORDS name: WORDS
selector: selector:
- conversation - conversation
- WORDS - WORDS
value: '' value: ''
value_type: string value_type: string
environment_variables: [] environment_variables: []
features: features:
file_upload: file_upload:
allowed_file_extensions: allowed_file_extensions:
- .JPG - .JPG
- .JPEG - .JPEG
- .PNG - .PNG
- .GIF - .GIF
- .WEBP - .WEBP
- .SVG - .SVG
allowed_file_types: allowed_file_types:
- image - image
allowed_file_upload_methods: allowed_file_upload_methods:
- local_file - local_file
- remote_url - remote_url
enabled: false enabled: false
fileUploadConfig: fileUploadConfig:
audio_file_size_limit: 50 audio_file_size_limit: 50
@@ -57,8 +57,8 @@ workflow:
enabled: false enabled: false
number_limits: 3 number_limits: 3
transfer_methods: transfer_methods:
- local_file - local_file
- remote_url - remote_url
number_limits: 3 number_limits: 3
opening_statement: '' opening_statement: ''
retriever_resource: retriever_resource:
@@ -76,312 +76,313 @@ workflow:
voice: '' voice: ''
graph: graph:
edges: edges:
- data: - data:
isInIteration: false isInIteration: false
isInLoop: true isInLoop: true
loop_id: '1758765136208' loop_id: '1758765136208'
sourceType: loop-start sourceType: loop-start
targetType: llm targetType: llm
id: 1758765136208start-source-1758765344915-target id: 1758765136208start-source-1758765344915-target
source: 1758765136208start source: 1758765136208start
sourceHandle: source sourceHandle: source
target: '1758765344915' target: '1758765344915'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 1002 zIndex: 1002
- data: - data:
isInLoop: false isInLoop: false
sourceType: loop sourceType: loop
targetType: answer targetType: answer
id: 1758765136208-source-answer-target id: 1758765136208-source-answer-target
source: '1758765136208' source: '1758765136208'
sourceHandle: source sourceHandle: source
target: answer target: answer
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
- data: - data:
isInIteration: false isInIteration: false
isInLoop: true isInLoop: true
loop_id: '1758765136208' loop_id: '1758765136208'
sourceType: llm sourceType: llm
targetType: code targetType: code
id: 1758765344915-source-1758765883132-target id: 1758765344915-source-1758765883132-target
source: '1758765344915' source: '1758765344915'
sourceHandle: source sourceHandle: source
target: '1758765883132' target: '1758765883132'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 1002 zIndex: 1002
- data: - data:
isInIteration: false isInIteration: false
isInLoop: true isInLoop: true
loop_id: '1758765136208' loop_id: '1758765136208'
sourceType: code sourceType: code
targetType: assigner targetType: assigner
id: 1758765883132-source-1758765428475-target id: 1758765883132-source-1758765428475-target
source: '1758765883132' source: '1758765883132'
sourceHandle: source sourceHandle: source
target: '1758765428475' target: '1758765428475'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 1002 zIndex: 1002
- data: - data:
isInIteration: false isInIteration: false
isInLoop: false isInLoop: false
sourceType: start sourceType: start
targetType: assigner targetType: assigner
id: 1758764476473-source-1758765920251-target id: 1758764476473-source-1758765920251-target
source: '1758764476473' source: '1758764476473'
sourceHandle: source sourceHandle: source
target: '1758765920251' target: '1758765920251'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
- data: - data:
isInLoop: false isInLoop: false
sourceType: assigner sourceType: assigner
targetType: loop targetType: loop
id: 1758765920251-source-1758765136208-target id: 1758765920251-source-1758765136208-target
source: '1758765920251' source: '1758765920251'
sourceHandle: source sourceHandle: source
target: '1758765136208' target: '1758765136208'
targetHandle: target targetHandle: target
type: custom type: custom
zIndex: 0 zIndex: 0
nodes: nodes:
- data: - data:
desc: '' desc: ''
selected: false
title: Start
type: start
variables: []
height: 54
id: '1758764476473'
position:
x: 115.88661184766386
y: 178.92252604934293
positionAbsolute:
x: 115.88661184766386
y: 178.92252604934293
selected: false selected: false
title: Start sourcePosition: right
type: start targetPosition: left
variables: [] type: custom
height: 54 width: 244
id: '1758764476473' - data:
position: answer: '{{#conversation.words#}}'
x: 115.88661184766386 desc: ''
y: 178.92252604934293 selected: false
positionAbsolute: title: Answer
x: 115.88661184766386 type: answer
y: 178.92252604934293 variables: []
selected: false height: 105
sourcePosition: right id: answer
targetPosition: left position:
type: custom x: 1456.1539893939312
width: 244 y: 178.92252604934293
- data: positionAbsolute:
answer: '{{#conversation.words#}}' x: 1456.1539893939312
desc: '' y: 178.92252604934293
selected: false selected: false
title: Answer sourcePosition: right
type: answer targetPosition: left
variables: [] type: custom
height: 105 width: 244
id: answer - data:
position: break_conditions: []
x: 1456.1539893939312 desc: ''
y: 178.92252604934293 error_handle_mode: terminated
positionAbsolute: height: 462
x: 1456.1539893939312 logical_operator: and
y: 178.92252604934293 loop_count: 5
selected: false loop_variables: []
sourcePosition: right selected: false
targetPosition: left start_node_id: 1758765136208start
type: custom title: Loop
width: 244 type: loop
- data: width: 682
break_conditions: []
desc: ''
error_handle_mode: terminated
height: 462 height: 462
logical_operator: and id: '1758765136208'
loop_count: 5 position:
loop_variables: [] x: 731.5560922291256
y: 187.5878472722298
positionAbsolute:
x: 731.5560922291256
y: 187.5878472722298
selected: false selected: false
start_node_id: 1758765136208start sourcePosition: right
title: Loop targetPosition: left
type: loop type: custom
width: 682 width: 682
height: 462 zIndex: 1
id: '1758765136208' - data:
position: desc: ''
x: 731.5560922291256 isInLoop: true
y: 187.5878472722298 selected: false
positionAbsolute: title: ''
x: 731.5560922291256 type: loop-start
y: 187.5878472722298 draggable: false
selected: false height: 48
sourcePosition: right id: 1758765136208start
targetPosition: left parentId: '1758765136208'
type: custom position:
width: 682 x: 24
zIndex: 1 y: 68
- data: positionAbsolute:
desc: '' x: 755.5560922291256
isInLoop: true y: 255.5878472722298
selectable: false
sourcePosition: right
targetPosition: left
type: custom-loop-start
width: 44
zIndex: 1002
- data:
context:
enabled: false
variable_selector: []
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1758765136208'
model:
completion_params:
temperature: 0.7
mode: chat
name: Qwen3-235B-A22B-Instruct-2507
provider: langgenius/gitee_ai/gitee_ai
prompt_template:
- id: e27d2ef8-3dc0-4fbf-8866-90ec3f10c758
role: system
text: write a word with a similar meaning to the words user input.
- id: 624597ef-8e03-49c7-8365-4b17dc487032
role: user
text: '{{#conversation.words#}}'
selected: false
title: LLM 2
type: llm
variables: []
vision:
enabled: false
height: 90
id: '1758765344915'
parentId: '1758765136208'
position:
x: 114.28909334084415
y: 65
positionAbsolute:
x: 845.8451855699698
y: 252.5878472722298
selected: true
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
desc: ''
isInIteration: false
isInLoop: true
items:
- input_type: variable
operation: over-write
value:
- '1758765883132'
- result
variable_selector:
- conversation
- words
write_mode: over-write
loop_id: '1758765136208'
selected: false
title: Variable Assigner
type: assigner
version: '2'
height: 88
id: '1758765428475'
parentId: '1758765136208'
position:
x: 418.79897041375784
y: 194.04290759019386
positionAbsolute:
x: 1150.3550626428835
y: 381.63075486242366
selected: false selected: false
title: '' sourcePosition: right
type: loop-start targetPosition: left
draggable: false type: custom
height: 48 width: 244
id: 1758765136208start zIndex: 1002
parentId: '1758765136208' - data:
position: code:
x: 24 "\ndef main(arg1: str, arg2: str) -> dict:\n return {\n \"\
y: 68 result\": arg1 +arg2 ,\n }\n"
positionAbsolute: code_language: python3
x: 755.5560922291256 desc: ''
y: 255.5878472722298 isInIteration: false
selectable: false isInLoop: true
sourcePosition: right loop_id: '1758765136208'
targetPosition: left outputs:
type: custom-loop-start result:
width: 44 children: null
zIndex: 1002 type: string
- data: selected: false
context: title: Connect
enabled: false type: code
variable_selector: [] variables:
desc: '' - value_selector:
isInIteration: false - conversation
isInLoop: true - words
loop_id: '1758765136208' variable: arg1
model: - value_selector:
completion_params: - '1758765344915'
temperature: 0.7 - text
mode: chat variable: arg2
name: Qwen3-235B-A22B-Instruct-2507 height: 54
provider: langgenius/gitee_ai/gitee_ai id: '1758765883132'
prompt_template: parentId: '1758765136208'
- id: e27d2ef8-3dc0-4fbf-8866-90ec3f10c758 position:
role: system x: 111.09640498433282
text: write a word with a similar meaning to the words user input. y: 202.0812369434865
- id: 624597ef-8e03-49c7-8365-4b17dc487032 positionAbsolute:
role: user x: 842.6524972134584
text: '{{#conversation.words#}}' y: 389.6690842157163
selected: false selected: false
title: LLM 2 sourcePosition: right
type: llm targetPosition: left
variables: [] type: custom
vision: width: 244
enabled: false zIndex: 1002
height: 90 - data:
id: '1758765344915' desc: ''
parentId: '1758765136208' items:
position: - input_type: variable
x: 114.28909334084415 operation: over-write
y: 65 value:
positionAbsolute: - sys
x: 845.8451855699698 - query
y: 252.5878472722298 variable_selector:
selected: true - conversation
sourcePosition: right - words
targetPosition: left write_mode: over-write
type: custom selected: false
width: 244 title: define words
zIndex: 1002 type: assigner
- data: version: '2'
desc: '' height: 88
isInIteration: false id: '1758765920251'
isInLoop: true position:
items: x: 404.0977808713518
- input_type: variable y: 178.92252604934293
operation: over-write positionAbsolute:
value: x: 404.0977808713518
- '1758765883132' y: 178.92252604934293
- result
variable_selector:
- conversation
- words
write_mode: over-write
loop_id: '1758765136208'
selected: false selected: false
title: Variable Assigner sourcePosition: right
type: assigner targetPosition: left
version: '2' type: custom
height: 88 width: 244
id: '1758765428475'
parentId: '1758765136208'
position:
x: 418.79897041375784
y: 194.04290759019386
positionAbsolute:
x: 1150.3550626428835
y: 381.63075486242366
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
code: "\ndef main(arg1: str, arg2: str) -> dict:\n return {\n \"\
result\": arg1 +arg2 ,\n }\n"
code_language: python3
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1758765136208'
outputs:
result:
children: null
type: string
selected: false
title: Connect
type: code
variables:
- value_selector:
- conversation
- words
variable: arg1
- value_selector:
- '1758765344915'
- text
variable: arg2
height: 54
id: '1758765883132'
parentId: '1758765136208'
position:
x: 111.09640498433282
y: 202.0812369434865
positionAbsolute:
x: 842.6524972134584
y: 389.6690842157163
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
desc: ''
items:
- input_type: variable
operation: over-write
value:
- sys
- query
variable_selector:
- conversation
- words
write_mode: over-write
selected: false
title: define words
type: assigner
version: '2'
height: 88
id: '1758765920251'
position:
x: 404.0977808713518
y: 178.92252604934293
positionAbsolute:
x: 404.0977808713518
y: 178.92252604934293
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
viewport: viewport:
x: 59.39316938104503 x: 59.39316938104503
y: 131.3127545416781 y: 131.3127545416781
@@ -56,13 +56,13 @@ API 无处不在,在后台默默工作。
- **Parameters:** `city=London` & `apiKey=your_access_key` - **Parameters:** `city=London` & `apiKey=your_access_key`
- **“响应”(回传的数据):** - **“响应”(回传的数据):**
`json `json
{ {
"city": "London", "city": "London",
"temperature": "15°C", "temperature": "15°C",
"condition": "Cloudy", "condition": "Cloudy",
"humidity": "70%" "humidity": "70%"
} }
` `
应用拿到这些数据后,再把它们漂亮地展示在你的手机屏幕上。 应用拿到这些数据后,再把它们漂亮地展示在你的手机屏幕上。
**地图服务 API** **地图服务 API**
@@ -73,16 +73,16 @@ API 无处不在,在后台默默工作。
- **Parameters:** `origin=Eiffel Tower` & `destination=Louvre Museum` & `mode=driving` - **Parameters:** `origin=Eiffel Tower` & `destination=Louvre Museum` & `mode=driving`
- **“响应”(回传的数据):** - **“响应”(回传的数据):**
`json `json
{ {
"total_distance": "4.5 kilometers", "total_distance": "4.5 kilometers",
"estimated_time": "15 minutes", "estimated_time": "15 minutes",
"route_steps": [ "route_steps": [
"1. Head east on Champ de Mars...", "1. Head east on Champ de Mars...",
"2. Turn left onto Quai Branly...", "2. Turn left onto Quai Branly...",
"..." "..."
] ]
} }
` `
通过这些数据,应用就能在地图上绘制路线并提供导航指令。 通过这些数据,应用就能在地图上绘制路线并提供导航指令。
**社交媒体登录 API** **社交媒体登录 API**
@@ -123,14 +123,14 @@ user_info = {
我们也可参考某家云厂商的[数据库选型推荐](https://help.aliyun.com/zh/govcloud/getting-started/select-database-services),根据场景可进行不同数据库类型的选择,你可以对比不同云厂商的数据库规格选出最合适的进行使用。 我们也可参考某家云厂商的[数据库选型推荐](https://help.aliyun.com/zh/govcloud/getting-started/select-database-services),根据场景可进行不同数据库类型的选择,你可以对比不同云厂商的数据库规格选出最合适的进行使用。
| 数据库类型 | 数据库名称 | 价格 | 适用场景 | | 数据库类型 | 数据库名称 | 价格 | 适用场景 |
| ------------ | ---------------- | ---- | -------------------------------------------------------------------------------------------------------------------------------- | | ------------ | ---------------- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 关系型数据库 | RDS MySQL版 | 低 | 基础版:学习以及小型网站高可用版:一定业务压力的中型数据库场景集群版:业务不允许中断,访问压力较大 | | 关系型数据库 | RDS MySQL版 | 低 | 基础版:学习以及小型网站高可用版:一定业务压力的中型数据库场景集群版:业务不允许中断,访问压力较大 |
| | RDS SQL server版 | 高 | 基础版:测试以及小型商业化网站高可用版:企业级商业化网站集群版:企业业务不允许中断,访问压力较大 | | | RDS SQL server版 | 高 | 基础版:测试以及小型商业化网站高可用版:企业级商业化网站集群版:企业业务不允许中断,访问压力较大 |
| | RDS PostgreSQL版 | 最低 | 基础版:学习以及小型网站高可用版:一定业务压力的中型数据库场景集群版:业务不允许中断,访问压力较大的场景,其性能较一般MySQL高 | | | RDS PostgreSQL版 | 最低 | 基础版:学习以及小型网站高可用版:一定业务压力的中型数据库场景集群版:业务不允许中断,访问压力较大的场景,其性能较一般MySQL高 |
| | RDS PPAS版 | 高 | 通用型:兼容Oracle业务,但业务压力Udacity,虚拟化可以满足其需求独享型:面对需要独享物理机的业务,一般为高并发Oracle类业务 | | | RDS PPAS版 | 高 | 通用型:兼容Oracle业务,但业务压力Udacity,虚拟化可以满足其需求独享型:面对需要独享物理机的业务,一般为高并发Oracle类业务 |
| | DRDS | 中 | 入门版本:4 Core 8 G,价格亲民,适合中小型在线业务企业版:16 Core 32 G,复杂 SQL 响应好,适合超高并发在线业务至尊版:32 Core 64 G,复杂 SQL 执行响应最好,提供超大规格选择 | | | DRDS | 中 | 入门版本:4 Core 8 G,价格亲民,适合中小型在线业务企业版:16 Core 32 G,复杂 SQL 响应好,适合超高并发在线业务至尊版:32 Core 64 G,复杂 SQL 执行响应最好,提供超大规格选择 |
| NoSQL数据库 | Redis | 中 | 双机热备Redis:一般作为持久化数据库提高业务可用性集群版本的Redis:一般作为缓存层,加速应用访问,解决一般数据库无法负载的读取压力 | | NoSQL数据库 | Redis | 中 | 双机热备Redis:一般作为持久化数据库提高业务可用性集群版本的Redis:一般作为缓存层,加速应用访问,解决一般数据库无法负载的读取压力 |
| | MongoDB版 | 中 | 单节点实例单节点:适用于开发、测试及其他非企业核心数据存储的场景副本集实例:适用于某些业务场景下对数据库有更高读取性能需求,如阅读类网站、订单查询系统等读多写少场景或有临时活动等突发业务需求分片集群实例:基于多个副本集(每个副本集沿用三副本模式)组成的分片集群实例,提供更高的读取性能需求,为实时在线业务提供高速读取性能 | | | MongoDB版 | 中 | 单节点实例单节点:适用于开发、测试及其他非企业核心数据存储的场景副本集实例:适用于某些业务场景下对数据库有更高读取性能需求,如阅读类网站、订单查询系统等读多写少场景或有临时活动等突发业务需求分片集群实例:基于多个副本集(每个副本集沿用三副本模式)组成的分片集群实例,提供更高的读取性能需求,为实时在线业务提供高速读取性能 |
光说不易理解,我们通过一个具体的“博客文章”场景,来看看同样的数据在关系数据库 (SQL) 和不同类型的非关系数据库 (NoSQL) 中是如何存储的。 光说不易理解,我们通过一个具体的“博客文章”场景,来看看同样的数据在关系数据库 (SQL) 和不同类型的非关系数据库 (NoSQL) 中是如何存储的。
@@ -1,4 +1,5 @@
# 如何构建一个最简单的微信小程序 # 如何构建一个最简单的微信小程序
# 1. 什么是微信小程序和微信小程序开发 # 1. 什么是微信小程序和微信小程序开发
在这篇教程中,我们将完整跑通一条闭环:从脑海中的一个想法,到在微信里可以搜索、可以扫码打开的真实小程序。 在这篇教程中,我们将完整跑通一条闭环:从脑海中的一个想法,到在微信里可以搜索、可以扫码打开的真实小程序。
@@ -105,7 +106,7 @@
![](images/image6.png) ![](images/image6.png)
**接着安装 HBuilderX** 。访问 https://www.dcloud.io/hbuilderx.html ,选择对应操作系统的发行包下载。HBuilderX 的包体非常小,启动速度也很快。安装完成后,你可以先熟悉一下它的界面,不需要深入研究功能;在后面的章节中,我们会用它来创建一个 uni-app 小程序模板,作为整个项目的起点。 **接着安装 HBuilderX** 。访问 https://www.dcloud.io/hbuilderx.html ,选择对应操作系统的发行包下载。HBuilderX 的包体非常小,启动速度也很快。安装完成后,你可以先熟悉一下它的界面,不需要深入研究功能;在后面的章节中,我们会用它来创建一个 uni-app 小程序模板,作为整个项目的起点。
![](images/image7.png) ![](images/image7.png)
@@ -197,7 +198,7 @@ AI 完成第一轮开发之后,代码已经落在项目里了,但这时候
这就是 vibecoding 的优势所在:你不必自己去翻代码,查找事件绑定的位置、计算坐标的逻辑,而是直接把想法告诉 AI。例如,你可以在 Trae 的对话框里这样描述: 这就是 vibecoding 的优势所在:你不必自己去翻代码,查找事件绑定的位置、计算坐标的逻辑,而是直接把想法告诉 AI。例如,你可以在 Trae 的对话框里这样描述:
把按键换成摇杆控制,并且用户松开摇杆时蛇保持同方向移动,直到用户再次松开摇杆。 把按键换成摇杆控制,并且用户松开摇杆时蛇保持同方向移动,直到用户再次松开摇杆。
只要你把需求讲得足够清楚,AI 会自动定位到对应界面和逻辑文件,替你完成控件样式、交互绑定和方向处理等改动。 只要你把需求讲得足够清楚,AI 会自动定位到对应界面和逻辑文件,替你完成控件样式、交互绑定和方向处理等改动。
@@ -211,13 +212,13 @@ AI 完成第一轮开发之后,代码已经落在项目里了,但这时候
AI 生成的版本不一定一开始就完美。有时候你会遇到这些情况: AI 生成的版本不一定一开始就完美。有时候你会遇到这些情况:
* 运行时报错,小程序无法正常打开; - 运行时报错,小程序无法正常打开;
* 功能大致正确,但细节和你想象的不太一样; - 功能大致正确,但细节和你想象的不太一样;
* 界面可以用,但你觉得还可以更好看或更顺手。 - 界面可以用,但你觉得还可以更好看或更顺手。
在这些时候,不需要自己钻进代码里盲改,而是可以把遇到的问题直接用自然语言重新描述给 Trae 中的 AI 助手,例如: 在这些时候,不需要自己钻进代码里盲改,而是可以把遇到的问题直接用自然语言重新描述给 Trae 中的 AI 助手,例如:
现在摇杆控制已经生效了,但有时候蛇会突然停止不动,请帮我检查当前实现哪里有问题。 或者: 现在游戏可以玩,但界面有点拥挤,我希望在手机上显示时上下留出更多空白。请你帮我调整布局。 现在摇杆控制已经生效了,但有时候蛇会突然停止不动,请帮我检查当前实现哪里有问题。 或者: 现在游戏可以玩,但界面有点拥挤,我希望在手机上显示时上下留出更多空白。请你帮我调整布局。
AI 会根据你当前的项目状态和描述,给出修改建议并直接应用在代码里。如果修改之后结果更糟或方向不对,你依然可以使用回退,把工程恢复到前一个稳定版本,再换一种说法尝试。 AI 会根据你当前的项目状态和描述,给出修改建议并直接应用在代码里。如果修改之后结果更糟或方向不对,你依然可以使用回退,把工程恢复到前一个稳定版本,再换一种说法尝试。
@@ -229,10 +230,10 @@ AI 会根据你当前的项目状态和描述,给出修改建议并直接应
经过一轮又一轮的 **自然语言叙述 → AI 修改 → 在微信开发者工具中预览 → 继续对话微调** ,我最终得到的是这样一个成品: 经过一轮又一轮的 **自然语言叙述 → AI 修改 → 在微信开发者工具中预览 → 继续对话微调** ,我最终得到的是这样一个成品:
* 有完整的游戏页面; - 有完整的游戏页面;
* 蛇可以顺畅移动并吃到食物; - 蛇可以顺畅移动并吃到食物;
* 支持摇杆控制; - 支持摇杆控制;
* 在小程序模拟器中可以顺利运行。 - 在小程序模拟器中可以顺利运行。
最终开发成品如下: 最终开发成品如下: