feat: migrate all files to typescript with strict false mode

This commit is contained in:
BenjaminNH 2025-09-05 20:46:56 +08:00
parent b3d4e14409
commit d56bca6692
35 changed files with 407 additions and 191 deletions

View File

@ -1,9 +1,9 @@
import dayjs from "dayjs"; import dayjs, { Dayjs } from "dayjs";
import quarterOfYear from "dayjs/plugin/quarterOfYear"; import quarterOfYear from "dayjs/plugin/quarterOfYear";
dayjs.extend(quarterOfYear); dayjs.extend(quarterOfYear);
export const datePresets = [ export const datePresets: Array<{ label: string; value: [Dayjs, Dayjs] }> = [
{ {
label: "本月", label: "本月",
value: [dayjs().startOf("month"), dayjs().endOf("month")], value: [dayjs().startOf("month"), dayjs().endOf("month")],

View File

@ -30,7 +30,7 @@ const authSlice = createSlice({
state.roles = payload.roles; state.roles = payload.roles;
state.token = payload.token; state.token = payload.token;
localStorage.setItem("userId", payload.userId); localStorage.setItem("userId", payload.userId.toString());
localStorage.setItem("name", payload.name); localStorage.setItem("name", payload.name);
localStorage.setItem("roles", JSON.stringify(payload.roles)); localStorage.setItem("roles", JSON.stringify(payload.roles));
localStorage.setItem("token", action.payload.token); localStorage.setItem("token", action.payload.token);

View File

@ -1,11 +0,0 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axiosInstance from "../../api/axios";
export const login = createAsyncThunk(
"auth/login",
async (values, thunkAPI) => {
const res = await axiosInstance.post("/login", values);
return res;
}
);

View File

@ -0,0 +1,15 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axiosInstance from "../../api/axios";
import { LoginResponse } from "./types";
export const login = createAsyncThunk<LoginResponse>(
"auth/login",
async (values) => {
const res = await axiosInstance.post<LoginResponse, LoginResponse>(
"/login",
values
);
return res;
}
);

View File

@ -0,0 +1,6 @@
export interface LoginResponse {
userId: number;
name: string;
roles: string[];
token: string;
}

View File

@ -3,7 +3,7 @@ import { createRoot } from "react-dom/client";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { RouterProvider } from "react-router-dom"; import { RouterProvider } from "react-router-dom";
import "./index.css"; import "./index.css";
import router from "./router/index.jsx"; import router from "./router/index.js";
import { store } from "./store/index.js"; import { store } from "./store/index.js";
import dayjs from "dayjs"; import dayjs from "dayjs";
import zhCN from "antd/locale/zh_CN"; import zhCN from "antd/locale/zh_CN";

View File

@ -4,9 +4,10 @@ import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { login } from "../features/auth/authThunk"; import { login } from "../features/auth/authThunk";
import roleRoute from "../config/roleRouteConfig"; import roleRoute from "../config/roleRouteConfig";
import { store } from "store";
export default function Login() { export default function Login() {
const dispatch = useDispatch(); const dispatch = useDispatch<typeof store.dispatch>();
const navigate = useNavigate(); const navigate = useNavigate();
const onFinish = async (values) => { const onFinish = async (values) => {

View File

@ -1,7 +1,14 @@
import { message, Modal, Space, Spin, Table } from "antd"; import { message, Modal, Spin, Table } from "antd";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
interface DeviceDetailStats {
applicantName: string;
applicantTeam: string;
startDay: Date;
endDay: Date;
}
export default function DeviceDetailStatsModal({ export default function DeviceDetailStatsModal({
visible, visible,
record, record,
@ -9,12 +16,15 @@ export default function DeviceDetailStatsModal({
onClose, onClose,
}) { }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [data, setData] = useState([]); const [data, setData] = useState<DeviceDetailStats[]>([]);
const fetchData = async () => { const fetchData = useCallback(async () => {
setLoading(true); setLoading(true);
try { try {
const res = await axiosInstance.get("/device/detail-stats", { const res = await axiosInstance.get<
DeviceDetailStats[],
DeviceDetailStats[]
>("/device/detail-stats", {
params: { params: {
deviceId: record.deviceId, deviceId: record.deviceId,
start: range[0].format("YYYY-MM-DD"), start: range[0].format("YYYY-MM-DD"),
@ -22,12 +32,12 @@ export default function DeviceDetailStatsModal({
}, },
}); });
setData(res); setData(res);
} catch (e) { } catch {
message.error("获取数据失败"); message.error("获取数据失败");
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; }, [record, range]);
const handleExport = async () => { const handleExport = async () => {
setLoading(true); setLoading(true);
@ -73,7 +83,7 @@ export default function DeviceDetailStatsModal({
if (visible) { if (visible) {
fetchData(); fetchData();
} }
}, [visible, record]); }, [visible, fetchData]);
const columns = [ const columns = [
{ {

View File

@ -1,41 +1,52 @@
import { Button, DatePicker, Input, Space, Spin, Table, message } from "antd"; import { Button, DatePicker, Input, Space, Spin, Table, message } from "antd";
import dayjs from "dayjs"; import dayjs, { Dayjs } from "dayjs";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
import { datePresets } from "../../config/datePresetsConfig"; import { datePresets } from "../../config/datePresetsConfig";
import DeviceDetailStatsModal from "./DeviceDetailStatsModal"; import DeviceDetailStatsModal from "./DeviceDetailStatsModal";
interface UsageStats {
deviceId: string;
deviceName: string;
usageCount: number;
totalUsageDays: number;
}
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
export default function DeviceStats() { export default function DeviceStats() {
const [data, setData] = useState([]); const [data, setData] = useState<UsageStats[]>([]);
const [filteredData, setFilteredData] = useState([]); const [filteredData, setFilteredData] = useState<UsageStats[]>([]);
const [range, setRange] = useState([ const [range, setRange] = useState<[Dayjs, Dayjs]>([
dayjs().startOf("month"), dayjs().startOf("month"),
dayjs().endOf("month"), dayjs().endOf("month"),
]); ]);
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [visiable, setVisiable] = useState(false); const [visiable, setVisiable] = useState(false);
const [selectedRecord, setSelectedRecord] = useState(null); const [selectedRecord, setSelectedRecord] = useState(null);
const fetchData = async () => { const fetchData = useCallback(async () => {
setLoading(true); setLoading(true);
try { try {
const res = await axiosInstance.get("/device/usage-stats", { const res = await axiosInstance.get<UsageStats[], UsageStats[]>(
params: { "/device/usage-stats",
start: range[0].format("YYYY-MM-DD"), {
end: range[1].format("YYYY-MM-DD"), params: {
}, start: range[0].format("YYYY-MM-DD"),
}); end: range[1].format("YYYY-MM-DD"),
},
}
);
setData(res); setData(res);
setFilteredData(res); setFilteredData(res);
} catch (e) { } catch {
message.error("获取数据失败"); message.error("获取数据失败");
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; }, [range]);
const handleSearch = (value) => { const handleSearch = (value) => {
setSearch(value); setSearch(value);
@ -89,7 +100,7 @@ export default function DeviceStats() {
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
}, [range]); }, [fetchData]);
const columns = [ const columns = [
{ {

View File

@ -1,38 +1,50 @@
import { Button, DatePicker, Input, Space, Spin, Table, message } from "antd"; import { Button, DatePicker, Input, Space, Spin, Table, message } from "antd";
import dayjs from "dayjs"; import dayjs, { Dayjs } from "dayjs";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
import { datePresets } from "../../config/datePresetsConfig"; import { datePresets } from "../../config/datePresetsConfig";
interface ReservationStat {
deviceId: string;
deviceName: string;
applicantName: string;
applicantTeam: string;
usageCount: number;
}
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
export default function ReservationStats() { export default function ReservationStats() {
const [data, setData] = useState([]); const [data, setData] = useState<ReservationStat[]>([]);
const [filteredData, setFilteredData] = useState([]); const [filteredData, setFilteredData] = useState<ReservationStat[]>([]);
const [range, setRange] = useState([ const [range, setRange] = useState<[Dayjs, Dayjs]>([
dayjs().startOf("month"), dayjs().startOf("month"),
dayjs().endOf("month"), dayjs().endOf("month"),
]); ]);
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const fetchData = async () => { const fetchData = useCallback(async () => {
setLoading(true); setLoading(true);
try { try {
const res = await axiosInstance.get("/reservation/stats", { const res = await axiosInstance.get<ReservationStat[], ReservationStat[]>(
params: { "/reservation/stats",
start: range[0].format("YYYY-MM-DD"), {
end: range[1].format("YYYY-MM-DD"), params: {
}, start: range[0].format("YYYY-MM-DD"),
}); end: range[1].format("YYYY-MM-DD"),
},
}
);
setData(res); setData(res);
setFilteredData(res); setFilteredData(res);
} catch (e) { } catch {
message.error("获取数据失败"); message.error("获取数据失败");
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; }, [range]);
const handleSearch = (value) => { const handleSearch = (value) => {
setSearch(value); setSearch(value);
@ -86,7 +98,7 @@ export default function ReservationStats() {
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
}, [range]); }, [fetchData]);
const columns = [ const columns = [
{ {

View File

@ -1,20 +1,22 @@
import { List, Modal, Typography } from "antd"; import { List, Modal } from "antd";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
export default function TeamDetailModal({ open, team, onclose }) { export default function TeamDetailModal({ open, team, onclose }) {
const [data, setData] = useState([]); const [data, setData] = useState<string[]>([]);
const fetchData = async () => { const fetchData = useCallback(async () => {
const data = await axiosInstance.get(`/user-team/${team.id}`); const data = await axiosInstance.get<string[], string[]>(
`/user-team/${team.id}`
);
setData(data); setData(data);
}; }, [team]);
useEffect(() => { useEffect(() => {
if (open) { if (open) {
fetchData(); fetchData();
} }
}, [open, team]); }, [open, fetchData]);
return ( return (
<Modal <Modal

View File

@ -4,19 +4,20 @@ import { useEffect, useState } from "react";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
import TeamDeleteButton from "../../components/TeamDeleteButton"; import TeamDeleteButton from "../../components/TeamDeleteButton";
import TeamDetailModal from "./TeamDetailModal"; import TeamDetailModal from "./TeamDetailModal";
import { Team } from "types/model";
export default function TeamManage() { export default function TeamManage() {
const [teams, setTeams] = useState([]); const [teams, setTeams] = useState<Team[]>([]);
const [data, setData] = useState([]); const [data, setData] = useState<Team[]>([]);
const [searchName, setSearchName] = useState(); const [searchName, setSearchName] = useState<string>();
const [editingId, setEditingId] = useState(); const [editingId, setEditingId] = useState();
const [editingName, setEditingName] = useState(""); const [editingName, setEditingName] = useState("");
const [newTeamName, setNewTeamName] = useState(); const [newTeamName, setNewTeamName] = useState("");
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [selectedTeam, setSelectedTeam] = useState(null); const [selectedTeam, setSelectedTeam] = useState(null);
const fetchData = async () => { const fetchData = async () => {
const data = await axiosInstance.get("/teams"); const data = await axiosInstance.get<Team[], Team[]>("/teams");
setData(data); setData(data);
setTeams(data); setTeams(data);
setSearchName(null); setSearchName(null);

View File

@ -1,7 +1,15 @@
import { Form, Input, message, Modal, Select } from "antd"; import { Form, Input, message, Modal, Select } from "antd";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
import Password from "antd/es/input/Password";
interface UserDTO {
username: string;
name: string;
phone: string;
password?: string;
teamId: string;
roleId: string;
}
export default function UserDetailModal({ export default function UserDetailModal({
visiable, visiable,
@ -12,11 +20,14 @@ export default function UserDetailModal({
onSuccess, onSuccess,
}) { }) {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [initialValues, setInitialValues] = useState(); const [initialValues, setInitialValues] = useState<UserDTO>();
const [teams, setTeams] = useState([]); const [teams, setTeams] = useState<{ label: string; value: string }[]>();
const fetchTeams = async () => { const fetchTeams = async () => {
const data = await axiosInstance.get("/team-label"); const data = await axiosInstance.get<
unknown,
{ label: string; value: string }[]
>("/team-label");
setTeams(data); setTeams(data);
}; };
@ -27,7 +38,7 @@ export default function UserDetailModal({
useEffect(() => { useEffect(() => {
if (visiable) { if (visiable) {
if (mode === "edit") { if (mode === "edit") {
const values = { const values: UserDTO = {
username: user.username, username: user.username,
name: user.name, name: user.name,
phone: user.phone, phone: user.phone,
@ -38,7 +49,7 @@ export default function UserDetailModal({
setInitialValues(values); setInitialValues(values);
form.setFieldsValue(values); form.setFieldsValue(values);
} else { } else {
const values = { const values: UserDTO = {
username: undefined, username: undefined,
password: undefined, password: undefined,
name: undefined, name: undefined,

View File

@ -2,29 +2,33 @@ import { Button, Flex, Input, Popconfirm, Space, Table } from "antd";
import Column from "antd/es/table/Column"; import Column from "antd/es/table/Column";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
import DeviceDetailModal from "../deviceAdmin/DeviceDetailModal";
import UserDetailModal from "./UserDetailModal"; import UserDetailModal from "./UserDetailModal";
import { PageResult, Pagination } from "types/common";
import { UserVo } from "types/model";
export default function UserManage() { export default function UserManage() {
const [users, setUsers] = useState([]); const [users, setUsers] = useState([]);
const [teams, setTeams] = useState([]); // const [teams, setTeams] = useState([]);
const [modalMode, setModalMode] = useState(); const [modalMode, setModalMode] = useState<string>();
const [selectedUser, setSelectedUser] = useState(); const [selectedUser, setSelectedUser] = useState<UserVo>();
const [modalOpen, setModalOpen] = useState(false); const [modalOpen, setModalOpen] = useState(false);
const [roles, setRoles] = useState([]); const [roles, setRoles] = useState<{ label: string; value: string }[]>([]);
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState<Pagination>({
current: 1, current: 1,
pageSize: 10, pageSize: 10,
total: 0, total: 0,
}); });
const fetchRoles = async () => { const fetchRoles = async () => {
const data = await axiosInstance.get("/role"); const data = await axiosInstance.get<
unknown,
{ label: string; value: string }[]
>("/role");
setRoles(data); setRoles(data);
}; };
const fetchData = async (pagination, name) => { const fetchData = async (pagination: Pagination, name?: string) => {
const data = await axiosInstance.get("/user", { const data = await axiosInstance.get<unknown, PageResult<UserVo>>("/user", {
params: { params: {
page: pagination.current, page: pagination.current,
size: pagination.pageSize, size: pagination.pageSize,
@ -108,7 +112,7 @@ export default function UserManage() {
/> />
<Column <Column
title="操作" title="操作"
render={(_, record) => { render={(_, record: UserVo) => {
return ( return (
<Space> <Space>
<Button <Button

View File

@ -15,6 +15,7 @@ import { useSelector } from "react-redux";
import axiosInstance, { baseURL } from "../../api/axios"; import axiosInstance, { baseURL } from "../../api/axios";
import { deviceStatusOptions } from "../../config/DeviceStatusConfig"; import { deviceStatusOptions } from "../../config/DeviceStatusConfig";
import { selectUserId } from "../../features/auth/authSlice"; import { selectUserId } from "../../features/auth/authSlice";
import { UploadFile } from "antd/lib";
export default function DeviceDetailModal({ export default function DeviceDetailModal({
visiable, visiable,
@ -26,7 +27,7 @@ export default function DeviceDetailModal({
const [form] = Form.useForm(); const [form] = Form.useForm();
const [imageFile, setImageFile] = useState(null); const [imageFile, setImageFile] = useState(null);
const [initialValues, setInitialValues] = useState({}); const [initialValues, setInitialValues] = useState({});
const [fileList, setFileList] = useState(); const [fileList, setFileList] = useState<UploadFile[]>();
const userId = useSelector(selectUserId); const userId = useSelector(selectUserId);
useEffect(() => { useEffect(() => {
if (visiable) { if (visiable) {

View File

@ -9,45 +9,61 @@ import {
Tag, Tag,
} from "antd"; } from "antd";
import Column from "antd/es/table/Column"; import Column from "antd/es/table/Column";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
import { deviceStatusOptions } from "../../config/DeviceStatusConfig"; import { deviceStatusOptions } from "../../config/DeviceStatusConfig";
import { selectUserId } from "../../features/auth/authSlice"; import { selectUserId } from "../../features/auth/authSlice";
import DeviceDetailModal from "./DeviceDetailModal"; import DeviceDetailModal from "./DeviceDetailModal";
import { PageResult, Pagination } from "types/common";
interface DeviceAdminVO {
deviceId: string;
name: string;
usageRequirement: string;
location: string;
imagePath: string;
status: string;
}
export default function DeviceManage() { export default function DeviceManage() {
const [devices, setDevices] = useState([]); const [devices, setDevices] = useState([]);
const [selectedDevice, setSelectedDevice] = useState(null); const [selectedDevice, setSelectedDevice] = useState(null);
const [modalOpen, setModalOpen] = useState(false); const [modalOpen, setModalOpen] = useState(false);
const [modalMode, setModalMode] = useState(null); const [modalMode, setModalMode] = useState(null);
const [searchName, setSearchName] = useState(null); const [searchName, setSearchName] = useState<string>(null);
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState<Pagination>({
current: 1, current: 1,
pageSize: 10, pageSize: 10,
total: 0, total: 0,
}); });
const userId = useSelector(selectUserId); const userId = useSelector(selectUserId);
const fetchData = async (pagination, name = searchName) => { const fetchData = useCallback(
const data = await axiosInstance.get(`/device/${userId}`, { async (pagination: Pagination, name: string = searchName) => {
params: { const data = await axiosInstance.get<unknown, PageResult<DeviceAdminVO>>(
page: pagination.current, `/device/${userId}`,
size: pagination.pageSize, {
name, params: {
}, page: pagination.current,
}); size: pagination.pageSize,
name,
},
}
);
setDevices(data.records); setDevices(data.records);
setPagination({ setPagination({
...pagination, ...pagination,
total: data.total, total: data.total,
}); });
}; },
[userId, searchName]
);
useEffect(() => { useEffect(() => {
fetchData(pagination); fetchData(pagination);
}, []); }, [fetchData, pagination]);
const handlePageChange = async (pagination) => { const handlePageChange = async (pagination) => {
await fetchData(pagination); await fetchData(pagination);

View File

@ -1,10 +1,22 @@
import { Button, message, Space, Table } from "antd"; import { Button, message, Space, Table } from "antd";
import Column from "antd/es/table/Column"; import Column from "antd/es/table/Column";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
import { selectUserId } from "../../features/auth/authSlice"; import { selectUserId } from "../../features/auth/authSlice";
import { selectUserRole } from "../../features/auth/authSlice"; import { selectUserRole } from "../../features/auth/authSlice";
import { PageResult, Pagination } from "types/common";
interface ReservationVO {
reservationId: string;
applicantName: string;
applicantTeam: string;
applicantContact: string;
deviceId: string;
deviceName: string;
startTime: Date;
endTime: Date;
}
export default function Approval() { export default function Approval() {
const [reservations, setReservations] = useState([]); const [reservations, setReservations] = useState([]);
@ -21,24 +33,30 @@ export default function Approval() {
showNeedAssist = true; showNeedAssist = true;
} }
const fetchData = async (pagination) => { const fetchData = useCallback(
const data = await axiosInstance.get(`/reservation/approval/${userId}`, { async (pagination: Pagination) => {
params: { const data = await axiosInstance.get<unknown, PageResult<ReservationVO>>(
page: pagination.current, `/reservation/approval/${userId}`,
size: pagination.pageSize, {
}, params: {
}); page: pagination.current,
size: pagination.pageSize,
},
}
);
setReservations(data.records); setReservations(data.records);
setPagination({ setPagination({
...pagination, ...pagination,
total: data.total, total: data.total,
}); });
}; },
[userId]
);
useEffect(() => { useEffect(() => {
fetchData(pagination); fetchData(pagination);
}, []); }, [fetchData, pagination]);
const handlePageChange = async (pagination) => { const handlePageChange = async (pagination) => {
await fetchData(pagination); await fetchData(pagination);

View File

@ -1,11 +1,25 @@
import { Button, DatePicker, Form, Input, message, Table, Tag } from "antd"; import { Button, DatePicker, Form, Input, message, Table, Tag } from "antd";
import { useForm } from "antd/es/form/Form"; import { useForm } from "antd/es/form/Form";
import Column from "antd/es/table/Column"; import Column from "antd/es/table/Column";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
import { selectUserId, selectUserRole } from "../../features/auth/authSlice"; import { selectUserId, selectUserRole } from "../../features/auth/authSlice";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { PageResult, Pagination } from "types/common";
interface ApprovalVO {
reservationId: string;
approvalId: string;
applicantName: string;
applicantTeam: string;
applicantContact: string;
deviceName: string;
startTime: Date;
endTime: Date;
decision: number;
status: string;
}
export default function MyApproval() { export default function MyApproval() {
const [approvals, setApprovals] = useState([]); const [approvals, setApprovals] = useState([]);
@ -21,26 +35,35 @@ export default function MyApproval() {
const userId = useSelector(selectUserId); const userId = useSelector(selectUserId);
const userRole = useSelector(selectUserRole); const userRole = useSelector(selectUserRole);
const fetchData = async (pagination, searchParam) => { const fetchData = useCallback(
const data = await axiosInstance.get(`/approval/${userId}`, { async (
params: { pagination: Pagination,
page: pagination.current, searchParam?: { applicantName: string; deviceName: string }
size: pagination.pageSize, ) => {
applicantName: searchParam?.applicantName, const data = await axiosInstance.get<unknown, PageResult<ApprovalVO>>(
deviceName: searchParam?.deviceName, `/approval/${userId}`,
}, {
}); params: {
page: pagination.current,
size: pagination.pageSize,
applicantName: searchParam?.applicantName,
deviceName: searchParam?.deviceName,
},
}
);
setApprovals(data.records); setApprovals(data.records);
setPagination({ setPagination({
...pagination, ...pagination,
total: data.total, total: data.total,
}); });
}; },
[userId]
);
useEffect(() => { useEffect(() => {
fetchData(pagination); fetchData(pagination);
}, []); }, [fetchData, pagination]);
const handlePageChange = async (pagination) => { const handlePageChange = async (pagination) => {
const values = await form.validateFields(); const values = await form.validateFields();
@ -66,7 +89,7 @@ export default function MyApproval() {
await fetchData(pagination, values); await fetchData(pagination, values);
setEditingRow(null); setEditingRow(null);
message.success("修改成功"); message.success("修改成功");
} catch (error) { } catch {
message.error("修改失败"); message.error("修改失败");
} }
}; };

View File

@ -1,14 +1,14 @@
import { Button, Col, Form, Input, message, Row } from "antd"; import { Button, Col, Form, Input, message, Row } from "antd";
import { useForm } from "antd/es/form/Form"; import { useCallback, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
import { selectUserId } from "../../features/auth/authSlice"; import { selectUserId } from "../../features/auth/authSlice";
import { UserVo } from "types/model";
export default function UserDetail() { export default function UserDetail() {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const [user, setUser] = useState({ const [user, setUser] = useState<UserVo>({
userId: "", userId: "",
username: "", username: "",
team: "", team: "",
@ -17,15 +17,20 @@ export default function UserDetail() {
}); });
const userId = useSelector(selectUserId); const userId = useSelector(selectUserId);
const fetchUser = async (userId) => { const fetchUser = useCallback(
const user = await axiosInstance.get(`/userdetail/${userId}`); async (userId: string) => {
setUser(user); const user = await axiosInstance.get<unknown, UserVo>(
form.setFieldsValue(user); `/userdetail/${userId}`
}; );
setUser(user);
form.setFieldsValue(user);
},
[form]
);
useEffect(() => { useEffect(() => {
fetchUser(userId); fetchUser(userId);
}, []); }, [fetchUser, userId]);
const handleReset = () => { const handleReset = () => {
form.resetFields(); form.resetFields();
@ -36,7 +41,7 @@ export default function UserDetail() {
message.error("两次输入的密码不一致"); message.error("两次输入的密码不一致");
return; return;
} }
const changedFields = {}; const changedFields: Record<string, string> = {};
for (const key in values) { for (const key in values) {
if (values[key] !== user[key] && values[key] !== undefined) { if (values[key] !== user[key] && values[key] !== undefined) {
changedFields[key] = values[key]; changedFields[key] = values[key];
@ -44,7 +49,10 @@ export default function UserDetail() {
} }
delete changedFields.confirmPassword; delete changedFields.confirmPassword;
const newUser = await axiosInstance.put(`/user/${userId}`, changedFields); const newUser = await axiosInstance.put<unknown, UserVo>(
`/user/${userId}`,
changedFields
);
setUser(newUser); setUser(newUser);
form.setFieldsValue(newUser); form.setFieldsValue(newUser);
message.success("修改成功"); message.success("修改成功");

View File

@ -10,21 +10,32 @@ import {
Select, Select,
Space, Space,
} from "antd"; } from "antd";
import dayjs from "dayjs"; import dayjs, { Dayjs } from "dayjs";
import isBetween from "dayjs/plugin/isBetween"; import isBetween from "dayjs/plugin/isBetween";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import axiosInstance, { baseURL } from "../../api/axios"; import axiosInstance, { baseURL } from "../../api/axios";
import { selectUserId } from "../../features/auth/authSlice";
import { UserVo } from "types/model";
interface FormValue {
name: string;
phone: string;
team: string;
}
export default function DeviceDetailModal({ visiable, device, onclose }) { export default function DeviceDetailModal({ visiable, device, onclose }) {
const [unavailableTimes, setUnavailableTims] = useState([]); const [unavailableTimes, setUnavailableTimes] = useState([]);
const [form] = Form.useForm(); const [form] = Form.useForm();
const userId = useSelector((state) => state.auth.userId); const userId = useSelector(selectUserId);
const [teams, setTeams] = useState([]); const [teams, setTeams] = useState([]);
const [initialValues, setInitialValues] = useState(); const [initialValues, setInitialValues] = useState<FormValue>();
const fetchTeams = async () => { const fetchTeams = async () => {
const data = await axiosInstance.get("/team-label"); const data = await axiosInstance.get<
unknown,
{ label: string; value: string }[]
>("/team-label");
const teams = data.map((item) => ({ const teams = data.map((item) => ({
label: item.label, label: item.label,
value: item.label, value: item.label,
@ -32,8 +43,10 @@ export default function DeviceDetailModal({ visiable, device, onclose }) {
setTeams(teams); setTeams(teams);
}; };
const fetchUser = async () => { const fetchUser = useCallback(async () => {
const data = await axiosInstance.get(`/userdetail/${userId}`); const data = await axiosInstance.get<unknown, UserVo>(
`/userdetail/${userId}`
);
const values = { const values = {
name: data.name, name: data.name,
phone: data.phone, phone: data.phone,
@ -41,7 +54,7 @@ export default function DeviceDetailModal({ visiable, device, onclose }) {
}; };
setInitialValues(values); setInitialValues(values);
form.setFieldsValue(values); form.setFieldsValue(values);
}; }, [userId, form]);
useEffect(() => { useEffect(() => {
fetchTeams(); fetchTeams();
@ -49,20 +62,23 @@ export default function DeviceDetailModal({ visiable, device, onclose }) {
useEffect(() => { useEffect(() => {
const fetchUnavailableTimes = async (id) => { const fetchUnavailableTimes = async (id) => {
const data = await axiosInstance.get(`/device/unavailable-times/${id}`); const data = await axiosInstance.get<
setUnavailableTims(data); unknown,
{ startTime: Date; endTime: Date }[]
>(`/device/unavailable-times/${id}`);
setUnavailableTimes(data);
}; };
if (visiable && device?.deviceId) { if (visiable && device?.deviceId) {
fetchUnavailableTimes(device.deviceId); fetchUnavailableTimes(device.deviceId);
fetchUser(); fetchUser();
} }
}, [visiable, device?.deviceId]); }, [visiable, device?.deviceId, fetchUser]);
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
const disabledDate = (current, { from } = {}) => { const disabledDate = (current: Dayjs, info: { from?: Dayjs } = {}) => {
if (!current) return false; if (!current) return false;
const { from } = info;
const today = dayjs().startOf("day"); const today = dayjs().startOf("day");
const currentDay = current.startOf("day"); const currentDay = current.startOf("day");

View File

@ -1,10 +1,24 @@
import { Table, Tag } from "antd"; import { Table, Tag } from "antd";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import Column from "antd/es/table/Column"; import Column from "antd/es/table/Column";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { selectUserId } from "../../features/auth/authSlice"; import { selectUserId } from "../../features/auth/authSlice";
import { PageResult } from "types/common";
interface UserReservationVO {
reservationId: string;
deviceName: string;
startTime: Date;
endTime: Date;
statusLabel: string;
deviceLeaderName: string;
deviceLeaderContact: string;
deviceAdminName: string;
deviceAdminContact: string;
createdTime: Date;
}
export default function MyReservation() { export default function MyReservation() {
const [reservations, setReservations] = useState([]); const [reservations, setReservations] = useState([]);
@ -16,23 +30,29 @@ export default function MyReservation() {
const userId = useSelector(selectUserId); const userId = useSelector(selectUserId);
const fetchData = async (pagination) => { const fetchData = useCallback(
const data = await axiosInstance.get(`/reservation/${userId}`, { async (pagination) => {
params: { const data = await axiosInstance.get<
page: pagination.current, unknown,
size: pagination.pageSize, PageResult<UserReservationVO>
}, >(`/reservation/${userId}`, {
}); params: {
setReservations(data.records); page: pagination.current,
setPagination({ size: pagination.pageSize,
...pagination, },
total: data.total, });
}); setReservations(data.records);
}; setPagination({
...pagination,
total: data.total,
});
},
[userId]
);
useEffect(() => { useEffect(() => {
fetchData(pagination); fetchData(pagination);
}, []); }, [fetchData, pagination]);
const handlePageChange = (pagination) => { const handlePageChange = (pagination) => {
fetchData(pagination); fetchData(pagination);

View File

@ -1,8 +1,9 @@
import { Input, Space, Table, Tag } from "antd"; import { Input, Space, Table, Tag } from "antd";
import Column from "antd/es/table/Column"; import Column from "antd/es/table/Column";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import axiosInstance from "../../api/axios"; import axiosInstance from "../../api/axios";
import DeviceDetailModal from "./DeviceDetailModal"; import DeviceDetailModal from "./DeviceDetailModal";
import { PageResult } from "types/common";
const statusColorMap = { const statusColorMap = {
: "green", : "green",
@ -11,6 +12,15 @@ const statusColorMap = {
: "gray", : "gray",
}; };
interface DeviceVO {
deviceId: string;
name: string;
usageRequirement: string;
location: string;
imagePath: string;
state: string;
}
export default function Reserve() { export default function Reserve() {
const [name, setName] = useState(null); const [name, setName] = useState(null);
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState({
@ -20,24 +30,31 @@ export default function Reserve() {
}); });
const [devices, setDevices] = useState([]); const [devices, setDevices] = useState([]);
const fetchData = async (pagination, searchName = name) => { const fetchData = useCallback(
const data = await axiosInstance.get("/device", { async (pagination, searchName = name) => {
params: { const data = await axiosInstance.get<unknown, PageResult<DeviceVO>>(
page: pagination.current, "/device",
size: pagination.pageSize, {
name: searchName, params: {
}, page: pagination.current,
}); size: pagination.pageSize,
name: searchName,
},
}
);
setDevices(data.records);
setPagination({
...pagination,
total: data.total,
});
},
[name]
);
setDevices(data.records);
setPagination({
...pagination,
total: data.total,
});
};
useEffect(() => { useEffect(() => {
fetchData(pagination); fetchData(pagination);
}, []); }, [fetchData, pagination]);
const handlePageChange = (pagination) => { const handlePageChange = (pagination) => {
fetchData(pagination); fetchData(pagination);
@ -74,7 +91,7 @@ export default function Reserve() {
title="使用要求" title="使用要求"
key="usageRequirement" key="usageRequirement"
dataIndex="usageRequirement" dataIndex="usageRequirement"
ellipsis="true" ellipsis={true}
/> />
<Column title="位置" key="location" dataIndex="location" /> <Column title="位置" key="location" dataIndex="location" />
<Column <Column

7
src/types/axios.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
import "axios";
declare module "axios" {
export interface AxiosRequestConfig {
skipInterceptor?: boolean;
}
}

12
src/types/common.ts Normal file
View File

@ -0,0 +1,12 @@
export interface PageResult<T> {
records: T[];
total: number;
size: number;
current: number;
}
export interface Pagination {
current: number;
pageSize: number;
total?: number;
}

15
src/types/model.ts Normal file
View File

@ -0,0 +1,15 @@
export interface Team {
id: string;
name: string;
size: number;
}
export interface UserVo {
userId: string;
username: string;
team: string;
teamId?: string;
name: string;
phone: string;
roleId?: string;
}

1
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />