Files
xinlong-shop-taro/src_bak/pages/title.tsx
赵忠林 1fa58040f3 feat(user): 新增收货地址管理及售后申请页面
- 新增地址类型定义,增强前端地址数据结构
- 新增地址编辑页面,支持地址智能识别和定位选点功能
- 地址编辑支持省市区选择及默认地址设置
- 新增地址列表页面,支持地址展示、删除、编辑和选择功能
- 实现售后申请页面,支持选择售后类型和退款原因
- 售后申请支持商品选择、退款金额计算和凭证上传
- 新增售后详情页面,支持售后状态展示及申请取消
- 优化页面加载和用户交互体验,增加错误提示和权限处理
2026-07-01 12:11:56 +08:00

298 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { View, Text, ScrollView, Input, Button } from '@tarojs/components';
import { useState, useEffect, useCallback } from 'react';
import { useDidShow } from '@tarojs/taro';
import Taro from '@tarojs/taro';
import NavBar from '@/components/NavBar';
import LoadMore from '@/components/common/LoadMore';
import EmptyState from '@/components/common/EmptyState';
import {
listShopInvoiceTitle,
addShopInvoiceTitle,
updateShopInvoiceTitle,
removeShopInvoiceTitle,
setDefaultInvoiceTitle
} from '@/api/shop/shopInvoiceTitle';
import type { ShopInvoiceTitle, ShopInvoiceTitleParam } from '@/api/shop/shopInvoiceTitle/model';
export default function InvoiceTitlePage() {
const [titleList, setTitleList] = useState<ShopInvoiceTitle[]>([]);
const [loading, setLoading] = useState(true);
const [showAddModal, setShowAddModal] = useState(false);
const [editItem, setEditItem] = useState<ShopInvoiceTitle | null>(null);
const [formData, setFormData] = useState({
type: 'personal' as 'personal' | 'company',
name: '',
taxNumber: '',
email: '',
});
// 加载数据
const loadData = useCallback(async () => {
setLoading(true);
try {
const params: ShopInvoiceTitleParam = {};
const result = await listShopInvoiceTitle(params);
setTitleList(result || []);
} catch (e: any) {
console.error('获取发票抬头失败:', e);
Taro.showToast({ title: e.message || '获取数据失败', icon: 'none' });
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
loadData();
}, [loadData]);
// 删除
const handleDelete = async (id: number) => {
Taro.showModal({
title: '确认删除',
content: '确定要删除该发票抬头吗?',
success: async (res) => {
if (res.confirm) {
try {
await removeShopInvoiceTitle(id);
Taro.showToast({ title: '删除成功', icon: 'success' });
loadData();
} catch (e: any) {
Taro.showToast({ title: e.message || '删除失败', icon: 'none' });
}
}
}
});
};
// 设置默认
const handleSetDefault = async (id: number) => {
try {
await setDefaultInvoiceTitle(id);
Taro.showToast({ title: '设置成功', icon: 'success' });
loadData();
} catch (e: any) {
Taro.showToast({ title: e.message || '设置失败', icon: 'none' });
}
};
// 编辑
const handleEdit = (item: ShopInvoiceTitle) => {
setEditItem(item);
setFormData({
type: (item.type as 'personal' | 'company') || 'personal',
name: item.name || '',
taxNumber: item.taxNumber || '',
email: item.email || '',
});
setShowAddModal(true);
};
// 添加/编辑提交
const handleSubmit = async () => {
if (!formData.name.trim()) {
Taro.showToast({ title: '请输入抬头名称', icon: 'none' });
return;
}
if (formData.type === 'company' && !formData.taxNumber.trim()) {
Taro.showToast({ title: '请输入税号', icon: 'none' });
return;
}
try {
if (editItem) {
await updateShopInvoiceTitle({
id: editItem.id,
...formData,
});
Taro.showToast({ title: '修改成功', icon: 'success' });
} else {
await addShopInvoiceTitle(formData);
Taro.showToast({ title: '添加成功', icon: 'success' });
}
setShowAddModal(false);
setEditItem(null);
setFormData({ type: 'personal', name: '', taxNumber: '', email: '' });
loadData();
} catch (e: any) {
Taro.showToast({ title: e.message || '操作失败', icon: 'none' });
}
};
// 取消编辑
const handleCancel = () => {
setShowAddModal(false);
setEditItem(null);
setFormData({ type: 'personal', name: '', taxNumber: '', email: '' });
};
return (
<View className="bg-gray-100 flex flex-col" style={{ minHeight: '100vh' }}>
<NavBar title="发票抬头管理" />
<View className='flex-1'>
{loading ? (
<LoadMore loading />
) : titleList.length === 0 ? (
<EmptyState message="暂无发票抬头" />
) : (
<ScrollView scrollY className="p-4">
{titleList.map(item => (
<View key={item.id} className="bg-white rounded-lg p-4 mb-4">
<View className="flex justify-between items-start mb-2">
<View className="flex-1">
<View className="flex items-center mb-2">
<Text className={`text-xs px-2 py-1 rounded mr-2 ${
item.type === 'personal' ? 'bg-blue-100 text-blue-600' : 'bg-orange-100 text-orange-600'
}`}>
{item.type === 'personal' ? '个人' : '企业'}
</Text>
{item.isDefault === 1 && (
<Text className="text-xs px-2 py-1 rounded bg-red-100 text-red-600">
</Text>
)}
</View>
<Text className="text-lg font-medium">{item.name}</Text>
{item.type === 'company' && item.taxNumber && (
<Text className="text-sm text-gray-500 mt-1 block">{item.taxNumber}</Text>
)}
{item.email && (
<Text className="text-sm text-gray-500 mt-1 block">{item.email}</Text>
)}
</View>
</View>
<View className="flex justify-between items-center pt-3 border-t border-gray-100">
<View
className="flex items-center"
onClick={() => handleSetDefault(item.id!)}
>
<View className={`w-5 h-5 rounded border-2 flex items-center justify-center ${
item.isDefault === 1 ? 'bg-red-500 border-red-500' : 'border-gray-300'
}`}>
{item.isDefault === 1 && <Text className="text-white text-xs"></Text>}
</View>
<Text className="text-sm text-gray-600 ml-2"></Text>
</View>
<View className="flex" style={{ gap: '16px' }}>
<Text
className="text-sm text-blue-500"
onClick={() => handleEdit(item)}
>
</Text>
<Text
className="text-sm text-red-500"
onClick={() => handleDelete(item.id!)}
>
</Text>
</View>
</View>
</View>
))}
</ScrollView>
)}
</View>
{/* 添加/编辑弹窗 */}
{showAddModal && (
<View className="absolute flex items-center justify-center z-50" style={{ top: 0, right: 0, bottom: 0, left: 0, backgroundColor: 'rgba(0,0,0,0.5)' }}>
<View className="bg-white rounded-lg p-6" style={{ width: '91.667%', maxWidth: '688px' }}>
<Text className="text-xl font-bold mb-4">{editItem ? '编辑发票抬头' : '添加发票抬头'}</Text>
{/* 抬头类型 */}
<View className="mb-4">
<Text className="text-sm text-gray-600 mb-2 block"></Text>
<View className="flex">
<View
className={`flex-1 py-2 text-center border rounded-l ${
formData.type === 'personal' ? 'bg-red-50 border-red-500 text-red-500' : 'border-gray-300'
}`}
onClick={() => setFormData(prev => ({ ...prev, type: 'personal' }))}
>
<Text></Text>
</View>
<View
className={`flex-1 py-2 text-center border rounded-r ${
formData.type === 'company' ? 'bg-red-50 border-red-500 text-red-500' : 'border-gray-300'
}`}
onClick={() => setFormData(prev => ({ ...prev, type: 'company' }))}
>
<Text></Text>
</View>
</View>
</View>
{/* 抬头名称 */}
<View className="mb-4">
<Text className="text-sm text-gray-600 mb-2 block"></Text>
<Input
className="border rounded px-3 py-2"
placeholder="请输入发票抬头名称"
value={formData.name}
onInput={(e: any) => setFormData(prev => ({ ...prev, name: e.detail.value }))}
/>
</View>
{/* 税号 */}
{formData.type === 'company' && (
<View className="mb-4">
<Text className="text-sm text-gray-600 mb-2 block"></Text>
<Input
className="border rounded px-3 py-2"
placeholder="请输入税号"
value={formData.taxNumber}
onInput={(e: any) => setFormData(prev => ({ ...prev, taxNumber: e.detail.value }))}
/>
</View>
)}
{/* 邮箱 */}
<View className="mb-4">
<Text className="text-sm text-gray-600 mb-2 block"></Text>
<Input
className="border rounded px-3 py-2"
type="email"
placeholder="用于接收电子发票"
value={formData.email}
onInput={(e: any) => setFormData(prev => ({ ...prev, email: e.detail.value }))}
/>
</View>
{/* 按钮 */}
<View className="flex" style={{ gap: '12px' }}>
<Button
className="flex-1 py-2 border border-gray-300 rounded"
onClick={handleCancel}
>
</Button>
<Button
className="flex-1 py-2 bg-red-500 text-white rounded"
onClick={handleSubmit}
>
</Button>
</View>
</View>
</View>
)}
{/* 底部添加按钮 */}
<View className="p-4 bg-white border-t border-gray-200" style={{ paddingBottom: '20px' }}>
<Button
className="bg-red-500 text-white rounded-full w-full h-12"
onClick={() => {
setEditItem(null);
setFormData({ type: 'personal', name: '', taxNumber: '', email: '' });
setShowAddModal(true);
}}
>
</Button>
</View>
</View>
);
}