feat: 优化活动奖励图片处理、登录流程及Authorization头设置,并改进对对碰活动奖励展示和排序逻辑
This commit is contained in:
parent
d1fd76e242
commit
0bd10c6a0d
@ -76,6 +76,32 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['update:visible', 'close'])
|
||||
|
||||
function cleanUrl(u) {
|
||||
if (!u) return '/static/logo.png'
|
||||
let s = String(u).trim()
|
||||
|
||||
// 尝试解析 JSON 数组字符串 (针对后端返回的 JSON 字符串图片地址)
|
||||
if (s.startsWith('[') && s.endsWith(']')) {
|
||||
try {
|
||||
const arr = JSON.parse(s)
|
||||
if (Array.isArray(arr) && arr.length > 0) {
|
||||
s = arr[0]
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('JSON parse failed for prize image:', s)
|
||||
}
|
||||
}
|
||||
|
||||
// 清理反引号、引号和空格
|
||||
s = s.replace(/[`'"]/g, '').trim()
|
||||
|
||||
// 提取 http 链接
|
||||
const m = s.match(/https?:\/\/[^\s]+/)
|
||||
if (m && m[0]) return m[0]
|
||||
|
||||
return s || '/static/logo.png'
|
||||
}
|
||||
|
||||
const groupedResults = computed(() => {
|
||||
const map = new Map()
|
||||
const arr = Array.isArray(props.results) ? props.results : []
|
||||
@ -90,7 +116,7 @@ const groupedResults = computed(() => {
|
||||
} else {
|
||||
map.set(key, {
|
||||
title: item.title || item.name || '神秘奖品',
|
||||
image: item.image || item.img || item.pic || '',
|
||||
image: cleanUrl(item.image || item.img || item.pic || ''),
|
||||
reward_id: rewardId,
|
||||
quantity: 1
|
||||
})
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
</view>
|
||||
<scroll-view class="preview-scroll" scroll-x>
|
||||
<view class="preview-item" v-for="(item, idx) in group.rewards" :key="item.id || idx">
|
||||
<view class="prize-tag" :class="{ 'tag-boss': item.boss }">{{ item.boss ? 'BOSS' : group.level }}</view>
|
||||
<view class="prize-tag tag-boss" v-if="item.boss">BOSS</view>
|
||||
<image class="preview-img" :src="item.image" mode="aspectFill" />
|
||||
<view class="preview-name">{{ item.title }}</view>
|
||||
</view>
|
||||
|
||||
@ -317,13 +317,21 @@ const currentIssueRewards = computed(() => {
|
||||
const rewardGroups = computed(() => {
|
||||
const groups = {}
|
||||
currentIssueRewards.value.forEach(item => {
|
||||
const level = item.level || '赏'
|
||||
let level = item.level || '赏'
|
||||
if (item.min_score > 0 && level !== 'BOSS') {
|
||||
level = `${item.min_score}对子`
|
||||
}
|
||||
if (!groups[level]) groups[level] = []
|
||||
groups[level].push(item)
|
||||
})
|
||||
return Object.keys(groups).sort((a, b) => {
|
||||
if (a === 'BOSS') return -1
|
||||
if (b === 'BOSS') return 1
|
||||
if (a === 'Last' || a === 'BOSS') return -1
|
||||
if (b === 'Last' || b === 'BOSS') return 1
|
||||
const numA = parseInt(a)
|
||||
const numB = parseInt(b)
|
||||
if (!isNaN(numA) && !isNaN(numB)) {
|
||||
return numB - numA
|
||||
}
|
||||
return a.localeCompare(b)
|
||||
}).map(key => {
|
||||
const rewards = groups[key]
|
||||
@ -506,11 +514,13 @@ function normalizeIssues(list) {
|
||||
function normalizeRewards(list) {
|
||||
const arr = unwrap(list)
|
||||
const items = arr.map((i, idx) => ({
|
||||
...i, // Spread original properties first
|
||||
id: i.product_id ?? i.id ?? String(idx),
|
||||
title: i.name ?? i.title ?? '',
|
||||
image: cleanUrl(i.product_image ?? i.image ?? i.img ?? i.pic ?? i.banner ?? ''),
|
||||
weight: Number(i.weight) || 0,
|
||||
boss: detectBoss(i),
|
||||
min_score: Number(i.min_score) || 0, // Extract min_score
|
||||
level: levelToAlpha(i.prize_level ?? i.level ?? (detectBoss(i) ? 'BOSS' : '赏'))
|
||||
}))
|
||||
const total = items.reduce((acc, it) => acc + (it.weight > 0 ? it.weight : 0), 0)
|
||||
@ -518,7 +528,8 @@ function normalizeRewards(list) {
|
||||
...it,
|
||||
percent: total > 0 ? Math.round((it.weight / total) * 1000) / 10 : 0
|
||||
}))
|
||||
enriched.sort((a, b) => (b.percent - a.percent))
|
||||
// 移除前端按百分比强制排序逻辑,保留后端原始排序
|
||||
// enriched.sort((a, b) => (b.percent - a.percent))
|
||||
return enriched
|
||||
}
|
||||
async function fetchRewardsForIssues(activityId) {
|
||||
@ -974,7 +985,7 @@ async function finishAndReport() {
|
||||
)
|
||||
return {
|
||||
title: item.title || item.name || found?.name || found?.title || '神秘奖励',
|
||||
image: item.image || item.img || found?.image || found?.pic || found?.product_image || '',
|
||||
image: cleanUrl(item.image || item.img || found?.image || found?.pic || found?.product_image || ''),
|
||||
reward_id: item.reward_id || item.id
|
||||
}
|
||||
})
|
||||
|
||||
@ -22,14 +22,14 @@
|
||||
:class="{ active: loginMode === 'wechat' }"
|
||||
@tap="switchMode('wechat')"
|
||||
>
|
||||
<text class="tab-text">微信快捷登录</text>
|
||||
<text class="tab-text">手机号快捷登录</text>
|
||||
</view>
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{ active: loginMode === 'sms' }"
|
||||
@tap="switchMode('sms')"
|
||||
>
|
||||
<text class="tab-text">手机号登录</text>
|
||||
<text class="tab-text">验证码登录</text>
|
||||
</view>
|
||||
<view class="tab-indicator" :class="{ right: loginMode === 'sms' }"></view>
|
||||
</view>
|
||||
@ -43,8 +43,8 @@
|
||||
<text class="icon-emoji">💬</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="panel-title">微信一键授权</text>
|
||||
<text class="panel-desc">使用微信授权获取手机号,安全快速</text>
|
||||
<text class="panel-title">一键获取手机号</text>
|
||||
<text class="panel-desc">授权获取本机手机号,安全快速登录</text>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button
|
||||
@ -53,13 +53,13 @@
|
||||
:disabled="loading"
|
||||
@getphonenumber="onGetPhoneNumber"
|
||||
>
|
||||
{{ loading ? '授权中...' : '微信授权登录' }}
|
||||
{{ loading ? '获取中...' : '一键获取手机号' }}
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<button class="btn-primary btn-login" disabled>
|
||||
请在微信小程序中使用
|
||||
当前环境不支持
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
@ -146,6 +146,8 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { request } from '../../utils/request'
|
||||
import { wechatLogin, bindPhone, getUserStats, getPointsBalance, sendSmsCode, smsLogin } from '../../api/appUser'
|
||||
|
||||
const loading = ref(false)
|
||||
@ -154,6 +156,38 @@ const agreementChecked = ref(false)
|
||||
// 登录模式
|
||||
const loginMode = ref('wechat')
|
||||
|
||||
// 静默获取 OpenID
|
||||
async function ensureOpenID() {
|
||||
const current = uni.getStorageSync('openid')
|
||||
if (current) return
|
||||
|
||||
console.log('[DEBUG] 本地缺少 openid, 尝试静默获取...')
|
||||
uni.login({
|
||||
provider: 'weixin',
|
||||
success: async (loginRes) => {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/api/app/common/openid',
|
||||
method: 'POST',
|
||||
data: { code: loginRes.code }
|
||||
})
|
||||
if (res && res.openid) {
|
||||
console.log('[DEBUG] 静默获取 openid 成功:', res.openid)
|
||||
uni.setStorageSync('openid', res.openid)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[DEBUG] 静默获取 openid 失败:', err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
ensureOpenID()
|
||||
})
|
||||
|
||||
const fetchExtraDataLoading = ref(false)
|
||||
|
||||
// 短信登录
|
||||
const mobile = ref('')
|
||||
const smsCode = ref('')
|
||||
@ -254,6 +288,7 @@ async function handleSmsLogin() {
|
||||
const inviterCode = uni.getStorageSync('inviter_code')
|
||||
const data = await smsLogin(mobile.value, smsCode.value, inviterCode)
|
||||
|
||||
console.log('[DEBUG] 短信登录响应原始数据:', data)
|
||||
saveUserData(data)
|
||||
|
||||
const isNew = data && data.is_new_user
|
||||
@ -301,18 +336,12 @@ function onGetPhoneNumber(e) {
|
||||
|
||||
saveUserData(data)
|
||||
|
||||
const openid = data && (data.openid || data.open_id)
|
||||
if (openid) uni.setStorageSync('openid', openid)
|
||||
|
||||
// 绑定手机号
|
||||
// 绑定手机号 (仅当后端反馈未绑定时调用)
|
||||
const isBound = data.phone || data.phone_number || data.mobile
|
||||
if (!isBound) {
|
||||
try {
|
||||
await bindPhone(data.user_id, phoneCode)
|
||||
uni.setStorageSync('phone_bound', true)
|
||||
} catch (e) {}
|
||||
} else {
|
||||
uni.setStorageSync('phone_bound', true)
|
||||
}
|
||||
|
||||
// 后台获取数据
|
||||
@ -334,6 +363,7 @@ function onGetPhoneNumber(e) {
|
||||
|
||||
function saveUserData(data) {
|
||||
if (!data) return
|
||||
console.log('[DEBUG] 准备执行 saveUserData, payload:', data)
|
||||
uni.setStorageSync('user_info', data)
|
||||
if (data.token) uni.setStorageSync('token', data.token)
|
||||
if (data.user_id) uni.setStorageSync('user_id', data.user_id)
|
||||
@ -341,6 +371,18 @@ function saveUserData(data) {
|
||||
if (data.nickname) uni.setStorageSync('nickname', data.nickname)
|
||||
if (data.invite_code) uni.setStorageSync('invite_code', data.invite_code)
|
||||
if (data.mobile) uni.setStorageSync('last_login_mobile', data.mobile)
|
||||
|
||||
// 核心修复:无论哪种登录方式,登录成功后都标记手机号已绑定
|
||||
uni.setStorageSync('phone_bound', true)
|
||||
|
||||
// 如果返回了 openid,则存储 (短信登录现在也会返回已关联的 openid)
|
||||
const openid = data.openid || data.open_id
|
||||
if (openid) {
|
||||
console.log('[DEBUG] 存储从登录接口获取的 openid:', openid)
|
||||
uni.setStorageSync('openid', openid)
|
||||
} else {
|
||||
console.warn('[DEBUG] 登录接口未返回 openid, 请检查后端或联系管理员')
|
||||
}
|
||||
}
|
||||
|
||||
function fetchExtraData(userId) {
|
||||
|
||||
@ -109,6 +109,7 @@ export function normalizeRewards(list, cleanUrl = (u) => u) {
|
||||
image: cleanUrl(i.product_image ?? i.image ?? i.img ?? i.pic ?? i.banner ?? ''),
|
||||
weight: Number(i.weight) || 0,
|
||||
boss: detectBoss(i),
|
||||
min_score: i.min_score || 0,
|
||||
level: levelToAlpha(i.prize_level ?? i.level ?? (detectBoss(i) ? 'BOSS' : '赏'))
|
||||
}))
|
||||
const total = items.reduce((acc, it) => acc + (it.weight > 0 ? it.weight : 0), 0)
|
||||
@ -119,7 +120,8 @@ export function normalizeRewards(list, cleanUrl = (u) => u) {
|
||||
percent: parseFloat(rawPercent.toFixed(2)) // 统一保留2位小数
|
||||
}
|
||||
})
|
||||
enriched.sort((a, b) => (b.percent - a.percent))
|
||||
// 移除前端按百分比强制排序逻辑,保留后端原始排序
|
||||
// enriched.sort((a, b) => (b.percent - a.percent))
|
||||
return enriched
|
||||
}
|
||||
|
||||
@ -150,13 +152,23 @@ export function pickLatestIssueId(list) {
|
||||
export function groupRewardsByLevel(rewards) {
|
||||
const groups = {}
|
||||
; (rewards || []).forEach(item => {
|
||||
const level = item.level || '赏'
|
||||
let level = item.level || '赏'
|
||||
// 如果是对对碰(具有 min_score 且不是 BOSS),则组名包含对子数
|
||||
if (item.min_score > 0 && level !== 'BOSS') {
|
||||
level = `${item.min_score}对子`
|
||||
}
|
||||
if (!groups[level]) groups[level] = []
|
||||
groups[level].push(item)
|
||||
})
|
||||
return Object.keys(groups).sort((a, b) => {
|
||||
if (a === 'BOSS') return -1
|
||||
if (b === 'BOSS') return 1
|
||||
if (a === 'Last' || a === 'BOSS') return -1
|
||||
if (b === 'Last' || b === 'BOSS') return 1
|
||||
// 数字对照组(比如 "9对子")需要按数字降序排列
|
||||
const numA = parseInt(a)
|
||||
const numB = parseInt(b)
|
||||
if (!isNaN(numA) && !isNaN(numB)) {
|
||||
return numB - numA
|
||||
}
|
||||
return a.localeCompare(b)
|
||||
}).map(key => {
|
||||
const levelRewards = groups[key]
|
||||
@ -164,7 +176,7 @@ export function groupRewardsByLevel(rewards) {
|
||||
return {
|
||||
level: key,
|
||||
rewards: levelRewards,
|
||||
totalPercent: total.toFixed(2) // 统一保留2位小数
|
||||
totalPercent: total.toFixed(2)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -55,10 +55,9 @@ export function authRequest(options) {
|
||||
const token = uni.getStorageSync('token')
|
||||
const base = buildDefaultHeaders()
|
||||
const header = { ...base, ...(options.header || {}) }
|
||||
// 不再添加 Bearer,直接原样透传 token
|
||||
// 设置Authorization头
|
||||
if (token) {
|
||||
header.Authorization = token
|
||||
header.authorization = token
|
||||
}
|
||||
return request({ ...options, header })
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user