增加业务员页面
This commit is contained in:
@@ -12,6 +12,7 @@ import CompanyPermissionsPage from './pages/CompanyPermissionsPage'
|
|||||||
import ValueAddedServicesPage from './pages/ValueAddedServicesPage'
|
import ValueAddedServicesPage from './pages/ValueAddedServicesPage'
|
||||||
import WorkOrderArchivePage from './pages/WorkOrderArchivePage'
|
import WorkOrderArchivePage from './pages/WorkOrderArchivePage'
|
||||||
import SettingsPage from './pages/SettingsPage'
|
import SettingsPage from './pages/SettingsPage'
|
||||||
|
import SalePage from './pages/salePage'
|
||||||
|
|
||||||
const PAGE_COMPONENTS = {
|
const PAGE_COMPONENTS = {
|
||||||
'statistics': StatisticsPage,
|
'statistics': StatisticsPage,
|
||||||
@@ -26,6 +27,7 @@ const PAGE_COMPONENTS = {
|
|||||||
'value-added-services': ValueAddedServicesPage,
|
'value-added-services': ValueAddedServicesPage,
|
||||||
'workorder-archive': WorkOrderArchivePage,
|
'workorder-archive': WorkOrderArchivePage,
|
||||||
'settings': SettingsPage,
|
'settings': SettingsPage,
|
||||||
|
'sale': SalePage,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DynamicPage() {
|
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