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