q q q
This commit is contained in:
@@ -745,7 +745,7 @@ export default function EquipmentPage() {
|
|||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Shield className="h-4 w-4 text-blue-600" />
|
<Shield className="h-4 w-4 text-blue-600" />
|
||||||
<span className="font-medium text-blue-600">{item.equipmentId}</span>
|
<span className="font-medium text-blue-600">{item.equId}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-medium">{item.equipmentName}</div>
|
<div className="text-sm font-medium">{item.equipmentName}</div>
|
||||||
<div className="text-xs text-gray-500">类型: {equipmentTypeDisplay}</div>
|
<div className="text-xs text-gray-500">类型: {equipmentTypeDisplay}</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react"
|
import { useState, useEffect } from "react"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||||
import { Button } from "../ui/button"
|
import { Button } from "../ui/button"
|
||||||
import { Input } from "../ui/input"
|
import { Input } from "../ui/input"
|
||||||
@@ -16,121 +16,165 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "../ui/dialog"
|
} from "../ui/dialog"
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table"
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table"
|
||||||
import { Plus, Edit, Trash2, Phone } from "lucide-react"
|
import { Plus, Edit, Trash2, Phone, ChevronLeft, ChevronRight } from "lucide-react"
|
||||||
|
import { getServiceList, createService, updateService, deleteService } from "@/lib/services/service"
|
||||||
interface ValueAddedService {
|
import { ValueAddedService } from "@/lib/types/service"
|
||||||
id: string
|
import { getUserByRoleKey } from "@/lib/services/user"
|
||||||
title: string
|
import type { User } from "@/lib/types/user"
|
||||||
description: string
|
|
||||||
status: "active" | "inactive"
|
|
||||||
assignedDealers: string[]
|
|
||||||
createdAt: string
|
|
||||||
updatedAt: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Dealer {
|
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
phone: string
|
|
||||||
region: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ValueAddedServicesPage() {
|
export default function ValueAddedServicesPage() {
|
||||||
const [services, setServices] = useState<ValueAddedService[]>([
|
const [services, setServices] = useState<ValueAddedService[]>([])
|
||||||
{
|
const [adminUsers, setAdminUsers] = useState<User[]>([])
|
||||||
id: "1",
|
const [commonUsers, setCommonUsers] = useState<User[]>([])
|
||||||
title: "消防设备年检服务",
|
|
||||||
description: "提供专业的消防设备年度检测服务,确保设备符合安全标准",
|
|
||||||
status: "active",
|
|
||||||
assignedDealers: ["dealer1", "dealer2"],
|
|
||||||
createdAt: "2024-01-15",
|
|
||||||
updatedAt: "2024-01-15",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
title: "紧急维修服务",
|
|
||||||
description: "24小时紧急维修服务,快速响应设备故障",
|
|
||||||
status: "active",
|
|
||||||
assignedDealers: ["dealer1", "dealer3"],
|
|
||||||
createdAt: "2024-01-10",
|
|
||||||
updatedAt: "2024-01-20",
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const [dealers] = useState<Dealer[]>([
|
|
||||||
{ id: "dealer1", name: "华东经销商", phone: "138****1234", region: "华东区" },
|
|
||||||
{ id: "dealer2", name: "华南经销商", phone: "139****5678", region: "华南区" },
|
|
||||||
{ id: "dealer3", name: "华北经销商", phone: "137****9012", region: "华北区" },
|
|
||||||
])
|
|
||||||
|
|
||||||
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
|
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
|
||||||
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
|
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
|
||||||
const [selectedService, setSelectedService] = 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 [total, setTotal] = useState(0)
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
title: "",
|
serviceTitle: "",
|
||||||
description: "",
|
description: "",
|
||||||
status: "active" as "active" | "inactive",
|
status: "1",
|
||||||
assignedDealers: [] as string[],
|
assignedAdmin: "",
|
||||||
|
assignedCommon: "",
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleCreateService = () => {
|
useEffect(() => {
|
||||||
const newService: ValueAddedService = {
|
loadServices()
|
||||||
id: Date.now().toString(),
|
loadUsers()
|
||||||
title: formData.title,
|
}, [pageNum, pageSize])
|
||||||
description: formData.description,
|
|
||||||
status: formData.status,
|
const loadServices = async () => {
|
||||||
assignedDealers: formData.assignedDealers,
|
setLoading(true)
|
||||||
createdAt: new Date().toISOString().split("T")[0],
|
try {
|
||||||
updatedAt: new Date().toISOString().split("T")[0],
|
const response = await getServiceList({ pageNum, pageSize })
|
||||||
|
if (response.code === 200) {
|
||||||
|
setServices(response.rows)
|
||||||
|
setTotal(response.total)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("加载服务列表失败:", error)
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
}
|
}
|
||||||
setServices([...services, newService])
|
}
|
||||||
|
|
||||||
|
const loadUsers = async () => {
|
||||||
|
try {
|
||||||
|
const [adminResponse, commonResponse] = await Promise.all([
|
||||||
|
getUserByRoleKey("admin"),
|
||||||
|
getUserByRoleKey("common"),
|
||||||
|
])
|
||||||
|
|
||||||
|
if (adminResponse.code === 200 && adminResponse.data) {
|
||||||
|
setAdminUsers(adminResponse.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commonResponse.code === 200 && commonResponse.data) {
|
||||||
|
setCommonUsers(commonResponse.data)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("加载用户列表失败:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCreateService = async () => {
|
||||||
|
setLoading(true)
|
||||||
|
try {
|
||||||
|
const response = await createService(formData)
|
||||||
|
if (response.code === 200) {
|
||||||
setIsCreateDialogOpen(false)
|
setIsCreateDialogOpen(false)
|
||||||
resetForm()
|
resetForm()
|
||||||
|
loadServices()
|
||||||
|
} else {
|
||||||
|
alert(response.msg || "创建失败")
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("创建服务失败:", error)
|
||||||
|
alert("创建服务失败")
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleEditService = () => {
|
const handleEditService = async () => {
|
||||||
if (!selectedService) return
|
if (!selectedService) return
|
||||||
|
|
||||||
const updatedServices = services.map((service) =>
|
setLoading(true)
|
||||||
service.id === selectedService.id
|
try {
|
||||||
? {
|
const response = await updateService({
|
||||||
...service,
|
id: selectedService.id,
|
||||||
title: formData.title,
|
...formData,
|
||||||
description: formData.description,
|
})
|
||||||
status: formData.status,
|
if (response.code === 200) {
|
||||||
assignedDealers: formData.assignedDealers,
|
|
||||||
updatedAt: new Date().toISOString().split("T")[0],
|
|
||||||
}
|
|
||||||
: service,
|
|
||||||
)
|
|
||||||
setServices(updatedServices)
|
|
||||||
setIsEditDialogOpen(false)
|
setIsEditDialogOpen(false)
|
||||||
resetForm()
|
resetForm()
|
||||||
|
loadServices()
|
||||||
|
} else {
|
||||||
|
alert(response.msg || "更新失败")
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("更新服务失败:", error)
|
||||||
|
alert("更新服务失败")
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDeleteService = (id: string) => {
|
const handleDeleteService = async (id: string) => {
|
||||||
setServices(services.filter((service) => service.id !== id))
|
if (!confirm("确定要删除该服务吗?")) return
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
try {
|
||||||
|
const response = await deleteService(id)
|
||||||
|
if (response.code === 200) {
|
||||||
|
loadServices()
|
||||||
|
} else {
|
||||||
|
alert(response.msg || "删除失败")
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("删除服务失败:", error)
|
||||||
|
alert("删除服务失败")
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleServiceStatus = (id: string) => {
|
const toggleServiceStatus = async (service: ValueAddedService) => {
|
||||||
const updatedServices = services.map((service) =>
|
setLoading(true)
|
||||||
service.id === id
|
try {
|
||||||
? {
|
const newStatus = service.status === "1" ? "0" : "1"
|
||||||
...service,
|
const response = await updateService({
|
||||||
status: service.status === "active" ? ("inactive" as const) : ("active" as const),
|
id: service.id,
|
||||||
updatedAt: new Date().toISOString().split("T")[0],
|
serviceTitle: service.serviceTitle,
|
||||||
}
|
description: service.description,
|
||||||
: service,
|
status: newStatus,
|
||||||
)
|
assignedAdmin: service.assignedAdmin || "",
|
||||||
setServices(updatedServices)
|
assignedCommon: service.assignedCommon || "",
|
||||||
|
})
|
||||||
|
if (response.code === 200) {
|
||||||
|
loadServices()
|
||||||
|
} else {
|
||||||
|
alert(response.msg || "更新状态失败")
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("更新状态失败:", error)
|
||||||
|
alert("更新状态失败")
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
setFormData({
|
setFormData({
|
||||||
title: "",
|
serviceTitle: "",
|
||||||
description: "",
|
description: "",
|
||||||
status: "active",
|
status: "1",
|
||||||
assignedDealers: [],
|
assignedAdmin: "",
|
||||||
|
assignedCommon: "",
|
||||||
})
|
})
|
||||||
setSelectedService(null)
|
setSelectedService(null)
|
||||||
}
|
}
|
||||||
@@ -138,16 +182,37 @@ export default function ValueAddedServicesPage() {
|
|||||||
const openEditDialog = (service: ValueAddedService) => {
|
const openEditDialog = (service: ValueAddedService) => {
|
||||||
setSelectedService(service)
|
setSelectedService(service)
|
||||||
setFormData({
|
setFormData({
|
||||||
title: service.title,
|
serviceTitle: service.serviceTitle,
|
||||||
description: service.description,
|
description: service.description,
|
||||||
status: service.status,
|
status: service.status,
|
||||||
assignedDealers: service.assignedDealers,
|
assignedAdmin: service.assignedAdmin || "",
|
||||||
|
assignedCommon: service.assignedCommon || "",
|
||||||
})
|
})
|
||||||
setIsEditDialogOpen(true)
|
setIsEditDialogOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getDealerName = (dealerId: string) => {
|
const getUserName = (userId: string, userList: User[]) => {
|
||||||
return dealers.find((dealer) => dealer.id === dealerId)?.name || "未知经销商"
|
const user = userList.find((u) => u.userId === userId)
|
||||||
|
return user ? (user.nickName || user.userName) : "未知用户"
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUsersByIds = (ids: string) => {
|
||||||
|
if (!ids) return []
|
||||||
|
return ids.split(",").filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(total / pageSize)
|
||||||
|
|
||||||
|
const handlePrevPage = () => {
|
||||||
|
if (pageNum > 1) {
|
||||||
|
setPageNum(pageNum - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNextPage = () => {
|
||||||
|
if (pageNum < totalPages) {
|
||||||
|
setPageNum(pageNum + 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -175,8 +240,8 @@ export default function ValueAddedServicesPage() {
|
|||||||
<Label htmlFor="title">服务标题</Label>
|
<Label htmlFor="title">服务标题</Label>
|
||||||
<Input
|
<Input
|
||||||
id="title"
|
id="title"
|
||||||
value={formData.title}
|
value={formData.serviceTitle}
|
||||||
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, serviceTitle: e.target.value })}
|
||||||
placeholder="请输入服务标题"
|
placeholder="请输入服务标题"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -190,49 +255,96 @@ export default function ValueAddedServicesPage() {
|
|||||||
rows={3}
|
rows={3}
|
||||||
/>
|
/>
|
||||||
</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">
|
||||||
|
{adminUsers.map((user) => {
|
||||||
|
const assignedIds = formData.assignedAdmin.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={`admin-${userId}`}
|
||||||
|
checked={assignedIds.includes(userId)}
|
||||||
|
onChange={(e) => {
|
||||||
|
const ids = formData.assignedAdmin.split(",").filter(Boolean)
|
||||||
|
if (e.target.checked) {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
assignedAdmin: [...ids, userId].join(","),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
assignedAdmin: 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>
|
||||||
|
<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>
|
<div>
|
||||||
<Label>分配经销商</Label>
|
<Label>分配经销商</Label>
|
||||||
<div className="grid grid-cols-1 gap-2 mt-2">
|
<div className="grid grid-cols-1 gap-2 mt-2 max-h-48 overflow-y-auto border rounded p-2">
|
||||||
{dealers.map((dealer) => (
|
{commonUsers.map((user) => {
|
||||||
<div key={dealer.id} className="flex items-center space-x-2 p-2 border rounded">
|
const assignedIds = formData.assignedCommon.split(",").filter(Boolean)
|
||||||
<input
|
const userId = user.userId
|
||||||
type="checkbox"
|
return (
|
||||||
id={dealer.id}
|
<div key={userId} className="flex items-center space-x-2 p-2 border rounded">
|
||||||
checked={formData.assignedDealers.includes(dealer.id)}
|
<input
|
||||||
onChange={(e) => {
|
type="checkbox"
|
||||||
if (e.target.checked) {
|
id={`common-${userId}`}
|
||||||
setFormData({
|
checked={assignedIds.includes(userId)}
|
||||||
...formData,
|
onChange={(e) => {
|
||||||
assignedDealers: [...formData.assignedDealers, dealer.id],
|
const ids = formData.assignedCommon.split(",").filter(Boolean)
|
||||||
})
|
if (e.target.checked) {
|
||||||
} else {
|
setFormData({
|
||||||
setFormData({
|
...formData,
|
||||||
...formData,
|
assignedCommon: [...ids, userId].join(","),
|
||||||
assignedDealers: formData.assignedDealers.filter((id) => id !== dealer.id),
|
})
|
||||||
})
|
} else {
|
||||||
}
|
setFormData({
|
||||||
}}
|
...formData,
|
||||||
/>
|
assignedCommon: ids.filter((id) => id !== userId).join(","),
|
||||||
<label htmlFor={dealer.id} className="flex-1 cursor-pointer">
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor={`common-${userId}`} className="flex-1 cursor-pointer">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="font-medium">{dealer.name}</span>
|
<span className="font-medium">{user.nickName || user.userName}</span>
|
||||||
<span className="text-sm text-gray-500">{dealer.region}</span>
|
<span className="text-sm text-gray-500 flex items-center">
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500 flex items-center">
|
|
||||||
<Phone className="h-3 w-3 mr-1" />
|
<Phone className="h-3 w-3 mr-1" />
|
||||||
{dealer.phone}
|
{user.phonenumber}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button variant="outline" onClick={() => setIsCreateDialogOpen(false)}>
|
<Button variant="outline" onClick={() => setIsCreateDialogOpen(false)} disabled={loading}>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleCreateService}>创建服务</Button>
|
<Button onClick={handleCreateService} disabled={loading}>
|
||||||
|
{loading ? "创建中..." : "创建服务"}
|
||||||
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@@ -251,56 +363,125 @@ export default function ValueAddedServicesPage() {
|
|||||||
<TableHead>服务标题</TableHead>
|
<TableHead>服务标题</TableHead>
|
||||||
<TableHead>描述</TableHead>
|
<TableHead>描述</TableHead>
|
||||||
<TableHead>状态</TableHead>
|
<TableHead>状态</TableHead>
|
||||||
<TableHead>分配经销商</TableHead>
|
<TableHead>总公司</TableHead>
|
||||||
|
<TableHead>经销商</TableHead>
|
||||||
<TableHead>创建时间</TableHead>
|
<TableHead>创建时间</TableHead>
|
||||||
<TableHead>操作</TableHead>
|
<TableHead>操作</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{services.map((service) => (
|
{loading ? (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={7} className="text-center py-8">
|
||||||
|
加载中...
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
) : services.length === 0 ? (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={7} className="text-center py-8 text-gray-500">
|
||||||
|
暂无数据
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
) : (
|
||||||
|
services.map((service) => (
|
||||||
<TableRow key={service.id}>
|
<TableRow key={service.id}>
|
||||||
<TableCell className="font-medium">{service.title}</TableCell>
|
<TableCell className="font-medium">{service.serviceTitle}</TableCell>
|
||||||
<TableCell className="max-w-xs truncate">{service.description}</TableCell>
|
<TableCell className="max-w-xs truncate">{service.description}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Switch
|
<Switch
|
||||||
checked={service.status === "active"}
|
checked={service.status === "1"}
|
||||||
onCheckedChange={() => toggleServiceStatus(service.id)}
|
onCheckedChange={() => toggleServiceStatus(service)}
|
||||||
|
disabled={loading}
|
||||||
/>
|
/>
|
||||||
<Badge variant={service.status === "active" ? "default" : "secondary"}>
|
<Badge variant={service.status === "1" ? "default" : "secondary"}>
|
||||||
{service.status === "active" ? "上架" : "下架"}
|
{service.status === "1" ? "上架" : "下架"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{service.assignedDealers.slice(0, 2).map((dealerId) => (
|
{getUsersByIds(service.assignedAdmin || "").length > 0 ? (
|
||||||
<Badge key={dealerId} variant="outline" className="text-xs">
|
<>
|
||||||
{getDealerName(dealerId)}
|
{getUsersByIds(service.assignedAdmin).slice(0, 2).map((userId) => (
|
||||||
</Badge>
|
<Badge key={userId} variant="outline" className="text-xs">
|
||||||
))}
|
{getUserName(userId, adminUsers)}
|
||||||
{service.assignedDealers.length > 2 && (
|
</Badge>
|
||||||
<Badge variant="outline" className="text-xs">
|
))}
|
||||||
+{service.assignedDealers.length - 2}
|
{getUsersByIds(service.assignedAdmin).length > 2 && (
|
||||||
</Badge>
|
<Badge variant="outline" className="text-xs">
|
||||||
)}
|
+{getUsersByIds(service.assignedAdmin).length - 2}
|
||||||
</div>
|
</Badge>
|
||||||
</TableCell>
|
)}
|
||||||
<TableCell>{service.createdAt}</TableCell>
|
</>
|
||||||
|
) : (
|
||||||
|
<span className="text-xs text-gray-400">-</span>
|
||||||
|
)}
|
||||||
|
</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>
|
<TableCell>
|
||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2">
|
||||||
<Button variant="outline" size="sm" onClick={() => openEditDialog(service)}>
|
<Button variant="outline" size="sm" onClick={() => openEditDialog(service)} disabled={loading}>
|
||||||
<Edit className="h-4 w-4" />
|
<Edit className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="outline" size="sm" onClick={() => handleDeleteService(service.id)}>
|
<Button variant="outline" size="sm" onClick={() => handleDeleteService(service.id)} disabled={loading}>
|
||||||
<Trash2 className="h-4 w-4" />
|
<Trash2 className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))
|
||||||
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
|
{/* Pagination */}
|
||||||
|
<div className="flex items-center justify-between mt-4">
|
||||||
|
<div className="text-sm text-gray-600">
|
||||||
|
共 {total} 条记录,第 {pageNum} / {totalPages || 1} 页
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={handlePrevPage}
|
||||||
|
disabled={pageNum === 1 || loading}
|
||||||
|
>
|
||||||
|
<ChevronLeft className="h-4 w-4 mr-1" />
|
||||||
|
上一页
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleNextPage}
|
||||||
|
disabled={pageNum >= totalPages || loading}
|
||||||
|
>
|
||||||
|
下一页
|
||||||
|
<ChevronRight className="h-4 w-4 ml-1" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@@ -316,8 +497,8 @@ export default function ValueAddedServicesPage() {
|
|||||||
<Label htmlFor="edit-title">服务标题</Label>
|
<Label htmlFor="edit-title">服务标题</Label>
|
||||||
<Input
|
<Input
|
||||||
id="edit-title"
|
id="edit-title"
|
||||||
value={formData.title}
|
value={formData.serviceTitle}
|
||||||
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, serviceTitle: e.target.value })}
|
||||||
placeholder="请输入服务标题"
|
placeholder="请输入服务标题"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -331,49 +512,96 @@ export default function ValueAddedServicesPage() {
|
|||||||
rows={3}
|
rows={3}
|
||||||
/>
|
/>
|
||||||
</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">
|
||||||
|
{adminUsers.map((user) => {
|
||||||
|
const assignedIds = formData.assignedAdmin.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-admin-${userId}`}
|
||||||
|
checked={assignedIds.includes(userId)}
|
||||||
|
onChange={(e) => {
|
||||||
|
const ids = formData.assignedAdmin.split(",").filter(Boolean)
|
||||||
|
if (e.target.checked) {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
assignedAdmin: [...ids, userId].join(","),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
assignedAdmin: 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>
|
||||||
|
<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>
|
<div>
|
||||||
<Label>分配经销商</Label>
|
<Label>分配经销商</Label>
|
||||||
<div className="grid grid-cols-1 gap-2 mt-2">
|
<div className="grid grid-cols-1 gap-2 mt-2 max-h-48 overflow-y-auto border rounded p-2">
|
||||||
{dealers.map((dealer) => (
|
{commonUsers.map((user) => {
|
||||||
<div key={dealer.id} className="flex items-center space-x-2 p-2 border rounded">
|
const assignedIds = formData.assignedCommon.split(",").filter(Boolean)
|
||||||
<input
|
const userId = user.userId
|
||||||
type="checkbox"
|
return (
|
||||||
id={`edit-${dealer.id}`}
|
<div key={userId} className="flex items-center space-x-2 p-2 border rounded">
|
||||||
checked={formData.assignedDealers.includes(dealer.id)}
|
<input
|
||||||
onChange={(e) => {
|
type="checkbox"
|
||||||
if (e.target.checked) {
|
id={`edit-common-${userId}`}
|
||||||
setFormData({
|
checked={assignedIds.includes(userId)}
|
||||||
...formData,
|
onChange={(e) => {
|
||||||
assignedDealers: [...formData.assignedDealers, dealer.id],
|
const ids = formData.assignedCommon.split(",").filter(Boolean)
|
||||||
})
|
if (e.target.checked) {
|
||||||
} else {
|
setFormData({
|
||||||
setFormData({
|
...formData,
|
||||||
...formData,
|
assignedCommon: [...ids, userId].join(","),
|
||||||
assignedDealers: formData.assignedDealers.filter((id) => id !== dealer.id),
|
})
|
||||||
})
|
} else {
|
||||||
}
|
setFormData({
|
||||||
}}
|
...formData,
|
||||||
/>
|
assignedCommon: ids.filter((id) => id !== userId).join(","),
|
||||||
<label htmlFor={`edit-${dealer.id}`} className="flex-1 cursor-pointer">
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor={`edit-common-${userId}`} className="flex-1 cursor-pointer">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<span className="font-medium">{dealer.name}</span>
|
<span className="font-medium">{user.nickName || user.userName}</span>
|
||||||
<span className="text-sm text-gray-500">{dealer.region}</span>
|
<span className="text-sm text-gray-500 flex items-center">
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-500 flex items-center">
|
|
||||||
<Phone className="h-3 w-3 mr-1" />
|
<Phone className="h-3 w-3 mr-1" />
|
||||||
{dealer.phone}
|
{user.phonenumber}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button variant="outline" onClick={() => setIsEditDialogOpen(false)}>
|
<Button variant="outline" onClick={() => setIsEditDialogOpen(false)} disabled={loading}>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleEditService}>保存修改</Button>
|
<Button onClick={handleEditService} disabled={loading}>
|
||||||
|
{loading ? "保存中..." : "保存修改"}
|
||||||
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
49
src/lib/services/service.ts
Normal file
49
src/lib/services/service.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
ServiceListParams,
|
||||||
|
ServiceListResponse,
|
||||||
|
ServiceCreateParams,
|
||||||
|
ServiceUpdateParams,
|
||||||
|
ServiceResponse
|
||||||
|
} from '@/lib/types/service'
|
||||||
|
import { apiGet, apiPost, apiPut, apiDelete } from './api'
|
||||||
|
|
||||||
|
export async function getServiceList(params: ServiceListParams): Promise<ServiceListResponse> {
|
||||||
|
try {
|
||||||
|
const queryParams = new URLSearchParams({
|
||||||
|
pageSize: params.pageSize.toString(),
|
||||||
|
pageNum: params.pageNum.toString(),
|
||||||
|
})
|
||||||
|
return await apiGet<ServiceListResponse>(`/back/services/list?${queryParams}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch services:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createService(data: ServiceCreateParams): Promise<ServiceResponse> {
|
||||||
|
try {
|
||||||
|
return await apiPost<ServiceResponse>('/back/services/', data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to create service:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateService(data: ServiceUpdateParams): Promise<ServiceResponse> {
|
||||||
|
try {
|
||||||
|
return await apiPut<ServiceResponse>('/back/services/', data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update service:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteService(id: string): Promise<ServiceResponse> {
|
||||||
|
try {
|
||||||
|
return await apiDelete<ServiceResponse>(`/back/services/${id}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to delete service:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
12
src/lib/services/user.ts
Normal file
12
src/lib/services/user.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { UserByRoleResponse } from '@/lib/types/user'
|
||||||
|
import { apiGet } from './api'
|
||||||
|
|
||||||
|
export async function getUserByRoleKey(roleKey: string): Promise<UserByRoleResponse> {
|
||||||
|
try {
|
||||||
|
return await apiGet<UserByRoleResponse>(`/back/user/getUserByRoleKey?roleKey=${roleKey}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to fetch users with role ${roleKey}:`, error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
45
src/lib/types/service.ts
Normal file
45
src/lib/types/service.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
export interface ValueAddedService {
|
||||||
|
id: string
|
||||||
|
serviceTitle: string
|
||||||
|
description: string
|
||||||
|
status: string
|
||||||
|
assignedAdmin: string
|
||||||
|
assignedCommon: string
|
||||||
|
createTime?: string
|
||||||
|
updateTime?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceListParams {
|
||||||
|
pageSize: number
|
||||||
|
pageNum: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceListResponse {
|
||||||
|
code: number
|
||||||
|
msg: string
|
||||||
|
rows: ValueAddedService[]
|
||||||
|
total: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceCreateParams {
|
||||||
|
serviceTitle: string
|
||||||
|
description: string
|
||||||
|
status: string
|
||||||
|
assignedAdmin: string
|
||||||
|
assignedCommon: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceUpdateParams {
|
||||||
|
id: string
|
||||||
|
serviceTitle: string
|
||||||
|
description: string
|
||||||
|
status: string
|
||||||
|
assignedAdmin: string
|
||||||
|
assignedCommon: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceResponse {
|
||||||
|
code: number
|
||||||
|
msg: string
|
||||||
|
data?: any
|
||||||
|
}
|
||||||
44
src/lib/types/user.ts
Normal file
44
src/lib/types/user.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
export interface User {
|
||||||
|
createBy: string
|
||||||
|
createTime: string
|
||||||
|
updateBy: string | null
|
||||||
|
updateTime: string | null
|
||||||
|
remark: string | null
|
||||||
|
userId: string
|
||||||
|
deptId: string | null
|
||||||
|
userName: string
|
||||||
|
nickName: string
|
||||||
|
email: string
|
||||||
|
phonenumber: string
|
||||||
|
sex: string
|
||||||
|
avatar: string
|
||||||
|
password: string
|
||||||
|
status: string
|
||||||
|
delFlag: string
|
||||||
|
loginIp: string
|
||||||
|
loginDate: string
|
||||||
|
pwdUpdateDate: string | null
|
||||||
|
dept: any
|
||||||
|
roles: any[]
|
||||||
|
roleIds: any
|
||||||
|
postIds: any
|
||||||
|
roleId: string | null
|
||||||
|
roleName: string | null
|
||||||
|
provinceCode: string
|
||||||
|
parentId: string | null
|
||||||
|
type: string | null
|
||||||
|
mallId: string | null
|
||||||
|
businessLicense: string | null
|
||||||
|
businessType: string | null
|
||||||
|
address: string | null
|
||||||
|
provinceAddress: string | null
|
||||||
|
shopAddress: string | null
|
||||||
|
admin: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserByRoleResponse {
|
||||||
|
msg: string
|
||||||
|
code: number
|
||||||
|
data: User[]
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user