shangc
This commit is contained in:
@@ -16,7 +16,7 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "../ui/dialog"
|
} from "../ui/dialog"
|
||||||
import { Plus, Download, MapPin, Calendar, Shield, ChevronLeft, ChevronRight, Minus, Plus as PlusIcon, Search } from "lucide-react"
|
import { Plus, Download, MapPin, Calendar, Shield, ChevronLeft, ChevronRight, Minus, Plus as PlusIcon, Search } from "lucide-react"
|
||||||
import { apiGet, apiPost, apiPut } from "../../lib/services/api"
|
import { apiGet, apiPost, apiPut, apiExportFile } from "../../lib/services/api"
|
||||||
|
|
||||||
// 商户数据类型(根据新接口返回格式定义)
|
// 商户数据类型(根据新接口返回格式定义)
|
||||||
interface ProvinceMerchant {
|
interface ProvinceMerchant {
|
||||||
@@ -98,6 +98,7 @@ export default function EquipmentPage() {
|
|||||||
const [loadingStats, setLoadingStats] = useState(false)
|
const [loadingStats, setLoadingStats] = useState(false)
|
||||||
const [merchantSearchForAdd, setMerchantSearchForAdd] = useState("")
|
const [merchantSearchForAdd, setMerchantSearchForAdd] = useState("")
|
||||||
const [merchantSearchForEdit, setMerchantSearchForEdit] = useState("")
|
const [merchantSearchForEdit, setMerchantSearchForEdit] = useState("")
|
||||||
|
const [isExporting, setIsExporting] = useState(false)
|
||||||
|
|
||||||
// 获取设备统计数据
|
// 获取设备统计数据
|
||||||
const fetchEquipmentStats = async () => {
|
const fetchEquipmentStats = async () => {
|
||||||
@@ -534,6 +535,29 @@ export default function EquipmentPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 导出设备数据
|
||||||
|
const handleExportEquipment = async () => {
|
||||||
|
setIsExporting(true)
|
||||||
|
try {
|
||||||
|
// 构建导出参数(包含当前搜索条件)
|
||||||
|
const exportParams = {
|
||||||
|
equipmentId: equipmentIdSearch.trim() || undefined,
|
||||||
|
equipmentName: equipmentNameSearch.trim() || undefined,
|
||||||
|
status: statusFilter !== "all" ? statusFilter : undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用导出接口
|
||||||
|
await apiExportFile('/back/equipment/export', exportParams, `设备列表_${new Date().toISOString().split('T')[0]}.xlsx`)
|
||||||
|
|
||||||
|
console.log('导出设备数据成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('导出设备数据失败:', error)
|
||||||
|
alert('导出失败:' + (error instanceof Error ? error.message : '未知错误'))
|
||||||
|
} finally {
|
||||||
|
setIsExporting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 直接使用从API返回的数据,不再进行前端过滤
|
// 直接使用从API返回的数据,不再进行前端过滤
|
||||||
const filteredEquipment = equipmentList
|
const filteredEquipment = equipmentList
|
||||||
|
|
||||||
@@ -1228,9 +1252,14 @@ export default function EquipmentPage() {
|
|||||||
<SelectItem value="3">已到期</SelectItem>
|
<SelectItem value="3">已到期</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<Button variant="outline" className="w-full sm:w-auto h-9 sm:h-10 text-sm">
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="w-full sm:w-auto h-9 sm:h-10 text-sm"
|
||||||
|
onClick={handleExportEquipment}
|
||||||
|
disabled={isExporting}
|
||||||
|
>
|
||||||
<Download className="h-4 w-4 mr-2" />
|
<Download className="h-4 w-4 mr-2" />
|
||||||
导出
|
{isExporting ? "导出中..." : "导出"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "../ui/dialog"
|
} from "../ui/dialog"
|
||||||
import { Plus, Search, Filter, Download, Store, Eye, Building2, MapPin, ChevronLeft, ChevronRight, Edit } from "lucide-react"
|
import { Plus, Search, Filter, Download, Store, Eye, Building2, MapPin, ChevronLeft, ChevronRight, Edit } from "lucide-react"
|
||||||
import { apiGet, apiPost, apiPut } from "../../lib/services/api"
|
import { apiGet, apiPost, apiPut, apiExportFile } from "../../lib/services/api"
|
||||||
|
|
||||||
// 总商户数据类型
|
// 总商户数据类型
|
||||||
interface TotalMerchant {
|
interface TotalMerchant {
|
||||||
@@ -87,6 +87,7 @@ export default function MerchantsPage() {
|
|||||||
countExpire: 0
|
countExpire: 0
|
||||||
})
|
})
|
||||||
const [loadingUserEquipmentCount, setLoadingUserEquipmentCount] = useState(false)
|
const [loadingUserEquipmentCount, setLoadingUserEquipmentCount] = useState(false)
|
||||||
|
const [isExporting, setIsExporting] = useState(false)
|
||||||
|
|
||||||
const [newMerchant, setNewMerchant] = useState({
|
const [newMerchant, setNewMerchant] = useState({
|
||||||
name: "",
|
name: "",
|
||||||
@@ -571,6 +572,30 @@ export default function MerchantsPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 导出商户数据
|
||||||
|
const handleExportMerchants = async () => {
|
||||||
|
setIsExporting(true)
|
||||||
|
try {
|
||||||
|
// 构建导出参数(包含当前搜索条件)
|
||||||
|
const exportParams = {
|
||||||
|
merchantName: merchantNameSearch.trim() || undefined,
|
||||||
|
contactPerson: contactPersonSearch.trim() || undefined,
|
||||||
|
contactPhone: contactPhoneSearch.trim() || undefined,
|
||||||
|
equipmentStatus: statusFilter !== "all" ? statusFilter : undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用导出接口
|
||||||
|
await apiExportFile('/back/merchants/export', exportParams, `商户列表_${new Date().toISOString().split('T')[0]}.xlsx`)
|
||||||
|
|
||||||
|
console.log('导出商户数据成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('导出商户数据失败:', error)
|
||||||
|
alert('导出失败:' + (error instanceof Error ? error.message : '未知错误'))
|
||||||
|
} finally {
|
||||||
|
setIsExporting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 提交编辑
|
// 提交编辑
|
||||||
const handleUpdateMerchant = async () => {
|
const handleUpdateMerchant = async () => {
|
||||||
if (!editMerchant.name || !editMerchant.contact || !editMerchant.phone) {
|
if (!editMerchant.name || !editMerchant.contact || !editMerchant.phone) {
|
||||||
@@ -740,9 +765,13 @@ export default function MerchantsPage() {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<Button variant="outline">
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={handleExportMerchants}
|
||||||
|
disabled={isExporting}
|
||||||
|
>
|
||||||
<Download className="h-4 w-4 mr-2" />
|
<Download className="h-4 w-4 mr-2" />
|
||||||
导出
|
{isExporting ? '导出中...' : '导出'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
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";
|
||||||
@@ -47,6 +48,7 @@ interface ArchivedWorkOrder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function WorkOrderArchivePage() {
|
export default function WorkOrderArchivePage() {
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const [archivedOrders, setArchivedOrders] = useState<ArchivedWorkOrder[]>([]);
|
const [archivedOrders, setArchivedOrders] = useState<ArchivedWorkOrder[]>([]);
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [statusFilter, setStatusFilter] = useState("all");
|
const [statusFilter, setStatusFilter] = useState("all");
|
||||||
@@ -81,14 +83,15 @@ export default function WorkOrderArchivePage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 获取归档工单列表
|
// 获取归档工单列表
|
||||||
const fetchArchivedOrders = async (pageNum = 1, pageSize = 10) => {
|
const fetchArchivedOrders = async (pageNum = 1, pageSize = 10, searchValue?: string) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
let url = `/back/workOrderArchive/list?pageNum=${pageNum}&pageSize=${pageSize}`;
|
let url = `/back/workOrderArchive/list?pageNum=${pageNum}&pageSize=${pageSize}`;
|
||||||
|
|
||||||
// 添加工单编号搜索参数
|
// 添加工单编号搜索参数,优先使用传入的搜索值,否则使用 state 中的 searchTerm
|
||||||
if (searchTerm.trim()) {
|
const searchValueToUse = searchValue !== undefined ? searchValue : searchTerm;
|
||||||
url += `&workOrderNumber=${encodeURIComponent(searchTerm.trim())}`;
|
if (searchValueToUse.trim()) {
|
||||||
|
url += `&workOrderNumber=${encodeURIComponent(searchValueToUse.trim())}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加合格/不合格筛选参数
|
// 添加合格/不合格筛选参数
|
||||||
@@ -112,9 +115,25 @@ export default function WorkOrderArchivePage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 从 URL 参数中读取工单号并设置搜索词
|
||||||
|
useEffect(() => {
|
||||||
|
const workOrderNumber = searchParams.get('workOrderNumber');
|
||||||
|
if (workOrderNumber) {
|
||||||
|
setSearchTerm(workOrderNumber);
|
||||||
|
// 立即搜索,不等待防抖
|
||||||
|
fetchArchivedOrders(1, 10, workOrderNumber);
|
||||||
|
// 清除 URL 参数,避免刷新时重复搜索
|
||||||
|
setSearchParams({}, { replace: true });
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchArchiveCount();
|
fetchArchiveCount();
|
||||||
fetchArchivedOrders(1, 10);
|
// 只有在没有 URL 参数时才执行初始加载(避免与上面的 useEffect 重复)
|
||||||
|
if (!searchParams.get('workOrderNumber')) {
|
||||||
|
fetchArchivedOrders(1, 10);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 监听搜索词和筛选条件变化,重新请求数据
|
// 监听搜索词和筛选条件变化,重新请求数据
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect } from "react"
|
import React, { useState, useEffect } from "react"
|
||||||
|
import { useNavigate } from "react-router-dom"
|
||||||
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"
|
||||||
@@ -27,6 +28,7 @@ import {
|
|||||||
ChevronRight,
|
ChevronRight,
|
||||||
Wrench,
|
Wrench,
|
||||||
Edit,
|
Edit,
|
||||||
|
FileText,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { apiGet, apiPost, apiPut } from "../../lib/services/api"
|
import { apiGet, apiPost, apiPut } from "../../lib/services/api"
|
||||||
|
|
||||||
@@ -153,6 +155,7 @@ interface WorkOrder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function WorkOrdersPage() {
|
export default function WorkOrdersPage() {
|
||||||
|
const navigate = useNavigate()
|
||||||
const [searchTerm, setSearchTerm] = useState("")
|
const [searchTerm, setSearchTerm] = useState("")
|
||||||
const [statusFilter, setStatusFilter] = useState("all")
|
const [statusFilter, setStatusFilter] = useState("all")
|
||||||
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
|
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
|
||||||
@@ -502,16 +505,30 @@ export default function WorkOrdersPage() {
|
|||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Button
|
<div className="flex items-center space-x-2">
|
||||||
variant="ghost"
|
<Button
|
||||||
size="sm"
|
variant="ghost"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
setEditingWorkOrder(item)
|
onClick={() => {
|
||||||
setIsEditDialogOpen(true)
|
setEditingWorkOrder(item)
|
||||||
}}
|
setIsEditDialogOpen(true)
|
||||||
>
|
}}
|
||||||
<Edit className="h-4 w-4" />
|
>
|
||||||
</Button>
|
<Edit className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
{item.status === "7" && (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`/admin/workorder-archive?workOrderNumber=${encodeURIComponent(item.workOrderNumber)}`)
|
||||||
|
}}
|
||||||
|
title="查看归档"
|
||||||
|
>
|
||||||
|
<FileText className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))
|
))
|
||||||
@@ -540,16 +557,30 @@ export default function WorkOrdersPage() {
|
|||||||
<div className="text-sm font-medium text-gray-900">{item.equipmentName}</div>
|
<div className="text-sm font-medium text-gray-900">{item.equipmentName}</div>
|
||||||
<div className="text-xs text-gray-500">设备: {item.equipmentId}</div>
|
<div className="text-xs text-gray-500">设备: {item.equipmentId}</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<div className="flex items-center space-x-2">
|
||||||
variant="ghost"
|
<Button
|
||||||
size="sm"
|
variant="ghost"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
setEditingWorkOrder(item)
|
onClick={() => {
|
||||||
setIsEditDialogOpen(true)
|
setEditingWorkOrder(item)
|
||||||
}}
|
setIsEditDialogOpen(true)
|
||||||
>
|
}}
|
||||||
<Edit className="h-4 w-4" />
|
>
|
||||||
</Button>
|
<Edit className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
{item.status === "7" && (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`/admin/workorder-archive?workOrderNumber=${encodeURIComponent(item.workOrderNumber)}`)
|
||||||
|
}}
|
||||||
|
title="查看归档"
|
||||||
|
>
|
||||||
|
<FileText className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border-t pt-3 space-y-2">
|
<div className="border-t pt-3 space-y-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user