chore: commit oxfmt formatting changes and verify artifacts

This commit is contained in:
2026-07-02 15:55:09 +08:00
parent 34726cc46d
commit 4f4cee157a
77 changed files with 5576 additions and 1400 deletions

View File

@@ -1,5 +1,18 @@
import React, { useEffect, useState, useMemo } from 'react';
import { DatePicker, Card, Row, Col, Statistic, Tag, Space, Button, Modal, Spin, Empty, Tooltip } from 'antd';
import {
DatePicker,
Card,
Row,
Col,
Statistic,
Tag,
Space,
Button,
Modal,
Spin,
Empty,
Tooltip,
} from 'antd';
import { CalendarOutlined, FileTextOutlined } from '@ant-design/icons';
import dayjs, { Dayjs } from 'dayjs';
import api from '../../api';
@@ -11,7 +24,10 @@ interface ScheduleData {
classrooms: any[];
tenants: any[];
matrix: Record<number, Record<number, any>>;
summary: Record<number, { totalDays: number; rentedDays: number; idleDays: number; occupancyRate: number }>;
summary: Record<
number,
{ totalDays: number; rentedDays: number; idleDays: number; occupancyRate: number }
>;
}
const ClassroomSchedulePage: React.FC = () => {
@@ -27,11 +43,15 @@ const ClassroomSchedulePage: React.FC = () => {
params: { year: month.year(), month: month.month() + 1 },
});
setData(res);
} catch (e) { console.error(e); }
} catch (e) {
console.error(e);
}
setLoading(false);
};
useEffect(() => { fetchData(); }, [month]);
useEffect(() => {
fetchData();
}, [month]);
// 按楼栋+楼层分组教室
const groups = useMemo(() => {
@@ -64,15 +84,21 @@ const ClassroomSchedulePage: React.FC = () => {
try {
const res: any = await api.get(`/classroom-rentals/${rentalId}`);
setDetailModal(res);
} catch (e) { console.error(e); }
} catch (e) {
console.error(e);
}
};
const handleDownloadContract = (id: number, filename?: string) => {
const baseURL = import.meta.env.PROD ? '/api' : `http://localhost:${import.meta.env.VITE_API_PORT || 3002}/api`;
const baseURL = import.meta.env.PROD
? '/api'
: `http://localhost:${import.meta.env.VITE_API_PORT || 3002}/api`;
const token = localStorage.getItem('token');
fetch(`${baseURL}/classroom-rentals/${id}/contract`, { headers: { Authorization: `Bearer ${token}` } })
.then(res => res.blob())
.then(blob => {
fetch(`${baseURL}/classroom-rentals/${id}/contract`, {
headers: { Authorization: `Bearer ${token}` },
})
.then((res) => res.blob())
.then((blob) => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
@@ -84,33 +110,80 @@ const ClassroomSchedulePage: React.FC = () => {
return (
<div>
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 }}>
<div
style={{
marginBottom: 16,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
flexWrap: 'wrap',
gap: 8,
}}
>
<Space>
<CalendarOutlined style={{ fontSize: 20 }} />
<h3 style={{ margin: 0 }}></h3>
</Space>
<Space>
<Button onClick={() => setMonth(month.subtract(1, 'month'))}></Button>
<DatePicker picker="month" value={month} onChange={(v) => v && setMonth(v)} allowClear={false} placeholder="选择月份" format="YYYY年M月" />
<DatePicker
picker="month"
value={month}
onChange={(v) => v && setMonth(v)}
allowClear={false}
placeholder="选择月份"
format="YYYY年M月"
/>
<Button onClick={() => setMonth(month.add(1, 'month'))}></Button>
<Button type="primary" onClick={() => setMonth(dayjs())}></Button>
<Button type="primary" onClick={() => setMonth(dayjs())}>
</Button>
</Space>
</div>
{/* 统计卡片 */}
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col xs={12} sm={6}><Card size="small"><Statistic title="教室总数" value={data?.classrooms.length || 0} /></Card></Col>
<Col xs={12} sm={6}><Card size="small"><Statistic title="本月天数" value={data?.days || 0} /></Card></Col>
<Col xs={12} sm={6}><Card size="small"><Statistic title="总占用天数" value={overall.rented} suffix={`/${overall.total}`} /></Card></Col>
<Col xs={12} sm={6}><Card size="small"><Statistic title="整体占用率" value={overall.rate} suffix="%" valueStyle={{ color: overall.rate > 70 ? '#cf1322' : overall.rate > 40 ? '#fa8c16' : '#3f8600' }} /></Card></Col>
<Col xs={12} sm={6}>
<Card size="small">
<Statistic title="教室总数" value={data?.classrooms.length || 0} />
</Card>
</Col>
<Col xs={12} sm={6}>
<Card size="small">
<Statistic title="本月天数" value={data?.days || 0} />
</Card>
</Col>
<Col xs={12} sm={6}>
<Card size="small">
<Statistic title="总占用天数" value={overall.rented} suffix={`/${overall.total}`} />
</Card>
</Col>
<Col xs={12} sm={6}>
<Card size="small">
<Statistic
title="整体占用率"
value={overall.rate}
suffix="%"
valueStyle={{
color: overall.rate > 70 ? '#cf1322' : overall.rate > 40 ? '#fa8c16' : '#3f8600',
}}
/>
</Card>
</Col>
</Row>
{/* 租赁方图例 */}
{data && data.tenants.length > 0 && (
<Card size="small" style={{ marginBottom: 16 }} title="租赁方图例">
<Space wrap>
{data.tenants.map(t => (
<Tag key={t.id} color={t.color} style={{ background: t.color, color: '#fff', borderColor: t.color }}>{t.name}</Tag>
{data.tenants.map((t) => (
<Tag
key={t.id}
color={t.color}
style={{ background: t.color, color: '#fff', borderColor: t.color }}
>
{t.name}
</Tag>
))}
</Space>
</Card>
@@ -121,7 +194,7 @@ const ClassroomSchedulePage: React.FC = () => {
<Empty description="暂无教室数据" />
) : (
<div style={{ overflowX: 'auto' }}>
{groups.map(group => (
{groups.map((group) => (
<Card
key={group.name}
size="small"
@@ -132,25 +205,88 @@ const ClassroomSchedulePage: React.FC = () => {
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12 }}>
<thead>
<tr style={{ background: '#fafafa' }}>
<th style={{ position: 'sticky', left: 0, background: '#fafafa', zIndex: 2, padding: '8px', border: '1px solid #f0f0f0', minWidth: 120, textAlign: 'left' }}></th>
<th style={{ padding: '8px 6px', border: '1px solid #f0f0f0', minWidth: 60 }}></th>
<th style={{ padding: '8px 6px', border: '1px solid #f0f0f0', minWidth: 70 }}></th>
{Array.from({ length: data.days }, (_, i) => i + 1).map(d => (
<th key={d} style={{ padding: '8px 4px', border: '1px solid #f0f0f0', minWidth: 26, textAlign: 'center' }}>{d}</th>
<th
style={{
position: 'sticky',
left: 0,
background: '#fafafa',
zIndex: 2,
padding: '8px',
border: '1px solid #f0f0f0',
minWidth: 120,
textAlign: 'left',
}}
>
</th>
<th style={{ padding: '8px 6px', border: '1px solid #f0f0f0', minWidth: 60 }}>
</th>
<th style={{ padding: '8px 6px', border: '1px solid #f0f0f0', minWidth: 70 }}>
</th>
{Array.from({ length: data.days }, (_, i) => i + 1).map((d) => (
<th
key={d}
style={{
padding: '8px 4px',
border: '1px solid #f0f0f0',
minWidth: 26,
textAlign: 'center',
}}
>
{d}
</th>
))}
</tr>
</thead>
<tbody>
{group.classrooms.map(c => {
const sum = data.summary[c.id] || { rentedDays: 0, totalDays: data.days, occupancyRate: 0 };
{group.classrooms.map((c) => {
const sum = data.summary[c.id] || {
rentedDays: 0,
totalDays: data.days,
occupancyRate: 0,
};
return (
<tr key={c.id}>
<td style={{ position: 'sticky', left: 0, background: '#fff', zIndex: 1, padding: '6px 8px', border: '1px solid #f0f0f0', fontWeight: 500 }}>{c.name}</td>
<td style={{ padding: '6px', border: '1px solid #f0f0f0', textAlign: 'center' }}>{c.roomType}</td>
<td style={{ padding: '6px', border: '1px solid #f0f0f0', textAlign: 'center', color: sum.occupancyRate > 0.7 ? '#cf1322' : sum.occupancyRate > 0.4 ? '#fa8c16' : '#3f8600' }}>
<td
style={{
position: 'sticky',
left: 0,
background: '#fff',
zIndex: 1,
padding: '6px 8px',
border: '1px solid #f0f0f0',
fontWeight: 500,
}}
>
{c.name}
</td>
<td
style={{
padding: '6px',
border: '1px solid #f0f0f0',
textAlign: 'center',
}}
>
{c.roomType}
</td>
<td
style={{
padding: '6px',
border: '1px solid #f0f0f0',
textAlign: 'center',
color:
sum.occupancyRate > 0.7
? '#cf1322'
: sum.occupancyRate > 0.4
? '#fa8c16'
: '#3f8600',
}}
>
{Math.round(sum.occupancyRate * 100)}%
</td>
{Array.from({ length: data.days }, (_, i) => i + 1).map(d => {
{Array.from({ length: data.days }, (_, i) => i + 1).map((d) => {
const cell = data.matrix[c.id]?.[d];
return (
<td
@@ -166,7 +302,9 @@ const ClassroomSchedulePage: React.FC = () => {
}}
>
{cell && (
<Tooltip title={`${cell.tenantName}${cell.hasContract ? ' · 有合同' : ''}`}>
<Tooltip
title={`${cell.tenantName}${cell.hasContract ? ' · 有合同' : ''}`}
>
<span style={{ color: '#fff', fontSize: 10, fontWeight: 600 }}>
{cell.hasContract ? '📄' : ''}
</span>
@@ -195,28 +333,64 @@ const ClassroomSchedulePage: React.FC = () => {
>
{detailModal && (
<div style={{ lineHeight: 2 }}>
<div><strong></strong>{detailModal.classroom?.building} · {detailModal.classroom?.name}{detailModal.classroom?.roomType}</div>
<div><strong></strong>
<Tag color={detailModal.tenant?.color} style={{ background: detailModal.tenant?.color, color: '#fff', borderColor: detailModal.tenant?.color }}>
<div>
<strong></strong>
{detailModal.classroom?.building} · {detailModal.classroom?.name}
{detailModal.classroom?.roomType}
</div>
<div>
<strong></strong>
<Tag
color={detailModal.tenant?.color}
style={{
background: detailModal.tenant?.color,
color: '#fff',
borderColor: detailModal.tenant?.color,
}}
>
{detailModal.tenant?.name}
</Tag>
</div>
<div><strong></strong>{detailModal.tenant?.contact || '-'} {detailModal.tenant?.phone || ''}</div>
<div><strong></strong>{detailModal.startDate} ~ {detailModal.endDate}{dayjs(detailModal.endDate).diff(dayjs(detailModal.startDate), 'day') + 1}</div>
{detailModal.dailyRate && <div><strong></strong>¥{detailModal.dailyRate}</div>}
{detailModal.totalAmount && <div><strong></strong>¥{detailModal.totalAmount}</div>}
{detailModal.notes && <div><strong></strong>{detailModal.notes}</div>}
<div>
<strong></strong>
{detailModal.tenant?.contact || '-'} {detailModal.tenant?.phone || ''}
</div>
<div>
<strong></strong>
{detailModal.startDate} ~ {detailModal.endDate}
{dayjs(detailModal.endDate).diff(dayjs(detailModal.startDate), 'day') + 1}
</div>
{detailModal.dailyRate && (
<div>
<strong></strong>¥{detailModal.dailyRate}
</div>
)}
{detailModal.totalAmount && (
<div>
<strong></strong>¥{detailModal.totalAmount}
</div>
)}
{detailModal.notes && (
<div>
<strong></strong>
{detailModal.notes}
</div>
)}
<div style={{ marginTop: 12 }}>
<strong></strong>
{detailModal.contractPath ? (
<Button
type="link"
icon={<FileTextOutlined />}
onClick={() => handleDownloadContract(detailModal.id, detailModal.contractOriginalName)}
onClick={() =>
handleDownloadContract(detailModal.id, detailModal.contractOriginalName)
}
>
{detailModal.contractOriginalName || '下载'}
</Button>
) : '未上传'}
) : (
'未上传'
)}
</div>
</div>
)}