fix(diy): 草稿持久化 + 进入工作台即设计中 + 编辑页空态兜底
修正工作流批次①(问题 2+3+4): - 问题2: 贴纸变化防抖 800ms 落缓存+服务端静默同步, useDidHide/卸载前强制 flush;中途退出不再丢布局 - 问题3: 进入工作台(新建/继续)即 PATCH designing(SUBMITTED), 取代原「确认完成才提交」触发点 - 问题4: 草稿实时写缓存后贴纸编辑页始终能读到 stickers; 另加 notFound 空态(未找到贴纸提示 + 返回工作台), handleEditSticker 跳转前 flushDraft 关闭防抖竞态窗口 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+62
-3
@@ -1,6 +1,6 @@
|
||||
import { View, Text, Image } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import './index.scss'
|
||||
import { getProductById } from '../../utils/productConfig'
|
||||
import { getDesignList, setDesignList, updateDesign, type DesignItem, type StickerItem } from '../../utils/store'
|
||||
@@ -27,7 +27,7 @@ export default function DIYPage() {
|
||||
const [previewMode, setPreviewMode] = useState(false)
|
||||
const [hasOverlap, setHasOverlap] = useState(false)
|
||||
|
||||
/** 进入 DIY 即创建清单条目:服务端创建(DRAFT,阶段0 决策#6),失败降级本地 */
|
||||
/** 进入 DIY 即创建清单条目,并推进到「设计中」(问题3 修正:进入工作台即 SUBMITTED),失败降级本地 */
|
||||
const createDesignEntry = (product: { id: string; name: string; price: number; icon: string }, count: number) => {
|
||||
createDesign({
|
||||
productId: product.id,
|
||||
@@ -38,6 +38,13 @@ export default function DIYPage() {
|
||||
.then(item => {
|
||||
setDesignList([item, ...getDesignList()]) // 回写缓存,checkout 等页面仍读缓存
|
||||
setDesignId(item.id)
|
||||
return updateDesignApi(item.id, { status: 'designing' })
|
||||
.then(updated => {
|
||||
setDesignList(getDesignList().map(d => d.id === item.id ? { ...d, status: updated.status } : d))
|
||||
})
|
||||
.catch(() => {
|
||||
setDesignList(getDesignList().map(d => d.id === item.id ? { ...d, status: 'designing' as const } : d))
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
const newDesign: DesignItem = {
|
||||
@@ -47,7 +54,7 @@ export default function DIYPage() {
|
||||
productIcon: product.icon,
|
||||
unitPrice: product.price,
|
||||
count,
|
||||
status: 'undesigned',
|
||||
status: 'designing',
|
||||
createdAt: new Date().toISOString().slice(0, 10)
|
||||
}
|
||||
setDesignList([...getDesignList(), newDesign])
|
||||
@@ -97,6 +104,12 @@ export default function DIYPage() {
|
||||
}
|
||||
setStickers([s])
|
||||
}
|
||||
|
||||
// 问题3 修正:继续设计同样进入「设计中」状态(单向状态机,undesigned → designing)
|
||||
if (design.status === 'undesigned') {
|
||||
updateDesign(dId, { status: 'designing' })
|
||||
updateDesignApi(dId, { status: 'designing' }).catch(() => {})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -111,6 +124,51 @@ export default function DIYPage() {
|
||||
}
|
||||
}, [])
|
||||
|
||||
// ---------- 草稿持久化(修正工作流 问题2)----------
|
||||
// 贴纸变化防抖 800ms:本地缓存即时同步 + 服务端静默同步;不推状态(SUBMITTED 只由显式动作触发)
|
||||
const draftTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const latestRef = useRef({ designId: '', quantity: 1, category: null as any, stickers: [] as StickerItem[] })
|
||||
latestRef.current = { designId, quantity, category, stickers }
|
||||
|
||||
const saveDraft = () => {
|
||||
const { designId: dId, quantity: qty, category: cat, stickers: st } = latestRef.current
|
||||
if (!dId || !cat) return
|
||||
// WCD 红线:基于缓存中服务端最新 designData 合并(保留 wordcloud 分组),只覆盖本页编辑字段
|
||||
const cached = getDesignList().find(d => d.id === dId)
|
||||
const designData = {
|
||||
...(cached && cached.designData ? cached.designData : {}),
|
||||
version: 1 as const,
|
||||
category: { id: cat.id, mask: cat.mask, ...(cat.tone ? { tone: cat.tone } : {}) },
|
||||
stickers: st
|
||||
}
|
||||
updateDesign(dId, { designData }) // 本地缓存即时同步(问题4:贴纸编辑页从这里读取)
|
||||
updateDesignApi(dId, {
|
||||
item: { productId: cat.id, productName: cat.name, unitPrice: cat.price, count: qty, designData }
|
||||
}).catch(() => {}) // 草稿静默降级:失败仅落缓存
|
||||
}
|
||||
|
||||
const flushDraft = () => {
|
||||
if (draftTimerRef.current) {
|
||||
clearTimeout(draftTimerRef.current)
|
||||
draftTimerRef.current = null
|
||||
}
|
||||
saveDraft()
|
||||
}
|
||||
|
||||
// 隐藏页面(切后台/跳转)时强制落盘,防止防抖窗口内的改动丢失
|
||||
Taro.useDidHide(flushDraft)
|
||||
// 卸载时同样落盘
|
||||
useEffect(() => flushDraft, [])
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
const skipDraftRef = useRef(true)
|
||||
useEffect(() => {
|
||||
if (skipDraftRef.current) { skipDraftRef.current = false; return }
|
||||
if (draftTimerRef.current) clearTimeout(draftTimerRef.current)
|
||||
draftTimerRef.current = setTimeout(saveDraft, 800)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [stickers])
|
||||
|
||||
// 矩形碰撞检测
|
||||
const checkOverlap = (list: StickerItem[]) => {
|
||||
const newList = list.map(s => ({ ...s, isOverlapping: false }))
|
||||
@@ -217,6 +275,7 @@ export default function DIYPage() {
|
||||
|
||||
const handleEditSticker = () => {
|
||||
if (!activeStickerId || !designId) return
|
||||
flushDraft() // 问题4:跳编辑页前清掉防抖窗口,缓存里保证有最新贴纸
|
||||
Taro.navigateTo({
|
||||
url: `/pages/diy/stickerEdit/index?designId=${designId}&stickerId=${activeStickerId}`
|
||||
})
|
||||
|
||||
@@ -226,3 +226,30 @@
|
||||
.mt-20 {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
/* 空态(贴纸未找到) */
|
||||
.edit-empty {
|
||||
margin: 24rpx 24rpx 0;
|
||||
padding: 60rpx 40rpx;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.edit-empty-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.edit-empty-desc {
|
||||
font-size: 26rpx;
|
||||
opacity: 0.65;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.edit-empty .btn-primary {
|
||||
padding: 16rpx 60rpx;
|
||||
border-radius: 44rpx;
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ export default function StickerEditPage() {
|
||||
const [designId, setDesignId] = useState('')
|
||||
const [stickerId, setStickerId] = useState('')
|
||||
const [sticker, setSticker] = useState<StickerItem | null>(null)
|
||||
const [notFound, setNotFound] = useState(false)
|
||||
|
||||
// 编辑参数
|
||||
const [brightness, setBrightness] = useState(0)
|
||||
@@ -152,6 +153,9 @@ export default function StickerEditPage() {
|
||||
setHue((st.edits && st.edits.hue) || 0)
|
||||
setContrast((st.edits && st.edits.contrast) || 0)
|
||||
initCanvas(st)
|
||||
} else {
|
||||
// 问题4 留意点:找不到贴纸时给出空态,而不是停在空白 Canvas
|
||||
setNotFound(true)
|
||||
}
|
||||
}, [initCanvas])
|
||||
|
||||
@@ -312,6 +316,29 @@ export default function StickerEditPage() {
|
||||
const hPct = `${((hue + 180) / 360) * 100}%`
|
||||
const cPct = `${((contrast + 100) / 200) * 100}%`
|
||||
|
||||
if (notFound) {
|
||||
return (
|
||||
<View className={`theme-${resolvedTheme}`}>
|
||||
<ThemedPageMeta />
|
||||
<ScrollTopMask title="编辑贴纸" targetSelector=".edit-header" showBack />
|
||||
<View className='sticker-edit-page'>
|
||||
<View className='edit-header' style={{ paddingTop: `${safe.statusBarHeight + 12}px` }}>
|
||||
<Text className='edit-back' onTap={() => Taro.navigateBack()}>←</Text>
|
||||
<Text className='edit-title'>编辑贴纸</Text>
|
||||
<Text className='edit-save' />
|
||||
</View>
|
||||
<View className='edit-empty surface-card mt-20'>
|
||||
<Text className='edit-empty-title'>未找到该贴纸</Text>
|
||||
<Text className='edit-empty-desc'>贴纸可能已被删除,请返回工作台重新添加</Text>
|
||||
<View className='btn-primary' onTap={() => Taro.navigateBack()}>
|
||||
<Text>返回工作台</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View className={`theme-${resolvedTheme}`}>
|
||||
<ThemedPageMeta />
|
||||
|
||||
Reference in New Issue
Block a user