经销商替换
This commit is contained in:
@@ -47,8 +47,8 @@ export default function AdminDashboard() {
|
||||
<h1 className="text-2xl font-bold text-gray-900">管理后台首页</h1>
|
||||
<p className="text-gray-600 mt-1">欢迎使用动态路由管理系统</p>
|
||||
</div>
|
||||
<Button
|
||||
onClick={refreshRoutes}
|
||||
<Button
|
||||
onClick={refreshRoutes}
|
||||
disabled={loading}
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
@@ -98,14 +98,14 @@ export default function AdminDashboard() {
|
||||
{error ? "连接失败" : "连接正常"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium">路由加载状态</span>
|
||||
<Badge variant={loading ? "secondary" : "default"}>
|
||||
{loading ? "加载中" : "加载完成"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium">菜单生成状态</span>
|
||||
<Badge variant={sidebarRoutes.length > 0 ? "default" : "secondary"}>
|
||||
@@ -131,8 +131,8 @@ export default function AdminDashboard() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start"
|
||||
onClick={refreshRoutes}
|
||||
disabled={loading}
|
||||
@@ -140,9 +140,9 @@ export default function AdminDashboard() {
|
||||
<RefreshCw className={`h-4 w-4 mr-2 ${loading ? 'animate-spin' : ''}`} />
|
||||
重新加载路由配置
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start"
|
||||
onClick={() => {
|
||||
console.log('Routes:', routes)
|
||||
@@ -152,9 +152,9 @@ export default function AdminDashboard() {
|
||||
<BarChart3 className="h-4 w-4 mr-2" />
|
||||
查看路由数据(控制台)
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start"
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
@@ -189,7 +189,7 @@ export default function AdminDashboard() {
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{routes.slice(0, 10).map((route, index) => (
|
||||
<div
|
||||
<div
|
||||
key={`${route.path}-${route.name}-${index}`}
|
||||
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg"
|
||||
>
|
||||
@@ -207,7 +207,7 @@ export default function AdminDashboard() {
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
|
||||
{routes.length > 10 && (
|
||||
<div className="text-center py-2 text-sm text-gray-500">
|
||||
还有 {routes.length - 10} 个路由配置...
|
||||
|
||||
@@ -93,13 +93,13 @@ export default function CompanyPermissionsPage() {
|
||||
// 根据角色ID获取用户列表
|
||||
const fetchUsersByRole = async (roleId: string, page: number = 1) => {
|
||||
if (!roleId) return
|
||||
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
const result: UserListResponse = await apiGet(
|
||||
`/system/user/listByRoleId?pageNum=${page}&pageSize=${pageSize}&roleId=${roleId}`
|
||||
)
|
||||
|
||||
|
||||
if (result.code === 200) {
|
||||
setUsers(result.rows || [])
|
||||
setTotal(parseInt(result.total) || 0)
|
||||
@@ -118,18 +118,18 @@ export default function CompanyPermissionsPage() {
|
||||
try {
|
||||
setLoading(true)
|
||||
const allUsers: UserData[] = []
|
||||
|
||||
const promises = roles.map(role =>
|
||||
|
||||
const promises = roles.map(role =>
|
||||
apiGet(`/system/user/listByRoleId?pageNum=1&pageSize=100&roleId=${role.roleId}`)
|
||||
.then((result: UserListResponse) => result.rows || [])
|
||||
.catch(() => [])
|
||||
)
|
||||
|
||||
|
||||
const results = await Promise.all(promises)
|
||||
results.forEach(roleUsers => {
|
||||
allUsers.push(...roleUsers)
|
||||
})
|
||||
|
||||
|
||||
setUsers(allUsers)
|
||||
setTotal(allUsers.length)
|
||||
} catch (error) {
|
||||
@@ -184,7 +184,7 @@ export default function CompanyPermissionsPage() {
|
||||
const getRoleInfo = (roleKey: string) => {
|
||||
const roleMap = {
|
||||
admin: { label: "总部管理员", icon: Shield, color: "bg-purple-100 text-purple-800" },
|
||||
common: { label: "经销商", icon: Building, color: "bg-blue-100 text-blue-800" },
|
||||
common: { label: "区域负责人", icon: Building, color: "bg-blue-100 text-blue-800" },
|
||||
mall: { label: "商场管理员", icon: Store, color: "bg-green-100 text-green-800" },
|
||||
merchant: { label: "商户", icon: User, color: "bg-yellow-100 text-yellow-800" },
|
||||
worker: { label: "维修工人", icon: Wrench, color: "bg-gray-100 text-gray-800" },
|
||||
@@ -210,16 +210,16 @@ export default function CompanyPermissionsPage() {
|
||||
user.nickName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
user.userName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(user.dept?.deptName || "").toLowerCase().includes(searchTerm.toLowerCase())
|
||||
|
||||
const matchesStatus = statusFilter === "all" ||
|
||||
|
||||
const matchesStatus = statusFilter === "all" ||
|
||||
(statusFilter === "active" && user.status === "0") ||
|
||||
(statusFilter === "inactive" && user.status === "1")
|
||||
|
||||
|
||||
return matchesSearch && matchesStatus
|
||||
})
|
||||
|
||||
const groupedUsers = roles.reduce((acc, role) => {
|
||||
acc[role.roleKey] = filteredUsers.filter(user =>
|
||||
acc[role.roleKey] = filteredUsers.filter(user =>
|
||||
user.roles.some(userRole => userRole.roleName === role.roleName)
|
||||
)
|
||||
return acc
|
||||
@@ -250,7 +250,7 @@ export default function CompanyPermissionsPage() {
|
||||
setIsSubmitting(true)
|
||||
try {
|
||||
const result = await apiDelete(`/system/user/${selectedUser.userId}`)
|
||||
|
||||
|
||||
if (result.code === 200) {
|
||||
alert('删除用户成功')
|
||||
setIsDeleteDialogOpen(false)
|
||||
@@ -309,11 +309,11 @@ export default function CompanyPermissionsPage() {
|
||||
const roleKey = roles.find(r => r.roleName === userRole?.roleName)?.roleKey || 'user'
|
||||
const roleInfo = getRoleInfo(roleKey)
|
||||
const RoleIcon = roleInfo.icon
|
||||
|
||||
|
||||
// 判断是否显示"总公司":总公司管理员(admin)或总商户
|
||||
const showHeadquarters = roleKey === 'admin' || (roleKey === 'merchant' && user.admin)
|
||||
const provinceDisplay = showHeadquarters ? '总公司' : (user.provinceName || '-')
|
||||
|
||||
|
||||
return (
|
||||
<TableRow key={`${user.userId}-${user.userName}-${index}`}>
|
||||
<TableCell>
|
||||
@@ -344,24 +344,24 @@ export default function CompanyPermissionsPage() {
|
||||
{isAdmin && (
|
||||
<TableCell>
|
||||
<div className="flex space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleViewUser(user)}
|
||||
title="查看"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleEditUser(user)}
|
||||
title="编辑"
|
||||
>
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleDeleteUser(user)}
|
||||
title="删除"
|
||||
@@ -414,7 +414,7 @@ export default function CompanyPermissionsPage() {
|
||||
const roleInfo = getRoleInfo(role.roleKey)
|
||||
const RoleIcon = roleInfo.icon
|
||||
const userCount = groupedUsers[role.roleKey]?.length || 0
|
||||
|
||||
|
||||
return (
|
||||
<Card key={role.roleId}>
|
||||
<CardContent className="p-4">
|
||||
@@ -510,7 +510,7 @@ export default function CompanyPermissionsPage() {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
{activeTab === "all" ? (
|
||||
<UserTable users={filteredUsers} />
|
||||
) : (
|
||||
@@ -569,8 +569,8 @@ export default function CompanyPermissionsPage() {
|
||||
<div>
|
||||
<Label className="text-gray-600">最后登录</Label>
|
||||
<div className="mt-1 text-sm">
|
||||
{selectedUser.loginDate
|
||||
? new Date(selectedUser.loginDate).toLocaleString('zh-CN')
|
||||
{selectedUser.loginDate
|
||||
? new Date(selectedUser.loginDate).toLocaleString('zh-CN')
|
||||
: '-'}
|
||||
</div>
|
||||
</div>
|
||||
@@ -593,9 +593,9 @@ export default function CompanyPermissionsPage() {
|
||||
<DialogDescription>修改用户信息</DialogDescription>
|
||||
</DialogHeader>
|
||||
{selectedUser && (
|
||||
<EditUserForm
|
||||
user={selectedUser}
|
||||
roles={roles}
|
||||
<EditUserForm
|
||||
user={selectedUser}
|
||||
roles={roles}
|
||||
onClose={() => {
|
||||
setIsEditDialogOpen(false)
|
||||
setSelectedUser(null)
|
||||
@@ -626,8 +626,8 @@ export default function CompanyPermissionsPage() {
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex justify-end space-x-2 mt-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setIsDeleteDialogOpen(false)
|
||||
setSelectedUser(null)
|
||||
@@ -636,8 +636,8 @@ export default function CompanyPermissionsPage() {
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleConfirmDelete}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
@@ -668,13 +668,13 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
|
||||
const fetchProvinces = async (roleKey: string) => {
|
||||
try {
|
||||
setProvincesLoading(true)
|
||||
|
||||
const endpoint = roleKey === "common"
|
||||
|
||||
const endpoint = roleKey === "common"
|
||||
? "/back/region/deProvinces"
|
||||
: "/back/region/provinces"
|
||||
|
||||
|
||||
const result = await apiGet(endpoint)
|
||||
|
||||
|
||||
if (result.code === 200) {
|
||||
setAvailableProvinces(result.data || [])
|
||||
}
|
||||
@@ -687,21 +687,21 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
|
||||
if (!formData.role) {
|
||||
alert('请选择角色')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
|
||||
|
||||
const selectedRole = roles.find(r => r.roleKey === formData.role)
|
||||
if (!selectedRole) {
|
||||
alert('选择的角色无效')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const selectedProvinceCodes = formData.provinces
|
||||
.map(provinceName => {
|
||||
const province = availableProvinces.find(p => p.name === provinceName)
|
||||
@@ -726,7 +726,7 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
|
||||
console.log('创建用户请求参数:', requestData)
|
||||
|
||||
const result = await apiPost('/system/user', requestData)
|
||||
|
||||
|
||||
if (result.code === 200) {
|
||||
alert('用户创建成功')
|
||||
onClose()
|
||||
@@ -818,7 +818,7 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
|
||||
<Label htmlFor="provinces">归属省份</Label>
|
||||
<div className="space-y-2">
|
||||
<div className="text-sm text-gray-500">
|
||||
{formData.role === "common" ? "经销商可选择多个省份" : "其他角色只能选择一个省份"}
|
||||
{formData.role === "common" ? "区域负责人可选择多个省份" : "其他角色只能选择一个省份"}
|
||||
</div>
|
||||
{provincesLoading ? (
|
||||
<div className="text-sm text-gray-500">正在加载省份数据...</div>
|
||||
@@ -877,8 +877,8 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
|
||||
<Button type="button" variant="outline" onClick={onClose} disabled={loading}>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={loading || !formData.username || !formData.name || !formData.password || !formData.role}
|
||||
>
|
||||
{loading ? '创建中...' : '创建用户'}
|
||||
@@ -888,12 +888,12 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
|
||||
)
|
||||
}
|
||||
|
||||
function EditUserForm({
|
||||
user,
|
||||
roles,
|
||||
onClose,
|
||||
onSuccess
|
||||
}: {
|
||||
function EditUserForm({
|
||||
user,
|
||||
roles,
|
||||
onClose,
|
||||
onSuccess
|
||||
}: {
|
||||
user: UserData
|
||||
roles: Role[]
|
||||
onClose: () => void
|
||||
@@ -917,13 +917,13 @@ function EditUserForm({
|
||||
const fetchProvinces = async (roleKey: string) => {
|
||||
try {
|
||||
setProvincesLoading(true)
|
||||
|
||||
const endpoint = roleKey === "common"
|
||||
|
||||
const endpoint = roleKey === "common"
|
||||
? "/back/region/deProvinces"
|
||||
: "/back/region/provinces"
|
||||
|
||||
|
||||
const result = await apiGet(endpoint)
|
||||
|
||||
|
||||
if (result.code === 200) {
|
||||
setAvailableProvinces(result.data || [])
|
||||
}
|
||||
@@ -960,21 +960,21 @@ function EditUserForm({
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
|
||||
if (!formData.role) {
|
||||
alert('请选择角色')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
|
||||
|
||||
const selectedRole = roles.find(r => r.roleKey === formData.role)
|
||||
if (!selectedRole) {
|
||||
alert('选择的角色无效')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const selectedProvinceCodes = formData.provinces
|
||||
.map(provinceName => {
|
||||
const province = availableProvinces.find(p => p.name === provinceName)
|
||||
@@ -1004,7 +1004,7 @@ function EditUserForm({
|
||||
console.log('更新用户请求参数:', requestData)
|
||||
|
||||
const result = await apiPut('/system/user', requestData)
|
||||
|
||||
|
||||
if (result.code === 200) {
|
||||
alert('用户更新成功')
|
||||
onClose()
|
||||
@@ -1113,7 +1113,7 @@ function EditUserForm({
|
||||
<Label htmlFor="edit-provinces">归属省份</Label>
|
||||
<div className="space-y-2">
|
||||
<div className="text-sm text-gray-500">
|
||||
{formData.role === "common" ? "经销商可选择多个省份" : "其他角色只能选择一个省份"}
|
||||
{formData.role === "common" ? "区域负责人可选择多个省份" : "其他角色只能选择一个省份"}
|
||||
</div>
|
||||
{provincesLoading ? (
|
||||
<div className="text-sm text-gray-500">正在加载省份数据...</div>
|
||||
@@ -1171,8 +1171,8 @@ function EditUserForm({
|
||||
<Button type="button" variant="outline" onClick={onClose} disabled={loading}>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={loading || !formData.username || !formData.name || !formData.role}
|
||||
>
|
||||
{loading ? '更新中...' : '更新用户'}
|
||||
|
||||
@@ -136,7 +136,7 @@ export default function EquipmentPage() {
|
||||
pageNum: pageNum.toString(),
|
||||
pageSize: pageSize.toString(),
|
||||
})
|
||||
|
||||
|
||||
if (equipmentId.trim()) {
|
||||
params.append('equipmentId', equipmentId.trim())
|
||||
}
|
||||
@@ -146,7 +146,7 @@ export default function EquipmentPage() {
|
||||
if (status && status !== "all") {
|
||||
params.append('status', status)
|
||||
}
|
||||
|
||||
|
||||
const response = await apiGet(`/back/equipment/list?${params.toString()}`)
|
||||
if (response.code === 200) {
|
||||
setEquipmentList(response.rows || [])
|
||||
@@ -265,25 +265,25 @@ export default function EquipmentPage() {
|
||||
const baseDate = newEquipment.installDate ? new Date(newEquipment.installDate) : new Date()
|
||||
baseDate.setFullYear(baseDate.getFullYear() + years)
|
||||
const newDateStr = baseDate.toISOString().split('T')[0]
|
||||
|
||||
|
||||
// 检查是否小于安装日期
|
||||
if (newEquipment.installDate && newDateStr < newEquipment.installDate) {
|
||||
alert('下一次检测时间不能小于安装日期')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
setNewEquipment({ ...newEquipment, nextInspectionDate: newDateStr })
|
||||
} else {
|
||||
const currentDate = new Date(newEquipment.nextInspectionDate)
|
||||
currentDate.setFullYear(currentDate.getFullYear() + years)
|
||||
const newDateStr = currentDate.toISOString().split('T')[0]
|
||||
|
||||
|
||||
// 检查是否小于安装日期
|
||||
if (newEquipment.installDate && newDateStr < newEquipment.installDate) {
|
||||
alert('下一次检测时间不能小于安装日期')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
setNewEquipment({ ...newEquipment, nextInspectionDate: newDateStr })
|
||||
}
|
||||
}
|
||||
@@ -295,25 +295,25 @@ export default function EquipmentPage() {
|
||||
const baseDate = editEquipment.installDate ? new Date(editEquipment.installDate) : new Date()
|
||||
baseDate.setFullYear(baseDate.getFullYear() + years)
|
||||
const newDateStr = baseDate.toISOString().split('T')[0]
|
||||
|
||||
|
||||
// 检查是否小于安装日期
|
||||
if (editEquipment.installDate && newDateStr < editEquipment.installDate) {
|
||||
alert('下一次检测时间不能小于安装日期')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
setEditEquipment({ ...editEquipment, nextInspectionDate: newDateStr })
|
||||
} else {
|
||||
const currentDate = new Date(editEquipment.nextInspectionDate)
|
||||
currentDate.setFullYear(currentDate.getFullYear() + years)
|
||||
const newDateStr = currentDate.toISOString().split('T')[0]
|
||||
|
||||
|
||||
// 检查是否小于安装日期
|
||||
if (editEquipment.installDate && newDateStr < editEquipment.installDate) {
|
||||
alert('下一次检测时间不能小于安装日期')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
setEditEquipment({ ...editEquipment, nextInspectionDate: newDateStr })
|
||||
}
|
||||
}
|
||||
@@ -635,7 +635,7 @@ export default function EquipmentPage() {
|
||||
onChange={(e) => {
|
||||
const installDate = e.target.value
|
||||
setNewEquipment({ ...newEquipment, installDate })
|
||||
|
||||
|
||||
// 自动填充下一次检测时间(安装日期+1年)
|
||||
if (installDate && !newEquipment.nextInspectionDate) {
|
||||
const nextDate = new Date(installDate)
|
||||
@@ -810,7 +810,7 @@ export default function EquipmentPage() {
|
||||
onChange={(e) => {
|
||||
const installDate = e.target.value
|
||||
setEditEquipment({ ...editEquipment, installDate })
|
||||
|
||||
|
||||
// 自动填充下一次检测时间(安装日期+1年)
|
||||
if (installDate && !editEquipment.nextInspectionDate) {
|
||||
const nextDate = new Date(installDate)
|
||||
@@ -1157,7 +1157,7 @@ export default function EquipmentPage() {
|
||||
<TableHead className="font-medium">商户信息</TableHead>
|
||||
<TableHead className="font-medium text-center">状态</TableHead>
|
||||
<TableHead className="font-medium">检测信息</TableHead>
|
||||
<TableHead className="font-medium">负责经销商</TableHead>
|
||||
<TableHead className="font-medium">负责区域负责人</TableHead>
|
||||
<TableHead className="font-medium text-center">操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
@@ -1233,16 +1233,16 @@ export default function EquipmentPage() {
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleViewEquipment(item)}
|
||||
className="text-xs sm:text-sm"
|
||||
>
|
||||
查看
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleEditEquipment(item)}
|
||||
className="text-xs sm:text-sm"
|
||||
@@ -1286,16 +1286,16 @@ export default function EquipmentPage() {
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1.5 flex-shrink-0">
|
||||
<Button
|
||||
variant="ghost"
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleViewEquipment(item)}
|
||||
className="h-8 px-3 text-xs whitespace-nowrap"
|
||||
>
|
||||
查看
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleEditEquipment(item)}
|
||||
className="h-8 px-3 text-xs whitespace-nowrap"
|
||||
@@ -1304,7 +1304,7 @@ export default function EquipmentPage() {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* 详细信息 */}
|
||||
<div className="border-t pt-3 space-y-3">
|
||||
{/* 商户信息 */}
|
||||
@@ -1321,7 +1321,7 @@ export default function EquipmentPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
{/* 检测信息 */}
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-xs font-medium text-gray-700">检测信息</div>
|
||||
@@ -1340,11 +1340,11 @@ export default function EquipmentPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 负责经销商 */}
|
||||
|
||||
{/* 负责区域负责人 */}
|
||||
{item.createdBy && (
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-xs font-medium text-gray-700">负责经销商</div>
|
||||
<div className="text-xs font-medium text-gray-700">负责区域负责人</div>
|
||||
<div className="font-medium text-sm text-gray-900 break-words">{item.createdBy}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function InventoryPage() {
|
||||
try {
|
||||
const result = await apiGet<any>('/back/category')
|
||||
console.log('分类数据:', result)
|
||||
|
||||
|
||||
if (result && result.code === 200 && result.data) {
|
||||
setCategories(result.data)
|
||||
} else {
|
||||
@@ -54,20 +54,20 @@ export default function InventoryPage() {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取经销商数据
|
||||
// 获取区域负责人数据
|
||||
const fetchDealers = async () => {
|
||||
try {
|
||||
const roleId = "2"
|
||||
const result = await apiGet<any>(`/back/findNextInfo?roleId=${roleId}`)
|
||||
console.log('经销商数据:', result)
|
||||
|
||||
console.log('区域负责人数据:', result)
|
||||
|
||||
if (result && result.code === 200 && result.data && Array.isArray(result.data)) {
|
||||
setDealers(result.data)
|
||||
} else {
|
||||
console.error('获取经销商失败:', result?.msg || '未知错误')
|
||||
console.error('获取区域负责人失败:', result?.msg || '未知错误')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求经销商失败:', error)
|
||||
console.error('请求区域负责人失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,9 +76,9 @@ export default function InventoryPage() {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
const result = await apiGet<any>(`/back/inventory/list?pageNum=${pageNum}&pageSize=${pageSizeParam}`)
|
||||
|
||||
|
||||
console.log('API 返回结果:', result)
|
||||
|
||||
|
||||
if (result && result.code === 200 && result.rows) {
|
||||
setInventory(result.rows)
|
||||
setTotal(result.total || 0)
|
||||
@@ -166,7 +166,7 @@ export default function InventoryPage() {
|
||||
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||
<div>
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900">库存管理</h1>
|
||||
<p className="text-sm sm:text-base text-gray-600">管理设备配件和耗材库存,总公司发货给经销商</p>
|
||||
<p className="text-sm sm:text-base text-gray-600">管理设备配件和耗材库存,总公司发货给区域负责人</p>
|
||||
</div>
|
||||
<div className="flex space-x-2 w-full sm:w-auto">
|
||||
<Dialog open={isAddDialogOpen} onOpenChange={setIsAddDialogOpen}>
|
||||
@@ -181,7 +181,7 @@ export default function InventoryPage() {
|
||||
<DialogTitle className="text-base sm:text-lg">添加库存物品</DialogTitle>
|
||||
<DialogDescription className="text-xs sm:text-sm">录入新的库存物品信息</DialogDescription>
|
||||
</DialogHeader>
|
||||
<AddInventoryForm
|
||||
<AddInventoryForm
|
||||
categories={categories}
|
||||
onClose={() => setIsAddDialogOpen(false)}
|
||||
onSuccess={() => {
|
||||
@@ -282,7 +282,7 @@ export default function InventoryPage() {
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg sm:text-xl">库存列表</CardTitle>
|
||||
<CardDescription className="text-xs sm:text-sm">查看和管理所有库存物品,支持向经销商发货</CardDescription>
|
||||
<CardDescription className="text-xs sm:text-sm">查看和管理所有库存物品,支持向区域负责人发货</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-6">
|
||||
@@ -441,7 +441,7 @@ export default function InventoryPage() {
|
||||
<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>
|
||||
<DialogDescription className="text-xs sm:text-sm">向区域负责人发货</DialogDescription>
|
||||
</DialogHeader>
|
||||
<OutboundForm
|
||||
item={item}
|
||||
@@ -567,7 +567,7 @@ export default function InventoryPage() {
|
||||
<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>
|
||||
<DialogDescription className="text-xs sm:text-sm">向区域负责人发货</DialogDescription>
|
||||
</DialogHeader>
|
||||
<OutboundForm
|
||||
item={item}
|
||||
@@ -700,7 +700,7 @@ function AddInventoryForm({ categories, onClose, onSuccess }: { categories: any;
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
setIsSubmitting(true)
|
||||
|
||||
|
||||
try {
|
||||
const requestData = {
|
||||
itemName: formData.name,
|
||||
@@ -868,7 +868,7 @@ function InboundForm({ item, onClose, onSuccess }: { item: any; onClose: () => v
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
setIsSubmitting(true)
|
||||
|
||||
|
||||
try {
|
||||
const inboundData = {
|
||||
id: item.id || "",
|
||||
@@ -977,7 +977,7 @@ function OutboundForm({ item, dealers, onRefreshDealers, onClose, onSuccess }: {
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
setIsSubmitting(true)
|
||||
|
||||
|
||||
try {
|
||||
const outboundData = {
|
||||
inventoryId: item.inventoryId,
|
||||
@@ -1036,15 +1036,15 @@ function OutboundForm({ item, dealers, onRefreshDealers, onClose, onSuccess }: {
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5 sm:space-y-2">
|
||||
<Label className="text-sm sm:text-base">收货经销商</Label>
|
||||
<Select
|
||||
value={formData.dealer}
|
||||
<Label className="text-sm sm:text-base">收货区域负责人</Label>
|
||||
<Select
|
||||
value={formData.dealer}
|
||||
onValueChange={(value) => {
|
||||
const selectedDealer = dealers.find(dealer =>
|
||||
const selectedDealer = dealers.find(dealer =>
|
||||
(dealer.userName || dealer.nickName) === value
|
||||
)
|
||||
setFormData({
|
||||
...formData,
|
||||
setFormData({
|
||||
...formData,
|
||||
dealer: value,
|
||||
dealerId: selectedDealer?.userId || ""
|
||||
})
|
||||
@@ -1056,7 +1056,7 @@ function OutboundForm({ item, dealers, onRefreshDealers, onClose, onSuccess }: {
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={dealers.length === 0 ? "加载经销商..." : "选择经销商"} />
|
||||
<SelectValue placeholder={dealers.length === 0 ? "加载区域负责人..." : "选择区域负责人"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{dealers.length === 0 ? (
|
||||
@@ -1065,11 +1065,11 @@ function OutboundForm({ item, dealers, onRefreshDealers, onClose, onSuccess }: {
|
||||
</SelectItem>
|
||||
) : (
|
||||
dealers.map((dealer, index) => (
|
||||
<SelectItem
|
||||
key={dealer.userId || `dealer-${index}`}
|
||||
<SelectItem
|
||||
key={dealer.userId || `dealer-${index}`}
|
||||
value={dealer.userName || dealer.nickName || `dealer-${index}`}
|
||||
>
|
||||
{dealer.userName || dealer.nickName || '未知经销商'}
|
||||
{dealer.userName || dealer.nickName || '未知区域负责人'}
|
||||
{dealer.nickName && dealer.userName !== dealer.nickName ? ` (${dealer.nickName})` : ''}
|
||||
</SelectItem>
|
||||
))
|
||||
@@ -1121,7 +1121,7 @@ function LogisticsTracking() {
|
||||
setIsLoading(true)
|
||||
const result = await apiGet<any>(`/back/logistics/list?pageNum=${pageNum}&pageSize=${pageSizeParam}`)
|
||||
console.log('物流数据:', result)
|
||||
|
||||
|
||||
if (result && result.code === 200 && result.rows) {
|
||||
setLogisticsData(result.rows)
|
||||
setTotal(result.total || 0)
|
||||
@@ -1437,4 +1437,4 @@ function LogisticsTracking() {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,13 +195,13 @@ export default function MallsPage() {
|
||||
// 获取商场下的商户列表
|
||||
const fetchMallMerchants = async (mallId: string) => {
|
||||
if (!mallId) return []
|
||||
|
||||
|
||||
setLoadingMallMerchants(prev => ({ ...prev, [mallId]: true }))
|
||||
try {
|
||||
const response = await apiGet(`/back/merchants/list?mallId=${mallId}`)
|
||||
if (response.code === 200) {
|
||||
const merchantsData = response.rows || []
|
||||
|
||||
|
||||
// 为每个商户获取设备数量
|
||||
const merchantsWithCounts = await Promise.all(
|
||||
merchantsData.map(async (merchant: any) => {
|
||||
@@ -215,7 +215,7 @@ export default function MallsPage() {
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
setMallMerchants(prev => ({ ...prev, [mallId]: merchantsWithCounts }))
|
||||
return merchantsWithCounts
|
||||
}
|
||||
@@ -384,13 +384,13 @@ export default function MallsPage() {
|
||||
|
||||
const toggleMallExpansion = (mall: Mall) => {
|
||||
const isExpanding = !expandedMalls.includes(mall.id)
|
||||
|
||||
setExpandedMalls((prev) =>
|
||||
prev.includes(mall.id)
|
||||
? prev.filter((id) => id !== mall.id)
|
||||
|
||||
setExpandedMalls((prev) =>
|
||||
prev.includes(mall.id)
|
||||
? prev.filter((id) => id !== mall.id)
|
||||
: [...prev, mall.id]
|
||||
)
|
||||
|
||||
|
||||
// 如果是展开操作,获取该商场的商户列表
|
||||
if (isExpanding && mall.mallId) {
|
||||
fetchMallMerchants(mall.mallId)
|
||||
@@ -416,7 +416,7 @@ export default function MallsPage() {
|
||||
const handleEditMall = (mall: Mall) => {
|
||||
// 解析 mallUser 字符串为数组(格式:userId1,userId2,)
|
||||
const mallUserIds = mall.mallUser ? mall.mallUser.split(',').filter(id => id.trim() !== '') : []
|
||||
|
||||
|
||||
setEditMall({
|
||||
id: mall.id,
|
||||
mallId: mall.mallId || "",
|
||||
@@ -427,12 +427,12 @@ export default function MallsPage() {
|
||||
description: mall.remark || "",
|
||||
status: mall.status,
|
||||
})
|
||||
|
||||
|
||||
// 如果省份已存在,获取对应的商场用户
|
||||
if (mall.province) {
|
||||
fetchMallUsersForEdit(mall.province)
|
||||
}
|
||||
|
||||
|
||||
setIsEditDialogOpen(true)
|
||||
}
|
||||
|
||||
@@ -864,7 +864,7 @@ export default function MallsPage() {
|
||||
<div className="relative w-full sm:w-auto">
|
||||
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="搜索商场名称、地址或经销商..."
|
||||
placeholder="搜索商场名称、地址或区域负责人..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-8 w-full sm:w-80"
|
||||
@@ -1001,9 +1001,9 @@ export default function MallsPage() {
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleViewEquipment(merchant)}
|
||||
>
|
||||
<Eye className="h-4 w-4 mr-1" />
|
||||
@@ -1032,9 +1032,9 @@ export default function MallsPage() {
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 mb-2">编号: {merchant.id}</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleViewEquipment(merchant)}
|
||||
className="text-xs flex-shrink-0"
|
||||
>
|
||||
@@ -1042,14 +1042,14 @@ export default function MallsPage() {
|
||||
{merchant.equipmentCount || 0}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="border-t pt-2 space-y-1.5 text-xs">
|
||||
<div>
|
||||
<div className="text-gray-500 mb-1">联系人</div>
|
||||
<div className="text-sm font-medium">{merchant.contactPerson || '无'}</div>
|
||||
<div className="text-gray-500">{merchant.contactPhone || '无'}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<div className="text-gray-500 mb-1">地址</div>
|
||||
<div className="flex items-start">
|
||||
@@ -1060,7 +1060,7 @@ export default function MallsPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<div className="text-gray-500 mb-1">设备数量</div>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -1202,7 +1202,7 @@ export default function MallsPage() {
|
||||
</div>
|
||||
<div className="flex-shrink-0">{getEquipmentStatusBadge(equipment.status)}</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* 详细信息 - 竖着显示 */}
|
||||
<div className="border-t pt-3 space-y-2.5">
|
||||
<div className="space-y-1">
|
||||
|
||||
@@ -57,7 +57,9 @@ interface TotalMerchant {
|
||||
}
|
||||
|
||||
export default function MerchantsPage() {
|
||||
const [searchTerm, setSearchTerm] = useState("")
|
||||
const [merchantNameSearch, setMerchantNameSearch] = useState("")
|
||||
const [contactPersonSearch, setContactPersonSearch] = useState("")
|
||||
const [contactPhoneSearch, setContactPhoneSearch] = useState("")
|
||||
const [statusFilter, setStatusFilter] = useState("all")
|
||||
const [isAddMerchantOpen, setIsAddMerchantOpen] = useState(false)
|
||||
const [isEditMerchantOpen, setIsEditMerchantOpen] = useState(false)
|
||||
@@ -241,10 +243,38 @@ export default function MerchantsPage() {
|
||||
}
|
||||
|
||||
// 获取商户列表
|
||||
const fetchMerchants = async (page = currentPage, size = pageSize) => {
|
||||
const fetchMerchants = async (
|
||||
page = currentPage,
|
||||
size = pageSize,
|
||||
merchantName = merchantNameSearch,
|
||||
contactPerson = contactPersonSearch,
|
||||
contactPhone = contactPhoneSearch,
|
||||
equipmentStatus = statusFilter
|
||||
) => {
|
||||
setLoadingMerchants(true)
|
||||
try {
|
||||
const response = await apiGet(`/back/merchants/list?pageNum=${page}&pageSize=${size}`)
|
||||
// 构建查询参数
|
||||
const params = new URLSearchParams({
|
||||
pageNum: page.toString(),
|
||||
pageSize: size.toString(),
|
||||
})
|
||||
|
||||
// 添加搜索参数(如果有值)
|
||||
if (merchantName.trim()) {
|
||||
params.append('merchantName', merchantName.trim())
|
||||
}
|
||||
if (contactPerson.trim()) {
|
||||
params.append('contactPerson', contactPerson.trim())
|
||||
}
|
||||
if (contactPhone.trim()) {
|
||||
params.append('contactPhone', contactPhone.trim())
|
||||
}
|
||||
// 添加状态筛选参数(如果不是全部)
|
||||
if (equipmentStatus && equipmentStatus !== "all") {
|
||||
params.append('equipmentStatus', equipmentStatus)
|
||||
}
|
||||
|
||||
const response = await apiGet(`/back/merchants/list?${params.toString()}`)
|
||||
if (response.code === 200) {
|
||||
const merchantsData = response.rows || []
|
||||
setTotal(parseInt(response.total) || 0)
|
||||
@@ -276,14 +306,14 @@ export default function MerchantsPage() {
|
||||
// 页码变化处理
|
||||
const handlePageChange = (page: number) => {
|
||||
setCurrentPage(page)
|
||||
fetchMerchants(page, pageSize)
|
||||
fetchMerchants(page, pageSize, merchantNameSearch, contactPersonSearch, contactPhoneSearch, statusFilter)
|
||||
}
|
||||
|
||||
// 每页大小变化处理
|
||||
const handlePageSizeChange = (size: number) => {
|
||||
setPageSize(size)
|
||||
setCurrentPage(1)
|
||||
fetchMerchants(1, size)
|
||||
fetchMerchants(1, size, merchantNameSearch, contactPersonSearch, contactPhoneSearch, statusFilter)
|
||||
}
|
||||
|
||||
// 组件加载时获取数据
|
||||
@@ -293,6 +323,18 @@ export default function MerchantsPage() {
|
||||
fetchUserEquipmentCount()
|
||||
}, [])
|
||||
|
||||
// 搜索条件变化时调用API(使用防抖)
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
// 重置到第一页
|
||||
setCurrentPage(1)
|
||||
fetchMerchants(1, pageSize, merchantNameSearch, contactPersonSearch, contactPhoneSearch, statusFilter)
|
||||
}, 500) // 500ms 防抖
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [merchantNameSearch, contactPersonSearch, contactPhoneSearch, statusFilter])
|
||||
|
||||
const [newEquipment, setNewEquipment] = useState({
|
||||
name: "",
|
||||
type: "",
|
||||
@@ -365,7 +407,7 @@ export default function MerchantsPage() {
|
||||
setIsAddMerchantOpen(false)
|
||||
|
||||
// 刷新商户列表
|
||||
fetchMerchants(currentPage, pageSize)
|
||||
fetchMerchants(currentPage, pageSize, merchantNameSearch, contactPersonSearch, contactPhoneSearch, statusFilter)
|
||||
} else {
|
||||
console.error('添加商户失败:', result)
|
||||
alert('添加商户失败:' + (result.msg || '未知错误'))
|
||||
@@ -504,7 +546,7 @@ export default function MerchantsPage() {
|
||||
console.log('编辑商户成功:', result)
|
||||
alert('编辑商户成功!')
|
||||
setIsEditMerchantOpen(false)
|
||||
fetchMerchants(currentPage, pageSize)
|
||||
fetchMerchants(currentPage, pageSize, merchantNameSearch, contactPersonSearch, contactPhoneSearch)
|
||||
} else {
|
||||
console.error('编辑商户失败:', result)
|
||||
alert('编辑商户失败:' + (result.msg || '未知错误'))
|
||||
@@ -525,23 +567,8 @@ export default function MerchantsPage() {
|
||||
fetchMerchantEquipments(merchantId)
|
||||
}
|
||||
|
||||
const filteredMerchants = merchants.filter((merchant) => {
|
||||
const matchesSearch =
|
||||
merchant.merchantName?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
merchant.contactPerson?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
merchant.mallLocation?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
|
||||
let matchesStatus = true
|
||||
if (statusFilter === "expired") {
|
||||
matchesStatus = merchant.expiredCount > 0
|
||||
} else if (statusFilter === "expiring") {
|
||||
matchesStatus = merchant.expiringCount > 0
|
||||
} else if (statusFilter === "normal") {
|
||||
matchesStatus = merchant.expiredCount === 0 && merchant.expiringCount === 0
|
||||
}
|
||||
|
||||
return matchesSearch && matchesStatus
|
||||
})
|
||||
// 直接使用从后端返回的商户列表(搜索和状态筛选已由后端处理)
|
||||
const filteredMerchants = merchants
|
||||
|
||||
return (
|
||||
<div className="space-y-4 sm:space-y-6 p-4 sm:p-6">
|
||||
@@ -565,44 +592,64 @@ export default function MerchantsPage() {
|
||||
<CardDescription>查看和管理所有商户信息及设备状态 - 共 {total} 条记录,当前第 {currentPage} 页</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-6">
|
||||
<div className="flex-1">
|
||||
<div className="flex flex-col gap-4 mb-6">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
<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)}
|
||||
placeholder="搜索商户名称..."
|
||||
value={merchantNameSearch}
|
||||
onChange={(e) => setMerchantNameSearch(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
<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={contactPersonSearch}
|
||||
onChange={(e) => setContactPersonSearch(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
<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={contactPhoneSearch}
|
||||
onChange={(e) => setContactPhoneSearch(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="w-full sm:w-48">
|
||||
<Filter className="h-4 w-4 mr-2" />
|
||||
<SelectValue placeholder="筛选状态" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">
|
||||
全部状态 {loadingUserEquipmentCount ? '' : `(${userEquipmentCount.totalCount})`}
|
||||
</SelectItem>
|
||||
<SelectItem value="normal">
|
||||
设备正常 {loadingUserEquipmentCount ? '' : `(${userEquipmentCount.count})`}
|
||||
</SelectItem>
|
||||
<SelectItem value="expiring">
|
||||
有设备即将到期 {loadingUserEquipmentCount ? '' : `(${userEquipmentCount.countEnd})`}
|
||||
</SelectItem>
|
||||
<SelectItem value="expired">
|
||||
有设备过期 {loadingUserEquipmentCount ? '' : `(${userEquipmentCount.countExpire})`}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="w-full sm:w-48">
|
||||
<Filter className="h-4 w-4 mr-2" />
|
||||
<SelectValue placeholder="筛选状态" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">
|
||||
全部状态 {loadingUserEquipmentCount ? '' : `(${userEquipmentCount.totalCount})`}
|
||||
</SelectItem>
|
||||
<SelectItem value="normal">
|
||||
设备正常 {loadingUserEquipmentCount ? '' : `(${userEquipmentCount.count})`}
|
||||
</SelectItem>
|
||||
<SelectItem value="expiring">
|
||||
有设备即将到期 {loadingUserEquipmentCount ? '' : `(${userEquipmentCount.countEnd})`}
|
||||
</SelectItem>
|
||||
<SelectItem value="expired">
|
||||
有设备过期 {loadingUserEquipmentCount ? '' : `(${userEquipmentCount.countExpire})`}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Button variant="outline">
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
导出
|
||||
</Button>
|
||||
<Button variant="outline">
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
导出
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Desktop Table View */}
|
||||
@@ -614,7 +661,7 @@ export default function MerchantsPage() {
|
||||
<TableHead>商户名称</TableHead>
|
||||
<TableHead>联系人</TableHead>
|
||||
<TableHead>所属商场</TableHead>
|
||||
<TableHead>经销商</TableHead>
|
||||
<TableHead>区域负责人</TableHead>
|
||||
<TableHead>地址</TableHead>
|
||||
<TableHead>设备数量</TableHead>
|
||||
<TableHead>状态</TableHead>
|
||||
@@ -730,14 +777,14 @@ export default function MerchantsPage() {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="border-t pt-3 space-y-2">
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">联系人</div>
|
||||
<div className="text-sm font-medium">{merchant.contactPerson}</div>
|
||||
<div className="text-xs text-gray-500">{merchant.contactPhone}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">所属商场</div>
|
||||
<div className="flex items-center text-sm">
|
||||
@@ -745,12 +792,12 @@ export default function MerchantsPage() {
|
||||
{merchant.mallLocation || "无"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">经销商</div>
|
||||
<div className="text-xs text-gray-500 mb-1">区域负责人</div>
|
||||
<div className="text-sm">{merchant.dealerName || "无"}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">地址</div>
|
||||
<div className="flex items-start text-xs">
|
||||
@@ -761,7 +808,7 @@ export default function MerchantsPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">设备数量</div>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -1357,7 +1404,7 @@ export default function MerchantsPage() {
|
||||
</div>
|
||||
<div className="flex-shrink-0">{getStatusBadge(equipment.status)}</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* 详细信息 - 竖着显示 */}
|
||||
<div className="border-t pt-3 space-y-2.5">
|
||||
<div className="space-y-1">
|
||||
|
||||
@@ -39,7 +39,7 @@ interface StatisticsData {
|
||||
totalMerchants: number
|
||||
}
|
||||
|
||||
// 定义经销商分布数据接口
|
||||
// 定义区域负责人分布数据接口
|
||||
interface DealerDistribution {
|
||||
equipmentCount: number
|
||||
dealerName: string
|
||||
@@ -110,7 +110,7 @@ export default function StatisticsPage() {
|
||||
setDealerDistributionData(response.data.distributionList)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取经销商分布数据失败:', error)
|
||||
console.error('获取区域负责人分布数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,9 +379,9 @@ export default function StatisticsPage() {
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center space-x-2">
|
||||
<MapPin className="h-5 w-5" />
|
||||
<span>全国经销商分布</span>
|
||||
<span>全国区域负责人分布</span>
|
||||
</CardTitle>
|
||||
<CardDescription>各省份经销商及业务数据概览</CardDescription>
|
||||
<CardDescription>各省份区域负责人及业务数据概览</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
|
||||
@@ -313,14 +313,14 @@ export default function WorkOrdersPage() {
|
||||
<DialogDescription className="text-xs sm:text-sm">修改工单信息</DialogDescription>
|
||||
</DialogHeader>
|
||||
{editingWorkOrder && (
|
||||
<EditWorkOrderForm
|
||||
<EditWorkOrderForm
|
||||
workOrder={editingWorkOrder}
|
||||
onClose={() => {
|
||||
setIsEditDialogOpen(false)
|
||||
setEditingWorkOrder(null)
|
||||
fetchWorkOrders(currentPage, pageSize)
|
||||
fetchStatusCounts()
|
||||
}}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</DialogContent>
|
||||
@@ -527,8 +527,8 @@ export default function WorkOrdersPage() {
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button
|
||||
variant="ghost"
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setEditingWorkOrder(item)
|
||||
@@ -565,8 +565,8 @@ export default function WorkOrdersPage() {
|
||||
<div className="text-sm font-medium text-gray-900">{item.equipmentName}</div>
|
||||
<div className="text-xs text-gray-500">设备: {item.equipmentId}</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setEditingWorkOrder(item)
|
||||
@@ -576,7 +576,7 @@ export default function WorkOrdersPage() {
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="border-t pt-3 space-y-2">
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">商户信息</div>
|
||||
@@ -587,18 +587,18 @@ export default function WorkOrdersPage() {
|
||||
{item.merchantAddress}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">负责人</div>
|
||||
<div className="font-medium text-sm">{item.workerName}</div>
|
||||
<div className="text-xs text-gray-500">{item.workerVirtualPhone}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">业务员</div>
|
||||
<div className="text-sm text-gray-600">{item.saleName || "-"}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="grid grid-cols-2 gap-2 text-xs">
|
||||
<div>
|
||||
<div className="text-gray-500 mb-1">创建时间</div>
|
||||
@@ -861,7 +861,7 @@ function CreateWorkOrderForm({ onClose }: { onClose: () => void }) {
|
||||
const workerNames = selectedWorkers.map(w => w.name).join(",")
|
||||
// 拼接工人电话(使用逗号分隔)
|
||||
const workerPhones = selectedWorkers.map(w => w.phone).join(",")
|
||||
// 使用第一个工人的经销商ID
|
||||
// 使用第一个工人的区域负责人ID
|
||||
const distributorUserId = selectedWorkers[0].distributorUserId || ""
|
||||
|
||||
// 生成工单编号
|
||||
@@ -1100,8 +1100,8 @@ function EditWorkOrderForm({ workOrder, onClose }: { workOrder: WorkOrder; onClo
|
||||
const merchantList = response.data || []
|
||||
setMerchants(merchantList)
|
||||
// 根据商户名称或ID查找对应的商户
|
||||
const foundMerchant = merchantList.find((m: ProvinceMerchant) =>
|
||||
m.merchantName === workOrder.merchantName ||
|
||||
const foundMerchant = merchantList.find((m: ProvinceMerchant) =>
|
||||
m.merchantName === workOrder.merchantName ||
|
||||
m.merchantsId === workOrder.merchantUserId ||
|
||||
m.id === workOrder.merchantUserId
|
||||
)
|
||||
@@ -1153,7 +1153,7 @@ function EditWorkOrderForm({ workOrder, onClose }: { workOrder: WorkOrder; onClo
|
||||
const equipmentList = response.rows || []
|
||||
setAvailableEquipment(equipmentList)
|
||||
// 查找当前工单对应的设备
|
||||
const foundEquipment = equipmentList.find((e: Equipment) =>
|
||||
const foundEquipment = equipmentList.find((e: Equipment) =>
|
||||
e.equipmentId === workOrder.equipmentId
|
||||
)
|
||||
if (foundEquipment) {
|
||||
@@ -1259,7 +1259,7 @@ function EditWorkOrderForm({ workOrder, onClose }: { workOrder: WorkOrder; onClo
|
||||
const workerNames = selectedWorkers.map(w => w.name).join(",")
|
||||
// 拼接工人电话(使用逗号分隔)
|
||||
const workerPhones = selectedWorkers.map(w => w.phone).join(",")
|
||||
// 使用第一个工人的经销商ID
|
||||
// 使用第一个工人的区域负责人ID
|
||||
const distributorUserId = selectedWorkers[0].distributorUserId || ""
|
||||
|
||||
// 构建API请求数据
|
||||
|
||||
@@ -115,7 +115,7 @@ export default function WorkersPage() {
|
||||
const fetchCompletedOrdersCount = async (workersId: string) => {
|
||||
try {
|
||||
const result = await apiGet<any>(`/back/orders/list?pageNum=1&pageSize=999&workersId=${workersId}&status=7`)
|
||||
|
||||
|
||||
if (result && result.code === 200) {
|
||||
const count = parseInt(result.total) || 0
|
||||
console.log(`工人 ${workersId} 完成工单数量:`, count)
|
||||
@@ -143,14 +143,14 @@ export default function WorkersPage() {
|
||||
const workersIdForQuery = worker.workersId || worker.jobNum || worker.id
|
||||
console.log(`正在获取工人 ${worker.name}(${workersIdForQuery}) 的完成工单数量`)
|
||||
const completedCount = await fetchCompletedOrdersCount(workersIdForQuery)
|
||||
|
||||
|
||||
return {
|
||||
id: worker.id,
|
||||
name: worker.name,
|
||||
workerId: worker.jobNum || worker.workersId || `W${worker.id}`,
|
||||
phone: worker.phone,
|
||||
dealerId: worker.distributorUserId,
|
||||
dealerName: worker.distributorUser || '未知经销商',
|
||||
dealerName: worker.distributorUser || '未知区域负责人',
|
||||
province: worker.province,
|
||||
skillLevel: mapAPISkillLevelToDisplay(worker.skillLevel),
|
||||
joinDate: worker.createdAt,
|
||||
@@ -217,7 +217,7 @@ export default function WorkersPage() {
|
||||
loadProvinces()
|
||||
}, [])
|
||||
|
||||
// 加载经销商数据
|
||||
// 加载区域负责人数据
|
||||
useEffect(() => {
|
||||
const loadDealers = async () => {
|
||||
try {
|
||||
@@ -225,7 +225,7 @@ export default function WorkersPage() {
|
||||
if (response.code === 200) {
|
||||
setDealers(response.data)
|
||||
} else {
|
||||
console.error('获取经销商数据失败:', response.msg)
|
||||
console.error('获取区域负责人数据失败:', response.msg)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error loading dealers:', err)
|
||||
@@ -614,13 +614,13 @@ export default function WorkersPage() {
|
||||
</Select>
|
||||
</div>
|
||||
<div className="grid gap-1.5 sm:gap-2">
|
||||
<Label htmlFor="dealer" className="text-sm sm:text-base">所属经销商</Label>
|
||||
<Label htmlFor="dealer" className="text-sm sm:text-base">所属区域负责人</Label>
|
||||
<Select
|
||||
value={newWorker.dealerId}
|
||||
onValueChange={(value) => setNewWorker({ ...newWorker, dealerId: value })}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择经销商" />
|
||||
<SelectValue placeholder="选择区域负责人" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="max-h-[300px]">
|
||||
{dealers.map((dealer) => (
|
||||
@@ -701,13 +701,13 @@ export default function WorkersPage() {
|
||||
</Select>
|
||||
</div>
|
||||
<div className="grid gap-1.5 sm:gap-2">
|
||||
<Label htmlFor="edit-dealer" className="text-sm sm:text-base">所属经销商</Label>
|
||||
<Label htmlFor="edit-dealer" className="text-sm sm:text-base">所属区域负责人</Label>
|
||||
<Select
|
||||
value={editWorker.dealerId}
|
||||
onValueChange={(value) => setEditWorker({ ...editWorker, dealerId: value })}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择经销商" />
|
||||
<SelectValue placeholder="选择区域负责人" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="max-h-[300px]">
|
||||
{dealers.map((dealer) => (
|
||||
@@ -771,7 +771,7 @@ export default function WorkersPage() {
|
||||
<span className="text-sm font-medium">{deletingWorker.phone}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-sm text-gray-600">经销商:</span>
|
||||
<span className="text-sm text-gray-600">区域负责人:</span>
|
||||
<span className="text-sm font-medium">{deletingWorker.dealerName}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -811,7 +811,7 @@ export default function WorkersPage() {
|
||||
<div className="relative flex-1 sm:flex-initial">
|
||||
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="搜索工人姓名、工号或经销商..."
|
||||
placeholder="搜索工人姓名、工号或区域负责人..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-8 w-full sm:w-80"
|
||||
|
||||
Reference in New Issue
Block a user