feat(穿搭详情): 支持社区公开穿搭只读访问单品
非属主查看社区公开穿搭时携带 outfit_uuid 凭证请求单品详情, 放行后端只读读取,避免名称退化为「未命名」;单品详情页同步 支持 outfit_uuid 只读凭证。点赞增加防连点,来源胶囊与状态 胶囊文案重复时只保留一个。
This commit is contained in:
@@ -104,8 +104,9 @@ function statusToStep(status: number): number {
|
||||
Page({
|
||||
data: {
|
||||
uuid: '',
|
||||
// 来自穿搭分享的只读查看:携带 share_uuid,隐藏编辑/删除
|
||||
// 只读查看的两个凭证:share_uuid(穿搭分享)/ outfit_uuid(社区公开穿搭),命中即隐藏编辑/删除
|
||||
shareUUID: '',
|
||||
outfitUUID: '',
|
||||
readonly: false,
|
||||
attrGroups: ATTR_GROUPS,
|
||||
colorPresets: COLOR_PRESETS,
|
||||
@@ -145,9 +146,10 @@ Page({
|
||||
onLoad(query: Record<string, string>) {
|
||||
const uuid = query.uuid || ''
|
||||
const shareUUID = query.share_uuid || ''
|
||||
const readonly = !!shareUUID
|
||||
const outfitUUID = query.outfit_uuid || ''
|
||||
const readonly = !!(shareUUID || outfitUUID)
|
||||
logger.debug(LOG, 'onLoad, uuid=', uuid, 'readonly=', readonly)
|
||||
this.setData({ uuid, shareUUID, readonly })
|
||||
this.setData({ uuid, shareUUID, outfitUUID, readonly })
|
||||
if (!uuid) {
|
||||
// uuid 缺失时不请求不轮询,否则会停在空白页且无法退出
|
||||
logger.warn(LOG, 'onLoad 缺少 uuid 参数')
|
||||
@@ -155,7 +157,7 @@ Page({
|
||||
return
|
||||
}
|
||||
this.fetchDetail().then(() => {
|
||||
// 只读(来自穿搭分享)场景无需轮询 AI 处理状态
|
||||
// 只读(来自穿搭分享 / 社区公开穿搭)场景无需轮询 AI 处理状态
|
||||
if (readonly) return
|
||||
// 详情响应已带处理状态:成功/失败等终态直接定态,仅处理中才轮询
|
||||
if (isProcessTerminal(this.data.detail)) return
|
||||
@@ -171,10 +173,10 @@ Page({
|
||||
},
|
||||
|
||||
fetchDetail(): Promise<void> {
|
||||
// 只读场景携带 share_uuid,后端据此放行非属主读取衣物详情
|
||||
const url = this.data.readonly
|
||||
? `/clothes/${this.data.uuid}?share_uuid=${this.data.shareUUID}`
|
||||
: `/clothes/${this.data.uuid}`
|
||||
// 只读场景需携带放行凭证:穿搭分享用 share_uuid,社区公开穿搭用 outfit_uuid
|
||||
let url = `/clothes/${this.data.uuid}`
|
||||
if (this.data.shareUUID) url += `?share_uuid=${this.data.shareUUID}`
|
||||
else if (this.data.outfitUUID) url += `?outfit_uuid=${this.data.outfitUUID}`
|
||||
return request<ClothesItem>({ url })
|
||||
.then((detail) => {
|
||||
logger.debug(LOG, '详情加载成功, name=', detail && detail.name)
|
||||
|
||||
@@ -227,27 +227,30 @@ Page({
|
||||
statePill: outfitStatusPill(outfit),
|
||||
})
|
||||
|
||||
// 封面与衣物缩略图直接使用穿搭明细里的抠图(processed_image),
|
||||
// 避免非属主逐件请求 /clothes/:uuid 被拒(50005)导致图片与名称全部丢失。
|
||||
// 封面与衣物缩略图先直接使用穿搭明细里的抠图(processed_image),
|
||||
// 即使后续单品详情拉取失败,图片也不会丢。
|
||||
const baseClothes = (outfit.items || []).map(
|
||||
(it) => ({ clothes_uuid: it.clothes_uuid, processed_image: it.processed_image }) as ClothesItem,
|
||||
)
|
||||
this.setData({ clothes: baseClothes })
|
||||
|
||||
// 属主可进一步拉取衣物详情补充名称;非属主无权限,跳过。
|
||||
if (this.data.isOwner) {
|
||||
const clothes = await Promise.all(
|
||||
(outfit.items || []).map(async (it) => {
|
||||
try {
|
||||
return await request<ClothesItem>({ url: `/clothes/${it.clothes_uuid}`, showError: false })
|
||||
} catch (e: any) {
|
||||
logger.warn(LOG, '单品详情拉取失败, uuid=', it.clothes_uuid, 'code=', e && e.code)
|
||||
return { clothes_uuid: it.clothes_uuid, processed_image: it.processed_image } as ClothesItem
|
||||
}
|
||||
})
|
||||
)
|
||||
this.setData({ clothes })
|
||||
}
|
||||
// 拉取单品详情补充名称与属性:属主直接读;非属主(社区公开穿搭 / 他人分享)携带本穿搭 uuid,
|
||||
// 由后端只读放行,避免被拒(50005)导致名称全部退化成「未命名」。
|
||||
const clothesQuery = this.data.isOwner ? '' : `?outfit_uuid=${this.data.uuid}`
|
||||
const clothes = await Promise.all(
|
||||
(outfit.items || []).map(async (it) => {
|
||||
try {
|
||||
return await request<ClothesItem>({
|
||||
url: `/clothes/${it.clothes_uuid}${clothesQuery}`,
|
||||
showError: false,
|
||||
})
|
||||
} catch (e: any) {
|
||||
logger.warn(LOG, '单品详情拉取失败, uuid=', it.clothes_uuid, 'code=', e && e.code)
|
||||
return { clothes_uuid: it.clothes_uuid, processed_image: it.processed_image } as ClothesItem
|
||||
}
|
||||
})
|
||||
)
|
||||
this.setData({ clothes })
|
||||
|
||||
// 点赞状态:失败静默保持默认,不影响详情展示
|
||||
request<{ is_liked: boolean; like_count: number }>({
|
||||
@@ -267,6 +270,12 @@ Page({
|
||||
},
|
||||
|
||||
onLike() {
|
||||
// 防连点:请求进行中忽略后续点击,避免误触/连点并发发出多条点赞、取消请求
|
||||
if ((this as any).liking) {
|
||||
logger.debug(LOG, '点赞请求进行中,忽略重复点击')
|
||||
return
|
||||
}
|
||||
;(this as any).liking = true
|
||||
const action = this.data.isLiked ? 2 : 1
|
||||
request({ url: '/like', method: 'POST', data: { target_uuid: this.data.uuid, action } })
|
||||
.then(() => {
|
||||
@@ -277,6 +286,9 @@ Page({
|
||||
})
|
||||
})
|
||||
.catch((e: any) => logger.warn(LOG, '点赞操作失败, action=', action, 'code=', e && e.code))
|
||||
.finally(() => {
|
||||
;(this as any).liking = false
|
||||
})
|
||||
},
|
||||
|
||||
onCollect() {
|
||||
@@ -416,14 +428,12 @@ Page({
|
||||
|
||||
onItemTap(e: WechatMiniprogram.TouchEvent) {
|
||||
const uuid = e.currentTarget.dataset.uuid as string
|
||||
// 非属主无权读取衣物详情(50005),点击仅提示;属主正常跳转。
|
||||
if (!this.data.isOwner) {
|
||||
logger.debug(LOG, '非属主点击衣物,仅可查看穿搭, uuid=', uuid)
|
||||
wx.showToast({ title: '仅可查看穿搭', icon: 'none' })
|
||||
return
|
||||
}
|
||||
// 非属主(社区公开穿搭 / 他人分享)携带本穿搭 uuid 进入只读详情,
|
||||
// 后端据此放行;不携带时后端会按无权限拒绝(50005)。
|
||||
const query = this.data.isOwner ? '' : `&outfit_uuid=${this.data.uuid}`
|
||||
logger.debug(LOG, '点击单品, uuid=', uuid, 'isOwner=', this.data.isOwner)
|
||||
wx.navigateTo({
|
||||
url: `/pages/clothes-detail/clothes-detail?uuid=${uuid}`,
|
||||
url: `/pages/clothes-detail/clothes-detail?uuid=${uuid}${query}`,
|
||||
fail: (err) => logger.warn(LOG, 'navigateTo 单品详情失败, err=', err && err.errMsg),
|
||||
})
|
||||
},
|
||||
|
||||
@@ -71,7 +71,12 @@ module.exports = { cls: cls }
|
||||
<view class="t-xl od-title">{{outfit.outfit_name}}</view>
|
||||
<view class="od-tags">
|
||||
<text wx:if="{{outfit.scene}}" class="pill pill--line">{{outfit.scene}}</text>
|
||||
<text class="pill {{outfit.source === 2 ? 'pill--ink' : ''}}">{{outfit.source === 2 ? 'AI 推荐' : '自建'}}</text>
|
||||
<!-- 来源胶囊:AI 来源的文案已由状态胶囊承担(见 utils/outfit-status.ts),
|
||||
两者相同时只保留一个,避免出现两个「AI 推荐」;待保存/已封禁等状态下仍保留来源标签 -->
|
||||
<text
|
||||
wx:if="{{statePill.text !== 'AI 推荐'}}"
|
||||
class="pill {{outfit.source === 2 ? 'pill--ink' : ''}}"
|
||||
>{{outfit.source === 2 ? 'AI 推荐' : '自建'}}</text>
|
||||
<text class="pill {{statePill.cls}}">{{statePill.text}}</text>
|
||||
</view>
|
||||
<view class="t-bd od-desc" wx:if="{{outfit.outfit_desc}}">{{outfit.outfit_desc}}</view>
|
||||
|
||||
Reference in New Issue
Block a user