优化小程序推荐码处理逻辑,支持通过扫码场景解析推荐码和初始章节ID。新增获取用户邀请码的功能以便于分享。更新分享配置,确保分享时自动带上推荐码。调整部分页面逻辑以提升用户体验。

This commit is contained in:
2026-02-12 15:09:52 +08:00
parent c57866ffe0
commit 448e908855
40 changed files with 1068 additions and 318 deletions

View File

@@ -0,0 +1,157 @@
/**
* Soul创业派对 - 扫码解析页
* 扫描二维码/条形码,展示解析内容
*/
const app = getApp()
Page({
data: {
statusBarHeight: 44,
// 最近一次解析结果
lastResult: null,
scanType: '',
charSet: '',
// 小程序码解析(路径、参数)
parsedPath: null,
parsedQuery: [],
canNavigate: false,
// 历史记录
history: []
},
onLoad() {
this.setData({
statusBarHeight: app.globalData.statusBarHeight || 44
})
this.loadHistory()
},
loadHistory() {
try {
const history = wx.getStorageSync('scanHistory') || []
this.setData({ history })
} catch (e) {
console.log('加载扫码历史失败:', e)
}
},
saveToHistory(result, scanType, charSet) {
const item = { result, scanType, charSet, time: new Date().toLocaleString() }
let history = wx.getStorageSync('scanHistory') || []
history = [item, ...history].slice(0, 10)
wx.setStorageSync('scanHistory', history)
this.setData({ history })
},
// 解析小程序码内容path?key=val 或 path
parseMiniProgramCode(result) {
if (!result || typeof result !== 'string') return { path: null, query: [], canNavigate: false }
const idx = result.indexOf('?')
let path = idx >= 0 ? result.slice(0, idx) : result
const qs = idx >= 0 ? result.slice(idx + 1) : ''
path = path.replace(/^\//, '').trim()
const query = []
if (qs) {
qs.split('&').forEach(pair => {
const eq = pair.indexOf('=')
const k = eq >= 0 ? pair.slice(0, eq) : pair
const v = eq >= 0 ? pair.slice(eq + 1) : ''
try {
if (k) query.push({ key: decodeURIComponent(k), value: decodeURIComponent(v) })
} catch (_) {
if (k) query.push({ key: k, value: v })
}
})
}
const isMiniProgramPath = /^pages\/[\w-]+\/[\w-]+$/.test(path)
return { path: path || null, query, canNavigate: isMiniProgramPath }
},
// 发起扫码(支持小程序码)
doScan() {
wx.scanCode({
onlyFromCamera: false,
scanType: ['qrCode', 'barCode'],
success: (res) => {
const { result, scanType, charSet } = res
const parsed = this.parseMiniProgramCode(result)
this.setData({
lastResult: result,
scanType: scanType || '未知',
charSet: charSet || '',
parsedPath: parsed.path,
parsedQuery: parsed.query,
canNavigate: parsed.canNavigate
})
this.saveToHistory(result, scanType, charSet)
},
fail: (err) => {
if (err.errMsg && err.errMsg.includes('cancel')) {
return
}
wx.showToast({ title: err.errMsg || '扫码失败', icon: 'none' })
}
})
},
// 复制内容
copyResult() {
const { lastResult } = this.data
if (!lastResult) {
wx.showToast({ title: '暂无解析内容', icon: 'none' })
return
}
wx.setClipboardData({
data: lastResult,
success: () => wx.showToast({ title: '已复制', icon: 'success' })
})
},
// 清空当前结果
clearResult() {
this.setData({
lastResult: null, scanType: '', charSet: '',
parsedPath: null, parsedQuery: [], canNavigate: false
})
},
// 打开解析出的小程序页面
openParsedPage() {
const { parsedPath, parsedQuery } = this.data
if (!parsedPath || !this.data.canNavigate) {
wx.showToast({ title: '非本小程序页面', icon: 'none' })
return
}
const queryStr = parsedQuery.length
? '?' + parsedQuery.map(q => `${encodeURIComponent(q.key)}=${encodeURIComponent(q.value)}`).join('&')
: ''
const url = `/${parsedPath}${queryStr}`
const tabPages = ['pages/index/index', 'pages/chapters/chapters', 'pages/match/match', 'pages/my/my']
if (tabPages.includes(parsedPath)) {
wx.switchTab({ url: `/${parsedPath}` })
} else {
wx.navigateTo({ url })
}
},
// 清空历史
clearHistory() {
wx.setStorageSync('scanHistory', [])
this.setData({ history: [], lastResult: null, scanType: '', charSet: '' })
wx.showToast({ title: '已清空', icon: 'success' })
},
// 点击历史项复制
onHistoryItemTap(e) {
const result = e.currentTarget.dataset.result
if (!result) return
wx.setClipboardData({
data: result,
success: () => wx.showToast({ title: '已复制', icon: 'success' })
})
},
goBack() {
wx.navigateBack()
}
})

View File

@@ -0,0 +1 @@
{"usingComponents":{},"navigationStyle":"custom","navigationBarTitleText":"扫码解析"}

View File

@@ -0,0 +1,81 @@
<!--pages/scan/scan.wxml-->
<!--扫码解析页 - 扫描二维码/条形码展示解析内容-->
<view class="page">
<!-- 自定义导航栏 -->
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
<view class="nav-content">
<view class="back-btn" bindtap="goBack">
<text class="back-icon">←</text>
</view>
<text class="nav-title">扫码解析</text>
</view>
</view>
<!-- 主内容 -->
<view class="main-content" style="padding-top: {{statusBarHeight + 56}}px;">
<!-- 扫码按钮 -->
<view class="scan-action">
<view class="scan-btn" bindtap="doScan">
<text class="scan-icon">📷</text>
<text class="scan-text">扫描小程序码 / 二维码</text>
</view>
</view>
<!-- 解析结果 -->
<view class="result-card" wx:if="{{lastResult}}">
<view class="result-header">
<text class="result-label">解析内容</text>
<view class="result-actions">
<view class="action-btn" bindtap="copyResult">复制</view>
<view class="action-btn" wx:if="{{canNavigate}}" bindtap="openParsedPage">打开</view>
<view class="action-btn secondary" bindtap="clearResult">清空</view>
</view>
</view>
<!-- 小程序码解析:路径 + 参数(仅当为 pages/ 路径时展示) -->
<view class="parsed-section" wx:if="{{parsedPath && (parsedPath.indexOf('pages/') === 0 || parsedQuery.length > 0)}}">
<view class="parsed-row">
<text class="parsed-label">路径</text>
<text class="parsed-value">{{parsedPath}}</text>
</view>
<view class="parsed-row" wx:for="{{parsedQuery}}" wx:key="key" wx:for-item="q">
<text class="parsed-label">{{q.key}}</text>
<text class="parsed-value">{{q.value}}</text>
</view>
</view>
<view class="result-meta" wx:if="{{scanType}}">
<text class="meta-item">类型: {{scanType}}</text>
<text class="meta-item" wx:if="{{charSet}}">字符集: {{charSet}}</text>
</view>
<scroll-view class="result-content" scroll-y="{{true}}">
<text class="result-text" selectable="{{true}}">{{lastResult}}</text>
</scroll-view>
</view>
<!-- 无结果提示 -->
<view class="empty-tip" wx:else>
<text class="empty-icon">📷</text>
<text class="empty-text">点击上方按钮扫码</text>
<text class="empty-desc">支持小程序码、二维码、条形码</text>
</view>
<!-- 扫码历史 -->
<view class="history-section" wx:if="{{history.length > 0}}">
<view class="history-header">
<text class="history-title">扫码历史</text>
<view class="clear-history" bindtap="clearHistory">清空</view>
</view>
<view class="history-list">
<view
class="history-item"
wx:for="{{history}}"
wx:key="index"
bindtap="onHistoryItemTap"
data-result="{{item.result}}"
>
<text class="history-content">{{item.result}}</text>
<text class="history-time">{{item.time}}</text>
</view>
</view>
</view>
</view>
</view>

View File

@@ -0,0 +1,248 @@
/* 扫码解析页样式 */
.page {
min-height: 100vh;
background: linear-gradient(180deg, #0a0a0a 0%, #111111 100%);
}
.nav-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
background: rgba(10, 10, 10, 0.95);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
.nav-content {
display: flex;
align-items: center;
padding: 8rpx 24rpx;
height: 88rpx;
}
.back-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.back-icon {
font-size: 40rpx;
color: #00CED1;
}
.nav-title {
flex: 1;
font-size: 34rpx;
font-weight: 600;
color: #fff;
text-align: center;
margin-right: 60rpx;
}
.main-content {
padding: 24rpx;
}
/* 扫码按钮 */
.scan-action {
padding: 60rpx 0;
}
.scan-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, rgba(32, 178, 170, 0.15) 100%);
border: 2rpx solid rgba(0, 206, 209, 0.4);
border-radius: 32rpx;
padding: 80rpx 48rpx;
}
.scan-icon {
font-size: 80rpx;
margin-bottom: 24rpx;
}
.scan-text {
font-size: 32rpx;
color: #00CED1;
font-weight: 500;
}
/* 解析结果卡片 */
.result-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 24rpx;
padding: 32rpx;
margin-top: 32rpx;
border: 1rpx solid rgba(255, 255, 255, 0.08);
}
.result-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
}
.result-label {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.6);
font-weight: 500;
}
.result-actions {
display: flex;
gap: 24rpx;
}
.action-btn {
font-size: 26rpx;
color: #00CED1;
padding: 8rpx 20rpx;
}
.action-btn.secondary {
color: rgba(255, 255, 255, 0.5);
}
/* 小程序码解析:路径+参数 */
.parsed-section {
background: rgba(0, 206, 209, 0.08);
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
border: 1rpx solid rgba(0, 206, 209, 0.2);
}
.parsed-row {
display: flex;
align-items: baseline;
margin-bottom: 12rpx;
}
.parsed-row:last-child {
margin-bottom: 0;
}
.parsed-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
min-width: 120rpx;
flex-shrink: 0;
}
.parsed-value {
font-size: 26rpx;
color: #00CED1;
word-break: break-all;
}
.result-meta {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
margin-bottom: 20rpx;
}
.meta-item {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.4);
}
.result-content {
max-height: 400rpx;
background: rgba(0, 0, 0, 0.3);
border-radius: 16rpx;
padding: 24rpx;
}
.result-text {
font-size: 28rpx;
color: #fff;
line-height: 1.6;
word-break: break-all;
white-space: pre-wrap;
}
/* 无结果提示 */
.empty-tip {
display: flex;
flex-direction: column;
align-items: center;
padding: 60rpx 0;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
opacity: 0.5;
}
.empty-text {
font-size: 30rpx;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 8rpx;
}
.empty-desc {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.4);
}
/* 扫码历史 */
.history-section {
margin-top: 48rpx;
}
.history-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
}
.history-title {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.6);
}
.clear-history {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.5);
}
.history-list {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.history-item {
background: rgba(255, 255, 255, 0.05);
border-radius: 16rpx;
padding: 24rpx;
border: 1rpx solid rgba(255, 255, 255, 0.06);
}
.history-content {
font-size: 26rpx;
color: #fff;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.history-time {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.4);
margin-top: 8rpx;
display: block;
}