Explorar o código

fix(frontend): index.vue 闪屏逻辑优化 + api.js 中文注释编码修复

index.vue: 首次启动跳过 TabTransition, 等待子组件 ready 后才隐藏闪屏; api.js: 修复乱码的中文注释

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
User hai 2 meses
pai
achega
f328cbcae9
Modificáronse 2 ficheiros con 108 adicións e 76 borrados
  1. 47 15
      cfc-frontend/pages/index/index.vue
  2. 61 61
      cfc-frontend/utils/api.js

+ 47 - 15
cfc-frontend/pages/index/index.vue

@@ -182,8 +182,8 @@
   <!-- 登录态 — 按角色渲染(带淡入淡出过渡)-->
   <view v-else class="role-switch-wrapper">
     <transition name="fade" mode="out-in">
-      <parent-index v-if="currentRole === 'parent'" key="parent" />
-      <child-index v-else-if="currentRole === 'child'" key="child" />
+      <parent-index v-if="currentRole === 'parent'" key="parent" @ready="onChildReady" />
+      <child-index v-else-if="currentRole === 'child'" key="child" @ready="onChildReady" />
       <!-- teacher-index 已废弃,教师功能折入 parent-index -->
       <view v-else class="loading-container" key="loading">
         <text>加载中...</text>
@@ -235,6 +235,7 @@ export default {
       // splash loading ovarlay(首次进入时展示,数据加载完关闭)
       splashOverlay: false,
       splashOverlayFading: false,
+      _splashReady: false,
 
       typeList: [
         { label: '全部', value: '' },
@@ -258,17 +259,17 @@ export default {
   onShow() {
     // 从 splash 首次进入:展示 splash overlay,数据加载完后自动关闭
     var showOverlay = uni.getStorageSync('_showSplashOverlay')
-    if (showOverlay === '1') {
+    var isFirstLaunch = (showOverlay === '1')
+    if (isFirstLaunch) {
       uni.removeStorageSync('_showSplashOverlay')
       this._showSplashOverlayMask()
     }
 
-    // Tabbar 切换过渡动画
-    this.showTabTransition = true
-    var self = this
-    setTimeout(function() {
-      self.showTabTransition = false
-    }, 1800)
+    // 首次启动由 flash 覆盖加载过程,跳过 TabTransition;后续 tab 切换才显示
+    if (!isFirstLaunch) {
+      this.showTabTransition = true
+      this._watchPageReady()
+    }
 
     // 始终执行初始化,确保登录态变更后及时切换视图
     this._doInit()
@@ -287,12 +288,12 @@ export default {
         self._tryHideSplash()
       }, 800)
 
-      // 最长 6 秒强制关闭,防止 API 卡死导致蒙板不消失
+      // 最长 5 秒强制关闭,防止 API 卡死导致蒙板不消失
       setTimeout(function() {
         if (self.splashOverlay) {
           self._hideSplashOverlay()
         }
-      }, 6000)
+      }, 5000)
     },
     _checkSplashReady() {
       this._splashLoadCount++
@@ -300,11 +301,13 @@ export default {
     },
     _tryHideSplash() {
       if (this.splashOverlayFading) return
-      // 最小展示时间未到 → 等待
       if (!this._splashMinPassed) return
-      // 已登录用户只需最小时间;游客需要等待 3 个 API 全部返回
-      if (this.isLoggedIn() || this._splashLoadCount >= 3) {
-        this._hideSplashOverlay()
+      if (this.isLoggedIn()) {
+        // 登录用户:等待子组件的 ready 信号
+        if (this._splashReady) this._hideSplashOverlay()
+      } else {
+        // 游客:等待 3 个 API 全部返回
+        if (this._splashLoadCount >= 3) this._hideSplashOverlay()
       }
     },
     _hideSplashOverlay() {
@@ -317,6 +320,35 @@ export default {
       }, 400)
     },
 
+    onChildReady() {
+      this._splashReady = true
+      this._tryHideSplash()
+    },
+
+    // 首页 TabTransition 动态关闭:检测关键内容是否就绪
+    _watchPageReady() {
+      var self = this
+      var unwatch = this.$watch(function() {
+        if (!self.isLoggedIn()) {
+          return !!(self.products && self.products.length > 0) ||
+                 !!(self.dimensionActivities && self.dimensionActivities.length > 0) ||
+                 !!(self.indexArticles && self.indexArticles.length > 0)
+        }
+        // 登录用户:currentRole 存在代表子组件已渲染
+        return !!self.currentRole
+      }, function(val) {
+        if (val) {
+          if (unwatch) unwatch()
+          self.showTabTransition = false
+        }
+      })
+      // 5s 超时兜底
+      setTimeout(function() {
+        if (unwatch) unwatch()
+        self.showTabTransition = false
+      }, 5000)
+    },
+
     _doInit(options) {
       options = options || {}
       if (!this.isLoggedIn()) {

+ 61 - 61
cfc-frontend/utils/api.js

@@ -44,7 +44,7 @@ const request = (url, method = 'POST', data = {}) => {
           resolve(res.data)
         } else {
           uni.showToast({
-            title: res.data.message || '璇锋眰澶辫触',
+            title: res.data.message || '请求失败',
             icon: 'none'
           })
           reject(res.data)
@@ -52,7 +52,7 @@ const request = (url, method = 'POST', data = {}) => {
       },
       fail: (err) => {
         uni.showToast({
-          title: '缃戠粶璇锋眰澶辫触',
+          title: '网络请求失败',
           icon: 'none'
         })
         reject(err)
@@ -61,69 +61,69 @@ const request = (url, method = 'POST', data = {}) => {
   })
 }
 
-// 鐧诲綍妯″潡
+// 登录模块
 export const wechatLogin = (code, userInfo) => {
   return request('/api/auth/wechat-login', 'POST', { code, ...userInfo })
 }
 
-// 鍙戦€侀獙璇佺爜
+// 发送验证码
 export const sendCode = (data) => {
   return request('/api/auth/send-code', 'POST', data)
 }
 
-// 鎵嬫満鍙烽獙璇佺爜鐧诲綍
+// 手机号码验证码登录
 export const phoneLogin = (data) => {
   return request('/api/auth/phone-login', 'POST', data)
 }
 
-// ===================== 琛楅亾鍦板潃鐩稿叧 =====================
+// ===================== 街道地址相关 =====================
 
-// 鑾峰彇鎵€鏈夌渷浠?
+// 获取所有省份
 export const getProvinces = () => {
   return request('/api/streets/provinces')
 }
 
-// 鏍规嵁鐖剁骇ID鑾峰彇瀛愮骇鍦板潃
+// 根据父级ID获取子级地址
 export const getStreetChildren = (parentId) => {
   return request(`/api/streets/children/${parentId}`)
 }
 
-// 鏍规嵁ID鑾峰彇鍦板潃璇︽儏
+// 根据ID获取地址详情
 export const getStreetById = (id) => {
   return request(`/api/streets/${id}`)
 }
 
-// 鑾峰彇鍦板潃瀹屾暣璺緞
+// 获取地址完整路径
 export const getStreetPath = (id) => {
   return request(`/api/streets/${id}/path`)
 }
 
-// 鎼滅储鍦板潃
+// 搜索地址
 export const searchStreet = (keyword) => {
   return request('/api/streets/search', 'POST', { keyword })
 }
 
-// 鍖哄煙鍖归厤
+// 区域匹配
 export const matchByStreet = (streetId) => {
   return request(`/api/streets/${streetId}/match`)
 }
 
-// 鎸夊尯鍩熺粺璁?
+// 按区域统计
 export const getStreetStats = (level = 4) => {
   return request('/api/streets/stats', 'POST', { level })
 }
 
-// 寰俊鎵嬫満鍙风櫥褰曪紙鏂扮増锛?
+// 微信手机号登录(新版)
 export const wechatPhoneLogin = (data) => {
   return request('/api/auth/wechat-phone-login', 'POST', data)
 }
 
-// 寰俊闈欓粯鐧诲綍
+// 微信静默登录
 export const silentLogin = (code) => {
   return request('/api/auth/silent-login', 'POST', { code })
 }
 
-// 閫氳繃openid鑷姩鐧诲綍
+// 通过openid自动登录
 export const autoLogin = (openid) => {
   return request('/api/auth/auto-login', 'POST', { openid })
 }
@@ -136,17 +136,17 @@ export const verifyPassword = (password) => {
   return request('/api/auth/verify-password', 'POST', { password })
 }
 
-// 妫€鏌ユ墜鏈哄彿鏄惁宸叉敞鍐?
+// 检查手机号是否已注册
 export const checkPhone = (phone) => {
   return request('/api/auth/check-phone', 'POST', { phone })
 }
 
-// 鐩存帴娉ㄥ唽锛堟祴璇曠幆澧冿級
+// 直接注册(测试环境)
 export const directRegister = (data) => {
   return request('/api/auth/direct-register', 'POST', data)
 }
 
-// 鐢ㄦ埛妯″潡
+// 用户模块
 export const getUserInfo = () => {
 	return request('/api/user/info', 'POST')
 }
@@ -163,17 +163,17 @@ export const createFamily = (name) => {
 	return request('/api/user/create-family', 'POST', { name })
 }
 
-// 鑾峰彇瀹跺涵鎵€鏈夋垚鍛橈紙瀹堕暱+瀛╁瓙锛?
+// 获取家庭所有成员(家长+孩子)
 export const getFamilyMembers = () => {
 	return request('/api/family/user/family-members', 'POST')
 }
 
-// 鑾峰彇瀵瑰搴垚鍛樺彲瑙佺殑鎴愬憳鍒楄〃
+// 获取对家庭成员可见的成员列表
 export const getVisibleFamilyMembers = () => {
 	return request('/api/family/user/members/visible', 'POST')
 }
 
-// 鏇存柊鎴愬憳鍙鎬?
+// 更新成员可见性
 export const updateMemberVisibility = (memberId, memberType, showToFamily) => {
 	return request('/api/family/user/members/update-visibility', 'POST', { memberId, memberType, showToFamily })
 }
@@ -198,7 +198,7 @@ export const switchRole = (role) => {
 	return request('/api/user/switch-role', 'POST', { role })
 }
 
-// 浼氬憳妯″潡
+// 会员模块
 export const getMembershipLevels = () => {
 	return request('/api/membership/levels', 'POST')
 }
@@ -243,7 +243,7 @@ export const updateChild = (childId, data) => {
   return request('/api/family/user/children/update', 'POST', Object.assign({}, data, { childId: childId }))
 }
 
-// 瀹跺涵鎴愬憳妯″潡锛堢粺涓€娣诲姞鎴愬憳锛?
+// 家庭成员模块(统一添加成员)
 export const addFamilyMember = (data) => {
   return request('/api/family/member/add', 'POST', data)
 }
@@ -268,7 +268,7 @@ export const getRelationshipTypes = () => {
   return request('/api/family/member/relationship-types', 'POST')
 }
 
-// 浠诲姟妯″潡
+// 任务模块
 export const createTask = (data) => {
   return request('/api/tasks', 'POST', data)
 }
@@ -313,7 +313,7 @@ export const batchDeleteTasks = (taskIds) => {
   return request('/api/tasks/batch-delete', 'POST', { taskIds })
 }
 
-// 绉垎妯″潡
+// 积分模块
 export const getPointsBalance = (childId) => {
   return request('/api/points/balance', 'POST', { childId })
 }
@@ -326,7 +326,7 @@ export const adjustPoints = (childId, amount, reason, password) => {
   return request('/api/points/adjust', 'POST', { childId, amount, reason, password })
 }
 
-// 濂栧姳妯″潡
+// 奖励模块
 export const createReward = (data) => {
   return request('/api/rewards', 'POST', data)
 }
@@ -352,7 +352,7 @@ export const getExchangeHistory = (childId, page = 1, size = 10) => {
 }
 
 // =============================================
-// 瀹堕暱浠诲姟妯″潡锛堝瀛愬垎閰嶇粰瀹堕暱鐨勪换鍔★級
+// 家长任务模块(孩子分配给家长的任务)
 // =============================================
 export const getTodayParentTasks = () => {
   return request('/api/tasks/today-parent', 'POST')
@@ -363,7 +363,7 @@ export const completeParentTask = (taskId) => {
 }
 
 // =============================================
-// 瀹堕暱蹇冩効鍗曟ā鍧?
+// 家长心愿单模块
 // =============================================
 export const createParentWishlist = (data) => {
   return request('/api/parent/wishlist', 'POST', data)
@@ -381,13 +381,13 @@ export const exchangeParentWishlist = (id) => {
   return request(`/api/parent/wishlist/${id}/exchange`, 'POST', {})
 }
 
-// 瀹堕暱瀹℃壒瀛╁瓙鐨勫績鎰垮苟濉啓绉垎
+// 家长审批孩子的心愿并填写积分
 export const approveParentWishlist = (id, pointsRequired) => {
   return request(`/api/parent/wishlist/${id}/approve`, 'POST', { pointsRequired })
 }
 
 // =============================================
-// 瀹堕暱蹇冩効绠$悊妯″潡
+// 家长心愿管理模块
 // =============================================
 export const getPendingWishes = (familyId) => {
   return request('/api/wishes/pending', 'POST', { familyId })
@@ -434,14 +434,14 @@ export const cancelWish = (id, reason) => {
 }
 
 // =============================================
-// 鎴愰暱瑙勫垝甯?濂楅妯″潡
+// 成长规划师/套餐模块
 // =============================================
-// 鐢熸垚缁戝畾閭€璇?
+// 生成绑定邀请
 export const generateBindInvite = () => {
   return request('/api/guide/bind/invite', 'POST')
 }
 
-// 鑾峰彇缁戝畾鐨勫搴垪琛?
+// 获取绑定的家庭列表
 export const getBoundFamilies = () => {
   return request('/api/guide/families', 'POST')
 }
@@ -451,87 +451,87 @@ export const confirmBind = (data) => {
   return request('/api/guide/bind/confirm', 'POST', data)
 }
 
-// 瑙i櫎缁戝畾
+// 解除绑定
 export const unbindFamily = (id) => {
   return request(`/api/guide/bind/${id}`, 'DELETE')
 }
 
-// 鍒涘缓濂楅
+// 创建套餐
 export const createPackage = (data) => {
   return request('/api/packages', 'POST', data)
 }
 
-// 鏇存柊濂楅
+// 更新套餐
 export const updatePackage = (id, data) => {
   return request(`/api/packages/${id}`, 'PUT', data)
 }
 
-// 鑾峰彇濂楅璇︽儏
+// 获取套餐详情
 export const getPackage = (id) => {
   return request(`/api/packages/${id}`, 'POST')
 }
 
-// 鑾峰彇鎴戠殑濂楅鍒楄〃
+// 获取我的套餐列表
 export const getMyPackages = () => {
   return request('/api/packages', 'POST')
 }
 
-// 鎻愪氦瀹℃牳
+// 提交审核
 export const submitPackageForReview = (id) => {
   return request(`/api/packages/${id}/submit`, 'POST')
 }
 
-// 鍙戝竷/涓嬫灦
+// 发布/下架
 export const publishPackage = (id, publish) => {
   return request(`/api/packages/${id}/publish`, 'POST', {}, { publish })
 }
 
-// 娣诲姞浠诲姟椤?
+// 添加任务项
 export const addPackageItem = (packageId, data) => {
   return request(`/api/packages/${packageId}/items`, 'POST', data)
 }
 
-// 鏇存柊浠诲姟椤?
+// 更新任务项
 export const updatePackageItem = (itemId, data) => {
   return request(`/api/packages/items/${itemId}`, 'PUT', data)
 }
 
-// 鍒犻櫎浠诲姟椤?
+// 删除任务项
 export const deletePackageItem = (itemId) => {
   return request(`/api/packages/items/${itemId}`, 'DELETE')
 }
 
-// 鑾峰彇濂楅浠诲姟椤?
+// 获取套餐任务项
 export const getPackageItems = (packageId) => {
   return request(`/api/packages/${packageId}/items`, 'POST')
 }
 
-// 鑾峰彇鍏紑濂楅鍒楄〃
+// 获取公开套餐列表
 export const getPublicPackages = (category) => {
   return request('/api/market/packages', 'POST', { category })
 }
 
-// 閫氳繃鍒嗕韩鐮佽幏鍙栧椁?
+// 通过分享码获取套餐
 export const getPackageByShareCode = (code) => {
   return request(`/api/market/share/${code}`, 'POST')
 }
 
-// 璐拱骞跺簲鐢ㄥ椁?
+// 购买并应用套餐
 export const purchasePackage = (packageId, data) => {
   return request(`/api/market/packages/${packageId}/purchase`, 'POST', data)
 }
 
-// 鑾峰彇鎴戠殑浠诲姟璁″垝
+// 获取我的任务计划
 export const getMyPlans = () => {
   return request('/api/market/plans', 'POST')
 }
 
-// 鑾峰彇璁″垝璇︽儏
+// 获取计划详情
 export const getPlanDetail = (id) => {
   return request(`/api/market/plans/${id}`, 'POST')
 }
 
-// 鍙栨秷璁″垝
+// 取消计划
 export const cancelPlan = (id) => {
   return request(`/api/market/plans/${id}/cancel`, 'POST')
 }
@@ -1002,46 +1002,46 @@ export const aiDeleteConversation = (id) => {
   return request('/api/ai/chat/conversations/' + id + '/delete', 'POST', {})
 }
 
-// 绮惧噯钀ュ吇鍔╂墜锛堝熀浜庤弻缇ゆ姤鍛婏級
+// 精准营养助手(基于体检报告)
 export const aiSendNutritionMessage = (data) => {
   return request('/api/ai/nutrition/send', 'POST', data)
 }
 
-// ===================== 娴犺壘绂忎勘涔愰儴 - 鏈嶅姟鍟嗘ā鍧?=====================
+// ===================== 幸福邻里部落 - 服务商模块 =====================
 
-// 鎻愪氦鏈嶅姟鍟嗗叆椹荤敵璇?
+// 提交服务商入驻申请
 export const vendorApply = (data) => {
   return request('/api/vendor/apply', 'POST', data)
 }
 
-// 鏌ヨ鍏ラ┗瀹℃牳鐘舵€?
+// 查询入驻审核状态
 export const vendorStatus = () => {
   return request('/api/vendor/status', 'POST', {})
 }
 
-// 鑾峰彇鏈嶅姟鍟嗕俊鎭?
+// 获取服务商信息
 export const vendorInfo = () => {
   return request('/api/vendor/info', 'POST', {})
 }
 
-// ===================== 娴犺壘绂忎勘涔愰儴 - 鍟嗗搧绯荤粺 =====================
+// ===================== 幸福邻里部落 - 商品系统 =====================
 
-// 鍟嗗搧鍒楄〃锛堝叕寮€锛?
+// 商品列表(公开)
 export const productList = (params) => {
   return request('/api/product/list', 'POST', params)
 }
 
-// 鍟嗗搧璇︽儏锛堝叕寮€锛?
+// 商品详情(公开)
 export const productDetail = (params) => {
   return request('/api/product/detail', 'POST', params)
 }
 
-// 鍟嗗搧SKU鍒楄〃
+// 商品SKU列表
 export const productSkuList = (params) => {
   return request('/api/product/sku/list', 'POST', params)
 }
 
-// 鏈嶅姟鍟嗗彂甯冨晢鍝?
+// 服务商发布商品
 export const productCreate = (params) => {
   return request('/api/product/create', 'POST', params)
 }