- 新增地址类型定义,增强前端地址数据结构 - 新增地址编辑页面,支持地址智能识别和定位选点功能 - 地址编辑支持省市区选择及默认地址设置 - 新增地址列表页面,支持地址展示、删除、编辑和选择功能 - 实现售后申请页面,支持选择售后类型和退款原因 - 售后申请支持商品选择、退款金额计算和凭证上传 - 新增售后详情页面,支持售后状态展示及申请取消 - 优化页面加载和用户交互体验,增加错误提示和权限处理
218 lines
8.1 KiB
TypeScript
218 lines
8.1 KiB
TypeScript
import React, { useState, useEffect } from 'react'
|
|
import { View, Text, ScrollView, Input, Switch } from '@tarojs/components'
|
|
import Taro from '@tarojs/taro'
|
|
import { useScrollHeight } from '@/hooks/useScrollHeight'
|
|
import { getShopSettingByCategory, batchSaveShopSetting } from '@/api/shop/shopSetting'
|
|
|
|
definePageConfig({
|
|
navigationBarTitleText: '商城设置',
|
|
})
|
|
|
|
type Category = 'basic' | 'order' | 'points' | 'dealer' | 'notify'
|
|
|
|
const CATEGORIES: { key: Category; label: string }[] = [
|
|
{ key: 'basic', label: '基础设置' },
|
|
{ key: 'order', label: '订单设置' },
|
|
{ key: 'points', label: '积分设置' },
|
|
{ key: 'dealer', label: '分销设置' },
|
|
{ key: 'notify', label: '通知设置' },
|
|
]
|
|
|
|
const ShopSettingPage: React.FC = () => {
|
|
const scrollHeight = useScrollHeight(44)
|
|
const [activeCategory, setActiveCategory] = useState<Category>('basic')
|
|
const [settings, setSettings] = useState<Record<string, string>>({})
|
|
const [loading, setLoading] = useState(false)
|
|
const [saving, setSaving] = useState(false)
|
|
|
|
useEffect(() => {
|
|
fetchSettings(activeCategory)
|
|
}, [activeCategory])
|
|
|
|
const fetchSettings = async (category: Category) => {
|
|
setLoading(true)
|
|
try {
|
|
const list = await getShopSettingByCategory(category)
|
|
const map: Record<string, string> = {}
|
|
list.forEach(item => {
|
|
if (item.settingKey) {
|
|
map[item.settingKey] = item.settingValue ?? ''
|
|
}
|
|
})
|
|
setSettings(map)
|
|
} catch (e: any) {
|
|
Taro.showToast({ title: e?.message || '加载失败', icon: 'none' })
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const handleSave = async () => {
|
|
setSaving(true)
|
|
try {
|
|
await batchSaveShopSetting(activeCategory, settings as Record<string, unknown>)
|
|
Taro.showToast({ title: '保存成功', icon: 'success' })
|
|
} catch (e: any) {
|
|
Taro.showToast({ title: e?.message || '保存失败', icon: 'none' })
|
|
} finally {
|
|
setSaving(false)
|
|
}
|
|
}
|
|
|
|
const handleChange = (key: string, value: string) => {
|
|
setSettings(prev => ({ ...prev, [key]: value }))
|
|
}
|
|
|
|
const handleSwitchChange = (key: string, checked: boolean) => {
|
|
setSettings(prev => ({ ...prev, [key]: checked ? '1' : '0' }))
|
|
}
|
|
|
|
const renderSettingItem = (key: string, label: string, type: 'text' | 'switch' | 'number' = 'text', placeholder = '') => {
|
|
const value = settings[key] ?? ''
|
|
if (type === 'switch') {
|
|
return (
|
|
<View key={key} className='flex items-center justify-between px-4 py-3 border-b border-gray-50'>
|
|
<Text className='text-sm text-gray-700'>{label}</Text>
|
|
<Switch
|
|
checked={value === '1' || value === 'true'}
|
|
color='#0e932e'
|
|
onChange={e => handleSwitchChange(key, e.detail.value)}
|
|
/>
|
|
</View>
|
|
)
|
|
}
|
|
return (
|
|
<View key={key} className='flex items-center justify-between px-4 py-3 border-b border-gray-50'>
|
|
<Text className='text-sm text-gray-700 flex-shrink-0 mr-3'>{label}</Text>
|
|
<Input
|
|
className='text-sm text-gray-800 text-right flex-1'
|
|
value={value}
|
|
type={type === 'number' ? 'number' : 'text'}
|
|
placeholder={placeholder || `请输入${label}`}
|
|
placeholderStyle='color:#ccc;font-size:12px'
|
|
onInput={e => handleChange(key, e.detail.value)}
|
|
/>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const renderCategorySettings = () => {
|
|
if (loading) {
|
|
return (
|
|
<View className='flex items-center justify-center py-10'>
|
|
<Text className='text-gray-400 text-sm'>加载中...</Text>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
switch (activeCategory) {
|
|
case 'basic':
|
|
return (
|
|
<View className='bg-white rounded-xl overflow-hidden'>
|
|
{renderSettingItem('shopName', '商城名称', 'text', '请输入商城名称')}
|
|
{renderSettingItem('shopLogo', '商城Logo', 'text', '请输入Logo地址')}
|
|
{renderSettingItem('shopDesc', '商城描述', 'text', '请输入商城描述')}
|
|
{renderSettingItem('shopPhone', '客服电话', 'text', '请输入客服电话')}
|
|
{renderSettingItem('shopAddress', '商城地址', 'text', '请输入商城地址')}
|
|
{renderSettingItem('shopEnabled', '商城开关', 'switch')}
|
|
</View>
|
|
)
|
|
case 'order':
|
|
return (
|
|
<View className='bg-white rounded-xl overflow-hidden'>
|
|
{renderSettingItem('orderAutoConfirmDays', '自动确认收货(天)', 'number', '如: 7')}
|
|
{renderSettingItem('orderAutoCloseMins', '未付款自动关闭(分钟)', 'number', '如: 30')}
|
|
{renderSettingItem('orderAutoCommentDays', '自动好评(天)', 'number', '如: 15')}
|
|
{renderSettingItem('orderFreightFreeAmount', '免运费金额', 'number', '如: 99')}
|
|
{renderSettingItem('orderRefundEnabled', '允许退款', 'switch')}
|
|
</View>
|
|
)
|
|
case 'points':
|
|
return (
|
|
<View className='bg-white rounded-xl overflow-hidden'>
|
|
{renderSettingItem('pointsEnabled', '积分功能', 'switch')}
|
|
{renderSettingItem('pointsSigninEnabled', '签到积分', 'switch')}
|
|
{renderSettingItem('pointsSigninAmount', '每日签到积分', 'number', '如: 10')}
|
|
{renderSettingItem('pointsOrderRate', '消费积分比例(元/积分)', 'number', '如: 10')}
|
|
{renderSettingItem('pointsExchangeRate', '积分兑换比例(积分/元)', 'number', '如: 100')}
|
|
</View>
|
|
)
|
|
case 'dealer':
|
|
return (
|
|
<View className='bg-white rounded-xl overflow-hidden'>
|
|
{renderSettingItem('dealerEnabled', '分销功能', 'switch')}
|
|
{renderSettingItem('dealerApplyEnabled', '开放申请', 'switch')}
|
|
{renderSettingItem('dealerLevel1Rate', '一级佣金比例(%)', 'number', '如: 10')}
|
|
{renderSettingItem('dealerLevel2Rate', '二级佣金比例(%)', 'number', '如: 5')}
|
|
{renderSettingItem('dealerWithdrawMin', '最低提现金额', 'number', '如: 10')}
|
|
</View>
|
|
)
|
|
case 'notify':
|
|
return (
|
|
<View className='bg-white rounded-xl overflow-hidden'>
|
|
{renderSettingItem('notifyOrderEnabled', '订单通知', 'switch')}
|
|
{renderSettingItem('notifyPayEnabled', '支付通知', 'switch')}
|
|
{renderSettingItem('notifyRefundEnabled', '退款通知', 'switch')}
|
|
{renderSettingItem('notifyShipEnabled', '发货通知', 'switch')}
|
|
{renderSettingItem('notifySignEnabled', '签收通知', 'switch')}
|
|
</View>
|
|
)
|
|
default:
|
|
return null
|
|
}
|
|
}
|
|
|
|
return (
|
|
<View className='min-h-screen bg-gray-50'>
|
|
{/* 分类 Tab */}
|
|
<View className='bg-white flex overflow-x-auto border-b border-gray-100'>
|
|
{CATEGORIES.map(cat => (
|
|
<View
|
|
key={cat.key}
|
|
className='flex-shrink-0 px-4 py-3 text-center'
|
|
onClick={() => setActiveCategory(cat.key)}
|
|
>
|
|
<Text
|
|
className='text-sm'
|
|
style={{ color: activeCategory === cat.key ? '#0e932e' : '#666' }}
|
|
>
|
|
{cat.label}
|
|
</Text>
|
|
{activeCategory === cat.key && (
|
|
<View
|
|
className='mt-1 mx-auto rounded-full'
|
|
style={{ height: '2px', backgroundColor: '#0e932e', width: '20px' }}
|
|
/>
|
|
)}
|
|
</View>
|
|
))}
|
|
</View>
|
|
|
|
<ScrollView scrollY style={{ height: scrollHeight }}>
|
|
<View className='p-3'>
|
|
{renderCategorySettings()}
|
|
</View>
|
|
|
|
{/* 保存按钮 */}
|
|
{!loading && (
|
|
<View className='px-4 py-3'>
|
|
<View
|
|
className='py-3 rounded-xl text-center'
|
|
style={{ backgroundColor: saving ? '#ccc' : '#0e932e' }}
|
|
onClick={!saving ? handleSave : undefined}
|
|
>
|
|
<Text className='text-white text-sm font-medium'>
|
|
{saving ? '保存中...' : '保存设置'}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
)}
|
|
|
|
<View className='h-6' />
|
|
</ScrollView>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
export default ShopSettingPage
|