This commit is contained in:
menxipeng
2025-11-29 17:34:10 +08:00
parent de3aa54bd2
commit 5164d2ec46
4 changed files with 330 additions and 124 deletions

View File

@@ -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 { Button } from "../ui/button"
import { Input } from "../ui/input"
@@ -74,6 +74,22 @@ export default function CompanyPermissionsPage() {
const [selectedUser, setSelectedUser] = useState<UserData | null>(null)
const [isSubmitting, setIsSubmitting] = useState(false)
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 () => {
@@ -157,22 +173,53 @@ export default function CompanyPermissionsPage() {
// 初始化数据
useEffect(() => {
fetchUserRole()
fetchRoles()
}, [])
// 初始化 common 角色的过滤器设置(只执行一次)
useEffect(() => {
if (userRoleFromApi === "common" && roles.length > 0 && !hasInitializedCommonRole.current) {
hasInitializedCommonRole.current = true
setRoleFilter("mall")
setActiveTab("mall")
}
}, [userRoleFromApi, roles])
// 当角色列表加载完成后,获取用户数据
useEffect(() => {
if (roles.length > 0) {
if (roleFilter === "all") {
fetchAllUsers()
} else {
const role = roles.find(r => r.roleKey === roleFilter)
if (role) {
fetchUsersByRole(role.roleId, currentPage)
// 如果用户角色是 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") {
fetchAllUsers()
} else {
const role = roles.find(r => r.roleKey === roleFilter)
if (role) {
fetchUsersByRole(role.roleId, currentPage)
}
}
}
}
}
}, [roles, roleFilter, currentPage])
}, [roles, roleFilter, currentPage, userRoleFromApi])
// 当选择的角色改变时,重置页码并获取数据
const handleRoleFilterChange = (value: string) => {
@@ -389,7 +436,7 @@ export default function CompanyPermissionsPage() {
<h1 className="text-3xl font-bold text-gray-900"></h1>
<p className="text-gray-600"></p>
</div>
{isAdmin && (
{(isAdmin || userRoleFromApi === "common") && (
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
<DialogTrigger asChild>
<Button>
@@ -402,7 +449,11 @@ export default function CompanyPermissionsPage() {
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<CreateUserForm roles={roles} onClose={() => setIsCreateDialogOpen(false)} />
<CreateUserForm
roles={roles}
userRoleFromApi={userRoleFromApi}
onClose={() => setIsCreateDialogOpen(false)}
/>
</DialogContent>
</Dialog>
)}
@@ -410,25 +461,33 @@ export default function CompanyPermissionsPage() {
{/* Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-5 gap-4">
{roles.map((role) => {
const roleInfo = getRoleInfo(role.roleKey)
const RoleIcon = roleInfo.icon
const userCount = groupedUsers[role.roleKey]?.length || 0
{roles
.filter((role) => {
// 如果用户角色是 common只显示商场管理员mall
if (userRoleFromApi === "common") {
return role.roleKey === "mall"
}
return true
})
.map((role) => {
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">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-600">{role.roleName}</p>
<p className="text-2xl font-bold">{userCount}</p>
return (
<Card key={role.roleId}>
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-600">{role.roleName}</p>
<p className="text-2xl font-bold">{userCount}</p>
</div>
<RoleIcon className="h-8 w-8 text-gray-600" />
</div>
<RoleIcon className="h-8 w-8 text-gray-600" />
</div>
</CardContent>
</Card>
)
})}
</CardContent>
</Card>
)
})}
</div>
{/* Filters and Search */}
@@ -456,12 +515,22 @@ export default function CompanyPermissionsPage() {
<SelectValue placeholder="角色" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all"></SelectItem>
{roles.map((role) => (
<SelectItem key={role.roleId} value={role.roleKey}>
{role.roleName}
</SelectItem>
))}
{userRoleFromApi !== "common" && (
<SelectItem value="all"></SelectItem>
)}
{roles
.filter((role) => {
// 如果用户角色是 common只显示商场管理员mall
if (userRoleFromApi === "common") {
return role.roleKey === "mall"
}
return true
})
.map((role) => (
<SelectItem key={role.roleId} value={role.roleKey}>
{role.roleName}
</SelectItem>
))}
</SelectContent>
</Select>
@@ -486,29 +555,39 @@ export default function CompanyPermissionsPage() {
{/* Tabs */}
<div className="space-y-4">
<div className="flex space-x-1 overflow-x-auto">
<button
className={`px-4 py-2 text-sm font-medium transition-colors whitespace-nowrap ${
activeTab === "all"
? "border-b-2 border-blue-600 text-blue-600"
: "text-gray-600 hover:text-gray-900"
}`}
onClick={() => handleRoleFilterChange("all")}
>
</button>
{roles.map((role) => (
{userRoleFromApi !== "common" && (
<button
key={role.roleId}
className={`px-4 py-2 text-sm font-medium transition-colors whitespace-nowrap ${
activeTab === role.roleKey
activeTab === "all"
? "border-b-2 border-blue-600 text-blue-600"
: "text-gray-600 hover:text-gray-900"
}`}
onClick={() => handleRoleFilterChange(role.roleKey)}
onClick={() => handleRoleFilterChange("all")}
>
{role.roleName}
</button>
))}
)}
{roles
.filter((role) => {
// 如果用户角色是 common只显示商场管理员mall
if (userRoleFromApi === "common") {
return role.roleKey === "mall"
}
return true
})
.map((role) => (
<button
key={role.roleId}
className={`px-4 py-2 text-sm font-medium transition-colors whitespace-nowrap ${
activeTab === role.roleKey
? "border-b-2 border-blue-600 text-blue-600"
: "text-gray-600 hover:text-gray-900"
}`}
onClick={() => handleRoleFilterChange(role.roleKey)}
>
{role.roleName}
</button>
))}
</div>
{activeTab === "all" ? (
@@ -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({
username: "",
name: "",
@@ -664,6 +751,15 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
const [availableProvinces, setAvailableProvinces] = useState<any[]>([])
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) => {
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) => {
e.preventDefault()
// 校验用户名
const usernameError = validateUsername(formData.username)
if (usernameError) {
alert(usernameError)
return
}
if (!formData.role) {
alert('请选择角色')
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"
return (
@@ -761,10 +893,14 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
<Input
id="username"
value={formData.username}
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
placeholder="请输入用户名"
onChange={handleUsernameChange}
placeholder="请输入用户名3-20个字符仅支持字母、数字、下划线、中划线"
required
maxLength={20}
/>
{formData.username && formData.username.length > 0 && formData.username.length < 3 && (
<p className="text-sm text-red-500">3</p>
)}
</div>
<div className="space-y-2">
@@ -799,18 +935,30 @@ function CreateUserForm({ roles, onClose }: { roles: Role[]; onClose: () => void
fetchProvinces(value)
}
}}
disabled={userRoleFromApi === "common"}
>
<SelectTrigger>
<SelectValue placeholder="选择角色" />
</SelectTrigger>
<SelectContent>
{roles.map((role) => (
<SelectItem key={role.roleId} value={role.roleKey}>
{role.roleName}
</SelectItem>
))}
{roles
.filter((role) => {
// 如果当前用户是 common 角色,只显示商场管理员
if (userRoleFromApi === "common") {
return role.roleKey === "mall"
}
return true
})
.map((role) => (
<SelectItem key={role.roleId} value={role.roleKey}>
{role.roleName}
</SelectItem>
))}
</SelectContent>
</Select>
{userRoleFromApi === "common" && (
<p className="text-sm text-gray-500"></p>
)}
</div>
{shouldShowProvinces && (
@@ -1039,9 +1187,9 @@ function EditUserForm({
<Input
id="edit-username"
value={formData.username}
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
placeholder="请输入用户名"
required
disabled
placeholder="用户名不可修改"
className="bg-gray-100 cursor-not-allowed"
/>
</div>

View File

@@ -165,43 +165,14 @@ export default function EquipmentPage() {
}
}
// 获取单个商户的设备数量
const fetchMerchantEquipmentCount = async (merchantId: string) => {
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 () => {
// 获取商户列表(用于添加/编辑设备选择,不获取设备数量
const fetchMerchantsForSelect = async () => {
setLoadingMerchants(true)
try {
const response = await apiGet('/back/general/provinceMerchants')
if (response.code === 200) {
const merchantsData = response.data || []
// 为每个商户获取设备数量
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)
setMerchants(merchantsData)
}
} catch (error) {
console.error('获取商户列表失败:', error)
@@ -361,7 +332,7 @@ export default function EquipmentPage() {
setIsEditEquipmentOpen(true)
// 加载商户和设备类型数据
if (merchants.length === 0) {
fetchMerchants()
fetchMerchantsForSelect()
}
if (Object.keys(equipmentTypes).length === 0) {
fetchEquipmentTypes()
@@ -613,7 +584,7 @@ export default function EquipmentPage() {
disabled={loadingMerchants}
onOpenChange={(open) => {
if (open && merchants.length === 0 && !loadingMerchants) {
fetchMerchants()
fetchMerchantsForSelect()
}
if (!open) {
setMerchantSearchForAdd("")
@@ -835,7 +806,7 @@ export default function EquipmentPage() {
disabled={loadingMerchants}
onOpenChange={(open) => {
if (open && merchants.length === 0 && !loadingMerchants) {
fetchMerchants()
fetchMerchantsForSelect()
}
if (!open) {
setMerchantSearchForEdit("")

View File

@@ -166,15 +166,25 @@ export default function MerchantsPage() {
// 获取商场列表
const fetchMalls = async (province?: string) => {
if (!province) {
setMalls([])
return
}
setLoadingMalls(true)
try {
const url = `/back/info/list?province=${province}&pageNum=1&pageSize=100`
const response = await apiGet(url)
if (response.code === 200) {
setMalls(response.rows || [])
const mallsData = response.rows || []
console.log('获取商场列表成功:', mallsData)
setMalls(mallsData)
} else {
console.error('获取商场列表失败:', response.msg)
setMalls([])
}
} catch (error) {
console.error('获取商场列表失败:', error)
setMalls([])
} finally {
setLoadingMalls(false)
}
@@ -343,6 +353,47 @@ export default function MerchantsPage() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [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({
name: "",
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) => {
setEditMerchant({
@@ -629,6 +689,7 @@ export default function MerchantsPage() {
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
<Input
id="search-merchant-name"
placeholder="搜索商户名称..."
value={merchantNameSearch}
onChange={(e) => setMerchantNameSearch(e.target.value)}
@@ -638,6 +699,7 @@ export default function MerchantsPage() {
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
<Input
id="search-contact-person"
placeholder="搜索联系人..."
value={contactPersonSearch}
onChange={(e) => setContactPersonSearch(e.target.value)}
@@ -647,6 +709,7 @@ export default function MerchantsPage() {
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
<Input
id="search-contact-phone"
placeholder="搜索联系电话..."
value={contactPhoneSearch}
onChange={(e) => setContactPhoneSearch(e.target.value)}
@@ -958,7 +1021,7 @@ export default function MerchantsPage() {
<div className="space-y-1.5 sm:space-y-2">
<Label htmlFor="mall" className="text-sm sm:text-base"></Label>
<Select
value={newMerchant.mall}
value={newMerchant.mallUserId}
onValueChange={(value) => {
const selectedMall = malls.find(mall => mall.mallUser === value)
setNewMerchant({
@@ -979,36 +1042,48 @@ export default function MerchantsPage() {
} />
</SelectTrigger>
<SelectContent className="max-h-[300px]">
{malls.map((mall) => (
<SelectItem key={mall.mallUser || mall.id} value={mall.mallUser}>
{mall.mallName}
{loadingMalls ? (
<SelectItem value="loading" disabled>
...
</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>
</Select>
</div>
<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
id="name"
id="new-merchant-name"
value={newMerchant.name}
onChange={(e) => setNewMerchant({ ...newMerchant, name: e.target.value })}
placeholder="请输入商户名称"
/>
</div>
<div className="space-y-2">
<Label htmlFor="contact"></Label>
<Label htmlFor="new-merchant-contact"></Label>
<Input
id="contact"
id="new-merchant-contact"
value={newMerchant.contact}
onChange={(e) => setNewMerchant({ ...newMerchant, contact: e.target.value })}
placeholder="请输入联系人姓名"
/>
</div>
<div className="space-y-2">
<Label htmlFor="phone"></Label>
<Label htmlFor="new-merchant-phone"></Label>
<Input
id="phone"
id="new-merchant-phone"
value={newMerchant.phone}
onChange={(e) => setNewMerchant({ ...newMerchant, phone: e.target.value })}
placeholder="请输入联系电话"
@@ -1035,8 +1110,8 @@ export default function MerchantsPage() {
</SelectItem>
) : (
totalMerchants.map((merchant) => (
<SelectItem key={merchant.userId} value={merchant.userId}>
totalMerchants.map((merchant, index) => (
<SelectItem key={merchant.userId || `total-merchant-${index}`} value={merchant.userId}>
{merchant.nickName}
</SelectItem>
))
@@ -1216,11 +1291,23 @@ export default function MerchantsPage() {
} />
</SelectTrigger>
<SelectContent className="max-h-[300px]">
{malls.map((mall) => (
<SelectItem key={mall.mallUser || mall.id} value={mall.mallUser}>
{mall.mallName}
{loadingMalls ? (
<SelectItem value="loading" disabled>
...
</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>
</Select>
</div>
@@ -1243,9 +1330,9 @@ export default function MerchantsPage() {
/>
</div>
<div className="space-y-2">
<Label htmlFor="edit-phone"></Label>
<Label htmlFor="edit-merchant-phone"></Label>
<Input
id="edit-phone"
id="edit-merchant-phone"
value={editMerchant.phone}
onChange={(e) => setEditMerchant({ ...editMerchant, phone: e.target.value })}
placeholder="请输入联系电话"
@@ -1386,16 +1473,16 @@ export default function MerchantsPage() {
</TableCell>
</TableRow>
) : (merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).length > 0 ? (
(merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).map((equipment: any) => (
<TableRow key={equipment.id}>
(merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).map((equipment: any, index: number) => (
<TableRow key={equipment.id || equipment.equipmentId || `equipment-${index}`}>
<TableCell className="font-medium truncate max-w-[120px]" title={equipment.equipmentId}>
{equipment.equipmentId}
</TableCell>
<TableCell className="truncate max-w-[150px]" title={equipment.equipmentName}>
{equipment.equipmentName}
</TableCell>
<TableCell className="truncate max-w-[120px]" title={equipment.equipmentType}>
{equipment.equipmentType}
<TableCell className="truncate max-w-[120px]" title={getEquipmentTypeName(equipment.equipmentType)}>
{getEquipmentTypeName(equipment.equipmentType)}
</TableCell>
<TableCell className="truncate max-w-[120px]" title={equipment.installationDate}>
{equipment.installationDate}
@@ -1425,8 +1512,8 @@ export default function MerchantsPage() {
{loadingMerchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] ? (
<div className="text-center py-8 text-gray-500 text-sm">...</div>
) : (merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).length > 0 ? (
(merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).map((equipment: any) => (
<Card key={equipment.id} className="overflow-hidden">
(merchantEquipments[selectedMerchant?.merchantsId || selectedMerchant?.id] || []).map((equipment: any, index: number) => (
<Card key={equipment.id || equipment.equipmentId || `equipment-card-${index}`} className="overflow-hidden">
<CardContent className="p-4 space-y-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="space-y-1">
<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 className="space-y-1">
<div className="text-xs font-medium text-gray-700"></div>

View File

@@ -718,7 +718,7 @@ function CreateWorkOrderForm({ onClose }: { onClose: () => void }) {
return merchants
}
const lowerSearch = searchTerm.toLowerCase()
return merchants.filter(merchant =>
return merchants.filter(merchant =>
merchant.merchantName.toLowerCase().includes(lowerSearch) ||
merchant.contactPerson.toLowerCase().includes(lowerSearch) ||
merchant.contactPhone.includes(lowerSearch) ||
@@ -1006,7 +1006,7 @@ function CreateWorkOrderForm({ onClose }: { onClose: () => void }) {
<SelectContent className="max-h-[300px] overflow-y-auto">
{availableEquipment.map((equipment) => (
<SelectItem key={equipment.id} value={equipment.equipmentId}>
{equipment.equipmentName} ({equipment.equipmentType})
{equipment.equipmentName}
</SelectItem>
))}
</SelectContent>