feat: MBTI头像与用户规则链路升级,三端页面与接口同步

Made-with: Cursor
This commit is contained in:
卡若
2026-03-24 01:22:50 +08:00
parent fa3da12b16
commit 1d56d0336c
71 changed files with 3848 additions and 1621 deletions

View File

@@ -8,6 +8,10 @@ const app = getApp()
const { trackClick } = require('../../utils/trackClick')
const { cleanSingleLineField } = require('../../utils/contentParser')
const { navigateMpPath } = require('../../utils/mpNavigate.js')
const { isSafeImageSrc } = require('../../utils/imageUrl.js')
const DEFAULT_KARUO_LINK_AVATAR = '/assets/images/karuo-link-avatar.png'
const KARUO_USER_ID = 'ogpTW5Wbbo9DfSyB3-xCWN6EGc-g'
/** 与首页固定「卡若」获客位重复时从横滑列表剔除(含历史误写「卡路」) */
function isKaruoHostDuplicateName(displayName) {
@@ -92,11 +96,12 @@ Page({
mpUiLogoTitle: '卡若创业派对',
mpUiLogoSubtitle: '来自派对房的真实故事',
mpUiLinkKaruoText: '点击链接卡若',
/** 最终展示:后台 linkKaruoAvatar 或本包默认卡若照片 */
mpUiLinkKaruoDisplay: DEFAULT_KARUO_LINK_AVATAR,
mpUiSearchPlaceholder: '搜索章节标题或内容...',
mpUiBannerTag: '推荐',
mpUiBannerReadMore: '点击阅读',
mpUiSuperTitle: '超级个体',
mpUiSuperLinkText: '获客入口',
mpUiPickTitle: '精选推荐',
mpUiLatestTitle: '最新新增'
},
@@ -321,51 +326,62 @@ Page({
_applyHomeMpUi() {
const h = app.globalData.configCache?.mpConfig?.mpUi?.homePage || {}
let linkKaruoAvatar = String(h.linkKaruoAvatar || h.linkKaruoImage || '').trim()
if (linkKaruoAvatar && !isSafeImageSrc(linkKaruoAvatar)) linkKaruoAvatar = ''
this.setData({
mpUiLogoTitle: String(h.logoTitle || '卡若创业派对').trim() || '卡若创业派对',
mpUiLogoSubtitle: String(h.logoSubtitle || '来自派对房的真实故事').trim() || '来自派对房的真实故事',
mpUiLinkKaruoText: String(h.linkKaruoText || '点击链接卡若').trim() || '点击链接卡若',
mpUiLinkKaruoDisplay: linkKaruoAvatar || DEFAULT_KARUO_LINK_AVATAR,
mpUiSearchPlaceholder: String(h.searchPlaceholder || '搜索章节标题或内容...').trim() || '搜索章节标题或内容...',
mpUiBannerTag: String(h.bannerTag || '推荐').trim() || '推荐',
mpUiBannerReadMore: String(h.bannerReadMoreText || '点击阅读').trim() || '点击阅读',
mpUiSuperTitle: String(h.superSectionTitle || '超级个体').trim() || '超级个体',
mpUiSuperLinkText: String(h.superSectionLinkText || '获客入口').trim() || '获客入口',
mpUiPickTitle: String(h.pickSectionTitle || '精选推荐').trim() || '精选推荐',
mpUiLatestTitle: String(h.latestSectionTitle || '最新新增').trim() || '最新新增'
})
if (!linkKaruoAvatar) this._loadKaruoAvatarLazy()
},
/** 超级个体右侧文案:默认跳转找伙伴 Tab路径可由 homePage.superSectionLinkPath 配置) */
goSuperSectionLink() {
const p = String(
app.globalData.configCache?.mpConfig?.mpUi?.homePage?.superSectionLinkPath || '/pages/match/match'
).trim()
if (p) navigateMpPath(p)
_loadKaruoAvatarLazy() {
app.request({ url: `/api/miniprogram/user/profile?userId=${KARUO_USER_ID}`, silent: true, timeout: 3000 })
.then(res => {
if (res?.success && res.data?.avatar && isSafeImageSrc(res.data.avatar)) {
this.setData({ mpUiLinkKaruoDisplay: res.data.avatar })
}
})
.catch(() => {})
},
async loadFeatureConfig() {
try {
const hasCachedFeatures = app.globalData.features && typeof app.globalData.features.searchEnabled === 'boolean'
if (hasCachedFeatures) {
this.setData({
searchEnabled: app.globalData.features.searchEnabled,
auditMode: app.globalData.auditMode || false
})
this._applyHomeMpUi()
return
if (!hasCachedFeatures) {
const res = await app.getConfig()
const features = (res && res.features) || (res && res.data && res.data.features) || {}
const searchEnabled = features.searchEnabled !== false
if (!app.globalData.features) app.globalData.features = {}
app.globalData.features.searchEnabled = searchEnabled
if (typeof features.matchEnabled === 'boolean') app.globalData.features.matchEnabled = features.matchEnabled
if (typeof features.referralEnabled === 'boolean') app.globalData.features.referralEnabled = features.referralEnabled
const mp = (res && res.mpConfig) || {}
app.globalData.auditMode = !!mp.auditMode
}
const res = await app.getConfig()
const features = (res && res.features) || {}
const mp = (res && res.mpConfig) || {}
const searchEnabled = features.searchEnabled !== false
const auditMode = !!mp.auditMode
if (!app.globalData.features) app.globalData.features = {}
app.globalData.features.searchEnabled = searchEnabled
app.globalData.auditMode = auditMode
this.setData({ searchEnabled, auditMode })
await app.getAuditMode()
const searchEnabled = app.globalData.features?.searchEnabled !== false
this.setData({
searchEnabled,
auditMode: app.globalData.auditMode || false
})
this._applyHomeMpUi()
} catch (e) {
this.setData({ searchEnabled: true, auditMode: app.globalData.auditMode || false })
try {
await app.getAuditMode()
} catch (_) {}
this.setData({
searchEnabled: app.globalData.features?.searchEnabled !== false,
auditMode: app.globalData.auditMode || false
})
this._applyHomeMpUi()
}
},
@@ -459,6 +475,22 @@ Page({
// 阻止弹窗内部点击事件冒泡到遮罩层
stopPropagation() {},
preventMove() {},
onLeadPrivacyAuthorize() {
this.onAgreePrivacyForLead()
},
onDisagreePrivacyForLead() {
if (app._privacyResolve) {
try {
app._privacyResolve({ event: 'disagree' })
} catch (_) {}
app._privacyResolve = null
}
this.setData({ showPrivacyModal: false })
},
onLeadPhoneInput(e) {
this.setData({ leadPhone: (e.detail.value || '').trim() })
},

View File

@@ -16,7 +16,12 @@
<text class="logo-subtitle">{{mpUiLogoSubtitle}}</text>
</view>
</view>
<view class="header-right"></view>
<view class="header-right" wx:if="{{!auditMode}}">
<view class="contact-btn" catchtap="onLinkKaruo" hover-class="none">
<image class="contact-avatar" src="{{mpUiLinkKaruoDisplay}}" mode="aspectFill"/>
<text class="contact-name">{{mpUiLinkKaruoText}}</text>
</view>
</view>
</view>
<!-- 搜索栏(根据配置显示) -->
@@ -52,7 +57,6 @@
<view class="section" wx:if="{{!auditMode}}">
<view class="section-header">
<text class="section-title">{{mpUiSuperTitle}}</text>
<text class="section-subtitle" bindtap="goSuperSectionLink">{{mpUiSuperLinkText}}</text>
</view>
<!-- 加载中:骨架动画 -->
<view wx:if="{{superMembersLoading}}" class="super-loading">
@@ -160,4 +164,30 @@
<!-- 底部留白 -->
<view class="bottom-space"></view>
<!-- 隐私授权(首页在 needPrivacy 列表内,需有遮罩否则无法完成 agree -->
<view class="privacy-mask" wx:if="{{showPrivacyModal}}" catchtouchmove="preventMove">
<view class="privacy-modal">
<text class="privacy-title">温馨提示</text>
<text class="privacy-desc">使用手机号能力前,请先同意《用户隐私保护指引》</text>
<button id="agree-privacy-btn" class="privacy-btn" open-type="agreePrivacyAuthorization" bindagreeprivacyauthorization="onLeadPrivacyAuthorize">同意</button>
<view class="privacy-cancel" bindtap="onDisagreePrivacyForLead">拒绝</view>
</view>
</view>
<!-- 链接卡若 - 留资弹窗 -->
<view class="lead-mask" wx:if="{{showLeadModal}}" catchtap="closeLeadModal">
<view class="lead-box" catchtap="stopPropagation">
<text class="lead-title">留下联系方式</text>
<text class="lead-desc">方便卡若与您联系</text>
<button class="lead-get-phone-btn" open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumberForLead">一键获取手机号</button>
<text class="lead-divider">或手动输入</text>
<view class="lead-input-wrap">
<input class="lead-input" placeholder="请输入手机号" type="number" maxlength="11" value="{{leadPhone}}" bindinput="onLeadPhoneInput"/>
</view>
<view class="lead-actions">
<button class="lead-btn lead-btn-cancel" bindtap="closeLeadModal">取消</button>
<button class="lead-btn lead-btn-submit" bindtap="submitLead">提交</button>
</view>
</view>
</view>
</view>

View File

@@ -85,6 +85,10 @@
font-size: 20rpx;
color: rgba(255, 255, 255, 0.7);
white-space: nowrap;
max-width: 140rpx;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
}
.logo-title {
@@ -963,6 +967,61 @@
height: 40rpx;
}
/* ===== 隐私授权(与 avatar-nickname 对齐) ===== */
.privacy-mask {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 2000;
display: flex;
align-items: center;
justify-content: center;
padding: 48rpx;
box-sizing: border-box;
}
.privacy-modal {
width: 100%;
max-width: 560rpx;
background: #17212F;
border-radius: 24rpx;
padding: 48rpx;
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box;
}
.privacy-title {
font-size: 36rpx;
font-weight: 700;
color: #fff;
margin-bottom: 24rpx;
}
.privacy-desc {
font-size: 28rpx;
color: #94A3B8;
text-align: center;
line-height: 1.5;
margin-bottom: 40rpx;
}
.privacy-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
text-align: center;
background: #5EEAD4;
color: #000;
font-size: 30rpx;
font-weight: 600;
border-radius: 16rpx;
border: none;
}
.privacy-btn::after { border: none; }
.privacy-cancel {
margin-top: 24rpx;
font-size: 28rpx;
color: #64748B;
}
/* ===== 链接卡若 - 留资弹窗 ===== */
.lead-mask {
position: fixed;
@@ -971,7 +1030,7 @@
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 1000;
z-index: 2100;
display: flex;
align-items: center;
justify-content: center;