商品浏览页构建前最后一次更新
This commit is contained in:
@@ -149,33 +149,29 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 空状态统一的在 app.scss */
|
||||
.login-guard {
|
||||
/* 空状态(主题适配) */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120px 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-guard-icon {
|
||||
font-size: 80px;
|
||||
.empty-icon-img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.login-guard-title {
|
||||
font-size: 36px;
|
||||
.empty-text {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.login-guard-desc {
|
||||
font-size: 28px;
|
||||
.empty-sub {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.login-guard-btn {
|
||||
padding: 20px 48px;
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,4 +1,4 @@
|
||||
import { View, Text, Input, Picker } from '@tarojs/components'
|
||||
import { View, Text, Image, Input, Picker } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState, useEffect } from 'react'
|
||||
import './index.scss'
|
||||
@@ -96,7 +96,7 @@ export default function AddressPage() {
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='flex-between' style={{ width: '100%' }}>
|
||||
<Text className='back-btn' onClick={() => Taro.navigateBack()}>←</Text>
|
||||
<Text className='back-btn' onTap={() => Taro.navigateBack()}>←</Text>
|
||||
<Text className='page-title'>收货地址</Text>
|
||||
<View style={{ width: '60px' }} />
|
||||
</View>
|
||||
@@ -119,16 +119,16 @@ export default function AddressPage() {
|
||||
</Text>
|
||||
</View>
|
||||
<View className='address-actions'>
|
||||
<Text className='address-act' onClick={() => openEdit(item)}>✏️ 编辑</Text>
|
||||
<Text className='address-act' onClick={() => handleCopy(item)}>📋 复制</Text>
|
||||
<Text className='address-act delete' onClick={() => handleDelete(item.id)}>🗑️ 删除</Text>
|
||||
<Text className='address-act' onTap={() => openEdit(item)}>✏️ 编辑</Text>
|
||||
<Text className='address-act' onTap={() => handleCopy(item)}>📋 复制</Text>
|
||||
<Text className='address-act delete' onTap={() => handleDelete(item.id)}>🗑️ 删除</Text>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
|
||||
{list.length === 0 && (
|
||||
<View className='empty-state'>
|
||||
<Text className='empty-icon'>📍</Text>
|
||||
<Image className='empty-icon-img' src='/icon/地址.png' mode='aspectFit' />
|
||||
<Text className='empty-text'>还没有收货地址</Text>
|
||||
<Text className='empty-sub'>点击下方按钮添加新地址</Text>
|
||||
</View>
|
||||
@@ -139,7 +139,7 @@ export default function AddressPage() {
|
||||
|
||||
{/* 底部添加按钮 */}
|
||||
<View className='action-bar'>
|
||||
<View className='btn-gradient' onClick={openAdd}>
|
||||
<View className='btn-gradient' onTap={openAdd}>
|
||||
<Text>新增收货地址</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -167,16 +167,16 @@ export default function AddressPage() {
|
||||
<Input className='form-input' placeholder='详细地址:街道、门牌号'
|
||||
value={detail} onInput={(e: any) => setDetail(e.detail.value)} />
|
||||
|
||||
<View className='form-row' onClick={() => setIsDefault(!isDefault)}>
|
||||
<View className='form-row' onTap={() => setIsDefault(!isDefault)}>
|
||||
<Text className='form-label'>设为默认地址</Text>
|
||||
<View className={`checkbox ${isDefault ? 'checked' : ''}`} />
|
||||
</View>
|
||||
|
||||
<View className='form-actions'>
|
||||
<View className='btn-outline' onClick={() => { setShowForm(false); resetForm() }}>
|
||||
<View className='btn-outline' onTap={() => { setShowForm(false); resetForm() }}>
|
||||
<Text>取消</Text>
|
||||
</View>
|
||||
<View className='btn-gradient' onClick={handleSave}>
|
||||
<View className='btn-gradient' onTap={handleSave}>
|
||||
<Text>保存</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -69,6 +69,154 @@
|
||||
color: var(--accent-pink);
|
||||
}
|
||||
|
||||
/* 地址卡片 */
|
||||
.checkout-address-card {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.checkout-address-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.address-change {
|
||||
padding: 6px 16px;
|
||||
border-radius: 8px;
|
||||
background: rgba(91, 140, 255, 0.08);
|
||||
}
|
||||
|
||||
.change-text {
|
||||
font-size: 24px;
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
.checkout-address-body {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.addr-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.addr-name {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.addr-phone {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.addr-detail {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.checkout-address-empty {
|
||||
padding: 32px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* 底部弹窗 — 地址选择(样式复用 app.scss 全局定义,移除非必要覆写) */
|
||||
|
||||
.addr-picker-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.addr-picker-title {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.addr-picker-close {
|
||||
font-size: 28px;
|
||||
color: var(--text-secondary);
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.addr-picker-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.addr-picker-item {
|
||||
padding: 20px 16px;
|
||||
border-bottom: 1px dashed rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.addr-picker-item.active {
|
||||
background: rgba(91, 140, 255, 0.06);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.addr-picker-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.addr-picker-name {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.addr-picker-phone {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.addr-picker-default {
|
||||
font-size: 22px;
|
||||
color: var(--accent-pink);
|
||||
border: 1px solid var(--accent-pink);
|
||||
padding: 2px 8px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.addr-picker-detail {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.addr-picker-empty {
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.addr-picker-add {
|
||||
margin-top: 16px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
border: 2px dashed var(--accent-blue);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.addr-picker-add-text {
|
||||
font-size: 28px;
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
/* 底部操作 */
|
||||
.checkout-actions {
|
||||
position: fixed;
|
||||
|
||||
+104
-25
@@ -1,14 +1,17 @@
|
||||
import { View, Text, Image } from '@tarojs/components'
|
||||
import { View, Text, Image, Button } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState, useEffect } from 'react'
|
||||
import './index.scss'
|
||||
import { getDesignList, designToOrder } from '../../utils/store'
|
||||
import { getDesignList, designToOrder, getDefaultAddress, getAddressList, type AddressItem } from '../../utils/store'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
|
||||
export default function CheckoutPage() {
|
||||
const { theme } = useThemeContext()
|
||||
const [design, setDesign] = useState<any>(null)
|
||||
const [address, setAddress] = useState<AddressItem | null>(null)
|
||||
const [showAddrPicker, setShowAddrPicker] = useState(false)
|
||||
const [addrList, setAddrList] = useState<AddressItem[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
const params = Taro.getCurrentInstance().router?.params
|
||||
@@ -17,8 +20,39 @@ export default function CheckoutPage() {
|
||||
const list = getDesignList()
|
||||
const d = list.find(x => x.id === dId)
|
||||
setDesign(d || null)
|
||||
setAddress(getDefaultAddress())
|
||||
setAddrList(getAddressList())
|
||||
}, [])
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!address) {
|
||||
Taro.showToast({ title: '请先添加收货地址', icon: 'none' })
|
||||
return
|
||||
}
|
||||
Taro.showModal({
|
||||
title: '确认下单',
|
||||
content: `确认后将从设计清单生成订单,并寄送至:\n${address.region?.join(' ')} ${address.detail}`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
designToOrder(design.id)
|
||||
Taro.showToast({ title: '下单成功', icon: 'success' })
|
||||
setTimeout(() => {
|
||||
Taro.switchTab({ url: '/pages/index/index' })
|
||||
}, 1500)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handlePickAddress = (addr: AddressItem) => {
|
||||
setAddress(addr)
|
||||
setShowAddrPicker(false)
|
||||
Taro.showToast({ title: '地址已更改', icon: 'success' })
|
||||
}
|
||||
|
||||
const hasStickers = design?.designData?.stickers && design.designData.stickers.length > 0
|
||||
const hasLegacyImage = design?.designData?.imageSrc
|
||||
|
||||
if (!design) {
|
||||
return (
|
||||
<View className={`theme-${theme}`}>
|
||||
@@ -31,7 +65,7 @@ export default function CheckoutPage() {
|
||||
)
|
||||
}
|
||||
|
||||
const maskStyle: any = { width: 300, height: 420 } // fallback
|
||||
const maskStyle: any = { width: 300, height: 420 }
|
||||
if (design.designData?.category?.mask) {
|
||||
const m = design.designData.category.mask
|
||||
maskStyle.width = m.width
|
||||
@@ -40,25 +74,6 @@ export default function CheckoutPage() {
|
||||
else maskStyle.borderRadius = m.borderRadius || 0
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
Taro.showModal({
|
||||
title: '确认下单',
|
||||
content: '确认后将从设计清单生成订单',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
designToOrder(design.id)
|
||||
Taro.showToast({ title: '下单成功', icon: 'success' })
|
||||
setTimeout(() => {
|
||||
Taro.switchTab({ url: '/pages/index/index' })
|
||||
}, 1500)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const hasStickers = design.designData?.stickers && design.designData.stickers.length > 0
|
||||
const hasLegacyImage = design.designData?.imageSrc
|
||||
|
||||
return (
|
||||
<View className={`theme-${theme}`}>
|
||||
<View className='checkout-page'>
|
||||
@@ -67,7 +82,7 @@ export default function CheckoutPage() {
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='flex-between' style={{ width: '100%' }}>
|
||||
<Text className='back-btn' onClick={() => Taro.navigateBack()}>←</Text>
|
||||
<Text className='back-btn' onTap={() => Taro.navigateBack()}>←</Text>
|
||||
<Text className='page-title'>设计效果确认</Text>
|
||||
<View style={{ width: '60px' }} />
|
||||
</View>
|
||||
@@ -125,17 +140,81 @@ export default function CheckoutPage() {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 收货地址 */}
|
||||
<View className='checkout-address-card dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='checkout-address-header'>
|
||||
<Text className='info-label'>收货地址</Text>
|
||||
<View className='address-change' onTap={() => setShowAddrPicker(true)}>
|
||||
<Text className='change-text'>更改收货地址</Text>
|
||||
</View>
|
||||
</View>
|
||||
{address ? (
|
||||
<View className='checkout-address-body'>
|
||||
<View className='addr-row'>
|
||||
<Text className='addr-name'>{address.name}</Text>
|
||||
<Text className='addr-phone'>{address.phone}</Text>
|
||||
</View>
|
||||
<Text className='addr-detail'>
|
||||
{address.region?.join(' ')} {address.detail}
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
<View className='checkout-address-empty' onTap={() => setShowAddrPicker(true)}>
|
||||
<Text className='empty-text'>暂无收货地址,点击添加</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 底部操作 */}
|
||||
<View className='checkout-actions'>
|
||||
<View className='btn-outline' onClick={() => Taro.navigateBack()}>
|
||||
<View className='btn-outline' onTap={() => Taro.navigateBack()}>
|
||||
<Text>返回修改</Text>
|
||||
</View>
|
||||
<View className='btn-gradient' onClick={handleConfirm}>
|
||||
<View className='btn-gradient' onTap={handleConfirm}>
|
||||
<Text>确认下单</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={{ height: '40px' }} />
|
||||
|
||||
{/* 地址选择底部弹窗 */}
|
||||
{showAddrPicker && (
|
||||
<View className='modal-overlay' style={{ alignItems: 'flex-end', justifyContent: 'flex-end' }}>
|
||||
<View className='addr-picker-sheet dashed-card'>
|
||||
<View className='addr-picker-header'>
|
||||
<Text className='addr-picker-title'>选择收货地址</Text>
|
||||
<Text className='addr-picker-close' onTap={() => setShowAddrPicker(false)}>✕</Text>
|
||||
</View>
|
||||
<View className='addr-picker-list'>
|
||||
{addrList.map((addr) => (
|
||||
<View
|
||||
key={addr.id}
|
||||
className={`addr-picker-item ${address?.id === addr.id ? 'active' : ''}`}
|
||||
onTap={() => handlePickAddress(addr)}
|
||||
>
|
||||
<View className='addr-picker-row'>
|
||||
<Text className='addr-picker-name'>{addr.name}</Text>
|
||||
<Text className='addr-picker-phone'>{addr.phone}</Text>
|
||||
{addr.isDefault && <Text className='addr-picker-default'>默认</Text>}
|
||||
</View>
|
||||
<Text className='addr-picker-detail'>
|
||||
{addr.region?.join(' ')} {addr.detail}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
{addrList.length === 0 && (
|
||||
<View className='addr-picker-empty'>
|
||||
<Text>暂无地址,请先添加收货地址</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View className='addr-picker-add' onTap={() => { setShowAddrPicker(false); Taro.navigateTo({ url: '/pages/address/index' }) }}>
|
||||
<Text className='addr-picker-add-text'>+ 新建收货地址</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
@@ -61,6 +61,12 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.design-icon-img {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.design-info {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -110,8 +116,9 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 80px;
|
||||
.empty-icon-img {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { View, Text, ScrollView } from '@tarojs/components'
|
||||
import { View, Text, ScrollView, Image } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import './index.scss'
|
||||
import { getDesignList, getUserInfo, type DesignItem } from '../../utils/store'
|
||||
import { PRODUCT_ICON_MAP } from '../../utils/productConfig'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
import LoginGuard from '../../components/LoginGuard'
|
||||
@@ -87,7 +88,7 @@ export default function DesignListPage() {
|
||||
<View
|
||||
key={tab.code}
|
||||
className={`tab-item ${activeTab === tab.code ? 'active' : ''}`}
|
||||
onClick={() => setActiveTab(tab.code)}
|
||||
onTap={() => setActiveTab(tab.code)}
|
||||
>
|
||||
<Text className='tab-label'>{tab.label}</Text>
|
||||
{activeTab === tab.code && <View className='tab-underline' />}
|
||||
@@ -100,11 +101,14 @@ export default function DesignListPage() {
|
||||
<ScrollView className='design-list' scrollY>
|
||||
{filtered.map(item => {
|
||||
const style = STATUS_STYLE[item.status]
|
||||
const iconSrc = item.productIcon?.startsWith('/icon/')
|
||||
? item.productIcon
|
||||
: PRODUCT_ICON_MAP[item.productId] || '/icon/四角星.png'
|
||||
return (
|
||||
<View key={item.id} className='design-card dashed-card'>
|
||||
<View className='star-badge' />
|
||||
<View className='design-body'>
|
||||
<Text className='design-icon'>{item.productIcon}</Text>
|
||||
<Image className='design-icon-img' src={iconSrc} mode='aspectFit' />
|
||||
<View className='design-info'>
|
||||
<View className='design-title-row'>
|
||||
<Text className='design-name'>{item.productName}</Text>
|
||||
@@ -116,12 +120,12 @@ export default function DesignListPage() {
|
||||
</View>
|
||||
<View className='design-actions'>
|
||||
{item.status !== 'ordered' && (
|
||||
<View className='btn-gradient design-btn' onClick={() => goDesign(item)}>
|
||||
<View className='btn-gradient design-btn' onTap={() => goDesign(item)}>
|
||||
<Text>{item.status === 'undesigned' ? '开始设计' : '继续设计'}</Text>
|
||||
</View>
|
||||
)}
|
||||
{item.status === 'ordered' && (
|
||||
<View className='btn-outline design-btn' onClick={() => Taro.switchTab({ url: '/pages/orders/index' })}>
|
||||
<View className='btn-outline design-btn' onTap={() => Taro.switchTab({ url: '/pages/orders/index' })}>
|
||||
<Text>查看订单</Text>
|
||||
</View>
|
||||
)}
|
||||
@@ -132,10 +136,10 @@ export default function DesignListPage() {
|
||||
|
||||
{filtered.length === 0 && (
|
||||
<View className='empty-state'>
|
||||
<Text className='empty-icon'>📐</Text>
|
||||
<Image className='empty-icon-img' src='/icon/调色盘.png' mode='aspectFit' />
|
||||
<Text className='empty-text'>还没有设计清单</Text>
|
||||
<Text className='empty-sub'>去首页逛逛,找到喜欢的定制品</Text>
|
||||
<View className='btn-gradient' onClick={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
||||
<View className='btn-gradient' onTap={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
||||
<Text>去逛逛</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
+11
-11
@@ -262,7 +262,7 @@ export default function DIYPage() {
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='flex-between' style={{ width: '100%' }}>
|
||||
<Text className='back-btn' onClick={goBack}>←</Text>
|
||||
<Text className='back-btn' onTap={goBack}>←</Text>
|
||||
<Text className='page-title'>设计工作台</Text>
|
||||
<View style={{ width: '60px' }} />
|
||||
</View>
|
||||
@@ -287,7 +287,7 @@ export default function DIYPage() {
|
||||
onTouchStart={(e) => handleStickerTouchStart(e, s.id)}
|
||||
onTouchMove={handleStickerTouchMove}
|
||||
onTouchEnd={handleTouchEnd}
|
||||
onClick={() => setActiveStickerId(s.id)}
|
||||
onTap={() => setActiveStickerId(s.id)}
|
||||
mode='aspectFit'
|
||||
/>
|
||||
))}
|
||||
@@ -302,13 +302,13 @@ export default function DIYPage() {
|
||||
<View className='sticker-list'>
|
||||
{stickers.map(s => (
|
||||
<View key={s.id} className={`sticker-thumb ${s.isOverlapping ? 'overlap' : ''} ${activeStickerId === s.id ? 'active' : ''}`}>
|
||||
<Image src={s.src} className='sticker-thumb-img' mode='aspectFit' onClick={() => setActiveStickerId(s.id)} />
|
||||
<View className='sticker-del' onClick={() => deleteSticker(s.id)}>
|
||||
<Image src={s.src} className='sticker-thumb-img' mode='aspectFit' onTap={() => setActiveStickerId(s.id)} />
|
||||
<View className='sticker-del' onTap={() => deleteSticker(s.id)}>
|
||||
<Text className='del-icon'>×</Text>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
<View className='sticker-add-btn' onClick={addSticker}>
|
||||
<View className='sticker-add-btn' onTap={addSticker}>
|
||||
<Text className='add-icon'>+</Text>
|
||||
<Text className='add-label'>添加</Text>
|
||||
</View>
|
||||
@@ -323,11 +323,11 @@ export default function DIYPage() {
|
||||
<View className='tool-row'>
|
||||
<Text className='tool-label'>缩放</Text>
|
||||
<View className='flex-center' style={{ gap: '20px' }}>
|
||||
<View className='tool-btn' onClick={() => handleScale(activeStickerId, -0.1)}>-</View>
|
||||
<View className='tool-btn' onTap={() => handleScale(activeStickerId, -0.1)}>-</View>
|
||||
<Text className='tool-value'>
|
||||
{Math.round((stickers.find(s => s.id === activeStickerId)?.scale || 1) * 100)}%
|
||||
</Text>
|
||||
<View className='tool-btn' onClick={() => handleScale(activeStickerId, 0.1)}>+</View>
|
||||
<View className='tool-btn' onTap={() => handleScale(activeStickerId, 0.1)}>+</View>
|
||||
</View>
|
||||
</View>
|
||||
<View className='tool-row'>
|
||||
@@ -339,10 +339,10 @@ export default function DIYPage() {
|
||||
|
||||
{/* 底部操作按钮 */}
|
||||
<View className='action-btns mt-20'>
|
||||
<View className='btn-gradient' onClick={() => setPreviewMode(true)}>
|
||||
<View className='btn-gradient' onTap={() => setPreviewMode(true)}>
|
||||
<Text>预览效果</Text>
|
||||
</View>
|
||||
<View className='btn-outline' onClick={addSticker}>
|
||||
<View className='btn-outline' onTap={addSticker}>
|
||||
<Text>添加贴纸</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -374,10 +374,10 @@ export default function DIYPage() {
|
||||
{hasOverlap && <Text className='overlap-hint'>贴纸有重叠,不可提交</Text>}
|
||||
</View>
|
||||
<View className='preview-actions'>
|
||||
<View className='btn-outline' onClick={() => setPreviewMode(false)}>
|
||||
<View className='btn-outline' onTap={() => setPreviewMode(false)}>
|
||||
<Text>返回修改</Text>
|
||||
</View>
|
||||
<View className={`btn-gradient ${hasOverlap ? 'disabled' : ''}`} onClick={handleComplete}>
|
||||
<View className={`btn-gradient ${hasOverlap ? 'disabled' : ''}`} onTap={handleComplete}>
|
||||
<Text>确认完成</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
+68
-36
@@ -12,7 +12,7 @@
|
||||
.header-title {
|
||||
font-size: 48px;
|
||||
font-weight: 800;
|
||||
color: #5c3a3a;
|
||||
color: var(--text-primary);
|
||||
letter-spacing: 4px;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f8f9fa;
|
||||
border: 1px dashed rgba(0, 0, 0, 0.06);
|
||||
border-radius: 40px;
|
||||
padding: 16px 24px;
|
||||
}
|
||||
@@ -34,29 +35,35 @@
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.search-icon-img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 28px;
|
||||
color: #5c3a3a;
|
||||
color: var(--text-primary);
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: #b08d8d;
|
||||
color: var(--text-secondary);
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.search-result-hint {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 2px dashed #eee;
|
||||
border-top: 2px dashed var(--line-star);
|
||||
}
|
||||
|
||||
.hint-text {
|
||||
font-size: 24px;
|
||||
color: #b08d8d;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* 成品展示轮播 */
|
||||
@@ -65,7 +72,7 @@
|
||||
}
|
||||
|
||||
.showcase-swiper {
|
||||
height: 460rpx;
|
||||
height: 520rpx;
|
||||
}
|
||||
|
||||
.showcase-swiper-item {
|
||||
@@ -79,44 +86,54 @@
|
||||
.showcase-swiper-card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-card);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.showcase-image-wrapper {
|
||||
width: 100%;
|
||||
height: 240rpx;
|
||||
border-radius: 16px;
|
||||
height: 320rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.showcase-emoji {
|
||||
font-size: 80px;
|
||||
.showcase-real-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.showcase-info {
|
||||
.showcase-text-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
background: var(--bg-card);
|
||||
padding: 16px 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.showcase-info .showcase-title {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: #5c3a3a;
|
||||
color: var(--text-primary);
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.showcase-info .showcase-desc {
|
||||
font-size: 24px;
|
||||
color: #b08d8d;
|
||||
color: var(--text-secondary);
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -124,12 +141,13 @@
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
background: rgba(255, 154, 158, 0.15);
|
||||
color: #ff6b81;
|
||||
background: rgba(255, 154, 158, 0.9);
|
||||
color: #ffffff;
|
||||
font-size: 20px;
|
||||
padding: 4px 16px;
|
||||
border-radius: 20px;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* 热门品类 */
|
||||
@@ -148,35 +166,48 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 32px 20px;
|
||||
padding: 0 0 24px;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.category-item:active {
|
||||
transform: scale(0.96);
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
font-size: 56px;
|
||||
margin-bottom: 12px;
|
||||
/* 品类顶部实物照片 */
|
||||
.category-img-wrapper {
|
||||
width: 100%;
|
||||
height: 180rpx;
|
||||
overflow: hidden;
|
||||
border-radius: 16px;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.category-icon-img {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: contain;
|
||||
margin-bottom: 12px;
|
||||
.category-img-real {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #5c3a3a;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.category-desc {
|
||||
font-size: 22px;
|
||||
color: #b08d8d;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.category-price {
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
color: var(--accent-pink);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
@@ -188,21 +219,22 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 64px;
|
||||
.empty-icon-img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
color: #5c3a3a;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.empty-sub {
|
||||
font-size: 24px;
|
||||
color: #b08d8d;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* 底部安全区 */
|
||||
|
||||
+46
-36
@@ -1,4 +1,4 @@
|
||||
import { View, Text, Swiper, SwiperItem, Input, Image } from '@tarojs/components'
|
||||
import { View, Text, Image, Input, Button, Swiper, SwiperItem } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState } from 'react'
|
||||
import './index.scss'
|
||||
@@ -6,16 +6,25 @@ import { CATEGORIES } from '../../utils/productConfig'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
|
||||
// 成品展示数据
|
||||
const SHOWCASES = [
|
||||
{ title: '毕业纪念笔记本', desc: '全班名字组成校徽', color: '#FFE4EC' },
|
||||
{ title: '铜质杯垫', desc: '金属质感桌面艺术', color: '#E8D5C4' },
|
||||
{ title: '竹制笔盒', desc: '自然竹纹文房雅器', color: '#E0F0D9' },
|
||||
{ title: '书本型灯', desc: '温暖光影点亮心意', color: '#FFF3CD' },
|
||||
{ title: '情侣定制礼', desc: '两个人的名字交织', color: '#FCE4EC' },
|
||||
{ title: '企业年会礼', desc: '员工名字组成Logo', color: '#E3F2FD' }
|
||||
// 成品展示配图(从 img 里取对应实物图)
|
||||
const SHOWCASE_LIST = [
|
||||
{ title: '毕业纪念笔记本', desc: '全班名字组成校徽', image: '/img/book_small/The1.jpg' },
|
||||
{ title: '铜质杯垫', desc: '金属质感桌面艺术', image: '/img/cup/The1.jpg' },
|
||||
{ title: '竹制笔盒', desc: '自然竹纹文房雅器', image: '/img/penbox/The1.jpg' },
|
||||
{ title: '书本型灯', desc: '温暖光影点亮心意', image: '/img/booklight/The1.jpg' },
|
||||
{ title: '情侣定制礼', desc: '两个人的名字交织', image: '/img/book_big/The1.jpg' },
|
||||
{ title: '企业年会礼', desc: '员工名字组成Logo', image: '/img/penbox/The2.jpg' }
|
||||
]
|
||||
|
||||
// 品类对应的实物照片映射(首页卡片顶部大图)
|
||||
const PRODUCT_IMG_MAP: Record<string, string> = {
|
||||
'notebook-small': '/img/book_small/The1.jpg',
|
||||
'notebook-large': '/img/book_big/The1.jpg',
|
||||
'coaster': '/img/cup/The1.jpg',
|
||||
'penbox': '/img/penbox/The1.jpg',
|
||||
'booklamp': '/img/booklight/The1.jpg'
|
||||
}
|
||||
|
||||
export default function Index() {
|
||||
const { theme } = useThemeContext()
|
||||
const [searchKey, setSearchKey] = useState('')
|
||||
@@ -48,7 +57,7 @@ export default function Index() {
|
||||
<View className='search-card dashed-card'>
|
||||
<View className='star-badge' />
|
||||
<View className='search-inner'>
|
||||
<Text className='search-icon'>🔍</Text>
|
||||
<Image className='search-icon-img' src='/icon/搜索.png' mode='aspectFit' />
|
||||
<Input
|
||||
className='search-input'
|
||||
type='text'
|
||||
@@ -84,21 +93,20 @@ export default function Index() {
|
||||
nextMargin='40rpx'
|
||||
indicatorDots
|
||||
>
|
||||
{SHOWCASES.map((item, idx) => (
|
||||
{SHOWCASE_LIST.map((item, idx) => (
|
||||
<SwiperItem key={idx} className='showcase-swiper-item'>
|
||||
<View className='showcase-swiper-card dashed-card'>
|
||||
<View className='star-badge' />
|
||||
<View
|
||||
className='showcase-image-wrapper'
|
||||
style={{ background: item.color }}
|
||||
>
|
||||
<Text className='showcase-emoji'>🎁</Text>
|
||||
<View className='showcase-image-wrapper'>
|
||||
<Image className='showcase-real-img' src={item.image} mode='aspectFill' />
|
||||
</View>
|
||||
<View className='showcase-info'>
|
||||
<Text className='showcase-title'>{item.title}</Text>
|
||||
<Text className='showcase-desc'>{item.desc}</Text>
|
||||
<View className='showcase-text-area'>
|
||||
<View className='showcase-info'>
|
||||
<Text className='showcase-title'>{item.title}</Text>
|
||||
<Text className='showcase-desc'>{item.desc}</Text>
|
||||
</View>
|
||||
<View className='showcase-tag'>成品案例</View>
|
||||
</View>
|
||||
<View className='showcase-tag'>成品案例</View>
|
||||
</View>
|
||||
</SwiperItem>
|
||||
))}
|
||||
@@ -110,26 +118,28 @@ export default function Index() {
|
||||
<Text className='section-title'>热门品类</Text>
|
||||
<View className='category-grid'>
|
||||
{filteredCategories.map((cat) => (
|
||||
<View
|
||||
key={cat.id}
|
||||
className='category-item dashed-card'
|
||||
onClick={() => navigateToProduct(cat.id)}
|
||||
>
|
||||
<View className='star-badge' />
|
||||
{cat.images && cat.images.length > 0 ? (
|
||||
<Image className='category-icon-img' src={cat.images[0]} mode='aspectFit' />
|
||||
) : (
|
||||
<Text className='category-icon'>{cat.icon}</Text>
|
||||
)}
|
||||
<Text className='category-name'>{cat.name}</Text>
|
||||
<Text className='category-desc'>{cat.desc}</Text>
|
||||
<Text className='category-price'>¥{cat.price} 起</Text>
|
||||
</View>
|
||||
<View
|
||||
key={cat.id}
|
||||
className='category-item dashed-card'
|
||||
onTap={() => navigateToProduct(cat.id)}
|
||||
>
|
||||
<View className='star-badge' />
|
||||
<View className='category-img-wrapper'>
|
||||
<Image
|
||||
className='category-img-real'
|
||||
src={PRODUCT_IMG_MAP[cat.id] || '/icon/四角星.png'}
|
||||
mode='aspectFill'
|
||||
/>
|
||||
</View>
|
||||
<Text className='category-name'>{cat.name}</Text>
|
||||
<Text className='category-desc'>{cat.desc}</Text>
|
||||
<Text className='category-price'>¥{cat.price} 起</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
{filteredCategories.length === 0 && (
|
||||
<View className='empty-category dashed-card'>
|
||||
<Text className='empty-icon'>🔍</Text>
|
||||
<Image className='empty-icon-img' src='/icon/搜索.png' mode='aspectFit' />
|
||||
<Text className='empty-text'>未找到匹配的品类</Text>
|
||||
<Text className='empty-sub'>尝试搜索“笔记本”、“杯垫”等</Text>
|
||||
</View>
|
||||
|
||||
@@ -99,29 +99,154 @@
|
||||
.address-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.address-icon {
|
||||
font-size: 32px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.address-title {
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.address-name,
|
||||
.address-phone,
|
||||
.address-change {
|
||||
padding: 6px 16px;
|
||||
border-radius: 8px;
|
||||
background: rgba(91, 140, 255, 0.08);
|
||||
}
|
||||
|
||||
.change-text {
|
||||
font-size: 24px;
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
.address-body {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.address-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.address-name {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.address-phone {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.address-detail {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
display: block;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.address-empty {
|
||||
padding: 32px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 底部弹窗 — 地址选择(样式复用 app.scss 全局定义,移除非必要覆写) */
|
||||
|
||||
.addr-picker-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.addr-picker-title {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.addr-picker-close {
|
||||
font-size: 28px;
|
||||
color: var(--text-secondary);
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.addr-picker-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.addr-picker-item {
|
||||
padding: 20px 16px;
|
||||
border-bottom: 1px dashed rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.addr-picker-item.active {
|
||||
background: rgba(91, 140, 255, 0.06);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.addr-picker-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.addr-picker-name {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.addr-picker-phone {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.addr-picker-default {
|
||||
font-size: 22px;
|
||||
color: var(--accent-pink);
|
||||
border: 1px solid var(--accent-pink);
|
||||
padding: 2px 8px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.addr-picker-detail {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.addr-picker-empty {
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.addr-picker-add {
|
||||
margin-top: 16px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
border: 2px dashed var(--accent-blue);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.addr-picker-add-text {
|
||||
font-size: 28px;
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
/* 卡片标题 icon(物流/地址) */
|
||||
.card-icon-img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* 商品卡片 */
|
||||
.product-card {
|
||||
padding: 32px;
|
||||
@@ -142,10 +267,14 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20px;
|
||||
overflow: hidden;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.product-icon {
|
||||
font-size: 56px;
|
||||
.product-icon-img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.product-info {
|
||||
@@ -220,7 +349,130 @@
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* 预览卡片 */
|
||||
/* 状态横幅 */
|
||||
.status-text {
|
||||
font-size: 48px;
|
||||
font-weight: 800;
|
||||
color: var(--text-primary);
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 物流信息 */
|
||||
.logistics-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px dashed rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.logistics-title {
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.logistics-num {
|
||||
font-size: 24px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.timeline {
|
||||
position: relative;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.timeline::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 8px;
|
||||
bottom: 8px;
|
||||
width: 2px;
|
||||
background: var(--line-star);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 24px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timeline-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.timeline-dot {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
background: var(--bg-card);
|
||||
border: 3px solid var(--line-star);
|
||||
margin-right: 20px;
|
||||
flex-shrink: 0;
|
||||
margin-left: -20px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.timeline-item.active .timeline-dot {
|
||||
background: var(--accent-pink);
|
||||
border-color: var(--accent-pink);
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.timeline-status {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.timeline-time {
|
||||
font-size: 24px;
|
||||
color: var(--text-secondary);
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.timeline-desc {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* 订单信息 */
|
||||
.meta-title {
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.meta-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.meta-label {
|
||||
font-size: 26px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.meta-value {
|
||||
font-size: 26px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.preview-card {
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
+169
-111
@@ -1,35 +1,48 @@
|
||||
import { View, Text, Image } from '@tarojs/components'
|
||||
import { View, Text, Image, Button } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState, useEffect } from 'react'
|
||||
import './index.scss'
|
||||
import { getOrderList, getDesignList } from '../../utils/store'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
import { getOrderList, getDefaultAddress, getAddressList, type AddressItem, type OrderItem } from '../../utils/store'
|
||||
import { PRODUCT_ICON_MAP } from '../../utils/productConfig'
|
||||
|
||||
export default function OrderDetailPage() {
|
||||
const { theme } = useThemeContext()
|
||||
const [order, setOrder] = useState<any>(null)
|
||||
const [design, setDesign] = useState<any>(null)
|
||||
const params = Taro.getCurrentInstance().router?.params
|
||||
const orderId = params?.orderId || ''
|
||||
|
||||
const [order, setOrder] = useState<OrderItem | null>(null)
|
||||
const [address, setAddress] = useState<AddressItem | null>(null)
|
||||
const [showAddrPicker, setShowAddrPicker] = useState(false)
|
||||
const [addrList, setAddrList] = useState<AddressItem[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
const params = Taro.getCurrentInstance().router?.params
|
||||
const orderId = params?.orderId
|
||||
if (!orderId) return
|
||||
const orders = getOrderList()
|
||||
const found = orders.find(o => o.id === orderId)
|
||||
setOrder(found || null)
|
||||
if (found) {
|
||||
const designs = getDesignList()
|
||||
const related = designs.find(d => d.orderId === orderId)
|
||||
setDesign(related || null)
|
||||
const o = orders.find(x => x.id === orderId)
|
||||
if (o) {
|
||||
setOrder(o)
|
||||
// 优先显示订单已有地址,否则用默认地址
|
||||
if ((o as any).address) {
|
||||
setAddress((o as any).address)
|
||||
} else {
|
||||
setAddress(getDefaultAddress())
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
setAddrList(getAddressList())
|
||||
}, [orderId])
|
||||
|
||||
const STATUS_MAP: Record<string, { label: string; badge: string; desc: string }> = {
|
||||
pending: { label: '待付款', badge: 'badge-pink', desc: '您尚未付款,请及时支付' },
|
||||
paid: { label: '待发货', badge: 'badge-blue', desc: '商家正在准备发货' },
|
||||
shipping: { label: '待收货', badge: 'badge-blue', desc: '快递运输中' },
|
||||
done: { label: '已完成', badge: 'badge-green', desc: '交易已完成' }
|
||||
const handlePickAddress = (addr: AddressItem) => {
|
||||
setAddress(addr)
|
||||
setShowAddrPicker(false)
|
||||
// 同步更新订单里的地址
|
||||
const orders = getOrderList()
|
||||
const idx = orders.findIndex(x => x.id === orderId)
|
||||
if (idx >= 0) {
|
||||
orders[idx] = { ...orders[idx], address: addr } as any
|
||||
// 这里不直接写回 store,用临时状态即可,实际后端会有 order update API
|
||||
}
|
||||
Taro.showToast({ title: '地址已更改', icon: 'success' })
|
||||
}
|
||||
|
||||
if (!order) {
|
||||
@@ -37,15 +50,13 @@ export default function OrderDetailPage() {
|
||||
<View className={`theme-${theme}`}>
|
||||
<View className='order-detail-page'>
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<Text className='page-title'>订单未找到</Text>
|
||||
<Text className='page-title'>订单不存在</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const status = STATUS_MAP[order.statusCode] || STATUS_MAP.pending
|
||||
|
||||
return (
|
||||
<View className={`theme-${theme}`}>
|
||||
<View className='order-detail-page'>
|
||||
@@ -54,70 +65,79 @@ export default function OrderDetailPage() {
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='flex-between' style={{ width: '100%' }}>
|
||||
<Text className='back-btn' onClick={() => Taro.navigateBack()}>←</Text>
|
||||
<Text className='back-btn' onTap={() => Taro.navigateBack()}>←</Text>
|
||||
<Text className='page-title'>订单详情</Text>
|
||||
<View style={{ width: '60px' }} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 状态卡片 */}
|
||||
<View className='status-card dashed-card mt-20'>
|
||||
{/* 状态横幅 */}
|
||||
<View className='status-banner dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='status-top'>
|
||||
<Text className={`badge ${status.badge} status-badge`}>{status.label}</Text>
|
||||
<Text className='status-desc'>{status.desc}</Text>
|
||||
</View>
|
||||
<View className='logistics-timeline'>
|
||||
<View className='timeline-item active'>
|
||||
<View className='timeline-dot' />
|
||||
<View className='timeline-content'>
|
||||
<Text className='timeline-title'>已支付</Text>
|
||||
<Text className='timeline-time'>{order.date}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className='timeline-item'>
|
||||
<View className='timeline-dot' />
|
||||
<View className='timeline-content'>
|
||||
<Text className='timeline-title'>商家发货</Text>
|
||||
<Text className='timeline-time'>待更新</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className='timeline-item'>
|
||||
<View className='timeline-dot' />
|
||||
<View className='timeline-content'>
|
||||
<Text className='timeline-title'>运输中</Text>
|
||||
<Text className='timeline-time'>待更新</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className='timeline-item'>
|
||||
<View className='timeline-dot' />
|
||||
<View className='timeline-content'>
|
||||
<Text className='timeline-title'>已签收</Text>
|
||||
<Text className='timeline-time'>待更新</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Text className='status-text'>
|
||||
{order.statusCode === 'pending' && '待付款'}
|
||||
{order.statusCode === 'paid' && '待发货'}
|
||||
{order.statusCode === 'shipping' && '待收货'}
|
||||
{order.statusCode === 'done' && '已完成'}
|
||||
</Text>
|
||||
<Text className='status-desc'>
|
||||
{order.statusCode === 'pending' && '请在30分钟内完成支付'}
|
||||
{order.statusCode === 'paid' && '商品正在打包中,即将发货'}
|
||||
{order.statusCode === 'shipping' && '快递运输中,请注意查收'}
|
||||
{order.statusCode === 'done' && '交易已完成,感谢惠顾'}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* 地址信息 */}
|
||||
<View className='address-card dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='address-header'>
|
||||
<Text className='address-icon'>📍</Text>
|
||||
<Text className='address-title'>收货地址</Text>
|
||||
{/* 物流信息(仅 shipping/done) */}
|
||||
{(order.statusCode === 'shipping' || order.statusCode === 'done') && (
|
||||
<View className='logistics-card dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='logistics-header'>
|
||||
<View className='flex-center'>
|
||||
<Image className='card-icon-img' src='/icon/包裹.png' mode='aspectFit' />
|
||||
<Text className='logistics-title'>物流信息</Text>
|
||||
</View>
|
||||
<Text className='logistics-num'>单号: SF1234567890</Text>
|
||||
</View>
|
||||
<View className='timeline'>
|
||||
<View className='timeline-item active'>
|
||||
<View className='timeline-dot' />
|
||||
<View className='timeline-content'>
|
||||
<Text className='timeline-status'>运输中</Text>
|
||||
<Text className='timeline-time'>2024-01-15 14:30</Text>
|
||||
<Text className='timeline-desc'>【北京市】快件已到达北京朝阳转运中心</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className='timeline-item'>
|
||||
<View className='timeline-dot' />
|
||||
<View className='timeline-content'>
|
||||
<Text className='timeline-status'>已发货</Text>
|
||||
<Text className='timeline-time'>2024-01-15 09:00</Text>
|
||||
<Text className='timeline-desc'>【深圳市】商家已发货,等待揽收</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className='timeline-item'>
|
||||
<View className='timeline-dot' />
|
||||
<View className='timeline-content'>
|
||||
<Text className='timeline-status'>已下单</Text>
|
||||
<Text className='timeline-time'>2024-01-14 20:15</Text>
|
||||
<Text className='timeline-desc'>订单已生成,等待商家发货</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Text className='address-name'>收货人:智绘微刻用户</Text>
|
||||
<Text className='address-phone'>138****8888</Text>
|
||||
<Text className='address-detail'>广东省深圳市南山区科技园智绘微刻总部</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 商品信息 */}
|
||||
<View className='product-card dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='section-title'>商品信息</Text>
|
||||
<View className='product-row'>
|
||||
<View className='product-icon-wrapper'>
|
||||
<Text className='product-icon'>{order.productIcon}</Text>
|
||||
<Image
|
||||
className='product-icon-img'
|
||||
src={order.productIcon?.startsWith('/icon/') ? order.productIcon : '/icon/四角星.png'}
|
||||
mode='aspectFit'
|
||||
/>
|
||||
</View>
|
||||
<View className='product-info'>
|
||||
<Text className='product-name'>{order.productName}</Text>
|
||||
@@ -125,59 +145,97 @@ export default function OrderDetailPage() {
|
||||
</View>
|
||||
</View>
|
||||
<View className='price-row'>
|
||||
<Text className='price-label'>商品总价</Text>
|
||||
<Text className='price-label'>实付款</Text>
|
||||
<Text className='price-value'>{order.price}</Text>
|
||||
</View>
|
||||
<View className='price-row'>
|
||||
<Text className='price-label'>运费</Text>
|
||||
<Text className='price-value'>¥0.00</Text>
|
||||
</View>
|
||||
<View className='price-row total'>
|
||||
<Text className='price-label total-label'>实付款</Text>
|
||||
<Text className='price-value total-value'>{order.price}</Text>
|
||||
</View>
|
||||
|
||||
{/* 收货地址 */}
|
||||
<View className='address-card dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='address-header'>
|
||||
<View className='flex-center'>
|
||||
<Image className='card-icon-img' src='/icon/地址.png' mode='aspectFit' />
|
||||
<Text className='address-title'>收货地址</Text>
|
||||
</View>
|
||||
<View className='address-change' onTap={() => setShowAddrPicker(true)}>
|
||||
<Text className='change-text'>更改收货地址</Text>
|
||||
</View>
|
||||
</View>
|
||||
{address ? (
|
||||
<View className='address-body'>
|
||||
<View className='address-row'>
|
||||
<Text className='address-name'>{address.name}</Text>
|
||||
<Text className='address-phone'>{address.phone}</Text>
|
||||
</View>
|
||||
<Text className='address-detail'>
|
||||
{address.region?.join(' ')} {address.detail}
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
<View className='address-empty'>
|
||||
<Text>暂无收货地址,请先添加</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 订单信息 */}
|
||||
<View className='info-card dashed-card mt-20'>
|
||||
<View className='meta-card dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='section-title'>订单信息</Text>
|
||||
<View className='info-row'>
|
||||
<Text className='info-label'>订单编号</Text>
|
||||
<Text className='info-value'>{order.id}</Text>
|
||||
<Text className='meta-title'>订单信息</Text>
|
||||
<View className='meta-row'>
|
||||
<Text className='meta-label'>订单编号</Text>
|
||||
<Text className='meta-value'>{order.id}</Text>
|
||||
</View>
|
||||
<View className='info-row'>
|
||||
<Text className='info-label'>创建时间</Text>
|
||||
<Text className='info-value'>{order.date}</Text>
|
||||
<View className='meta-row'>
|
||||
<Text className='meta-label'>创建时间</Text>
|
||||
<Text className='meta-value'>{order.date}</Text>
|
||||
</View>
|
||||
<View className='info-row'>
|
||||
<Text className='info-label'>支付方式</Text>
|
||||
<Text className='info-value'>微信支付</Text>
|
||||
<View className='meta-row'>
|
||||
<Text className='meta-label'>数量</Text>
|
||||
<Text className='meta-value'>{order.count} 件</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 设计效果 */}
|
||||
{design?.designData?.stickers ? (
|
||||
<View className='preview-card dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='section-title'>设计效果</Text>
|
||||
<View className='design-preview' style={{ width: design.designData.category?.mask?.width || 300, height: design.designData.category?.mask?.height || 420 }}>
|
||||
{design.designData.stickers.map((s: any) => (
|
||||
<Image key={s.id} className='preview-sticker' src={s.src} style={{ transform: `translate(${s.x}px, ${s.y}px) scale(${s.scale || 1})`, width: s.width || 200, height: s.height || 200 }} mode='aspectFit' />
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
) : design?.designData?.imageSrc ? (
|
||||
<View className='preview-card dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='section-title'>设计效果</Text>
|
||||
<View className='design-preview' style={{ width: design.designData.category?.mask?.width || 300, height: design.designData.category?.mask?.height || 420 }}>
|
||||
<Image className='preview-sticker' src={design.designData.imageSrc} style={{ transform: `translate(${design.designData.imagePos?.x || 0}px, ${design.designData.imagePos?.y || 0}px) scale(${design.designData.imagePos?.scale || 1})` }} mode='aspectFit' />
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
<View style={{ height: '40px' }} />
|
||||
|
||||
{/* 地址选择底部弹窗 */}
|
||||
{showAddrPicker && (
|
||||
<View className='modal-overlay' style={{ alignItems: 'flex-end', justifyContent: 'flex-end' }}>
|
||||
<View className='addr-picker-sheet dashed-card'>
|
||||
<View className='addr-picker-header'>
|
||||
<Text className='addr-picker-title'>选择收货地址</Text>
|
||||
<Text className='addr-picker-close' onTap={() => setShowAddrPicker(false)}>✕</Text>
|
||||
</View>
|
||||
<View className='addr-picker-list'>
|
||||
{addrList.map((addr) => (
|
||||
<View
|
||||
key={addr.id}
|
||||
className={`addr-picker-item ${address?.id === addr.id ? 'active' : ''}`}
|
||||
onTap={() => handlePickAddress(addr)}
|
||||
>
|
||||
<View className='addr-picker-row'>
|
||||
<Text className='addr-picker-name'>{addr.name}</Text>
|
||||
<Text className='addr-picker-phone'>{addr.phone}</Text>
|
||||
{addr.isDefault && <Text className='addr-picker-default'>默认</Text>}
|
||||
</View>
|
||||
<Text className='addr-picker-detail'>
|
||||
{addr.region?.join(' ')} {addr.detail}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
{addrList.length === 0 && (
|
||||
<View className='addr-picker-empty'>
|
||||
<Text>暂无地址,请先添加收货地址</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View className='addr-picker-add' onTap={() => { setShowAddrPicker(false); Taro.navigateTo({ url: '/pages/address/index' }) }}>
|
||||
<Text className='addr-picker-add-text'>+ 新建收货地址</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
@@ -81,6 +81,10 @@
|
||||
.order-icon {
|
||||
font-size: 56px;
|
||||
}
|
||||
.order-icon-img {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
.order-info {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -141,18 +145,19 @@
|
||||
align-items: center;
|
||||
padding: 120px 40px;
|
||||
}
|
||||
.empty-icon {
|
||||
font-size: 100px;
|
||||
.empty-icon-img {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.empty-text {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
color: #5c3a3a;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.empty-sub {
|
||||
font-size: 28px;
|
||||
color: #b08d8d;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
+15
-11
@@ -1,8 +1,9 @@
|
||||
import { View, Text, ScrollView } from '@tarojs/components'
|
||||
import { View, Text, ScrollView, Image } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState, useEffect } from 'react'
|
||||
import './index.scss'
|
||||
import { getOrderList, type OrderItem } from '../../utils/store'
|
||||
import { PRODUCT_ICON_MAP } from '../../utils/productConfig'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
import LoginGuard from '../../components/LoginGuard'
|
||||
@@ -87,7 +88,7 @@ export default function OrdersPage() {
|
||||
<View
|
||||
key={tab.code}
|
||||
className={`tab-item ${activeTab === tab.code ? 'active' : ''}`}
|
||||
onClick={() => setActiveTab(tab.code)}
|
||||
onTap={() => setActiveTab(tab.code)}
|
||||
>
|
||||
<Text className='tab-label'>{tab.label}</Text>
|
||||
{activeTab === tab.code && <View className='tab-underline' />}
|
||||
@@ -100,8 +101,11 @@ export default function OrdersPage() {
|
||||
<ScrollView className='orders-list' scrollY>
|
||||
{filteredOrders.map((order) => {
|
||||
const status = STATUS_MAP[order.statusCode] || STATUS_MAP.pending
|
||||
const iconSrc = order.productIcon?.startsWith('/icon/')
|
||||
? order.productIcon
|
||||
: '/icon/四角星.png'
|
||||
return (
|
||||
<View key={order.id} className='order-card dashed-card' onClick={() => goDetail(order)}>
|
||||
<View key={order.id} className='order-card dashed-card' onTap={() => goDetail(order)}>
|
||||
<View className='star-badge' />
|
||||
<View className='order-header'>
|
||||
<Text className='order-date'>{order.date}</Text>
|
||||
@@ -109,7 +113,7 @@ export default function OrdersPage() {
|
||||
</View>
|
||||
<View className='order-body'>
|
||||
<View className='order-icon-wrapper'>
|
||||
<Text className='order-icon'>{order.productIcon}</Text>
|
||||
<Image className='order-icon-img' src={iconSrc} mode='aspectFit' />
|
||||
</View>
|
||||
<View className='order-info'>
|
||||
<Text className='order-product'>{order.productName}</Text>
|
||||
@@ -135,25 +139,25 @@ export default function OrdersPage() {
|
||||
)}
|
||||
{order.statusCode === 'shipping' && (
|
||||
<>
|
||||
<View className='btn-outline order-btn' onClick={(e) => { e.stopPropagation(); Taro.showToast({ title: '查看物流', icon: 'none' }) }}>
|
||||
<View className='btn-outline order-btn' onTap={(e) => { e.stopPropagation(); Taro.showToast({ title: '查看物流', icon: 'none' }) }}>
|
||||
<Text>查看物流</Text>
|
||||
</View>
|
||||
<View className='btn-gradient order-btn' onClick={(e) => { e.stopPropagation(); Taro.showToast({ title: '确认收货', icon: 'none' }) }}>
|
||||
<View className='btn-gradient order-btn' onTap={(e) => { e.stopPropagation(); Taro.showToast({ title: '确认收货', icon: 'none' }) }}>
|
||||
<Text>确认收货</Text>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
{order.statusCode === 'done' && (
|
||||
<>
|
||||
<View className='btn-outline order-btn' onClick={(e) => { e.stopPropagation(); Taro.showToast({ title: '申请售后', icon: 'none' }) }}>
|
||||
<View className='btn-outline order-btn' onTap={(e) => { e.stopPropagation(); Taro.showToast({ title: '申请售后', icon: 'none' }) }}>
|
||||
<Text>申请售后</Text>
|
||||
</View>
|
||||
<View className='btn-gradient order-btn' onClick={(e) => { e.stopPropagation(); Taro.showToast({ title: '再来一单', icon: 'none' }) }}>
|
||||
<View className='btn-gradient order-btn' onTap={(e) => { e.stopPropagation(); Taro.showToast({ title: '再来一单', icon: 'none' }) }}>
|
||||
<Text>再来一单</Text>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
<View className='btn-outline order-btn' onClick={(e) => { e.stopPropagation(); goDetail(order) }}>
|
||||
<View className='btn-outline order-btn' onTap={(e) => { e.stopPropagation(); goDetail(order) }}>
|
||||
<Text>详情</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -164,10 +168,10 @@ export default function OrdersPage() {
|
||||
|
||||
{filteredOrders.length === 0 && (
|
||||
<View className='empty-state'>
|
||||
<Text className='empty-icon'>📦</Text>
|
||||
<Image className='empty-icon-img' src='/icon/包裹.png' mode='aspectFit' />
|
||||
<Text className='empty-text'>暂无订单</Text>
|
||||
<Text className='empty-sub'>去首页开始定制吧</Text>
|
||||
<View className='btn-gradient' onClick={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
||||
<View className='btn-gradient' onTap={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
||||
<Text>去定制</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -33,6 +33,12 @@
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.hero-icon-img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.hero-name {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
@@ -148,6 +154,11 @@
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.modal-icon-img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.modal-name {
|
||||
font-size: 32px;
|
||||
color: var(--text-primary);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { getProductById } from '../../utils/productConfig'
|
||||
import { addDesign } from '../../utils/store'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
import { getProductIconImg } from '../../utils/productConfig'
|
||||
|
||||
const HERO_COLORS = ['#FFE4EC', '#FFF0F5', '#FCE4EC']
|
||||
|
||||
@@ -60,7 +61,7 @@ export default function ProductPage() {
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='flex-between'>
|
||||
<Text className='back-btn' onClick={() => Taro.navigateBack()}>←</Text>
|
||||
<Text className='back-btn' onTap={() => Taro.navigateBack()}>←</Text>
|
||||
<Text className='page-title'>商品详情</Text>
|
||||
<View style={{ width: '60px' }} />
|
||||
</View>
|
||||
@@ -92,7 +93,7 @@ export default function ProductPage() {
|
||||
<SwiperItem key={idx}>
|
||||
<View className='hero-card dashed-card' style={{ background: color }}>
|
||||
<View className='star-badge' />
|
||||
<Text className='hero-icon'>{product.icon}</Text>
|
||||
<Image className='hero-icon-img' src={getProductIconImg(product)} mode='aspectFit' />
|
||||
<Text className='hero-name'>{product.name}</Text>
|
||||
</View>
|
||||
</SwiperItem>
|
||||
@@ -131,10 +132,10 @@ export default function ProductPage() {
|
||||
<Text className='summary-label'>起价</Text>
|
||||
<Text className='summary-price'>¥{product.price}</Text>
|
||||
</View>
|
||||
<View className='btn-outline' onClick={handleAddToList}>
|
||||
<View className='btn-outline' onTap={handleAddToList}>
|
||||
<Text>加入设计清单</Text>
|
||||
</View>
|
||||
<View className='btn-gradient' onClick={goToDIY}>
|
||||
<View className='btn-gradient' onTap={goToDIY}>
|
||||
<Text>立即下单</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -149,15 +150,15 @@ export default function ProductPage() {
|
||||
<View className='star-badge' />
|
||||
<Text className='modal-title'>加入设计清单</Text>
|
||||
<View className='modal-product'>
|
||||
<Text className='modal-icon'>{product.icon}</Text>
|
||||
<Image className='modal-icon-img' src={getProductIconImg(product)} mode='aspectFit' />
|
||||
<Text className='modal-name'>{product.name}</Text>
|
||||
</View>
|
||||
<View className='quantity-row'>
|
||||
<Text className='qty-label'>数量</Text>
|
||||
<View className='qty-control'>
|
||||
<View className='qty-btn' onClick={() => modQty(-1)}>-</View>
|
||||
<View className='qty-btn' onTap={() => modQty(-1)}>-</View>
|
||||
<Text className='qty-num'>{quantity}</Text>
|
||||
<View className='qty-btn' onClick={() => modQty(1)}>+</View>
|
||||
<View className='qty-btn' onTap={() => modQty(1)}>+</View>
|
||||
</View>
|
||||
</View>
|
||||
<View className='modal-total'>
|
||||
@@ -165,10 +166,10 @@ export default function ProductPage() {
|
||||
<Text className='total-price'>¥{(product.price * quantity).toFixed(2)}</Text>
|
||||
</View>
|
||||
<View className='modal-actions'>
|
||||
<View className='btn-outline' onClick={() => setShowModal(false)}>
|
||||
<View className='btn-outline' onTap={() => setShowModal(false)}>
|
||||
<Text>取消</Text>
|
||||
</View>
|
||||
<View className='btn-gradient' onClick={confirmAdd}>
|
||||
<View className='btn-gradient' onTap={confirmAdd}>
|
||||
<Text>确定</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -70,22 +70,20 @@
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.status-row.row2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2px 1fr;
|
||||
}
|
||||
|
||||
.status-row.row3 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2px 1fr 2px 1fr;
|
||||
/* 每组(item + divider)作为一个 flex 单元 */
|
||||
.status-group {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
padding: 8px 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.status-num {
|
||||
@@ -105,6 +103,7 @@
|
||||
height: 48px;
|
||||
background: var(--line-star);
|
||||
opacity: 0.25;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status-divider-h {
|
||||
@@ -168,7 +167,37 @@
|
||||
border-radius: 40px !important;
|
||||
}
|
||||
|
||||
/* 登录弹窗 */
|
||||
.menu-icon-img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/* 状态 icon 图片 */
|
||||
.status-icon-img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* 企业定制icon */
|
||||
.enterprise-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
/* 头像占位图 */
|
||||
.avatar-icon-img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
/* 登录弹窗头像占位 */
|
||||
.avatar-placeholder-img {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
||||
+83
-59
@@ -1,18 +1,35 @@
|
||||
import { View, Text, Image, Input, Button } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState, useEffect } from 'react'
|
||||
import Taro, { useDidShow } from '@tarojs/taro'
|
||||
import { useState } from 'react'
|
||||
import './index.scss'
|
||||
import { getOrderList, getUserInfo, setUserInfo, getDesignList } from '../../utils/store'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
|
||||
const ICON_MAP: Record<string, string> = {
|
||||
'我的设计': '/icon/调色盘.png',
|
||||
'词云生成': '/icon/词云生成.png',
|
||||
'收货地址': '/icon/地址.png',
|
||||
'联系客服': '/icon/电话.png',
|
||||
'使用帮助': '/icon/使用帮助.png',
|
||||
'设置': '/icon/设置.png'
|
||||
}
|
||||
|
||||
const STATUS_MAP = [
|
||||
{ key: 'toDesign', label: '待设计', icon: '/icon/三角尺.png' },
|
||||
{ key: 'pending', label: '待付款', icon: '/icon/四角星.png' },
|
||||
{ key: 'paid', label: '待发货', icon: '/icon/包裹.png' },
|
||||
{ key: 'shipping', label: '待收货', icon: '/icon/杯子.png' },
|
||||
{ key: 'done', label: '已完成', icon: '/icon/书本.png' }
|
||||
]
|
||||
|
||||
const MENU_ITEMS = [
|
||||
{ icon: '🎨', label: '我的设计', path: '/pages/designList/index' },
|
||||
{ icon: '✨', label: '词云生成', path: '/pages/wordcloud/index' },
|
||||
{ icon: '📍', label: '收货地址', path: '/pages/address/index' },
|
||||
{ icon: '📞', label: '联系客服', path: '/pages/service/index' },
|
||||
{ icon: '📋', label: '使用帮助', path: '/pages/agreement/index' },
|
||||
{ icon: '⚙️', label: '设置', path: '/pages/settings/index' }
|
||||
{ label: '我的设计', path: '/pages/designList/index' },
|
||||
{ label: '词云生成', path: '/pages/wordcloud/index' },
|
||||
{ label: '收货地址', path: '/pages/address/index' },
|
||||
{ label: '联系客服', path: '/pages/service/index' },
|
||||
{ label: '使用帮助', path: '/pages/agreement/index' },
|
||||
{ label: '设置', path: '/pages/settings/index' }
|
||||
]
|
||||
|
||||
export default function ProfilePage() {
|
||||
@@ -38,14 +55,21 @@ export default function ProfilePage() {
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const sync = () => {
|
||||
const info = getUserInfo()
|
||||
if (info) {
|
||||
if (info?.openid) {
|
||||
setIsLoggedIn(true)
|
||||
setUserInfoState({ nickName: info.nickName || '', avatarUrl: info.avatarUrl || '' })
|
||||
} else {
|
||||
setIsLoggedIn(false)
|
||||
setUserInfoState({ nickName: '', avatarUrl: '' })
|
||||
}
|
||||
loadStats()
|
||||
}, [])
|
||||
}
|
||||
|
||||
useDidShow(() => {
|
||||
sync()
|
||||
})
|
||||
|
||||
const handleOpenLogin = () => setShowLogin(true)
|
||||
const handleCloseLogin = () => setShowLogin(false)
|
||||
@@ -77,18 +101,12 @@ export default function ProfilePage() {
|
||||
}
|
||||
|
||||
const handleQuickClick = (type: string) => {
|
||||
if (!isLoggedIn) {
|
||||
Taro.showToast({ title: '请先登录', icon: 'none' })
|
||||
setShowLogin(true)
|
||||
return
|
||||
}
|
||||
switch (type) {
|
||||
case 'toDesign':
|
||||
Taro.setStorageSync('designList:filter', 'toDesign')
|
||||
Taro.switchTab({ url: '/pages/designList/index' })
|
||||
break
|
||||
case 'pending':
|
||||
// 跳转订单页并带状态参数 - 这里用 event 或 storage 传递
|
||||
Taro.setStorageSync('orders:filter', 'pending')
|
||||
Taro.switchTab({ url: '/pages/orders/index' })
|
||||
break
|
||||
@@ -107,18 +125,24 @@ export default function ProfilePage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleMenuClick = (path: string) => {
|
||||
if (!path) {
|
||||
Taro.showToast({ title: '功能开发中', icon: 'none' })
|
||||
const handleMenuClick = (path: string, label: string) => {
|
||||
if (!isLoggedIn && (path.includes('designList') || path.includes('orders'))) {
|
||||
Taro.showToast({ title: '请先登录', icon: 'none' })
|
||||
setShowLogin(true)
|
||||
return
|
||||
}
|
||||
if (label === '联系客服') {
|
||||
Taro.navigateTo({
|
||||
url: '/pages/service/index',
|
||||
fail: (err: any) => {
|
||||
console.error('navigateTo service fail', err)
|
||||
Taro.showToast({ title: '跳转失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
if (path.startsWith('/pages/')) {
|
||||
if (path.includes('designList') || path.includes('orders')) {
|
||||
if (!isLoggedIn) {
|
||||
Taro.showToast({ title: '请先登录', icon: 'none' })
|
||||
setShowLogin(true)
|
||||
return
|
||||
}
|
||||
Taro.switchTab({
|
||||
url: path,
|
||||
fail: (err: any) => {
|
||||
@@ -139,7 +163,7 @@ export default function ProfilePage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<View className={`theme-${theme}`}>
|
||||
<View className={`theme-${theme}`} style={{ minHeight: '100vh' }}>
|
||||
<View className='profile-page'>
|
||||
<ThemeToggle />
|
||||
|
||||
@@ -150,8 +174,8 @@ export default function ProfilePage() {
|
||||
{isLoggedIn && userInfo.avatarUrl ? (
|
||||
<Image className='avatar' src={userInfo.avatarUrl} mode='aspectFill' />
|
||||
) : (
|
||||
<View className='avatar-placeholder' onClick={handleOpenLogin}>
|
||||
<Text className='avatar-icon'>👤</Text>
|
||||
<View className='avatar-placeholder' onTap={handleOpenLogin}>
|
||||
<Image className='avatar-icon-img' src='/icon/个人.png' mode='aspectFit' />
|
||||
</View>
|
||||
)}
|
||||
<View className='user-meta'>
|
||||
@@ -162,42 +186,39 @@ export default function ProfilePage() {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Text className='user-name' onClick={handleOpenLogin}>点击登录</Text>
|
||||
<Text className='user-name' onTap={handleOpenLogin}>点击登录</Text>
|
||||
<Text className='user-level'>授权微信,开启专属定制</Text>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 5状态快捷入口 */}
|
||||
{/* 5 状态快捷入口 */}
|
||||
<View className='status-grid'>
|
||||
<View className='status-row row2'>
|
||||
<View className='status-item' onClick={() => handleQuickClick('toDesign')}>
|
||||
<Text className='status-num'>{orderStats.toDesign}</Text>
|
||||
<Text className='status-label'>待设计</Text>
|
||||
</View>
|
||||
<View className='status-divider' />
|
||||
<View className='status-item' onClick={() => handleQuickClick('pending')}>
|
||||
<Text className='status-num'>{orderStats.pending}</Text>
|
||||
<Text className='status-label'>待付款</Text>
|
||||
</View>
|
||||
{STATUS_MAP.slice(0, 2).map((s) => (
|
||||
<View key={s.key} className='status-group'>
|
||||
<View className='status-item' onTap={() => handleQuickClick(s.key)}>
|
||||
<Image className='status-icon-img' src={s.icon} mode='aspectFit' />
|
||||
<Text className='status-num'>{orderStats[s.key as keyof typeof orderStats]}</Text>
|
||||
<Text className='status-label'>{s.label}</Text>
|
||||
</View>
|
||||
<View className='status-divider' />
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
<View className='status-divider-h' />
|
||||
<View className='status-row row3'>
|
||||
<View className='status-item' onClick={() => handleQuickClick('paid')}>
|
||||
<Text className='status-num'>{orderStats.paid}</Text>
|
||||
<Text className='status-label'>待发货</Text>
|
||||
</View>
|
||||
<View className='status-divider' />
|
||||
<View className='status-item' onClick={() => handleQuickClick('shipping')}>
|
||||
<Text className='status-num'>{orderStats.shipping}</Text>
|
||||
<Text className='status-label'>待收货</Text>
|
||||
</View>
|
||||
<View className='status-divider' />
|
||||
<View className='status-item' onClick={() => handleQuickClick('done')}>
|
||||
<Text className='status-num'>{orderStats.done}</Text>
|
||||
<Text className='status-label'>已完成</Text>
|
||||
</View>
|
||||
{STATUS_MAP.slice(2).map((s, idx) => (
|
||||
<View key={s.key} className='status-group'>
|
||||
<View className='status-item' onTap={() => handleQuickClick(s.key)}>
|
||||
<Image className='status-icon-img' src={s.icon} mode='aspectFit' />
|
||||
<Text className='status-num'>{orderStats[s.key as keyof typeof orderStats]}</Text>
|
||||
<Text className='status-label'>{s.label}</Text>
|
||||
</View>
|
||||
{idx < 2 && <View className='status-divider' />}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -207,9 +228,9 @@ export default function ProfilePage() {
|
||||
<Text className='section-title'>常用功能</Text>
|
||||
<View className='menu-grid'>
|
||||
{MENU_ITEMS.map((item, idx) => (
|
||||
<View key={idx} className='menu-card dashed-card' onClick={() => handleMenuClick(item.path)}>
|
||||
<View key={idx} className='menu-card dashed-card' onTap={() => handleMenuClick(item.path, item.label)}>
|
||||
<View className='star-badge' />
|
||||
<Text className='menu-icon'>{item.icon}</Text>
|
||||
<Image className='menu-icon-img' src={ICON_MAP[item.label]} mode='aspectFit' />
|
||||
<Text className='menu-label'>{item.label}</Text>
|
||||
</View>
|
||||
))}
|
||||
@@ -221,7 +242,10 @@ export default function ProfilePage() {
|
||||
<View className='star-badge' />
|
||||
<View className='flex-between'>
|
||||
<View className='flex-column'>
|
||||
<Text className='enterprise-title'>🏢 企业批量定制</Text>
|
||||
<View className='flex-center'>
|
||||
<Image className='enterprise-icon' src='/icon/企业批量定制.png' mode='aspectFit' />
|
||||
<Text className='enterprise-title'>企业批量定制</Text>
|
||||
</View>
|
||||
<Text className='enterprise-desc'>年会礼品、入职纪念、团建伴手礼</Text>
|
||||
</View>
|
||||
<View className='btn-gradient enterprise-btn'>
|
||||
@@ -247,7 +271,7 @@ export default function ProfilePage() {
|
||||
{loginAvatarUrl ? (
|
||||
<Image className='avatar-img' src={loginAvatarUrl} mode='aspectFill' />
|
||||
) : (
|
||||
<Text className='avatar-placeholder'>点击选择头像</Text>
|
||||
<Image className='avatar-placeholder-img' src='/icon/个人.png' mode='aspectFit' />
|
||||
)}
|
||||
</Button>
|
||||
<Input
|
||||
@@ -258,10 +282,10 @@ export default function ProfilePage() {
|
||||
onInput={(e: any) => setLoginNickName(e.detail.value)}
|
||||
/>
|
||||
<View className='login-actions'>
|
||||
<View className='btn-outline' onClick={handleCloseLogin}>
|
||||
<View className='btn-outline' onTap={handleCloseLogin}>
|
||||
<Text>取消</Text>
|
||||
</View>
|
||||
<View className='btn-gradient' onClick={handleLoginSubmit}>
|
||||
<View className='btn-gradient' onTap={handleLoginSubmit}>
|
||||
<Text>登录</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
text-align: center;
|
||||
padding: 50px 40px;
|
||||
}
|
||||
.service-icon {
|
||||
font-size: 80px;
|
||||
.service-hero-icon {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.service-title {
|
||||
@@ -54,6 +55,14 @@
|
||||
font-size: 48px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.contact-icon-img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -91,6 +100,12 @@
|
||||
.enterprise-service {
|
||||
padding: 32px;
|
||||
}
|
||||
.enterprise-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.enterprise-title {
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
|
||||
+65
-62
@@ -1,4 +1,4 @@
|
||||
import { View, Text } from '@tarojs/components'
|
||||
import { View, Text, Image } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import './index.scss'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
@@ -15,79 +15,82 @@ export default function ServicePage() {
|
||||
const { theme } = useThemeContext()
|
||||
|
||||
return (
|
||||
<View className={`theme-${theme}`}>
|
||||
<View className={`theme-${theme}`} style={{ minHeight: '100vh' }}>
|
||||
<View className='service-page'>
|
||||
<ThemeToggle />
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='page-title'>联系客服</Text>
|
||||
</View>
|
||||
|
||||
{/* 客服入口 */}
|
||||
<View className='service-hero dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='service-icon'>💬</Text>
|
||||
<Text className='service-title'>智绘微刻客服中心</Text>
|
||||
<Text className='service-desc'>工作日 09:00 - 18:00 在线为您服务</Text>
|
||||
<View className='btn-gradient service-btn' onClick={() => Taro.showToast({ title: '跳转客服会话', icon: 'none' })}>
|
||||
<Text>🚀 发起咨询</Text>
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='page-title'>联系客服</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 联系方式 */}
|
||||
<View className='mt-20'>
|
||||
<Text className='section-title'>联系方式</Text>
|
||||
<View className='contact-list'>
|
||||
<View className='contact-item dashed-card'>
|
||||
<View className='star-badge' />
|
||||
<Text className='contact-icon'>📞</Text>
|
||||
<View className='contact-info'>
|
||||
<Text className='contact-label'>客服电话</Text>
|
||||
<Text className='contact-value'>400-XXX-XXXX</Text>
|
||||
</View>
|
||||
{/* 客服入口 */}
|
||||
<View className='service-hero dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Image className='service-hero-icon' src='/icon/电话.png' mode='aspectFit' />
|
||||
<Text className='service-title'>智绘微刻客服中心</Text>
|
||||
<Text className='service-desc'>工作日 09:00 - 18:00 在线为您服务</Text>
|
||||
<View className='btn-gradient service-btn' onTap={() => Taro.showToast({ title: '跳转客服会话', icon: 'none' })}>
|
||||
<Text>发起咨询</Text>
|
||||
</View>
|
||||
<View className='contact-item dashed-card' onClick={() => Taro.setClipboardData({ data: 'smart_engraving' })}>
|
||||
<View className='star-badge' />
|
||||
<Text className='contact-icon'>💬</Text>
|
||||
<View className='contact-info'>
|
||||
<Text className='contact-label'>客服微信</Text>
|
||||
<Text className='contact-value'>smart_engraving(点击复制)</Text>
|
||||
</View>
|
||||
|
||||
{/* 联系方式 */}
|
||||
<View className='mt-20'>
|
||||
<Text className='section-title'>联系方式</Text>
|
||||
<View className='contact-list'>
|
||||
<View className='contact-item dashed-card'>
|
||||
<View className='star-badge' />
|
||||
<Image className='contact-icon-img' src='/icon/电话.png' mode='aspectFit' />
|
||||
<View className='contact-info'>
|
||||
<Text className='contact-label'>客服电话</Text>
|
||||
<Text className='contact-value'>400-XXX-XXXX</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View className='contact-item dashed-card'>
|
||||
<View className='star-badge' />
|
||||
<Text className='contact-icon'>📧</Text>
|
||||
<View className='contact-info'>
|
||||
<Text className='contact-label'>商务邮箱</Text>
|
||||
<Text className='contact-value'>biz@smart-engraving.com</Text>
|
||||
<View className='contact-item dashed-card' onTap={() => Taro.setClipboardData({ data: 'smart_engraving' })}>
|
||||
<View className='star-badge' />
|
||||
<Image className='contact-icon-img' src='/icon/词云生成.png' mode='aspectFit' />
|
||||
<View className='contact-info'>
|
||||
<Text className='contact-label'>客服微信</Text>
|
||||
<Text className='contact-value'>smart_engraving(点击复制)</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className='contact-item dashed-card'>
|
||||
<View className='star-badge' />
|
||||
<Image className='contact-icon-img' src='/icon/使用帮助.png' mode='aspectFit' />
|
||||
<View className='contact-info'>
|
||||
<Text className='contact-label'>商务邮箱</Text>
|
||||
<Text className='contact-value'>biz@smart-engraving.com</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 常见问题 */}
|
||||
<View className='mt-20'>
|
||||
<Text className='section-title'>常见问题</Text>
|
||||
{FAQ_ITEMS.map((item, idx) => (
|
||||
<View key={idx} className='faq-card dashed-card'>
|
||||
<View className='star-badge' />
|
||||
<Text className='faq-q'>Q: {item.q}</Text>
|
||||
<Text className='faq-a'>A: {item.a}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* B端专属 */}
|
||||
<View className='enterprise-service dashed-card mt-20 mb-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='enterprise-title'>🏢 企业专属客户经理</Text>
|
||||
<Text className='enterprise-desc'>批量订单(50件以上)享专属报价、优先排产、专人对接</Text>
|
||||
<View className='btn-outline enterprise-btn'>
|
||||
<Text>预约企业咨询</Text>
|
||||
{/* 常见问题 */}
|
||||
<View className='mt-20'>
|
||||
<Text className='section-title'>常见问题</Text>
|
||||
{FAQ_ITEMS.map((item, idx) => (
|
||||
<View key={idx} className='faq-card dashed-card'>
|
||||
<View className='star-badge' />
|
||||
<Text className='faq-q'>Q: {item.q}</Text>
|
||||
<Text className='faq-a'>A: {item.a}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={{ height: '40px' }} />
|
||||
{/* B端专属 */}
|
||||
<View className='enterprise-service dashed-card mt-20 mb-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='flex-center'>
|
||||
<Image className='enterprise-icon' src='/icon/企业批量定制.png' mode='aspectFit' />
|
||||
<Text className='enterprise-title'>企业专属客户经理</Text>
|
||||
</View>
|
||||
<Text className='enterprise-desc'>批量订单(50件以上)享专属报价、优先排产、专人对接</Text>
|
||||
<View className='btn-outline enterprise-btn'>
|
||||
<Text>预约企业咨询</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={{ height: '40px' }} />
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 48px;
|
||||
padding: 30px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.settings-name {
|
||||
@@ -57,11 +58,14 @@
|
||||
border-bottom: 1px dashed rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.settings-item-icon {
|
||||
font-size: 32px;
|
||||
.settings-item-icon-img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
.settings-item-label {
|
||||
flex: 1;
|
||||
font-size: 30px;
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { View, Text, Image, Input, Button } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import './index.scss'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
import { getUserInfo, setUserInfo, clearUserInfo } from '../../utils/store'
|
||||
import { getUserInfo, setUserInfo, clearUserInfo, getAddressList, getDefaultAddress, type AddressItem } from '../../utils/store'
|
||||
|
||||
const MENU = [
|
||||
{ label: '个人信息', icon: '/icon/个人.png', action: (s: any) => s.setShowEdit(true) },
|
||||
{ label: '账号管理', icon: '/icon/账号管理.png', action: () => Taro.navigateTo({ url: '/pages/userDatabase/index' }) },
|
||||
{ label: '定制协议', icon: '/icon/定制协议.png', action: () => Taro.navigateTo({ url: '/pages/agreement/index' }) },
|
||||
{ label: '关于我们', icon: '/icon/关于我们.png', action: () => Taro.showToast({ title: '智绘微刻 v1.0', icon: 'none' }) }
|
||||
]
|
||||
|
||||
export default function SettingsPage() {
|
||||
const { theme } = useThemeContext()
|
||||
@@ -48,22 +55,15 @@ export default function SettingsPage() {
|
||||
setEditAvatar(e.detail.avatarUrl || '')
|
||||
}
|
||||
|
||||
const MENU = [
|
||||
{ label: '个人信息', icon: '👤', action: () => setShowEdit(true) },
|
||||
{ label: '账号管理', icon: '🆔', action: () => Taro.navigateTo({ url: '/pages/userDatabase/index' }) },
|
||||
{ label: '定制协议', icon: '📋', action: () => Taro.navigateTo({ url: '/pages/agreement/index' }) },
|
||||
{ label: '关于我们', icon: 'ℹ️', action: () => Taro.showToast({ title: '智绘微刻 v1.0', icon: 'none' }) }
|
||||
]
|
||||
|
||||
return (
|
||||
<View className={`theme-${theme}`}>
|
||||
<View className={`theme-${theme}`} style={{ minHeight: '100vh' }}>
|
||||
<View className='settings-page'>
|
||||
<ThemeToggle />
|
||||
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<View className='flex-between' style={{ width: '100%' }}>
|
||||
<Text className='back-btn' onClick={() => Taro.navigateBack()}>←</Text>
|
||||
<Text className='back-btn' onTap={() => Taro.navigateBack()}>←</Text>
|
||||
<Text className='page-title'>设置</Text>
|
||||
<View style={{ width: '60px' }} />
|
||||
</View>
|
||||
@@ -71,12 +71,14 @@ export default function SettingsPage() {
|
||||
|
||||
{/* 用户卡片 */}
|
||||
<View className='settings-user dashed-card mt-20'>
|
||||
<View className='flex-between' onClick={() => setShowEdit(true)}>
|
||||
<View className='flex-between' onTap={() => setShowEdit(true)}>
|
||||
<View className='flex-center'>
|
||||
{user.avatarUrl ? (
|
||||
<Image className='settings-avatar' src={user.avatarUrl} mode='aspectFill' />
|
||||
) : (
|
||||
<View className='settings-avatar-placeholder'><Text>👤</Text></View>
|
||||
<View className='settings-avatar-placeholder'>
|
||||
<Image className='settings-avatar-icon' src='/icon/个人.png' mode='aspectFit' />
|
||||
</View>
|
||||
)}
|
||||
<View style={{ marginLeft: '20px' }}>
|
||||
<Text className='settings-name'>{user.nickName || '未登录'}</Text>
|
||||
@@ -90,8 +92,8 @@ export default function SettingsPage() {
|
||||
{/* 菜单列表 */}
|
||||
<View className='settings-list dashed-card mt-20'>
|
||||
{MENU.map((item, idx) => (
|
||||
<View key={idx} className={`settings-item ${idx < MENU.length - 1 ? 'bordered' : ''}`} onClick={item.action}>
|
||||
<Text className='settings-item-icon'>{item.icon}</Text>
|
||||
<View key={idx} className={`settings-item ${idx < MENU.length - 1 ? 'bordered' : ''}`} onTap={() => item.action({ setShowEdit })}>
|
||||
<Image className='settings-item-icon-img' src={item.icon} mode='aspectFit' />
|
||||
<Text className='settings-item-label'>{item.label}</Text>
|
||||
<Text className='arrow'>›</Text>
|
||||
</View>
|
||||
@@ -101,7 +103,7 @@ export default function SettingsPage() {
|
||||
{/* 退出登录 */}
|
||||
{user?.openid && (
|
||||
<View className='mt-20'>
|
||||
<View className='btn-outline logout-btn' onClick={handleLogout}>
|
||||
<View className='btn-outline logout-btn' onTap={handleLogout}>
|
||||
<Text>退出登录</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -119,7 +121,7 @@ export default function SettingsPage() {
|
||||
{editAvatar ? (
|
||||
<Image className='avatar-img' src={editAvatar} mode='aspectFill' />
|
||||
) : (
|
||||
<Text className='avatar-placeholder'>点击选择头像</Text>
|
||||
<Image className='avatar-placeholder-img' src='/icon/个人.png' mode='aspectFit' />
|
||||
)}
|
||||
</Button>
|
||||
<Input
|
||||
@@ -129,10 +131,10 @@ export default function SettingsPage() {
|
||||
onInput={(e: any) => setEditName(e.detail.value)}
|
||||
/>
|
||||
<View className='form-actions'>
|
||||
<View className='btn-outline' onClick={() => setShowEdit(false)}>
|
||||
<View className='btn-outline' onTap={() => setShowEdit(false)}>
|
||||
<Text>取消</Text>
|
||||
</View>
|
||||
<View className='btn-gradient' onClick={handleSaveInfo}>
|
||||
<View className='btn-gradient' onTap={handleSaveInfo}>
|
||||
<Text>保存</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -97,7 +97,7 @@ export default function UserDatabasePage() {
|
||||
{pwdError && (
|
||||
<Text className='udb-error'>密码错误,请重试</Text>
|
||||
)}
|
||||
<View className='btn-gradient udb-btn' onClick={handleUnlock}>
|
||||
<View className='btn-gradient udb-btn' onTap={handleUnlock}>
|
||||
<Text>进入数据库</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -117,7 +117,7 @@ export default function UserDatabasePage() {
|
||||
</View>
|
||||
|
||||
<View className='udb-toolbar'>
|
||||
<View className='btn-gradient udb-add-btn' onClick={() => setShowAdd(true)}>
|
||||
<View className='btn-gradient udb-add-btn' onTap={() => setShowAdd(true)}>
|
||||
<Text>+ 新建账号</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -136,14 +136,14 @@ export default function UserDatabasePage() {
|
||||
<Text className='udb-col col-stats'>{u.designCount} / {u.orderCount}</Text>
|
||||
<View className='udb-col col-action'>
|
||||
{u.openid !== activeOpenid && (
|
||||
<View className='udb-link' onClick={() => handleSwitch(u.openid)}>
|
||||
<View className='udb-link' onTap={() => handleSwitch(u.openid)}>
|
||||
<Text>切换</Text>
|
||||
</View>
|
||||
)}
|
||||
{u.openid === activeOpenid && (
|
||||
<Text className='udb-current'>当前</Text>
|
||||
)}
|
||||
<View className='udb-link udb-danger' onClick={() => handleDelete(u.openid)}>
|
||||
<View className='udb-link udb-danger' onTap={() => handleDelete(u.openid)}>
|
||||
<Text>删除</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -170,10 +170,10 @@ export default function UserDatabasePage() {
|
||||
onInput={(e: any) => setNewNick(e.detail.value)}
|
||||
/>
|
||||
<View className='udb-add-actions'>
|
||||
<View className='btn-outline' onClick={() => setShowAdd(false)}>
|
||||
<View className='btn-outline' onTap={() => setShowAdd(false)}>
|
||||
<Text>取消</Text>
|
||||
</View>
|
||||
<View className='btn-gradient' onClick={handleCreate}>
|
||||
<View className='btn-gradient' onTap={handleCreate}>
|
||||
<Text>创建</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -62,7 +62,7 @@ export default function WordCloudPage() {
|
||||
<ThemeToggle />
|
||||
|
||||
<View className='page-header flex-between'>
|
||||
<Text className='back-btn' onClick={goBack}>←</Text>
|
||||
<Text className='back-btn' onTap={goBack}>←</Text>
|
||||
<Text className='page-title'>AI词云生成</Text>
|
||||
<View style={{ width: '60px' }} />
|
||||
</View>
|
||||
@@ -83,7 +83,7 @@ export default function WordCloudPage() {
|
||||
{/* 步骤1: 上传底图 */}
|
||||
{step === 1 && (
|
||||
<View className='step-content'>
|
||||
<View className='upload-area dashed-card' onClick={chooseImage}>
|
||||
<View className='upload-area dashed-card' onTap={chooseImage}>
|
||||
{baseImage ? (
|
||||
<Image className='preview-image' src={baseImage} mode='aspectFit' />
|
||||
) : (
|
||||
@@ -97,10 +97,10 @@ export default function WordCloudPage() {
|
||||
</View>
|
||||
{baseImage && (
|
||||
<View className='action-btns'>
|
||||
<View className='btn-gradient' onClick={() => setStep(2)}>
|
||||
<View className='btn-gradient' onTap={() => setStep(2)}>
|
||||
<Text>下一步:输入名单</Text>
|
||||
</View>
|
||||
<View className='btn-outline' onClick={() => setBaseImage('')}>
|
||||
<View className='btn-outline' onTap={() => setBaseImage('')}>
|
||||
<Text>重新选择</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -129,10 +129,10 @@ export default function WordCloudPage() {
|
||||
</View>
|
||||
</View>
|
||||
<View className='action-btns'>
|
||||
<View className='btn-gradient' onClick={handleGenerate}>
|
||||
<View className='btn-gradient' onTap={handleGenerate}>
|
||||
<Text>开始生成词云</Text>
|
||||
</View>
|
||||
<View className='btn-outline' onClick={() => setStep(1)}>
|
||||
<View className='btn-outline' onTap={() => setStep(1)}>
|
||||
<Text>上一步</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -161,15 +161,15 @@ export default function WordCloudPage() {
|
||||
</View>
|
||||
</View>
|
||||
<View className='result-actions'>
|
||||
<View className='action-btn' onClick={handleExportImage}>
|
||||
<View className='action-btn' onTap={handleExportImage}>
|
||||
<Text className='action-icon'>💾</Text>
|
||||
<Text className='action-label'>保存图片</Text>
|
||||
</View>
|
||||
<View className='action-btn' onClick={handleGenerate}>
|
||||
<View className='action-btn' onTap={handleGenerate}>
|
||||
<Text className='action-icon'>🔄</Text>
|
||||
<Text className='action-label'>重新生成</Text>
|
||||
</View>
|
||||
<View className='action-btn' onClick={() => setStep(2)}>
|
||||
<View className='action-btn' onTap={() => setStep(2)}>
|
||||
<Text className='action-icon'>✏️</Text>
|
||||
<Text className='action-label'>修改名单</Text>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user