feat: 实现管理员使用人统计页面

This commit is contained in:
BenjaminNH 2025-07-02 23:14:53 +08:00
parent 8a5f231ae2
commit 886a969477
3 changed files with 154 additions and 1 deletions

View File

@ -55,7 +55,7 @@ const menuConfig = [
label: "设备统计", label: "设备统计",
}, },
{ {
path: "/admin/stats-user", path: "/admin/stats-reservation",
label: "使用人统计", label: "使用人统计",
}, },
], ],

View File

@ -0,0 +1,148 @@
import { Button, DatePicker, Input, Space, Spin, Table, message } from "antd";
import dayjs from "dayjs";
import { useEffect, useState } from "react";
import axiosInstance from "../../api/axios";
import { datePresets } from "../../config/datePresetsConfig";
const { RangePicker } = DatePicker;
export default function ReservationStats() {
const [data, setData] = useState([]);
const [filteredData, setFilteredData] = useState([]);
const [range, setRange] = useState([
dayjs().startOf("month"),
dayjs().endOf("month"),
]);
const [search, setSearch] = useState("");
const [loading, setLoading] = useState(false);
const fetchData = async () => {
setLoading(true);
try {
const res = await axiosInstance.get("/reservation/stats", {
params: {
start: range[0].format("YYYY-MM-DD"),
end: range[1].format("YYYY-MM-DD"),
},
});
setData(res);
setFilteredData(res);
} catch (e) {
message.error("获取数据失败");
} finally {
setLoading(false);
}
};
const handleSearch = (value) => {
setSearch(value);
const filtered = data.filter((item) =>
item.deviceName.toLowerCase().includes(value.toLowerCase())
);
setFilteredData(filtered);
};
const handleDownload = async () => {
setLoading(true);
try {
const response = await axiosInstance.get("/reservation/stats/export", {
params: {
start: range[0].format("YYYY-MM-DD"),
end: range[1].format("YYYY-MM-DD"),
},
responseType: "blob", //
skipInterceptor: true,
});
//
const disposition = response.headers["content-disposition"];
let fileName = "使用人统计.xlsx";
if (disposition) {
const match = disposition.match(/filename="?([^"]+)"?/);
if (match && match[1]) fileName = decodeURIComponent(match[1]);
}
// blob
const blob = new Blob([response.data], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
message.success("导出成功");
} catch (error) {
message.error("导出失败,请稍后重试");
console.error(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, [range]);
const columns = [
{
title: "设备名称",
dataIndex: "deviceName",
key: "deviceName",
sorter: (a, b) => a.deviceName.localeCompare(b.deviceName),
},
{
title: "使用人",
dataIndex: "applicantName",
key: "applicantName",
sorter: (a, b) => a.applicantName.localeCompare(b.applicantName),
},
{
title: "所属团队",
dataIndex: "applicantTeam",
key: "applicantTeam",
sorter: (a, b) => a.applicantTeam.localeCompare(b.applicantTeam),
},
{
title: "使用次数",
dataIndex: "usageCount",
key: "usageCount",
sorter: (a, b) => a.usageCount - b.usageCount,
},
];
return (
<Spin spinning={loading}>
<Space className="p-4">
<RangePicker
value={range}
onChange={(dates) => {
if (dates) setRange(dates);
}}
presets={datePresets}
/>
<Input.Search
allowClear
placeholder="搜索设备名称"
onSearch={handleSearch}
style={{ width: 200 }}
/>
<Button type="primary" onClick={handleDownload}>
导出 Excel
</Button>
</Space>
<Table
rowKey={(record) =>
`${record.deviceId}_${record.applicantName}_${record.applicantTeam}`
}
dataSource={filteredData}
columns={columns}
/>
</Spin>
);
}

View File

@ -10,6 +10,7 @@ import ProtectedRoute from "./ProtectedRoute";
import DeviceManage from "../pages/deviceAdmin/DeviceManage"; import DeviceManage from "../pages/deviceAdmin/DeviceManage";
import UserManage from "../pages/admin/UserManage"; import UserManage from "../pages/admin/UserManage";
import DeviceStats from "../pages/admin/DeviceStats"; import DeviceStats from "../pages/admin/DeviceStats";
import ReservationStats from "../pages/admin/ReservationStats";
const router = createBrowserRouter([ const router = createBrowserRouter([
{ {
@ -73,6 +74,10 @@ const router = createBrowserRouter([
path: "stats-device", path: "stats-device",
element: <DeviceStats />, element: <DeviceStats />,
}, },
{
path: "stats-reservation",
element: <ReservationStats />,
},
], ],
}, },
{ {