feat: 支持游客免登录浏览核心页面
未登录用户可先浏览衣橱、穿搭、运势、分享等内容,登录改为用户主动选择,并在登录页增加协议勾选确认与暂不登录入口。
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { request, redirectToLogin } from '../../utils/request'
|
||||
import { request } from '../../utils/request'
|
||||
import * as auth from '../../utils/auth'
|
||||
import { PagedResult, ClosetShareItem, OutfitShareItem } from '../../types/models'
|
||||
import { ShareStatus, ShareType } from '../../utils/config'
|
||||
import { normalizePaged } from '../../utils/paged'
|
||||
import { setTabBarSelected } from '../../utils/tabbar'
|
||||
import { navScroll } from '../../utils/nav'
|
||||
import { GUEST_SHARES, goLogin } from '../../utils/guest'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
||||
// 日志模块名
|
||||
@@ -25,6 +26,20 @@ interface ShareListItem {
|
||||
_invalid: boolean
|
||||
}
|
||||
|
||||
// 游客态示例列表:与真实列表同构,复用同一套模板渲染,仅补「示例」标注
|
||||
function guestShareList(): ShareListItem[] {
|
||||
return GUEST_SHARES.map((it) => ({
|
||||
kind: it.kind,
|
||||
share_uuid: it.key,
|
||||
title: it.title,
|
||||
typeText: it.typeText,
|
||||
expireText: it.expireText,
|
||||
status: ShareStatus.NORMAL,
|
||||
created_at: 0,
|
||||
_invalid: false,
|
||||
}))
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
tabs: [
|
||||
@@ -40,16 +55,36 @@ Page({
|
||||
hasMore: true,
|
||||
// 滚动收缩:由 utils/nav.ts 的共享 handler 驱动
|
||||
navScrolled: false,
|
||||
// 游客态(未登录):展示本地示例分享,不发需要授权的请求
|
||||
guest: false,
|
||||
},
|
||||
|
||||
// scroll-view 滚动 → 导航收缩为玻璃小标题
|
||||
onNavScroll: navScroll,
|
||||
|
||||
onLoad() {
|
||||
// 首帧渲染前判定游客态:未登录直接渲染示例列表,避免首帧 setData 冲突
|
||||
if (!auth.isLoggedIn()) {
|
||||
this.setData({ guest: true, list: guestShareList(), loading: false, hasMore: false })
|
||||
logger.info(LOG, '未登录进入分享页,渲染游客态示例内容')
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.syncTabBar()
|
||||
// 未登录直接回登录页(幂等跳转),不再发出必然 401 的请求
|
||||
// 未登录:游客态浏览,零请求,不再强制跳登录(微信审核合规要求)
|
||||
if (!auth.isLoggedIn()) {
|
||||
redirectToLogin()
|
||||
if (!this.data.guest) {
|
||||
this.setData({ guest: true, list: guestShareList(), loading: false, hasMore: false })
|
||||
}
|
||||
return
|
||||
}
|
||||
// 登录成功回到本页:清掉示例数据,切回真实列表
|
||||
if (this.data.guest) {
|
||||
logger.info(LOG, '检测到登录态,从游客态切回真实分享列表')
|
||||
;(this as any).firstShown = true
|
||||
this.setData({ guest: false, list: [], loading: true, hasMore: true })
|
||||
this.refresh()
|
||||
return
|
||||
}
|
||||
// 首帧渲染未结束就 setData 会触发「Expected updated data but get first rendering data」,
|
||||
@@ -69,16 +104,28 @@ Page({
|
||||
onTabTap(e: WechatMiniprogram.TouchEvent) {
|
||||
const index = Number(e.currentTarget.dataset.index)
|
||||
if (index === this.data.activeTab) return
|
||||
// 游客态:分段仍可切换,示例内容用于体验浏览,不发请求
|
||||
if (this.data.guest) {
|
||||
this.setData({ activeTab: index })
|
||||
return
|
||||
}
|
||||
this.setData({ activeTab: index, list: [], loading: true })
|
||||
this.refresh()
|
||||
},
|
||||
|
||||
// 游客态点击示例分享 / 受限入口:统一进入登录页,由用户自行选择是否登录
|
||||
onGuestLogin() {
|
||||
logger.debug(LOG, '游客态点击受限入口,引导登录')
|
||||
goLogin()
|
||||
},
|
||||
|
||||
refresh() {
|
||||
this.setData({ page: 1, hasMore: true, loading: this.data.list.length === 0 })
|
||||
this.fetchList(1)
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
if (this.data.guest) return
|
||||
if (!this.data.hasMore || this.data.loading || this.data.loadingMore) return
|
||||
this.fetchList(this.data.page + 1)
|
||||
},
|
||||
@@ -216,6 +263,11 @@ Page({
|
||||
},
|
||||
|
||||
onCreate() {
|
||||
// 新建分享需登录:游客态先引导登录,不直接进入创建页
|
||||
if (this.data.guest) {
|
||||
this.onGuestLogin()
|
||||
return
|
||||
}
|
||||
wx.navigateTo({
|
||||
url: '/pages/closet-share-create/closet-share-create',
|
||||
fail: (e) => logger.warn(LOG, 'navigateTo 创建分享失败, err=', e && e.errMsg),
|
||||
@@ -223,6 +275,10 @@ Page({
|
||||
},
|
||||
|
||||
onItemTap(e: WechatMiniprogram.TouchEvent) {
|
||||
if (this.data.guest) {
|
||||
this.onGuestLogin()
|
||||
return
|
||||
}
|
||||
const uuid = e.currentTarget.dataset.uuid as string
|
||||
const kind = e.currentTarget.dataset.kind as string
|
||||
logger.debug(LOG, '点击分享项, kind=', kind, 'uuid=', uuid)
|
||||
|
||||
@@ -10,6 +10,14 @@
|
||||
<view class="display">分享</view>
|
||||
</view>
|
||||
|
||||
<!-- 游客态提示条:未登录也可先浏览,登录由用户自行选择 -->
|
||||
<view wx:if="{{guest}}" class="guest-bar" bindtap="onGuestLogin">
|
||||
<view class="icon icon-lock"></view>
|
||||
<text class="gb-t">浏览模式 · 以下为示例分享</text>
|
||||
<text class="gb-a">登录</text>
|
||||
<view class="icon icon-chevron-right"></view>
|
||||
</view>
|
||||
|
||||
<!-- 下划线式文字分段 -->
|
||||
<view class="segbar">
|
||||
<view class="seg">
|
||||
@@ -24,7 +32,7 @@
|
||||
</view>
|
||||
|
||||
<empty-state
|
||||
wx:if="{{!loading && !list.length}}"
|
||||
wx:if="{{!guest && !loading && !list.length}}"
|
||||
icon="icon-share"
|
||||
title="{{activeTab === 0 ? '还没有分享' : '还没有收到分享'}}"
|
||||
desc="{{activeTab === 0 ? '把衣橱、精选衣物或穿搭分享给好友' : '好友分享给你的衣橱或穿搭会出现在这里'}}"
|
||||
@@ -65,6 +73,7 @@
|
||||
</view>
|
||||
<view class="cs-t">{{item.title}}</view>
|
||||
<view class="cs-tags">
|
||||
<text wx:if="{{guest}}" class="pill">示例</text>
|
||||
<text class="pill">{{item.typeText}}</text>
|
||||
<text wx:if="{{item._invalid}}" class="pill pill--line">{{item.expireText}}</text>
|
||||
<text wx:else class="cs-exp">{{item.expireText}}</text>
|
||||
@@ -73,7 +82,11 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{list.length && !hasMore}}" class="foot-note">没有更多了</view>
|
||||
<view wx:if="{{!guest && list.length && !hasMore}}" class="foot-note">没有更多了</view>
|
||||
<view wx:if="{{guest}}" class="guest-foot" bindtap="onGuestLogin">
|
||||
<text>登录后可创建分享,并查看好友分享给你的内容</text>
|
||||
<text class="gf-a">去登录</text>
|
||||
</view>
|
||||
<view class="gap-64"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { request, redirectToLogin } from '../../utils/request'
|
||||
import { request } from '../../utils/request'
|
||||
import * as auth from '../../utils/auth'
|
||||
import { HoroscopeData } from '../../types/models'
|
||||
import { setTabBarSelected } from '../../utils/tabbar'
|
||||
import { navScroll } from '../../utils/nav'
|
||||
import { guestHoroscope, goLogin } from '../../utils/guest'
|
||||
import { logger } from '../../utils/logger'
|
||||
import { STORAGE_KEYS } from '../../utils/config'
|
||||
|
||||
@@ -41,16 +42,52 @@ Page({
|
||||
errorText: '',
|
||||
// 滚动收缩:由 utils/nav.ts 的共享 handler 驱动
|
||||
navScrolled: false,
|
||||
// 游客态(未登录):展示本地示例运势,不发需要授权的请求
|
||||
guest: false,
|
||||
},
|
||||
|
||||
// scroll-view 滚动 → 导航收缩为玻璃小标题
|
||||
onNavScroll: navScroll,
|
||||
|
||||
onLoad() {
|
||||
// 首帧渲染前判定游客态:未登录直接用本地示例运势渲染,避免首帧 setData 冲突
|
||||
if (!auth.isLoggedIn()) {
|
||||
const demo = guestHoroscope()
|
||||
this.setData({
|
||||
guest: true,
|
||||
horoscope: demo,
|
||||
statDate: this.formatStatDate(demo.stat_date),
|
||||
luckyColorHex: COLOR_MAP[demo.lucky_color] || '#AEAEB2',
|
||||
loading: false,
|
||||
})
|
||||
logger.info(LOG, '未登录进入运势页,渲染游客态示例运势')
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.syncTabBar()
|
||||
// 未登录直接回登录页(幂等跳转),不再发出必然 401 的请求
|
||||
// 未登录:游客态浏览,零请求,不再强制跳登录(微信审核合规要求)
|
||||
if (!auth.isLoggedIn()) {
|
||||
redirectToLogin()
|
||||
if (!this.data.guest) {
|
||||
const demo = guestHoroscope()
|
||||
this.setData({
|
||||
guest: true,
|
||||
horoscope: demo,
|
||||
statDate: this.formatStatDate(demo.stat_date),
|
||||
luckyColorHex: COLOR_MAP[demo.lucky_color] || '#AEAEB2',
|
||||
loading: false,
|
||||
noBirth: false,
|
||||
error: false,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
// 登录成功回到本页:清掉示例运势,拉取真实数据
|
||||
if (this.data.guest) {
|
||||
logger.info(LOG, '检测到登录态,从游客态切回真实运势')
|
||||
;(this as any).firstShown = true
|
||||
this.setData({ guest: false, horoscope: null, loading: true })
|
||||
this.fetchHoroscope(0)
|
||||
return
|
||||
}
|
||||
// 首帧渲染未结束就 setData 会触发「Expected updated data but get first rendering data」,
|
||||
@@ -116,15 +153,30 @@ Page({
|
||||
},
|
||||
|
||||
onRetry() {
|
||||
if (this.data.guest) {
|
||||
this.onGuestLogin()
|
||||
return
|
||||
}
|
||||
this.fetchHoroscope(0)
|
||||
},
|
||||
|
||||
// 游客态点击受限入口(设置生日 / 生成穿搭):统一进入登录页
|
||||
onGuestLogin() {
|
||||
logger.debug(LOG, '游客态点击受限入口,引导登录')
|
||||
goLogin()
|
||||
},
|
||||
|
||||
formatStatDate(d: number): string {
|
||||
const s = String(d)
|
||||
return `${s.slice(0, 4)}.${s.slice(4, 6)}.${s.slice(6, 8)}`
|
||||
},
|
||||
|
||||
onGoProfile() {
|
||||
// 设置生日需登录:游客态先引导登录,不做强制跳转
|
||||
if (!auth.isLoggedIn()) {
|
||||
this.onGuestLogin()
|
||||
return
|
||||
}
|
||||
// switchTab 不支持 query 传参,用 storage 标记让「我的」页 onShow 自动弹出编辑弹窗
|
||||
wx.setStorageSync(STORAGE_KEYS.OPEN_PROFILE_EDIT, 1)
|
||||
wx.switchTab({
|
||||
@@ -138,6 +190,10 @@ Page({
|
||||
},
|
||||
|
||||
onGenerate() {
|
||||
if (this.data.guest) {
|
||||
this.onGuestLogin()
|
||||
return
|
||||
}
|
||||
wx.navigateTo({
|
||||
url: '/pages/ai-recommend/ai-recommend',
|
||||
fail: (e) => logger.warn(LOG, 'navigateTo AI 推荐失败, err=', e && e.errMsg),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<view class="page page-flex">
|
||||
<navigation-bar title="每日运势" back="{{false}}" scrolled="{{navScrolled}}">
|
||||
<view slot="right" class="nav-action" bindtap="onGoProfile">去设置生日</view>
|
||||
<view wx:if="{{guest}}" slot="right" class="nav-action" bindtap="onGuestLogin">登录</view>
|
||||
<view wx:else slot="right" class="nav-action" bindtap="onGoProfile">去设置生日</view>
|
||||
</navigation-bar>
|
||||
|
||||
<scroll-view scroll-y class="scroll-body pb-tab" bindscroll="onNavScroll">
|
||||
@@ -10,6 +11,14 @@
|
||||
<view class="display">每日运势</view>
|
||||
</view>
|
||||
|
||||
<!-- 游客态提示条:未登录也可先浏览,登录由用户自行选择 -->
|
||||
<view wx:if="{{guest}}" class="guest-bar" bindtap="onGuestLogin">
|
||||
<view class="icon icon-lock"></view>
|
||||
<text class="gb-t">浏览模式 · 以下为示例运势</text>
|
||||
<text class="gb-a">登录</text>
|
||||
<view class="icon icon-chevron-right"></view>
|
||||
</view>
|
||||
|
||||
<!-- 骨架屏 -->
|
||||
<view wx:if="{{loading}}" class="hs-skel">
|
||||
<view class="sk sk-ln" style="width: 38%; height: 28rpx;"></view>
|
||||
@@ -46,6 +55,7 @@
|
||||
<view class="hs-zodiac">
|
||||
<view class="icon icon-sparkles"></view>
|
||||
<text class="t-xl">{{horoscope.zodiac}}</text>
|
||||
<text wx:if="{{guest}}" class="pill">示例</text>
|
||||
</view>
|
||||
<text class="hs-date num">{{statDate}}</text>
|
||||
</view>
|
||||
@@ -136,6 +146,11 @@
|
||||
<view class="hs-foot">
|
||||
<view class="btn btn--ghost btn--block" bindtap="onGenerate">生成今日推荐穿搭</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{guest}}" class="guest-foot" bindtap="onGuestLogin">
|
||||
<text>登录并设置生日,查看你的专属运势</text>
|
||||
<text class="gf-a">去登录</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<view class="gap-64"></view>
|
||||
|
||||
@@ -27,11 +27,27 @@ Page({
|
||||
{ text: '稍后再说', value: '' },
|
||||
{ text: '去完善', value: 'ok', type: 'main' },
|
||||
],
|
||||
// 协议同意:默认不勾选,由用户自主阅读后自行选择(微信审核合规要求)
|
||||
agreed: false,
|
||||
consentVisible: false,
|
||||
consentButtons: [
|
||||
{ text: '不同意', value: '' },
|
||||
{ text: '同意并继续', value: 'ok', type: 'main' },
|
||||
],
|
||||
},
|
||||
|
||||
onLoad(query: Record<string, string>) {
|
||||
logger.debug(LOG, 'onLoad, query=', JSON.stringify(query), 'isLoggedIn=', auth.isLoggedIn())
|
||||
if (query.redirect) this.setData({ redirect: query.redirect })
|
||||
if (query.redirect) {
|
||||
// goLogin 侧对 redirect 做了 encodeURIComponent,这里解码还原(decode 失败时用原值兜底)
|
||||
let redirect = query.redirect
|
||||
try {
|
||||
redirect = decodeURIComponent(redirect)
|
||||
} catch (e) {
|
||||
logger.warn(LOG, 'redirect 解码失败,使用原值, err=', e)
|
||||
}
|
||||
this.setData({ redirect })
|
||||
}
|
||||
if (auth.isLoggedIn()) {
|
||||
this.goAfterLogin()
|
||||
}
|
||||
@@ -57,11 +73,54 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
// 勾选 / 取消勾选协议:完全由用户操作驱动,不存在默认勾选
|
||||
onToggleAgree() {
|
||||
const agreed = !this.data.agreed
|
||||
logger.debug(LOG, '协议勾选状态切换, agreed=', agreed)
|
||||
this.setData({ agreed })
|
||||
},
|
||||
|
||||
// 未勾选协议点击登录:弹窗二次确认,由用户主动选择是否同意
|
||||
onLogin() {
|
||||
if (this.data.loading) {
|
||||
logger.debug(LOG, '登录中,忽略重复点击')
|
||||
return
|
||||
}
|
||||
if (!this.data.agreed) {
|
||||
logger.info(LOG, '未勾选协议,弹出确认弹窗')
|
||||
this.setData({ consentVisible: true })
|
||||
return
|
||||
}
|
||||
this.doLogin()
|
||||
},
|
||||
|
||||
onConsentCancel() {
|
||||
logger.debug(LOG, '用户未同意协议,停留在登录页')
|
||||
this.setData({ consentVisible: false })
|
||||
},
|
||||
|
||||
onConsentConfirm(e: WechatMiniprogram.CustomEvent) {
|
||||
this.setData({ consentVisible: false })
|
||||
// 弹窗主按钮才代表同意;其余分支(不同意)只关闭弹窗,用户仍可浏览
|
||||
if (e.detail.value !== 'ok') {
|
||||
logger.info(LOG, '用户选择不同意协议')
|
||||
return
|
||||
}
|
||||
logger.info(LOG, '用户同意协议,继续登录')
|
||||
this.setData({ agreed: true })
|
||||
this.doLogin()
|
||||
},
|
||||
|
||||
// 暂不登录:回到衣橱首页继续浏览(游客态),不产生任何授权请求
|
||||
onBrowse() {
|
||||
logger.info(LOG, '用户选择先逛逛,返回衣橱继续浏览')
|
||||
wx.switchTab({
|
||||
url: '/pages/wardrobe/wardrobe',
|
||||
fail: (e) => logger.warn(LOG, 'switchTab 衣橱失败, err=', e && e.errMsg),
|
||||
})
|
||||
},
|
||||
|
||||
doLogin() {
|
||||
logger.debug(LOG, '点击登录, redirect=', this.data.redirect)
|
||||
this.setData({ loading: true })
|
||||
|
||||
|
||||
@@ -23,12 +23,22 @@
|
||||
<view class="wechat-icon"></view>
|
||||
<text>{{loading ? '登录中…' : '微信一键登录'}}</text>
|
||||
</view>
|
||||
<view class="lg-terms">
|
||||
<text>登录即代表同意</text>
|
||||
<text class="link" data-type="user" bindtap="openAgreement">《用户协议》</text>
|
||||
<text>和</text>
|
||||
<text class="link" data-type="privacy" bindtap="openAgreement">《隐私政策》</text>
|
||||
|
||||
<!-- 协议同意:默认不勾选,由用户阅读后自行选择是否同意 -->
|
||||
<view class="lg-consent">
|
||||
<view class="lg-check {{agreed ? 'is-on' : ''}}" bindtap="onToggleAgree">
|
||||
<view class="icon icon-check"></view>
|
||||
</view>
|
||||
<view class="lg-consent-t" bindtap="onToggleAgree">
|
||||
<text>我已阅读并同意</text>
|
||||
<text class="link" data-type="user" catchtap="openAgreement">《用户协议》</text>
|
||||
<text>和</text>
|
||||
<text class="link" data-type="privacy" catchtap="openAgreement">《隐私政策》</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 暂不登录:返回衣橱继续浏览,不做任何授权 -->
|
||||
<view class="lg-browse" bindtap="onBrowse">暂不登录,先逛逛</view>
|
||||
</view>
|
||||
|
||||
<!-- 新手礼包:居中玻璃卡 -->
|
||||
@@ -65,4 +75,15 @@
|
||||
bind:confirm="onBirthConfirm"
|
||||
bind:cancel="onBirthCancel"
|
||||
/>
|
||||
|
||||
<!-- 未勾选协议的二次确认:是否同意由用户自行选择 -->
|
||||
<v2-overlay
|
||||
visible="{{consentVisible}}"
|
||||
type="modal"
|
||||
title="请先阅读并同意协议"
|
||||
content="登录前请阅读《用户协议》和《隐私政策》,同意后我们才会为你创建账号并保存衣橱数据。"
|
||||
buttons="{{consentButtons}}"
|
||||
bind:confirm="onConsentConfirm"
|
||||
bind:cancel="onConsentCancel"
|
||||
/>
|
||||
</view>
|
||||
|
||||
@@ -81,15 +81,48 @@
|
||||
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M9.5 3C5.36 3 2 5.69 2 9c0 1.87 1.03 3.54 2.62 4.65L4 16.5l3.27-1.72c.73.2 1.47.3 2.23.3h.5A6.2 6.2 0 0 1 9 12c0-3.3 3.36-6 7.5-6h.5A5.7 5.7 0 0 0 9.5 3z'/%3E%3Cpath d='M22 12c0-2.76-2.69-5-6-5s-6 2.24-6 5 2.69 5 6 5c.72 0 1.42-.1 2.06-.28L20.5 18l-.42-2.6c1.17-.88 1.92-2.06 1.92-3.4z'/%3E%3C/svg%3E") no-repeat center / 100% 100%;
|
||||
}
|
||||
|
||||
.lg-terms {
|
||||
margin-top: 28rpx;
|
||||
text-align: center;
|
||||
/* 协议同意行:默认未勾选(空心细描边),勾选后为墨色实底 + 白色对勾 */
|
||||
.lg-consent {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16rpx;
|
||||
margin-top: 30rpx;
|
||||
padding: 0 4rpx;
|
||||
}
|
||||
|
||||
.lg-check {
|
||||
width: 34rpx;
|
||||
height: 34rpx;
|
||||
margin-top: 5rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 8rpx;
|
||||
box-shadow: inset 0 0 0 1rpx var(--ink-4);
|
||||
color: transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background .18s var(--ease), box-shadow .18s var(--ease), color .18s var(--ease);
|
||||
}
|
||||
|
||||
.lg-check .icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
.lg-check.is-on {
|
||||
background: var(--ink);
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.lg-consent-t {
|
||||
flex: 1;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.75;
|
||||
color: var(--ink-3);
|
||||
}
|
||||
|
||||
.lg-terms .link {
|
||||
.lg-consent-t .link {
|
||||
color: var(--ink-2);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 6rpx;
|
||||
@@ -97,6 +130,23 @@
|
||||
text-decoration-color: var(--ink-4);
|
||||
}
|
||||
|
||||
/* 暂不登录:细描边文字按钮,与主按钮形成主次 */
|
||||
.lg-browse {
|
||||
margin-top: 28rpx;
|
||||
height: 84rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 26rpx;
|
||||
color: var(--ink-2);
|
||||
letter-spacing: .02em;
|
||||
transition: opacity .18s var(--ease);
|
||||
}
|
||||
|
||||
.lg-browse:active {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
/* 新手礼包 */
|
||||
.lg-gift-ic {
|
||||
width: 88rpx;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { request, redirectToLogin } from '../../utils/request'
|
||||
import { request } from '../../utils/request'
|
||||
import * as auth from '../../utils/auth'
|
||||
import { PagedResult, OutfitItem, CommunityOutfitItem } from '../../types/models'
|
||||
import { OutfitSource, STORAGE_KEYS } from '../../utils/config'
|
||||
@@ -8,6 +8,7 @@ import { OutfitStatusPill, isPendingSave, outfitStatusPill } from '../../utils/o
|
||||
import { setTabBarSelected } from '../../utils/tabbar'
|
||||
import { formatRelativeTime } from '../../utils/format'
|
||||
import { navScroll } from '../../utils/nav'
|
||||
import { GUEST_OUTFITS, goLogin } from '../../utils/guest'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
||||
// 日志模块名
|
||||
@@ -44,16 +45,36 @@ Page({
|
||||
pendingCount: 0,
|
||||
// 滚动收缩:由 utils/nav.ts 的共享 handler 驱动
|
||||
navScrolled: false,
|
||||
// 游客态(未登录):展示本地示例穿搭,不发需要授权的请求
|
||||
guest: false,
|
||||
guestList: GUEST_OUTFITS,
|
||||
},
|
||||
|
||||
// scroll-view 滚动 → 导航收缩为玻璃小标题
|
||||
onNavScroll: navScroll,
|
||||
|
||||
onLoad() {
|
||||
// 首帧渲染前判定游客态,避免首帧 setData 冲突
|
||||
if (!auth.isLoggedIn()) {
|
||||
this.setData({ guest: true, loading: false })
|
||||
logger.info(LOG, '未登录进入穿搭页,渲染游客态示例内容')
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.syncTabBar()
|
||||
// 未登录直接回登录页(幂等跳转),不再发出必然 401 的请求
|
||||
// 未登录:游客态浏览,零请求,不再强制跳登录(微信审核合规要求)
|
||||
if (!auth.isLoggedIn()) {
|
||||
redirectToLogin()
|
||||
if (!this.data.guest) this.setData({ guest: true, list: [], loading: false })
|
||||
return
|
||||
}
|
||||
// 登录成功回到本页:清空示例数据,切回真实列表
|
||||
if (this.data.guest) {
|
||||
logger.info(LOG, '检测到登录态,从游客态切换为真实穿搭列表')
|
||||
;(this as any).firstShown = true
|
||||
this.setData({ guest: false, guestList: [], list: [], loading: true })
|
||||
this.refresh()
|
||||
this.fetchPendingCount()
|
||||
return
|
||||
}
|
||||
const openCollect = wx.getStorageSync(STORAGE_KEYS.OPEN_COLLECT)
|
||||
@@ -86,16 +107,32 @@ Page({
|
||||
onTabChange(e: WechatMiniprogram.TouchEvent) {
|
||||
const index = Number(e.currentTarget.dataset.index)
|
||||
if (index === this.data.activeTab) return
|
||||
// 游客态:分段仍可切换,示例内容用于体验浏览,不发请求
|
||||
if (this.data.guest) {
|
||||
this.setData({ activeTab: index })
|
||||
return
|
||||
}
|
||||
this.setData({ activeTab: index, list: [], loading: true })
|
||||
this.refresh()
|
||||
},
|
||||
|
||||
// 游客态点击示例穿搭 / 受限入口:统一进入登录页,由用户自行选择是否登录
|
||||
onGuestLogin() {
|
||||
logger.debug(LOG, '游客态点击受限入口,引导登录')
|
||||
goLogin()
|
||||
},
|
||||
|
||||
refresh() {
|
||||
this.setData({ page: 1, hasMore: true, loading: this.data.list.length === 0 })
|
||||
this.fetchList(1)
|
||||
},
|
||||
|
||||
onRefresh() {
|
||||
// 游客态没有服务端数据可刷新,直接收起下拉动画
|
||||
if (this.data.guest) {
|
||||
this.setData({ refreshing: false })
|
||||
return
|
||||
}
|
||||
this.setData({ refreshing: true })
|
||||
Promise.all([this.fetchList(1), this.fetchPendingCount()]).finally(() =>
|
||||
this.setData({ refreshing: false })
|
||||
@@ -103,6 +140,7 @@ Page({
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
if (this.data.guest) return
|
||||
if (!this.data.hasMore || this.data.loading || this.data.loadingMore) return
|
||||
this.fetchList(this.data.page + 1)
|
||||
},
|
||||
@@ -192,6 +230,10 @@ Page({
|
||||
},
|
||||
|
||||
onCreate() {
|
||||
if (this.data.guest) {
|
||||
this.onGuestLogin()
|
||||
return
|
||||
}
|
||||
wx.navigateTo({
|
||||
url: '/pages/outfit-edit/outfit-edit',
|
||||
fail: (e) => logger.warn(LOG, 'navigateTo 新建穿搭失败, err=', e && e.errMsg),
|
||||
@@ -199,6 +241,10 @@ Page({
|
||||
},
|
||||
|
||||
onAiRecommend() {
|
||||
if (this.data.guest) {
|
||||
this.onGuestLogin()
|
||||
return
|
||||
}
|
||||
wx.navigateTo({
|
||||
url: '/pages/ai-recommend/ai-recommend',
|
||||
fail: (e) => logger.warn(LOG, 'navigateTo AI 推荐失败, err=', e && e.errMsg),
|
||||
@@ -207,6 +253,10 @@ Page({
|
||||
|
||||
// 进入 AI 穿搭页:AI 生成结果的统一管理入口(待保存 / 已保存)
|
||||
onAiPending() {
|
||||
if (this.data.guest) {
|
||||
this.onGuestLogin()
|
||||
return
|
||||
}
|
||||
wx.navigateTo({
|
||||
url: '/pages/ai-pending/ai-pending',
|
||||
fail: (e) => logger.warn(LOG, 'navigateTo AI 穿搭失败, err=', e && e.errMsg),
|
||||
@@ -214,6 +264,10 @@ Page({
|
||||
},
|
||||
|
||||
onOutfitTap(e: WechatMiniprogram.TouchEvent) {
|
||||
if (this.data.guest) {
|
||||
this.onGuestLogin()
|
||||
return
|
||||
}
|
||||
const uuid = e.currentTarget.dataset.uuid as string
|
||||
const owner = this.data.activeTab === 0 ? '1' : '0'
|
||||
logger.debug(LOG, '点击穿搭, uuid=', uuid, 'owner=', owner)
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
<view class="display">穿搭</view>
|
||||
</view>
|
||||
|
||||
<!-- 游客态提示条:未登录也可先浏览,登录由用户自行选择 -->
|
||||
<view wx:if="{{guest}}" class="guest-bar" bindtap="onGuestLogin">
|
||||
<view class="icon icon-lock"></view>
|
||||
<text class="gb-t">浏览模式 · 以下为示例穿搭</text>
|
||||
<text class="gb-a">登录</text>
|
||||
<view class="icon icon-chevron-right"></view>
|
||||
</view>
|
||||
|
||||
<!-- 下划线式文字分段 -->
|
||||
<view class="segbar">
|
||||
<view class="seg">
|
||||
@@ -32,12 +40,12 @@
|
||||
<view class="ae-t">AI 穿搭</view>
|
||||
<view class="ae-s">AI 生成的搭配在这里保存与管理</view>
|
||||
</view>
|
||||
<text wx:if="{{pendingCount > 0}}" class="pill pill--ink">{{pendingCount}} 套待保存</text>
|
||||
<text wx:if="{{!guest && pendingCount > 0}}" class="pill pill--ink">{{pendingCount}} 套待保存</text>
|
||||
<view class="icon icon-chevron-right ae-ch"></view>
|
||||
</view>
|
||||
|
||||
<!-- 骨架屏 -->
|
||||
<view wx:if="{{loading}}" class="of-skel">
|
||||
<view wx:if="{{!guest && loading}}" class="of-skel">
|
||||
<view class="of-skel-item" wx:for="{{[1, 2]}}" wx:key="*this">
|
||||
<view class="sk sk-img sk-cover"></view>
|
||||
<view class="sk sk-ln" style="width: 44%;"></view>
|
||||
@@ -47,7 +55,7 @@
|
||||
|
||||
<!-- 空态 -->
|
||||
<empty-state
|
||||
wx:elif="{{!list.length}}"
|
||||
wx:elif="{{!guest && !list.length}}"
|
||||
icon="icon-shirt"
|
||||
title="{{activeTab === 0 ? '还没有穿搭' : activeTab === 1 ? '还没有收藏的穿搭' : '还没有公开的穿搭'}}"
|
||||
desc="{{activeTab === 0 ? '从衣橱挑几件,搭一套吧' : activeTab === 1 ? '在社区或 AI 推荐中点亮小星星,喜欢的穿搭会出现在这里' : '把喜欢的穿搭公开到社区,让大家看到你的搭配'}}"
|
||||
@@ -55,6 +63,31 @@
|
||||
bind:action="onCreate"
|
||||
/>
|
||||
|
||||
<!-- 游客态:本地示例穿搭(点击任一示例进入登录页) -->
|
||||
<view wx:elif="{{guest}}" class="of-list">
|
||||
<view class="of-card" wx:for="{{guestList}}" wx:key="key" bindtap="onGuestLogin">
|
||||
<view class="collage collage--{{item.icons.length}}">
|
||||
<view class="cg" wx:for="{{item.icons}}" wx:for-item="ic" wx:key="*this">
|
||||
<view class="ph icon {{ic}}"></view>
|
||||
</view>
|
||||
<view class="guest-flag">示例</view>
|
||||
</view>
|
||||
<view class="of-trow">
|
||||
<text class="of-title">{{item.name}}</text>
|
||||
<text class="pill pill--line">{{item.scene}}</text>
|
||||
</view>
|
||||
<view class="of-desc">{{item.desc}}</view>
|
||||
<view class="of-meta">
|
||||
<text class="of-m">{{item.icons.length}} 件衣物</text>
|
||||
<view class="spacer"></view>
|
||||
<view class="of-m">
|
||||
<view class="icon icon-heart"></view>
|
||||
<text class="num">-</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片流 -->
|
||||
<view wx:else class="of-list">
|
||||
<view
|
||||
@@ -105,7 +138,11 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{list.length && !hasMore}}" class="foot-note">没有更多了</view>
|
||||
<view wx:if="{{!guest && list.length && !hasMore}}" class="foot-note">没有更多了</view>
|
||||
<view wx:if="{{guest}}" class="guest-foot" bindtap="onGuestLogin">
|
||||
<text>登录后查看你的专属穿搭与收藏</text>
|
||||
<text class="gf-a">去登录</text>
|
||||
</view>
|
||||
<view class="gap-64"></view>
|
||||
</scroll-view>
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { request, uploadFile, redirectToLogin } from '../../utils/request'
|
||||
import { request, uploadFile } from '../../utils/request'
|
||||
import * as auth from '../../utils/auth'
|
||||
import { UserInfo, QuotaItem } from '../../types/models'
|
||||
import { QuotaType, QUOTA_NAMES, VIP_TYPE_NAMES, STORAGE_KEYS } from '../../utils/config'
|
||||
import { formatDate } from '../../utils/format'
|
||||
import { setTabBarSelected, setTabBarUnread, setTabBarHidden } from '../../utils/tabbar'
|
||||
import { navScroll } from '../../utils/nav'
|
||||
import { goLogin } from '../../utils/guest'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
||||
// 日志模块名
|
||||
@@ -51,6 +52,8 @@ Page({
|
||||
quotaStats: [] as Array<{ key: number; label: string; remain: number; total: number }>,
|
||||
// 滚动收缩:由 utils/nav.ts 的共享 handler 驱动
|
||||
navScrolled: false,
|
||||
// 游客态(未登录):展示资料引导卡,只读入口仍可访问,不发请求
|
||||
guest: false,
|
||||
},
|
||||
|
||||
// scroll-view 滚动 → 导航收缩为玻璃小标题
|
||||
@@ -58,6 +61,11 @@ Page({
|
||||
|
||||
onLoad() {
|
||||
this.setToday()
|
||||
// 首帧渲染前判定游客态,避免首帧 setData 冲突
|
||||
if (!auth.isLoggedIn()) {
|
||||
this.setData({ guest: true })
|
||||
logger.info(LOG, '未登录进入我的页,渲染游客态资料卡')
|
||||
}
|
||||
},
|
||||
|
||||
// 跨零点后回到本页时,生日选择器上限需重算,不能停留在昨天
|
||||
@@ -72,17 +80,46 @@ Page({
|
||||
this.setToday()
|
||||
// 回到本页时按抽屉状态校准标签栏,防止切前后台/切 tab 后残留隐藏
|
||||
setTabBarHidden(!!this.data.editVisible)
|
||||
// 未登录直接回登录页(幂等跳转),不再发出必然 401 的请求
|
||||
// 未登录:游客态展示引导卡,零请求,不再强制跳登录(微信审核合规要求)
|
||||
if (!auth.isLoggedIn()) {
|
||||
redirectToLogin()
|
||||
if (!this.data.guest) {
|
||||
// 退出登录后回到本页时清掉残留资料,避免展示上一账号信息
|
||||
this.setData({
|
||||
guest: true,
|
||||
userInfo: {} as UserInfo,
|
||||
vipLabel: '',
|
||||
zodiac: '',
|
||||
quotaStats: [],
|
||||
uploadRemain: 0,
|
||||
unread: 0,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
// 登录成功回到本页:清掉游客态,拉取真实资料
|
||||
if (this.data.guest) {
|
||||
logger.info(LOG, '检测到登录态,从游客态切回真实资料')
|
||||
this.setData({ guest: false })
|
||||
}
|
||||
this.consumeOpenEditMark()
|
||||
this.fetchUser()
|
||||
this.fetchQuota()
|
||||
this.fetchUnread()
|
||||
},
|
||||
|
||||
// 未登录时的统一拦截:进入登录页,由用户自行选择是否登录
|
||||
needLogin(): boolean {
|
||||
if (auth.isLoggedIn()) return false
|
||||
logger.debug(LOG, '游客态点击受限入口,引导登录')
|
||||
goLogin()
|
||||
return true
|
||||
},
|
||||
|
||||
// 游客态点击资料卡 / 登录引导
|
||||
onGuestLogin() {
|
||||
this.needLogin()
|
||||
},
|
||||
|
||||
// 离开本页时恢复标签栏:此时抽屉不再可见,标签栏不能残留隐藏
|
||||
onHide() {
|
||||
setTabBarHidden(false)
|
||||
@@ -172,6 +209,7 @@ Page({
|
||||
},
|
||||
|
||||
onEdit() {
|
||||
if (this.needLogin()) return
|
||||
const u = this.data.userInfo
|
||||
// 抽屉打开期间隐藏悬浮标签栏:tabBar 由框架渲染在页面之上,否则会压住抽屉底部按钮
|
||||
setTabBarHidden(true)
|
||||
@@ -284,24 +322,28 @@ Page({
|
||||
},
|
||||
|
||||
onTapMall() {
|
||||
if (this.needLogin()) return
|
||||
wx.navigateTo({
|
||||
url: '/pages/mall/mall',
|
||||
fail: (e) => logger.warn(LOG, 'navigateTo 商城失败, err=', e && e.errMsg),
|
||||
})
|
||||
},
|
||||
onTapOrder() {
|
||||
if (this.needLogin()) return
|
||||
wx.navigateTo({
|
||||
url: '/pages/order/order',
|
||||
fail: (e) => logger.warn(LOG, 'navigateTo 订单失败, err=', e && e.errMsg),
|
||||
})
|
||||
},
|
||||
onTapNotification() {
|
||||
if (this.needLogin()) return
|
||||
wx.navigateTo({
|
||||
url: '/pages/notification/notification',
|
||||
fail: (e) => logger.warn(LOG, 'navigateTo 通知失败, err=', e && e.errMsg),
|
||||
})
|
||||
},
|
||||
onTapCollect() {
|
||||
if (this.needLogin()) return
|
||||
wx.setStorageSync(STORAGE_KEYS.OPEN_COLLECT, 1)
|
||||
wx.switchTab({
|
||||
url: '/pages/outfit/outfit',
|
||||
@@ -309,6 +351,7 @@ Page({
|
||||
})
|
||||
},
|
||||
onTapFlow() {
|
||||
if (this.needLogin()) return
|
||||
wx.navigateTo({
|
||||
url: '/pages/mall/mall?tab=flow',
|
||||
fail: (e) => logger.warn(LOG, 'navigateTo 流水失败, err=', e && e.errMsg),
|
||||
|
||||
@@ -7,8 +7,25 @@
|
||||
<view class="eyebrow">Profile</view>
|
||||
</view>
|
||||
|
||||
<!-- 游客态资料卡:点击进入登录页,登录与否由用户自行选择 -->
|
||||
<view wx:if="{{guest}}" class="pf-hero" bindtap="onGuestLogin">
|
||||
<view class="pf-av">
|
||||
<view class="icon icon-user"></view>
|
||||
</view>
|
||||
<view class="pf-info">
|
||||
<view class="pf-name">未登录</view>
|
||||
<view class="pf-sub">
|
||||
<text class="pill pill--line">浏览模式</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="pf-edit">
|
||||
点击登录
|
||||
<view class="icon icon-chevron-right"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 资料区:大号头像 + 衬线昵称 + 胶囊标签 -->
|
||||
<view class="pf-hero">
|
||||
<view wx:else class="pf-hero">
|
||||
<view class="pf-av">
|
||||
<image wx:if="{{userInfo.avatar}}" class="pf-av-img" src="{{userInfo.avatar}}" mode="aspectFill" />
|
||||
<view wx:else class="icon icon-user"></view>
|
||||
@@ -36,8 +53,8 @@
|
||||
<view class="icon icon-chevron-right"></view>
|
||||
</view>
|
||||
|
||||
<!-- 我的额度:四栏发丝线数字条 -->
|
||||
<view class="pf-quota" bindtap="onTapMall">
|
||||
<!-- 我的额度:四栏发丝线数字条(游客态不展示,避免 0/0 误导) -->
|
||||
<view wx:if="{{!guest}}" class="pf-quota" bindtap="onTapMall">
|
||||
<view class="pf-quota-hd">
|
||||
<text class="eyebrow">Quota</text>
|
||||
<text class="qk">我的额度</text>
|
||||
@@ -113,6 +130,10 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{guest}}" class="guest-foot" bindtap="onGuestLogin">
|
||||
<text>登录后同步你的衣橱、穿搭与额度</text>
|
||||
<text class="gf-a">去登录</text>
|
||||
</view>
|
||||
<view class="pf-ver">v1.0.0</view>
|
||||
<view class="gap-64"></view>
|
||||
</scroll-view>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { request, redirectToLogin } from '../../utils/request'
|
||||
import { request } from '../../utils/request'
|
||||
import * as auth from '../../utils/auth'
|
||||
import { PagedResult, ClothesItem, QuotaItem } from '../../types/models'
|
||||
import { QuotaType } from '../../utils/config'
|
||||
@@ -7,6 +7,7 @@ import { clothesStatusFlag } from '../../utils/clothes-status'
|
||||
import { resolvePendingClothes } from '../../utils/pending-clothes'
|
||||
import { setTabBarSelected } from '../../utils/tabbar'
|
||||
import { navScroll } from '../../utils/nav'
|
||||
import { GUEST_WARDROBE, GuestClothes, goLogin } from '../../utils/guest'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
||||
// 日志模块名
|
||||
@@ -22,6 +23,12 @@ const CATEGORIES = [
|
||||
{ name: '配饰', value: '配饰' },
|
||||
]
|
||||
|
||||
// 游客态分类筛选:与分类条 value 对齐,「全部」返回完整示例
|
||||
function filterGuestClothes(index: number): GuestClothes[] {
|
||||
const cat = CATEGORIES[index] ? CATEGORIES[index].value : ''
|
||||
return cat ? GUEST_WARDROBE.filter((it) => it.category === cat) : GUEST_WARDROBE
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
categories: CATEGORIES,
|
||||
@@ -45,16 +52,37 @@ Page({
|
||||
{ text: '知道了', value: '' },
|
||||
{ text: '查看衣物', value: 'ok', type: 'main' },
|
||||
],
|
||||
// 游客态(未登录):展示本地示例内容,不发任何需要授权的请求
|
||||
guest: false,
|
||||
guestList: GUEST_WARDROBE,
|
||||
},
|
||||
|
||||
// scroll-view 滚动 → 导航收缩为玻璃小标题
|
||||
onNavScroll: navScroll,
|
||||
|
||||
onLoad() {
|
||||
// 首帧渲染前判定游客态:未登录直接渲染本地示例,避免首帧 setData 冲突
|
||||
if (!auth.isLoggedIn()) {
|
||||
this.setData({ guest: true, loading: false })
|
||||
logger.info(LOG, '未登录进入首页,渲染游客态示例内容')
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.syncTabBar()
|
||||
// 未登录直接回登录页(幂等跳转),不再发出必然 401 的请求
|
||||
// 未登录:游客态浏览,零请求,不再强制跳登录(微信审核合规要求:
|
||||
// 用户可先浏览体验功能服务,登录与否由用户自行选择)
|
||||
if (!auth.isLoggedIn()) {
|
||||
redirectToLogin()
|
||||
if (!this.data.guest) this.setData({ guest: true, list: [], loading: false })
|
||||
return
|
||||
}
|
||||
// 登录成功回到首页:清空示例数据,切回真实衣橱
|
||||
if (this.data.guest) {
|
||||
logger.info(LOG, '检测到登录态,从游客态切换为真实衣橱')
|
||||
;(this as any).firstShown = true
|
||||
this.setData({ guest: false, guestList: [], list: [], total: 0, uploadRemain: 0, loading: true })
|
||||
this.resolvePending()
|
||||
this.refresh()
|
||||
return
|
||||
}
|
||||
// 核对「上传后退出小程序」的衣物最终处理结果(pending 为空时零请求)
|
||||
@@ -82,6 +110,11 @@ Page({
|
||||
},
|
||||
|
||||
onRefresh() {
|
||||
// 游客态没有服务端数据可刷新,直接收起下拉动画
|
||||
if (this.data.guest) {
|
||||
this.setData({ refreshing: false })
|
||||
return
|
||||
}
|
||||
this.setData({ refreshing: true })
|
||||
Promise.all([this.fetchList(1), this.fetchQuota()]).finally(() => {
|
||||
this.setData({ refreshing: false })
|
||||
@@ -89,6 +122,7 @@ Page({
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
if (this.data.guest) return
|
||||
if (!this.data.hasMore || this.data.loading || this.data.loadingMore) return
|
||||
this.fetchList(this.data.page + 1)
|
||||
},
|
||||
@@ -186,11 +220,27 @@ Page({
|
||||
onCategoryTap(e: WechatMiniprogram.TouchEvent) {
|
||||
const index = Number(e.currentTarget.dataset.index)
|
||||
if (index === this.data.activeCategory) return
|
||||
this.setData({ activeCategory: index, list: [], loading: true })
|
||||
this.setData({ activeCategory: index })
|
||||
// 游客态:本地示例数据即时筛选,提供可操作的浏览体验,不发请求
|
||||
if (this.data.guest) {
|
||||
this.setData({ guestList: filterGuestClothes(index) })
|
||||
return
|
||||
}
|
||||
this.setData({ list: [], loading: true })
|
||||
this.refresh()
|
||||
},
|
||||
|
||||
// 游客态点击示例内容 / 受限入口:统一进入登录页,由用户自行选择是否登录
|
||||
onGuestLogin() {
|
||||
logger.debug(LOG, '游客态点击受限入口,引导登录')
|
||||
goLogin()
|
||||
},
|
||||
|
||||
onClothesTap(e: WechatMiniprogram.CustomEvent) {
|
||||
if (this.data.guest) {
|
||||
this.onGuestLogin()
|
||||
return
|
||||
}
|
||||
const item = e.detail.item as ClothesItem
|
||||
logger.debug(LOG, '点击衣物, uuid=', item && item.clothes_uuid)
|
||||
wx.navigateTo({
|
||||
@@ -200,6 +250,11 @@ Page({
|
||||
},
|
||||
|
||||
onUpload() {
|
||||
// 上传需登录:游客态先引导登录,不直接进入上传页
|
||||
if (!auth.isLoggedIn()) {
|
||||
this.onGuestLogin()
|
||||
return
|
||||
}
|
||||
wx.navigateTo({
|
||||
url: '/pages/upload/upload',
|
||||
fail: (e) => logger.warn(LOG, 'navigateTo 上传页失败, err=', e && e.errMsg),
|
||||
|
||||
@@ -14,7 +14,16 @@
|
||||
<view class="head">
|
||||
<view class="eyebrow">Wardrobe</view>
|
||||
<view class="display">我的衣橱</view>
|
||||
<view class="wd-stat num">共 {{total}} 件 · 上传额度剩余 {{uploadRemain}} 次</view>
|
||||
<view wx:if="{{guest}}" class="wd-stat">浏览模式 · 以下为功能示例,登录后同步你的衣物</view>
|
||||
<view wx:else class="wd-stat num">共 {{total}} 件 · 上传额度剩余 {{uploadRemain}} 次</view>
|
||||
</view>
|
||||
|
||||
<!-- 游客态提示条:未登录也可先浏览,登录由用户自行选择 -->
|
||||
<view wx:if="{{guest}}" class="guest-bar" bindtap="onGuestLogin">
|
||||
<view class="icon icon-lock"></view>
|
||||
<text class="gb-t">浏览模式 · 示例内容仅供体验</text>
|
||||
<text class="gb-a">登录</text>
|
||||
<view class="icon icon-chevron-right"></view>
|
||||
</view>
|
||||
|
||||
<!-- 下划线式极简分类条 -->
|
||||
@@ -33,7 +42,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 骨架屏 -->
|
||||
<view wx:if="{{loading}}" class="wd-grid">
|
||||
<view wx:if="{{!guest && loading}}" class="wd-grid">
|
||||
<view class="wd-skel" wx:for="{{[1, 2, 3, 4, 5, 6]}}" wx:key="*this">
|
||||
<view class="sk sk-img"></view>
|
||||
<view class="sk sk-ln" style="width: 70%;"></view>
|
||||
@@ -43,7 +52,7 @@
|
||||
|
||||
<!-- 空态 -->
|
||||
<empty-state
|
||||
wx:elif="{{!list.length}}"
|
||||
wx:elif="{{!guest && !list.length}}"
|
||||
icon="icon-hanger"
|
||||
title="衣橱还空着"
|
||||
desc="拍照上传第一件衣物,AI 自动抠图并识别属性"
|
||||
@@ -51,6 +60,19 @@
|
||||
bind:action="onUpload"
|
||||
/>
|
||||
|
||||
<!-- 游客态:本地示例衣物网格(可切换分类即时筛选) -->
|
||||
<view wx:elif="{{guest}}" class="wd-grid">
|
||||
<view class="wd-tile" wx:for="{{guestList}}" wx:key="key" bindtap="onGuestLogin">
|
||||
<view class="gd-img">
|
||||
<view class="icon {{item.icon}} gd-ph"></view>
|
||||
<view class="guest-flag">示例</view>
|
||||
</view>
|
||||
<view class="tile-name">{{item.name}}</view>
|
||||
<view class="tile-sub">{{item.category}} · {{item.color}}色</view>
|
||||
</view>
|
||||
<view wx:if="{{!guestList.length}}" class="gd-none">该分类暂无示例内容</view>
|
||||
</view>
|
||||
|
||||
<!-- 衣物网格 -->
|
||||
<view wx:else class="wd-grid">
|
||||
<view class="wd-tile" wx:for="{{list}}" wx:key="clothes_uuid">
|
||||
@@ -63,7 +85,11 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{list.length && !hasMore}}" class="foot-note">没有更多了</view>
|
||||
<view wx:if="{{!guest && list.length && !hasMore}}" class="foot-note">没有更多了</view>
|
||||
<view wx:if="{{guest}}" class="guest-foot" bindtap="onGuestLogin">
|
||||
<text>登录后上传你的衣物,AI 自动抠图与识别</text>
|
||||
<text class="gf-a">去登录</text>
|
||||
</view>
|
||||
<view class="gap-64"></view>
|
||||
</scroll-view>
|
||||
|
||||
|
||||
@@ -52,3 +52,32 @@
|
||||
.wd-tile:active {
|
||||
transform: scale(.975);
|
||||
}
|
||||
|
||||
/* ===== 游客态:示例衣物瓦片(线稿占位 + 示例角标) ===== */
|
||||
.gd-img {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
background: var(--paper-2);
|
||||
border-radius: var(--r-img);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gd-img .gd-ph {
|
||||
width: 56%;
|
||||
height: 56%;
|
||||
color: var(--art);
|
||||
}
|
||||
|
||||
/* 分类下无示例时的整行占位 */
|
||||
.gd-none {
|
||||
grid-column: 1 / -1;
|
||||
padding: 72rpx 0;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
color: var(--ink-3);
|
||||
letter-spacing: .02em;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user