【vue3】孩子一岁半了,压力很大,给她开发一个学习平台吧,持续更新
本帖最后由 完成大我 于 2023-2-2 18:27 编辑1.项目背景楼主回老家的国企工作之后一直很无聊,一周的工作2天做完,写ppt比写代码多,再这样下去就废了。孩子一岁半了,还有一年半就上幼儿园了,给她开发一个学习平台吧,顺便学学前端技术,楼主是理科生,比较注重理科的学习,学习平台优先侧重于理科需求,当然后面会根据实际需求去不断完善平台。
2.需求
| 需求编号 | 需求名称 | 需求描述 | 需求备注 |
| -------- | -------- | ------------------------------------------------------------ | -------- |
| 1 | 算术题 | 支持加减乘除及混合运算的自动生成,及批改;<br />支持整数、小数、分数等各种类型数字的生成条件;<br />支持数字范围、多参运算的生成条件。 | |
| 2 | 应用题 | 常见题型概念、分析、样例;<br />应用题自动生成及批改; | |
| 3 | 公式大全 | 常见公式的增删改查;<br />便于常用公式的快速检索,按名称、分类、内容等条件模糊搜索; | |
| 4 | 题库管理 | 非典型题库的增删改查;<br />也用于自动生成非标准题目的随机抽取; | |
| 5 | 知识库 | 包含小学数学课程的全部知识点;<br />支持按年级、标题(模糊)、分类、内容(模糊)搜索。 | |
| 6 | 待规划 | .... | |
3.技术方案
前端:vue3.0+typeScript+ElentmentPlus+vatex
楼主是后端工程师,不太懂前端,选择主流易上手的vue,typeScript的语法接近后端也很容易上手,ElentmentUI是开源的组件库,丰富全面美观,开箱即用,vatex是公式组件,用于页面上更好的展示公式。
本文对于通用功能,比如系统登录,前端动态路由等功能实现不做赘述。只专注于需求核心代码的介绍。
后端:SpringBoot+mybatisPlus+poi-tl
考虑到部署问题,楼主没太多钱买服务器,能用前端实现的功能就用前端实现,后端主要用于必要数据库的增删改查操作,还有一些前端不易实现的功能,比如office文档操作,根据模板生成文档等功能。
4.具体实现
4.1 应用题
页面效果
每个问题类型对应一个子页面组件,使用<component :is="currentName"></component>组件,根据左侧问题类型选择树选项动态的加载组件。
应用题模块页面代码:
<template>
<div style="float:left;">
<dir><el-tree ref="treeRef" default-expand-all :data="typeTree" :props="props" :height="800"
@node-click="handleNodeClick" /></dir>
</div>
<div style="float:left;">
<component :is="currentName"></component>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from 'vue'
import { questionTypeTreeData } from '@/type/treeData'
import { ElTree } from 'element-plus'
import ChickenRabbitCage from '@/components/wordproblem/ChickenRabbitCage.vue'
import Unification from "@/components/wordproblem/Unification.vue"
export default defineComponent({
components: { ChickenRabbitCage, Unification},
setup() {
const treeRef = ref<InstanceType<typeof ElTree>>()
const typeTree = reactive(questionTypeTreeData)
const props = reactive({
value: 'value',
label: 'label',
children: 'children',
})
const currentName = ref('')
const handleNodeClick = () => {
currentName.value = treeRef.value!.getCurrentNode().value
console.log(currentName.value);
}
return {
typeTree,
props,
handleNodeClick,
treeRef,
currentName
}
}
})
</script>
<style lang="scss" scoped>
.el-tree {
width: 200px;
}
</style>
归一问题子页面代码:
<template>
<h2>归一问题</h2>
<div>
<div>
<el-card class="box-card">
<template #header>
<div class="card-header">
<h3>问题背景</h3>
</div>
</template>
<p> 归一问题是指根据已知条件,在解题时要先求出一份是多少(归一,单一量),如单位时间内的工作量,单位面积的产量、商品的单价、单位时间内所行的路程等,
然后再求出所求问题的应用题叫归一问题。归一问题分为正归一问题和反归一问题。
</p>
<p>
归一问题可分为正归一问题和反归一问题。正归一是指,已知某物数量和总量(比如总价),求新的数量对应的总量是多少。反归一问题是指,已知某物数量和总量,
求新的总价对应的某物数量是多少。
</p>
</el-card>
</div>
<div>
<el-card class="box-card">
<template #header>
<div class="card-header">
<h3>解题思路</h3>
</div>
</template>
<h4>正归一问题</h4>
<p>总量/数量=单一量</p>
<p>单一量*新的数量=新的总量</p>
<p>综合式:总量/数量*新的数量=新的总量</p>
<h4>反归一问题</h4>
<p>总量/数量=单一量</p>
<p>新的总量/单一量=新的数量</p>
<p>综合式:新的总量/(总量/数量)=新的数量</p>
<h4>问题变种</h4>
<p>数量可以是两个参数甚至更多的参数,比如数量参数有n个,有一组已知数量参数及对应的总量,求新的一组参数对应的总量;
或有一组已知数量参数及对应的总量,求新的总量,及新的一组(n-1)个参数,求未知的那个参数。</p>
</el-card>
</div>
<div>
<el-card class="box-card">
<template #header>
<div class="card-header">
<h3>例题解答</h3>
</div>
</template>
<p>学校买6个同样的篮球共用468元,照这样计算,1014元可以买多少个这样的篮球?</p>
<p>解:</p>
<p>(1)这是典型的反归一问题,单一量 = 468÷6 = 78(元/个) </p>
<p>(2)用新的总量求新的数量,新的数量 = 1014 ÷ 78 = 13(个)</p>
<p>答:1014元可以买13个这样的篮球</p>
</el-card>
</div>
<!-- 问题自动生成 -->
<div class="d-button" @click="drawerStatus = true">
<el-button>问题生成</el-button>
</div>
<div>
<el-drawer v-model="drawerStatus" @close="closeDrawer" size="40%">
<template #header>
<h4>归一问题自动生成</h4>
</template>
<template #default>
<div>
<el-button color="green" @click="generateAQuestion" class="d-button">一键生成</el-button>
</div>
<!-- 问题展示 -->
<div>
<el-card v-show="answerStatus">
<p>
{{ questionData.problem }}
</p>
</el-card>
</div>
<!-- 解答区 -->
<div>
<el-card v-show="answerStatus">
<template #header>
<div class="card-header">
<h4>题解</h4>
</div>
</template>
<p>单一量为:{{ questionData.totalKnown }} ÷ {{ questionData.numberKnown }} =
<span>
<el-input-number v-model="userAnswer.singleValue" class="mx-4" />
</span>
{{ questionData.totalUnit }}/{{ questionData.numberUnit }};
</p>
<p v-if="questionData.type == '1'">
<span>
<span>
则新的数量{{ questionData.numberNew }}{{questionData.numberUnit}}对应的新的总量是: {{ questionData.numberNew }} ×
{{ userAnswer.singleValue }} =
</span>
<el-input-number v-model="userAnswer.userTotalNew" class="mx-4" />
{{ questionData.totalUnit }}
</span>
</p>
<p v-if="questionData.type == '2'">
<span>
<span>
则新的总量{{ questionData.totalNew }}{{ questionData.totalUnit }}对应的数量是:{{ questionData.totalNew }} ÷
{{ userAnswer.singleValue }} =
</span>
<el-input-number v-model="userAnswer.userNumberNew" class="mx-4" />
{{questionData.numberUnit}}
</span>
</p>
<p v-if = "questionData.type == '1'">
{{
questionData.answerTemplate.replace("totalNew", String(userAnswer.userTotalNew)).replace("numberNew", String(questionData.numberNew))
}}
</p>
<p v-else>
{{
questionData.answerTemplate.replace("numberNew", String(userAnswer.userNumberNew)).replace("totalNew", String(questionData.totalNew))
}}
</p>
</el-card>
<!-- 批改区 -->
<div v-show="answerStatus">
<div style="float:right;">
<el-button @click="correct" class=".d-button">批改</el-button>
</div>
</div>
<div v-show="correctStatus">
<el-card>
<div>
<el-tag class="ml-2" type="success" v-if="isCorrect">正确</el-tag>
<el-tag class="ml-2" type="warning" v-else>错误</el-tag>
</div>
<div>
<p>{{ questionData.answer }}</p>
</div>
</el-card>
</div>
</div>
</template>
</el-drawer>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue'
import { UnificationQuestionGenerator } from '@/function/wordQuestionGenP1'
export default defineComponent({
setup() {
const data = reactive({
drawerStatus: false,
answerStatus: false,
correctStatus: false,
isCorrect: false,
questionData: {
single: 0,
numberKnown: 0,
totalKnown: 0,
numberNew: 0,
totalNew: 0,
problem: "",
answer: "",
type: "",
prolemTemplate: "",
answerTemplate: "",
numberUnit: "",
totalUnit: ""
},
userAnswer: {
singleValue: 0,
userNumberNew: 0,
userTotalNew: 0
}
})
const closeDrawer = () => {
data.drawerStatus = false
data.answerStatus = false
data.correctStatus = false
data.isCorrect = false
}
const generateAQuestion = () => {
data.correctStatus = false
data.isCorrect = false
// 用户答案初始化
data.userAnswer ={
singleValue: 0,
userNumberNew: 0,
userTotalNew: 0
}
data.questionData = UnificationQuestionGenerator()
data.answerStatus = true
}
// 批改函数
const correct = () => {
data.correctStatus= true
if(data.questionData.type == '1'){
if(data.questionData.totalNew == data.userAnswer.userTotalNew) data.isCorrect = true
}
else {
if(data.questionData.numberNew == data.userAnswer.userNumberNew) data.isCorrect = true
}
}
return {
...toRefs(data),
closeDrawer,
generateAQuestion,
correct
}
}
})
</script>
<style scoped>
.box-card {
width: 1200px;
}
h2 {
color: rgb(78, 77, 77);
}
h3 {
color: burlywood;
}
p {
text-indent: 25px
}
</style>
核心ts函数,问题的自动生成:
//归一问题
export interface UnificationQuestionData {
single: number
numberKnown: number
totalKnown: number
numberNew: number
totalNew: number
problem: string
answer: string
type: string
prolemTemplate: string
answerTemplate: string
numberUnit: string
totalUnit: string
}
interface Problem {
problem: string
answer: string
type: string
magnitude: number
numberUnit: string
totalUnit: string
}
const UnificationProblems: Array<Problem> = [
{
'problem': "一列火车numberKnown小时行驶totalKnown千米,照这样算,numberNew小时行驶多少千米?",
'answer': "答:numberNew小时行驶totalNew千米",
'type': '1',
'numberUnit': '小时',
'totalUnit': '公里',
'magnitude': 300
},
{
'problem': "学校买numberKnown个同样的篮球共用totalKnown元,照这样计算,totalNew元可以买多少个这样的篮球?",
'answer': "答:totalNew元可以买numberNew个这样的篮球",
'type': '2',
'numberUnit': '个',
'totalUnit': '元',
'magnitude': 100
},
{
'problem': "小红骑车numberKnown分钟骑行totalKnown米,照这样的速度她从家到学校骑行了numberNew分钟,小红家到学校有多少米?",
'answer': "答:小红家到学校有totalNew米",
'type': '1',
'numberUnit': '分钟',
'totalUnit': '米',
'magnitude': 500
},
{
'problem': "一只小蜗牛numberKnown分钟爬行totalKnown分米,照这样的速度,numberNew分钟爬行多少分米?",
'answer': "答:numberNew分钟爬行了totalNew分米",
'type': '1',
'numberUnit': '分钟',
'totalUnit': '分米',
'magnitude': 10
},
{
'problem': "一艘轮船numberKnown小时航行totalKnown千米,照这样的速度,继续航行totalNew千米,共需多少小时?",
'answer': "答:继续航行totalNew千米,共需numberNew小时",
'type': '2',
'numberUnit': '小时',
'totalUnit': '千米',
'magnitude': 50
}
]
export const UnificationQuestionGenerator = () => {
const result: UnificationQuestionData = {
single: 0,
numberKnown: 0,
totalKnown: 0,
numberNew: 0,
totalNew: 0,
problem: "",
answer: "",
type: "",
prolemTemplate: "",
answerTemplate: "",
numberUnit: "",
totalUnit: ""
}
const index = random(0, UnificationProblems.length - 1)
result.prolemTemplate = UnificationProblems.problem
result.answerTemplate = UnificationProblems.answer
result.numberUnit = UnificationProblems.numberUnit
result.totalUnit = UnificationProblems.totalUnit
result.type = UnificationProblems.type
result.single = random(Math.ceil(UnificationProblems.magnitude / 5), UnificationProblems.magnitude)
result.numberKnown = random(2, 10)
result.totalKnown = result.single * result.numberKnown
result.numberNew = random(11, 20)
result.totalNew = result.single * result.numberNew
result.problem = result.prolemTemplate.replace('numberKnown', String(result.numberKnown))
result.problem = result.problem.replace('totalKnown', String(result.totalKnown))
result.problem = result.problem.replace('numberNew', String(result.numberNew))
result.problem = result.problem.replace('totalNew', String(result.totalNew))
result.answer = result.answerTemplate.replace("numberNew", String(result.numberNew))
result.answer = result.answer.replace('totalNew', String(result.totalNew))
return result
}
其中UnificationQuestionData 是定义了归一问题,问题模型的数据格式,即题目中涉及到的所有字段;
Problem是定义了问题模板库的数据格式,问题模板字段包括,problem--问题模板,answer答案模板,type问题类型(区分正归一,反归一),numberUnit数量单位,totalUnit总量单位,magnitude数据量级,用于生成限定合理的随机数范围;
UnificationProblems是问题模板库,生成问题时,从UnificationProblems里随机抽取一条作为模板。
生成的过程为:
[*]数据初始化
[*]随机抽取一个模板
[*]根据magnitude生成随机数,更新返回数据
[*]将问题模板、答案模板内的标志符替换为生成的随机数
[*]返回生成的问题数据
页面效果:
今天就到这里,持续码代码中,后面有时间更新其他章节。
https://static.52pojie.cn/static/image/hrline/4.gif
典型的应用题型知识点,题目自动生成及批改,实现的思路都与归一问题实现类似,小学节点典型的问题有三十种,所有应用题都可以抽象为这三十种的一种。目前完成归一问题、归总问题、鸡兔同笼问题三个子组件的开发,其他题型会持续完善。
4.2.算术题自动生成
算术题的自动生成及批改功能,包括生成条件栏、问题列表、批改按钮及分数展示三部分。生成条件包括问题类型、题目数量、数字范围;问题列表展示问题,作答区,及批改结果;批改后批改按钮下侧会自动展示分数。
代码:
<template>
<div>
<div class="filter-condition">
<el-form :inline="true" ref="ruleFormRef" status-icon :model="filterdata" :rules="rules"
class="demo-form-inline">
<el-form-item label="问题类型" prop="type" class="inputitem">
<el-select v-model="filterdata.type" placeholder="问题类型">
<el-option label="加法" value="+" />
<el-option label="减法" value="-" />
<el-option label="加减混合" value="+-" />
<el-option label="乘法" value="×" />
<el-option label="整数除法" value="÷" />
<el-option label="分数" value="/" />
</el-select>
</el-form-item>
<el-form-item label="题目数量" prop="quantity" class="inputitem">
<el-select v-model="filterdata.quantity" placeholder="题目数量">
<el-option label="20" value="20" />
<el-option label="40" value="40" />
<el-option label="60" value="60" />
</el-select>
</el-form-item>
<el-form-item label="数字范围" prop="numberRange" class="inputitem">
<el-select v-model="filterdata.numberRange" placeholder="问题范围">
<el-option label="0~9" value="0,9" />
<el-option label="0~19" value="0,19" />
<el-option label="0~29" value="0,29" />
<el-option label="0~39" value="0,39" />
<el-option label="0~49" value="0,49" />
<el-option label="0~59" value="0,59" />
<el-option label="0~69" value="0,69" />
<el-option label="0~79" value="0,79" />
<el-option label="0~89" value="0,89" />
<el-option label="0~99" value="0,99" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="genQuestions(ruleFormRef)">生成</el-button>
</el-form-item>
<el-form-item>
<!-- <el-button type="primary" @click="exportQuestion">导出</el-button> -->
</el-form-item>
</el-form>
</div>
<div class="question">
<div style="float:left;">
<el-table ref="tableRef" row-key="date" :data="generatedData.slice(0, filterdata.quantity / 2)"
style="width: 100%" :header-cell-style="{ 'background-color': '#F1F4FF', 'text-align': 'center' }">
<!-- <el-table-column prop="content" label="题目" width="240" /> -->
<el-table-column label="题目" width="240">
<template #default="scope">
<vue-latex :expression="scope.row.content" display-mode />
</template>
</el-table-column>
<el-table-column label="答案" width="180">
<template #default="scope">
<el-input v-model="scope.row.solve" size="small" placeholder="请输入答案" />
</template>
</el-table-column>
<el-table-column label="是否正确" width="180">
<template #default="scope">
<div v-if="isClickCorrectButton">
<el-tag key="正确" class="mx-1" v-if="scope.row.isCorrect" closable
type="success">正确</el-tag>
<el-tag key="错误" class="mx-1" v-else closable type="warning">错误</el-tag>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div style="float:left;">
<el-table ref="tableRef" row-key="date"
:data="generatedData.slice(filterdata.quantity / 2, filterdata.quantity)" style="width: 100%"
:header-cell-style="{ 'background-color': '#F1F4FF', 'text-align': 'center' }">
<el-table-column label="题目" width="180">
<template #default="scope">
<vue-latex :expression="scope.row.content" display-mode />
</template>
</el-table-column>
<el-table-column label="答案" width="240">
<template #default="scope">
<el-input v-model="scope.row.solve" size="small" placeholder="请输入答案" />
</template>
</el-table-column>
<el-table-column label="是否正确" width="180">
<template #default="scope">
<div v-if="isClickCorrectButton">
<el-tag key="正确" class="mx-1" v-if="scope.row.isCorrect" closable
type="success">正确</el-tag>
<el-tag key="错误" class="mx-1" v-else closable type="warning">错误</el-tag>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div style="float:left;" class="correct">
<el-button @click="correctCal" color="green">批改</el-button>
<div>
<el-rate v-if="scoredata.ismark" v-model="scoredata.score" show-score text-color="#ff9900" disabled
allow-half="true" size="large" :score-template="scoredata.score * 20 + '分'"
:colors="['#CC3300', '#FFFF99', '#66FF33']" />
</div>
</div>
</div>
<div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, ref } from 'vue'
import { GenData, FilterConditionInterface } from '@/type/questionGener'
import { QuestionInterface } from '@/type/questionQueryer'
import type { FormInstance } from 'element-plus'
import { random } from '@/function/random'
import { deepClone } from '@/function/deepClone'
import { divisors, maxdivisor } from '@/function/divisors'
export default defineComponent({
setup() {
const ruleFormRef = ref<FormInstance>()
const data = reactive(new GenData())
let isClickCorrectButton = ref(false)
let scoredata = reactive({
score: 0,
ismark: false
})
const rules = {
type: [
{
required: true,
message: "请选择问题类型",
trigger: "blur",
}
],
quantity: [
{
required: true,
message: "请输入问题数量",
trigger: "blur",
}
],
numberRange: [
{
required: true,
message: "请选择数字范围",
trigger: "blur",
}
]
}
// 生成一个问题
const generateAQuestion = (condition: FilterConditionInterface) => {
const question: QuestionInterface = {
title: "",
type: "",
content: "",
answer: "",
}
let num1 = random(condition.numberRange, condition.numberRange)
const num2 = random(condition.numberRange, condition.numberRange)
let num3, num4, div, max_divisor
switch (condition.type) {
case "+":
question.content = num1 + "+" + num2;
question.answer = num1 + num2
break;
case "-":
num3 = random(condition.numberRange, num1)
question.content = num1 + "-" + num3;
question.answer = num1 - num3
break;
case "×":
question.content = num1 + "×" + num2;
question.answer = num1 * num2
break;
case "÷":
num1 = random(1, condition.numberRange)
div = divisors(num1)
div.push(1)
div.push(num1)
console.log(div);
num3 = div
question.content = num1 + "÷" + num3;
question.answer = num1 / num3
break;
case "/+":
num3 = random(1, condition.numberRange)
num4 = random(1, condition.numberRange)
question.content = '\\frac{' + num1 + '}{' + num3 + '}' + '+' + '\\frac{' + num2 + '}{' + num4 + '}'
max_divisor = maxdivisor(num1 * num4 + num2 * num3, num3 * num4)
question.answer = (num1 * num4 + num2 * num3) / max_divisor + '/' + (num3 * num4) / max_divisor
break
case "/-":
num3 = random(1, condition.numberRange)
num4 = random(1, condition.numberRange)
if (num1 * num4 > num2 * num3) {
question.content = '\\frac{' + num1 + '}{' + num3 + '}' + '-' + '\\frac{' + num2 + '}{' + num4 + '}'
max_divisor = maxdivisor(num1 * num4 + num2 * num3, num3 * num4)
question.answer = (num1 * num4 - num2 * num3) / max_divisor + '/' + (num3 * num4) / max_divisor
}
else {
question.content = '\\frac{' + num1 + '}{' + num3 + '}' + '-' + '\\frac{' + num2 + '}{' + num4 + '}'
max_divisor = maxdivisor(num1 * num4 + num2 * num3, num3 * num4)
question.answer = (num2 * num3 - num1 * num4) / max_divisor + '/' + (num3 * num4) / max_divisor
}
break
default:
break
}
return question
}
//问题生成入口
const genQuestions = (formEl: FormInstance | undefined) => {
//清除判题展示
isClickCorrectButton.value = false
scoredata.ismark = false
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
//问题生成
const operator = ['+', "-", "/+", "/-"]
let filter = deepClone(data.filterdata)
data.generatedData = []
filter.numberRange = data.filterdata.numberRange.split(',') as Array<number>
for (var _i = 0; _i < data.filterdata.quantity; _i++) {
//混合运算处理
if (data.filterdata.type == '+-') {
filter.type = operator
}
if (data.filterdata.type == '/') {
filter.type = operator
}
data.generatedData.push(generateAQuestion(filter))
}
} else {
console.log('error submit!')
return false
}
})
}
//判题函数
const correctCal = () => {
isClickCorrectButton.value = true
let correct_count = 0
data.generatedData.forEach((val, idx, array) => {
if (val.answer == (val.solve as number)) {
val.isCorrect = true
correct_count++
}
else {
val.isCorrect = false
}
});
scoredata.ismark = true
scoredata.score = 5 * correct_count / data.filterdata.quantity
}
return {
ruleFormRef,
...toRefs(data),
genQuestions,
correctCal,
isClickCorrectButton,
rules,
scoredata
}
}
})
</script>
<style scoped>
el-result {
height: 20px;
}
.correct {
margin-left: 20px;
margin-right: 20px;
}
</style>
页面效果:
欢迎大家积极提出好的建议与点子,有时间楼主再更新
https://static.52pojie.cn/static/image/hrline/2.gif
https://static.52pojie.cn/static/image/hrline/2.gif
为了方便观看,新开贴,古诗词模块更新:【vue3】给女儿开发的学习平台之古诗词篇~服务部署成功
(出处: 吾爱破解论坛)
5.服务地址
楼主花了70块买了个垃圾云服务器,卡的呀批,终于把服务部署起来了,楼主都是利用工作之余写一下,目前比较简陋,还望海涵;
爬取的视频数据,可能因为关键词不准,有些古诗的视频对不上号,后面楼主会优化关键词重新爬取,目前只有唐诗300首,古诗也会继续丰富
上地址http://1.15.179.113/
自己的体会,太早的提前教学会毁了孩子!别听什么“别输在起跑线上“,实际上有这样想法的人已经输了!!!!!! 才一岁半啊,你怎么下得去手的啊!:rggrg 孩子:你问过我意见啦吗{:301_985:} 工具很好。
=======================
小娃娃,多动手搭搭积木
可以看娃娃书,不要教其他内容
特别不要去练写字
要培养孩子阅读的习惯
这个家长要花大量的时间 才一岁半啊!你就做个人吧,孩子能开开心心的童年没几年 楼主的逻辑是,孩子压力很大,所以给他多做点题不让他想太多吗? 楼主,我不懂技术,但是从你这个图来看,感觉你是在给大学生做平台。小孩子学东西完全是靠兴趣,因此,很多奖励之类的评价就很关键了。在玩中学,在学中玩。说的就是这了。 孩子:哒 口羊 期待成品 {:1_921:}}666 楼主真有远见,我都是随遇而安 有很多早教系统