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
+2 -3
View File
@@ -47,7 +47,7 @@ export default defineConfig({
{
text: '1. 认识 AI IDE 工具',
link: '/stage-1/1.1-introduction-to-ai-ide/'
},
},
{
text: '2. 动手做出原型',
link: '/stage-1/1.2-building-prototype/'
@@ -71,8 +71,7 @@ export default defineConfig({
{
text: '附录 B:常见报错及解决方案',
link: '/stage-1/appendix-b-common-errors/'
}
,
},
{
text: '附录示例:贪吃蛇游戏教程',
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 { useData } from 'vitepress'
import TextType from './components/TextType.vue'
import { onMounted, ref, watch } from 'vue'
const { frontmatter } = useData()
@@ -12,16 +13,251 @@ const homeTaglineTyping = {
postDeletingDelay: 500,
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>
<template>
<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>
<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>
<TextType :text="frontmatter.hero.tagline" v-bind="homeTaglineTyping" :loop="true" />
<TextType
:text="frontmatter.hero.tagline"
v-bind="homeTaglineTyping"
:loop="true"
/>
</ClientOnly>
</div>
</template>
</DefaultTheme.Layout>
</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: {
type: Array,
default: () => [
{ title: "困境与机会", description: "普通人的编程新可能" },
{ title: "能力初探", description: "60秒极速开发体验" },
{ title: "原生实战", description: "打造AI原生贪吃蛇" },
{ title: "拓展创造", description: "举一反三做游戏" }
{ title: '困境与机会', description: '普通人的编程新可能' },
{ title: '能力初探', description: '60秒极速开发体验' },
{ title: '原生实战', description: '打造AI原生贪吃蛇' },
{ title: '拓展创造', description: '举一反三做游戏' }
]
}
})
+55 -18
View File
@@ -1,5 +1,12 @@
<script setup>
import { computed, onMounted, onUnmounted, ref, useAttrs, watchEffect } from 'vue'
import {
computed,
onMounted,
onUnmounted,
ref,
useAttrs,
watchEffect
} from 'vue'
const props = defineProps({
text: {
@@ -91,7 +98,9 @@ const currentTextIndex = ref(0)
const isVisible = ref(!props.startOnVisible)
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(() => ({
animationDuration: `${props.cursorBlinkDuration}s`
@@ -104,8 +113,14 @@ const currentColor = computed(() => {
const getRandomSpeed = () => {
if (!props.variableSpeed) return props.typingSpeed
const min = typeof props.variableSpeed.min === 'number' ? props.variableSpeed.min : props.typingSpeed
const max = typeof props.variableSpeed.max === 'number' ? props.variableSpeed.max : props.typingSpeed
const min =
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
return Math.random() * (max - min) + min
}
@@ -114,7 +129,7 @@ let observer
onMounted(() => {
if (!props.startOnVisible || !containerRef.value) return
observer = new IntersectionObserver(
entries => {
(entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
isVisible.value = true
@@ -131,7 +146,7 @@ onUnmounted(() => {
if (observer) observer.disconnect()
})
watchEffect(onCleanup => {
watchEffect((onCleanup) => {
if (!isVisible.value) return
if (!textArray.value.length) {
@@ -140,13 +155,16 @@ watchEffect(onCleanup => {
}
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) {
return
}
const shouldStopAtEnd = !props.loop && currentTextIndex.value === textArray.value.length - 1
const shouldStopAtEnd =
!props.loop && currentTextIndex.value === textArray.value.length - 1
let timeoutId
@@ -155,11 +173,15 @@ watchEffect(onCleanup => {
if (!displayedText.value) {
isDeleting.value = false
if (props.onSentenceComplete) {
props.onSentenceComplete(textArray.value[currentTextIndex.value], currentTextIndex.value)
props.onSentenceComplete(
textArray.value[currentTextIndex.value],
currentTextIndex.value
)
}
if (shouldStopAtEnd) return
timeoutId = setTimeout(() => {
currentTextIndex.value = (currentTextIndex.value + 1) % textArray.value.length
currentTextIndex.value =
(currentTextIndex.value + 1) % textArray.value.length
currentCharIndex.value = 0
}, props.postDeletingDelay)
return
@@ -172,10 +194,13 @@ watchEffect(onCleanup => {
}
if (currentCharIndex.value < processedText.length) {
timeoutId = setTimeout(() => {
displayedText.value += processedText[currentCharIndex.value]
currentCharIndex.value += 1
}, props.variableSpeed ? getRandomSpeed() : props.typingSpeed)
timeoutId = setTimeout(
() => {
displayedText.value += processedText[currentCharIndex.value]
currentCharIndex.value += 1
},
props.variableSpeed ? getRandomSpeed() : props.typingSpeed
)
return
}
@@ -185,7 +210,11 @@ watchEffect(onCleanup => {
}, props.pauseDuration)
}
if (currentCharIndex.value === 0 && !isDeleting.value && !displayedText.value) {
if (
currentCharIndex.value === 0 &&
!isDeleting.value &&
!displayedText.value
) {
timeoutId = setTimeout(schedule, props.initialDelay)
} else {
schedule()
@@ -197,7 +226,9 @@ watchEffect(onCleanup => {
const shouldHideCursor = computed(() => {
if (!props.hideCursorWhileTyping) return false
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
})
</script>
@@ -209,13 +240,19 @@ const shouldHideCursor = computed(() => {
:class="['text-type', className]"
v-bind="attrs"
>
<span class="text-type__content" :style="{ color: currentColor || 'inherit' }">
<span
class="text-type__content"
:style="{ color: currentColor || 'inherit' }"
>
{{ displayedText }}
</span>
<span
v-if="showCursor"
class="text-type__cursor"
:class="[cursorClassName, shouldHideCursor ? 'text-type__cursor--hidden' : '']"
:class="[
cursorClassName,
shouldHideCursor ? 'text-type__cursor--hidden' : ''
]"
:style="cursorStyle"
>
{{ cursorCharacter }}
+14 -9
View File
@@ -7,13 +7,17 @@ import TypeIt from 'typeit'
import { onMounted, watch, nextTick } from 'vue'
import { useRoute, useData } from 'vitepress'
import './style.css'
import Layout from './Layout.vue'
import StepBar from './components/StepBar.vue'
import ChapterIntroduction from './components/ChapterIntroduction.vue'
export default {
extends: DefaultTheme,
Layout,
enhanceApp({ app }) {
app.use(ElementPlus)
app.component('StepBar', StepBar)
app.component('ChapterIntroduction', ChapterIntroduction)
},
setup() {
const route = useRoute()
@@ -67,7 +71,7 @@ export default {
const taglineEl = document.querySelector('.VPHomeHero .tagline')
if (taglineEl) {
taglineEl.innerHTML = ''
const typeIt = new TypeIt(taglineEl, {
speed: 50,
startDelay: 500,
@@ -75,9 +79,9 @@ export default {
})
taglineData.forEach((text) => {
typeIt.type(text).pause(2000).delete().pause(500)
typeIt.type(text).pause(2000).delete().pause(500)
})
typeIt.go()
}
}
@@ -85,7 +89,7 @@ export default {
const optimizeImages = () => {
const images = document.querySelectorAll('.vp-doc img')
images.forEach(img => {
images.forEach((img) => {
if (img.complete) {
applyImageStyle(img)
} else {
@@ -133,11 +137,12 @@ export default {
watch(
() => route.path,
() => nextTick(() => {
initViewer()
initTypewriter()
optimizeImages()
})
() =>
nextTick(() => {
initViewer()
initTypewriter()
optimizeImages()
})
)
}
}
+64 -2
View File
@@ -2,10 +2,70 @@
/* Easy-Vibe Theme Fix v2025-01-12 */
/* 通过变量控制分组底部留白(默认 24px) */
--vp-sidebar-nav-section-gap: 8px;
--ev-doc-font-size: 13px;
--ev-doc-line-height: 1.5;
}
.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 选择器更高优先级。
@@ -44,7 +104,9 @@
/* 进一步压缩分组标题与第一项之间的间距 */
: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;
}
+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 产品经理
@@ -48,32 +48,32 @@
#### 前端部分
| 章节 | 关键内容 | 状态 |
| :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :--- |
| 前端零:使用 lovart 生产素材 | 学会用 lovart 批量生成人物、场景等视觉素材,为 UI 设计和前端开发提供素材基础 | 🚧 |
| 前端一:Figma 与 MasterGo 入门 | 用设计工具梳理信息架构和页面结构,为前端实现打基础 | 🚧 |
| 前端二:构建第一个现代应用程序-UI 设计 | 基于设计稿完成组件化界面,实现从设计到代码的第一条链路 | 🚧 |
| 前端三:参考 UI 设计规范与多产品 UI 设计 | 围绕统一主视觉扩展多产品界面,练习系统化设计能力 | 🚧 |
| 章节 | 关键内容 | 状态 |
| :------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------- | :--- |
| 前端零:使用 lovart 生产素材 | 学会用 lovart 批量生成人物、场景等视觉素材,为 UI 设计和前端开发提供素材基础 | 🚧 |
| 前端一:Figma 与 MasterGo 入门 | 用设计工具梳理信息架构和页面结构,为前端实现打基础 | 🚧 |
| 前端二:构建第一个现代应用程序-UI 设计 | 基于设计稿完成组件化界面,实现从设计到代码的第一条链路 | 🚧 |
| 前端三:参考 UI 设计规范与多产品 UI 设计 | 围绕统一主视觉扩展多产品界面,练习系统化设计能力 | 🚧 |
| [前端四:一起做霍格沃茨画像](/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,打通数据模型与前端页面 | 🚧 |
| 后端三:大模型辅助编写接口代码与接口文档 | 用大模型协助生成接口与数据库文档及代码,实现可读可测的后端 | 🚧 |
| 后端四:Git 工作流与 Zeabur 部署 | 在 Git 工作流中管理代码,并将应用部署到 Zeabur 上线 | 🚧 |
| 后端五:现代 CLI 开发工具 | 使用 CLI 类 AI 编程工具加速开发与调试,形成个人工程化工作流 | 🚧 |
| 后端六:如何集成 stripe 等收费系统 | 接入支付系统,完成收费链路与基础结算流程 | 🚧 |
| 大作业 1:构建第一个现代应用程序-全栈应用 | 综合前端、后端与支付模块,完成可上线的全栈 Web 应用 | 🚧 |
| 大作业 2:现代前端组件库 + Trae 实战 | 使用现代前端组件库与 Trae,独立完成可登录注册并支持收费的产品 | 🚧 |
| 后端三:大模型辅助编写接口代码与接口文档 | 用大模型协助生成接口与数据库文档及代码,实现可读可测的后端 | 🚧 |
| 后端四:Git 工作流与 Zeabur 部署 | 在 Git 工作流中管理代码,并将应用部署到 Zeabur 上线 | 🚧 |
| 后端五:现代 CLI 开发工具 | 使用 CLI 类 AI 编程工具加速开发与调试,形成个人工程化工作流 | 🚧 |
| 后端六:如何集成 stripe 等收费系统 | 接入支付系统,完成收费链路与基础结算流程 | 🚧 |
| 大作业 1:构建第一个现代应用程序-全栈应用 | 综合前端、后端与支付模块,完成可上线的全栈 Web 应用 | 🚧 |
| 大作业 2:现代前端组件库 + Trae 实战 | 使用现代前端组件库与 Trae,独立完成可登录注册并支持收费的产品 | 🚧 |
#### 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 二:学会查询 AI 词典与集成多模态 API | 学会查找合适的模型与 API,并把文本、图像等多模态能力接入产品 | 🚧 |
| AI 二:学会查询 AI 词典与集成多模态 API | 学会查找合适的模型与 API,并把文本、图像等多模态能力接入产品 | 🚧 |
### 三、高级开发工程师
+61 -1
View File
@@ -1,10 +1,70 @@
:root {
/* 调整侧边栏分组之间的间距 */
--vp-sidebar-nav-section-gap: 8px;
--ev-doc-font-size: 13px;
--ev-doc-line-height: 1.5;
}
.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 第一阶段完整课程大纲 (点击收起)
**模块一:AI 时代,会说话就会编程**
- **1.1** 普通人的困境与机会?
- **1.2** AI 能帮你做到什么程度?
- **1.3** 动手:你的第一个 AI 原生应用
**模块二:认识 AI IDE 工具**
- **2.1** 写代码需要什么环境和工具
- **2.2** 什么是 IDE,为什么需要 IDE
- **2.3** AI IDE 和普通 IDE 有什么不同
@@ -175,6 +177,7 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
- **2.5** 怎么跟 AI 说话才有效
**模块三:动手做出原型**
- **3.1** 把需求变成代码的过程
- **3.2** 从一个单页面开始
- **3.3** 遇到报错了怎么办
@@ -182,6 +185,7 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
- **3.5** 把原型做得像那么回事
**模块四:给原型加上 AI 能力**
- **4.1** 什么是 AI 能力接入(API 调用)
- **4.2** 如何接入文生图能力
- **4.3** 如何接入视频生成能力
@@ -189,27 +193,31 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
- **4.5** 成本控制和错误处理
**模块五:完整项目实战**
- **5.1** 制造模拟数据让原型看起来真实
- **5.2** 收集反馈并快速调整
- **5.3** 展示你的成果
**大作业**
- 做一个完整的 Web 应用原型并展示
**附录A:产品思维补充**
- **A.1** 什么是好的产品想法
- **A.2** 如何发现用户真正的需求
- **A.3** 功能优先级怎么排
- **A.4** MVP 思维:最小可行产品
**附录B:常见报错及解决方案**
- **B.1** 页面显示空白或不加载
- **B.2** 数据保存不成功
- **B.3** 样式显示不正常
- **B.4** 点击按钮没反应
- **B.5** API 调用失败
- **B.6** 如何把报错信息有效地反馈给 AI
:::
:::
# 为什么要用项目制来训练?
@@ -231,7 +239,6 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
短期来看,这种训练确实比较折磨人;但从长期来看,它会极大提高你在求职和职业发展中的竞争力:你会更能扛事儿,更能在不确定环境中找到突破口,也更有能力把 AI 变成真正落地的产品,而不是停留在“玩玩 Demo”阶段。
# 提问的艺术:AI 时代的必备技能
在 AI 时代,提问也属于一种 “基本功”。同一份代码、同一个报错,**你怎么提问,几乎决定了 AI 能给出怎样的答案**:是泛泛而谈,还是一步一步给出可落地的改法。
@@ -254,10 +261,10 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
两种方式都可以,但用途不同:
| 方式 | 适用场景 | 关键要求 |
| --- | --- | --- |
| **复制粘贴** | 报错堆栈、日志、代码、配置、API 返回 | 尽量完整,不要只截一行关键字 |
| **截图** | UI 布局问题、交互异常、工具界面找不到按钮 | 截全屏 + 标注重点区域,最好配一句文字说明 |
| 方式 | 适用场景 | 关键要求 |
| ------------ | ----------------------------------------- | ----------------------------------------- |
| **复制粘贴** | 报错堆栈、日志、代码、配置、API 返回 | 尽量完整,不要只截一行关键字 |
| **截图** | UI 布局问题、交互异常、工具界面找不到按钮 | 截全屏 + 标注重点区域,最好配一句文字说明 |
::: info ⭐ 建议优先级
能截图就截图;截图沟通不了再复制粘贴沟通。
@@ -268,9 +275,10 @@ AI coding的出现正在改写传统编程学习的规则。你不再需要花
如果你不是只要答案,而是要“学会”答案。使用类似下面指令能显著提升解释质量:
::: tip 🧠 学习型提问示例
- “请先用 5 句话讲清楚这个概念,再给几个问题提问我验证我理解对了没。”
- ”请你详细解释一下这个报错信息,我不理解为什么会报错。”
:::
:::
# 坚持了好久还是搞不定,我想放弃了
@@ -1,6 +1,5 @@
# 初级一:AI 时代,会说话就会编程
这是一个**基于项目制学习**的学习教程。我们鼓励你跟随步骤一步步操作,并尝试复现结果。
不要担心犯错或修改内容,我们永远相信你可以做到,请你永远记住:
@@ -12,16 +11,18 @@
</div>
</el-card>
## 本章导读
::: info 🎯 学习目标
在这一节,你会用对话式 AI 做出第一个 AI 原生小游戏——一款会“吃单词、写诗、画画”的贪吃蛇,并借此搞清楚 AI 编程的初步效果。
:::
<ChapterIntroduction
duration="约 <strong>4 小时</strong>,可分多次完成"
:tags="['对话式 AI 编程', 'AI 原生小游戏', '贪吃蛇实战']"
coreOutput="AI 原生贪吃蛇 + 自创小游戏"
expectedOutput="1 个可运行的 AI 原生贪吃蛇 + (可选)1 个你自创的 AI 原生小游戏或 Demo"
>
- 预计时间:约 **4 小时**,可分多次完成
- 预期产出:1 个可运行的 AI 原生贪吃蛇 + (可选)1 个你自创的 AI 原生小游戏或 Demo
- Assignment:复现贪吃蛇,并(可选)实现一种你感兴趣的 AI 原生游戏
在这一节,你会用对话式 AI 做出第一个 AI 原生小游戏——一款会“吃单词、写诗、画画”的贪吃蛇,并借此搞清楚 AI 编程的初步效果。
</ChapterIntroduction>
<div style="margin: 50px 0;">
<ClientOnly>
@@ -168,10 +169,11 @@ AI 出现之后,第一次给了普通人一个全新的可能:你不需要会
一个可参考的经验是:
::: warning ⚠️ 适用场景指南
- **原型 / Demo / 内部自用工具**:非常适合先交给 AI 打第一版,再由你迭代细节。
- **面向真实用户的大型产品**:通常需要工程师在架构、抽象、性能和维护上长期投入。
- **强安全 / 强合规系统(如支付、风控、医疗等)**:在当前阶段,不宜“生成完就直接上线”,必须引入严格的审查与测试流程。
:::
:::
在当下,你可以相对安心地把 AI 视作一个高效的 Demo 与自用工具搭档:
只要你愿意多测试、多迭代,多问几轮“这里不对,帮我修一下并解释原因”,在原型与内部工具这一级别,整体质量通常是足够且具备实践价值的。
@@ -2,12 +2,16 @@
## 本章导读
::: info 🎯 学习目标
本章将围绕本地开发环境展开讲解:你将学会从 z.ai 过渡到本地的开发环境,学会在自己的电脑上搭建完整的开发环境,理解什么是 IDE、什么是 AI IDE,以及如何在日常开发中高效地使用它们。
:::
<ChapterIntroduction
duration="约 <strong>1 天</strong>,可分多次完成"
:tags="['本地开发环境搭建', 'IDE 与 AI IDE', '高效开发技巧']"
coreOutput="1 个自创小游戏"
expectedOutput="使用 Trae 产出"
>
- 预计时间:约 **1 天**,可分多次完成
- 预期产出:使用 Trae 产出 **1 个自创小游戏**
本章将围绕本地开发环境展开讲解:你将学会从 z.ai 过渡到本地的开发环境,学会在自己的电脑上搭建完整的开发环境,理解什么是 IDE、什么是 AI IDE,以及如何在日常开发中高效地使用它们。
</ChapterIntroduction>
<div style="margin: 50px 0;">
<ClientOnly>
@@ -548,23 +552,44 @@ AI 很认真地给了你一段代码,你也老老实实地复制进去了,
<div style="font-weight: bold; font-size: 16px;">🚀 挑战任务:打造你的专属游戏</div>
</template>
你已经用本地 AI IDE 做过一个贪吃蛇。现在请你再挑战一个更复杂一点的小游戏,完整走一遍“描述需求 → 生成项目 → 本地运行 → 调试迭代”的流程。
<p>
你已经用本地 AI IDE 做过一个贪吃蛇。现在请你再挑战一个更复杂一点的小游戏,完整走一遍“描述需求 →
生成项目 → 本地运行 → 调试迭代”的流程。
</p>
1. **选择一个比贪吃蛇更复杂的游戏**
- 可以是“俄罗斯方块”“打地鼠”“扫雷”“2048””飞机大战“之类
- 或者你自己想象的一个简单原创游戏
2. **必须用本地 AI IDE 来完成整个过程**
- 新建一个空文件夹,用 AI IDE 打开
- 在侧边栏聊天里描述清楚你的游戏需求
- 让 AI 负责创建文件、搭建项目结构和实现主要逻辑
- 在本地启动开发服务器,确保游戏可以正常运行
3. **有基本的“可玩性”和反馈**
- 至少包含开始、进行中、结束三种状态
- 玩家有明确的操作方式(键盘或鼠标)
- 屏幕上有清晰的得分或进度反馈
4. **至少进行 2 轮以上的迭代**
- 第一轮让 AI 做出“能玩”的版本
- 第二轮以后,逐步提出具体改进(样式、难度、交互优化等)
<ol>
<li>
<strong>选择一个比贪吃蛇更复杂的游戏</strong>
<ul>
<li>可以是“俄罗斯方块”“打地鼠”“扫雷”“2048”“飞机大战”之类</li>
<li>或者你自己想象的一个简单原创游戏</li>
</ul>
</li>
<li>
<strong>必须用本地 AI IDE 来完成整个过程</strong>
<ul>
<li>新建一个空文件夹,用 AI IDE 打开</li>
<li>在侧边栏聊天里描述清楚你的游戏需求</li>
<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>
## 附录
@@ -120,7 +120,6 @@ AI Agent 是一种软件系统,它能够感知环境、做出决策,并自
> - 蛇吃掉的不是食物,而是英文单词。
> - 页面侧边栏展示已收集单词及其数量。
> - 游戏结束后,已收集的单词仍然保留,并在新一局中延续。
>
> - 页面 2:写诗页面(Make Poem
> - 展示与游戏页面相同的单词列表(数据一致)。
> - 提供一个按钮,将当前收集的单词发送给后端生成一首诗。
@@ -6,37 +6,37 @@ app:
name: Log in
use_icon_as_answer_icon: false
dependencies:
- current_identifier: null
type: marketplace
value:
marketplace_plugin_unique_identifier: langgenius/gitee_ai:0.1.4@f621ace33bb3c140f5a1e3533fcb518f558c7b945d63523c0f85810a4b4a8b93
- current_identifier: null
type: marketplace
value:
marketplace_plugin_unique_identifier: langgenius/gitee_ai:0.1.4@f621ace33bb3c140f5a1e3533fcb518f558c7b945d63523c0f85810a4b4a8b93
kind: app
version: 0.3.0
workflow:
conversation_variables:
- description: ''
id: f8cc215e-ef91-437a-a823-7e80a8d345a3
name: LOGIN
selector:
- conversation
- LOGIN
value: none
value_type: string
- description: ''
id: f8cc215e-ef91-437a-a823-7e80a8d345a3
name: LOGIN
selector:
- conversation
- LOGIN
value: none
value_type: string
environment_variables: []
features:
file_upload:
allowed_file_extensions:
- .JPG
- .JPEG
- .PNG
- .GIF
- .WEBP
- .SVG
- .JPG
- .JPEG
- .PNG
- .GIF
- .WEBP
- .SVG
allowed_file_types:
- image
- image
allowed_file_upload_methods:
- local_file
- remote_url
- local_file
- remote_url
enabled: false
fileUploadConfig:
audio_file_size_limit: 50
@@ -49,8 +49,8 @@ workflow:
enabled: false
number_limits: 3
transfer_methods:
- local_file
- remote_url
- local_file
- remote_url
number_limits: 3
opening_statement: 'Please log in with passwords:'
retriever_resource:
@@ -68,436 +68,438 @@ workflow:
voice: ''
graph:
edges:
- data:
sourceType: llm
targetType: answer
id: llm-answer
source: llm
sourceHandle: source
target: answer
targetHandle: target
type: custom
- data:
isInIteration: false
isInLoop: false
sourceType: start
targetType: if-else
id: 1758767725822-source-1758767750205-target
source: '1758767725822'
sourceHandle: source
target: '1758767750205'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: if-else
targetType: assigner
id: 1758767920912-true-1758768026915-target
source: '1758767920912'
sourceHandle: 'true'
target: '1758768026915'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: if-else
targetType: assigner
id: 1758767920912-false-1758768059939-target
source: '1758767920912'
sourceHandle: 'false'
target: '1758768059939'
targetHandle: target
type: custom
zIndex: 0
- data:
isInLoop: false
sourceType: if-else
targetType: llm
id: 1758767750205-f599486b-e4d3-4cdd-9425-31257bf28e82-llm-target
source: '1758767750205'
sourceHandle: f599486b-e4d3-4cdd-9425-31257bf28e82
target: llm
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: if-else
targetType: llm
id: 1758767750205-5f242bc3-7f06-4a88-82e5-235a859d92bf-1758768238460-target
source: '1758767750205'
sourceHandle: 5f242bc3-7f06-4a88-82e5-235a859d92bf
target: '1758768238460'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: llm
targetType: answer
id: 1758768238460-source-1758768309599-target
source: '1758768238460'
sourceHandle: source
target: '1758768309599'
targetHandle: target
type: custom
zIndex: 0
- data:
isInLoop: false
sourceType: if-else
targetType: if-else
id: 1758767750205-true-1758767920912-target
source: '1758767750205'
sourceHandle: 'true'
target: '1758767920912'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: assigner
targetType: answer
id: 1758768026915-source-1758768400561-target
source: '1758768026915'
sourceHandle: source
target: '1758768400561'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: assigner
targetType: answer
id: 1758768059939-source-1758768418040-target
source: '1758768059939'
sourceHandle: source
target: '1758768418040'
targetHandle: target
type: custom
zIndex: 0
- data:
sourceType: llm
targetType: answer
id: llm-answer
source: llm
sourceHandle: source
target: answer
targetHandle: target
type: custom
- data:
isInIteration: false
isInLoop: false
sourceType: start
targetType: if-else
id: 1758767725822-source-1758767750205-target
source: '1758767725822'
sourceHandle: source
target: '1758767750205'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: if-else
targetType: assigner
id: 1758767920912-true-1758768026915-target
source: '1758767920912'
sourceHandle: 'true'
target: '1758768026915'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: if-else
targetType: assigner
id: 1758767920912-false-1758768059939-target
source: '1758767920912'
sourceHandle: 'false'
target: '1758768059939'
targetHandle: target
type: custom
zIndex: 0
- data:
isInLoop: false
sourceType: if-else
targetType: llm
id: 1758767750205-f599486b-e4d3-4cdd-9425-31257bf28e82-llm-target
source: '1758767750205'
sourceHandle: f599486b-e4d3-4cdd-9425-31257bf28e82
target: llm
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: if-else
targetType: llm
id: 1758767750205-5f242bc3-7f06-4a88-82e5-235a859d92bf-1758768238460-target
source: '1758767750205'
sourceHandle: 5f242bc3-7f06-4a88-82e5-235a859d92bf
target: '1758768238460'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: llm
targetType: answer
id: 1758768238460-source-1758768309599-target
source: '1758768238460'
sourceHandle: source
target: '1758768309599'
targetHandle: target
type: custom
zIndex: 0
- data:
isInLoop: false
sourceType: if-else
targetType: if-else
id: 1758767750205-true-1758767920912-target
source: '1758767750205'
sourceHandle: 'true'
target: '1758767920912'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: assigner
targetType: answer
id: 1758768026915-source-1758768400561-target
source: '1758768026915'
sourceHandle: source
target: '1758768400561'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: assigner
targetType: answer
id: 1758768059939-source-1758768418040-target
source: '1758768059939'
sourceHandle: source
target: '1758768418040'
targetHandle: target
type: custom
zIndex: 0
nodes:
- data:
desc: ''
- data:
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
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
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:
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
context:
enabled: false
size: 10
model:
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" '
variable_selector: []
desc: ''
memory:
query_prompt_template: '{{#sys.query#}}'
role_prefix:
assistant: ''
user: ''
window:
enabled: false
size: 10
model:
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
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
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
answer: '{{#llm.text#}}'
desc: ''
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
answer: '{{#llm.text#}}'
desc: ''
selected: true
title: Answer
type: answer
variables: []
height: 104
id: answer
position:
x: 707.3520684153225
y: 673.6318886739399
positionAbsolute:
x: 707.3520684153225
y: 673.6318886739399
selected: true
title: Answer
type: answer
variables: []
height: 104
id: answer
position:
x: 707.3520684153225
y: 673.6318886739399
positionAbsolute:
x: 707.3520684153225
y: 673.6318886739399
selected: true
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
cases:
- case_id: 'true'
conditions:
- comparison_operator: is
id: cf0932c4-e5f4-478e-9437-1db63af30ffd
value: none
varType: string
variable_selector:
- conversation
- LOGIN
id: 'true'
logical_operator: and
- case_id: f599486b-e4d3-4cdd-9425-31257bf28e82
conditions:
- comparison_operator: is
id: 18c716ea-aac5-4af7-9a50-1fd5dc40d18e
value: guest
varType: string
variable_selector:
- conversation
- LOGIN
id: f599486b-e4d3-4cdd-9425-31257bf28e82
logical_operator: and
- case_id: 5f242bc3-7f06-4a88-82e5-235a859d92bf
conditions:
- comparison_operator: is
id: 490f9251-1012-4f85-8bb7-29f355ca6b5c
value: admin
varType: string
variable_selector:
- conversation
- LOGIN
id: 5f242bc3-7f06-4a88-82e5-235a859d92bf
logical_operator: and
desc: ''
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
cases:
- case_id: 'true'
conditions:
- comparison_operator: is
id: cf0932c4-e5f4-478e-9437-1db63af30ffd
value: none
varType: string
variable_selector:
- conversation
- LOGIN
id: 'true'
logical_operator: and
- case_id: f599486b-e4d3-4cdd-9425-31257bf28e82
conditions:
- comparison_operator: is
id: 18c716ea-aac5-4af7-9a50-1fd5dc40d18e
value: guest
varType: string
variable_selector:
- conversation
- LOGIN
id: f599486b-e4d3-4cdd-9425-31257bf28e82
logical_operator: and
- case_id: 5f242bc3-7f06-4a88-82e5-235a859d92bf
conditions:
- comparison_operator: is
id: 490f9251-1012-4f85-8bb7-29f355ca6b5c
value: admin
varType: string
variable_selector:
- conversation
- LOGIN
id: 5f242bc3-7f06-4a88-82e5-235a859d92bf
logical_operator: and
desc: ''
selected: false
title: IF/ELSE
type: if-else
height: 221
id: '1758767750205'
position:
x: 78.91702348010949
y: 535.9787937998216
positionAbsolute:
x: 78.91702348010949
y: 535.9787937998216
selected: false
title: IF/ELSE
type: if-else
height: 221
id: '1758767750205'
position:
x: 78.91702348010949
y: 535.9787937998216
positionAbsolute:
x: 78.91702348010949
y: 535.9787937998216
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
cases:
- case_id: 'true'
conditions:
- comparison_operator: is
id: ab112997-31e2-453c-8511-40adc3006e76
value: AIID
varType: string
variable_selector:
- sys
- query
id: 'true'
logical_operator: and
desc: ''
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
cases:
- case_id: 'true'
conditions:
- comparison_operator: is
id: ab112997-31e2-453c-8511-40adc3006e76
value: AIID
varType: string
variable_selector:
- sys
- query
id: 'true'
logical_operator: and
desc: ''
selected: false
title: IF/ELSE 2
type: if-else
height: 125
id: '1758767920912'
position:
x: 659.9247584894487
y: 459.4548203041172
positionAbsolute:
x: 659.9247584894487
y: 459.4548203041172
selected: false
title: IF/ELSE 2
type: if-else
height: 125
id: '1758767920912'
position:
x: 659.9247584894487
y: 459.4548203041172
positionAbsolute:
x: 659.9247584894487
y: 459.4548203041172
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
desc: ''
items:
- input_type: constant
operation: set
value: admin
variable_selector:
- conversation
- LOGIN
write_mode: over-write
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
desc: ''
items:
- input_type: constant
operation: set
value: admin
variable_selector:
- conversation
- LOGIN
write_mode: over-write
selected: false
title: Variable Assigner
type: assigner
version: '2'
height: 87
id: '1758768026915'
position:
x: 997.3839309072489
y: 426.91916104676926
positionAbsolute:
x: 997.3839309072489
y: 426.91916104676926
selected: false
title: Variable Assigner
type: assigner
version: '2'
height: 87
id: '1758768026915'
position:
x: 997.3839309072489
y: 426.91916104676926
positionAbsolute:
x: 997.3839309072489
y: 426.91916104676926
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
desc: ''
items:
- input_type: constant
operation: set
value: guest
variable_selector:
- conversation
- LOGIN
write_mode: over-write
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
desc: ''
items:
- input_type: constant
operation: set
value: guest
variable_selector:
- conversation
- LOGIN
write_mode: over-write
selected: false
title: Variable Assigner 2
type: assigner
version: '2'
height: 87
id: '1758768059939'
position:
x: 1010.8775065168395
y: 553.9191610467692
positionAbsolute:
x: 1010.8775065168395
y: 553.9191610467692
selected: false
title: Variable Assigner 2
type: assigner
version: '2'
height: 87
id: '1758768059939'
position:
x: 1010.8775065168395
y: 553.9191610467692
positionAbsolute:
x: 1010.8775065168395
y: 553.9191610467692
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
context:
enabled: false
variable_selector: []
desc: ''
model:
completion_params:
temperature: 0.7
mode: chat
name: Qwen3-235B-A22B-Instruct-2507
provider: langgenius/gitee_ai/gitee_ai
prompt_template:
- id: 1129723a-50cb-4350-a118-3b9ac6dac523
role: system
text: 'You are the computer that answers the admin''s question. Always start
with "hello admin" . You know AIID(AI innovative Design) is a master program
in open FIESTA in Shenzhen. '
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
context:
enabled: false
variable_selector: []
desc: ''
model:
completion_params:
temperature: 0.7
mode: chat
name: Qwen3-235B-A22B-Instruct-2507
provider: langgenius/gitee_ai/gitee_ai
prompt_template:
- id: 1129723a-50cb-4350-a118-3b9ac6dac523
role: system
text:
'You are the computer that answers the admin''s question. Always start
with "hello admin" . You know AIID(AI innovative Design) is a master program
in open FIESTA in Shenzhen. '
selected: false
title: Admin LLM
type: llm
variables: []
vision:
enabled: false
height: 89
id: '1758768238460'
position:
x: 389.3839309072489
y: 792.6856819774588
positionAbsolute:
x: 389.3839309072489
y: 792.6856819774588
selected: false
title: Admin LLM
type: llm
variables: []
vision:
enabled: false
height: 89
id: '1758768238460'
position:
x: 389.3839309072489
y: 792.6856819774588
positionAbsolute:
x: 389.3839309072489
y: 792.6856819774588
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
answer: '{{#1758768238460.text#}}'
desc: ''
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
answer: '{{#1758768238460.text#}}'
desc: ''
selected: false
title: Answer 3
type: answer
variables: []
height: 104
id: '1758768309599'
position:
x: 707.3520684153225
y: 832.4705087633828
positionAbsolute:
x: 707.3520684153225
y: 832.4705087633828
selected: false
title: Answer 3
type: answer
variables: []
height: 104
id: '1758768309599'
position:
x: 707.3520684153225
y: 832.4705087633828
positionAbsolute:
x: 707.3520684153225
y: 832.4705087633828
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
answer: Password Correct! You are now log in as ADMIN
desc: ''
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
answer: Password Correct! You are now log in as ADMIN
desc: ''
selected: false
title: Answer 3
type: answer
variables: []
height: 117
id: '1758768400561'
position:
x: 1301.383930907249
y: 426.91916104676926
positionAbsolute:
x: 1301.383930907249
y: 426.91916104676926
selected: false
title: Answer 3
type: answer
variables: []
height: 117
id: '1758768400561'
position:
x: 1301.383930907249
y: 426.91916104676926
positionAbsolute:
x: 1301.383930907249
y: 426.91916104676926
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
answer: Password Incorrect! You are now log in as GUEST
desc: ''
sourcePosition: right
targetPosition: left
type: custom
width: 243
- data:
answer: Password Incorrect! You are now log in as GUEST
desc: ''
selected: false
title: Answer 4
type: answer
variables: []
height: 117
id: '1758768418040'
position:
x: 1498.8808102839819
y: 553.9191610467692
positionAbsolute:
x: 1498.8808102839819
y: 553.9191610467692
selected: false
title: Answer 4
type: answer
variables: []
height: 117
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
sourcePosition: right
targetPosition: left
type: custom
width: 243
viewport:
x: 273.4245102591761
y: -8.654295375493462
@@ -6,45 +6,45 @@ app:
name: Love Loop
use_icon_as_answer_icon: false
dependencies:
- current_identifier: null
type: marketplace
value:
marketplace_plugin_unique_identifier: langgenius/gitee_ai:0.1.4@f621ace33bb3c140f5a1e3533fcb518f558c7b945d63523c0f85810a4b4a8b93
- current_identifier: null
type: marketplace
value:
marketplace_plugin_unique_identifier: langgenius/gitee_ai:0.1.4@f621ace33bb3c140f5a1e3533fcb518f558c7b945d63523c0f85810a4b4a8b93
kind: app
version: 0.3.0
workflow:
conversation_variables:
- description: ''
id: 411d934a-94cb-4899-a892-31300f69228e
name: words
selector:
- conversation
- words
value: love
value_type: string
- description: ''
id: 7aca1d78-2dbc-4ccf-8428-0f0eed7b203c
name: WORDS
selector:
- conversation
- WORDS
value: ''
value_type: string
- description: ''
id: 411d934a-94cb-4899-a892-31300f69228e
name: words
selector:
- conversation
- words
value: love
value_type: string
- description: ''
id: 7aca1d78-2dbc-4ccf-8428-0f0eed7b203c
name: WORDS
selector:
- conversation
- WORDS
value: ''
value_type: string
environment_variables: []
features:
file_upload:
allowed_file_extensions:
- .JPG
- .JPEG
- .PNG
- .GIF
- .WEBP
- .SVG
- .JPG
- .JPEG
- .PNG
- .GIF
- .WEBP
- .SVG
allowed_file_types:
- image
- image
allowed_file_upload_methods:
- local_file
- remote_url
- local_file
- remote_url
enabled: false
fileUploadConfig:
audio_file_size_limit: 50
@@ -57,8 +57,8 @@ workflow:
enabled: false
number_limits: 3
transfer_methods:
- local_file
- remote_url
- local_file
- remote_url
number_limits: 3
opening_statement: ''
retriever_resource:
@@ -76,312 +76,313 @@ workflow:
voice: ''
graph:
edges:
- data:
isInIteration: false
isInLoop: true
loop_id: '1758765136208'
sourceType: loop-start
targetType: llm
id: 1758765136208start-source-1758765344915-target
source: 1758765136208start
sourceHandle: source
target: '1758765344915'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInLoop: false
sourceType: loop
targetType: answer
id: 1758765136208-source-answer-target
source: '1758765136208'
sourceHandle: source
target: answer
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: true
loop_id: '1758765136208'
sourceType: llm
targetType: code
id: 1758765344915-source-1758765883132-target
source: '1758765344915'
sourceHandle: source
target: '1758765883132'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1758765136208'
sourceType: code
targetType: assigner
id: 1758765883132-source-1758765428475-target
source: '1758765883132'
sourceHandle: source
target: '1758765428475'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: false
sourceType: start
targetType: assigner
id: 1758764476473-source-1758765920251-target
source: '1758764476473'
sourceHandle: source
target: '1758765920251'
targetHandle: target
type: custom
zIndex: 0
- data:
isInLoop: false
sourceType: assigner
targetType: loop
id: 1758765920251-source-1758765136208-target
source: '1758765920251'
sourceHandle: source
target: '1758765136208'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: true
loop_id: '1758765136208'
sourceType: loop-start
targetType: llm
id: 1758765136208start-source-1758765344915-target
source: 1758765136208start
sourceHandle: source
target: '1758765344915'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInLoop: false
sourceType: loop
targetType: answer
id: 1758765136208-source-answer-target
source: '1758765136208'
sourceHandle: source
target: answer
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: true
loop_id: '1758765136208'
sourceType: llm
targetType: code
id: 1758765344915-source-1758765883132-target
source: '1758765344915'
sourceHandle: source
target: '1758765883132'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1758765136208'
sourceType: code
targetType: assigner
id: 1758765883132-source-1758765428475-target
source: '1758765883132'
sourceHandle: source
target: '1758765428475'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: false
sourceType: start
targetType: assigner
id: 1758764476473-source-1758765920251-target
source: '1758764476473'
sourceHandle: source
target: '1758765920251'
targetHandle: target
type: custom
zIndex: 0
- data:
isInLoop: false
sourceType: assigner
targetType: loop
id: 1758765920251-source-1758765136208-target
source: '1758765920251'
sourceHandle: source
target: '1758765136208'
targetHandle: target
type: custom
zIndex: 0
nodes:
- data:
desc: ''
- data:
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
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
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: '{{#conversation.words#}}'
desc: ''
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: '{{#conversation.words#}}'
desc: ''
selected: false
title: Answer
type: answer
variables: []
height: 105
id: answer
position:
x: 1456.1539893939312
y: 178.92252604934293
positionAbsolute:
x: 1456.1539893939312
y: 178.92252604934293
selected: false
title: Answer
type: answer
variables: []
height: 105
id: answer
position:
x: 1456.1539893939312
y: 178.92252604934293
positionAbsolute:
x: 1456.1539893939312
y: 178.92252604934293
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
break_conditions: []
desc: ''
error_handle_mode: terminated
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
break_conditions: []
desc: ''
error_handle_mode: terminated
height: 462
logical_operator: and
loop_count: 5
loop_variables: []
selected: false
start_node_id: 1758765136208start
title: Loop
type: loop
width: 682
height: 462
logical_operator: and
loop_count: 5
loop_variables: []
id: '1758765136208'
position:
x: 731.5560922291256
y: 187.5878472722298
positionAbsolute:
x: 731.5560922291256
y: 187.5878472722298
selected: false
start_node_id: 1758765136208start
title: Loop
type: loop
sourcePosition: right
targetPosition: left
type: custom
width: 682
height: 462
id: '1758765136208'
position:
x: 731.5560922291256
y: 187.5878472722298
positionAbsolute:
x: 731.5560922291256
y: 187.5878472722298
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 682
zIndex: 1
- data:
desc: ''
isInLoop: true
zIndex: 1
- data:
desc: ''
isInLoop: true
selected: false
title: ''
type: loop-start
draggable: false
height: 48
id: 1758765136208start
parentId: '1758765136208'
position:
x: 24
y: 68
positionAbsolute:
x: 755.5560922291256
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
title: ''
type: loop-start
draggable: false
height: 48
id: 1758765136208start
parentId: '1758765136208'
position:
x: 24
y: 68
positionAbsolute:
x: 755.5560922291256
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#}}'
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
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'
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
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
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
sourcePosition: right
targetPosition: left
type: custom
width: 244
viewport:
x: 59.39316938104503
y: 131.3127545416781
@@ -56,13 +56,13 @@ API 无处不在,在后台默默工作。
- **Parameters:** `city=London` & `apiKey=your_access_key`
- **“响应”(回传的数据):**
`json
{
"city": "London",
"temperature": "15°C",
"condition": "Cloudy",
"humidity": "70%"
}
`
{
"city": "London",
"temperature": "15°C",
"condition": "Cloudy",
"humidity": "70%"
}
`
应用拿到这些数据后,再把它们漂亮地展示在你的手机屏幕上。
**地图服务 API**
@@ -73,16 +73,16 @@ API 无处不在,在后台默默工作。
- **Parameters:** `origin=Eiffel Tower` & `destination=Louvre Museum` & `mode=driving`
- **“响应”(回传的数据):**
`json
{
"total_distance": "4.5 kilometers",
"estimated_time": "15 minutes",
"route_steps": [
"1. Head east on Champ de Mars...",
"2. Turn left onto Quai Branly...",
"..."
]
}
`
{
"total_distance": "4.5 kilometers",
"estimated_time": "15 minutes",
"route_steps": [
"1. Head east on Champ de Mars...",
"2. Turn left onto Quai Branly...",
"..."
]
}
`
通过这些数据,应用就能在地图上绘制路线并提供导航指令。
**社交媒体登录 API**
@@ -123,14 +123,14 @@ user_info = {
我们也可参考某家云厂商的[数据库选型推荐](https://help.aliyun.com/zh/govcloud/getting-started/select-database-services),根据场景可进行不同数据库类型的选择,你可以对比不同云厂商的数据库规格选出最合适的进行使用。
| 数据库类型 | 数据库名称 | 价格 | 适用场景 |
| ------------ | ---------------- | ---- | -------------------------------------------------------------------------------------------------------------------------------- |
| 关系型数据库 | RDS MySQL版 | 低 | 基础版:学习以及小型网站高可用版:一定业务压力的中型数据库场景集群版:业务不允许中断,访问压力较大 |
| | RDS SQL server版 | 高 | 基础版:测试以及小型商业化网站高可用版:企业级商业化网站集群版:企业业务不允许中断,访问压力较大 |
| | RDS PostgreSQL版 | 最低 | 基础版:学习以及小型网站高可用版:一定业务压力的中型数据库场景集群版:业务不允许中断,访问压力较大的场景,其性能较一般MySQL高 |
| | RDS PPAS版 | 高 | 通用型:兼容Oracle业务,但业务压力Udacity,虚拟化可以满足其需求独享型:面对需要独享物理机的业务,一般为高并发Oracle类业务 |
| | DRDS | 中 | 入门版本:4 Core 8 G,价格亲民,适合中小型在线业务企业版:16 Core 32 G,复杂 SQL 响应好,适合超高并发在线业务至尊版:32 Core 64 G,复杂 SQL 执行响应最好,提供超大规格选择 |
| NoSQL数据库 | Redis | 中 | 双机热备Redis:一般作为持久化数据库提高业务可用性集群版本的Redis:一般作为缓存层,加速应用访问,解决一般数据库无法负载的读取压力 |
| 数据库类型 | 数据库名称 | 价格 | 适用场景 |
| ------------ | ---------------- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 关系型数据库 | RDS MySQL版 | 低 | 基础版:学习以及小型网站高可用版:一定业务压力的中型数据库场景集群版:业务不允许中断,访问压力较大 |
| | RDS SQL server版 | 高 | 基础版:测试以及小型商业化网站高可用版:企业级商业化网站集群版:企业业务不允许中断,访问压力较大 |
| | RDS PostgreSQL版 | 最低 | 基础版:学习以及小型网站高可用版:一定业务压力的中型数据库场景集群版:业务不允许中断,访问压力较大的场景,其性能较一般MySQL高 |
| | RDS PPAS版 | 高 | 通用型:兼容Oracle业务,但业务压力Udacity,虚拟化可以满足其需求独享型:面对需要独享物理机的业务,一般为高并发Oracle类业务 |
| | DRDS | 中 | 入门版本:4 Core 8 G,价格亲民,适合中小型在线业务企业版:16 Core 32 G,复杂 SQL 响应好,适合超高并发在线业务至尊版:32 Core 64 G,复杂 SQL 执行响应最好,提供超大规格选择 |
| NoSQL数据库 | Redis | 中 | 双机热备Redis:一般作为持久化数据库提高业务可用性集群版本的Redis:一般作为缓存层,加速应用访问,解决一般数据库无法负载的读取压力 |
| | MongoDB版 | 中 | 单节点实例单节点:适用于开发、测试及其他非企业核心数据存储的场景副本集实例:适用于某些业务场景下对数据库有更高读取性能需求,如阅读类网站、订单查询系统等读多写少场景或有临时活动等突发业务需求分片集群实例:基于多个副本集(每个副本集沿用三副本模式)组成的分片集群实例,提供更高的读取性能需求,为实时在线业务提供高速读取性能 |
光说不易理解,我们通过一个具体的“博客文章”场景,来看看同样的数据在关系数据库 (SQL) 和不同类型的非关系数据库 (NoSQL) 中是如何存储的。
@@ -1,4 +1,5 @@
# 如何构建一个最简单的微信小程序
# 1. 什么是微信小程序和微信小程序开发
在这篇教程中,我们将完整跑通一条闭环:从脑海中的一个想法,到在微信里可以搜索、可以扫码打开的真实小程序。
@@ -105,7 +106,7 @@
![](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)
@@ -197,7 +198,7 @@ AI 完成第一轮开发之后,代码已经落在项目里了,但这时候
这就是 vibecoding 的优势所在:你不必自己去翻代码,查找事件绑定的位置、计算坐标的逻辑,而是直接把想法告诉 AI。例如,你可以在 Trae 的对话框里这样描述:
把按键换成摇杆控制,并且用户松开摇杆时蛇保持同方向移动,直到用户再次松开摇杆。
把按键换成摇杆控制,并且用户松开摇杆时蛇保持同方向移动,直到用户再次松开摇杆。
只要你把需求讲得足够清楚,AI 会自动定位到对应界面和逻辑文件,替你完成控件样式、交互绑定和方向处理等改动。
@@ -211,13 +212,13 @@ AI 完成第一轮开发之后,代码已经落在项目里了,但这时候
AI 生成的版本不一定一开始就完美。有时候你会遇到这些情况:
* 运行时报错,小程序无法正常打开;
* 功能大致正确,但细节和你想象的不太一样;
* 界面可以用,但你觉得还可以更好看或更顺手。
- 运行时报错,小程序无法正常打开;
- 功能大致正确,但细节和你想象的不太一样;
- 界面可以用,但你觉得还可以更好看或更顺手。
在这些时候,不需要自己钻进代码里盲改,而是可以把遇到的问题直接用自然语言重新描述给 Trae 中的 AI 助手,例如:
现在摇杆控制已经生效了,但有时候蛇会突然停止不动,请帮我检查当前实现哪里有问题。 或者: 现在游戏可以玩,但界面有点拥挤,我希望在手机上显示时上下留出更多空白。请你帮我调整布局。
现在摇杆控制已经生效了,但有时候蛇会突然停止不动,请帮我检查当前实现哪里有问题。 或者: 现在游戏可以玩,但界面有点拥挤,我希望在手机上显示时上下留出更多空白。请你帮我调整布局。
AI 会根据你当前的项目状态和描述,给出修改建议并直接应用在代码里。如果修改之后结果更糟或方向不对,你依然可以使用回退,把工程恢复到前一个稳定版本,再换一种说法尝试。
@@ -229,10 +230,10 @@ AI 会根据你当前的项目状态和描述,给出修改建议并直接应
经过一轮又一轮的 **自然语言叙述 → AI 修改 → 在微信开发者工具中预览 → 继续对话微调** ,我最终得到的是这样一个成品:
* 有完整的游戏页面;
* 蛇可以顺畅移动并吃到食物;
* 支持摇杆控制;
* 在小程序模拟器中可以顺利运行。
- 有完整的游戏页面;
- 蛇可以顺畅移动并吃到食物;
- 支持摇杆控制;
- 在小程序模拟器中可以顺利运行。
最终开发成品如下: