feat:ui add

This commit is contained in:
2026-09-13 03:09:01 +08:00
parent fe3cd08229
commit 323f520d7b
5 changed files with 560 additions and 75 deletions
@@ -6,24 +6,90 @@ import { logger } from '../../utils/logger'
// 日志模块名
const LOG = 'clothes-detail'
const ATTRS = [
{ key: 'category_main', label: '大类', type: 'text' },
{ key: 'category_sub', label: '小类', type: 'text' },
{ key: 'style', label: '风格', type: 'text' },
{ key: 'color_main', label: '主色', type: 'color' },
{ key: 'color_sub', label: '副色', type: 'text' },
{ key: 'thickness', label: '厚度', type: 'text' },
{ key: 'version', label: '版型', type: 'text' },
{ key: 'material', label: '材质', type: 'text' },
{ key: 'pattern', label: '花纹', type: 'text' },
{ key: 'sleeve_type', label: '袖型', type: 'text' },
{ key: 'clothes_type', label: '衣长', type: 'text' },
{ key: 'neckline_type', label: '领型', type: 'text' },
{ key: 'formality', label: '正式程度', type: 'score' },
{ key: 'occasions', label: '适用场合', type: 'text' },
{ key: 'visual_impact', label: '视觉冲击力', type: 'score' },
// 属性分组定义(对齐设计稿:基础属性 / 版型与材质 / 智能量化评估)
const ATTR_GROUPS = [
{
title: '基础属性',
attrs: [
{ key: 'category_main', label: '大类', type: 'text' },
{ key: 'category_sub', label: '小类', type: 'text' },
{ key: 'style', label: '风格', type: 'text' },
{ key: 'color_main', label: '主色', type: 'color' },
{ key: 'color_sub', label: '副色', type: 'text' },
],
},
{
title: '版型与材质',
attrs: [
{ key: 'thickness', label: '厚度', type: 'text' },
{ key: 'version', label: '版型', type: 'text' },
{ key: 'material', label: '材质', type: 'text' },
{ key: 'pattern', label: '花纹', type: 'text' },
{ key: 'sleeve_type', label: '袖型', type: 'text' },
{ key: 'clothes_type', label: '衣长', type: 'text' },
{ key: 'neckline_type', label: '领型', type: 'text' },
],
},
{
title: '智能量化评估',
attrs: [
{ key: 'formality', label: '正式程度', type: 'score' },
{ key: 'occasions', label: '适用场合', type: 'multi' },
{ key: 'visual_impact', label: '视觉冲击力', type: 'score' },
],
},
]
// 属性快捷选项(选择面板内点选,仍支持自定义输入)
const ATTR_OPTIONS: Record<string, string[]> = {
category_main: ['上装', '下装', '连衣裙', '外套', '鞋', '配饰'],
style: ['简约', '休闲', '通勤', '运动', '甜美', '复古'],
thickness: ['薄', '适中', '厚'],
version: ['宽松', '修身', '阔腿', '紧身', '常规合身'],
material: ['纯棉', '牛仔布', '针织', '皮质', '羊毛', '涤纶'],
pattern: ['纯色', '条纹', '格纹', '印花', '波点'],
sleeve_type: ['无袖', '短袖', '长袖'],
clothes_type: ['短款', '常规', '中长', '长款'],
neckline_type: ['圆领', 'V领', '方领', '翻领', '高领'],
occasions: ['休闲', '通勤', '约会', '运动', '聚会', '正式', '度假'],
}
// 主色色板(名称 → 色值),供面板点选
const COLOR_PRESETS = [
{ name: '白色', value: '#FFFFFF' },
{ name: '黑色', value: '#1C1C1E' },
{ name: '灰色', value: '#8E8E93' },
{ name: '红色', value: '#FF3B30' },
{ name: '粉色', value: '#FF2D55' },
{ name: '橙色', value: '#FF9500' },
{ name: '黄色', value: '#FFCC00' },
{ name: '绿色', value: '#34C759' },
{ name: '蓝色', value: '#007AFF' },
{ name: '紫色', value: '#5856D6' },
{ name: '棕色', value: '#A2845E' },
{ name: '米色', value: '#F5EFE0' },
]
interface AttrMeta {
key: string
label: string
type: string
}
// 多值字段(适用场合)按分隔符拆分为标签
function splitValues(v: string): string[] {
if (!v) return []
return v.split(/[、,,/|;;\s]+/).filter((s) => !!s)
}
function findAttr(key: string): AttrMeta | null {
for (const group of ATTR_GROUPS) {
const hit = (group.attrs as AttrMeta[]).find((a) => a.key === key)
if (hit) return hit
}
return null
}
// 将后端 image_process_status 映射为前端展示步骤:0=排队中 1=AI 抠图 2=智能分类
function statusToStep(status: number): number {
if (status === ProcessStatus.QUEUED) return 0
@@ -35,7 +101,8 @@ function statusToStep(status: number): number {
Page({
data: {
uuid: '',
attrs: ATTRS,
attrGroups: ATTR_GROUPS,
colorPresets: COLOR_PRESETS,
detail: {} as ClothesItem,
formData: {} as ClothesItem,
imageStatus: 0,
@@ -44,6 +111,14 @@ Page({
loading: true,
editMode: false,
notFound: false,
// 底部属性选择面板
sheetVisible: false,
sheetKey: '',
sheetLabel: '',
sheetType: 'text',
sheetOptions: [] as string[],
sheetValue: '',
sheetValues: [] as string[],
},
onLoad(query: Record<string, string>) {
@@ -124,7 +199,7 @@ Page({
onToggleEdit() {
if (this.data.editMode) {
this.setData({ editMode: false })
this.setData({ editMode: false, sheetVisible: false })
} else {
this.setData({ editMode: true, formData: { ...this.data.detail } })
}
@@ -138,39 +213,70 @@ Page({
this.setData({ 'formData.remark': e.detail.value })
},
// 点击属性行:打开底部选择面板
onAttrTap(e: WechatMiniprogram.TouchEvent) {
if (!this.data.editMode) return
const key = e.currentTarget.dataset.key as string
const attr = ATTRS.find((a) => a.key === key)
const attr = findAttr(key)
if (!attr) return
const current = (this.data.formData as any)[key] || ''
if (attr.type === 'score') {
wx.showActionSheet({
itemList: ['1', '2', '3', '4', '5'],
success: (res) => {
this.setData({ [`formData.${key}`]: String(res.tapIndex + 1) })
},
})
return
}
wx.showModal({
title: `编辑${attr.label}`,
editable: true,
placeholderText: `请输入${attr.label}`,
content: current,
success: (res) => {
if (res.confirm) {
this.setData({ [`formData.${key}`]: res.content })
}
},
const current = (((this.data.formData as any)[key] || '') + '') as string
logger.debug(LOG, '打开属性面板, key=', key, 'type=', attr.type)
this.setData({
sheetVisible: true,
sheetKey: key,
sheetLabel: attr.label,
sheetType: attr.type,
sheetOptions: ATTR_OPTIONS[key] || [],
sheetValue: attr.type === 'multi' ? '' : current,
sheetValues: attr.type === 'multi' ? splitValues(current) : [],
})
},
onSheetClose() {
this.setData({ sheetVisible: false })
},
onSheetInput(e: WechatMiniprogram.Input) {
this.setData({ sheetValue: e.detail.value })
},
// 单选快捷选项
onSheetOptionTap(e: WechatMiniprogram.TouchEvent) {
this.setData({ sheetValue: e.currentTarget.dataset.value as string })
},
// 多选快捷选项(适用场合)
onSheetValueTap(e: WechatMiniprogram.TouchEvent) {
const value = e.currentTarget.dataset.value as string
const list = this.data.sheetValues.slice()
const idx = list.indexOf(value)
if (idx >= 0) list.splice(idx, 1)
else list.push(value)
this.setData({ sheetValues: list })
},
// 色板点选
onSheetColorTap(e: WechatMiniprogram.TouchEvent) {
this.setData({ sheetValue: e.currentTarget.dataset.name as string })
},
// 评分点选
onSheetScoreTap(e: WechatMiniprogram.TouchEvent) {
this.setData({ sheetValue: String(e.currentTarget.dataset.value) })
},
onSheetConfirm() {
const { sheetKey, sheetType, sheetValue, sheetValues } = this.data
if (!sheetKey) return
const value = sheetType === 'multi' ? sheetValues.join('、') : (sheetValue || '').trim()
logger.info(LOG, '属性更新, key=', sheetKey, 'value=', value)
this.setData({ [`formData.${sheetKey}`]: value, sheetVisible: false })
},
onSave() {
const fd = this.data.formData as any
const body: Record<string, any> = {}
// 后端 ClothesUpdateByUUIDRequest.ClothesUUID 为必填(binding required),请求体需携带
const body: Record<string, any> = { clothes_uuid: this.data.uuid }
const fields: Array<[string, string]> = [
['name', 'name'],
['remark', 'remark'],