feat: 实现普通用户预约设备
This commit is contained in:
parent
b0ea73befb
commit
4dfe6b491e
1
package-lock.json
generated
1
package-lock.json
generated
@ -13,6 +13,7 @@
|
||||
"@tailwindcss/vite": "^4.1.10",
|
||||
"antd": "^5.26.1",
|
||||
"axios": "^1.10.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-redux": "^9.2.0",
|
||||
|
@ -15,6 +15,7 @@
|
||||
"@tailwindcss/vite": "^4.1.10",
|
||||
"antd": "^5.26.1",
|
||||
"axios": "^1.10.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-redux": "^9.2.0",
|
||||
|
@ -2,8 +2,10 @@ import { message } from "antd";
|
||||
import axios from "axios";
|
||||
import { logout } from "../features/auth/authSlice";
|
||||
|
||||
const baseURL = "http://127.0.0.1:8080";
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: "http://127.0.0.1:8080",
|
||||
baseURL: baseURL,
|
||||
timeout: 2000, // 2秒
|
||||
});
|
||||
|
||||
@ -42,4 +44,5 @@ axiosInstance.interceptors.response.use(
|
||||
}
|
||||
);
|
||||
|
||||
export { baseURL };
|
||||
export default axiosInstance;
|
||||
|
13
src/main.jsx
13
src/main.jsx
@ -5,11 +5,18 @@ import { RouterProvider } from "react-router-dom";
|
||||
import "./index.css";
|
||||
import router from "./router/index.jsx";
|
||||
import { store } from "./store/index.js";
|
||||
import dayjs from "dayjs";
|
||||
import zhCN from "antd/locale/zh_CN";
|
||||
import { ConfigProvider } from "antd";
|
||||
|
||||
dayjs.locale("zh-cn");
|
||||
|
||||
createRoot(document.getElementById("root")).render(
|
||||
<StrictMode>
|
||||
<Provider store={store}>
|
||||
<RouterProvider router={router} />
|
||||
</Provider>
|
||||
<ConfigProvider locale={zhCN}>
|
||||
<Provider store={store}>
|
||||
<RouterProvider router={router} />
|
||||
</Provider>
|
||||
</ConfigProvider>
|
||||
</StrictMode>
|
||||
);
|
||||
|
104
src/pages/user/DeviceDetailModal.jsx
Normal file
104
src/pages/user/DeviceDetailModal.jsx
Normal file
@ -0,0 +1,104 @@
|
||||
import {
|
||||
DatePicker,
|
||||
Descriptions,
|
||||
Form,
|
||||
Image,
|
||||
message,
|
||||
Modal,
|
||||
Space,
|
||||
} from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import axiosInstance, { baseURL } from "../../api/axios";
|
||||
import { useEffect, useState } from "react";
|
||||
import isBetween from "dayjs/plugin/isBetween";
|
||||
import { store } from "../../store";
|
||||
import { useSelector, useStore } from "react-redux";
|
||||
|
||||
export default function DeviceDetailModal({ visiable, device, onclose }) {
|
||||
const [unavailableTimes, setUnavailableTims] = useState([]);
|
||||
useEffect(() => {
|
||||
const fetchUnavailableTimes = async (id) => {
|
||||
const data = await axiosInstance.get(`/device/unavailable-times/${id}`);
|
||||
setUnavailableTims(data);
|
||||
};
|
||||
|
||||
if (visiable && device?.deviceId) {
|
||||
fetchUnavailableTimes(device.deviceId);
|
||||
}
|
||||
}, [visiable, device?.deviceId]);
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
const disabledDate = (current) => {
|
||||
if (!current) return false;
|
||||
const today = dayjs().startOf("day");
|
||||
const currentDay = current.startOf("day");
|
||||
if (currentDay.isBefore(today)) {
|
||||
return true;
|
||||
}
|
||||
dayjs.extend(isBetween);
|
||||
|
||||
return unavailableTimes.some(({ startTime, endTime }) => {
|
||||
const start = dayjs(startTime).startOf("day");
|
||||
const end = dayjs(endTime).endOf("day");
|
||||
return currentDay.isBetween(start, end, null, "[]");
|
||||
});
|
||||
};
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const userId = useSelector((state) => state.auth.userId);
|
||||
|
||||
const handleOK = async () => {
|
||||
const values = await form.validateFields();
|
||||
|
||||
const [startTime, endTime] = values.date;
|
||||
|
||||
const payload = {
|
||||
deviceId: device.deviceId,
|
||||
userId,
|
||||
startTime: startTime.format("YYYY-MM-DD"),
|
||||
endTime: endTime.format("YYYY-MM-DD"),
|
||||
};
|
||||
console.log(payload);
|
||||
|
||||
await axiosInstance.post("/reservation", payload);
|
||||
message.success("预约成功");
|
||||
form.resetFields();
|
||||
onclose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="预约设备"
|
||||
open={visiable}
|
||||
onCancel={() => {
|
||||
onclose();
|
||||
}}
|
||||
onOk={handleOK}
|
||||
>
|
||||
<Space direction="vertical">
|
||||
<Descriptions column={1}>
|
||||
<Descriptions.Item label="设备名称">{device?.name}</Descriptions.Item>
|
||||
<Descriptions.Item label="使用要求">
|
||||
{device?.usageRequirement}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="设备位置">
|
||||
{device?.location}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="设备图片">
|
||||
<Image src={`${baseURL}/${device?.imagePath}`} />
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<Form form={form} layout="inline" className="mt-14">
|
||||
<Form.Item
|
||||
name="date"
|
||||
label="预约日期"
|
||||
rules={[{ required: true, message: "请选择预约日期" }]}
|
||||
>
|
||||
<RangePicker disabledDate={disabledDate} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Space>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -2,6 +2,7 @@ import { Input, Space, Table, Tag } from "antd";
|
||||
import Column from "antd/es/table/Column";
|
||||
import { useEffect, useState } from "react";
|
||||
import axiosInstance from "../../api/axios";
|
||||
import DeviceDetailModal from "./DeviceDetailModal";
|
||||
|
||||
const statusColorMap = {
|
||||
空闲: "green",
|
||||
@ -100,11 +101,11 @@ export default function Reserve() {
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
{/* <ReservationModal
|
||||
<DeviceDetailModal
|
||||
visiable={!!selectedDevice}
|
||||
device={selectedDevice}
|
||||
onclose={() => setSelectedDevice(null)}
|
||||
/> */}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user