Files
zhengchiliu f169777f4a feat(image-cache): 新增图片本地缓存并全站接入
引入 cached-image 组件与 image-cache 工具,实现签名 URL 稳定 key 剥离、downloadFile 落盘及 TTL/LRU 清理,替换 11 个页面与 clothes-card 共 22 处网络图片渲染点,减少 CDN 重复流量。
2026-09-26 19:49:54 +08:00

330 lines
11 KiB
Plaintext
Raw Permalink 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.
<wxs module="fmt">
var COLOR_MAP = {
'白色': '#FFFFFF',
'黑色': '#1C1C1E',
'灰色': '#8E8E93',
'红色': '#FF3B30',
'粉色': '#FF2D55',
'橙色': '#FF9500',
'黄色': '#FFCC00',
'绿色': '#34C759',
'蓝色': '#007AFF',
'紫色': '#5856D6',
'棕色': '#A2845E',
'米色': '#F5EFE0'
}
function hex(v) {
if (!v) return ''
if (v.charAt(0) === '#') return v
return COLOR_MAP[v] || ''
}
function tags(v) {
if (!v) return []
var sep = '、,,/|;; \t\n\r'
var out = []
var cur = ''
for (var i = 0; i < v.length; i++) {
var ch = v.charAt(i)
if (sep.indexOf(ch) >= 0) {
if (cur) {
out.push(cur)
cur = ''
}
} else {
cur += ch
}
}
if (cur) out.push(cur)
return out
}
module.exports = { hex: hex, tags: tags }
</wxs>
<view class="page page-flex">
<navigation-bar title="衣物详情" scrolled="{{navScrolled}}">
<view
wx:if="{{!readonly && imageStatus !== 1 && imageStatus !== 2 && imageStatus !== 3}}"
slot="right"
class="nav-action nav-action--ink"
bindtap="onToggleEdit"
>{{editMode ? '取消' : '编辑'}}</view>
</navigation-bar>
<scroll-view
scroll-y
class="scroll-body {{editMode ? 'pb-dock' : 'pb-safe'}}"
bindscroll="onNavScroll"
>
<!-- 缺少衣物标识 -->
<empty-state
wx:if="{{notFound}}"
icon="icon-hanger"
title="衣物不存在"
desc="缺少衣物标识,无法查看详情"
buttonText="返回衣橱"
bind:action="onBackWardrobe"
/>
<!-- 处理中:三步发丝线进度轨 -->
<block wx:elif="{{imageStatus === 1 || imageStatus === 2 || imageStatus === 3}}">
<view class="head">
<view class="eyebrow">Processing</view>
<view class="display">AI 正在识别你的衣物…</view>
<view class="cd-note">通常需要 10 秒左右;处理较慢时可先返回,稍后再来查看</view>
</view>
<view class="up-preview">
<view class="ph icon icon-hanger"></view>
</view>
<view class="up-steps">
<view class="up-step {{step > 0 ? 'is-done' : ''}} {{step === 0 ? 'is-on' : ''}}">
<view class="up-dot"></view>
<text class="up-lb">排队中</text>
</view>
<view class="up-step {{step > 1 ? 'is-done' : ''}} {{step === 1 ? 'is-on' : ''}}">
<view class="up-dot"></view>
<text class="up-lb">AI 抠图</text>
</view>
<view class="up-step {{step === 2 ? 'is-on' : ''}}">
<view class="up-dot"></view>
<text class="up-lb">智能分类</text>
</view>
</view>
</block>
<!-- 识别失败 -->
<block wx:elif="{{imageStatus === 5}}">
<view class="up-preview">
<view class="up-bang">!</view>
</view>
<view class="head">
<view class="display">识别失败</view>
<view class="cd-note">{{failReason || '未知原因'}} · 本次上传额度已自动退回,你可以删除或重新上传</view>
</view>
<view class="up-duo">
<view class="btn btn--ghost" bindtap="onDelete">删除</view>
<view class="btn btn--ink" bindtap="onBackWardrobe">重新上传</view>
</view>
</block>
<!-- 正常详情 -->
<block wx:else>
<!-- 大图:买手店目录式线稿/实拍 -->
<view class="cd-photo">
<cached-image
wx:if="{{detail.processed_image}}"
class="pic"
src="{{detail.processed_image}}"
mode="aspectFit"
/>
<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}}">
<view class="grp-hd cd-name-hd">
<text class="gh-cn">名称</text>
<text class="gh-en">Name</text>
</view>
<input
class="cd-input"
value="{{formData.name}}"
bindinput="onNameInput"
placeholder="衣物名称"
placeholder-class="input-ph"
/>
</block>
<view wx:else class="t-lg">{{detail.name || '未命名衣物'}}</view>
</view>
<!-- 属性规格表 -->
<view class="cd-group" wx:for="{{attrGroups}}" wx:key="title" wx:for-item="group">
<view class="grp-hd">
<text class="gh-cn">{{group.title}}</text>
</view>
<view class="rows">
<view
class="spec {{editMode ? 'is-edit' : ''}}"
wx:for="{{group.attrs}}"
wx:key="key"
data-key="{{item.key}}"
bindtap="onAttrTap"
>
<text class="spec-k">{{item.label}}</text>
<view class="spec-v">
<!-- 量化评分:细描边方点 -->
<block wx:if="{{item.type === 'score'}}">
<view class="dots">
<view
class="d {{dotIdx < ((editMode ? formData[item.key] : detail[item.key]) || 0) ? 'on' : ''}}"
wx:for="{{[1, 2, 3, 4, 5]}}"
wx:for-item="dot"
wx:for-index="dotIdx"
wx:key="*this"
></view>
</view>
<text class="cd-score num">{{(editMode ? formData[item.key] : detail[item.key]) || 0}}/5</text>
</block>
<!-- 适用场合:无底色描边胶囊 -->
<block wx:elif="{{item.type === 'multi'}}">
<view class="cd-tags" wx:if="{{fmt.tags(editMode ? formData.occasions : detail.occasions).length}}">
<text
class="pill pill--line"
wx:for="{{fmt.tags(editMode ? formData.occasions : detail.occasions)}}"
wx:for-item="scene"
wx:key="*this"
>{{scene}}</text>
</view>
<text wx:else class="c-3">—</text>
</block>
<!-- 普通文本 / 主色 -->
<block wx:else>
<text class="spec-t">{{(editMode ? formData[item.key] : detail[item.key]) || '—'}}</text>
<view
wx:if="{{item.key === 'color_main' && fmt.hex(editMode ? formData.color_main : detail.color_main)}}"
class="dot"
style="background: {{fmt.hex(editMode ? formData.color_main : detail.color_main)}};"
></view>
</block>
</view>
<view wx:if="{{editMode}}" class="icon icon-chevron-right cd-arrow"></view>
</view>
</view>
</view>
<!-- 备注 -->
<view class="cd-group">
<view class="grp-hd">
<text class="gh-cn">备注信息</text>
<text class="gh-en">Note</text>
</view>
<view class="cd-remark">
<textarea
wx:if="{{editMode}}"
class="textarea"
value="{{formData.remark}}"
bindinput="onRemarkInput"
placeholder="添加备注…"
placeholder-class="input-ph"
/>
<view wx:elif="{{detail.remark}}" class="t-bd">{{detail.remark}}</view>
<view wx:else class="c-3">暂无备注</view>
</view>
</view>
</block>
</scroll-view>
<!-- 底部悬浮操作条 -->
<view
wx:if="{{!readonly && !notFound && imageStatus !== 1 && imageStatus !== 2 && imageStatus !== 3 && imageStatus !== 5}}"
class="dock"
>
<block wx:if="{{editMode}}">
<view class="btn btn--ink btn--block" bindtap="onSave">保存</view>
</block>
<block wx:else>
<view class="dock-row">
<view class="btn--text" bindtap="onDelete">删除这件衣物</view>
</view>
</block>
</view>
<!-- 底部属性选择面板 -->
<view wx:if="{{sheetVisible}}" class="scrim" bindtap="onSheetClose"></view>
<view wx:if="{{sheetVisible}}" class="sheet-wrap">
<view class="sheet" catchtap="noop">
<view class="sheet-grab"></view>
<view class="sheet-hd">
<text class="sheet-t">{{sheetLabel}}</text>
<text class="sheet-x" bindtap="onSheetConfirm">完成</text>
</view>
<scroll-view scroll-y class="sheet-body">
<!-- 评分:大号点选圆点 -->
<block wx:if="{{sheetType === 'score'}}">
<view class="sheet-score">
<view
class="sheet-score-dot {{index < (sheetValue * 1) ? 'on' : ''}}"
wx:for="{{[1, 2, 3, 4, 5]}}"
wx:key="*this"
data-value="{{item}}"
bindtap="onSheetScoreTap"
>{{item}}</view>
</view>
</block>
<!-- 多选(适用场合) -->
<block wx:elif="{{sheetType === 'multi'}}">
<view class="sheet-tags">
<view
class="sheet-tag {{sheetValues.indexOf(item) >= 0 ? 'on' : ''}}"
wx:for="{{sheetOptions}}"
wx:key="*this"
data-value="{{item}}"
bindtap="onSheetValueTap"
>{{item}}</view>
</view>
</block>
<!-- 主色:色板 + 自定义 -->
<block wx:elif="{{sheetType === 'color'}}">
<view class="sheet-colors">
<view
class="sheet-color {{sheetValue === item.name || sheetValue === item.value ? 'on' : ''}}"
wx:for="{{colorPresets}}"
wx:key="name"
data-name="{{item.name}}"
bindtap="onSheetColorTap"
>
<view class="sheet-color-dot" style="background: {{item.value}};"></view>
</view>
</view>
<input
class="input sheet-input"
value="{{sheetValue}}"
bindinput="onSheetInput"
placeholder="也可输入色值,如 #FFFFFF"
placeholder-class="input-ph"
/>
</block>
<!-- 文本:快捷选项 + 自定义输入 -->
<block wx:else>
<view class="sheet-tags" wx:if="{{sheetOptions.length}}">
<view
class="sheet-tag {{sheetValue === item ? 'on' : ''}}"
wx:for="{{sheetOptions}}"
wx:key="*this"
data-value="{{item}}"
bindtap="onSheetOptionTap"
>{{item}}</view>
</view>
<input
class="input sheet-input"
value="{{sheetValue}}"
bindinput="onSheetInput"
placeholder="请输入{{sheetLabel}}"
placeholder-class="input-ph"
/>
</block>
</scroll-view>
</view>
</view>
<!-- 删除确认:v2 玻璃弹窗 -->
<v2-overlay
visible="{{delVisible}}"
type="modal"
title="删除这件衣物"
content="{{delText}}"
buttons="{{delButtons}}"
bind:confirm="onDelConfirm"
bind:cancel="onDelCancel"
/>
</view>