Files
hejiu/pages/share-profile/share-profile.vue
T
cg dd3c670021 feat: add guest mode and optimize login experience
针对微信审核要求新增游客浏览模式,用户可无需登录直接浏览应用内容,仅在使用发布、点赞、记录等需要身份的功能时才弹窗引导登录。具体修改包括:
1. 新增游客态判断工具函数与登录守卫逻辑
2. 为登录页添加游客直接进入入口
3. 为首页、酒友圈、个人页、动态页、记录页添加游客适配逻辑
4. 优化登录态失效处理逻辑,不再强制跳转登录页
5. 调整部分页面的返回逻辑适配分享直接进入场景
6. 为各页面添加游客引导UI与交互
2026-09-10 21:43:46 +08:00

499 lines
12 KiB
Vue
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.
<template>
<view :class="themeClass" class="page-container share-page">
<!-- 氛围背景 -->
<view class="ambient">
<view class="glow glow-1"></view>
<view class="glow glow-2"></view>
</view>
<!-- 酒桌名片 -->
<view class="biz-card">
<!-- 品牌条 -->
<view class="card-brand">
<text class="brand-mark">🍻 碰盏日记</text>
<text class="brand-label">饮 酒 名 片</text>
</view>
<!-- 身份区 -->
<view class="identity">
<view class="id-avatar-wrap">
<image
v-if="info.avatar && !avatarError"
class="id-avatar"
:src="info.avatar"
mode="aspectFill"
@error="avatarError = true"
></image>
<view v-else class="id-avatar id-avatar-fallback">
<text class="id-avatar-emoji">🥃</text>
</view>
</view>
<text class="id-name">{{ info.nick }}</text>
<text class="id-slogan">这是我的饮酒档案,来交个酒友吧</text>
</view>
<!-- 票券撕边分割线 -->
<view class="perf-divider">
<view class="perf-hole perf-hole-l"></view>
<view class="perf-line"></view>
<view class="perf-hole perf-hole-r"></view>
</view>
<!-- 数据存根 -->
<view class="stats-stub">
<view class="stub-item">
<text class="stub-num text-num">{{ anim.days }}</text>
<text class="stub-label">打卡天数</text>
</view>
<view class="stub-sep"></view>
<view class="stub-item">
<text class="stub-num text-num">{{ anim.records }}</text>
<text class="stub-label">饮酒次数</text>
</view>
<view class="stub-sep"></view>
<view class="stub-item">
<text class="stub-num stub-num-amber text-num">{{ anim.cups }}</text>
<text class="stub-label">标准杯</text>
</view>
</view>
<!-- 连续记录 + 最爱酒类 -->
<view class="extra-row">
<view class="extra-chip">
<text class="extra-chip-emoji">{{ info.streakType === 'drank' ? '🔥' : '💪' }}</text>
<text class="extra-chip-text">{{ info.streakType === 'drank' ? '连续饮酒' : '连续戒酒' }} {{ info.streak }} 天</text>
</view>
<view v-if="favName" class="extra-chip">
<text class="extra-chip-emoji">{{ favEmoji }}</text>
<text class="extra-chip-text">最爱{{ favName }}</text>
</view>
</view>
<!-- CTA -->
<button class="cta-btn" @click="enterApp">
<text class="cta-text">我也想记 · 打开碰盏日记</text>
</button>
</view>
<text class="page-footer">让每一杯酒都有迹可循</text>
</view>
</template>
<script>
import themeMixin from '../../common/theme-mixin'
import { DRINK_CATEGORIES } from '../../common/constants'
export default {
mixins: [themeMixin],
data() {
return {
info: {
nick: '酒友',
avatar: '',
days: 0,
records: 0,
cups: 0,
streak: 0,
streakType: 'drank',
fav: ''
},
anim: { days: 0, records: 0, cups: 0 },
avatarError: false
}
},
computed: {
favCat() {
return DRINK_CATEGORIES.find(c => c.id === this.info.fav) || null
},
favName() {
return this.favCat ? this.favCat.name : ''
},
favEmoji() {
return this.favCat ? this.favCat.emoji : '🍶'
}
},
onLoad(options) {
this.parseOptions(options)
},
onReady() {
this.animateStats()
},
// 好友可继续转发这张名片(病毒式传播,数据保持原始分享者)
onShareAppMessage() {
return {
title: `${this.info.nick}的饮酒名片 - 碰盏日记`,
path: `/pages/share-profile/share-profile?${this.buildQuery()}`
}
},
onShareTimeline() {
return {
title: `${this.info.nick}的饮酒名片 - 碰盏日记`,
query: this.buildQuery()
}
},
methods: {
parseOptions(options) {
if (!options) return
try {
if (options.nick) this.info.nick = decodeURIComponent(options.nick)
if (options.avatar) this.info.avatar = decodeURIComponent(options.avatar)
this.info.days = parseInt(options.days) || 0
this.info.records = parseInt(options.records) || 0
this.info.cups = parseFloat(options.cups) || 0
this.info.streak = parseInt(options.streak) || 0
if (options.st) this.info.streakType = options.st === 'drank' ? 'drank' : 'abstain'
if (options.fav) this.info.fav = String(options.fav)
} catch (e) {
console.warn('名片参数解析失败:', e)
}
},
buildQuery() {
const parts = [`nick=${encodeURIComponent(this.info.nick)}`]
if (this.info.avatar) parts.push(`avatar=${encodeURIComponent(this.info.avatar)}`)
parts.push(`days=${this.info.days}`)
parts.push(`records=${this.info.records}`)
parts.push(`cups=${this.info.cups}`)
parts.push(`streak=${this.info.streak}`)
parts.push(`st=${this.info.streakType}`)
if (this.info.fav) parts.push(`fav=${this.info.fav}`)
return parts.join('&')
},
// 数字滚动动画
animateStats() {
const t = { days: this.info.days, records: this.info.records, cups: this.info.cups }
const hasDecimal = t.cups % 1 !== 0
const duration = 900
const start = Date.now()
const timer = setInterval(() => {
const p = Math.min((Date.now() - start) / duration, 1)
const ease = 1 - Math.pow(1 - p, 3)
this.anim.days = Math.round(t.days * ease)
this.anim.records = Math.round(t.records * ease)
this.anim.cups = hasDecimal
? Math.round(t.cups * ease * 10) / 10
: Math.round(t.cups * ease)
if (p >= 1) clearInterval(timer)
}, 16)
},
enterApp() {
const isLoggedIn = uni.getStorageSync('is_logged_in')
if (isLoggedIn === 'true') {
uni.switchTab({ url: '/pages/index/index' })
} else {
// 未登录以游客身份进入首页浏览,不强制跳登录页
uni.setStorageSync('is_first_launch', 'false')
uni.setStorageSync('is_guest', 'true')
uni.switchTab({
url: '/pages/index/index',
fail: () => uni.reLaunch({ url: '/pages/index/index' })
})
}
}
}
}
</script>
<style lang="scss" scoped>
.share-page {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: $sp-xl;
position: relative;
overflow: hidden;
}
/* 氛围光 */
.ambient {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
z-index: 0;
}
.glow {
position: absolute;
border-radius: 50%;
filter: blur(60rpx);
}
.glow-1 {
width: 480rpx;
height: 480rpx;
top: -120rpx;
right: -100rpx;
background: radial-gradient(circle, rgba(232,168,56,0.16) 0%, transparent 70%);
animation: glow-drift 6s ease-in-out infinite alternate;
}
.glow-2 {
width: 400rpx;
height: 400rpx;
bottom: -80rpx;
left: -120rpx;
background: radial-gradient(circle, rgba(139,133,184,0.12) 0%, transparent 70%);
animation: glow-drift 7s ease-in-out infinite alternate-reverse;
}
@keyframes glow-drift {
from { transform: translate(0, 0) scale(1); }
to { transform: translate(30rpx, 40rpx) scale(1.12); }
}
/* 名片主体 */
.biz-card {
width: 100%;
max-width: 620rpx;
position: relative;
z-index: 2;
background: linear-gradient(165deg, var(--card-gradient-start, #1A1510) 0%, var(--card-gradient-end, #0D0B08) 100%);
border: 1rpx solid rgba(232,168,56,0.28);
border-radius: $radius-xl;
padding: $sp-xl $sp-xl $sp-2xl;
box-shadow: $shadow-amber;
overflow: hidden;
animation: card-in 0.6s $ease-out both;
}
@keyframes card-in {
from {
opacity: 0;
transform: translateY(60rpx) scale(0.96);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* 品牌条 */
.card-brand {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: $sp-xl;
}
.brand-mark {
font-size: $fs-sm;
color: $text-secondary;
font-weight: $fw-medium;
}
.brand-label {
font-size: $fs-xs;
color: $amber;
letter-spacing: 4rpx;
font-weight: $fw-medium;
padding: 6rpx 16rpx;
border: 1rpx solid rgba(232,168,56,0.35);
border-radius: $radius-full;
}
/* 身份区 */
.identity {
display: flex;
flex-direction: column;
align-items: center;
padding: $sp-md 0 $sp-xl;
}
.id-avatar-wrap {
position: relative;
margin-bottom: $sp-lg;
}
.id-avatar-wrap::before {
content: '';
position: absolute;
top: -16rpx;
left: -16rpx;
right: -16rpx;
bottom: -16rpx;
border-radius: 50%;
background: radial-gradient(circle, rgba(232,168,56,0.22) 0%, transparent 70%);
filter: blur(12rpx);
}
.id-avatar {
width: 168rpx;
height: 168rpx;
border-radius: 50%;
border: 4rpx solid $amber;
position: relative;
z-index: 1;
}
.id-avatar-fallback {
background: $bg-elevated;
display: flex;
align-items: center;
justify-content: center;
}
.id-avatar-emoji {
font-size: 84rpx;
}
.id-name {
font-size: $fs-2xl;
font-weight: $fw-black;
color: $text-primary;
margin-bottom: $sp-sm;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.id-slogan {
font-size: $fs-sm;
color: $text-tertiary;
}
/* 票券撕边 */
.perf-divider {
position: relative;
display: flex;
align-items: center;
margin: 0 (-$sp-xl);
padding: 0 $sp-md;
}
.perf-line {
flex: 1;
border-top: 2rpx dashed rgba(232,168,56,0.3);
}
.perf-hole {
position: absolute;
width: 44rpx;
height: 44rpx;
border-radius: 50%;
background: $bg-base;
top: 50%;
transform: translateY(-50%);
}
.perf-hole-l {
left: -22rpx;
}
.perf-hole-r {
right: -22rpx;
}
/* 数据存根 */
.stats-stub {
display: flex;
align-items: center;
justify-content: space-between;
padding: $sp-xl $sp-md;
}
.stub-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: $sp-xs;
}
.stub-num {
font-size: $fs-3xl;
font-weight: $fw-black;
color: $text-primary;
line-height: $lh-tight;
}
.stub-num-amber {
color: $amber;
}
.stub-label {
font-size: $fs-xs;
color: $text-tertiary;
letter-spacing: 2rpx;
}
.stub-sep {
width: 1rpx;
height: 56rpx;
background: rgba(232,168,56,0.2);
}
/* 标签行 */
.extra-row {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: $sp-md;
padding: 0 $sp-md $sp-xl;
}
.extra-chip {
display: inline-flex;
align-items: center;
gap: $sp-xs;
padding: $sp-sm $sp-lg;
background: rgba(232,168,56,0.1);
border: 1rpx solid rgba(232,168,56,0.22);
border-radius: $radius-full;
}
.extra-chip-emoji {
font-size: $fs-base;
}
.extra-chip-text {
font-size: $fs-sm;
color: $text-secondary;
}
/* CTA */
.cta-btn {
margin: 0 $sp-md;
background: linear-gradient(135deg, $amber-light, $amber, $amber-deep);
border-radius: $radius-full;
padding: $sp-md 0;
border: none;
line-height: 1.4;
animation: cta-glow 2.4s ease-in-out infinite;
&::after {
border: none;
}
&:active {
transform: scale(0.97);
opacity: 0.9;
}
}
@keyframes cta-glow {
0%, 100% { box-shadow: 0 8rpx 28rpx rgba(232,168,56,0.28); }
50% { box-shadow: 0 8rpx 44rpx rgba(232,168,56,0.5); }
}
.cta-text {
font-size: $fs-lg;
font-weight: $fw-bold;
color: $text-on-amber;
}
/* 底部 */
.page-footer {
position: relative;
z-index: 2;
margin-top: $sp-2xl;
font-size: $fs-xs;
color: $text-tertiary;
letter-spacing: 4rpx;
animation: card-in 0.6s $ease-out 0.15s both;
}
</style>