增加业务员页面
This commit is contained in:
@@ -12,6 +12,7 @@ import CompanyPermissionsPage from './pages/CompanyPermissionsPage'
|
||||
import ValueAddedServicesPage from './pages/ValueAddedServicesPage'
|
||||
import WorkOrderArchivePage from './pages/WorkOrderArchivePage'
|
||||
import SettingsPage from './pages/SettingsPage'
|
||||
import SalePage from './pages/salePage'
|
||||
|
||||
const PAGE_COMPONENTS = {
|
||||
'statistics': StatisticsPage,
|
||||
@@ -26,6 +27,7 @@ const PAGE_COMPONENTS = {
|
||||
'value-added-services': ValueAddedServicesPage,
|
||||
'workorder-archive': WorkOrderArchivePage,
|
||||
'settings': SettingsPage,
|
||||
'sale': SalePage,
|
||||
}
|
||||
|
||||
export default function DynamicPage() {
|
||||
|
||||
447
src/components/pages/salePage.tsx
Normal file
447
src/components/pages/salePage.tsx
Normal file
@@ -0,0 +1,447 @@
|
||||
import { useState, useEffect } from "react"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||
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 } from "lucide-react"
|
||||
import { apiGet, apiPost } from "../../services/api"
|
||||
import { getUserData } from "../../utils/storage"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../ui/dialog"
|
||||
import { Label } from "../ui/label"
|
||||
|
||||
// 业务员数据类型
|
||||
interface SaleUser {
|
||||
userId: string
|
||||
userName: string
|
||||
nickName: string
|
||||
phonenumber: string
|
||||
createTime: string
|
||||
email: string
|
||||
status: string
|
||||
}
|
||||
|
||||
interface ApiResponse {
|
||||
total: string
|
||||
rows: SaleUser[]
|
||||
code: number
|
||||
msg: string
|
||||
}
|
||||
|
||||
export default function SalePage() {
|
||||
const [searchTerm, setSearchTerm] = useState("")
|
||||
const [salesUsers, setSalesUsers] = useState<SaleUser[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [total, setTotal] = useState(0)
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(10)
|
||||
|
||||
// 新增业务员对话框状态
|
||||
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false)
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const [formData, setFormData] = useState({
|
||||
userName: "",
|
||||
nickName: "",
|
||||
phonenumber: "",
|
||||
email: "",
|
||||
password: "",
|
||||
})
|
||||
|
||||
// 获取业务员列表
|
||||
const fetchSalesUsers = async (page = currentPage, size = pageSize) => {
|
||||
setLoading(true)
|
||||
try {
|
||||
// 获取当前登录用户信息
|
||||
const userData = getUserData()
|
||||
const parentId = userData?.userId || userData?.user?.userId || "1"
|
||||
|
||||
console.log("当前用户ID:", parentId)
|
||||
|
||||
// 调用接口获取业务员列表
|
||||
const response: ApiResponse = await apiGet(
|
||||
`/back/sale/list?type=sale&parentId=${parentId}&pageNum=${page}&pageSize=${size}`
|
||||
)
|
||||
|
||||
console.log("业务员列表响应:", response)
|
||||
|
||||
if (response.code === 200) {
|
||||
setSalesUsers(response.rows || [])
|
||||
setTotal(parseInt(response.total) || 0)
|
||||
} else {
|
||||
console.error("获取业务员列表失败:", response.msg)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取业务员列表请求失败:", error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 页码变化处理
|
||||
const handlePageChange = (page: number) => {
|
||||
setCurrentPage(page)
|
||||
fetchSalesUsers(page, pageSize)
|
||||
}
|
||||
|
||||
// 每页大小变化处理
|
||||
const handlePageSizeChange = (size: number) => {
|
||||
setPageSize(size)
|
||||
setCurrentPage(1)
|
||||
fetchSalesUsers(1, size)
|
||||
}
|
||||
|
||||
// 组件加载时获取数据
|
||||
useEffect(() => {
|
||||
fetchSalesUsers()
|
||||
}, [])
|
||||
|
||||
// 过滤业务员列表
|
||||
const filteredSalesUsers = salesUsers.filter((user) => {
|
||||
const matchesSearch =
|
||||
user.userName?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
user.nickName?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
user.phonenumber?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
return matchesSearch
|
||||
})
|
||||
|
||||
// 导出数据
|
||||
const handleExport = () => {
|
||||
console.log("导出业务员列表数据")
|
||||
// TODO: 实现导出功能
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
setFormData({
|
||||
userName: "",
|
||||
nickName: "",
|
||||
phonenumber: "",
|
||||
email: "",
|
||||
password: "",
|
||||
})
|
||||
}
|
||||
|
||||
// 打开新增对话框
|
||||
const handleOpenAddDialog = () => {
|
||||
resetForm()
|
||||
setIsAddDialogOpen(true)
|
||||
}
|
||||
|
||||
// 关闭新增对话框
|
||||
const handleCloseAddDialog = () => {
|
||||
setIsAddDialogOpen(false)
|
||||
resetForm()
|
||||
}
|
||||
|
||||
// 表单字段变化处理
|
||||
const handleFormChange = (field: string, value: string) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}))
|
||||
}
|
||||
|
||||
// 提交新增业务员
|
||||
const handleSubmitAdd = async () => {
|
||||
// 表单验证
|
||||
if (!formData.userName.trim()) {
|
||||
alert("请输入用户名")
|
||||
return
|
||||
}
|
||||
if (!formData.nickName.trim()) {
|
||||
alert("请输入昵称")
|
||||
return
|
||||
}
|
||||
if (!formData.phonenumber.trim()) {
|
||||
alert("请输入手机号")
|
||||
return
|
||||
}
|
||||
if (!formData.email.trim()) {
|
||||
alert("请输入邮箱")
|
||||
return
|
||||
}
|
||||
if (!formData.password.trim()) {
|
||||
alert("请输入初始密码")
|
||||
return
|
||||
}
|
||||
|
||||
setIsSubmitting(true)
|
||||
try {
|
||||
// 获取当前登录用户信息
|
||||
const userData = getUserData()
|
||||
const parentId = userData?.userId || userData?.user?.userId || "1"
|
||||
|
||||
// 构建请求数据
|
||||
const requestData = {
|
||||
userName: formData.userName,
|
||||
nickName: formData.nickName,
|
||||
phonenumber: formData.phonenumber,
|
||||
email: formData.email,
|
||||
password: formData.password,
|
||||
parentId: parentId,
|
||||
type: "sale",
|
||||
}
|
||||
|
||||
console.log("新增业务员请求数据:", requestData)
|
||||
|
||||
// 发送请求
|
||||
const response: any = await apiPost("/back/sale/add", requestData)
|
||||
|
||||
console.log("新增业务员响应:", response)
|
||||
|
||||
if (response.code === 200) {
|
||||
alert("新增业务员成功")
|
||||
handleCloseAddDialog()
|
||||
// 刷新列表
|
||||
fetchSalesUsers(1, pageSize)
|
||||
setCurrentPage(1)
|
||||
} else {
|
||||
alert(`新增业务员失败: ${response.msg || "未知错误"}`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("新增业务员请求失败:", error)
|
||||
alert("新增业务员失败,请重试")
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Page Header */}
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">业务员管理</h1>
|
||||
<p className="text-gray-600">管理和查看业务员信息</p>
|
||||
</div>
|
||||
<Button onClick={handleOpenAddDialog}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
新增业务员
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Main Content Card */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>业务员列表</CardTitle>
|
||||
<CardDescription>
|
||||
查看所有业务员的基本信息 - 共 {total} 条记录,当前第 {currentPage} 页
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{/* Search Bar */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-6">
|
||||
<div className="flex-1">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
||||
<Input
|
||||
placeholder="搜索用户名、昵称或手机号..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button variant="outline" onClick={handleExport}>
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
导出
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>用户ID</TableHead>
|
||||
<TableHead>用户名</TableHead>
|
||||
<TableHead>用户昵称</TableHead>
|
||||
<TableHead>手机号</TableHead>
|
||||
<TableHead>创建时间</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{loading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={5} className="text-center py-8">
|
||||
加载中...
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : filteredSalesUsers.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={5} className="text-center py-8">
|
||||
暂无数据
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
filteredSalesUsers.map((user) => (
|
||||
<TableRow key={user.userId}>
|
||||
<TableCell className="font-medium">{user.userId}</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="h-8 w-8 bg-blue-100 rounded-full flex items-center justify-center">
|
||||
<User className="h-4 w-4 text-blue-600" />
|
||||
</div>
|
||||
<span>{user.userName}</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>{user.nickName}</TableCell>
|
||||
<TableCell>{user.phonenumber}</TableCell>
|
||||
<TableCell>
|
||||
<div className="text-sm">{user.createTime}</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
<div className="flex items-center justify-between space-x-2 py-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<p className="text-sm font-medium">每页显示</p>
|
||||
<Select
|
||||
value={pageSize.toString()}
|
||||
onValueChange={(value) => handlePageSizeChange(parseInt(value))}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-[70px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent side="top">
|
||||
<SelectItem value="5">5</SelectItem>
|
||||
<SelectItem value="10">10</SelectItem>
|
||||
<SelectItem value="20">20</SelectItem>
|
||||
<SelectItem value="50">50</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-sm text-muted-foreground">共 {total} 条记录</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<p className="text-sm font-medium">
|
||||
第 {currentPage} 页,共 {Math.ceil(total / pageSize)} 页
|
||||
</p>
|
||||
<div className="flex items-center space-x-1">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handlePageChange(currentPage - 1)}
|
||||
disabled={currentPage <= 1 || loading}
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
<span className="sr-only">上一页</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handlePageChange(currentPage + 1)}
|
||||
disabled={currentPage >= Math.ceil(total / pageSize) || loading}
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
<span className="sr-only">下一页</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 新增业务员对话框 */}
|
||||
<Dialog open={isAddDialogOpen} onOpenChange={setIsAddDialogOpen}>
|
||||
<DialogContent className="sm:max-w-[500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>新增业务员</DialogTitle>
|
||||
<DialogDescription>请填写业务员信息,所有字段都为必填项。</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="grid gap-4 py-4">
|
||||
{/* 用户名 */}
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="userName">用户名</Label>
|
||||
<Input
|
||||
id="userName"
|
||||
placeholder="请输入用户名"
|
||||
value={formData.userName}
|
||||
onChange={(e) => handleFormChange("userName", e.target.value)}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 昵称 */}
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="nickName">昵称</Label>
|
||||
<Input
|
||||
id="nickName"
|
||||
placeholder="请输入昵称"
|
||||
value={formData.nickName}
|
||||
onChange={(e) => handleFormChange("nickName", e.target.value)}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 手机号 */}
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="phonenumber">手机号</Label>
|
||||
<Input
|
||||
id="phonenumber"
|
||||
placeholder="请输入手机号"
|
||||
value={formData.phonenumber}
|
||||
onChange={(e) => handleFormChange("phonenumber", e.target.value)}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 邮箱 */}
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="email">邮箱</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="请输入邮箱"
|
||||
value={formData.email}
|
||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 初始密码 */}
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="password">初始密码</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="请输入初始密码"
|
||||
value={formData.password}
|
||||
onChange={(e) => handleFormChange("password", e.target.value)}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleCloseAddDialog}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleSubmitAdd} disabled={isSubmitting}>
|
||||
{isSubmitting ? "提交中..." : "确认新增"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user