针对个人主页与照片显示做了修改
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '收货地址'
|
||||
})
|
||||
@@ -0,0 +1,181 @@
|
||||
.address-page {
|
||||
padding: 0 24px 24px;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
font-size: 40px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.address-list {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.address-card {
|
||||
padding: 24px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.address-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.address-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.address-name {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.address-phone {
|
||||
font-size: 28px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.default-tag {
|
||||
margin-left: 12px;
|
||||
background: var(--accent-pink);
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
padding: 2px 10px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.address-full {
|
||||
font-size: 28px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.address-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 24px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px dashed rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.address-act {
|
||||
font-size: 26px;
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
.address-act.delete {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
/* 表单弹窗 */
|
||||
.address-form {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
background: var(--bg-input);
|
||||
border: 3px dashed var(--line-star);
|
||||
border-radius: 16px;
|
||||
padding: 0 24px;
|
||||
font-size: 28px;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-picker {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
background: var(--bg-input);
|
||||
border: 3px dashed var(--line-star);
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 24px;
|
||||
margin-bottom: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-picker-val {
|
||||
font-size: 28px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.form-picker-ph {
|
||||
font-size: 28px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 28px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border: 3px dashed var(--line-star);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.checkbox.checked {
|
||||
background: var(--accent-pink);
|
||||
border-color: var(--accent-pink);
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.form-actions .btn-outline,
|
||||
.form-actions .btn-gradient {
|
||||
flex: 1;
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 空状态统一的在 app.scss */
|
||||
.login-guard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120px 40px;
|
||||
}
|
||||
|
||||
.login-guard-icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.login-guard-title {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.login-guard-desc {
|
||||
font-size: 28px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.login-guard-btn {
|
||||
padding: 20px 48px;
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
import { View, Text, Input, Picker } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState, useEffect } from 'react'
|
||||
import './index.scss'
|
||||
import { getAddressList, addAddress, updateAddress, deleteAddress } from '../../utils/store'
|
||||
import type { AddressItem } from '../../types'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
|
||||
export default function AddressPage() {
|
||||
const { theme } = useThemeContext()
|
||||
const [list, setList] = useState<AddressItem[]>([])
|
||||
const [editing, setEditing] = useState<AddressItem | null>(null)
|
||||
const [showForm, setShowForm] = useState(false)
|
||||
|
||||
const [name, setName] = useState('')
|
||||
const [phone, setPhone] = useState('')
|
||||
const [region, setRegion] = useState<string[]>([])
|
||||
const [detail, setDetail] = useState('')
|
||||
const [isDefault, setIsDefault] = useState(false)
|
||||
|
||||
const load = () => setList(getAddressList())
|
||||
|
||||
useEffect(() => {
|
||||
load()
|
||||
}, [])
|
||||
|
||||
const resetForm = () => {
|
||||
setName('')
|
||||
setPhone('')
|
||||
setRegion([])
|
||||
setDetail('')
|
||||
setIsDefault(false)
|
||||
setEditing(null)
|
||||
}
|
||||
|
||||
const openAdd = () => {
|
||||
resetForm()
|
||||
setShowForm(true)
|
||||
}
|
||||
|
||||
const openEdit = (item: AddressItem) => {
|
||||
setEditing(item)
|
||||
setName(item.name)
|
||||
setPhone(item.phone)
|
||||
setRegion(item.region)
|
||||
setDetail(item.detail)
|
||||
setIsDefault(item.isDefault)
|
||||
setShowForm(true)
|
||||
}
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
Taro.showModal({
|
||||
title: '确认删除',
|
||||
content: '删除后将无法恢复该地址',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
deleteAddress(id)
|
||||
load()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleCopy = (item: AddressItem) => {
|
||||
const text = `${item.name} ${item.phone}\n${item.region.join(' ')} ${item.detail}`
|
||||
Taro.setClipboardData({ data: text })
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
if (!name.trim() || !phone.trim() || region.length === 0 || !detail.trim()) {
|
||||
Taro.showToast({ title: '请填写完整信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const payload = { name, phone, region, detail, isDefault }
|
||||
if (editing) {
|
||||
updateAddress(editing.id, payload)
|
||||
} else {
|
||||
addAddress(payload)
|
||||
}
|
||||
Taro.showToast({ title: '保存成功', icon: 'success' })
|
||||
setShowForm(false)
|
||||
resetForm()
|
||||
load()
|
||||
}
|
||||
|
||||
const onRegionChange = (e: any) => {
|
||||
setRegion(e.detail.value || [])
|
||||
}
|
||||
|
||||
return (
|
||||
<View className={`theme-${theme}`}>
|
||||
<View className='address-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='page-title'>收货地址</Text>
|
||||
<View style={{ width: '60px' }} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 地址列表 */}
|
||||
<View className='address-list'>
|
||||
{list.map(item => (
|
||||
<View key={item.id} className='address-card dashed-card'>
|
||||
<View className='address-header'>
|
||||
<View className='address-user'>
|
||||
<Text className='address-name'>{item.name}</Text>
|
||||
<Text className='address-phone'>{item.phone}</Text>
|
||||
{item.isDefault && (
|
||||
<View className='default-tag'><Text>默认</Text></View>
|
||||
)}
|
||||
</View>
|
||||
<Text className='address-full'>
|
||||
{item.region.join(' ')} {item.detail}
|
||||
</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>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
|
||||
{list.length === 0 && (
|
||||
<View className='empty-state'>
|
||||
<Text className='empty-icon'>📍</Text>
|
||||
<Text className='empty-text'>还没有收货地址</Text>
|
||||
<Text className='empty-sub'>点击下方按钮添加新地址</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<View className='safe-bottom-placeholder' />
|
||||
|
||||
{/* 底部添加按钮 */}
|
||||
<View className='action-bar'>
|
||||
<View className='btn-gradient' onClick={openAdd}>
|
||||
<Text>新增收货地址</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 新增/编辑弹窗 */}
|
||||
{showForm && (
|
||||
<View className='modal-overlay'>
|
||||
<View className='modal-card dashed-card address-form'>
|
||||
<View className='star-badge' />
|
||||
<Text className='modal-title'>{editing ? '编辑地址' : '新增地址'}</Text>
|
||||
|
||||
<Input className='form-input' placeholder='收货人姓名'
|
||||
value={name} onInput={(e: any) => setName(e.detail.value)} />
|
||||
<Input className='form-input' placeholder='手机号码' type='number'
|
||||
value={phone} onInput={(e: any) => setPhone(e.detail.value)} />
|
||||
|
||||
<Picker mode='region' value={region} onChange={onRegionChange}>
|
||||
<View className='form-picker'>
|
||||
<Text className={region.length ? 'form-picker-val' : 'form-picker-ph'}>
|
||||
{region.length ? region.join(' / ') : '选择省 / 市 / 区'}
|
||||
</Text>
|
||||
</View>
|
||||
</Picker>
|
||||
|
||||
<Input className='form-input' placeholder='详细地址:街道、门牌号'
|
||||
value={detail} onInput={(e: any) => setDetail(e.detail.value)} />
|
||||
|
||||
<View className='form-row' onClick={() => 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() }}>
|
||||
<Text>取消</Text>
|
||||
</View>
|
||||
<View className='btn-gradient' onClick={handleSave}>
|
||||
<Text>保存</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '定制协议'
|
||||
})
|
||||
@@ -0,0 +1,36 @@
|
||||
.agreement-page {
|
||||
padding: 0 24px 24px;
|
||||
}
|
||||
|
||||
.agreement-title {
|
||||
font-size: 40px;
|
||||
font-weight: 800;
|
||||
color: var(--text-primary);
|
||||
display: block;
|
||||
margin-bottom: 32px;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.agreement-content {
|
||||
padding: 40px 32px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.agreement-section {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.agreement-h3 {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.agreement-p {
|
||||
font-size: 28px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.8;
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { View, Text } from '@tarojs/components'
|
||||
import './index.scss'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
|
||||
export default function AgreementPage() {
|
||||
const { theme } = useThemeContext()
|
||||
|
||||
const SECTIONS = [
|
||||
{
|
||||
title: '一、用户权责',
|
||||
content: '用户确认提交的设计内容不侵犯第三方知识产权,定制商品一经确认下单即进入生产流程,非质量问题不支持退换。'
|
||||
},
|
||||
{
|
||||
title: '二、隐私保护',
|
||||
content: '我们严格保护用户个人信息,头像与昵称仅用于提升服务体验,不与第三方分享。'
|
||||
},
|
||||
{
|
||||
title: '三、售后说明',
|
||||
content: '收到商品后如有质量问题,请在7天内联系客服,我们将为您免费重做或退款。'
|
||||
},
|
||||
{
|
||||
title: '四、定制流程',
|
||||
content: `1. 选择商品品类并加入设计清单
|
||||
2. 在DIY工作台上传图片或输入文字
|
||||
3. 确认设计效果并提交订单
|
||||
4. 支付后进入生产,约3-7个工作日发货
|
||||
5. 物流跟踪至确认收货`
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<View className={`theme-${theme}`}>
|
||||
<View className='agreement-page'>
|
||||
<ThemeToggle />
|
||||
<View className='agreement-content'>
|
||||
<Text className='agreement-title'>定制协议</Text>
|
||||
{SECTIONS.map((sec, idx) => (
|
||||
<View key={idx} className='agreement-section'>
|
||||
<Text className='agreement-h3'>{sec.title}</Text>
|
||||
<Text className='agreement-p'>{sec.content}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -2,12 +2,14 @@ import { View, Text, ScrollView } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import './index.scss'
|
||||
import { getDesignList, getTheme, type DesignItem } from '../../utils/store'
|
||||
import { getDesignList, getUserInfo, type DesignItem } from '../../utils/store'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
import LoginGuard from '../../components/LoginGuard'
|
||||
|
||||
const STATUS_TABS = [
|
||||
{ code: 'all', label: '全部' },
|
||||
{ code: 'toDesign', label: '待设计' },
|
||||
{ code: 'undesigned', label: '未设计' },
|
||||
{ code: 'designing', label: '设计中' },
|
||||
{ code: 'ordered', label: '已下单' }
|
||||
@@ -28,23 +30,34 @@ export default function DesignListPage() {
|
||||
setList(getDesignList())
|
||||
}, [])
|
||||
|
||||
// 页面每次显示时刷新(保证加入清单后切回能看到最新数据)
|
||||
useEffect(() => {
|
||||
const onShow = () => load()
|
||||
const page = Taro.getCurrentInstance().page
|
||||
if (page) {
|
||||
const orig = page.onShow
|
||||
page.onShow = function () {
|
||||
onShow()
|
||||
if (orig) orig.apply(this)
|
||||
}
|
||||
// 自动读取外部传入的筛选状态
|
||||
const init = useCallback(() => {
|
||||
const filter = Taro.getStorageSync('designList:filter')
|
||||
if (filter && STATUS_TABS.some(t => t.code === filter)) {
|
||||
setActiveTab(filter)
|
||||
Taro.removeStorageSync('designList:filter')
|
||||
}
|
||||
load()
|
||||
}, [load])
|
||||
|
||||
// 页面每次显示时刷新
|
||||
useEffect(() => {
|
||||
const page = Taro.getCurrentInstance().page
|
||||
if (page) {
|
||||
const orig = page.onShow
|
||||
page.onShow = function () {
|
||||
init()
|
||||
if (orig) orig.apply(this)
|
||||
}
|
||||
}
|
||||
init()
|
||||
}, [init])
|
||||
|
||||
const filtered = activeTab === 'all'
|
||||
? list
|
||||
: list.filter(d => d.status === activeTab)
|
||||
: activeTab === 'toDesign'
|
||||
? list.filter(d => d.status === 'undesigned' || d.status === 'designing')
|
||||
: list.filter(d => d.status === activeTab)
|
||||
|
||||
const goDesign = (item: DesignItem) => {
|
||||
if (item.status === 'ordered') {
|
||||
@@ -58,78 +71,80 @@ export default function DesignListPage() {
|
||||
|
||||
return (
|
||||
<View className={`theme-${theme}`}>
|
||||
<View className='design-page'>
|
||||
<ThemeToggle />
|
||||
<LoginGuard>
|
||||
<View className='design-page'>
|
||||
<ThemeToggle />
|
||||
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='page-title'>我的设计清单</Text>
|
||||
</View>
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='page-title'>我的设计清单</Text>
|
||||
</View>
|
||||
|
||||
{/* 状态筛选 */}
|
||||
<View className='status-tabs'>
|
||||
<ScrollView className='tabs-scroll' scrollX>
|
||||
{STATUS_TABS.map(tab => (
|
||||
<View
|
||||
key={tab.code}
|
||||
className={`tab-item ${activeTab === tab.code ? 'active' : ''}`}
|
||||
onClick={() => setActiveTab(tab.code)}
|
||||
>
|
||||
<Text className='tab-label'>{tab.label}</Text>
|
||||
{activeTab === tab.code && <View className='tab-underline' />}
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
{/* 状态筛选 */}
|
||||
<View className='status-tabs'>
|
||||
<ScrollView className='tabs-scroll' scrollX>
|
||||
{STATUS_TABS.map(tab => (
|
||||
<View
|
||||
key={tab.code}
|
||||
className={`tab-item ${activeTab === tab.code ? 'active' : ''}`}
|
||||
onClick={() => setActiveTab(tab.code)}
|
||||
>
|
||||
<Text className='tab-label'>{tab.label}</Text>
|
||||
{activeTab === tab.code && <View className='tab-underline' />}
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* 列表 */}
|
||||
<ScrollView className='design-list' scrollY>
|
||||
{filtered.map(item => {
|
||||
const style = STATUS_STYLE[item.status]
|
||||
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>
|
||||
<View className='design-info'>
|
||||
<View className='design-title-row'>
|
||||
<Text className='design-name'>{item.productName}</Text>
|
||||
<Text className={`badge ${style.cls}`}>{style.label}</Text>
|
||||
{/* 列表 */}
|
||||
<ScrollView className='design-list' scrollY>
|
||||
{filtered.map(item => {
|
||||
const style = STATUS_STYLE[item.status]
|
||||
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>
|
||||
<View className='design-info'>
|
||||
<View className='design-title-row'>
|
||||
<Text className='design-name'>{item.productName}</Text>
|
||||
<Text className={`badge ${style.cls}`}>{style.label}</Text>
|
||||
</View>
|
||||
<Text className='design-meta'>数量: {item.count} 件 | 单价: ¥{item.unitPrice}</Text>
|
||||
<Text className='design-total'>小计: ¥{(item.unitPrice * item.count).toFixed(2)}</Text>
|
||||
</View>
|
||||
<Text className='design-meta'>数量: {item.count} 件 | 单价: ¥{item.unitPrice}</Text>
|
||||
<Text className='design-total'>小计: ¥{(item.unitPrice * item.count).toFixed(2)}</Text>
|
||||
</View>
|
||||
<View className='design-actions'>
|
||||
{item.status !== 'ordered' && (
|
||||
<View className='btn-gradient design-btn' onClick={() => goDesign(item)}>
|
||||
<Text>{item.status === 'undesigned' ? '开始设计' : '继续设计'}</Text>
|
||||
</View>
|
||||
)}
|
||||
{item.status === 'ordered' && (
|
||||
<View className='btn-outline design-btn' onClick={() => Taro.switchTab({ url: '/pages/orders/index' })}>
|
||||
<Text>查看订单</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
<View className='design-actions'>
|
||||
{item.status !== 'ordered' && (
|
||||
<View className='btn-gradient design-btn' onClick={() => goDesign(item)}>
|
||||
<Text>{item.status === 'undesigned' ? '开始设计' : '继续设计'}</Text>
|
||||
</View>
|
||||
)}
|
||||
{item.status === 'ordered' && (
|
||||
<View className='btn-outline design-btn' onClick={() => Taro.switchTab({ url: '/pages/orders/index' })}>
|
||||
<Text>查看订单</Text>
|
||||
</View>
|
||||
)}
|
||||
)
|
||||
})}
|
||||
|
||||
{filtered.length === 0 && (
|
||||
<View className='empty-state'>
|
||||
<Text className='empty-icon'>📐</Text>
|
||||
<Text className='empty-text'>还没有设计清单</Text>
|
||||
<Text className='empty-sub'>去首页逛逛,找到喜欢的定制品</Text>
|
||||
<View className='btn-gradient' onClick={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
||||
<Text>去逛逛</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
})}
|
||||
)}
|
||||
</ScrollView>
|
||||
|
||||
{filtered.length === 0 && (
|
||||
<View className='empty-state'>
|
||||
<Text className='empty-icon'>📐</Text>
|
||||
<Text className='empty-text'>还没有设计清单</Text>
|
||||
<Text className='empty-sub'>去首页逛逛,找到喜欢的定制品</Text>
|
||||
<View className='btn-gradient' onClick={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
||||
<Text>去逛逛</Text>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
|
||||
<View style={{ height: '40px' }} />
|
||||
</View>
|
||||
<View style={{ height: '40px' }} />
|
||||
</View>
|
||||
</LoginGuard>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
+95
-92
@@ -5,6 +5,7 @@ import './index.scss'
|
||||
import { getOrderList, type OrderItem } from '../../utils/store'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
import LoginGuard from '../../components/LoginGuard'
|
||||
|
||||
const STATUS_TABS = [
|
||||
{ code: 'all', label: '全部' },
|
||||
@@ -70,110 +71,112 @@ export default function OrdersPage() {
|
||||
|
||||
return (
|
||||
<View className={`theme-${theme}`}>
|
||||
<View className='orders-page'>
|
||||
<ThemeToggle />
|
||||
<LoginGuard>
|
||||
<View className='orders-page'>
|
||||
<ThemeToggle />
|
||||
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='page-title'>我的订单</Text>
|
||||
</View>
|
||||
<View className='page-header dashed-card mt-20'>
|
||||
<View className='star-badge' />
|
||||
<Text className='page-title'>我的订单</Text>
|
||||
</View>
|
||||
|
||||
{/* 状态筛选Tab */}
|
||||
<View className='status-tabs'>
|
||||
<ScrollView className='tabs-scroll' scrollX>
|
||||
{STATUS_TABS.map((tab) => (
|
||||
<View
|
||||
key={tab.code}
|
||||
className={`tab-item ${activeTab === tab.code ? 'active' : ''}`}
|
||||
onClick={() => setActiveTab(tab.code)}
|
||||
>
|
||||
<Text className='tab-label'>{tab.label}</Text>
|
||||
{activeTab === tab.code && <View className='tab-underline' />}
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* 订单列表 */}
|
||||
<ScrollView className='orders-list' scrollY>
|
||||
{filteredOrders.map((order) => {
|
||||
const status = STATUS_MAP[order.statusCode] || STATUS_MAP.pending
|
||||
return (
|
||||
<View key={order.id} className='order-card dashed-card' onClick={() => goDetail(order)}>
|
||||
<View className='star-badge' />
|
||||
<View className='order-header'>
|
||||
<Text className='order-date'>{order.date}</Text>
|
||||
<Text className={`badge ${status.badge}`}>{status.label}</Text>
|
||||
{/* 状态筛选Tab */}
|
||||
<View className='status-tabs'>
|
||||
<ScrollView className='tabs-scroll' scrollX>
|
||||
{STATUS_TABS.map((tab) => (
|
||||
<View
|
||||
key={tab.code}
|
||||
className={`tab-item ${activeTab === tab.code ? 'active' : ''}`}
|
||||
onClick={() => setActiveTab(tab.code)}
|
||||
>
|
||||
<Text className='tab-label'>{tab.label}</Text>
|
||||
{activeTab === tab.code && <View className='tab-underline' />}
|
||||
</View>
|
||||
<View className='order-body'>
|
||||
<View className='order-icon-wrapper'>
|
||||
<Text className='order-icon'>{order.productIcon}</Text>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* 订单列表 */}
|
||||
<ScrollView className='orders-list' scrollY>
|
||||
{filteredOrders.map((order) => {
|
||||
const status = STATUS_MAP[order.statusCode] || STATUS_MAP.pending
|
||||
return (
|
||||
<View key={order.id} className='order-card dashed-card' onClick={() => goDetail(order)}>
|
||||
<View className='star-badge' />
|
||||
<View className='order-header'>
|
||||
<Text className='order-date'>{order.date}</Text>
|
||||
<Text className={`badge ${status.badge}`}>{status.label}</Text>
|
||||
</View>
|
||||
<View className='order-info'>
|
||||
<Text className='order-product'>{order.productName}</Text>
|
||||
<Text className='order-sku'>{order.sku}</Text>
|
||||
<Text className='order-meta'>数量: {order.count} 件 | {order.id}</Text>
|
||||
<View className='order-body'>
|
||||
<View className='order-icon-wrapper'>
|
||||
<Text className='order-icon'>{order.productIcon}</Text>
|
||||
</View>
|
||||
<View className='order-info'>
|
||||
<Text className='order-product'>{order.productName}</Text>
|
||||
<Text className='order-sku'>{order.sku}</Text>
|
||||
<Text className='order-meta'>数量: {order.count} 件 | {order.id}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View className='order-footer'>
|
||||
<View className='order-price-bar'>
|
||||
<Text className='price-label'>实付款</Text>
|
||||
<Text className='price-value'>{order.price}</Text>
|
||||
</View>
|
||||
<View className='order-actions'>
|
||||
{order.statusCode === 'pending' && (
|
||||
<View className='btn-gradient order-btn'>
|
||||
<Text>立即付款</Text>
|
||||
<View className='order-footer'>
|
||||
<View className='order-price-bar'>
|
||||
<Text className='price-label'>实付款</Text>
|
||||
<Text className='price-value'>{order.price}</Text>
|
||||
</View>
|
||||
<View className='order-actions'>
|
||||
{order.statusCode === 'pending' && (
|
||||
<View className='btn-gradient order-btn'>
|
||||
<Text>立即付款</Text>
|
||||
</View>
|
||||
)}
|
||||
{order.statusCode === 'paid' && (
|
||||
<View className='btn-outline order-btn'>
|
||||
<Text>催发货</Text>
|
||||
</View>
|
||||
)}
|
||||
{order.statusCode === 'shipping' && (
|
||||
<>
|
||||
<View className='btn-outline order-btn' onClick={(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' }) }}>
|
||||
<Text>确认收货</Text>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
{order.statusCode === 'done' && (
|
||||
<>
|
||||
<View className='btn-outline order-btn' onClick={(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' }) }}>
|
||||
<Text>再来一单</Text>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
<View className='btn-outline order-btn' onClick={(e) => { e.stopPropagation(); goDetail(order) }}>
|
||||
<Text>详情</Text>
|
||||
</View>
|
||||
)}
|
||||
{order.statusCode === 'paid' && (
|
||||
<View className='btn-outline order-btn'>
|
||||
<Text>催发货</Text>
|
||||
</View>
|
||||
)}
|
||||
{order.statusCode === 'shipping' && (
|
||||
<>
|
||||
<View className='btn-outline order-btn' onClick={(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' }) }}>
|
||||
<Text>确认收货</Text>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
{order.statusCode === 'done' && (
|
||||
<>
|
||||
<View className='btn-outline order-btn' onClick={(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' }) }}>
|
||||
<Text>再来一单</Text>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
<View className='btn-outline order-btn' onClick={(e) => { e.stopPropagation(); goDetail(order) }}>
|
||||
<Text>详情</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
})}
|
||||
)
|
||||
})}
|
||||
|
||||
{filteredOrders.length === 0 && (
|
||||
<View className='empty-state'>
|
||||
<Text className='empty-icon'>📦</Text>
|
||||
<Text className='empty-text'>暂无订单</Text>
|
||||
<Text className='empty-sub'>去首页开始定制吧</Text>
|
||||
<View className='btn-gradient' onClick={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
||||
<Text>去定制</Text>
|
||||
{filteredOrders.length === 0 && (
|
||||
<View className='empty-state'>
|
||||
<Text className='empty-icon'>📦</Text>
|
||||
<Text className='empty-text'>暂无订单</Text>
|
||||
<Text className='empty-sub'>去首页开始定制吧</Text>
|
||||
<View className='btn-gradient' onClick={() => Taro.switchTab({ url: '/pages/index/index' })}>
|
||||
<Text>去定制</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
)}
|
||||
</ScrollView>
|
||||
|
||||
<View style={{ height: '40px' }} />
|
||||
</View>
|
||||
<View style={{ height: '40px' }} />
|
||||
</View>
|
||||
</LoginGuard>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ import { useThemeContext } from '../../context/ThemeContext'
|
||||
const MENU_ITEMS = [
|
||||
{ icon: '🎨', label: '我的设计', path: '/pages/designList/index' },
|
||||
{ icon: '✨', label: '词云生成', path: '/pages/wordcloud/index' },
|
||||
{ icon: '📍', label: '收货地址', path: '' },
|
||||
{ icon: '📍', label: '收货地址', path: '/pages/address/index' },
|
||||
{ icon: '📞', label: '联系客服', path: '/pages/service/index' },
|
||||
{ icon: '📋', label: '使用帮助', path: '' },
|
||||
{ icon: '⚙️', label: '设置', path: '' }
|
||||
{ icon: '📋', label: '使用帮助', path: '/pages/agreement/index' },
|
||||
{ icon: '⚙️', label: '设置', path: '/pages/settings/index' }
|
||||
]
|
||||
|
||||
export default function ProfilePage() {
|
||||
@@ -84,6 +84,7 @@ export default function ProfilePage() {
|
||||
}
|
||||
switch (type) {
|
||||
case 'toDesign':
|
||||
Taro.setStorageSync('designList:filter', 'toDesign')
|
||||
Taro.switchTab({ url: '/pages/designList/index' })
|
||||
break
|
||||
case 'pending':
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '设置'
|
||||
})
|
||||
@@ -0,0 +1,78 @@
|
||||
.settings-page {
|
||||
padding: 0 24px 24px;
|
||||
}
|
||||
|
||||
.settings-user {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.settings-avatar {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
border: 3px dashed var(--line-star);
|
||||
}
|
||||
|
||||
.settings-avatar-placeholder {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
background: var(--bg-input);
|
||||
border: 3px dashed var(--line-star);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.settings-name {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.settings-id {
|
||||
font-size: 24px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.arrow {
|
||||
font-size: 36px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.settings-list {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.settings-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 28px 0;
|
||||
}
|
||||
|
||||
.settings-item.bordered {
|
||||
border-bottom: 1px dashed rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.settings-item-icon {
|
||||
font-size: 32px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.settings-item-label {
|
||||
flex: 1;
|
||||
font-size: 30px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
text-align: center;
|
||||
padding: 24px 0;
|
||||
color: #ff6b6b;
|
||||
border: 3px dashed #ff6b6b;
|
||||
}
|
||||
|
||||
/* 弹窗复用 address 弹窗和 profile 弹窗样式 */
|
||||
@@ -0,0 +1,144 @@
|
||||
import { View, Text, Image, Input, Button } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { useState, useEffect } from 'react'
|
||||
import './index.scss'
|
||||
import ThemeToggle from '../../components/ThemeToggle'
|
||||
import { useThemeContext } from '../../context/ThemeContext'
|
||||
import { getUserInfo, setUserInfo, clearUserInfo } from '../../utils/store'
|
||||
|
||||
export default function SettingsPage() {
|
||||
const { theme } = useThemeContext()
|
||||
const [user, setUser] = useState<any>({})
|
||||
const [editName, setEditName] = useState('')
|
||||
const [editAvatar, setEditAvatar] = useState('')
|
||||
const [showEdit, setShowEdit] = useState(false)
|
||||
|
||||
const loadUser = () => {
|
||||
const u = getUserInfo() || {}
|
||||
setUser(u)
|
||||
setEditName(u.nickName || '')
|
||||
setEditAvatar(u.avatarUrl || '')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadUser()
|
||||
}, [])
|
||||
|
||||
const handleSaveInfo = () => {
|
||||
setUserInfo({ ...user, nickName: editName, avatarUrl: editAvatar })
|
||||
Taro.showToast({ title: '修改成功', icon: 'success' })
|
||||
setShowEdit(false)
|
||||
loadUser()
|
||||
}
|
||||
|
||||
const handleLogout = () => {
|
||||
Taro.showModal({
|
||||
title: '确认退出',
|
||||
content: '退出后将清空当前登录状态,确定吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
clearUserInfo()
|
||||
Taro.switchTab({ url: '/pages/index/index' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const onChooseAvatar = (e: any) => {
|
||||
setEditAvatar(e.detail.avatarUrl || '')
|
||||
}
|
||||
|
||||
const MENU = [
|
||||
{ label: '个人信息', icon: '👤', action: () => setShowEdit(true) },
|
||||
{ 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='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='page-title'>设置</Text>
|
||||
<View style={{ width: '60px' }} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 用户卡片 */}
|
||||
<View className='settings-user dashed-card mt-20'>
|
||||
<View className='flex-between' onClick={() => 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 style={{ marginLeft: '20px' }}>
|
||||
<Text className='settings-name'>{user.nickName || '未登录'}</Text>
|
||||
<Text className='settings-id'>ID: {user.openid ? user.openid.slice(-8) : '---'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Text className='arrow'>›</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 菜单列表 */}
|
||||
<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>
|
||||
<Text className='settings-item-label'>{item.label}</Text>
|
||||
<Text className='arrow'>›</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* 退出登录 */}
|
||||
{user?.openid && (
|
||||
<View className='mt-20'>
|
||||
<View className='btn-outline logout-btn' onClick={handleLogout}>
|
||||
<Text>退出登录</Text>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View style={{ height: '40px' }} />
|
||||
|
||||
{/* 编辑弹窗 */}
|
||||
{showEdit && (
|
||||
<View className='modal-overlay'>
|
||||
<View className='modal-card dashed-card'>
|
||||
<View className='star-badge' />
|
||||
<Text className='modal-title'>修改信息</Text>
|
||||
<Button className='avatar-btn' openType='chooseAvatar' onChooseAvatar={onChooseAvatar}>
|
||||
{editAvatar ? (
|
||||
<Image className='avatar-img' src={editAvatar} mode='aspectFill' />
|
||||
) : (
|
||||
<Text className='avatar-placeholder'>点击选择头像</Text>
|
||||
)}
|
||||
</Button>
|
||||
<Input
|
||||
className='nickname-input'
|
||||
placeholder='请输入昵称'
|
||||
value={editName}
|
||||
onInput={(e: any) => setEditName(e.detail.value)}
|
||||
/>
|
||||
<View className='form-actions'>
|
||||
<View className='btn-outline' onClick={() => setShowEdit(false)}>
|
||||
<Text>取消</Text>
|
||||
</View>
|
||||
<View className='btn-gradient' onClick={handleSaveInfo}>
|
||||
<Text>保存</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user