数据统计
This commit is contained in:
@@ -70,7 +70,8 @@ interface Equipment {
|
||||
}
|
||||
|
||||
export default function EquipmentPage() {
|
||||
const [searchTerm, setSearchTerm] = useState("")
|
||||
const [equipmentIdSearch, setEquipmentIdSearch] = useState("")
|
||||
const [equipmentNameSearch, setEquipmentNameSearch] = useState("")
|
||||
const [statusFilter, setStatusFilter] = useState("all")
|
||||
const [isAddEquipmentOpen, setIsAddEquipmentOpen] = useState(false)
|
||||
const [isEditEquipmentOpen, setIsEditEquipmentOpen] = useState(false)
|
||||
@@ -125,10 +126,26 @@ export default function EquipmentPage() {
|
||||
}
|
||||
|
||||
// 获取设备列表
|
||||
const fetchEquipmentList = async (pageNum = 1, pageSize = 10) => {
|
||||
const fetchEquipmentList = async (pageNum = 1, pageSize = 10, equipmentId = "", equipmentName = "", status = "") => {
|
||||
setLoadingEquipmentList(true)
|
||||
try {
|
||||
const response = await apiGet(`/back/equipment/list?pageNum=${pageNum}&pageSize=${pageSize}`)
|
||||
// 构建查询参数
|
||||
const params = new URLSearchParams({
|
||||
pageNum: pageNum.toString(),
|
||||
pageSize: pageSize.toString(),
|
||||
})
|
||||
|
||||
if (equipmentId.trim()) {
|
||||
params.append('equipmentId', equipmentId.trim())
|
||||
}
|
||||
if (equipmentName.trim()) {
|
||||
params.append('equipmentName', equipmentName.trim())
|
||||
}
|
||||
if (status && status !== "all") {
|
||||
params.append('status', status)
|
||||
}
|
||||
|
||||
const response = await apiGet(`/back/equipment/list?${params.toString()}`)
|
||||
if (response.code === 200) {
|
||||
setEquipmentList(response.rows || [])
|
||||
setPagination({
|
||||
@@ -195,6 +212,25 @@ export default function EquipmentPage() {
|
||||
fetchEquipmentList()
|
||||
}, [])
|
||||
|
||||
// 搜索条件变化时调用API(使用防抖)
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
// 重置到第一页
|
||||
const newPageNum = 1
|
||||
fetchEquipmentList(
|
||||
newPageNum,
|
||||
pagination.pageSize,
|
||||
equipmentIdSearch,
|
||||
equipmentNameSearch,
|
||||
statusFilter
|
||||
)
|
||||
setPagination(prev => ({ ...prev, pageNum: newPageNum }))
|
||||
}, 500) // 500ms 防抖
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [equipmentIdSearch, equipmentNameSearch, statusFilter])
|
||||
|
||||
const [newEquipment, setNewEquipment] = useState({
|
||||
name: "",
|
||||
type: "",
|
||||
@@ -345,7 +381,13 @@ export default function EquipmentPage() {
|
||||
console.log('编辑设备成功:', result)
|
||||
alert('编辑设备成功!')
|
||||
setIsEditEquipmentOpen(false)
|
||||
fetchEquipmentList(pagination.pageNum, pagination.pageSize)
|
||||
fetchEquipmentList(
|
||||
pagination.pageNum,
|
||||
pagination.pageSize,
|
||||
equipmentIdSearch,
|
||||
equipmentNameSearch,
|
||||
statusFilter
|
||||
)
|
||||
} else {
|
||||
console.error('编辑设备失败:', result)
|
||||
alert('编辑设备失败:' + (result.msg || '未知错误'))
|
||||
@@ -415,7 +457,13 @@ export default function EquipmentPage() {
|
||||
setIsAddEquipmentOpen(false)
|
||||
|
||||
// 刷新设备列表
|
||||
fetchEquipmentList(pagination.pageNum, pagination.pageSize)
|
||||
fetchEquipmentList(
|
||||
pagination.pageNum,
|
||||
pagination.pageSize,
|
||||
equipmentIdSearch,
|
||||
equipmentNameSearch,
|
||||
statusFilter
|
||||
)
|
||||
} else {
|
||||
console.error('添加设备失败:', result)
|
||||
alert('添加设备失败:' + (result.msg || '未知错误'))
|
||||
@@ -444,30 +492,8 @@ export default function EquipmentPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const filteredEquipment = equipmentList.filter((item) => {
|
||||
// 安全地处理可能为 null 或 undefined 的字段
|
||||
const equipmentName = item.equipmentName || ""
|
||||
const merchantName = item.merchantName || ""
|
||||
const mallName = item.mallName || ""
|
||||
const searchLower = searchTerm.toLowerCase()
|
||||
|
||||
const matchesSearch =
|
||||
equipmentName.toLowerCase().includes(searchLower) ||
|
||||
merchantName.toLowerCase().includes(searchLower) ||
|
||||
mallName.toLowerCase().includes(searchLower)
|
||||
|
||||
// 修正状态筛选逻辑
|
||||
let matchesStatus = true
|
||||
if (statusFilter === "1" || statusFilter === "normal") {
|
||||
matchesStatus = item.status === "1"
|
||||
} else if (statusFilter === "2" || statusFilter === "expiring") {
|
||||
matchesStatus = item.status === "2"
|
||||
} else if (statusFilter === "3" || statusFilter === "expired") {
|
||||
matchesStatus = item.status === "3"
|
||||
}
|
||||
|
||||
return matchesSearch && matchesStatus
|
||||
})
|
||||
// 直接使用从API返回的数据,不再进行前端过滤
|
||||
const filteredEquipment = equipmentList
|
||||
|
||||
return (
|
||||
<div className="space-y-4 sm:space-y-6 p-4 sm:p-6">
|
||||
@@ -931,18 +957,28 @@ export default function EquipmentPage() {
|
||||
<CardContent>
|
||||
{/* 筛选区域 */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-6">
|
||||
<div className="flex-1">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="relative">
|
||||
<MapPin className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
||||
<Shield 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={equipmentIdSearch}
|
||||
onChange={(e) => setEquipmentIdSearch(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="relative">
|
||||
<MapPin className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
|
||||
<Input
|
||||
placeholder="搜索设备名称..."
|
||||
value={equipmentNameSearch}
|
||||
onChange={(e) => setEquipmentNameSearch(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="w-full sm:w-40">
|
||||
<SelectValue placeholder="筛选状态" />
|
||||
@@ -954,8 +990,7 @@ export default function EquipmentPage() {
|
||||
<SelectItem value="3">已到期</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Button variant="outline">
|
||||
<Button variant="outline" className="w-full sm:w-auto">
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
导出
|
||||
</Button>
|
||||
@@ -1144,7 +1179,13 @@ export default function EquipmentPage() {
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (pagination.pageNum > 1) {
|
||||
fetchEquipmentList(pagination.pageNum - 1, pagination.pageSize)
|
||||
fetchEquipmentList(
|
||||
pagination.pageNum - 1,
|
||||
pagination.pageSize,
|
||||
equipmentIdSearch,
|
||||
equipmentNameSearch,
|
||||
statusFilter
|
||||
)
|
||||
}
|
||||
}}
|
||||
disabled={pagination.pageNum <= 1 || loadingEquipmentList}
|
||||
@@ -1161,7 +1202,13 @@ export default function EquipmentPage() {
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (pagination.pageNum < Math.ceil(pagination.total / pagination.pageSize)) {
|
||||
fetchEquipmentList(pagination.pageNum + 1, pagination.pageSize)
|
||||
fetchEquipmentList(
|
||||
pagination.pageNum + 1,
|
||||
pagination.pageSize,
|
||||
equipmentIdSearch,
|
||||
equipmentNameSearch,
|
||||
statusFilter
|
||||
)
|
||||
}
|
||||
}}
|
||||
disabled={pagination.pageNum >= Math.ceil(pagination.total / pagination.pageSize) || loadingEquipmentList}
|
||||
|
||||
@@ -76,22 +76,23 @@ export default function StatisticsPage() {
|
||||
const [deviceDistributionData, setDeviceDistributionData] = useState<DeviceDistribution[]>([])
|
||||
const [workOrderTrendData, setWorkOrderTrendData] = useState<WorkOrderTrend[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [timeScope, setTimeScope] = useState<string>('month')
|
||||
const userData = getUserData()
|
||||
|
||||
// 判断是否是总公司账号(根据实际业务逻辑调整)
|
||||
const isHeadquarters = userData?.role === 'admin' || userData?.roleType === 'headquarters'
|
||||
|
||||
useEffect(() => {
|
||||
fetchStatistics()
|
||||
fetchStatistics(timeScope)
|
||||
fetchDealerDistribution()
|
||||
fetchDeviceDistribution()
|
||||
fetchWorkOrderTrend()
|
||||
}, [])
|
||||
}, [timeScope])
|
||||
|
||||
const fetchStatistics = async () => {
|
||||
const fetchStatistics = async (scope: string = 'month') => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const response = await apiGet<{ code: number; msg: string; data: StatisticsData }>('/back/statistics/planTop')
|
||||
const response = await apiGet<{ code: number; msg: string; data: StatisticsData }>(`/back/statistics/planTop?scope=${scope}`)
|
||||
if (response.code === 200) {
|
||||
setStatisticsData(response.data)
|
||||
}
|
||||
@@ -246,7 +247,7 @@ export default function StatisticsPage() {
|
||||
<p className="text-gray-600">系统概览、关键指标和数据分析</p>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<Select defaultValue="month">
|
||||
<Select value={timeScope} onValueChange={setTimeScope}>
|
||||
<SelectTrigger className="w-32">
|
||||
<Filter className="h-4 w-4 mr-2" />
|
||||
<SelectValue />
|
||||
|
||||
@@ -39,7 +39,6 @@ export default function WorkersPage() {
|
||||
const [provinces, setProvinces] = useState<Province[]>([])
|
||||
const [dealers, setDealers] = useState<Dealer[]>([])
|
||||
const [searchTerm, setSearchTerm] = useState("")
|
||||
const [provinceFilter, setProvinceFilter] = useState("all")
|
||||
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false)
|
||||
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
|
||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
|
||||
@@ -262,9 +261,7 @@ export default function WorkersPage() {
|
||||
worker.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
worker.workerId.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
worker.dealerName.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
const matchesProvince = provinceFilter === "all" || worker.province === provinceFilter ||
|
||||
provinces.find(p => p.code === provinceFilter)?.name === worker.province
|
||||
return matchesSearch && matchesProvince
|
||||
return matchesSearch
|
||||
})
|
||||
|
||||
const handleAddWorker = async () => {
|
||||
@@ -820,19 +817,6 @@ export default function WorkersPage() {
|
||||
className="pl-8 w-full sm:w-80"
|
||||
/>
|
||||
</div>
|
||||
<Select value={provinceFilter} onValueChange={setProvinceFilter}>
|
||||
<SelectTrigger className="w-full sm:w-32">
|
||||
<SelectValue placeholder="省份" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="max-h-[300px]">
|
||||
<SelectItem value="all">全部省份</SelectItem>
|
||||
{provinces.map((province) => (
|
||||
<SelectItem key={province.code} value={province.code}>
|
||||
{province.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select value={selectedWorkOrder} onValueChange={setSelectedWorkOrder}>
|
||||
<SelectTrigger className="w-full sm:w-64">
|
||||
<SelectValue placeholder="筛选工人" />
|
||||
|
||||
Reference in New Issue
Block a user