Files
smart-project/h5/log.html
Jesxion e70e5cc69d feat(h5): 修复alert.html认证Bug + 新增log-detail.html日志详情页
- alert.html: requireAuth()后加return防止未登录时继续执行
- 新增log-detail.html: 独立详情页(骨架屏+设备标签+安全备注+照片)
- logs.html: 列表项跳转log-detail.html?id=xxx
- mock.js: MOCK_LOGS补全part/equipment/author/created_at/safety_note字段
- 补全equip-checkbox等CSS样式
2026-04-14 18:22:00 +08:00

502 lines
18 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>施工日志 - 郑州智慧工地</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/weui@2.5.4/dist/style/weui.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/remixicon@4.5.0/fonts/remixicon.min.css">
<link rel="stylesheet" href="css/style.css">
<style>
/* ===== 详情视图 ===== */
.log-detail-header {
background: var(--color-card);
border-radius: 12px;
padding: 16px;
margin-bottom: 12px;
}
.log-detail-date {
font-size: 20px;
font-weight: 700;
color: var(--color-text);
margin-bottom: 4px;
}
.log-detail-part {
font-size: 14px;
color: var(--color-primary);
font-weight: 600;
}
.log-detail-stats {
display: flex;
gap: 16px;
margin-top: 14px;
padding-top: 14px;
border-top: 1px solid var(--color-border);
}
.log-stat-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: var(--color-text-secondary);
}
.log-stat-item svg { width: 14px; height: 14px; }
.log-stat-item .val {
font-weight: 700;
color: var(--color-text);
font-family: var(--font-mono);
}
.log-detail-section {
background: var(--color-card);
border-radius: 12px;
padding: 16px;
margin-bottom: 12px;
}
.log-detail-section-title {
font-size: 13px;
font-weight: 600;
color: var(--color-primary);
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 6px;
}
.log-detail-section-title svg { width: 14px; height: 14px; }
.log-detail-text {
font-size: 14px;
color: var(--color-text);
line-height: 1.7;
}
.log-equip-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.log-equip-tag {
padding: 4px 12px;
border-radius: 20px;
background: rgba(30, 58, 95, 0.08);
color: var(--color-primary);
font-size: 12px;
font-weight: 500;
}
.log-detail-author {
display: flex;
align-items: center;
gap: 10px;
font-size: 12px;
color: var(--color-text-secondary);
padding: 12px 16px;
background: var(--color-bg);
border-radius: 8px;
margin-top: 12px;
}
.log-detail-author svg { width: 14px; height: 14px; }
/* ===== 新建/编辑表单 ===== */
.photo-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.photo-item { position: relative; aspect-ratio: 1; border-radius: 8px; overflow: hidden; background: var(--color-bg); }
.photo-item img { width: 100%; height: 100%; object-fit: cover; }
.photo-item-delete {
position: absolute; top: 4px; right: 4px;
width: 20px; height: 20px; border-radius: 50%;
background: rgba(0,0,0,0.5); color: #fff;
display: flex; align-items: center; justify-content: center;
cursor: pointer; font-size: 12px;
}
.photo-add {
aspect-ratio: 1; border-radius: 8px;
border: 1px dashed var(--color-border);
display: flex; flex-direction: column;
align-items: center; justify-content: center;
gap: 4px; cursor: pointer;
font-size: 12px; color: var(--color-text-secondary);
}
.photo-add svg { font-size: 24px; color: var(--color-text-secondary); }
/* ===== 骨架屏 ===== */
.skeleton-detail .skeleton-line {
height: 14px; background: var(--color-border);
border-radius: 4px; margin-bottom: 10px;
animation: skeleton-pulse 1.5s ease-in-out infinite;
}
@keyframes skeleton-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
</style>
</head>
<body>
<div class="page">
<!-- 顶栏 -->
<header class="page-hd">
<a href="logs.html" class="page-hd__back">
<i class="ri-arrow-left-s-line"></i>
</a>
<div class="page-hd__title" id="pageTitle">写日志</div>
<div style="width:32px;"></div>
</header>
<!-- ===== 详情视图 ===== -->
<div id="detailView" class="page-bd" style="display:none;"></div>
<!-- ===== 骨架屏 ===== -->
<div id="skeletonView" class="page-bd" style="display:none;padding:12px;">
<div class="skeleton-detail">
<div style="background:var(--color-card);border-radius:12px;padding:16px;margin-bottom:12px;">
<div class="skeleton-line" style="width:40%;margin-bottom:12px;"></div>
<div class="skeleton-line" style="width:60%;margin-bottom:6px;"></div>
<div class="skeleton-line short"></div>
</div>
<div style="background:var(--color-card);border-radius:12px;padding:16px;margin-bottom:12px;">
<div class="skeleton-line" style="width:50%;margin-bottom:8px;"></div>
<div class="skeleton-line" style="width:80%;margin-bottom:6px;"></div>
<div class="skeleton-line medium"></div>
</div>
</div>
</div>
<!-- ===== 新建/编辑表单 ===== -->
<div id="formView" class="page-bd">
<!-- 照片 -->
<div class="weui-panel">
<div class="weui-panel__hd">
<i class="ri-image-line" style="margin-right:6px;"></i>现场照片(选填)
</div>
<div class="weui-panel__bd">
<div class="photo-grid" id="photoGrid"></div>
<input type="file" id="photoInput" accept="image/*" multiple style="display:none;">
<div class="photo-add" id="addPhotoBtn" onclick="document.getElementById('photoInput').click()">
<i class="ri-camera-line"></i>
<span>添加照片</span>
</div>
</div>
</div>
<!-- 日志表单 -->
<div class="weui-panel">
<div class="weui-panel__hd">
<i class="ri-file-list-3-line" style="margin-right:6px;"></i>日志信息
</div>
<div class="weui-panel__bd">
<div class="weui-cells">
<!-- 日期 -->
<div class="weui-cell">
<div class="weui-cell__hd">
<label class="weui-label">
<i class="ri-calendar-line" style="margin-right:6px;"></i>日期
</label>
</div>
<div class="weui-cell__bd">
<input type="date" id="dateInput" class="weui-input" required>
</div>
</div>
<!-- 施工部位 -->
<div class="weui-cell">
<div class="weui-cell__hd">
<label class="weui-label">施工部位</label>
</div>
<div class="weui-cell__bd">
<input type="text" id="partInput" class="weui-input" placeholder="请填写施工部位" required>
</div>
</div>
<!-- 作业内容 -->
<div class="weui-cell">
<div class="weui-cell__hd">
<label class="weui-label">作业内容</label>
</div>
<div class="weui-cell__bd">
<textarea id="contentInput" class="weui-textarea" placeholder="请填写作业内容" rows="3" required></textarea>
</div>
</div>
<!-- 人员出勤 -->
<div class="weui-cell">
<div class="weui-cell__hd">
<label class="weui-label">
<i class="ri-user-line" style="margin-right:6px;"></i>人员出勤
</label>
</div>
<div class="weui-cell__bd">
<input type="number" id="workersInput" class="weui-input" placeholder="人数" min="0" required>
</div>
<div class="weui-cell__ft"></div>
</div>
<!-- 设备运行 -->
<div class="weui-cell weui-cell__hd__label">
<div class="weui-cell__hd">
<i class="ri-settings-3-line" style="margin-right:6px;color:var(--color-text-secondary);"></i>设备运行
</div>
<div class="weui-cell__bd" style="display:flex;flex-wrap:wrap;gap:8px;padding:8px 0;">
<label class="equip-checkbox">
<input type="checkbox" name="equipment" value="tower_crane"> 塔吊
</label>
<label class="equip-checkbox">
<input type="checkbox" name="equipment" value="elevator"> 升降机
</label>
<label class="equip-checkbox">
<input type="checkbox" name="equipment" value="truck"> 运输车
</label>
<label class="equip-checkbox">
<input type="checkbox" name="equipment" value="crane"> 吊车
</label>
</div>
</div>
</div>
</div>
</div>
<!-- 安全备注 -->
<div class="weui-panel">
<div class="weui-panel__hd">
<i class="ri-shield-line" style="margin-right:6px;"></i>安全备注(选填)
</div>
<div class="weui-panel__bd">
<textarea id="safetyInput" class="weui-textarea" placeholder="安全情况、注意事项等" rows="2"></textarea>
</div>
</div>
<!-- 备注 -->
<div class="weui-panel">
<div class="weui-panel__hd">
<i class="ri-sticky-note-line" style="margin-right:6px;"></i>备注(选填)
</div>
<div class="weui-panel__bd">
<textarea id="noteInput" class="weui-textarea" placeholder="补充说明" rows="2"></textarea>
</div>
</div>
<!-- 提交按钮 -->
<div style="padding:16px 12px 32px;">
<a href="javascript:;" class="weui-btn weui-btn_primary" id="submitBtn" onclick="submitLog()">提交日志</a>
</div>
</div>
<!-- 底部TabBar -->
<div class="tab-bar">
<a href="index.html" class="tab-item">
<i class="ri-home-line tab-item-icon"></i>
<span class="tab-item-text">首页</span>
</a>
<a href="devices.html" class="tab-item">
<i class="ri-archive-line tab-item-icon"></i>
<span class="tab-item-text">设备</span>
</a>
<a href="reports.html" class="tab-item">
<i class="ri-camera-line tab-item-icon"></i>
<span class="tab-item-text">随手拍</span>
</a>
<a href="logs.html" class="tab-item active">
<i class="ri-file-list-3-fill tab-item-icon"></i>
<span class="tab-item-text">日志</span>
</a>
</div>
</div>
<script src="js/mock.js"></script>
<script src="js/api.js"></script>
<script src="js/app.js"></script>
<script>
// ===== URL 参数判断 =====
var logId = getQueryParam('id');
var isDetailMode = !!logId;
// 设备文字映射
var equipText = {
'tower_crane': '塔吊',
'elevator': '升降机',
'truck': '运输车',
'crane': '吊车'
};
// ===== 初始化 =====
if (isDetailMode) {
// --- 详情模式 ---
document.getElementById('pageTitle').textContent = '日志详情';
document.getElementById('formView').style.display = 'none';
document.getElementById('skeletonView').style.display = 'block';
loadDetail();
} else {
// --- 新建模式 ---
document.getElementById('pageTitle').textContent = '写日志';
document.getElementById('formView').style.display = 'block';
document.getElementById('skeletonView').style.display = 'none';
document.getElementById('dateInput').value = new Date().toISOString().split('T')[0];
renderPhotos();
}
// ===== 详情加载 =====
async function loadDetail() {
var res = await apiGetLogDetail(logId);
var item = res.data;
if (!item) {
document.getElementById('skeletonView').innerHTML =
'<div class="empty-state"><div class="empty-state-text">记录不存在</div></div>';
return;
}
var equipTags = '';
if (item.equipment && item.equipment.length > 0) {
var tags = item.equipment.map(function(e) {
return '<span class="log-equip-tag"><i class="' + getEquipIcon(e) + '" style="margin-right:4px;"></i>' + (equipText[e] || e) + '</span>';
});
equipTags = '<div class="log-equip-tags" style="margin-top:10px;">' + tags.join('') + '</div>';
}
var photoHtml = '';
if (item.photos && item.photos.length > 0) {
photoHtml =
'<div class="log-detail-section" style="padding:12px 16px 16px;">' +
'<div class="log-detail-section-title"><i class="ri-image-line"></i>现场照片</div>' +
'<div class="photo-grid">';
item.photos.forEach(function(url) {
photoHtml += '<div class="photo-item"><img src="' + url + '" alt="photo"></div>';
});
photoHtml += '</div></div>';
}
document.getElementById('detailView').style.display = 'block';
document.getElementById('detailView').innerHTML =
'<div style="padding:12px;">' +
// 头部信息
'<div class="log-detail-header">' +
'<div class="log-detail-date">' + item.date + '</div>' +
'<div class="log-detail-part">' + item.part + '</div>' +
'<div class="log-detail-stats">' +
'<div class="log-stat-item">' +
'<i class="ri-user-line"></i><span>出勤</span>' +
'<span class="val">' + item.workers + '</span><span>人</span>' +
'</div>' +
(item.equipment && item.equipment.length > 0 ?
'<div class="log-stat-item">' +
'<i class="ri-settings-3-line"></i><span>设备</span>' +
'<span class="val">' + item.equipment.length + '</span><span>台</span>' +
'</div>' : '') +
'</div>' +
'</div>' +
// 作业内容
'<div class="log-detail-section">' +
'<div class="log-detail-section-title"><i class="ri-file-list-3-line"></i>作业内容</div>' +
'<div class="log-detail-text">' + item.content + '</div>' +
equipTags +
'</div>' +
// 安全备注
(item.safety_note ?
'<div class="log-detail-section">' +
'<div class="log-detail-section-title" style="color:var(--color-danger);"><i class="ri-shield-line"></i>安全备注</div>' +
'<div class="log-detail-text" style="color:var(--color-danger);">' + item.safety_note + '</div>' +
'</div>' : '') +
// 备注
(item.note ?
'<div class="log-detail-section">' +
'<div class="log-detail-section-title"><i class="ri-sticky-note-line"></i>备注</div>' +
'<div class="log-detail-text">' + item.note + '</div>' +
'</div>' : '') +
photoHtml +
// 作者信息
'<div class="log-detail-author">' +
'<i class="ri-user-line"></i>' +
'<span>' + item.author + '</span>' +
'<span>·</span>' +
'<span>' + item.created_at + '</span>' +
'</div>' +
'</div>';
}
function getEquipIcon(type) {
var icons = {
'tower_crane': 'ri-hammer-line',
'elevator': 'ri-arrow-up-down-line',
'truck': 'ri-truck-line',
'crane': 'ri-install-line'
};
return icons[type] || 'ri-settings-line';
}
// ===== 照片(新建模式)=====
var photos = [];
function renderPhotos() {
var grid = document.getElementById('photoGrid');
var html = photos.map(function(photo, index) {
return '<div class="photo-item">' +
'<img src="' + photo + '" alt="photo' + (index + 1) + '">' +
'<div class="photo-item-delete" onclick="removePhoto(' + index + ')">' +
'<i class="ri-close-line"></i>' +
'</div>' +
'</div>';
}).join('');
grid.innerHTML = html;
// 最后一张是添加按钮
if (photos.length < 4) {
grid.innerHTML +=
'<div class="photo-add" onclick="document.getElementById(\'photoInput\').click()">' +
'<i class="ri-camera-line"></i><span>添加照片</span>' +
'</div>';
}
}
function removePhoto(index) {
photos.splice(index, 1);
renderPhotos();
}
document.getElementById('photoInput').addEventListener('change', function(e) {
var files = Array.from(e.target.files);
files.forEach(function(file) {
var reader = new FileReader();
reader.onload = function(evt) {
if (photos.length < 4) {
photos.push(evt.target.result);
renderPhotos();
}
};
reader.readAsDataURL(file);
});
e.target.value = '';
});
// ===== 提交 =====
async function submitLog() {
var date = document.getElementById('dateInput').value;
var part = document.getElementById('partInput').value.trim();
var content = document.getElementById('contentInput').value.trim();
var workers = parseInt(document.getElementById('workersInput').value);
var equipment = Array.from(document.querySelectorAll('input[name="equipment"]:checked')).map(function(el) { return el.value; });
var safety = document.getElementById('safetyInput').value.trim();
var note = document.getElementById('noteInput').value.trim();
if (!date) { showToast('请选择日期'); return; }
if (!part) { showToast('请填写施工部位'); return; }
if (!content) { showToast('请填写作业内容'); return; }
if (!workers && workers !== 0) { showToast('请填写人员出勤'); return; }
var res = await apiSubmitLog({ date, part, content, workers, equipment, safety, note, attachments: photos });
if (res.code === 0) {
showToast('提交成功');
setTimeout(function() { location.href = 'logs.html'; }, 800);
} else {
showToast(res.message || '提交失败');
}
}
initTabBar();
</script>
</body>
</html>