[PHP] 纯文本查看 复制代码
<?php
date_default_timezone_set('Asia/Shanghai');
require_once 'includes/db.php';
// 获取字号设置
$stmt = $pdo->query("SELECT keming, xingming FROM zihao ORDER BY id DESC LIMIT 1");
$fontSizeSettings = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$fontSizeSettings) {
// 默认值
$fontSizeSettings = ['keming' => 13, 'xingming' => 13];
}
// 获取7天日期
$dates = [];
for ($i = 0; $i < 7; $i++) {
$dateObj = new DateTime(date('Y-m-d', strtotime("+$i days")));
$dayOfWeek = (int)$dateObj->format('w');
$weekDay = match($dayOfWeek) {
0 => '星期日',
1 => '星期一',
2 => '星期二',
3 => '星期三',
4 => '星期四',
5 => '星期五',
6 => '星期六',
default => '未知',
};
$displayDate = (int)$dateObj->format('m') . '月' . (int)$dateObj->format('d') . '日';
$label = $i === 0 ? '(今天)' : '';
$dates[] = [
'date' => $dateObj->format('Y-m-d'),
'display_date' => "$displayDate $weekDay" . $label,
'is_today' => $i === 0,
];
}
// 获取科室列表
$stmt = $pdo->query("SELECT id, name FROM departments ORDER BY order_num ASC");
$departments = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 获取每个日期的排班信息
$schedules_by_date = [];
foreach ($dates as $day) {
$stmt = $pdo->prepare("
SELECT d.name AS doctor_name, dp.id AS department_id, s.session, d.is_expert
FROM schedules s
JOIN doctors d ON s.doctor_id = d.id
JOIN departments dp ON d.department_id = dp.id
WHERE s.date = ?
ORDER BY s.session ASC
");
$stmt->execute([$day['date']]);
$schedules_by_date[$day['date']] = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>科室排班表</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body, html {
width: 100%;
height: 100%;
overflow: hidden;
font-family: "Segoe UI", "Microsoft YaHei", sans-serif;
background-color: #f9f9f9;
}
.slider-container {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
padding: 0;
display: flex;
flex-direction: column;
}
.slides {
display: flex;
transition: transform 0.5s ease-in-out;
height: 100%;
width: 100%;
flex: 1;
}
.slide {
min-width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding: 0;
box-sizing: border-box;
overflow: hidden;
}
.table-container {
width: 100%;
height: 100%;
overflow: auto;
background-color: white;
padding: 0;
}
table {
width: 100%;
border-collapse: collapse;
min-width: 100%;
table-layout: fixed;
height: 100%;
}
th, td {
border: 1px solid #ddd;
padding: 4px;
text-align: center;
vertical-align: middle;
word-wrap: break-word;
font-size: 14px;
line-height: 1.2;
}
th {
position: sticky;
top: 0;
z-index: 3;
background: linear-gradient(to bottom, #0099ff, #007BFF);
color: white;
font-weight: bold;
height: 40px;
}
.first-col-bg {
position: sticky;
left: 0;
z-index: 2;
background-color: #007BFF !important;
color: white;
font-weight: bold;
min-width: 80px;
width: 80px;
}
/* 浅红色底色 */
.today-bg {
background-color: #ffb3b3 !important; /* 浅红色 */
color: #333 !important;
}
.today-bold {
font-weight: bold;
}
.schedule-bg-lightgreen {
background-color: #D7F3E3 !important;
}
.schedule-bg-lightgray {
background-color: #EAEAEA !important;
}
.expert-badge {
display: inline-block;
background-color: #ffd700;
color: #333;
font-size: 10px;
padding: 1px 4px;
border-radius: 3px;
margin-left: 2px;
white-space: nowrap;
font-weight: bold;
}
.doctor-name {
font-weight: bold;
display: block;
margin: 0;
padding: 0;
line-height: 1.1;
}
.department-name {
font-weight: bold;
}
/* 添加页码指示器 */
.pagination {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
display: flex;
z-index: 10;
}
.pagination-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #ccc;
margin: 0 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.pagination-dot.active {
background-color: #007BFF;
}
/* 响应式调整 */
[url=home.php?mod=space&uid=945662]@media[/url] (max-height: 700px) {
th, td {
font-size: 12px;
padding: 2px;
}
th {
height: 35px;
}
}
@media (max-height: 600px) {
th, td {
font-size: 11px;
padding: 1px;
}
.expert-badge {
font-size: 8px;
padding: 0 2px;
}
}
/* 刷新指示器 */
.refresh-indicator {
position: absolute;
top: 10px;
right: 10px;
background-color: rgba(0, 123, 255, 0.8);
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
z-index: 100;
display: none;
}
/* 确保所有列占满宽度 */
.date-column {
width: calc(100% / var(--date-count)); /* 动态计算宽度 */
}
/* 固定左侧列宽度 */
.fixed-left-col {
width: 80px;
min-width: 80px;
}
/* 固定行高 */
tbody tr {
height: 40px;
}
tbody td {
height: 40px;
}
/* 空白行样式 */
.empty-row {
visibility: hidden;
}
/* 在媒体查询中覆盖固定行高 */
@media (max-height: 700px) {
tbody tr {
height: 35px;
}
tbody td {
height: 35px;
}
}
@media (max-height: 600px) {
tbody tr {
height: 30px;
}
tbody td {
height: 30px;
}
}
</style>
</head>
<body>
<div class="slider-container">
<div class="refresh-indicator" id="refreshIndicator">数据刷新中...</div>
<div class="slides" id="slides">
<!-- 动态插入分页内容 -->
</div>
<div class="pagination" id="pagination"></div>
</div>
<script>
// 全局变量存储数据
let departments = <?= json_encode($departments) ?>;
let dates = <?= json_encode($dates) ?>;
let schedulesByDate = <?= json_encode($schedules_by_date) ?>;
let fontSizeSettings = <?= json_encode($fontSizeSettings) ?>; // 获取字号设置
// 固定每页显示5个科室
const PAGE_SIZE = 5;
// 刷新状态管理
let refreshInterval = null;
let refreshTimeout = null;
let lastRefreshTime = Date.now();
let currentPage = 0;
let totalPages = 0;
let isRefreshing = false;
function createPage(deptChunk) {
const container = document.createElement('div');
container.className = 'slide';
const tableContainer = document.createElement('div');
tableContainer.className = 'table-container';
container.appendChild(tableContainer);
const table = document.createElement('table');
// 设置CSS变量,用于动态计算列宽
table.style.setProperty('--date-count', dates.length);
// 表头:只一行
const thead = document.createElement('thead');
const headerRow = document.createElement('tr');
const deptHeader = document.createElement('th');
deptHeader.textContent = '科室';
deptHeader.classList.add('first-col-bg', 'department-name', 'fixed-left-col');
// 应用科室名称字号
deptHeader.style.fontSize = `${fontSizeSettings.keming}px`;
headerRow.appendChild(deptHeader);
dates.forEach(day => {
const th = document.createElement('th');
const dateParts = day.display_date.split(' ');
th.innerHTML = `${dateParts[0]}<br>${dateParts[1]}`;
th.classList.add('date-column'); // 添加日期列类
if (day.is_today) {
th.classList.add('today-bg', 'today-bold');
}
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// 表体:每科室占两行
const tbody = document.createElement('tbody');
// 添加实际科室行
deptChunk.forEach((dept, idx) => {
const bgClass = idx % 2 === 0 ? 'schedule-bg-lightgreen' : 'schedule-bg-lightgray';
// 上午行
const amRow = document.createElement('tr');
const deptCell = document.createElement('td');
deptCell.textContent = dept.name;
deptCell.setAttribute('rowspan', '2');
deptCell.classList.add('first-col-bg', 'department-name', 'fixed-left-col');
// 应用科室名称字号
deptCell.style.fontSize = `${fontSizeSettings.keming}px`;
amRow.appendChild(deptCell);
dates.forEach(day => {
const cell = document.createElement('td');
cell.classList.add('date-column'); // 添加日期列类
// 如果是今天,添加特殊样式
if (day.is_today) {
cell.classList.add('today-bg', 'today-bold');
} else {
cell.classList.add(bgClass);
}
const amDoctors = schedulesByDate[day.date].filter(s =>
s.department_id == dept.id && s.session.includes('AM')
);
if (amDoctors.length > 0) {
amDoctors.forEach(d => {
const doctorContainer = document.createElement('div');
const nameSpan = document.createElement('span');
nameSpan.className = 'doctor-name';
nameSpan.textContent = d.doctor_name;
// 应用医师姓名字号
nameSpan.style.fontSize = `${fontSizeSettings.xingming}px`;
if (d.is_expert) {
const badge = document.createElement('span');
badge.textContent = '专家';
badge.className = 'expert-badge';
nameSpan.appendChild(badge);
}
doctorContainer.appendChild(nameSpan);
cell.appendChild(doctorContainer);
});
} else {
cell.textContent = '-';
}
amRow.appendChild(cell);
});
// 下午行
const pmRow = document.createElement('tr');
dates.forEach(day => {
const cell = document.createElement('td');
cell.classList.add('date-column'); // 添加日期列类
// 如果是今天,添加特殊样式
if (day.is_today) {
cell.classList.add('today-bg', 'today-bold');
} else {
cell.classList.add(bgClass);
}
const pmDoctors = schedulesByDate[day.date].filter(s =>
s.department_id == dept.id && s.session.includes('PM')
);
if (pmDoctors.length > 0) {
pmDoctors.forEach(d => {
const doctorContainer = document.createElement('div');
const nameSpan = document.createElement('span');
nameSpan.className = 'doctor-name';
nameSpan.textContent = d.doctor_name;
// 应用医师姓名字号
nameSpan.style.fontSize = `${fontSizeSettings.xingming}px`;
if (d.is_expert) {
const badge = document.createElement('span');
badge.textContent = '专家';
badge.className = 'expert-badge';
nameSpan.appendChild(badge);
}
doctorContainer.appendChild(nameSpan);
cell.appendChild(doctorContainer);
});
} else {
cell.textContent = '-';
}
pmRow.appendChild(cell);
});
tbody.appendChild(amRow);
tbody.appendChild(pmRow);
});
// 确保每页都有5个科室的高度(10行)
const actualRows = deptChunk.length * 2;
const neededRows = PAGE_SIZE * 2;
const emptyRows = neededRows - actualRows;
// 只在需要时添加空白行
if (emptyRows > 0) {
for (let i = 0; i < emptyRows; i++) {
const emptyRow = document.createElement('tr');
emptyRow.classList.add('empty-row');
// 科室列(只在第一行添加)
if (i === 0) {
const deptCell = document.createElement('td');
deptCell.classList.add('first-col-bg', 'fixed-left-col');
deptCell.setAttribute('rowspan', emptyRows);
emptyRow.appendChild(deptCell);
}
// 日期列
dates.forEach(() => {
const cell = document.createElement('td');
cell.textContent = '';
cell.classList.add('date-column');
emptyRow.appendChild(cell);
});
tbody.appendChild(emptyRow);
}
}
table.appendChild(tbody);
tableContainer.appendChild(table);
return container;
}
function buildSlider() {
const slidesContainer = document.getElementById('slides');
const paginationContainer = document.getElementById('pagination');
// 固定每页显示5个科室
const pageSize = PAGE_SIZE;
totalPages = Math.ceil(departments.length / pageSize);
// 清空容器
slidesContainer.innerHTML = '';
paginationContainer.innerHTML = '';
// 创建分页指示器
for (let i = 0; i < totalPages; i++) {
const dot = document.createElement('div');
dot.className = 'pagination-dot';
dot.dataset.page = i;
if (i === 0) dot.classList.add('active');
paginationContainer.appendChild(dot);
}
// 创建分页内容
for (let i = 0; i < departments.length; i += pageSize) {
const chunk = departments.slice(i, i + pageSize);
const page = createPage(chunk);
slidesContainer.appendChild(page);
}
// 初始显示当前页
updateSlider(currentPage);
// 添加分页点击事件
document.querySelectorAll('.pagination-dot').forEach(dot => {
dot.addEventListener('click', () => {
currentPage = parseInt(dot.dataset.page);
updateSlider(currentPage);
resetAutoSlide();
});
});
}
// 更新滑块位置
function updateSlider(pageIndex) {
const slidesContainer = document.getElementById('slides');
slidesContainer.style.transform = `translateX(-${pageIndex * 100}%)`;
// 更新分页指示器
document.querySelectorAll('.pagination-dot').forEach((dot, index) => {
dot.classList.toggle('active', index === pageIndex);
});
// 检查是否在最后一页
if (pageIndex === totalPages - 1) {
// 在最后一页显示后安排整页刷新
scheduleFullRefresh();
}
}
// 重置自动轮播
function resetAutoSlide() {
// 清除旧定时器
clearInterval(refreshInterval);
// 启动新的轮播
refreshInterval = setInterval(() => {
currentPage = (currentPage + 1) % totalPages;
updateSlider(currentPage);
}, 7000);
}
// 安排整页刷新
function scheduleFullRefresh() {
// 取消之前的刷新安排
clearTimeout(refreshTimeout);
// 安排在5秒后整页刷新
refreshTimeout = setTimeout(() => {
// 整页刷新(强制从服务器获取)
location.reload(true);
}, 5000); // 在最后一页停留5秒后刷新
}
window.onload = () => {
buildSlider();
resetAutoSlide();
// 在页面加载后立即检查是否需要刷新
setTimeout(() => {
// 如果超过30秒未刷新,且当前是最后一页
if (currentPage === totalPages - 1 && Date.now() - lastRefreshTime > 30000) {
scheduleFullRefresh();
}
}, 1000);
};
// 窗口大小变化时重新构建滑块
window.addEventListener('resize', () => {
// 重新构建滑块
buildSlider();
updateSlider(currentPage);
resetAutoSlide();
});
</script>
</body>
</html>