training.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. <template>
  2. <view class="container">
  3. <PageBanner theme="mind-wisdom"
  4. tagline="能力训练"
  5. :quote="currentQuote" />
  6. <!-- 训练概览 -->
  7. <view class="stats-card" v-if="stats">
  8. <view class="stats-row">
  9. <view class="stats-item">
  10. <text class="stats-value">{{ stats.totalGames || 0 }}</text>
  11. <text class="stats-label">总训练次数</text>
  12. </view>
  13. <view class="stats-divider"></view>
  14. <view class="stats-item">
  15. <text class="stats-value">{{ stats.bestScore || 0 }}</text>
  16. <text class="stats-label">最高分</text>
  17. </view>
  18. <view class="stats-divider"></view>
  19. <view class="stats-item">
  20. <text class="stats-value">{{ stats.avgScore ? Math.round(stats.avgScore) : 0 }}</text>
  21. <text class="stats-label">平均分</text>
  22. </view>
  23. <view class="stats-divider"></view>
  24. <view class="stats-item">
  25. <text class="stats-value">{{ stats.totalPoints || 0 }}</text>
  26. <text class="stats-label">累计积分</text>
  27. </view>
  28. </view>
  29. </view>
  30. <view class="stats-card stats-loading" v-else-if="statsLoading">
  31. <text class="stats-loading-text">加载训练数据...</text>
  32. </view>
  33. <!-- 训练维度描述 -->
  34. <view class="dimension-card" v-if="currentDim">
  35. <view class="dimension-icon-wrap" :style="{ background: currentDim.lightColor }">
  36. <text class="dimension-icon">{{ currentDim.icon }}</text>
  37. </view>
  38. <text class="dimension-name">{{ currentDim.name }}</text>
  39. <text class="dimension-desc">{{ currentDim.desc }}</text>
  40. </view>
  41. <!-- 推荐游戏 -->
  42. <view class="section" v-if="currentDim && currentDim.games.length > 0">
  43. <view class="section-header">
  44. <text class="section-title">推荐训练</text>
  45. </view>
  46. <view class="game-list">
  47. <view class="game-card" v-for="game in currentDim.games" :key="game.code" @click="goGame(game)">
  48. <view class="game-icon-wrap" :style="{ background: currentDim.lightColor }">
  49. <text class="game-card-icon">{{ game.icon }}</text>
  50. </view>
  51. <view class="game-info">
  52. <text class="game-title">{{ game.name }}</text>
  53. <text class="game-desc">{{ game.desc }}</text>
  54. </view>
  55. <text class="game-arrow">&#x2192;</text>
  56. </view>
  57. </view>
  58. </view>
  59. <!-- 成长建议 -->
  60. <view class="section" v-if="currentDim">
  61. <view class="section-header">
  62. <text class="section-title">成长建议</text>
  63. </view>
  64. <view class="tip-card">
  65. <view class="tip-left-bar" :style="{ background: currentDim.color }"></view>
  66. <view class="tip-body">
  67. <text class="tip-content">{{ currentDim.tip }}</text>
  68. </view>
  69. </view>
  70. </view>
  71. <!-- 其他维度训练 -->
  72. <view class="section">
  73. <view class="section-header">
  74. <text class="section-title">其他维度</text>
  75. </view>
  76. <view class="other-grid">
  77. <view class="other-item" v-for="dim in otherDimensions" :key="dim.key" @click="switchDimension(dim.key)">
  78. <view class="other-icon-wrap" :style="{ background: dim.lightColor }">
  79. <text class="other-icon">{{ dim.icon }}</text>
  80. </view>
  81. <text class="other-name">{{ dim.name }}</text>
  82. </view>
  83. </view>
  84. </view>
  85. <!-- 底部占位 -->
  86. <view class="bottom-spacer"></view>
  87. </view>
  88. </template>
  89. <script>
  90. import PageBanner from '../../components/PageBanner.vue'
  91. import { getGameStats } from '../../utils/api.js'
  92. var DIMENSION_MAP = {
  93. perception: {
  94. key: 'perception',
  95. name: '感知训练',
  96. icon: '\u{1F441}',
  97. color: '#8B5CF6',
  98. lightColor: '#EDE9FE',
  99. desc: '通过图形匹配、颜色分辨等练习提升感知能力',
  100. tip: '感知力是认知的起点。多观察身边事物的细节,锻炼视觉辨识能力,能有效提升感知水平。',
  101. games: [
  102. { code: 'schulte', name: '舒尔特方格', icon: '\u{1F3AF}', desc: '快速定位数字,训练视觉搜索' }
  103. ]
  104. },
  105. focus: {
  106. key: 'focus',
  107. name: '专注训练',
  108. icon: '\u{1F3AF}',
  109. color: '#F97316',
  110. lightColor: '#FFF7ED',
  111. desc: '通过舒尔特方格、数字追踪等练习提升专注力',
  112. tip: '专注力是一切学习的基础。每天坚持10分钟专注训练,持续21天会有显著提升。',
  113. games: [
  114. { code: 'schulte', name: '舒尔特方格', icon: '\u{1F3AF}', desc: '按序点击数字,训练持续专注' }
  115. ]
  116. },
  117. memory: {
  118. key: 'memory',
  119. name: '记忆训练',
  120. icon: '\u{1F9E0}',
  121. color: '#10B981',
  122. lightColor: '#ECFDF5',
  123. desc: '通过数字记忆、序列回忆等练习提升记忆力',
  124. tip: '记忆力可以通过科学训练显著提升。联想记忆法和间隔重复是最有效的两种方法。',
  125. games: [
  126. { code: '1a2b', name: '猜数字', icon: '\u{1F522}', desc: '记忆数字排列,训练工作记忆' }
  127. ]
  128. },
  129. logic: {
  130. key: 'logic',
  131. name: '逻辑训练',
  132. icon: '\u{1F9E9}',
  133. color: '#3B82F6',
  134. lightColor: '#EFF6FF',
  135. desc: '通过推理题、模式识别等练习提升逻辑思维',
  136. tip: '逻辑思维是解决复杂问题的关键。多做推理和归纳练习,能帮助建立清晰的思维框架。',
  137. games: [
  138. { code: 'sudoku', name: '数独', icon: '\u{1F9E9}', desc: '数字逻辑推理,训练演绎思维' },
  139. { code: '1a2b', name: '猜数字', icon: '\u{1F522}', desc: '逻辑排除推理,训练分析能力' }
  140. ]
  141. },
  142. spatial: {
  143. key: 'spatial',
  144. name: '空间训练',
  145. icon: '\u{1F9CA}',
  146. color: '#8D6E63',
  147. lightColor: '#EFEBE9',
  148. desc: '通过空间旋转、立体构建等练习提升空间思维能力',
  149. tip: '空间思维对数学和科学学习至关重要。拼图和积木是提升空间能力的最佳方式。',
  150. games: [
  151. { code: 'sudoku', name: '数独', icon: '\u{1F9E9}', desc: '九宫格空间布局,训练空间推理' }
  152. ]
  153. },
  154. processingSpeed: {
  155. key: 'processingSpeed',
  156. name: '加工速度训练',
  157. icon: '\u26A1',
  158. color: '#FFD700',
  159. lightColor: '#FFFBEB',
  160. desc: '通过快速反应、速度测试等练习提升加工速度',
  161. tip: '加工速度影响学习和反应效率。速算、速读等限时练习是提升加工速度的有效方法。',
  162. games: [
  163. { code: 'schulte', name: '舒尔特方格', icon: '\u{1F3AF}', desc: '限时完成,训练反应速度' },
  164. { code: '1a2b', name: '猜数字', icon: '\u{1F522}', desc: '快速推理,训练信息处理速度' }
  165. ]
  166. }
  167. }
  168. var GAME_PAGES = {
  169. schulte: '/pages/games/schulte',
  170. '1a2b': '/pages/games/1a2b',
  171. sudoku: '/pages/games/sudoku'
  172. }
  173. export default {
  174. components: { PageBanner },
  175. data: function() {
  176. return {
  177. dimension: null,
  178. memberId: null,
  179. stats: null,
  180. statsLoading: false
  181. }
  182. },
  183. computed: {
  184. currentDim: function() {
  185. var dim = DIMENSION_MAP[this.dimension]
  186. return dim || null
  187. },
  188. currentQuote: function() {
  189. var dim = this.currentDim
  190. return dim ? dim.desc : '知己知彼,百战不殆'
  191. },
  192. otherDimensions: function() {
  193. var self = this
  194. var keys = Object.keys(DIMENSION_MAP)
  195. var result = []
  196. for (var i = 0; i < keys.length; i++) {
  197. if (keys[i] !== self.dimension) {
  198. result.push(DIMENSION_MAP[keys[i]])
  199. }
  200. }
  201. return result
  202. }
  203. },
  204. onLoad: function(options) {
  205. if (options && options.dimension) {
  206. this.dimension = options.dimension
  207. }
  208. if (options && options.memberId) {
  209. this.memberId = options.memberId
  210. } else {
  211. this.memberId = uni.getStorageSync('currentChildId') || null
  212. }
  213. uni.setNavigationBarTitle({
  214. title: this.currentDim ? this.currentDim.name : '能力训练'
  215. })
  216. this.loadStats()
  217. },
  218. methods: {
  219. loadStats: function() {
  220. var self = this
  221. if (!self.memberId) return
  222. self.statsLoading = true
  223. getGameStats({ memberId: self.memberId }).then(function(res) {
  224. if (res && res.data) {
  225. self.stats = res.data
  226. }
  227. }).catch(function() {
  228. // silent — stats not critical
  229. }).finally(function() {
  230. self.statsLoading = false
  231. })
  232. },
  233. goGame: function(game) {
  234. var url = GAME_PAGES[game.code]
  235. if (!url) {
  236. uni.showToast({ title: '游戏暂未开放', icon: 'none' })
  237. return
  238. }
  239. var memberId = this.memberId || ''
  240. url = url + '?memberId=' + memberId + '&dimension=' + (this.dimension || '')
  241. uni.navigateTo({ url: url })
  242. },
  243. switchDimension: function(key) {
  244. this.dimension = key
  245. uni.setNavigationBarTitle({
  246. title: DIMENSION_MAP[key].name
  247. })
  248. }
  249. }
  250. }
  251. </script>
  252. <style scoped>
  253. .container {
  254. min-height: 100vh;
  255. background: #f5f7fa;
  256. padding-bottom: 120rpx;
  257. }
  258. /* ===== 训练概览卡 ===== */
  259. .stats-card {
  260. margin: 30rpx;
  261. background: #fff;
  262. border-radius: 20rpx;
  263. padding: 36rpx 24rpx;
  264. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  265. }
  266. .stats-row {
  267. display: flex;
  268. flex-direction: row;
  269. align-items: center;
  270. justify-content: space-around;
  271. }
  272. .stats-item {
  273. display: flex;
  274. flex-direction: column;
  275. align-items: center;
  276. flex: 1;
  277. }
  278. .stats-value {
  279. font-size: 44rpx;
  280. font-weight: bold;
  281. color: #F97316;
  282. margin-bottom: 8rpx;
  283. }
  284. .stats-label {
  285. font-size: 22rpx;
  286. color: #999;
  287. }
  288. .stats-divider {
  289. width: 1rpx;
  290. height: 60rpx;
  291. background: #eee;
  292. flex-shrink: 0;
  293. }
  294. .stats-loading {
  295. display: flex;
  296. align-items: center;
  297. justify-content: center;
  298. padding: 24rpx;
  299. }
  300. .stats-loading-text {
  301. font-size: 26rpx;
  302. color: #bbb;
  303. }
  304. /* ===== 维度描述卡 ===== */
  305. .dimension-card {
  306. margin: 30rpx;
  307. background: #fff;
  308. border-radius: 20rpx;
  309. padding: 40rpx 32rpx;
  310. display: flex;
  311. flex-direction: column;
  312. align-items: center;
  313. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  314. }
  315. .dimension-icon-wrap {
  316. width: 120rpx;
  317. height: 120rpx;
  318. border-radius: 60rpx;
  319. display: flex;
  320. align-items: center;
  321. justify-content: center;
  322. margin-bottom: 20rpx;
  323. }
  324. .dimension-icon {
  325. font-size: 56rpx;
  326. }
  327. .dimension-name {
  328. font-size: 36rpx;
  329. font-weight: bold;
  330. color: #333;
  331. margin-bottom: 16rpx;
  332. }
  333. .dimension-desc {
  334. font-size: 26rpx;
  335. color: #888;
  336. text-align: center;
  337. line-height: 1.6;
  338. }
  339. /* ===== 通用区段 ===== */
  340. .section {
  341. margin: 20rpx 30rpx;
  342. }
  343. .section-header {
  344. margin-bottom: 20rpx;
  345. }
  346. .section-title {
  347. font-size: 30rpx;
  348. font-weight: bold;
  349. color: #333;
  350. }
  351. /* ===== 推荐游戏列表 ===== */
  352. .game-list {
  353. display: flex;
  354. flex-direction: column;
  355. }
  356. .game-card {
  357. display: flex;
  358. flex-direction: row;
  359. align-items: center;
  360. background: #fff;
  361. border-radius: 20rpx;
  362. padding: 24rpx;
  363. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  364. margin-bottom: 20rpx;
  365. }
  366. .game-card:active {
  367. opacity: 0.7;
  368. }
  369. .game-icon-wrap {
  370. width: 80rpx;
  371. height: 80rpx;
  372. border-radius: 40rpx;
  373. display: flex;
  374. align-items: center;
  375. justify-content: center;
  376. margin-right: 20rpx;
  377. flex-shrink: 0;
  378. }
  379. .game-card-icon {
  380. font-size: 40rpx;
  381. }
  382. .game-info {
  383. flex: 1;
  384. display: flex;
  385. flex-direction: column;
  386. }
  387. .game-title {
  388. font-size: 28rpx;
  389. font-weight: bold;
  390. color: #333;
  391. margin-bottom: 6rpx;
  392. }
  393. .game-desc {
  394. font-size: 22rpx;
  395. color: #999;
  396. }
  397. .game-arrow {
  398. font-size: 28rpx;
  399. color: #ccc;
  400. margin-left: 16rpx;
  401. flex-shrink: 0;
  402. }
  403. /* ===== 成长建议卡 ===== */
  404. .tip-card {
  405. display: flex;
  406. flex-direction: row;
  407. background: #fff;
  408. border-radius: 20rpx;
  409. box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
  410. overflow: hidden;
  411. }
  412. .tip-left-bar {
  413. width: 6rpx;
  414. flex-shrink: 0;
  415. }
  416. .tip-body {
  417. flex: 1;
  418. padding: 28rpx 24rpx;
  419. }
  420. .tip-content {
  421. font-size: 26rpx;
  422. color: #666;
  423. line-height: 1.7;
  424. }
  425. /* ===== 其他维度网格 ===== */
  426. .other-grid {
  427. display: flex;
  428. flex-direction: row;
  429. flex-wrap: wrap;
  430. margin: 0 -10rpx;
  431. }
  432. .other-item {
  433. width: calc(33.33% - 20rpx);
  434. display: flex;
  435. flex-direction: column;
  436. align-items: center;
  437. background: #fff;
  438. border-radius: 16rpx;
  439. padding: 24rpx 8rpx;
  440. margin: 0 10rpx 20rpx;
  441. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05);
  442. }
  443. .other-item:active {
  444. opacity: 0.7;
  445. }
  446. .other-icon-wrap {
  447. width: 64rpx;
  448. height: 64rpx;
  449. border-radius: 32rpx;
  450. display: flex;
  451. align-items: center;
  452. justify-content: center;
  453. margin-bottom: 10rpx;
  454. }
  455. .other-icon {
  456. font-size: 32rpx;
  457. }
  458. .other-name {
  459. font-size: 22rpx;
  460. color: #666;
  461. font-weight: 500;
  462. }
  463. /* ===== 底部 ===== */
  464. .bottom-spacer {
  465. height: 120rpx;
  466. }
  467. </style>