业务员等
This commit is contained in:
@@ -445,10 +445,16 @@ export default function EquipmentPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const filteredEquipment = equipmentList.filter((item) => {
|
const filteredEquipment = equipmentList.filter((item) => {
|
||||||
|
// 安全地处理可能为 null 或 undefined 的字段
|
||||||
|
const equipmentName = item.equipmentName || ""
|
||||||
|
const merchantName = item.merchantName || ""
|
||||||
|
const mallName = item.mallName || ""
|
||||||
|
const searchLower = searchTerm.toLowerCase()
|
||||||
|
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
item.equipmentName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
equipmentName.toLowerCase().includes(searchLower) ||
|
||||||
item.merchantName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
merchantName.toLowerCase().includes(searchLower) ||
|
||||||
item.mallName.toLowerCase().includes(searchTerm.toLowerCase())
|
mallName.toLowerCase().includes(searchLower)
|
||||||
|
|
||||||
// 修正状态筛选逻辑
|
// 修正状态筛选逻辑
|
||||||
let matchesStatus = true
|
let matchesStatus = true
|
||||||
|
|||||||
@@ -24,27 +24,26 @@ import type { User } from "@/lib/types/user"
|
|||||||
|
|
||||||
export default function ValueAddedServicesPage() {
|
export default function ValueAddedServicesPage() {
|
||||||
const [services, setServices] = useState<ValueAddedService[]>([])
|
const [services, setServices] = useState<ValueAddedService[]>([])
|
||||||
const [adminUsers, setAdminUsers] = useState<User[]>([])
|
const [users, setUsers] = useState<User[]>([])
|
||||||
const [commonUsers, setCommonUsers] = useState<User[]>([])
|
|
||||||
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
|
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
|
||||||
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
|
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
|
||||||
|
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
|
||||||
|
const [serviceToDelete, setServiceToDelete] = useState<ValueAddedService | null>(null)
|
||||||
const [selectedService, setSelectedService] = useState<ValueAddedService | null>(null)
|
const [selectedService, setSelectedService] = useState<ValueAddedService | null>(null)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [pageNum, setPageNum] = useState(1)
|
const [pageNum, setPageNum] = useState(1)
|
||||||
const [pageSize, setPageSize] = useState(10)
|
const [pageSize] = useState(10)
|
||||||
const [total, setTotal] = useState(0)
|
const [total, setTotal] = useState(0)
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
serviceTitle: "",
|
serviceTitle: "",
|
||||||
description: "",
|
description: "",
|
||||||
status: "1",
|
status: "1",
|
||||||
assignedAdmin: "",
|
assignedUsers: "",
|
||||||
assignedCommon: "",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadServices()
|
loadServices()
|
||||||
loadUsers()
|
|
||||||
}, [pageNum, pageSize])
|
}, [pageNum, pageSize])
|
||||||
|
|
||||||
const loadServices = async () => {
|
const loadServices = async () => {
|
||||||
@@ -64,17 +63,10 @@ export default function ValueAddedServicesPage() {
|
|||||||
|
|
||||||
const loadUsers = async () => {
|
const loadUsers = async () => {
|
||||||
try {
|
try {
|
||||||
const [adminResponse, commonResponse] = await Promise.all([
|
// 传空字符串获取所有用户,或者根据实际需求传入特定的 roleKey
|
||||||
getUserByRoleKey()
|
const response = await getUserByRoleKey("")
|
||||||
// getUserByRoleKey("common"),
|
if (response.code === 200 && response.data) {
|
||||||
])
|
setUsers(response.data)
|
||||||
|
|
||||||
if (adminResponse.code === 200 && adminResponse.data) {
|
|
||||||
setAdminUsers(adminResponse.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commonResponse.code === 200 && commonResponse.data) {
|
|
||||||
setCommonUsers(commonResponse.data)
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("加载用户列表失败:", error)
|
console.error("加载用户列表失败:", error)
|
||||||
@@ -84,10 +76,17 @@ export default function ValueAddedServicesPage() {
|
|||||||
const handleCreateService = async () => {
|
const handleCreateService = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const response = await createService(formData)
|
// 将 assignedUsers 同时赋值给 assignedAdmin 和 assignedCommon 以保持 API 兼容性
|
||||||
|
const response = await createService({
|
||||||
|
serviceTitle: formData.serviceTitle,
|
||||||
|
description: formData.description,
|
||||||
|
status: formData.status,
|
||||||
|
assignedAdmin: formData.assignedUsers,
|
||||||
|
assignedCommon: formData.assignedUsers,
|
||||||
|
})
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
setIsCreateDialogOpen(false)
|
setIsCreateDialogOpen(false)
|
||||||
resetForm()
|
resetForm()
|
||||||
loadServices()
|
loadServices()
|
||||||
} else {
|
} else {
|
||||||
alert(response.msg || "创建失败")
|
alert(response.msg || "创建失败")
|
||||||
@@ -105,13 +104,18 @@ export default function ValueAddedServicesPage() {
|
|||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
|
// 将 assignedUsers 同时赋值给 assignedAdmin 和 assignedCommon 以保持 API 兼容性
|
||||||
const response = await updateService({
|
const response = await updateService({
|
||||||
id: selectedService.id,
|
id: selectedService.id,
|
||||||
...formData,
|
serviceTitle: formData.serviceTitle,
|
||||||
|
description: formData.description,
|
||||||
|
status: formData.status,
|
||||||
|
assignedAdmin: formData.assignedUsers,
|
||||||
|
assignedCommon: formData.assignedUsers,
|
||||||
})
|
})
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
setIsEditDialogOpen(false)
|
setIsEditDialogOpen(false)
|
||||||
resetForm()
|
resetForm()
|
||||||
loadServices()
|
loadServices()
|
||||||
} else {
|
} else {
|
||||||
alert(response.msg || "更新失败")
|
alert(response.msg || "更新失败")
|
||||||
@@ -124,13 +128,20 @@ export default function ValueAddedServicesPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDeleteService = async (id: string) => {
|
const handleDeleteService = (service: ValueAddedService) => {
|
||||||
if (!confirm("确定要删除该服务吗?")) return
|
setServiceToDelete(service)
|
||||||
|
setIsDeleteDialogOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConfirmDelete = async () => {
|
||||||
|
if (!serviceToDelete) return
|
||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const response = await deleteService(id)
|
const response = await deleteService(serviceToDelete.id)
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
|
setIsDeleteDialogOpen(false)
|
||||||
|
setServiceToDelete(null)
|
||||||
loadServices()
|
loadServices()
|
||||||
} else {
|
} else {
|
||||||
alert(response.msg || "删除失败")
|
alert(response.msg || "删除失败")
|
||||||
@@ -147,13 +158,18 @@ export default function ValueAddedServicesPage() {
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const newStatus = service.status === "1" ? "0" : "1"
|
const newStatus = service.status === "1" ? "0" : "1"
|
||||||
|
// 合并 assignedAdmin 和 assignedCommon 为 assignedUsers,然后同时赋值给两个字段以保持 API 兼容性
|
||||||
|
const assignedUsers = [
|
||||||
|
...(service.assignedAdmin || "").split(",").filter(Boolean),
|
||||||
|
...(service.assignedCommon || "").split(",").filter(Boolean),
|
||||||
|
].join(",")
|
||||||
const response = await updateService({
|
const response = await updateService({
|
||||||
id: service.id,
|
id: service.id,
|
||||||
serviceTitle: service.serviceTitle,
|
serviceTitle: service.serviceTitle,
|
||||||
description: service.description,
|
description: service.description,
|
||||||
status: newStatus,
|
status: newStatus,
|
||||||
assignedAdmin: service.assignedAdmin || "",
|
assignedAdmin: assignedUsers,
|
||||||
assignedCommon: service.assignedCommon || "",
|
assignedCommon: assignedUsers,
|
||||||
})
|
})
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
loadServices()
|
loadServices()
|
||||||
@@ -173,26 +189,31 @@ export default function ValueAddedServicesPage() {
|
|||||||
serviceTitle: "",
|
serviceTitle: "",
|
||||||
description: "",
|
description: "",
|
||||||
status: "1",
|
status: "1",
|
||||||
assignedAdmin: "",
|
assignedUsers: "",
|
||||||
assignedCommon: "",
|
|
||||||
})
|
})
|
||||||
setSelectedService(null)
|
setSelectedService(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const openEditDialog = (service: ValueAddedService) => {
|
const openEditDialog = async (service: ValueAddedService) => {
|
||||||
setSelectedService(service)
|
setSelectedService(service)
|
||||||
|
// 合并 assignedAdmin 和 assignedCommon 为 assignedUsers
|
||||||
|
const assignedUsers = [
|
||||||
|
...(service.assignedAdmin || "").split(",").filter(Boolean),
|
||||||
|
...(service.assignedCommon || "").split(",").filter(Boolean),
|
||||||
|
].join(",")
|
||||||
setFormData({
|
setFormData({
|
||||||
serviceTitle: service.serviceTitle,
|
serviceTitle: service.serviceTitle,
|
||||||
description: service.description,
|
description: service.description,
|
||||||
status: service.status,
|
status: service.status,
|
||||||
assignedAdmin: service.assignedAdmin || "",
|
assignedUsers: assignedUsers,
|
||||||
assignedCommon: service.assignedCommon || "",
|
|
||||||
})
|
})
|
||||||
|
// 打开编辑对话框时加载用户列表
|
||||||
|
await loadUsers()
|
||||||
setIsEditDialogOpen(true)
|
setIsEditDialogOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUserName = (userId: string, userList: User[]) => {
|
const getUserName = (userId: string) => {
|
||||||
const user = userList.find((u) => u.userId === userId)
|
const user = users.find((u) => u.userId === userId)
|
||||||
return user ? (user.nickName || user.userName) : "未知用户"
|
return user ? (user.nickName || user.userName) : "未知用户"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +222,11 @@ export default function ValueAddedServicesPage() {
|
|||||||
return ids.split(",").filter(Boolean)
|
return ids.split(",").filter(Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleOpenCreateDialog = async () => {
|
||||||
|
await loadUsers()
|
||||||
|
setIsCreateDialogOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
const totalPages = Math.ceil(total / pageSize)
|
const totalPages = Math.ceil(total / pageSize)
|
||||||
|
|
||||||
const handlePrevPage = () => {
|
const handlePrevPage = () => {
|
||||||
@@ -216,133 +242,99 @@ export default function ValueAddedServicesPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-4 sm:space-y-6 p-4 sm:p-6">
|
||||||
{/* Page Header */}
|
{/* Page Header */}
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold text-gray-900">增值服务管理</h1>
|
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900">增值服务管理</h1>
|
||||||
<p className="text-gray-600">管理总公司提供的增值服务,分配给经销商</p>
|
<p className="text-sm sm:text-base text-gray-600">管理增值服务,分配给用户</p>
|
||||||
</div>
|
</div>
|
||||||
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
|
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button>
|
<Button onClick={handleOpenCreateDialog} className="w-full sm:w-auto">
|
||||||
<Plus className="h-4 w-4 mr-2" />
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
新增服务
|
新增服务
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-w-2xl">
|
<DialogContent className="max-w-2xl max-h-[95vh] overflow-y-auto w-[95vw] sm:w-full p-3 sm:p-6">
|
||||||
<DialogHeader>
|
<DialogHeader className="pb-2 sm:pb-4">
|
||||||
<DialogTitle>新增增值服务</DialogTitle>
|
<DialogTitle className="text-base sm:text-lg">新增增值服务</DialogTitle>
|
||||||
<DialogDescription>创建新的增值服务并分配给经销商</DialogDescription>
|
<DialogDescription className="text-xs sm:text-sm">创建新的增值服务并分配给用户</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-4">
|
<div className="space-y-3 sm:space-y-4">
|
||||||
<div>
|
<div className="space-y-1.5 sm:space-y-2">
|
||||||
<Label htmlFor="title">服务标题</Label>
|
<Label htmlFor="title" className="text-sm sm:text-base">服务标题</Label>
|
||||||
<Input
|
<Input
|
||||||
id="title"
|
id="title"
|
||||||
value={formData.serviceTitle}
|
value={formData.serviceTitle}
|
||||||
onChange={(e) => setFormData({ ...formData, serviceTitle: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, serviceTitle: e.target.value })}
|
||||||
placeholder="请输入服务标题"
|
placeholder="请输入服务标题"
|
||||||
|
className="text-sm sm:text-base"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-1.5 sm:space-y-2">
|
||||||
<Label htmlFor="description">服务描述</Label>
|
<Label htmlFor="description" className="text-sm sm:text-base">服务描述</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
id="description"
|
id="description"
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||||
placeholder="请输入服务详细描述"
|
placeholder="请输入服务详细描述"
|
||||||
rows={3}
|
rows={3}
|
||||||
|
className="text-sm sm:text-base resize-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-1.5 sm:space-y-2">
|
||||||
<Label>分配总公司用户</Label>
|
<Label className="text-sm sm:text-base">分配用户</Label>
|
||||||
<div className="grid grid-cols-1 gap-2 mt-2 max-h-48 overflow-y-auto border rounded p-2">
|
<div className="grid grid-cols-1 gap-2 mt-2 max-h-48 sm:max-h-64 overflow-y-auto border rounded p-2">
|
||||||
{adminUsers.map((user) => {
|
{users.length === 0 ? (
|
||||||
const assignedIds = formData.assignedAdmin.split(",").filter(Boolean)
|
<div className="text-center py-4 text-sm text-gray-500">加载中...</div>
|
||||||
const userId = user.userId
|
) : (
|
||||||
return (
|
users.map((user) => {
|
||||||
<div key={userId} className="flex items-center space-x-2 p-2 border rounded">
|
const assignedIds = formData.assignedUsers.split(",").filter(Boolean)
|
||||||
<input
|
const userId = user.userId
|
||||||
type="checkbox"
|
return (
|
||||||
id={`admin-${userId}`}
|
<div key={userId} className="flex items-center space-x-2 p-2 sm:p-3 border rounded hover:bg-gray-50">
|
||||||
checked={assignedIds.includes(userId)}
|
<input
|
||||||
onChange={(e) => {
|
type="checkbox"
|
||||||
const ids = formData.assignedAdmin.split(",").filter(Boolean)
|
id={`user-${userId}`}
|
||||||
if (e.target.checked) {
|
checked={assignedIds.includes(userId)}
|
||||||
setFormData({
|
onChange={(e) => {
|
||||||
...formData,
|
const ids = formData.assignedUsers.split(",").filter(Boolean)
|
||||||
assignedAdmin: [...ids, userId].join(","),
|
if (e.target.checked) {
|
||||||
})
|
setFormData({
|
||||||
} else {
|
...formData,
|
||||||
setFormData({
|
assignedUsers: [...ids, userId].join(","),
|
||||||
...formData,
|
})
|
||||||
assignedAdmin: ids.filter((id) => id !== userId).join(","),
|
} else {
|
||||||
})
|
setFormData({
|
||||||
}
|
...formData,
|
||||||
}}
|
assignedUsers: ids.filter((id) => id !== userId).join(","),
|
||||||
/>
|
})
|
||||||
<label htmlFor={`admin-${userId}`} className="flex-1 cursor-pointer">
|
}
|
||||||
<div className="flex justify-between">
|
}}
|
||||||
<span className="font-medium">{user.nickName || user.userName}</span>
|
className="flex-shrink-0"
|
||||||
<span className="text-sm text-gray-500 flex items-center">
|
/>
|
||||||
<Phone className="h-3 w-3 mr-1" />
|
<label htmlFor={`user-${userId}`} className="flex-1 cursor-pointer min-w-0">
|
||||||
{user.phonenumber}
|
<div className="flex flex-col sm:flex-row sm:justify-between gap-1 sm:gap-0">
|
||||||
</span>
|
<span className="font-medium text-sm sm:text-base truncate">{user.nickName || user.userName}</span>
|
||||||
</div>
|
<span className="text-xs sm:text-sm text-gray-500 flex items-center">
|
||||||
</label>
|
<Phone className="h-3 w-3 mr-1 flex-shrink-0" />
|
||||||
</div>
|
<span className="truncate">{user.phonenumber}</span>
|
||||||
)
|
</span>
|
||||||
})}
|
</div>
|
||||||
</div>
|
</label>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>分配经销商</Label>
|
|
||||||
<div className="grid grid-cols-1 gap-2 mt-2 max-h-48 overflow-y-auto border rounded p-2">
|
|
||||||
{commonUsers.map((user) => {
|
|
||||||
const assignedIds = formData.assignedCommon.split(",").filter(Boolean)
|
|
||||||
const userId = user.userId
|
|
||||||
return (
|
|
||||||
<div key={userId} className="flex items-center space-x-2 p-2 border rounded">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id={`common-${userId}`}
|
|
||||||
checked={assignedIds.includes(userId)}
|
|
||||||
onChange={(e) => {
|
|
||||||
const ids = formData.assignedCommon.split(",").filter(Boolean)
|
|
||||||
if (e.target.checked) {
|
|
||||||
setFormData({
|
|
||||||
...formData,
|
|
||||||
assignedCommon: [...ids, userId].join(","),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
setFormData({
|
|
||||||
...formData,
|
|
||||||
assignedCommon: ids.filter((id) => id !== userId).join(","),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label htmlFor={`common-${userId}`} className="flex-1 cursor-pointer">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="font-medium">{user.nickName || user.userName}</span>
|
|
||||||
<span className="text-sm text-gray-500 flex items-center">
|
|
||||||
<Phone className="h-3 w-3 mr-1" />
|
|
||||||
{user.phonenumber}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</label>
|
)
|
||||||
</div>
|
})
|
||||||
)
|
)}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
<DialogFooter className="flex-col-reverse sm:flex-row gap-2 pt-2">
|
||||||
<Button variant="outline" onClick={() => setIsCreateDialogOpen(false)} disabled={loading}>
|
<Button variant="outline" onClick={() => setIsCreateDialogOpen(false)} disabled={loading} className="w-full sm:w-auto">
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleCreateService} disabled={loading}>
|
<Button onClick={handleCreateService} disabled={loading} className="w-full sm:w-auto">
|
||||||
{loading ? "创建中..." : "创建服务"}
|
{loading ? "创建中..." : "创建服务"}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
@@ -353,120 +345,186 @@ export default function ValueAddedServicesPage() {
|
|||||||
{/* Services List */}
|
{/* Services List */}
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>服务列表</CardTitle>
|
<CardTitle className="text-lg sm:text-xl">服务列表</CardTitle>
|
||||||
<CardDescription>管理所有增值服务的上架、下架和分配</CardDescription>
|
<CardDescription className="text-xs sm:text-sm">管理所有增值服务的上架、下架和分配</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Table>
|
{/* Desktop Table View */}
|
||||||
<TableHeader>
|
<div className="hidden md:block rounded-md border overflow-x-auto">
|
||||||
<TableRow>
|
<Table>
|
||||||
<TableHead>服务标题</TableHead>
|
<TableHeader>
|
||||||
<TableHead>描述</TableHead>
|
|
||||||
<TableHead>状态</TableHead>
|
|
||||||
<TableHead>总公司</TableHead>
|
|
||||||
<TableHead>经销商</TableHead>
|
|
||||||
<TableHead>创建时间</TableHead>
|
|
||||||
<TableHead>操作</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{loading ? (
|
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={7} className="text-center py-8">
|
<TableHead>服务标题</TableHead>
|
||||||
加载中...
|
<TableHead>描述</TableHead>
|
||||||
</TableCell>
|
<TableHead>状态</TableHead>
|
||||||
|
<TableHead>分配用户</TableHead>
|
||||||
|
<TableHead>创建时间</TableHead>
|
||||||
|
<TableHead>操作</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
) : services.length === 0 ? (
|
</TableHeader>
|
||||||
<TableRow>
|
<TableBody>
|
||||||
<TableCell colSpan={7} className="text-center py-8 text-gray-500">
|
{loading ? (
|
||||||
暂无数据
|
<TableRow>
|
||||||
</TableCell>
|
<TableCell colSpan={6} className="text-center py-8">
|
||||||
</TableRow>
|
加载中...
|
||||||
) : (
|
|
||||||
services.map((service) => (
|
|
||||||
<TableRow key={service.id}>
|
|
||||||
<TableCell className="font-medium">{service.serviceTitle}</TableCell>
|
|
||||||
<TableCell className="max-w-xs truncate">{service.description}</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Switch
|
|
||||||
checked={service.status === "1"}
|
|
||||||
onCheckedChange={() => toggleServiceStatus(service)}
|
|
||||||
disabled={loading}
|
|
||||||
/>
|
|
||||||
<Badge variant={service.status === "1" ? "default" : "secondary"}>
|
|
||||||
{service.status === "1" ? "上架" : "下架"}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<div className="flex flex-wrap gap-1">
|
|
||||||
{getUsersByIds(service.assignedAdmin || "").length > 0 ? (
|
|
||||||
<>
|
|
||||||
{getUsersByIds(service.assignedAdmin).slice(0, 2).map((userId) => (
|
|
||||||
<Badge key={userId} variant="outline" className="text-xs">
|
|
||||||
{getUserName(userId, adminUsers)}
|
|
||||||
</Badge>
|
|
||||||
))}
|
|
||||||
{getUsersByIds(service.assignedAdmin).length > 2 && (
|
|
||||||
<Badge variant="outline" className="text-xs">
|
|
||||||
+{getUsersByIds(service.assignedAdmin).length - 2}
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<span className="text-xs text-gray-400">-</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
</TableRow>
|
||||||
<div className="flex flex-wrap gap-1">
|
) : services.length === 0 ? (
|
||||||
{getUsersByIds(service.assignedCommon || "").length > 0 ? (
|
<TableRow>
|
||||||
<>
|
<TableCell colSpan={6} className="text-center py-8 text-gray-500">
|
||||||
{getUsersByIds(service.assignedCommon).slice(0, 2).map((userId) => (
|
暂无数据
|
||||||
<Badge key={userId} variant="outline" className="text-xs">
|
|
||||||
{getUserName(userId, commonUsers)}
|
|
||||||
</Badge>
|
|
||||||
))}
|
|
||||||
{getUsersByIds(service.assignedCommon).length > 2 && (
|
|
||||||
<Badge variant="outline" className="text-xs">
|
|
||||||
+{getUsersByIds(service.assignedCommon).length - 2}
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<span className="text-xs text-gray-400">-</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{service.createdTime || "-"}</TableCell>
|
</TableRow>
|
||||||
<TableCell>
|
) : (
|
||||||
<div className="flex space-x-2">
|
services.map((service) => {
|
||||||
<Button variant="outline" size="sm" onClick={() => openEditDialog(service)} disabled={loading}>
|
// 合并 assignedAdmin 和 assignedCommon
|
||||||
<Edit className="h-4 w-4" />
|
const allAssignedUsers = [
|
||||||
</Button>
|
...getUsersByIds(service.assignedAdmin || ""),
|
||||||
<Button variant="outline" size="sm" onClick={() => handleDeleteService(service.id)} disabled={loading}>
|
...getUsersByIds(service.assignedCommon || ""),
|
||||||
<Trash2 className="h-4 w-4" />
|
]
|
||||||
</Button>
|
return (
|
||||||
</div>
|
<TableRow key={service.id}>
|
||||||
</TableCell>
|
<TableCell className="font-medium">{service.serviceTitle}</TableCell>
|
||||||
</TableRow>
|
<TableCell className="max-w-xs truncate">{service.description}</TableCell>
|
||||||
))
|
<TableCell>
|
||||||
)}
|
<div className="flex items-center space-x-2">
|
||||||
</TableBody>
|
<Switch
|
||||||
</Table>
|
checked={service.status === "1"}
|
||||||
|
onCheckedChange={() => toggleServiceStatus(service)}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
<Badge variant={service.status === "1" ? "default" : "secondary"}>
|
||||||
|
{service.status === "1" ? "上架" : "下架"}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex flex-wrap gap-1">
|
||||||
|
{allAssignedUsers.length > 0 ? (
|
||||||
|
<>
|
||||||
|
{allAssignedUsers.slice(0, 2).map((userId) => (
|
||||||
|
<Badge key={userId} variant="outline" className="text-xs">
|
||||||
|
{getUserName(userId)}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
{allAssignedUsers.length > 2 && (
|
||||||
|
<Badge variant="outline" className="text-xs">
|
||||||
|
+{allAssignedUsers.length - 2}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<span className="text-xs text-gray-400">-</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{service.createTime || "-"}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<Button variant="outline" size="sm" onClick={() => openEditDialog(service)} disabled={loading}>
|
||||||
|
<Edit className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" size="sm" onClick={() => handleDeleteService(service)} disabled={loading}>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Card View */}
|
||||||
|
<div className="md:hidden space-y-4">
|
||||||
|
{loading ? (
|
||||||
|
<div className="text-center py-8 text-gray-500">加载中...</div>
|
||||||
|
) : services.length === 0 ? (
|
||||||
|
<div className="text-center py-8 text-gray-500">暂无数据</div>
|
||||||
|
) : (
|
||||||
|
services.map((service) => {
|
||||||
|
// 合并 assignedAdmin 和 assignedCommon
|
||||||
|
const allAssignedUsers = [
|
||||||
|
...getUsersByIds(service.assignedAdmin || ""),
|
||||||
|
...getUsersByIds(service.assignedCommon || ""),
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<Card key={service.id}>
|
||||||
|
<CardContent className="p-4 space-y-3">
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="font-medium text-sm truncate">{service.serviceTitle}</div>
|
||||||
|
<div className="text-xs text-gray-500 mt-1 line-clamp-2">{service.description}</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 flex-shrink-0">
|
||||||
|
<Switch
|
||||||
|
checked={service.status === "1"}
|
||||||
|
onCheckedChange={() => toggleServiceStatus(service)}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
<Badge variant={service.status === "1" ? "default" : "secondary"} className="text-xs">
|
||||||
|
{service.status === "1" ? "上架" : "下架"}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2 text-sm">
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<span className="text-gray-500">分配用户:</span>
|
||||||
|
<div className="flex flex-wrap gap-1 justify-end flex-1 ml-2">
|
||||||
|
{allAssignedUsers.length > 0 ? (
|
||||||
|
<>
|
||||||
|
{allAssignedUsers.slice(0, 2).map((userId) => (
|
||||||
|
<Badge key={userId} variant="outline" className="text-xs">
|
||||||
|
{getUserName(userId)}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
{allAssignedUsers.length > 2 && (
|
||||||
|
<Badge variant="outline" className="text-xs">
|
||||||
|
+{allAssignedUsers.length - 2}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<span className="text-xs text-gray-400">-</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-gray-500">创建时间:</span>
|
||||||
|
<span className="font-medium text-xs truncate ml-2">{service.createTime || "-"}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex space-x-2 pt-2 border-t">
|
||||||
|
<Button variant="outline" size="sm" onClick={() => openEditDialog(service)} disabled={loading} className="flex-1">
|
||||||
|
<Edit className="h-4 w-4 mr-1" />
|
||||||
|
编辑
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" size="sm" onClick={() => handleDeleteService(service)} disabled={loading} className="flex-1">
|
||||||
|
<Trash2 className="h-4 w-4 mr-1" />
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
<div className="flex items-center justify-between mt-4">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mt-4 pt-4 border-t">
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-xs sm:text-sm text-gray-600">
|
||||||
共 {total} 条记录,第 {pageNum} / {totalPages || 1} 页
|
共 {total} 条记录,第 {pageNum} / {totalPages || 1} 页
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2 w-full sm:w-auto">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={handlePrevPage}
|
onClick={handlePrevPage}
|
||||||
disabled={pageNum === 1 || loading}
|
disabled={pageNum === 1 || loading}
|
||||||
|
className="flex-1 sm:flex-initial text-xs sm:text-sm"
|
||||||
>
|
>
|
||||||
<ChevronLeft className="h-4 w-4 mr-1" />
|
<ChevronLeft className="h-4 w-4 mr-1" />
|
||||||
上一页
|
上一页
|
||||||
@@ -476,6 +534,7 @@ export default function ValueAddedServicesPage() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
onClick={handleNextPage}
|
onClick={handleNextPage}
|
||||||
disabled={pageNum >= totalPages || loading}
|
disabled={pageNum >= totalPages || loading}
|
||||||
|
className="flex-1 sm:flex-initial text-xs sm:text-sm"
|
||||||
>
|
>
|
||||||
下一页
|
下一页
|
||||||
<ChevronRight className="h-4 w-4 ml-1" />
|
<ChevronRight className="h-4 w-4 ml-1" />
|
||||||
@@ -487,124 +546,139 @@ export default function ValueAddedServicesPage() {
|
|||||||
|
|
||||||
{/* Edit Dialog */}
|
{/* Edit Dialog */}
|
||||||
<Dialog open={isEditDialogOpen} onOpenChange={setIsEditDialogOpen}>
|
<Dialog open={isEditDialogOpen} onOpenChange={setIsEditDialogOpen}>
|
||||||
<DialogContent className="max-w-2xl">
|
<DialogContent className="max-w-2xl max-h-[95vh] overflow-y-auto w-[95vw] sm:w-full p-3 sm:p-6">
|
||||||
<DialogHeader>
|
<DialogHeader className="pb-2 sm:pb-4">
|
||||||
<DialogTitle>编辑增值服务</DialogTitle>
|
<DialogTitle className="text-base sm:text-lg">编辑增值服务</DialogTitle>
|
||||||
<DialogDescription>修改服务信息和经销商分配</DialogDescription>
|
<DialogDescription className="text-xs sm:text-sm">修改服务信息和用户分配</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-4">
|
<div className="space-y-3 sm:space-y-4">
|
||||||
<div>
|
<div className="space-y-1.5 sm:space-y-2">
|
||||||
<Label htmlFor="edit-title">服务标题</Label>
|
<Label htmlFor="edit-title" className="text-sm sm:text-base">服务标题</Label>
|
||||||
<Input
|
<Input
|
||||||
id="edit-title"
|
id="edit-title"
|
||||||
value={formData.serviceTitle}
|
value={formData.serviceTitle}
|
||||||
onChange={(e) => setFormData({ ...formData, serviceTitle: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, serviceTitle: e.target.value })}
|
||||||
placeholder="请输入服务标题"
|
placeholder="请输入服务标题"
|
||||||
|
className="text-sm sm:text-base"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-1.5 sm:space-y-2">
|
||||||
<Label htmlFor="edit-description">服务描述</Label>
|
<Label htmlFor="edit-description" className="text-sm sm:text-base">服务描述</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
id="edit-description"
|
id="edit-description"
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||||
placeholder="请输入服务详细描述"
|
placeholder="请输入服务详细描述"
|
||||||
rows={3}
|
rows={3}
|
||||||
|
className="text-sm sm:text-base resize-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-1.5 sm:space-y-2">
|
||||||
<Label>分配总公司用户</Label>
|
<Label className="text-sm sm:text-base">分配用户</Label>
|
||||||
<div className="grid grid-cols-1 gap-2 mt-2 max-h-48 overflow-y-auto border rounded p-2">
|
<div className="grid grid-cols-1 gap-2 mt-2 max-h-48 sm:max-h-64 overflow-y-auto border rounded p-2">
|
||||||
{adminUsers.map((user) => {
|
{users.length === 0 ? (
|
||||||
const assignedIds = formData.assignedAdmin.split(",").filter(Boolean)
|
<div className="text-center py-4 text-sm text-gray-500">加载中...</div>
|
||||||
const userId = user.userId
|
) : (
|
||||||
return (
|
users.map((user) => {
|
||||||
<div key={userId} className="flex items-center space-x-2 p-2 border rounded">
|
const assignedIds = formData.assignedUsers.split(",").filter(Boolean)
|
||||||
<input
|
const userId = user.userId
|
||||||
type="checkbox"
|
return (
|
||||||
id={`edit-admin-${userId}`}
|
<div key={userId} className="flex items-center space-x-2 p-2 sm:p-3 border rounded hover:bg-gray-50">
|
||||||
checked={assignedIds.includes(userId)}
|
<input
|
||||||
onChange={(e) => {
|
type="checkbox"
|
||||||
const ids = formData.assignedAdmin.split(",").filter(Boolean)
|
id={`edit-user-${userId}`}
|
||||||
if (e.target.checked) {
|
checked={assignedIds.includes(userId)}
|
||||||
setFormData({
|
onChange={(e) => {
|
||||||
...formData,
|
const ids = formData.assignedUsers.split(",").filter(Boolean)
|
||||||
assignedAdmin: [...ids, userId].join(","),
|
if (e.target.checked) {
|
||||||
})
|
setFormData({
|
||||||
} else {
|
...formData,
|
||||||
setFormData({
|
assignedUsers: [...ids, userId].join(","),
|
||||||
...formData,
|
})
|
||||||
assignedAdmin: ids.filter((id) => id !== userId).join(","),
|
} else {
|
||||||
})
|
setFormData({
|
||||||
}
|
...formData,
|
||||||
}}
|
assignedUsers: ids.filter((id) => id !== userId).join(","),
|
||||||
/>
|
})
|
||||||
<label htmlFor={`edit-admin-${userId}`} className="flex-1 cursor-pointer">
|
}
|
||||||
<div className="flex justify-between">
|
}}
|
||||||
<span className="font-medium">{user.nickName || user.userName}</span>
|
className="flex-shrink-0"
|
||||||
<span className="text-sm text-gray-500 flex items-center">
|
/>
|
||||||
<Phone className="h-3 w-3 mr-1" />
|
<label htmlFor={`edit-user-${userId}`} className="flex-1 cursor-pointer min-w-0">
|
||||||
{user.phonenumber}
|
<div className="flex flex-col sm:flex-row sm:justify-between gap-1 sm:gap-0">
|
||||||
</span>
|
<span className="font-medium text-sm sm:text-base truncate">{user.nickName || user.userName}</span>
|
||||||
</div>
|
<span className="text-xs sm:text-sm text-gray-500 flex items-center">
|
||||||
</label>
|
<Phone className="h-3 w-3 mr-1 flex-shrink-0" />
|
||||||
</div>
|
<span className="truncate">{user.phonenumber}</span>
|
||||||
)
|
</span>
|
||||||
})}
|
</div>
|
||||||
</div>
|
</label>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>分配经销商</Label>
|
|
||||||
<div className="grid grid-cols-1 gap-2 mt-2 max-h-48 overflow-y-auto border rounded p-2">
|
|
||||||
{commonUsers.map((user) => {
|
|
||||||
const assignedIds = formData.assignedCommon.split(",").filter(Boolean)
|
|
||||||
const userId = user.userId
|
|
||||||
return (
|
|
||||||
<div key={userId} className="flex items-center space-x-2 p-2 border rounded">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id={`edit-common-${userId}`}
|
|
||||||
checked={assignedIds.includes(userId)}
|
|
||||||
onChange={(e) => {
|
|
||||||
const ids = formData.assignedCommon.split(",").filter(Boolean)
|
|
||||||
if (e.target.checked) {
|
|
||||||
setFormData({
|
|
||||||
...formData,
|
|
||||||
assignedCommon: [...ids, userId].join(","),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
setFormData({
|
|
||||||
...formData,
|
|
||||||
assignedCommon: ids.filter((id) => id !== userId).join(","),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label htmlFor={`edit-common-${userId}`} className="flex-1 cursor-pointer">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="font-medium">{user.nickName || user.userName}</span>
|
|
||||||
<span className="text-sm text-gray-500 flex items-center">
|
|
||||||
<Phone className="h-3 w-3 mr-1" />
|
|
||||||
{user.phonenumber}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</label>
|
)
|
||||||
</div>
|
})
|
||||||
)
|
)}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
<DialogFooter className="flex-col-reverse sm:flex-row gap-2 pt-2">
|
||||||
<Button variant="outline" onClick={() => setIsEditDialogOpen(false)} disabled={loading}>
|
<Button variant="outline" onClick={() => setIsEditDialogOpen(false)} disabled={loading} className="w-full sm:w-auto">
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleEditService} disabled={loading}>
|
<Button onClick={handleEditService} disabled={loading} className="w-full sm:w-auto">
|
||||||
{loading ? "保存中..." : "保存修改"}
|
{loading ? "保存中..." : "保存修改"}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
{/* Delete Confirmation Dialog */}
|
||||||
|
<Dialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
|
||||||
|
<DialogContent className="sm:max-w-[400px] max-h-[95vh] overflow-y-auto w-[95vw] sm:w-full p-3 sm:p-6">
|
||||||
|
<DialogHeader className="pb-2 sm:pb-4">
|
||||||
|
<DialogTitle className="text-base sm:text-lg">确认删除</DialogTitle>
|
||||||
|
<DialogDescription className="text-xs sm:text-sm">
|
||||||
|
确定要删除服务 "{serviceToDelete?.serviceTitle}" 吗?此操作不可撤销。
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="py-4">
|
||||||
|
{serviceToDelete && (
|
||||||
|
<div className="space-y-2 text-sm">
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">服务标题:</span>
|
||||||
|
<span className="font-medium">{serviceToDelete.serviceTitle}</span>
|
||||||
|
</div>
|
||||||
|
{serviceToDelete.description && (
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">服务描述:</span>
|
||||||
|
<span className="font-medium line-clamp-2">{serviceToDelete.description}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<DialogFooter className="flex-col-reverse sm:flex-row gap-2 pt-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setIsDeleteDialogOpen(false)
|
||||||
|
setServiceToDelete(null)
|
||||||
|
}}
|
||||||
|
disabled={loading}
|
||||||
|
className="w-full sm:w-auto"
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={handleConfirmDelete}
|
||||||
|
disabled={loading}
|
||||||
|
className="w-full sm:w-auto"
|
||||||
|
>
|
||||||
|
{loading ? "删除中..." : "确认删除"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui
|
|||||||
import { Button } from "../ui/button"
|
import { Button } from "../ui/button"
|
||||||
import { Input } from "../ui/input"
|
import { Input } from "../ui/input"
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table"
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table"
|
||||||
import { Search, Download, User, ChevronLeft, ChevronRight, Plus, Edit } from "lucide-react"
|
import { Search, Download, User, ChevronLeft, ChevronRight, Plus, Edit, Trash2 } from "lucide-react"
|
||||||
import { apiGet, apiPost, apiPut } from "../../services/api"
|
import { apiGet, apiPost, apiPut, apiDelete } from "../../services/api"
|
||||||
import { getUserData } from "../../utils/storage"
|
import { getUserData } from "../../utils/storage"
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"
|
||||||
import {
|
import {
|
||||||
@@ -46,6 +46,8 @@ export default function SalePage() {
|
|||||||
// 新增业务员对话框状态
|
// 新增业务员对话框状态
|
||||||
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false)
|
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false)
|
||||||
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
|
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
|
||||||
|
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
|
||||||
|
const [userToDelete, setUserToDelete] = useState<SaleUser | null>(null)
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
userName: "",
|
userName: "",
|
||||||
@@ -248,6 +250,40 @@ export default function SalePage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 打开删除确认对话框
|
||||||
|
const handleDeleteUser = (user: SaleUser) => {
|
||||||
|
setUserToDelete(user)
|
||||||
|
setIsDeleteDialogOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认删除业务员
|
||||||
|
const handleConfirmDelete = async () => {
|
||||||
|
if (!userToDelete) return
|
||||||
|
|
||||||
|
setIsSubmitting(true)
|
||||||
|
try {
|
||||||
|
// 调用删除接口
|
||||||
|
const response: any = await apiDelete(`/back/sale/${userToDelete.userId}`)
|
||||||
|
|
||||||
|
console.log("删除业务员响应:", response)
|
||||||
|
|
||||||
|
if (response.code === 200) {
|
||||||
|
alert("删除业务员成功")
|
||||||
|
setIsDeleteDialogOpen(false)
|
||||||
|
setUserToDelete(null)
|
||||||
|
// 刷新列表
|
||||||
|
fetchSalesUsers(currentPage, pageSize)
|
||||||
|
} else {
|
||||||
|
alert(`删除业务员失败: ${response.msg || "未知错误"}`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("删除业务员请求失败:", error)
|
||||||
|
alert("删除业务员失败,请重试")
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 提交新增业务员
|
// 提交新增业务员
|
||||||
const handleSubmitAdd = async () => {
|
const handleSubmitAdd = async () => {
|
||||||
// 表单验证
|
// 表单验证
|
||||||
@@ -400,14 +436,27 @@ export default function SalePage() {
|
|||||||
<div className="text-sm">{user.createTime}</div>
|
<div className="text-sm">{user.createTime}</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-center">
|
<TableCell className="text-center">
|
||||||
<Button
|
<div className="flex items-center justify-center space-x-2">
|
||||||
variant="ghost"
|
<Button
|
||||||
size="sm"
|
variant="ghost"
|
||||||
onClick={() => handleOpenEditDialog(user)}
|
size="sm"
|
||||||
>
|
onClick={() => handleOpenEditDialog(user)}
|
||||||
<Edit className="h-4 w-4 mr-1" />
|
disabled={loading}
|
||||||
编辑
|
>
|
||||||
</Button>
|
<Edit className="h-4 w-4 mr-1" />
|
||||||
|
编辑
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleDeleteUser(user)}
|
||||||
|
disabled={loading}
|
||||||
|
className="text-red-600 hover:text-red-700 hover:bg-red-50"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 mr-1" />
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))
|
))
|
||||||
@@ -436,15 +485,26 @@ export default function SalePage() {
|
|||||||
<div className="text-xs text-gray-500 truncate">{user.nickName}</div>
|
<div className="text-xs text-gray-500 truncate">{user.nickName}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<div className="flex items-center gap-2 flex-shrink-0">
|
||||||
variant="ghost"
|
<Button
|
||||||
size="sm"
|
variant="ghost"
|
||||||
onClick={() => handleOpenEditDialog(user)}
|
size="sm"
|
||||||
className="flex-shrink-0 h-8 px-2 sm:px-3 text-xs sm:text-sm"
|
onClick={() => handleOpenEditDialog(user)}
|
||||||
>
|
className="h-8 px-2 sm:px-3 text-xs sm:text-sm"
|
||||||
<Edit className="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1" />
|
>
|
||||||
编辑
|
<Edit className="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1" />
|
||||||
</Button>
|
编辑
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleDeleteUser(user)}
|
||||||
|
className="h-8 px-2 sm:px-3 text-xs sm:text-sm text-red-600 hover:text-red-700 hover:bg-red-50"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1" />
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border-t pt-3 space-y-2">
|
<div className="border-t pt-3 space-y-2">
|
||||||
@@ -711,6 +771,67 @@ export default function SalePage() {
|
|||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
{/* 删除确认对话框 */}
|
||||||
|
<Dialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
|
||||||
|
<DialogContent className="sm:max-w-[400px] max-h-[95vh] overflow-y-auto w-[95vw] sm:w-full p-3 sm:p-6">
|
||||||
|
<DialogHeader className="pb-2 sm:pb-4">
|
||||||
|
<DialogTitle className="text-base sm:text-lg">确认删除</DialogTitle>
|
||||||
|
<DialogDescription className="text-xs sm:text-sm">
|
||||||
|
确定要删除业务员 "{userToDelete?.userName || userToDelete?.nickName}" 吗?此操作不可撤销。
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="py-4">
|
||||||
|
{userToDelete && (
|
||||||
|
<div className="space-y-2 text-sm">
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">用户ID:</span>
|
||||||
|
<span className="font-medium">{userToDelete.userId}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">用户名:</span>
|
||||||
|
<span className="font-medium">{userToDelete.userName}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">昵称:</span>
|
||||||
|
<span className="font-medium">{userToDelete.nickName}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">手机号:</span>
|
||||||
|
<span className="font-medium">{userToDelete.phonenumber}</span>
|
||||||
|
</div>
|
||||||
|
{userToDelete.email && (
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">邮箱:</span>
|
||||||
|
<span className="font-medium">{userToDelete.email}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<DialogFooter className="flex-col-reverse sm:flex-row gap-2 pt-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setIsDeleteDialogOpen(false)
|
||||||
|
setUserToDelete(null)
|
||||||
|
}}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
className="w-full sm:w-auto"
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={handleConfirmDelete}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
className="w-full sm:w-auto"
|
||||||
|
>
|
||||||
|
{isSubmitting ? "删除中..." : "确认删除"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user