import { View, Text, Image } from '@tarojs/components' import Taro from '@tarojs/taro' import { useState, useEffect } from 'react' import './index.scss' import { getProductById } from '../../utils/productConfig' import { getDesignList, setDesignList, updateDesign, type DesignItem, type StickerItem } from '../../utils/store' import { useThemeContext } from '../../context/ThemeContext' import { useSafeArea } from '../../hooks/useSafeArea' import { useStatusBar } from '../../hooks/useStatusBar' export default function DIYPage() { const { theme, resolvedTheme } = useThemeContext() const safe = useSafeArea() useStatusBar(resolvedTheme) const [category, setCategory] = useState(null) const [step, setStep] = useState(1) const [designId, setDesignId] = useState('') const [quantity, setQuantity] = useState(1) const [stickers, setStickers] = useState([]) const [activeStickerId, setActiveStickerId] = useState(null) const [isDragging, setIsDragging] = useState(false) const [startPos, setStartPos] = useState({ x: 0, y: 0 }) const [previewMode, setPreviewMode] = useState(false) const [hasOverlap, setHasOverlap] = useState(false) useEffect(() => { const params = Taro.getCurrentInstance().router?.params const source = params?.source const productId = params?.productId || params?.category if (source === 'product' && productId) { const product = getProductById(productId) if (product) { setCategory(product) setQuantity(Number(params?.quantity) || 1) const newDesign: DesignItem = { id: 'DSG' + Date.now(), productId: product.id, productName: product.name, productIcon: product.icon, unitPrice: product.price, count: Number(params?.quantity) || 1, status: 'designing', createdAt: new Date().toISOString().slice(0, 10) } const list = getDesignList() setDesignList([...list, newDesign]) setDesignId(newDesign.id) } return } if (source === 'designList' && params?.designId) { const dId = params.designId const list = getDesignList() const design = list.find(d => d.id === dId) if (design) { setDesignId(dId) setQuantity(design.count) const product = getProductById(design.productId) if (product) setCategory(product) if (design.designData?.stickers) { setStickers(design.designData.stickers) } else if (design.designData?.imageSrc) { // 兼容旧版数据 const s: StickerItem = { id: 'legacy_' + Date.now(), src: design.designData.imageSrc, x: design.designData.imagePos?.x || 0, y: design.designData.imagePos?.y || 0, scale: design.designData.imagePos?.scale || 1, width: 200, height: 200, isOverlapping: false } setStickers([s]) } } return } // 默认情况(从首页 old category 参数兼容) if (productId) { const found = getProductById(productId) if (found) { setCategory(found) const newDesign: DesignItem = { id: 'DSG' + Date.now(), productId: found.id, productName: found.name, productIcon: found.icon, unitPrice: found.price, count: 1, status: 'designing', createdAt: new Date().toISOString().slice(0, 10) } const list = getDesignList() setDesignList([...list, newDesign]) setDesignId(newDesign.id) } } }, []) // 矩形碰撞检测 const checkOverlap = (list: StickerItem[]) => { const newList = list.map(s => ({ ...s, isOverlapping: false })) for (let i = 0; i < newList.length; i++) { for (let j = i + 1; j < newList.length; j++) { const a = newList[i] const b = newList[j] const aw = a.width * a.scale const ah = a.height * a.scale const bw = b.width * b.scale const bh = b.height * b.scale if ( a.x < b.x + bw && a.x + aw > b.x && a.y < b.y + bh && a.y + ah > b.y ) { newList[i].isOverlapping = true newList[j].isOverlapping = true } } } const overlapAny = newList.some(s => s.isOverlapping) setHasOverlap(overlapAny) return newList } const addSticker = () => { Taro.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: (res) => { const src = res.tempFilePaths[0] // 获取图片尺寸用于碰撞检测 Taro.getImageInfo({ src, success: (info) => { const newSticker: StickerItem = { id: 'stk_' + Date.now(), src, x: 0, y: 0, scale: 1, width: info.width, height: info.height, isOverlapping: false } const next = [...stickers, newSticker] const checked = checkOverlap(next) setStickers(checked) setActiveStickerId(newSticker.id) }, fail: () => { // fallback 尺寸 const newSticker: StickerItem = { id: 'stk_' + Date.now(), src, x: 0, y: 0, scale: 1, width: 200, height: 200, isOverlapping: false } const next = [...stickers, newSticker] const checked = checkOverlap(next) setStickers(checked) setActiveStickerId(newSticker.id) } }) } }) } const deleteSticker = (id: string) => { const next = stickers.filter(s => s.id !== id) const checked = checkOverlap(next) setStickers(checked) if (activeStickerId === id) setActiveStickerId(null) } const handleStickerTouchStart = (e: any, id: string) => { const touch = e.touches[0] const s = stickers.find(x => x.id === id) if (!s) return setActiveStickerId(id) setIsDragging(true) setStartPos({ x: touch.clientX - s.x, y: touch.clientY - s.y }) } const handleStickerTouchMove = (e: any) => { if (!isDragging || !activeStickerId) return const touch = e.touches[0] const next = stickers.map(s => { if (s.id !== activeStickerId) return s return { ...s, x: touch.clientX - startPos.x, y: touch.clientY - startPos.y } }) const checked = checkOverlap(next) setStickers(checked) } const handleTouchEnd = () => setIsDragging(false) const handleEditSticker = () => { if (!activeStickerId || !designId) return Taro.navigateTo({ url: `/pages/diy/stickerEdit/index?designId=${designId}&stickerId=${activeStickerId}` }) } const handleScale = (id: string, delta: number) => { const next = stickers.map(s => { if (s.id !== id) return s return { ...s, scale: Math.max(0.3, Math.min(3, s.scale + delta)) } }) const checked = checkOverlap(next) setStickers(checked) } const goBack = () => { Taro.navigateBack() } const getMaskStyle = () => { if (!category) return { width: 300, height: 420 } const base: any = { width: category.mask.width, height: category.mask.height } if (category.mask.shape === 'rect') { base.borderRadius = category.mask.borderRadius || 0 } if (category.mask.shape === 'circle') { base.borderRadius = '50%' } return base } const handleComplete = () => { if (hasOverlap) { Taro.showToast({ title: '贴纸不能重叠', icon: 'none' }) return } if (designId) { updateDesign(designId, { designData: { stickers, category } }) } setPreviewMode(false) Taro.navigateTo({ url: `/pages/checkout/index?designId=${designId}` }) } if (!category) { return ( 加载中... ) } return ( 设计工作台 {/* 画布区域 — catchtouchmove 阻止事件冒泡导致页面滚动 */} {stickers.map(s => ( handleStickerTouchStart(e, s.id)} onTouchMove={handleStickerTouchMove} onTouchEnd={handleTouchEnd} onTap={() => setActiveStickerId(s.id)} mode='aspectFit' /> ))} {/* 贴纸列表与控制 */} 贴纸 {stickers.map(s => ( setActiveStickerId(s.id)} /> deleteSticker(s.id)}> × ))} + 添加 {hasOverlap && ⚠️ 有贴纸重叠,请调整位置} {/* 控制工具栏 */} {activeStickerId && ( 缩放 handleScale(activeStickerId, -0.1)}>- {Math.round((stickers.find(s => s.id === activeStickerId)?.scale || 1) * 100)}% handleScale(activeStickerId, 0.1)}>+ 提示 拖动调整位置,贴纸不可重叠 )} {/* 常驻编辑按钮 */} 编辑 {/* 底部操作按钮 */} setPreviewMode(true)}> 预览效果 添加贴纸 {/* 预览弹层 */} {previewMode && ( 确认效果 {stickers.map(s => ( ))} {category.name} 画布尺寸: {category.mask.width} × {category.mask.height} px {hasOverlap && 贴纸有重叠,不可提交} setPreviewMode(false)}> 返回修改 确认完成 )} ) }