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 { 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>
|
||||
|
||||
|
||||
@@ -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("")
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user