feat(guest): 支持游客态浏览示例衣物与搭配
游客可点开示例衣物详情与搭配抽屉,不再在浏览路径上要求登录。
@@ -0,0 +1,164 @@
|
||||
{"items": [
|
||||
{
|
||||
"source_file": "01_cream_sweater.jpg",
|
||||
"clothes": {
|
||||
"category_main": "上装",
|
||||
"category_sub": "毛衣",
|
||||
"style": "休闲风",
|
||||
"color_main": "米色",
|
||||
"color_sub": "红色",
|
||||
"thickness": "厚",
|
||||
"version": "宽松",
|
||||
"material": "针织",
|
||||
"pattern": "logo图案",
|
||||
"sleeve_type": "长袖",
|
||||
"clothes_type": "短款",
|
||||
"neckline_type": "圆领",
|
||||
"formality": "2",
|
||||
"occasions": "日常通勤,校园上课",
|
||||
"visual_impact": "3",
|
||||
"name": "米色圆领针织毛衣",
|
||||
"remark": "可见红色胸前图案。",
|
||||
"processed_image": "01_cream_sweater.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_file": "02_ivory_strap_dress.jpg",
|
||||
"clothes": {
|
||||
"category_main": "连衣裙",
|
||||
"category_sub": "吊带裙",
|
||||
"style": "优雅风",
|
||||
"color_main": "米色",
|
||||
"color_sub": "金色",
|
||||
"thickness": "适中",
|
||||
"version": "修身",
|
||||
"material": "",
|
||||
"pattern": "纯色",
|
||||
"sleeve_type": "吊带",
|
||||
"clothes_type": "短款",
|
||||
"neckline_type": "",
|
||||
"formality": "3",
|
||||
"occasions": "约会聚会",
|
||||
"visual_impact": "4",
|
||||
"name": "象牙白金扣吊带连衣短裙",
|
||||
"remark": "象牙白偏米色;胸部为心形领。",
|
||||
"processed_image": "02_ivory_strap_dress.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_file": "03_white_jk_blouse.jpg",
|
||||
"clothes": {
|
||||
"category_main": "上装",
|
||||
"category_sub": "衬衫",
|
||||
"style": "学院风",
|
||||
"color_main": "白色",
|
||||
"color_sub": "藏青色",
|
||||
"thickness": "适中",
|
||||
"version": "常规合身",
|
||||
"material": "",
|
||||
"pattern": "条纹",
|
||||
"sleeve_type": "长袖",
|
||||
"clothes_type": "短款",
|
||||
"neckline_type": "翻领",
|
||||
"formality": "2",
|
||||
"occasions": "校园上课,约会聚会",
|
||||
"visual_impact": "4",
|
||||
"name": "白色长袖JK水手服上衣",
|
||||
"remark": "藏青领口及袖口配白条纹,另有酒红色蝴蝶结;水手领归入翻领。",
|
||||
"processed_image": "03_white_jk_blouse.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_file": "04_pink_plaid_skirt.jpg",
|
||||
"clothes": {
|
||||
"category_main": "下装",
|
||||
"category_sub": "半身裙",
|
||||
"style": "学院风",
|
||||
"color_main": "粉色",
|
||||
"color_sub": "黑色",
|
||||
"thickness": "适中",
|
||||
"version": "常规合身",
|
||||
"material": "",
|
||||
"pattern": "格纹",
|
||||
"sleeve_type": "",
|
||||
"clothes_type": "短款",
|
||||
"neckline_type": "",
|
||||
"formality": "2",
|
||||
"occasions": "校园上课,约会聚会",
|
||||
"visual_impact": "4",
|
||||
"name": "粉色格纹百褶短裙",
|
||||
"remark": "另有白色格纹;百褶A字廓形。",
|
||||
"processed_image": "04_pink_plaid_skirt.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_file": "05_black_jk_blouse.jpg",
|
||||
"clothes": {
|
||||
"category_main": "上装",
|
||||
"category_sub": "衬衫",
|
||||
"style": "学院风",
|
||||
"color_main": "黑色",
|
||||
"color_sub": "酒红色",
|
||||
"thickness": "适中",
|
||||
"version": "常规合身",
|
||||
"material": "",
|
||||
"pattern": "条纹",
|
||||
"sleeve_type": "短袖",
|
||||
"clothes_type": "常规",
|
||||
"neckline_type": "翻领",
|
||||
"formality": "2",
|
||||
"occasions": "校园上课,约会聚会",
|
||||
"visual_impact": "4",
|
||||
"name": "黑色短袖JK水手服上衣",
|
||||
"remark": "酒红色蝴蝶结,领口另有白条纹;水手领归入翻领。",
|
||||
"processed_image": "05_black_jk_blouse.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_file": "06_navy_pleated_skirt.jpg",
|
||||
"clothes": {
|
||||
"category_main": "下装",
|
||||
"category_sub": "半身裙",
|
||||
"style": "学院风",
|
||||
"color_main": "藏青色",
|
||||
"color_sub": "",
|
||||
"thickness": "适中",
|
||||
"version": "常规合身",
|
||||
"material": "",
|
||||
"pattern": "纯色",
|
||||
"sleeve_type": "",
|
||||
"clothes_type": "短款",
|
||||
"neckline_type": "",
|
||||
"formality": "2",
|
||||
"occasions": "校园上课,日常通勤",
|
||||
"visual_impact": "2",
|
||||
"name": "藏青色百褶半身裙",
|
||||
"remark": "",
|
||||
"processed_image": "06_navy_pleated_skirt.jpg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_file": "07_ivory_pearl_high_heels.jpg",
|
||||
"clothes": {
|
||||
"category_main": "鞋",
|
||||
"category_sub": "高跟鞋",
|
||||
"style": "优雅风",
|
||||
"color_main": "米色",
|
||||
"color_sub": "银色",
|
||||
"thickness": "",
|
||||
"version": "",
|
||||
"material": "",
|
||||
"pattern": "纯色",
|
||||
"sleeve_type": "",
|
||||
"clothes_type": "",
|
||||
"neckline_type": "",
|
||||
"formality": "4",
|
||||
"occasions": "婚礼宴会,约会聚会",
|
||||
"visual_impact": "4",
|
||||
"name": "米白色珍珠踝链尖头细高跟鞋",
|
||||
"remark": "可见珍珠式踝链、银色链条和同色立体纹理。",
|
||||
"processed_image": "07_ivory_pearl_high_heels.jpg"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 107 KiB |
@@ -878,20 +878,6 @@ swiper-item {
|
||||
color: var(--ink);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
/* 示例内容角标:浅填充底 + 灰字,弱于业务状态胶囊 */
|
||||
.guest-flag {
|
||||
position: absolute;
|
||||
top: 14rpx;
|
||||
left: 14rpx;
|
||||
height: 38rpx;
|
||||
padding: 0 14rpx;
|
||||
border-radius: 20rpx;
|
||||
background: var(--fill-2);
|
||||
font-size: 19rpx;
|
||||
font-weight: 500;
|
||||
line-height: 38rpx;
|
||||
color: var(--ink-2);
|
||||
}
|
||||
/* 游客态底部一行登录引导 */
|
||||
.guest-foot {
|
||||
display: flex;
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
import { ClothesItem } from '../types/models'
|
||||
import { ProcessStatus } from '../utils/config'
|
||||
|
||||
/**
|
||||
* damo 示例素材(游客态唯一数据源)。
|
||||
*
|
||||
* 合规背景(微信审核整改):未登录用户必须能完整浏览与点击体验功能服务,
|
||||
* 因此衣橱示例卡片可点开「示例衣物详情」,详情完全由本地数据渲染,零请求、只读。
|
||||
*
|
||||
* 素材来源:项目根目录 damo/damo.json 与 damo/image/(原图 1024×1024),
|
||||
* 图片已压缩为 512px / quality 82 并复制到 miniprogram/damo/image/ 随包发布,
|
||||
* 页面通过包内绝对路径引用(如 /damo/image/01_cream_sweater.jpg)。
|
||||
*/
|
||||
|
||||
// 包内图片目录(绝对路径,结尾不带斜杠)
|
||||
const DAMO_IMAGE = '/damo/image'
|
||||
|
||||
// 衣物属性:与 ClothesItem 除 uuid / 图片 / 处理状态之外的字段一一对应
|
||||
interface DamoAttr {
|
||||
category_main: string
|
||||
category_sub: string
|
||||
style: string
|
||||
color_main: string
|
||||
color_sub: string
|
||||
thickness: string
|
||||
version: string
|
||||
material: string
|
||||
pattern: string
|
||||
sleeve_type: string
|
||||
clothes_type: string
|
||||
neckline_type: string
|
||||
formality: string
|
||||
occasions: string
|
||||
visual_impact: string
|
||||
name: string
|
||||
remark: string
|
||||
}
|
||||
|
||||
interface DamoRaw {
|
||||
uuid: string
|
||||
file: string
|
||||
attr: DamoAttr
|
||||
}
|
||||
|
||||
const DAMO_RAW: DamoRaw[] = [
|
||||
{
|
||||
uuid: 'damo-01',
|
||||
file: '01_cream_sweater.jpg',
|
||||
attr: {
|
||||
category_main: '上装',
|
||||
category_sub: '毛衣',
|
||||
style: '休闲风',
|
||||
color_main: '米色',
|
||||
color_sub: '红色',
|
||||
thickness: '厚',
|
||||
version: '宽松',
|
||||
material: '针织',
|
||||
pattern: 'logo图案',
|
||||
sleeve_type: '长袖',
|
||||
clothes_type: '短款',
|
||||
neckline_type: '圆领',
|
||||
formality: '2',
|
||||
occasions: '日常通勤,校园上课',
|
||||
visual_impact: '3',
|
||||
name: '米色圆领针织毛衣',
|
||||
remark: '可见红色胸前图案。',
|
||||
},
|
||||
},
|
||||
{
|
||||
uuid: 'damo-02',
|
||||
file: '02_ivory_strap_dress.jpg',
|
||||
attr: {
|
||||
category_main: '连衣裙',
|
||||
category_sub: '吊带裙',
|
||||
style: '优雅风',
|
||||
color_main: '米色',
|
||||
color_sub: '金色',
|
||||
thickness: '适中',
|
||||
version: '修身',
|
||||
material: '',
|
||||
pattern: '纯色',
|
||||
sleeve_type: '吊带',
|
||||
clothes_type: '短款',
|
||||
neckline_type: '',
|
||||
formality: '3',
|
||||
occasions: '约会聚会',
|
||||
visual_impact: '4',
|
||||
name: '象牙白金扣吊带连衣短裙',
|
||||
remark: '象牙白偏米色;胸部为心形领。',
|
||||
},
|
||||
},
|
||||
{
|
||||
uuid: 'damo-03',
|
||||
file: '03_white_jk_blouse.jpg',
|
||||
attr: {
|
||||
category_main: '上装',
|
||||
category_sub: '衬衫',
|
||||
style: '学院风',
|
||||
color_main: '白色',
|
||||
color_sub: '藏青色',
|
||||
thickness: '适中',
|
||||
version: '常规合身',
|
||||
material: '',
|
||||
pattern: '条纹',
|
||||
sleeve_type: '长袖',
|
||||
clothes_type: '短款',
|
||||
neckline_type: '翻领',
|
||||
formality: '2',
|
||||
occasions: '校园上课,约会聚会',
|
||||
visual_impact: '4',
|
||||
name: '白色长袖JK水手服上衣',
|
||||
remark: '藏青领口及袖口配白条纹,另有酒红色蝴蝶结;水手领归入翻领。',
|
||||
},
|
||||
},
|
||||
{
|
||||
uuid: 'damo-04',
|
||||
file: '04_pink_plaid_skirt.jpg',
|
||||
attr: {
|
||||
category_main: '下装',
|
||||
category_sub: '半身裙',
|
||||
style: '学院风',
|
||||
color_main: '粉色',
|
||||
color_sub: '黑色',
|
||||
thickness: '适中',
|
||||
version: '常规合身',
|
||||
material: '',
|
||||
pattern: '格纹',
|
||||
sleeve_type: '',
|
||||
clothes_type: '短款',
|
||||
neckline_type: '',
|
||||
formality: '2',
|
||||
occasions: '校园上课,约会聚会',
|
||||
visual_impact: '4',
|
||||
name: '粉色格纹百褶短裙',
|
||||
remark: '另有白色格纹;百褶A字廓形。',
|
||||
},
|
||||
},
|
||||
{
|
||||
uuid: 'damo-05',
|
||||
file: '05_black_jk_blouse.jpg',
|
||||
attr: {
|
||||
category_main: '上装',
|
||||
category_sub: '衬衫',
|
||||
style: '学院风',
|
||||
color_main: '黑色',
|
||||
color_sub: '酒红色',
|
||||
thickness: '适中',
|
||||
version: '常规合身',
|
||||
material: '',
|
||||
pattern: '条纹',
|
||||
sleeve_type: '短袖',
|
||||
clothes_type: '常规',
|
||||
neckline_type: '翻领',
|
||||
formality: '2',
|
||||
occasions: '校园上课,约会聚会',
|
||||
visual_impact: '4',
|
||||
name: '黑色短袖JK水手服上衣',
|
||||
remark: '酒红色蝴蝶结,领口另有白条纹;水手领归入翻领。',
|
||||
},
|
||||
},
|
||||
{
|
||||
uuid: 'damo-06',
|
||||
file: '06_navy_pleated_skirt.jpg',
|
||||
attr: {
|
||||
category_main: '下装',
|
||||
category_sub: '半身裙',
|
||||
style: '学院风',
|
||||
color_main: '藏青色',
|
||||
color_sub: '',
|
||||
thickness: '适中',
|
||||
version: '常规合身',
|
||||
material: '',
|
||||
pattern: '纯色',
|
||||
sleeve_type: '',
|
||||
clothes_type: '短款',
|
||||
neckline_type: '',
|
||||
formality: '2',
|
||||
occasions: '校园上课,日常通勤',
|
||||
visual_impact: '2',
|
||||
name: '藏青色百褶半身裙',
|
||||
remark: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
uuid: 'damo-07',
|
||||
file: '07_ivory_pearl_high_heels.jpg',
|
||||
attr: {
|
||||
category_main: '鞋',
|
||||
category_sub: '高跟鞋',
|
||||
style: '优雅风',
|
||||
color_main: '米色',
|
||||
color_sub: '银色',
|
||||
thickness: '',
|
||||
version: '',
|
||||
material: '',
|
||||
pattern: '纯色',
|
||||
sleeve_type: '',
|
||||
clothes_type: '',
|
||||
neckline_type: '',
|
||||
formality: '4',
|
||||
occasions: '婚礼宴会,约会聚会',
|
||||
visual_impact: '4',
|
||||
name: '米白色珍珠踝链尖头细高跟鞋',
|
||||
remark: '可见珍珠式踝链、银色链条和同色立体纹理。',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
// 示例衣物:处理状态固定为成功,详情页据此直接渲染正常视图
|
||||
export const DAMO_CLOTHES: ClothesItem[] = DAMO_RAW.map(
|
||||
(it): ClothesItem => ({
|
||||
...it.attr,
|
||||
clothes_uuid: it.uuid,
|
||||
processed_image: `${DAMO_IMAGE}/${it.file}`,
|
||||
image_process_status: ProcessStatus.SUCCESS,
|
||||
}),
|
||||
)
|
||||
|
||||
const DAMO_MAP: Record<string, ClothesItem> = DAMO_CLOTHES.reduce(
|
||||
(acc, it) => {
|
||||
acc[it.clothes_uuid] = it
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, ClothesItem>,
|
||||
)
|
||||
|
||||
// 按 uuid 取示例衣物:未命中返回 null,由调用方决定降级展示
|
||||
export function findDamoClothes(uuid: string): ClothesItem | null {
|
||||
return DAMO_MAP[uuid] || null
|
||||
}
|
||||
|
||||
// 游客态唯一一套示例搭配(黑色短袖JK水手服上衣 + 藏青色百褶半身裙)
|
||||
export const DAMO_OUTFIT_ITEMS: ClothesItem[] = ['damo-05', 'damo-06']
|
||||
.map((uuid) => findDamoClothes(uuid))
|
||||
.filter((it): it is ClothesItem => !!it)
|
||||
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -95,9 +95,35 @@ Page({
|
||||
this.setData({ scene: e.detail.value })
|
||||
},
|
||||
|
||||
onSkipScene() {
|
||||
this.setData({ showSceneSheet: false })
|
||||
this.startGenerate(1, '')
|
||||
// 取消场景选择:退出 AI 推荐页返回来源页(不再用「每日推荐」自动生成)
|
||||
onCancelScene() {
|
||||
logger.info(LOG, '取消场景选择,返回来源页')
|
||||
this.backToSource()
|
||||
},
|
||||
|
||||
// 取消的统一出口:优先返回来源页(从运势进回运势、从穿搭进回穿搭);
|
||||
// 页面栈不足(分享卡片/深链直达)时兜底 switchTab 回运势页
|
||||
backToSource() {
|
||||
const pages = typeof getCurrentPages === 'function' ? getCurrentPages() : []
|
||||
if (pages.length > 1) {
|
||||
wx.navigateBack({
|
||||
delta: 1,
|
||||
fail: (e) => {
|
||||
logger.warn(LOG, 'navigateBack 失败,兜底回运势页, err=', e && e.errMsg)
|
||||
this.switchToHoroscope()
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
logger.warn(LOG, '页面栈不足,兜底回运势页, 栈深=', pages.length)
|
||||
this.switchToHoroscope()
|
||||
},
|
||||
|
||||
switchToHoroscope() {
|
||||
wx.switchTab({
|
||||
url: '/pages/horoscope/horoscope',
|
||||
fail: (e) => logger.warn(LOG, 'switchTab 运势失败, err=', e && e.errMsg),
|
||||
})
|
||||
},
|
||||
|
||||
onStartGenerate() {
|
||||
@@ -112,16 +138,19 @@ Page({
|
||||
this.setData({ confirmVisible: true })
|
||||
},
|
||||
|
||||
// 取消生成:回到场景选择抽屉,避免停在无任何可渲染内容的空页
|
||||
// 取消生成:退出 AI 推荐页返回来源页(与抽屉「取消」语义一致,避免停在空页)
|
||||
onConfirmCancel() {
|
||||
this.setData({ confirmVisible: false, showSceneSheet: true })
|
||||
logger.info(LOG, '确认弹窗取消,返回来源页')
|
||||
this.setData({ confirmVisible: false })
|
||||
this.backToSource()
|
||||
},
|
||||
|
||||
onConfirmGenerate(e: WechatMiniprogram.CustomEvent) {
|
||||
this.setData({ confirmVisible: false })
|
||||
if (e.detail.value !== 'ok') {
|
||||
// 兜底:非「开始生成」按钮同样回到场景抽屉(取消按钮已走 cancel 分支)
|
||||
this.setData({ showSceneSheet: true })
|
||||
// 兜底:非「开始生成」按钮同样按取消处理(取消按钮已走 cancel 分支)
|
||||
logger.info(LOG, '确认弹窗非主按钮,按取消处理,返回来源页')
|
||||
this.backToSource()
|
||||
return
|
||||
}
|
||||
const pending = (this as any).pendingScene || { sceneType: 1, scene: '' }
|
||||
|
||||
@@ -95,13 +95,13 @@ module.exports = { cls: cls }
|
||||
</view>
|
||||
|
||||
<!-- 场景选择抽屉 -->
|
||||
<view wx:if="{{showSceneSheet}}" class="scrim" bindtap="onSkipScene"></view>
|
||||
<view wx:if="{{showSceneSheet}}" class="scrim" bindtap="onCancelScene"></view>
|
||||
<view wx:if="{{showSceneSheet}}" class="sheet-wrap">
|
||||
<view class="sheet" catchtap="noop">
|
||||
<view class="sheet-grab"></view>
|
||||
<view class="sheet-hd">
|
||||
<text class="sheet-t">选择推荐场景</text>
|
||||
<text class="sheet-x" bindtap="onSkipScene">跳过</text>
|
||||
<text class="sheet-x" bindtap="onCancelScene">取消</text>
|
||||
</view>
|
||||
<view class="sheet-body">
|
||||
<view class="scene-card {{sceneType === 1 ? 'is-on' : ''}}" bindtap="selectDaily">
|
||||
|
||||
@@ -275,8 +275,9 @@ Page({
|
||||
},
|
||||
|
||||
onItemTap(e: WechatMiniprogram.TouchEvent) {
|
||||
// 游客态示例条目仅作视觉展示:浏览路径上不要求登录(登录入口在页脚)
|
||||
if (this.data.guest) {
|
||||
this.onGuestLogin()
|
||||
logger.debug(LOG, '游客态点击示例分享条目,仅展示不跳转')
|
||||
return
|
||||
}
|
||||
const uuid = e.currentTarget.dataset.uuid as string
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ClothesItem } from '../../types/models'
|
||||
import { ProcessStatus } from '../../utils/config'
|
||||
import { isProcessTerminal } from '../../utils/clothes-status'
|
||||
import { removePendingClothes } from '../../utils/pending-clothes'
|
||||
import { findDamoClothes } from '../../damo/damo'
|
||||
import { navScroll } from '../../utils/nav'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
||||
@@ -108,6 +109,8 @@ Page({
|
||||
shareUUID: '',
|
||||
outfitUUID: '',
|
||||
readonly: false,
|
||||
// 游客态示例:由本地 damo 素材渲染的只读详情,零请求
|
||||
demo: false,
|
||||
attrGroups: ATTR_GROUPS,
|
||||
colorPresets: COLOR_PRESETS,
|
||||
detail: {} as ClothesItem,
|
||||
@@ -144,6 +147,25 @@ Page({
|
||||
noop() {},
|
||||
|
||||
onLoad(query: Record<string, string>) {
|
||||
const demo = query.demo || ''
|
||||
// 示例模式:游客从衣橱示例卡片进入,本地数据只读渲染,不发请求、不轮询
|
||||
if (demo) {
|
||||
const detail = findDamoClothes(demo)
|
||||
logger.info(LOG, '示例模式加载, demo=', demo, '命中=', !!detail)
|
||||
if (!detail) {
|
||||
this.setData({ demo: true, readonly: true, loading: false, notFound: true })
|
||||
return
|
||||
}
|
||||
this.setData({
|
||||
demo: true,
|
||||
readonly: true,
|
||||
uuid: detail.clothes_uuid,
|
||||
detail,
|
||||
loading: false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const uuid = query.uuid || ''
|
||||
const shareUUID = query.share_uuid || ''
|
||||
const outfitUUID = query.outfit_uuid || ''
|
||||
|
||||
@@ -119,6 +119,12 @@ module.exports = { hex: hex, tags: tags }
|
||||
<view wx:else class="ph icon icon-hanger"></view>
|
||||
</view>
|
||||
|
||||
<!-- 示例内容说明:游客态只读浏览,不要求登录 -->
|
||||
<view wx:if="{{demo}}" class="cd-demo">
|
||||
<view class="icon icon-info"></view>
|
||||
<text>示例内容 · 登录后可上传并识别你自己的衣物</text>
|
||||
</view>
|
||||
|
||||
<!-- 名称:编辑态给标签 + 浅填充输入框,一眼看出可输入 -->
|
||||
<view class="cd-name">
|
||||
<block wx:if="{{editMode}}">
|
||||
|
||||
@@ -176,3 +176,22 @@
|
||||
width: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 示例内容说明:游客态只读浏览(不引导、不打断) */
|
||||
.cd-demo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin: 28rpx var(--pad) 0;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.5;
|
||||
color: var(--ink-3);
|
||||
letter-spacing: .01em;
|
||||
}
|
||||
|
||||
.cd-demo .icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
color: var(--ink-4);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -8,7 +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 { GUEST_OUTFIT, goLogin } from '../../utils/guest'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
||||
// 日志模块名
|
||||
@@ -45,9 +45,11 @@ Page({
|
||||
pendingCount: 0,
|
||||
// 滚动收缩:由 utils/nav.ts 的共享 handler 驱动
|
||||
navScrolled: false,
|
||||
// 游客态(未登录):展示本地示例穿搭,不发需要授权的请求
|
||||
// 游客态(未登录):只展示本地示例穿搭,不发需要授权的请求
|
||||
guest: false,
|
||||
guestList: GUEST_OUTFITS,
|
||||
guestOutfit: GUEST_OUTFIT,
|
||||
// 游客态示例搭配抽屉:可完整浏览示例单品,浏览路径上不要求登录
|
||||
guestSheetVisible: false,
|
||||
},
|
||||
|
||||
// scroll-view 滚动 → 导航收缩为玻璃小标题
|
||||
@@ -72,7 +74,7 @@ Page({
|
||||
if (this.data.guest) {
|
||||
logger.info(LOG, '检测到登录态,从游客态切换为真实穿搭列表')
|
||||
;(this as any).firstShown = true
|
||||
this.setData({ guest: false, guestList: [], list: [], loading: true })
|
||||
this.setData({ guest: false, guestSheetVisible: false, list: [], loading: true })
|
||||
this.refresh()
|
||||
this.fetchPendingCount()
|
||||
return
|
||||
@@ -116,12 +118,32 @@ Page({
|
||||
this.refresh()
|
||||
},
|
||||
|
||||
// 游客态点击示例穿搭 / 受限入口:统一进入登录页,由用户自行选择是否登录
|
||||
// 游客态点击受限入口:进入登录页,由用户自行选择是否登录
|
||||
onGuestLogin() {
|
||||
logger.debug(LOG, '游客态点击受限入口,引导登录')
|
||||
goLogin()
|
||||
},
|
||||
|
||||
// 抽屉内容点击不穿透到遮罩
|
||||
noop() {},
|
||||
|
||||
// 游客态点击示例搭配:打开只读示例抽屉(浏览路径上不要求登录)
|
||||
onGuestOutfitTap() {
|
||||
logger.debug(LOG, '游客态打开示例搭配抽屉')
|
||||
this.setData({ guestSheetVisible: true })
|
||||
},
|
||||
|
||||
onGuestSheetClose() {
|
||||
this.setData({ guestSheetVisible: false })
|
||||
},
|
||||
|
||||
// 抽屉底部入口:用户主动选择登录时才进入登录页
|
||||
onGuestSheetLogin() {
|
||||
logger.info(LOG, '示例搭配抽屉内主动登录')
|
||||
this.setData({ guestSheetVisible: false })
|
||||
goLogin()
|
||||
},
|
||||
|
||||
refresh() {
|
||||
this.setData({ page: 1, hasMore: true, loading: this.data.list.length === 0 })
|
||||
this.fetchList(1)
|
||||
|
||||
@@ -63,26 +63,25 @@
|
||||
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 class="of-card" bindtap="onGuestOutfitTap">
|
||||
<view class="collage collage--2">
|
||||
<view class="cg" wx:for="{{guestOutfit.items}}" wx:key="clothes_uuid">
|
||||
<image class="pic" src="{{item.processed_image}}" mode="aspectFit" />
|
||||
</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>
|
||||
<text class="of-title">{{guestOutfit.name}}</text>
|
||||
<text class="pill pill--line">{{guestOutfit.scene}}</text>
|
||||
</view>
|
||||
<view class="of-desc">{{item.desc}}</view>
|
||||
<view class="of-desc">{{guestOutfit.desc}}</view>
|
||||
<view class="of-meta">
|
||||
<text class="of-m">{{item.icons.length}} 件衣物</text>
|
||||
<text class="pill">示例</text>
|
||||
<text class="of-m">{{guestOutfit.items.length}} 件衣物</text>
|
||||
<view class="spacer"></view>
|
||||
<view class="of-m">
|
||||
<view class="icon icon-heart"></view>
|
||||
<text class="num">-</text>
|
||||
<view class="icon icon-chevron-right"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -151,4 +150,38 @@
|
||||
<view class="btn btn--glass" bindtap="onCreate">创建穿搭</view>
|
||||
<view class="btn btn--ink" bindtap="onAiRecommend">AI 推荐</view>
|
||||
</view>
|
||||
|
||||
<!-- 游客态示例搭配抽屉:可完整浏览示例单品,登录仅由用户主动选择 -->
|
||||
<view wx:if="{{guestSheetVisible}}" class="scrim" bindtap="onGuestSheetClose"></view>
|
||||
<view wx:if="{{guestSheetVisible}}" class="sheet-wrap">
|
||||
<view class="sheet" catchtap="noop">
|
||||
<view class="sheet-grab"></view>
|
||||
<view class="sheet-hd">
|
||||
<text class="sheet-t">示例搭配</text>
|
||||
<text class="sheet-x" bindtap="onGuestSheetClose">关闭</text>
|
||||
</view>
|
||||
<view class="sheet-body">
|
||||
<view class="gd-hd">
|
||||
<view class="t-lg">{{guestOutfit.name}}</view>
|
||||
<view class="gd-tags">
|
||||
<text class="pill pill--line">{{guestOutfit.scene}}</text>
|
||||
<text class="pill">示例</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="gd-desc">{{guestOutfit.desc}}</view>
|
||||
<view class="gd-items">
|
||||
<view class="gd-item" wx:for="{{guestOutfit.items}}" wx:key="clothes_uuid">
|
||||
<view class="gd-item-img">
|
||||
<image class="pic" src="{{item.processed_image}}" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="tile-name">{{item.name}}</view>
|
||||
<view class="tile-sub">{{item.category_main}}{{item.category_sub ? ' · ' + item.category_sub : ''}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sheet-foot">
|
||||
<view class="btn btn--ghost btn--block" bindtap="onGuestSheetLogin">登录后可创建你的穿搭</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -192,3 +192,45 @@
|
||||
width: 26rpx;
|
||||
height: 26rpx;
|
||||
}
|
||||
|
||||
/* ===== 游客态:示例搭配抽屉(只读浏览,不要求登录) ===== */
|
||||
.gd-hd {
|
||||
padding: 8rpx var(--pad) 0;
|
||||
}
|
||||
|
||||
.gd-tags {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.gd-desc {
|
||||
margin-top: 18rpx;
|
||||
padding: 0 var(--pad);
|
||||
font-size: 26rpx;
|
||||
line-height: 1.6;
|
||||
color: var(--ink-2);
|
||||
}
|
||||
|
||||
.gd-items {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 32rpx 26rpx;
|
||||
padding: 40rpx var(--pad) 0;
|
||||
}
|
||||
|
||||
.gd-item-img {
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
background: var(--paper-2);
|
||||
border-radius: var(--r-img);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.gd-item-img .pic {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -7,7 +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 { GUEST_WARDROBE, goLogin } from '../../utils/guest'
|
||||
import { logger } from '../../utils/logger'
|
||||
|
||||
// 日志模块名
|
||||
@@ -24,9 +24,9 @@ const CATEGORIES = [
|
||||
]
|
||||
|
||||
// 游客态分类筛选:与分类条 value 对齐,「全部」返回完整示例
|
||||
function filterGuestClothes(index: number): GuestClothes[] {
|
||||
function filterGuestClothes(index: number): ClothesItem[] {
|
||||
const cat = CATEGORIES[index] ? CATEGORIES[index].value : ''
|
||||
return cat ? GUEST_WARDROBE.filter((it) => it.category === cat) : GUEST_WARDROBE
|
||||
return cat ? GUEST_WARDROBE.filter((it) => it.category_main === cat) : GUEST_WARDROBE
|
||||
}
|
||||
|
||||
Page({
|
||||
@@ -73,14 +73,22 @@ Page({
|
||||
// 未登录:游客态浏览,零请求,不再强制跳登录(微信审核合规要求:
|
||||
// 用户可先浏览体验功能服务,登录与否由用户自行选择)
|
||||
if (!auth.isLoggedIn()) {
|
||||
if (!this.data.guest) this.setData({ guest: true, list: [], loading: false })
|
||||
if (!this.data.guest) {
|
||||
// 重新回到游客态(如登录态失效后选择先逛逛):按当前分类还原示例数据
|
||||
this.setData({
|
||||
guest: true,
|
||||
guestList: filterGuestClothes(this.data.activeCategory),
|
||||
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.setData({ guest: false, list: [], total: 0, uploadRemain: 0, loading: true })
|
||||
this.resolvePending()
|
||||
this.refresh()
|
||||
return
|
||||
@@ -237,11 +245,16 @@ Page({
|
||||
},
|
||||
|
||||
onClothesTap(e: WechatMiniprogram.CustomEvent) {
|
||||
const item = e.detail.item as ClothesItem
|
||||
// 游客态:打开本地示例衣物详情(只读、零请求),浏览路径上不要求登录
|
||||
if (this.data.guest) {
|
||||
this.onGuestLogin()
|
||||
logger.debug(LOG, '游客态点击示例衣物, uuid=', item && item.clothes_uuid)
|
||||
wx.navigateTo({
|
||||
url: `/pages/clothes-detail/clothes-detail?demo=${item.clothes_uuid}`,
|
||||
fail: (err) => logger.warn(LOG, 'navigateTo 示例衣物详情失败, err=', err && err.errMsg),
|
||||
})
|
||||
return
|
||||
}
|
||||
const item = e.detail.item as ClothesItem
|
||||
logger.debug(LOG, '点击衣物, uuid=', item && item.clothes_uuid)
|
||||
wx.navigateTo({
|
||||
url: `/pages/clothes-detail/clothes-detail?uuid=${item.clothes_uuid}`,
|
||||
|
||||
@@ -60,15 +60,14 @@
|
||||
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 class="wd-tile" wx:for="{{guestList}}" wx:key="clothes_uuid">
|
||||
<clothes-card
|
||||
item="{{item}}"
|
||||
statusText="示例"
|
||||
bind:select="onClothesTap"
|
||||
/>
|
||||
</view>
|
||||
<view wx:if="{{!guestList.length}}" class="gd-none">该分类暂无示例内容</view>
|
||||
</view>
|
||||
|
||||
@@ -53,26 +53,7 @@
|
||||
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);
|
||||
}
|
||||
|
||||
/* 分类下无示例时的整行占位 */
|
||||
/* ===== 游客态:示例衣物网格(复用 clothes-card,仅保留无示例时的占位) ===== */
|
||||
.gd-none {
|
||||
grid-column: 1 / -1;
|
||||
padding: 72rpx 0;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { HoroscopeData } from '../types/models'
|
||||
import { ClothesItem, HoroscopeData } from '../types/models'
|
||||
import { DAMO_CLOTHES, DAMO_OUTFIT_ITEMS } from '../damo/damo'
|
||||
import { logger } from './logger'
|
||||
|
||||
// 日志模块名
|
||||
@@ -7,29 +8,32 @@ const LOG = 'guest'
|
||||
/**
|
||||
* 游客态唯一数据源:未登录浏览时展示的本地示例内容 + 统一登录引导。
|
||||
*
|
||||
* 合规背景(微信审核驳回整改):
|
||||
* 合规背景(微信审核整改):
|
||||
* 1. 打开小程序不得直接要求授权登录,用户应能先浏览体验功能服务;
|
||||
* 2. 仅在用户主动点击受限功能时,才由用户自行选择是否登录。
|
||||
* 2. 示例内容必须能完整浏览与点开查看详情,不得在浏览路径上要求登录;
|
||||
* 3. 仅在用户主动使用受限功能时,才由用户自行选择是否登录。
|
||||
*
|
||||
* 本模块不发起任何网络请求、不改动登录态,只提供静态示例数据与登录跳转入口。
|
||||
* 本模块不发起任何网络请求、不改动登录态:示例数据全部来自本地 damo 素材。
|
||||
*/
|
||||
|
||||
// 衣物示例:icon 取 styles/icons.wxss 的线稿图标类名
|
||||
export interface GuestClothes {
|
||||
key: string
|
||||
name: string
|
||||
category: string // 与衣橱分类条 value 对齐
|
||||
color: string
|
||||
icon: string
|
||||
}
|
||||
// 衣橱示例:damo 素材(含包内图片路径),分类筛选由调用方按 category_main 本地完成
|
||||
export const GUEST_WARDROBE: ClothesItem[] = DAMO_CLOTHES
|
||||
|
||||
// 穿搭示例:icons 为拼图封面里的线稿占位图标
|
||||
// 穿搭示例:游客态只展示这一套(黑色短袖JK水手服上衣 + 藏青色百褶半身裙)
|
||||
export interface GuestOutfit {
|
||||
key: string
|
||||
name: string
|
||||
scene: string
|
||||
desc: string
|
||||
icons: string[]
|
||||
items: ClothesItem[]
|
||||
}
|
||||
|
||||
export const GUEST_OUTFIT: GuestOutfit = {
|
||||
key: 'damo-outfit-1',
|
||||
name: '藏青学院一套',
|
||||
scene: '校园上课',
|
||||
desc: '黑色短袖水手服上衣配藏青百褶半身裙,酒红蝴蝶结与白条纹作点缀',
|
||||
items: DAMO_OUTFIT_ITEMS,
|
||||
}
|
||||
|
||||
// 分享示例
|
||||
@@ -41,44 +45,9 @@ export interface GuestShare {
|
||||
expireText: string
|
||||
}
|
||||
|
||||
export const GUEST_WARDROBE: GuestClothes[] = [
|
||||
{ key: 'g1', name: '白衬衫', category: '上装', color: '白', icon: 'icon-shirt' },
|
||||
{ key: 'g2', name: '直筒牛仔裤', category: '下装', color: '蓝', icon: 'icon-hanger' },
|
||||
{ key: 'g3', name: '针织开衫', category: '外套', color: '灰', icon: 'icon-shirt' },
|
||||
{ key: 'g4', name: '黑色连衣裙', category: '连衣裙', color: '黑', icon: 'icon-hanger' },
|
||||
{ key: 'g5', name: '小白鞋', category: '鞋', color: '白', icon: 'icon-image' },
|
||||
{ key: 'g6', name: '丝巾', category: '配饰', color: '米', icon: 'icon-sparkles' },
|
||||
{ key: 'g7', name: '风衣', category: '外套', color: '卡其', icon: 'icon-shirt' },
|
||||
{ key: 'g8', name: '半身裙', category: '下装', color: '棕', icon: 'icon-hanger' },
|
||||
]
|
||||
|
||||
export const GUEST_OUTFITS: GuestOutfit[] = [
|
||||
{
|
||||
key: 'go1',
|
||||
name: '通勤的干净线条',
|
||||
scene: '通勤',
|
||||
desc: '衬衫配直筒裤,外搭一件风衣',
|
||||
icons: ['icon-shirt', 'icon-hanger', 'icon-image'],
|
||||
},
|
||||
{
|
||||
key: 'go2',
|
||||
name: '周末的松弛感',
|
||||
scene: '周末',
|
||||
desc: '针织开衫配半身裙,鞋选小白鞋',
|
||||
icons: ['icon-shirt', 'icon-hanger'],
|
||||
},
|
||||
{
|
||||
key: 'go3',
|
||||
name: '晚间约会的黑色',
|
||||
scene: '约会',
|
||||
desc: '黑色连衣裙加一条丝巾点亮',
|
||||
icons: ['icon-hanger', 'icon-sparkles'],
|
||||
},
|
||||
]
|
||||
|
||||
export const GUEST_SHARES: GuestShare[] = [
|
||||
{ key: 'gs1', kind: 'closet', title: '衣柜分享 · 12 件', typeText: '全量衣橱', expireText: '示例内容' },
|
||||
{ key: 'gs2', kind: 'outfit', title: '通勤的干净线条', typeText: '穿搭分享 · 3 件', expireText: '示例内容' },
|
||||
{ key: 'gs2', kind: 'outfit', title: '藏青学院一套', typeText: '穿搭分享 · 2 件', expireText: '示例内容' },
|
||||
]
|
||||
|
||||
// 运势示例:日期取当天,保证页头日期不出现过去时间
|
||||
@@ -107,7 +76,7 @@ let lastNavAt = 0
|
||||
*
|
||||
* 与 utils/request.ts 的 redirectToLogin 的区别:
|
||||
* - redirectToLogin 用于「登录态失效」的被动兜底,reLaunch 清栈;
|
||||
* - goLogin 用于用户主动点击受限功能的场景,navigateTo 保留返回路径,
|
||||
* - goLogin 用于用户主动使用受限功能的场景,navigateTo 保留返回路径,
|
||||
* 用户可随时返回继续浏览,不会被强制清栈。
|
||||
*/
|
||||
export function goLogin(options: { redirect?: string } = {}): void {
|
||||
|
||||