Files
Mycontent/miniprogram/utils/linkedMiniprogramNavigate.js
乘风 7cd8ffdd26 feat: add MBTI tag functionality to user profile
- Introduced a new MBTI tag in the user profile section, displaying the user's MBTI type or a default label if not available.
- Implemented navigation to a mini program for MBTI testing, ensuring user phone number is included if logged in.
- Enhanced styling for the MBTI tag to improve visibility and user interaction.

Made-with: Cursor
2026-04-20 11:10:50 +08:00

192 lines
6.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Soul 创业派对 - 关联小程序跳转(与阅读页 onLinkTagTap miniprogram 分支一致)
*/
const app = getApp()
function normalizeLinkTagLabel(raw) {
return String(raw || '')
.replace(/^[#\s\u00a0\u200b\u3000]+/u, '')
.replace(/[\s\u00a0\u200b\u3000]+$/u, '')
.trim()
.toLowerCase()
}
function resolveLinkTagByLabel(label) {
const normalized = normalizeLinkTagLabel(label)
if (!normalized) return null
const tags = Array.isArray(app.globalData.linkTagsConfig) ? app.globalData.linkTagsConfig : []
for (const t of tags) {
if (!t) continue
const candidates = [t.label]
if (typeof t.aliases === 'string' && t.aliases.trim()) {
candidates.push(...t.aliases.split(','))
}
for (const c of candidates) {
if (normalizeLinkTagLabel(c) === normalized) return t
}
}
return null
}
function pickLinkTagField(tag, keys, defaultValue = '') {
if (!tag || typeof tag !== 'object') return defaultValue
for (const key of keys) {
const v = tag[key]
if (v == null) continue
const s = String(v).trim()
if (s) return s
}
return defaultValue
}
function parseDatasetBool(v) {
if (typeof v === 'boolean') return v
const s = String(v || '').trim().toLowerCase()
return s === '1' || s === 'true' || s === 'yes' || s === 'on'
}
function normalizeQueryKey(raw, defaultKey = 'phone') {
const s = String(raw || '').trim()
if (!s) return defaultKey
if (!/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(s)) return defaultKey
return s
}
/** 跳转其他小程序时在 path 上追加/覆盖 query保留已有 ?…&…,避免重复同名 key */
function appendQueryToPath(path, key, value) {
if (value == null || value === '') return (path || '').trim()
const base = (path || '').trim()
const hashIdx = base.indexOf('#')
const hashPart = hashIdx >= 0 ? base.slice(hashIdx) : ''
const noHash = hashIdx >= 0 ? base.slice(0, hashIdx) : base
const qIdx = noHash.indexOf('?')
const pathname = qIdx >= 0 ? noHash.slice(0, qIdx) : noHash
const query = qIdx >= 0 ? noHash.slice(qIdx + 1) : ''
const params = []
if (query) {
query.split('&').forEach((pair) => {
if (!pair) return
const [k = '', v = ''] = pair.split('=')
const dk = decodeURIComponent(k || '')
if (dk && dk !== key) params.push(`${encodeURIComponent(dk)}=${v}`)
})
}
params.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
return `${pathname}${params.length ? '?' + params.join('&') : ''}${hashPart}`
}
function getLoggedInUserPhone() {
const u = app.globalData.userInfo || {}
return (u.phone || u.phoneNumber || wx.getStorageSync('user_phone') || '').trim().replace(/\s/g, '')
}
async function ensureLoggedInUserPhone() {
let phone = getLoggedInUserPhone()
if (phone) return phone
try {
const profileRes = await app.request({ url: '/api/miniprogram/user/profile', silent: true, timeout: 3000 })
const p = (profileRes && profileRes.data) ? profileRes.data : {}
phone = String(p.phone || p.phoneNumber || '').trim().replace(/\s/g, '')
if (phone) {
const merged = { ...(app.globalData.userInfo || {}), ...p, phone }
app.globalData.userInfo = merged
try {
wx.setStorageSync('userInfo', merged)
wx.setStorageSync('user_phone', phone)
} catch (_) {}
}
} catch (e) {}
return phone
}
function resolveMiniprogramLinkFromTag(tag) {
if (!tag || typeof tag !== 'object') return null
const tagType = pickLinkTagField(tag, ['type', 'tagType'], '').toLowerCase()
if (tagType !== 'miniprogram') return null
const mpKey = pickLinkTagField(tag, ['mpKey', 'mp_key', 'appId', 'app_id'], '')
const pagePath = pickLinkTagField(tag, ['pagePath', 'page_path'], '')
let passPhone = true
const hasCachedPassPhone = !(tag.passPhone == null || tag.passPhone === '')
if (hasCachedPassPhone) passPhone = parseDatasetBool(tag.passPhone)
const phoneParamName = normalizeQueryKey(tag.phoneParamName, 'phone')
return { mpKey, pagePath, passPhone, phoneParamName }
}
/**
* @param {{ mpKey: string, pagePath?: string, passPhone?: boolean, phoneParamName?: string }} opts
*/
async function navigateLinkedMiniprogram(opts) {
const mpKey = String(opts.mpKey || '').trim()
if (!mpKey) {
wx.showToast({ title: '未配置小程序', icon: 'none' })
return
}
await app.getReadExtras()
const linked = (app.globalData.linkedMiniprograms || []).find((m) => m.key === mpKey)
if (!linked || !linked.appId) {
wx.showToast({ title: '未找到关联小程序配置', icon: 'none' })
return
}
let targetPath = (opts.pagePath != null && String(opts.pagePath).trim())
? String(opts.pagePath).trim()
: (linked.path || '')
const phone = await ensureLoggedInUserPhone()
const passPhone = opts.passPhone !== false
const phoneParamName = normalizeQueryKey(opts.phoneParamName, 'phone')
if (passPhone && phone) {
targetPath = appendQueryToPath(targetPath, phoneParamName, phone)
}
wx.navigateToMiniProgram({
appId: linked.appId,
path: targetPath || '',
envVersion: 'release',
success: () => {},
fail: (err) => {
wx.showToast({ title: err.errMsg || '跳转失败', icon: 'none' })
},
})
}
async function navigateMiniprogramFromLinkTagConfig(tag) {
const r = resolveMiniprogramLinkFromTag(tag)
if (!r || !r.mpKey) return false
await navigateLinkedMiniprogram(r)
return true
}
/**
* @param {Record<string, unknown>} myPageUi mp_config.mpUi.myPage
*/
function findMbtiLinkTagInConfig(myPageUi) {
const tags = Array.isArray(app.globalData.linkTagsConfig) ? app.globalData.linkTagsConfig : []
const preferred = myPageUi && String(myPageUi.mbtiLinkLabel || '').trim()
if (preferred) {
const t = resolveLinkTagByLabel(preferred)
if (t) {
const r = resolveMiniprogramLinkFromTag(t)
if (r && r.mpKey) return t
}
}
for (const t of tags) {
const r = resolveMiniprogramLinkFromTag(t)
if (!r || !r.mpKey) continue
const lbl = pickLinkTagField(t, ['label'], '').toLowerCase()
const key = String(r.mpKey || '').toLowerCase()
const path = String(r.pagePath || '').toLowerCase()
if (lbl.includes('mbti') || key.includes('mbti') || path.includes('mbti')) return t
}
return null
}
module.exports = {
appendQueryToPath,
getLoggedInUserPhone,
ensureLoggedInUserPhone,
navigateLinkedMiniprogram,
navigateMiniprogramFromLinkTagConfig,
findMbtiLinkTagInConfig,
resolveLinkTagByLabel,
resolveMiniprogramLinkFromTag,
normalizeQueryKey,
}