youhua
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react"
|
import React, { useState, useEffect, useRef } 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"
|
||||||
@@ -74,6 +74,22 @@ export default function CompanyPermissionsPage() {
|
|||||||
const [selectedUser, setSelectedUser] = useState<UserData | null>(null)
|
const [selectedUser, setSelectedUser] = useState<UserData | null>(null)
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
const [currentUserRole, setCurrentUserRole] = useState<string>("")
|
const [currentUserRole, setCurrentUserRole] = useState<string>("")
|
||||||
|
const [userRoleFromApi, setUserRoleFromApi] = useState<string>("")
|
||||||
|
const hasInitializedCommonRole = useRef(false)
|
||||||
|
const lastFetchedRoleFilter = useRef<string>("")
|
||||||
|
const lastFetchedPage = useRef<number>(0)
|
||||||
|
|
||||||
|
// 获取当前用户角色
|
||||||
|
const fetchUserRole = async () => {
|
||||||
|
try {
|
||||||
|
const result = await apiGet('/back/getUserRole')
|
||||||
|
if (result.code === 200 && result.data) {
|
||||||
|
setUserRoleFromApi(result.data)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取用户角色失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取角色列表
|
// 获取角色列表
|
||||||
const fetchRoles = async () => {
|
const fetchRoles = async () => {
|
||||||
@@ -157,12 +173,41 @@ export default function CompanyPermissionsPage() {
|
|||||||
|
|
||||||
// 初始化数据
|
// 初始化数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
fetchUserRole()
|
||||||
fetchRoles()
|
fetchRoles()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// 初始化 common 角色的过滤器设置(只执行一次)
|
||||||
|
useEffect(() => {
|
||||||
|
if (userRoleFromApi === "common" && roles.length > 0 && !hasInitializedCommonRole.current) {
|
||||||
|
hasInitializedCommonRole.current = true
|
||||||
|
setRoleFilter("mall")
|
||||||
|
setActiveTab("mall")
|
||||||
|
}
|
||||||
|
}, [userRoleFromApi, roles])
|
||||||
|
|
||||||
// 当角色列表加载完成后,获取用户数据
|
// 当角色列表加载完成后,获取用户数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (roles.length > 0) {
|
if (roles.length > 0) {
|
||||||
|
// 如果用户角色是 common,强制显示商场管理员
|
||||||
|
if (userRoleFromApi === "common") {
|
||||||
|
const mallRole = roles.find(r => r.roleKey === "mall")
|
||||||
|
if (mallRole) {
|
||||||
|
// 避免重复请求:检查是否与上次请求的参数相同
|
||||||
|
const currentFilter = "mall"
|
||||||
|
if (lastFetchedRoleFilter.current !== currentFilter || lastFetchedPage.current !== currentPage) {
|
||||||
|
lastFetchedRoleFilter.current = currentFilter
|
||||||
|
lastFetchedPage.current = currentPage
|
||||||
|
fetchUsersByRole(mallRole.roleId, currentPage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (userRoleFromApi !== "") {
|
||||||
|
// 正常情况:根据 roleFilter 获取用户数据
|
||||||
|
// 只有当 userRoleFromApi 已经获取到值时才执行
|
||||||
|
// 避免重复请求:检查是否与上次请求的参数相同
|
||||||
|
if (lastFetchedRoleFilter.current !== roleFilter || lastFetchedPage.current !== currentPage) {
|
||||||
|
lastFetchedRoleFilter.current = roleFilter
|
||||||
|
lastFetchedPage.current = currentPage
|
||||||
if (roleFilter === "all") {
|
if (roleFilter === "all") {
|
||||||
fetchAllUsers()
|
fetchAllUsers()
|
||||||
} else {
|
} else {
|
||||||
@@ -172,7 +217,9 @@ export default function CompanyPermissionsPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [roles, roleFilter, currentPage])
|
}
|
||||||
|
}
|
||||||
|
}, [roles, roleFilter, currentPage, userRoleFromApi])
|
||||||
|
|
||||||
// 当选择的角色改变时,重置页码并获取数据
|
// 当选择的角色改变时,重置页码并获取数据
|
||||||
const handleRoleFilterChange = (value: string) => {
|
const handleRoleFilterChange = (value: string) => {
|
||||||
@@ -389,7 +436,7 @@ export default function CompanyPermissionsPage() {
|
|||||||
<h1 className="text-3xl font-bold text-gray-900">公司权限管理</h1>
|
<h1 className="text-3xl font-bold text-gray-900">公司权限管理</h1>
|
||||||
<p className="text-gray-600">管理公司内部用户账户和权限分配</p>
|
<p className="text-gray-600">管理公司内部用户账户和权限分配</p>
|
||||||
</div>
|
</div>
|
||||||
{isAdmin && (
|
{(isAdmin || userRoleFromApi === "common") && (
|
||||||
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
|
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button>
|
<Button>
|
||||||
@@ -402,7 +449,11 @@ export default function CompanyPermissionsPage() {
|
|||||||
<DialogTitle>创建新用户</DialogTitle>
|
<DialogTitle>创建新用户</DialogTitle>
|
||||||
<DialogDescription>填写用户信息并分配相应的角色权限</DialogDescription>
|
<DialogDescription>填写用户信息并分配相应的角色权限</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<CreateUserForm roles={roles} onClose={() => setIsCreateDialogOpen(false)} />
|
<CreateUserForm
|
||||||
|
roles={roles}
|
||||||
|
userRoleFromApi={userRoleFromApi}
|
||||||
|
onClose={() => setIsCreateDialogOpen(false)}
|
||||||
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
@@ -410,7 +461,15 @@ export default function CompanyPermissionsPage() {
|
|||||||
|
|
||||||
{/* Stats Cards */}
|
{/* Stats Cards */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
|
||||||
{roles.map((role) => {
|
{roles
|
||||||
|
.filter((role) => {
|
||||||
|
// 如果用户角色是 common,只显示商场管理员(mall)
|
||||||
|
if (userRoleFromApi === "common") {
|
||||||
|
return role.roleKey === "mall"
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.map((role) => {
|
||||||
const roleInfo = getRoleInfo(role.roleKey)
|
const roleInfo = getRoleInfo(role.roleKey)
|
||||||
const RoleIcon = roleInfo.icon
|
const RoleIcon = roleInfo.icon
|
||||||
const userCount = groupedUsers[role.roleKey]?.length || 0
|
const userCount = groupedUsers[role.roleKey]?.length || 0
|
||||||
@@ -456,8 +515,18 @@ export default function CompanyPermissionsPage() {
|
|||||||
<SelectValue placeholder="角色" />
|
<SelectValue placeholder="角色" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
{userRoleFromApi !== "common" && (
|
||||||
<SelectItem value="all">全部角色</SelectItem>
|
<SelectItem value="all">全部角色</SelectItem>
|
||||||
{roles.map((role) => (
|
)}
|
||||||
|
{roles
|
||||||
|
.filter((role) => {
|
||||||
|
// 如果用户角色是 common,只显示商场管理员(mall)
|
||||||
|
if (userRoleFromApi === "common") {
|
||||||
|
return role.roleKey === "mall"
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.map((role) => (
|
||||||
<SelectItem key={role.roleId} value={role.roleKey}>
|
<SelectItem key={role.roleId} value={role.roleKey}>
|
||||||
{role.roleName}
|
{role.roleName}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
@@ -486,6 +555,7 @@ export default function CompanyPermissionsPage() {
|
|||||||
{/* Tabs */}
|
{/* Tabs */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex space-x-1 overflow-x-auto">
|
<div className="flex space-x-1 overflow-x-auto">
|
||||||
|
{userRoleFromApi !== "common" && (
|
||||||
<button
|
<button
|
||||||
className={`px-4 py-2 text-sm font-medium transition-colors whitespace-nowrap ${
|
className={`px-4 py-2 text-sm font-medium transition-colors whitespace-nowrap ${
|
||||||
activeTab === "all"
|
activeTab === "all"
|
||||||
@@ -496,7 +566,16 @@ export default function CompanyPermissionsPage() {
|
|||||||
>
|
>
|
||||||
全部
|
全部
|
||||||
</button>
|
</button>
|
||||||
{roles.map((role) => (
|
)}
|
||||||
|
{roles
|
||||||
|
.filter((role) => {
|
||||||
|
// 如果用户角色是 common,只显示商场管理员(mall)
|
||||||
|
if (userRoleFromApi === "common") {
|
||||||
|
return role.roleKey === "mall"
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.map((role) => (
|
||||||
<button
|
<button
|
||||||
key={role.roleId}
|
key={role.roleId}
|
||||||
className={`px-4 py-2 text-sm font-medium transition-colors whitespace-nowrap ${
|
className={`px-4 py-2 text-sm font-medium transition-colors whitespace-nowrap ${
|
||||||
@@ -650,7 +729,15 @@ export default function CompanyPermissionsPage() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void }) {
|
function CreateUserForm({
|
||||||
|
roles,
|
||||||
|
userRoleFromApi,
|
||||||
|
onClose
|
||||||
|
}: {
|
||||||
|
roles: Role[];
|
||||||
|
userRoleFromApi: string;
|
||||||
|
onClose: () => void
|
||||||
|
}) {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
username: "",
|
username: "",
|
||||||
name: "",
|
name: "",
|
||||||
@@ -664,6 +751,15 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
|
|||||||
const [availableProvinces, setAvailableProvinces] = useState<any[]>([])
|
const [availableProvinces, setAvailableProvinces] = useState<any[]>([])
|
||||||
const [provincesLoading, setProvincesLoading] = useState(false)
|
const [provincesLoading, setProvincesLoading] = useState(false)
|
||||||
|
|
||||||
|
// 如果当前用户是 common 角色,默认选中商场管理员并加载省份数据
|
||||||
|
useEffect(() => {
|
||||||
|
if (userRoleFromApi === "common") {
|
||||||
|
setFormData(prev => ({ ...prev, role: "mall" }))
|
||||||
|
fetchProvinces("mall")
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [userRoleFromApi])
|
||||||
|
|
||||||
// 根据角色获取省份数据
|
// 根据角色获取省份数据
|
||||||
const fetchProvinces = async (roleKey: string) => {
|
const fetchProvinces = async (roleKey: string) => {
|
||||||
try {
|
try {
|
||||||
@@ -685,9 +781,37 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 用户名校验函数
|
||||||
|
const validateUsername = (username: string): string | null => {
|
||||||
|
if (!username || username.trim() === '') {
|
||||||
|
return '用户名不能为空'
|
||||||
|
}
|
||||||
|
// 检查是否包含空格
|
||||||
|
if (username.includes(' ')) {
|
||||||
|
return '用户名不能包含空格'
|
||||||
|
}
|
||||||
|
// 检查是否包含其他非法字符(只允许字母、数字、下划线、中划线)
|
||||||
|
const validPattern = /^[a-zA-Z0-9_-]+$/
|
||||||
|
if (!validPattern.test(username)) {
|
||||||
|
return '用户名只能包含字母、数字、下划线和中划线'
|
||||||
|
}
|
||||||
|
// 检查长度(根据实际需求调整)
|
||||||
|
if (username.length < 3 || username.length > 20) {
|
||||||
|
return '用户名长度应在3-20个字符之间'
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
|
// 校验用户名
|
||||||
|
const usernameError = validateUsername(formData.username)
|
||||||
|
if (usernameError) {
|
||||||
|
alert(usernameError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!formData.role) {
|
if (!formData.role) {
|
||||||
alert('请选择角色')
|
alert('请选择角色')
|
||||||
return
|
return
|
||||||
@@ -751,6 +875,14 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理用户名输入,过滤非法字符
|
||||||
|
const handleUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
let value = e.target.value
|
||||||
|
// 移除空格和其他非法字符(只保留字母、数字、下划线、中划线)
|
||||||
|
value = value.replace(/[^a-zA-Z0-9_-]/g, '')
|
||||||
|
setFormData({ ...formData, username: value })
|
||||||
|
}
|
||||||
|
|
||||||
const shouldShowProvinces = formData.role && formData.role !== "admin" && formData.role !== "merchant"
|
const shouldShowProvinces = formData.role && formData.role !== "admin" && formData.role !== "merchant"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -761,10 +893,14 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
|
|||||||
<Input
|
<Input
|
||||||
id="username"
|
id="username"
|
||||||
value={formData.username}
|
value={formData.username}
|
||||||
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
|
onChange={handleUsernameChange}
|
||||||
placeholder="请输入用户名"
|
placeholder="请输入用户名(3-20个字符,仅支持字母、数字、下划线、中划线)"
|
||||||
required
|
required
|
||||||
|
maxLength={20}
|
||||||
/>
|
/>
|
||||||
|
{formData.username && formData.username.length > 0 && formData.username.length < 3 && (
|
||||||
|
<p className="text-sm text-red-500">用户名长度至少3个字符</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -799,18 +935,30 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
|
|||||||
fetchProvinces(value)
|
fetchProvinces(value)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
disabled={userRoleFromApi === "common"}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="选择角色" />
|
<SelectValue placeholder="选择角色" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{roles.map((role) => (
|
{roles
|
||||||
|
.filter((role) => {
|
||||||
|
// 如果当前用户是 common 角色,只显示商场管理员
|
||||||
|
if (userRoleFromApi === "common") {
|
||||||
|
return role.roleKey === "mall"
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.map((role) => (
|
||||||
<SelectItem key={role.roleId} value={role.roleKey}>
|
<SelectItem key={role.roleId} value={role.roleKey}>
|
||||||
{role.roleName}
|
{role.roleName}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
{userRoleFromApi === "common" && (
|
||||||
|
<p className="text-sm text-gray-500">您只能添加商场管理员</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{shouldShowProvinces && (
|
{shouldShowProvinces && (
|
||||||
@@ -1039,9 +1187,9 @@ function EditUserForm({
|
|||||||
<Input
|
<Input
|
||||||
id="edit-username"
|
id="edit-username"
|
||||||
value={formData.username}
|
value={formData.username}
|
||||||
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
|
disabled
|
||||||
placeholder="请输入用户名"
|
placeholder="用户名不可修改"
|
||||||
required
|
className="bg-gray-100 cursor-not-allowed"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -165,43 +165,14 @@ export default function EquipmentPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取单个商户的设备数量
|
// 获取商户列表(用于添加/编辑设备选择,不获取设备数量)
|
||||||
const fetchMerchantEquipmentCount = async (merchantId: string) => {
|
const fetchMerchantsForSelect = async () => {
|
||||||
try {
|
|
||||||
const response = await apiGet(`/back/equipment/merchant/${merchantId}/count`)
|
|
||||||
if (response.code === 200) {
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`获取商户${merchantId}设备数量失败:`, error)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取商户列表
|
|
||||||
const fetchMerchants = async () => {
|
|
||||||
setLoadingMerchants(true)
|
setLoadingMerchants(true)
|
||||||
try {
|
try {
|
||||||
const response = await apiGet('/back/general/provinceMerchants')
|
const response = await apiGet('/back/general/provinceMerchants')
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
const merchantsData = response.data || []
|
const merchantsData = response.data || []
|
||||||
|
setMerchants(merchantsData)
|
||||||
// 为每个商户获取设备数量
|
|
||||||
const merchantsWithCounts = await Promise.all(
|
|
||||||
merchantsData.map(async (merchant: any) => {
|
|
||||||
const equipmentCount = await fetchMerchantEquipmentCount(merchant.id)
|
|
||||||
return {
|
|
||||||
...merchant,
|
|
||||||
equipmentCount: equipmentCount?.totalCount || 0,
|
|
||||||
normalCount: equipmentCount?.count || 0,
|
|
||||||
expiringCount: equipmentCount?.countEnd || 0,
|
|
||||||
expiredCount: equipmentCount?.countExpire || 0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
setMerchants(merchantsWithCounts)
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取商户列表失败:', error)
|
console.error('获取商户列表失败:', error)
|
||||||
@@ -361,7 +332,7 @@ export default function EquipmentPage() {
|
|||||||
setIsEditEquipmentOpen(true)
|
setIsEditEquipmentOpen(true)
|
||||||
// 加载商户和设备类型数据
|
// 加载商户和设备类型数据
|
||||||
if (merchants.length === 0) {
|
if (merchants.length === 0) {
|
||||||
fetchMerchants()
|
fetchMerchantsForSelect()
|
||||||
}
|
}
|
||||||
if (Object.keys(equipmentTypes).length === 0) {
|
if (Object.keys(equipmentTypes).length === 0) {
|
||||||
fetchEquipmentTypes()
|
fetchEquipmentTypes()
|
||||||
@@ -613,7 +584,7 @@ export default function EquipmentPage() {
|
|||||||
disabled={loadingMerchants}
|
disabled={loadingMerchants}
|
||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
if (open && merchants.length === 0 && !loadingMerchants) {
|
if (open && merchants.length === 0 && !loadingMerchants) {
|
||||||
fetchMerchants()
|
fetchMerchantsForSelect()
|
||||||
}
|
}
|
||||||
if (!open) {
|
if (!open) {
|
||||||
setMerchantSearchForAdd("")
|
setMerchantSearchForAdd("")
|
||||||
@@ -835,7 +806,7 @@ export default function EquipmentPage() {
|
|||||||
disabled={loadingMerchants}
|
disabled={loadingMerchants}
|
||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
if (open && merchants.length === 0 && !loadingMerchants) {
|
if (open && merchants.length === 0 && !loadingMerchants) {
|
||||||
fetchMerchants()
|
fetchMerchantsForSelect()
|
||||||
}
|
}
|
||||||
if (!open) {
|
if (!open) {
|
||||||
setMerchantSearchForEdit("")
|
setMerchantSearchForEdit("")
|
||||||
|
|||||||
@@ -166,15 +166,25 @@ export default function MerchantsPage() {
|
|||||||
|
|
||||||
// 获取商场列表
|
// 获取商场列表
|
||||||
const fetchMalls = async (province?: string) => {
|
const fetchMalls = async (province?: string) => {
|
||||||
|
if (!province) {
|
||||||
|
setMalls([])
|
||||||
|
return
|
||||||
|
}
|
||||||
setLoadingMalls(true)
|
setLoadingMalls(true)
|
||||||
try {
|
try {
|
||||||
const url = `/back/info/list?province=${province}&pageNum=1&pageSize=100`
|
const url = `/back/info/list?province=${province}&pageNum=1&pageSize=100`
|
||||||
const response = await apiGet(url)
|
const response = await apiGet(url)
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
setMalls(response.rows || [])
|
const mallsData = response.rows || []
|
||||||
|
console.log('获取商场列表成功:', mallsData)
|
||||||
|
setMalls(mallsData)
|
||||||
|
} else {
|
||||||
|
console.error('获取商场列表失败:', response.msg)
|
||||||
|
setMalls([])
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取商场列表失败:', error)
|
console.error('获取商场列表失败:', error)
|
||||||
|
setMalls([])
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingMalls(false)
|
setLoadingMalls(false)
|
||||||
}
|
}
|
||||||
@@ -343,6 +353,47 @@ export default function MerchantsPage() {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [merchantNameSearch, contactPersonSearch, contactPhoneSearch, statusFilter])
|
}, [merchantNameSearch, contactPersonSearch, contactPhoneSearch, statusFilter])
|
||||||
|
|
||||||
|
// 当添加商户对话框打开且已选择省份时,自动加载商场列表
|
||||||
|
useEffect(() => {
|
||||||
|
if (isAddMerchantOpen && newMerchant.province && malls.length === 0 && !loadingMalls) {
|
||||||
|
fetchMalls(newMerchant.province)
|
||||||
|
}
|
||||||
|
}, [isAddMerchantOpen, newMerchant.province])
|
||||||
|
|
||||||
|
// 当编辑商户对话框打开且已选择省份时,自动加载商场列表
|
||||||
|
useEffect(() => {
|
||||||
|
if (isEditMerchantOpen && editMerchant.province && !loadingMalls) {
|
||||||
|
// 如果商场列表为空,或者当前商场不在列表中,重新加载
|
||||||
|
if (malls.length === 0 || (editMerchant.mall && !malls.find(m => m.mallId === editMerchant.mall))) {
|
||||||
|
fetchMalls(editMerchant.province)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isEditMerchantOpen, editMerchant.province, editMerchant.mall])
|
||||||
|
|
||||||
|
// 当商场列表加载完成且编辑对话框打开时,根据 mallId 设置 mallUserId
|
||||||
|
useEffect(() => {
|
||||||
|
if (isEditMerchantOpen && editMerchant.mall && malls.length > 0 && !loadingMalls) {
|
||||||
|
// 如果 mallUserId 为空,尝试根据 mallId 找到对应的 mallUser
|
||||||
|
if (!editMerchant.mallUserId) {
|
||||||
|
const foundMall = malls.find(m =>
|
||||||
|
m.mallId === editMerchant.mall ||
|
||||||
|
m.id === editMerchant.mall ||
|
||||||
|
(m.mallId && editMerchant.mall && m.mallId.toString() === editMerchant.mall.toString())
|
||||||
|
)
|
||||||
|
if (foundMall && foundMall.mallUser) {
|
||||||
|
console.log('找到匹配的商场:', foundMall)
|
||||||
|
setEditMerchant(prev => ({
|
||||||
|
...prev,
|
||||||
|
mallUserId: foundMall.mallUser
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
console.log('未找到匹配的商场,mallId:', editMerchant.mall, '商场列表:', malls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [isEditMerchantOpen, editMerchant.mall, malls.length, loadingMalls])
|
||||||
|
|
||||||
const [newEquipment, setNewEquipment] = useState({
|
const [newEquipment, setNewEquipment] = useState({
|
||||||
name: "",
|
name: "",
|
||||||
type: "",
|
type: "",
|
||||||
@@ -459,6 +510,15 @@ export default function MerchantsPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将设备类型代码转换为中文
|
||||||
|
const getEquipmentTypeName = (type: string) => {
|
||||||
|
const typeMap: { [key: string]: string } = {
|
||||||
|
"kitchen_automatic_fire_extinguisher": "厨房自动灭火",
|
||||||
|
"fire_extinguisher": "动火离人"
|
||||||
|
}
|
||||||
|
return typeMap[type] || type || '-'
|
||||||
|
}
|
||||||
|
|
||||||
// 打开编辑对话框
|
// 打开编辑对话框
|
||||||
const handleEditMerchant = (merchant: any) => {
|
const handleEditMerchant = (merchant: any) => {
|
||||||
setEditMerchant({
|
setEditMerchant({
|
||||||
@@ -629,6 +689,7 @@ export default function MerchantsPage() {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
||||||
<Input
|
<Input
|
||||||
|
id="search-merchant-name"
|
||||||
placeholder="搜索商户名称..."
|
placeholder="搜索商户名称..."
|
||||||
value={merchantNameSearch}
|
value={merchantNameSearch}
|
||||||
onChange={(e) => setMerchantNameSearch(e.target.value)}
|
onChange={(e) => setMerchantNameSearch(e.target.value)}
|
||||||
@@ -638,6 +699,7 @@ export default function MerchantsPage() {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
||||||
<Input
|
<Input
|
||||||
|
id="search-contact-person"
|
||||||
placeholder="搜索联系人..."
|
placeholder="搜索联系人..."
|
||||||
value={contactPersonSearch}
|
value={contactPersonSearch}
|
||||||
onChange={(e) => setContactPersonSearch(e.target.value)}
|
onChange={(e) => setContactPersonSearch(e.target.value)}
|
||||||
@@ -647,6 +709,7 @@ export default function MerchantsPage() {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
||||||
<Input
|
<Input
|
||||||
|
id="search-contact-phone"
|
||||||
placeholder="搜索联系电话..."
|
placeholder="搜索联系电话..."
|
||||||
value={contactPhoneSearch}
|
value={contactPhoneSearch}
|
||||||
onChange={(e) => setContactPhoneSearch(e.target.value)}
|
onChange={(e) => setContactPhoneSearch(e.target.value)}
|
||||||
@@ -958,7 +1021,7 @@ export default function MerchantsPage() {
|
|||||||
<div className="space-y-1.5 sm:space-y-2">
|
<div className="space-y-1.5 sm:space-y-2">
|
||||||
<Label htmlFor="mall" className="text-sm sm:text-base">所属商场(可选)</Label>
|
<Label htmlFor="mall" className="text-sm sm:text-base">所属商场(可选)</Label>
|
||||||
<Select
|
<Select
|
||||||
value={newMerchant.mall}
|
value={newMerchant.mallUserId}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
const selectedMall = malls.find(mall => mall.mallUser === value)
|
const selectedMall = malls.find(mall => mall.mallUser === value)
|
||||||
setNewMerchant({
|
setNewMerchant({
|
||||||
@@ -979,36 +1042,48 @@ export default function MerchantsPage() {
|
|||||||
} />
|
} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="max-h-[300px]">
|
<SelectContent className="max-h-[300px]">
|
||||||
{malls.map((mall) => (
|
{loadingMalls ? (
|
||||||
<SelectItem key={mall.mallUser || mall.id} value={mall.mallUser}>
|
<SelectItem value="loading" disabled>
|
||||||
{mall.mallName}
|
加载中...
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
) : malls.length === 0 ? (
|
||||||
|
<SelectItem value="no-data" disabled>
|
||||||
|
{!newMerchant.province ? "请先选择省份" : "该省份暂无商场数据"}
|
||||||
|
</SelectItem>
|
||||||
|
) : (
|
||||||
|
malls
|
||||||
|
.filter(mall => mall.mallUser) // 过滤掉没有 mallUser 的项
|
||||||
|
.map((mall, index) => (
|
||||||
|
<SelectItem key={mall.id || mall.mallId || `mall-${index}`} value={mall.mallUser}>
|
||||||
|
{mall.mallName || '未命名商场'}
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5 sm:space-y-2">
|
<div className="space-y-1.5 sm:space-y-2">
|
||||||
<Label htmlFor="name" className="text-sm sm:text-base">商户名称</Label>
|
<Label htmlFor="new-merchant-name" className="text-sm sm:text-base">商户名称</Label>
|
||||||
<Input
|
<Input
|
||||||
id="name"
|
id="new-merchant-name"
|
||||||
value={newMerchant.name}
|
value={newMerchant.name}
|
||||||
onChange={(e) => setNewMerchant({ ...newMerchant, name: e.target.value })}
|
onChange={(e) => setNewMerchant({ ...newMerchant, name: e.target.value })}
|
||||||
placeholder="请输入商户名称"
|
placeholder="请输入商户名称"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="contact">联系人</Label>
|
<Label htmlFor="new-merchant-contact">联系人</Label>
|
||||||
<Input
|
<Input
|
||||||
id="contact"
|
id="new-merchant-contact"
|
||||||
value={newMerchant.contact}
|
value={newMerchant.contact}
|
||||||
onChange={(e) => setNewMerchant({ ...newMerchant, contact: e.target.value })}
|
onChange={(e) => setNewMerchant({ ...newMerchant, contact: e.target.value })}
|
||||||
placeholder="请输入联系人姓名"
|
placeholder="请输入联系人姓名"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="phone">联系电话</Label>
|
<Label htmlFor="new-merchant-phone">联系电话</Label>
|
||||||
<Input
|
<Input
|
||||||
id="phone"
|
id="new-merchant-phone"
|
||||||
value={newMerchant.phone}
|
value={newMerchant.phone}
|
||||||
onChange={(e) => setNewMerchant({ ...newMerchant, phone: e.target.value })}
|
onChange={(e) => setNewMerchant({ ...newMerchant, phone: e.target.value })}
|
||||||
placeholder="请输入联系电话"
|
placeholder="请输入联系电话"
|
||||||
@@ -1035,8 +1110,8 @@ export default function MerchantsPage() {
|
|||||||
点击加载总商户数据
|
点击加载总商户数据
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
) : (
|
) : (
|
||||||
totalMerchants.map((merchant) => (
|
totalMerchants.map((merchant, index) => (
|
||||||
<SelectItem key={merchant.userId} value={merchant.userId}>
|
<SelectItem key={merchant.userId || `total-merchant-${index}`} value={merchant.userId}>
|
||||||
{merchant.nickName}
|
{merchant.nickName}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))
|
))
|
||||||
@@ -1216,11 +1291,23 @@ export default function MerchantsPage() {
|
|||||||
} />
|
} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="max-h-[300px]">
|
<SelectContent className="max-h-[300px]">
|
||||||
{malls.map((mall) => (
|
{loadingMalls ? (
|
||||||
<SelectItem key={mall.mallUser || mall.id} value={mall.mallUser}>
|
<SelectItem value="loading" disabled>
|
||||||
{mall.mallName}
|
加载中...
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
) : malls.length === 0 ? (
|
||||||
|
<SelectItem value="no-data" disabled>
|
||||||
|
{!editMerchant.province ? "请先选择省份" : "该省份暂无商场数据"}
|
||||||
|
</SelectItem>
|
||||||
|
) : (
|
||||||
|
malls
|
||||||
|
.filter(mall => mall.mallUser) // 过滤掉没有 mallUser 的项
|
||||||
|
.map((mall, index) => (
|
||||||
|
<SelectItem key={mall.id || mall.mallId || `mall-${index}`} value={mall.mallUser}>
|
||||||
|
{mall.mallName || '未命名商场'}
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
@@ -1243,9 +1330,9 @@ export default function MerchantsPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="edit-phone">联系电话</Label>
|
<Label htmlFor="edit-merchant-phone">联系电话</Label>
|
||||||
<Input
|
<Input
|
||||||
id="edit-phone"
|
id="edit-merchant-phone"
|
||||||
value={editMerchant.phone}
|
value={editMerchant.phone}
|
||||||
onChange={(e) => setEditMerchant({ ...editMerchant, phone: e.target.value })}
|
onChange={(e) => setEditMerchant({ ...editMerchant, phone: e.target.value })}
|
||||||
placeholder="请输入联系电话"
|
placeholder="请输入联系电话"
|
||||||
@@ -1386,16 +1473,16 @@ export default function MerchantsPage() {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
) : (merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).length > 0 ? (
|
) : (merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).length > 0 ? (
|
||||||
(merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).map((equipment: any) => (
|
(merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).map((equipment: any, index: number) => (
|
||||||
<TableRow key={equipment.id}>
|
<TableRow key={equipment.id || equipment.equipmentId || `equipment-${index}`}>
|
||||||
<TableCell className="font-medium truncate max-w-[120px]" title={equipment.equipmentId}>
|
<TableCell className="font-medium truncate max-w-[120px]" title={equipment.equipmentId}>
|
||||||
{equipment.equipmentId}
|
{equipment.equipmentId}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="truncate max-w-[150px]" title={equipment.equipmentName}>
|
<TableCell className="truncate max-w-[150px]" title={equipment.equipmentName}>
|
||||||
{equipment.equipmentName}
|
{equipment.equipmentName}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="truncate max-w-[120px]" title={equipment.equipmentType}>
|
<TableCell className="truncate max-w-[120px]" title={getEquipmentTypeName(equipment.equipmentType)}>
|
||||||
{equipment.equipmentType}
|
{getEquipmentTypeName(equipment.equipmentType)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="truncate max-w-[120px]" title={equipment.installationDate}>
|
<TableCell className="truncate max-w-[120px]" title={equipment.installationDate}>
|
||||||
{equipment.installationDate}
|
{equipment.installationDate}
|
||||||
@@ -1425,8 +1512,8 @@ export default function MerchantsPage() {
|
|||||||
{loadingMerchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] ? (
|
{loadingMerchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] ? (
|
||||||
<div className="text-center py-8 text-gray-500 text-sm">加载中...</div>
|
<div className="text-center py-8 text-gray-500 text-sm">加载中...</div>
|
||||||
) : (merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).length > 0 ? (
|
) : (merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).length > 0 ? (
|
||||||
(merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).map((equipment: any) => (
|
(merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).map((equipment: any, index: number) => (
|
||||||
<Card key={equipment.id} className="overflow-hidden">
|
<Card key={equipment.id || equipment.equipmentId || `equipment-card-${index}`} className="overflow-hidden">
|
||||||
<CardContent className="p-4 space-y-3">
|
<CardContent className="p-4 space-y-3">
|
||||||
{/* 设备基本信息 */}
|
{/* 设备基本信息 */}
|
||||||
<div className="flex items-start justify-between gap-3">
|
<div className="flex items-start justify-between gap-3">
|
||||||
@@ -1441,7 +1528,7 @@ export default function MerchantsPage() {
|
|||||||
<div className="border-t pt-3 space-y-2.5">
|
<div className="border-t pt-3 space-y-2.5">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-xs font-medium text-gray-700">类型</div>
|
<div className="text-xs font-medium text-gray-700">类型</div>
|
||||||
<div className="text-sm text-gray-900 break-words">{equipment.equipmentType || '-'}</div>
|
<div className="text-sm text-gray-900 break-words">{getEquipmentTypeName(equipment.equipmentType)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-xs font-medium text-gray-700">安装日期</div>
|
<div className="text-xs font-medium text-gray-700">安装日期</div>
|
||||||
|
|||||||
@@ -1006,7 +1006,7 @@ function CreateWorkOrderForm({ onClose }: { onClose: () => void }) {
|
|||||||
<SelectContent className="max-h-[300px] overflow-y-auto">
|
<SelectContent className="max-h-[300px] overflow-y-auto">
|
||||||
{availableEquipment.map((equipment) => (
|
{availableEquipment.map((equipment) => (
|
||||||
<SelectItem key={equipment.id} value={equipment.equipmentId}>
|
<SelectItem key={equipment.id} value={equipment.equipmentId}>
|
||||||
{equipment.equipmentName} ({equipment.equipmentType})
|
{equipment.equipmentName}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|||||||
Reference in New Issue
Block a user