feat: 闯关页场景/拼音/朗读/互动分发集成,学堂弹层注音朗读
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,7 @@
|
||||
|
||||
<script>
|
||||
import { api } from '../../api/index.js'
|
||||
import { setChildId } from '../../store/user.js'
|
||||
import { setChildId, setChildAge } from '../../store/user.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -70,6 +70,7 @@ export default {
|
||||
},
|
||||
enter(c) {
|
||||
setChildId(c.child_id)
|
||||
setChildAge(c.age_group)
|
||||
uni.reLaunch({ url: '/pages/map/map' })
|
||||
},
|
||||
async create() {
|
||||
@@ -83,6 +84,7 @@ export default {
|
||||
age_group: this.form.ageGroup
|
||||
})
|
||||
setChildId(data.child_id)
|
||||
setChildAge(this.form.ageGroup)
|
||||
uni.reLaunch({ url: '/pages/map/map' })
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
<view v-if="showLearn && strategy" class="mask" @click="closeLearn">
|
||||
<view class="learn-panel card" @click.stop>
|
||||
<view class="learn-title">{{ strategy.name }} <text class="learn-pinyin">{{ strategy.pinyin }}</text></view>
|
||||
<view class="learn-meaning">{{ strategy.meaning }}</view>
|
||||
<view v-if="strategy.teach_audio" class="audio-btn" @click="playAudio(strategy.teach_audio)">🔊 听一听</view>
|
||||
<view class="learn-content">{{ strategy.teach_content }}</view>
|
||||
<view class="learn-meaning"><RubyText :text="strategy.meaning" :pinyin="strategy.meaning_pinyin" :show="showPinyin" /></view>
|
||||
<view class="learn-content"><RubyText :text="strategy.teach_content" :pinyin="strategy.teach_content_pinyin" :show="showPinyin" /></view>
|
||||
<SpeakButton :text="strategy.teach_content" :file="strategy.teach_audio" label="读一读学堂" />
|
||||
|
||||
<view v-if="strategy.levels && strategy.levels.length" class="level-list">
|
||||
<view
|
||||
@@ -56,7 +56,9 @@
|
||||
|
||||
<script>
|
||||
import { api } from '../../api/index.js'
|
||||
import { getChildId } from '../../store/user.js'
|
||||
import { getChildId, getChildAge } from '../../store/user.js'
|
||||
import RubyText from '../../components/RubyText.vue'
|
||||
import SpeakButton from '../../components/SpeakButton.vue'
|
||||
|
||||
const GROUP_DESC = {
|
||||
1: '胜利要用巧办法',
|
||||
@@ -68,12 +70,14 @@ const GROUP_DESC = {
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { RubyText, SpeakButton },
|
||||
data() {
|
||||
return {
|
||||
groups: [],
|
||||
showLearn: false,
|
||||
strategy: null,
|
||||
childId: getChildId()
|
||||
childId: getChildId(),
|
||||
showPinyin: getChildAge() === '4-6'
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
@@ -121,9 +125,6 @@ export default {
|
||||
closeLearn() {
|
||||
this.showLearn = false
|
||||
},
|
||||
playAudio(src) {
|
||||
uni.showToast({ title: '音频资源暂未配置', icon: 'none' })
|
||||
},
|
||||
enterLevel(lv) {
|
||||
if (!lv.unlocked) {
|
||||
uni.showToast({ title: '关卡未解锁', icon: 'none' })
|
||||
|
||||
+168
-176
@@ -3,44 +3,93 @@
|
||||
<view class="topbar">
|
||||
<view class="back" @click="goBack">‹</view>
|
||||
<view class="top-title">{{ detail ? detail.title : '' }}</view>
|
||||
<view class="top-spacer"></view>
|
||||
<view class="top-actions">
|
||||
<view class="py-toggle" @click="showPinyin = !showPinyin">{{ showPinyin ? '拼音开' : '拼音关' }}</view>
|
||||
<view class="sound-toggle" @click="toggleSound">{{ soundOn ? '🔊' : '🔇' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="detail" class="content">
|
||||
<!-- 进度点:每走一步点亮一个 -->
|
||||
<view class="progress-dots">
|
||||
<view v-for="i in 6" :key="i" class="dot" :class="{ on: i <= stepCount }"></view>
|
||||
</view>
|
||||
|
||||
<!-- 场景面板:环境 + 情境描述 + 朗读 + 漂浮装饰 -->
|
||||
<view class="scene-card card" :style="{ background: scene.color + '44' }">
|
||||
<image v-if="sceneImg" class="scene-img" :src="sceneImg" mode="aspectFill" />
|
||||
<view class="scene-float f1">☁️</view>
|
||||
<view class="scene-float f2">⭐</view>
|
||||
<view class="scene-head">
|
||||
<view class="scene-emoji">{{ scene.emoji }}</view>
|
||||
<view class="scene-name">{{ detail.scene ? detail.scene.name : '' }}</view>
|
||||
<view v-if="combo > 1" class="combo-badge">🔥 ×{{ combo }}</view>
|
||||
</view>
|
||||
<RubyText class="scene-text" :text="detail.scene_content" :pinyin="detail.scene_content_pinyin" :show="showPinyin" />
|
||||
<SpeakButton class="scene-speak" :text="detail.scene_content" :file="detail.scene_audio" label="读一读情境" />
|
||||
</view>
|
||||
|
||||
<!-- 节点:互动分发 -->
|
||||
<view v-if="curNode" class="node card">
|
||||
<view class="node-title">{{ curNode.title }}</view>
|
||||
<view v-if="curNode.character" class="character">
|
||||
<view class="char-avatar">{{ curNode.character.name.slice(0, 1) }}</view>
|
||||
<view class="char-name">{{ curNode.character.name }}</view>
|
||||
<view class="char-card">
|
||||
<image v-if="charImg" class="char-img" :src="charImg" mode="aspectFit" />
|
||||
<view v-else class="char-avatar" :style="{ background: personColor(curNode.character.id) }">{{ curNode.character.name.slice(0, 1) }}</view>
|
||||
<view class="char-mood">{{ mood }}</view>
|
||||
</view>
|
||||
<view class="char-info">
|
||||
<view class="char-name">{{ curNode.character.name }}</view>
|
||||
<SpeakButton :text="curNode.character.name" :file="curNode.character.audio" label="读一读" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="node-content">{{ curNode.content }}</view>
|
||||
<view class="node-content">
|
||||
<RubyText :text="curNode.content" :pinyin="curNode.content_pinyin" :show="showPinyin" />
|
||||
</view>
|
||||
<SpeakButton class="node-speak" :text="curNode.content" :file="curNode.audio" label="读一读" />
|
||||
|
||||
<view v-if="curNode.interaction_type === 1" class="options">
|
||||
<!-- 互动组件(触控 5 种) -->
|
||||
<PropPick v-if="curNode.interaction_type === 2" :config="curNode.config" @done="interactionDone" />
|
||||
<FindSpot v-else-if="curNode.interaction_type === 7" :config="curNode.config" @done="interactionDone" />
|
||||
<DragPlace v-else-if="curNode.interaction_type === 5" :config="curNode.config" @done="interactionDone" />
|
||||
<StepSort v-else-if="curNode.interaction_type === 3" :config="curNode.config" @done="interactionDone" />
|
||||
<LinkMatch v-else-if="curNode.interaction_type === 8" :config="curNode.config" @done="interactionDone" />
|
||||
|
||||
<!-- 选项选择(type 1) -->
|
||||
<view v-else-if="curNode.interaction_type === 1" class="options">
|
||||
<view
|
||||
v-for="(o, idx) in curNode.options"
|
||||
:key="o.option_id"
|
||||
class="option"
|
||||
:class="{ disabled: choosing }"
|
||||
:class="['c' + (idx + 1), { disabled: choosing }]"
|
||||
@click="choose(o, idx)"
|
||||
>
|
||||
<view class="option-mark">{{ ['A', 'B', 'C', 'D'][idx] }}</view>
|
||||
<view class="option-text">{{ o.text }}</view>
|
||||
<view v-if="o.prop" class="option-icon">
|
||||
<image v-if="o.prop.image" class="option-icon-img" :src="o.prop.image" mode="aspectFit" />
|
||||
<text v-else class="option-icon-emoji">{{ propEmoji(o.prop.name) }}</text>
|
||||
</view>
|
||||
<view class="option-text">
|
||||
<RubyText :text="o.text" :pinyin="o.text_pinyin" :show="showPinyin" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="soon card">
|
||||
该互动玩法(类型 {{ curNode.interaction_type }})敬请期待
|
||||
</view>
|
||||
<view v-else class="soon card">该互动玩法(类型 {{ curNode.interaction_type }})敬请期待</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 反馈面板:你的选择 + 好处 + 对比(对比点评三段式) -->
|
||||
<view v-if="feedback" class="mask">
|
||||
<view class="feedback card">
|
||||
<view class="fb-title">你的选择</view>
|
||||
<view class="fb-option">{{ feedback.text }}</view>
|
||||
<view class="fb-option"><RubyText :text="feedback.text" :pinyin="feedback.textPinyin" :show="showPinyin" /></view>
|
||||
<view v-if="feedback.pros" class="fb-line fb-pros fb-in"><text class="fb-mark">✓</text>{{ feedback.pros }}</view>
|
||||
<view v-if="feedback.cons" class="fb-line fb-cons fb-in d2"><text class="fb-mark">✗</text>{{ feedback.cons }}</view>
|
||||
<view v-if="feedback.prop" class="fb-prop">
|
||||
<view class="fb-prop-name">{{ feedback.prop.name }}</view>
|
||||
<view class="fb-prop-desc">{{ feedback.prop.description }}</view>
|
||||
<view v-if="feedback.prop.description" class="fb-prop-desc">{{ feedback.prop.description }}</view>
|
||||
</view>
|
||||
<SpeakButton :text="feedback.text" :file="feedback.audio" label="听点评" />
|
||||
<view class="btn-primary fb-continue" @click="continuePlay">继续 ›</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -49,11 +98,21 @@
|
||||
|
||||
<script>
|
||||
import { api } from '../../api/index.js'
|
||||
import { getChildId } from '../../store/user.js'
|
||||
import { getChildId, getChildAge } from '../../store/user.js'
|
||||
import { sceneVisual, personVisual, propVisual } from '../../utils/visual.js'
|
||||
import { playSound, isSoundEnabled, setSoundEnabled } from '../../utils/sound.js'
|
||||
import RubyText from '../../components/RubyText.vue'
|
||||
import SpeakButton from '../../components/SpeakButton.vue'
|
||||
import PropPick from '../../components/interactions/PropPick.vue'
|
||||
import FindSpot from '../../components/interactions/FindSpot.vue'
|
||||
import DragPlace from '../../components/interactions/DragPlace.vue'
|
||||
import StepSort from '../../components/interactions/StepSort.vue'
|
||||
import LinkMatch from '../../components/interactions/LinkMatch.vue'
|
||||
|
||||
const RESULT_KEY = '36wisdom_result'
|
||||
|
||||
export default {
|
||||
components: { RubyText, SpeakButton, PropPick, FindSpot, DragPlace, StepSort, LinkMatch },
|
||||
data() {
|
||||
return {
|
||||
childId: getChildId(),
|
||||
@@ -61,13 +120,32 @@ export default {
|
||||
detail: null,
|
||||
curNode: null,
|
||||
feedback: null,
|
||||
choosing: false
|
||||
choosing: false,
|
||||
showPinyin: getChildAge() === '4-6',
|
||||
routeSteps: [],
|
||||
mood: '🤔',
|
||||
combo: 0,
|
||||
soundOn: isSoundEnabled()
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.levelId = Number(options.level_id || 0)
|
||||
this.load()
|
||||
},
|
||||
computed: {
|
||||
scene() {
|
||||
return sceneVisual(this.detail && this.detail.scene ? this.detail.scene.name : '')
|
||||
},
|
||||
sceneImg() {
|
||||
return this.detail && this.detail.scene && this.detail.scene.image ? this.detail.scene.image : ''
|
||||
},
|
||||
charImg() {
|
||||
return this.curNode && this.curNode.character && this.curNode.character.image ? this.curNode.character.image : ''
|
||||
},
|
||||
stepCount() {
|
||||
return Math.min(this.routeSteps.length + 1, 6)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
try {
|
||||
@@ -81,20 +159,54 @@ export default {
|
||||
goBack() {
|
||||
uni.navigateBack({ fail: () => uni.reLaunch({ url: '/pages/map/map' }) })
|
||||
},
|
||||
async choose(option, idx) {
|
||||
personColor(id) {
|
||||
return personVisual(id).color
|
||||
},
|
||||
// 触控互动判定完成:提交成功/失败出口选项
|
||||
interactionDone({ success }) {
|
||||
this.mood = success ? '😊' : '😢'
|
||||
const opt = success ? this.curNode.options[0] : this.curNode.options[1]
|
||||
if (opt) this.submit(opt)
|
||||
},
|
||||
propEmoji(name) {
|
||||
return propVisual(name).emoji
|
||||
},
|
||||
toggleSound() {
|
||||
this.soundOn = !this.soundOn
|
||||
setSoundEnabled(this.soundOn)
|
||||
},
|
||||
choose(o) {
|
||||
if (this.choosing) return
|
||||
playSound('click')
|
||||
this.submit(o)
|
||||
},
|
||||
async submit(o) {
|
||||
this.choosing = true
|
||||
try {
|
||||
const data = await api.levelChoose(this.childId, this.levelId, this.curNode.node_id, option.option_id)
|
||||
const data = await api.levelChoose(this.childId, this.levelId, this.curNode.node_id, o.option_id)
|
||||
this.routeSteps.push({ text: o.text, pros: o.feedback_pros || '' })
|
||||
if (data.next) {
|
||||
this.feedback = { text: option.text, prop: option.prop, next: data.next }
|
||||
this.feedback = {
|
||||
text: o.text,
|
||||
textPinyin: o.text_pinyin,
|
||||
prop: o.prop,
|
||||
audio: o.audio,
|
||||
pros: o.feedback_pros,
|
||||
cons: o.feedback_cons,
|
||||
next: data.next
|
||||
}
|
||||
this.mood = o.feedback_cons ? '😢' : '😊'
|
||||
this.combo = o.feedback_cons ? 0 : Math.min(this.combo + 1, 9)
|
||||
playSound(o.feedback_cons ? 'bad' : 'good')
|
||||
} else if (data.final) {
|
||||
uni.setStorageSync(RESULT_KEY, {
|
||||
level_id: this.levelId,
|
||||
title: this.detail.title,
|
||||
scene_name: this.detail.scene ? this.detail.scene.name : '',
|
||||
route_steps: this.routeSteps,
|
||||
final: data.final
|
||||
})
|
||||
playSound('finish')
|
||||
uni.redirectTo({ url: '/pages/result/result' })
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -105,169 +217,49 @@ export default {
|
||||
continuePlay() {
|
||||
this.curNode = this.feedback.next
|
||||
this.feedback = null
|
||||
this.mood = '🤔'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 32rpx;
|
||||
padding-top: calc(24rpx + env(safe-area-inset-top));
|
||||
}
|
||||
.back {
|
||||
font-size: 56rpx;
|
||||
color: #ff6b35;
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.top-title {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #5b4636;
|
||||
}
|
||||
.top-spacer {
|
||||
width: 72rpx;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 24rpx 32rpx 60rpx;
|
||||
}
|
||||
.node {
|
||||
padding: 48rpx 40rpx;
|
||||
}
|
||||
.node-title {
|
||||
font-size: 38rpx;
|
||||
font-weight: 800;
|
||||
color: #ff6b35;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.character {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.char-avatar {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #4ecdc4, #6c8cff);
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
.char-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #7a6248;
|
||||
}
|
||||
.node-content {
|
||||
font-size: 32rpx;
|
||||
line-height: 1.9;
|
||||
color: #5b4636;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
.options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
.option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f8f2e8;
|
||||
border: 4rpx solid transparent;
|
||||
border-radius: 24rpx;
|
||||
padding: 26rpx 28rpx;
|
||||
}
|
||||
.option.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.option:active {
|
||||
border-color: #ff9a3d;
|
||||
}
|
||||
.option-mark {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
border-radius: 50%;
|
||||
background: #ff9a3d;
|
||||
color: #fff;
|
||||
font-weight: 800;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.option-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #5b4636;
|
||||
}
|
||||
.soon {
|
||||
padding: 40rpx;
|
||||
text-align: center;
|
||||
color: #a08c74;
|
||||
background: #f8f2e8;
|
||||
}
|
||||
|
||||
.mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(91, 70, 54, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
}
|
||||
.feedback {
|
||||
width: 82%;
|
||||
padding: 48rpx 40rpx;
|
||||
}
|
||||
.fb-title {
|
||||
font-size: 26rpx;
|
||||
color: #a08c74;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.fb-option {
|
||||
font-size: 34rpx;
|
||||
font-weight: 800;
|
||||
color: #ff6b35;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.fb-prop {
|
||||
background: #fff7ec;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx 28rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
.fb-prop-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #7a6248;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.fb-prop-desc {
|
||||
font-size: 26rpx;
|
||||
line-height: 1.7;
|
||||
color: #a08c74;
|
||||
}
|
||||
.fb-continue {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
.page { min-height: 100vh; display: flex; flex-direction: column; }
|
||||
.topbar { display: flex; align-items: center; padding: 24rpx 32rpx; padding-top: calc(24rpx + env(safe-area-inset-top)); }
|
||||
.back { font-size: 56rpx; color: #ff6b35; width: 72rpx; height: 72rpx; display: flex; align-items: center; }
|
||||
.top-title { flex: 1; text-align: center; font-size: 32rpx; font-weight: 700; color: #5b4636; }
|
||||
.py-toggle { font-size: 24rpx; color: #ff6b35; background: #fff0e5; border-radius: 30rpx; padding: 8rpx 20rpx; }
|
||||
.content { flex: 1; padding: 24rpx 32rpx 60rpx; }
|
||||
.scene-card { padding: 32rpx; margin-bottom: 24rpx; }
|
||||
.scene-head { display: flex; align-items: center; margin-bottom: 16rpx; }
|
||||
.scene-emoji { font-size: 64rpx; margin-right: 20rpx; }
|
||||
.scene-name { font-size: 32rpx; font-weight: 800; color: #5b4636; }
|
||||
.scene-text { margin-bottom: 20rpx; }
|
||||
.scene-speak { margin-top: 8rpx; }
|
||||
.node { padding: 48rpx 40rpx; }
|
||||
.node-title { font-size: 38rpx; font-weight: 800; color: #ff6b35; margin-bottom: 24rpx; }
|
||||
.character { display: flex; align-items: center; margin-bottom: 24rpx; }
|
||||
.char-avatar { width: 72rpx; height: 72rpx; border-radius: 50%; color: #fff; font-size: 32rpx; font-weight: 700; display: flex; align-items: center; justify-content: center; margin-right: 20rpx; }
|
||||
.char-name { font-size: 28rpx; font-weight: 700; color: #7a6248; margin-right: 20rpx; }
|
||||
.node-content { font-size: 32rpx; line-height: 1.9; color: #5b4636; margin-bottom: 24rpx; }
|
||||
.node-speak { margin-bottom: 32rpx; }
|
||||
.options { display: flex; flex-direction: column; gap: 20rpx; }
|
||||
.option { display: flex; align-items: center; background: #f8f2e8; border: 4rpx solid transparent; border-radius: 24rpx; padding: 26rpx 28rpx; }
|
||||
.option.disabled { opacity: 0.6; }
|
||||
.option:active { border-color: #ff9a3d; }
|
||||
.option-mark { width: 56rpx; height: 56rpx; border-radius: 50%; background: #ff9a3d; color: #fff; font-weight: 800; display: flex; align-items: center; justify-content: center; margin-right: 24rpx; flex-shrink: 0; }
|
||||
.option-text { font-size: 30rpx; font-weight: 600; color: #5b4636; }
|
||||
.soon { padding: 40rpx; text-align: center; color: #a08c74; background: #f8f2e8; }
|
||||
.mask { position: fixed; inset: 0; background: rgba(91, 70, 54, 0.5); display: flex; align-items: center; justify-content: center; z-index: 10; }
|
||||
.feedback { width: 82%; padding: 48rpx 40rpx; }
|
||||
.fb-title { font-size: 26rpx; color: #a08c74; margin-bottom: 16rpx; }
|
||||
.fb-option { font-size: 34rpx; font-weight: 800; color: #ff6b35; margin-bottom: 24rpx; }
|
||||
.fb-line { font-size: 28rpx; line-height: 1.7; border-radius: 16rpx; padding: 16rpx 24rpx; margin-bottom: 16rpx; }
|
||||
.fb-pros { background: #e8f7f3; color: #1fa98f; }
|
||||
.fb-cons { background: #fff0e5; color: #e07b39; }
|
||||
.fb-mark { font-weight: 800; margin-right: 8rpx; }
|
||||
.fb-prop { background: #fff7ec; border-radius: 20rpx; padding: 24rpx 28rpx; margin-bottom: 32rpx; }
|
||||
.fb-prop-name { font-size: 30rpx; font-weight: 700; color: #7a6248; margin-bottom: 8rpx; }
|
||||
.fb-prop-desc { font-size: 26rpx; line-height: 1.7; color: #a08c74; }
|
||||
.fb-continue { margin-top: 24rpx; font-size: 30rpx; }
|
||||
</style>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { reactive } from 'vue'
|
||||
|
||||
const AUTH_KEY = '36wisdom_auth'
|
||||
const CHILD_KEY = '36wisdom_child'
|
||||
const AGE_KEY = '36wisdom_child_age'
|
||||
|
||||
function loadAuth() {
|
||||
const saved = uni.getStorageSync(AUTH_KEY)
|
||||
@@ -46,3 +47,11 @@ export function clearAuth() {
|
||||
state.parentId = 0
|
||||
uni.removeStorageSync(AUTH_KEY)
|
||||
}
|
||||
|
||||
export function getChildAge() {
|
||||
return uni.getStorageSync(AGE_KEY) || '4-6'
|
||||
}
|
||||
|
||||
export function setChildAge(age) {
|
||||
uni.setStorageSync(AGE_KEY, age)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user