|
|
@@ -118,37 +118,21 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- ④ 内容区 — 快捷功能入口(单排) -->
|
|
|
+ <!-- ④ 内容区 — 快捷功能入口(动态渲染,可在应用库自定义) -->
|
|
|
|
|
|
<view class="section">
|
|
|
+ <view class="section-header">
|
|
|
+ <text class="section-title">⚡ 快捷功能</text>
|
|
|
+ <text class="section-more" @click="goAppLibrary">应用库 ›</text>
|
|
|
+ </view>
|
|
|
<view class="quick-bar">
|
|
|
- <view class="quick-item" @click="navTo('/pages/tasks/tasks')">
|
|
|
- <view class="quick-icon" style="background:rgba(99,102,241,0.15)"><text>📋</text></view>
|
|
|
- <text class="quick-label">任务</text>
|
|
|
- </view>
|
|
|
- <view class="quick-item" @click="navTo('/pages/action-detail/interaction-log')">
|
|
|
- <view class="quick-icon" style="background:rgba(16,185,129,0.15)"><text>💬</text></view>
|
|
|
- <text class="quick-label">互动</text>
|
|
|
- </view>
|
|
|
- <view class="quick-item" @click="navTo('/pages/mind-detail/family-dashboard')">
|
|
|
- <view class="quick-icon" style="background:rgba(139,92,246,0.15)"><text>🏠</text></view>
|
|
|
- <text class="quick-label">天盘</text>
|
|
|
- </view>
|
|
|
- <view class="quick-item" @click="navTo('/pages/profile-extra/family-members')">
|
|
|
- <view class="quick-icon" style="background:rgba(245,158,11,0.15)"><text>👨👩👧👦</text></view>
|
|
|
- <text class="quick-label">成员</text>
|
|
|
- </view>
|
|
|
- <view class="quick-item" @click="navTo('/pages/discover/circles')">
|
|
|
- <view class="quick-icon" style="background:rgba(255,107,157,0.15)"><text>🤝</text></view>
|
|
|
- <text class="quick-label">圈子</text>
|
|
|
- </view>
|
|
|
- <view class="quick-item" @click="navTo('/pages/shop/index/index')">
|
|
|
- <view class="quick-icon" style="background:rgba(249,115,22,0.15)"><text>🛒</text></view>
|
|
|
- <text class="quick-label">商城</text>
|
|
|
+ <view class="quick-item" v-for="(app, idx) in quickApps" :key="app.key" @click="onQuickTap(app)">
|
|
|
+ <view class="quick-icon" :style="getQuickIconStyle(idx)"><text>{{ app.icon }}</text></view>
|
|
|
+ <text class="quick-label">{{ app.name }}</text>
|
|
|
</view>
|
|
|
- <view class="quick-item" @click="navTo('/pages/diet/food-query')">
|
|
|
- <view class="quick-icon" style="background:rgba(16,185,129,0.15)"><text>🥗</text></view>
|
|
|
- <text class="quick-label">食材</text>
|
|
|
+ <view class="quick-item" v-if="quickApps.length === 0" @click="goAppLibrary">
|
|
|
+ <view class="quick-icon" style="background:rgba(99,102,241,0.15)"><text>+</text></view>
|
|
|
+ <text class="quick-label">去添加</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -431,7 +415,8 @@ import DailyTaskCard from '../../components/daily-task-card.vue'
|
|
|
import ReportUploadCard from '../../components/report-upload-card.vue'
|
|
|
import OnboardingProgressBar from '../../components/OnboardingProgressBar.vue'
|
|
|
import OnboardingGuide from '../../components/OnboardingGuide.vue'
|
|
|
-import { acceptParentInvite, getActivityList, getFeaturedArticles, getEnergySandbox, getEnergyOverview, getFamilyMemberList, getChallengeList, getGuideConfig, switchToFamilyMember, getMyMembership, updateChallengeProgress, respondChallenge, getTodayTasks, completeTask as completeTaskApi, getTodayParentTasks, completeParentTask as completeParentTaskApi, getTaskHistory, getNotices, getNotificationList, getSurveyStatus, getSelfCheckStatus, getReferralCode, extractReferralCode } from '../../utils/api.js'
|
|
|
+import { acceptParentInvite, getActivityList, getFeaturedArticles, getEnergySandbox, getEnergyOverview, getFamilyMemberList, getChallengeList, getGuideConfig, switchToFamilyMember, getMyMembership, updateChallengeProgress, respondChallenge, getTodayTasks, completeTask as completeTaskApi, getTodayParentTasks, completeParentTask as completeParentTaskApi, getTaskHistory, getNotices, getNotificationList, getSurveyStatus, getSelfCheckStatus, getReferralCode, extractReferralCode, getAppLibrary, reportAppEnter } from '../../utils/api.js'
|
|
|
+import { findAppByKey, getShortcutableApps } from '../../utils/app-library.js'
|
|
|
import nav from '../../utils/nav.js'
|
|
|
import config from '@/config.js'
|
|
|
import { parseDate } from '../../utils/format.js'
|
|
|
@@ -516,7 +501,13 @@ membershipDays: 0,
|
|
|
|
|
|
// 分享邀请码
|
|
|
referralCode: '',
|
|
|
- referralCodeLoaded: false
|
|
|
+ referralCodeLoaded: false,
|
|
|
+
|
|
|
+ // 首页快捷区(应用库自定义)
|
|
|
+ quickApps: [],
|
|
|
+ myShortcuts: [],
|
|
|
+ appUsage: {},
|
|
|
+ quickLoaded: false
|
|
|
|
|
|
|
|
|
}
|
|
|
@@ -751,10 +742,95 @@ membershipDays: 0,
|
|
|
}, 400)
|
|
|
},
|
|
|
|
|
|
+ // ===== 首页快捷区(应用库自定义) =====
|
|
|
+ // 排序算法:锁定占位 + 频度填满剩余(最多 6 个)
|
|
|
+ computeQuickApps: function(myShortcuts, usage) {
|
|
|
+ var lockedApps = []
|
|
|
+ var myShortcutsArr = myShortcuts || []
|
|
|
+ // 1. 锁定应用按 sortOrder 升序
|
|
|
+ lockedApps = myShortcutsArr.filter(function(s) { return s.locked === 1 })
|
|
|
+ .sort(function(a, b) { return a.sortOrder - b.sortOrder })
|
|
|
+ if (lockedApps.length >= 6) {
|
|
|
+ return lockedApps.slice(0, 6)
|
|
|
+ }
|
|
|
+ // 2. 剩余格数:候选 = 可快捷且未被锁定的应用,按频度降序填充
|
|
|
+ var remaining = 6 - lockedApps.length
|
|
|
+ var lockedKeys = {}
|
|
|
+ lockedApps.forEach(function(s) { lockedKeys[s.appKey] = true })
|
|
|
+ var candidates = getShortcutableApps().filter(function(app) { return !lockedKeys[app.key] })
|
|
|
+ candidates.sort(function(a, b) {
|
|
|
+ var ca = (usage && usage[a.key]) || 0
|
|
|
+ var cb = (usage && usage[b.key]) || 0
|
|
|
+ return cb - ca
|
|
|
+ })
|
|
|
+ return lockedApps.concat(candidates.slice(0, remaining))
|
|
|
+ },
|
|
|
+ // 后端返回 myShortcuts(含 locked/sortOrder),无配置时为空数组 → 前端按频度填充
|
|
|
+ loadQuickApps: function() {
|
|
|
+ var self = this
|
|
|
+ getAppLibrary().then(function(res) {
|
|
|
+ if (res.code === 200 && res.data) {
|
|
|
+ self.myShortcuts = res.data.myShortcuts || []
|
|
|
+ self.appUsage = res.data.usage || {}
|
|
|
+ var result = self.computeQuickApps(self.myShortcuts, self.appUsage)
|
|
|
+ // 转成展示对象:从应用库取 icon/name/route
|
|
|
+ var shown = []
|
|
|
+ result.forEach(function(item) {
|
|
|
+ var appKey = item.appKey
|
|
|
+ var app = findAppByKey(appKey)
|
|
|
+ if (app) {
|
|
|
+ shown.push({
|
|
|
+ key: app.key,
|
|
|
+ name: app.name,
|
|
|
+ icon: app.icon,
|
|
|
+ route: app.route,
|
|
|
+ locked: item.locked
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ self.quickApps = shown
|
|
|
+ self.quickLoaded = true
|
|
|
+ }
|
|
|
+ }).catch(function() {
|
|
|
+ // 接口失败:回退到默认 6 个
|
|
|
+ var defaultKeys = ['task', 'interaction', 'tiandi', 'members', 'circle', 'shop']
|
|
|
+ var shown = []
|
|
|
+ defaultKeys.forEach(function(key) {
|
|
|
+ var app = findAppByKey(key)
|
|
|
+ if (app) shown.push({ key: app.key, name: app.name, icon: app.icon, route: app.route, locked: 0 })
|
|
|
+ })
|
|
|
+ self.quickApps = shown
|
|
|
+ self.quickLoaded = true
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onQuickTap: function(app) {
|
|
|
+ if (!app || !app.route) return
|
|
|
+ // 记录进入次数(静默,失败不影响跳转)
|
|
|
+ if (app.key) {
|
|
|
+ reportAppEnter(app.key).catch(function() {})
|
|
|
+ }
|
|
|
+ this.navTo(app.route)
|
|
|
+ },
|
|
|
+ goAppLibrary: function() {
|
|
|
+ uni.navigateTo({ url: '/pages/home/app-library' })
|
|
|
+ },
|
|
|
+ getQuickIconStyle: function(idx) {
|
|
|
+ var palette = [
|
|
|
+ 'rgba(99,102,241,0.15)',
|
|
|
+ 'rgba(16,185,129,0.15)',
|
|
|
+ 'rgba(139,92,246,0.15)',
|
|
|
+ 'rgba(245,158,11,0.15)',
|
|
|
+ 'rgba(255,107,157,0.15)',
|
|
|
+ 'rgba(249,115,22,0.15)'
|
|
|
+ ]
|
|
|
+ return { background: palette[idx % palette.length] }
|
|
|
+ },
|
|
|
+
|
|
|
// ===== 登录态数据加载 =====
|
|
|
loadLoggedInData: function() {
|
|
|
this.loadFamilyData()
|
|
|
this.loadReferralCode()
|
|
|
+ this.loadQuickApps()
|
|
|
},
|
|
|
// 合并原 loadFamilyMembers + loadChildren:仅调用一次 getFamilyMemberList
|
|
|
loadFamilyData: function() {
|