<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>报告管理 v2.1 - 江苏国能智慧生态综合服务平台</title>
<style>
/* --- 全局与基础样式 (与v2.0相同) --- */
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap');
:root {
--bg-color: #0a192f;
--primary-text-color: #ccd6f6;
--secondary-text-color: #8892b0;
--accent-color: #00bcd4;
--accent-color-hover: #14e5ff;
--table-header-bg: #112240;
--table-row-hover-bg: #112240;
--border-color: #233554;
--modal-bg-color: #0e1f3a;
}
body {
font-family: 'Noto Sans SC', sans-serif;
background-color: var(--bg-color);
color: var(--primary-text-color);
margin: 0;
padding: 20px;
font-size: 14px;
}
.container {
max-width: 1400px; /* 增加宽度以容纳更多筛选条件 */
margin: 0 auto;
padding: 20px;
background-color: rgba(17, 34, 64, 0.3);
border: 1px solid var(--border-color);
border-radius: 8px;
}
h1 {
font-size: 24px;
font-weight: 500;
color: var(--primary-text-color);
border-bottom: 2px solid var(--accent-color);
padding-bottom: 10px;
margin-top: 0;
}
/* --- 操作与筛选区 --- */
.top-controls {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.filters-container {
background-color: var(--table-header-bg);
padding: 15px;
border-radius: 6px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
}
.filter-item label {
color: var(--secondary-text-color);
white-space: nowrap;
}
.filter-item select, .filter-item input {
background-color: var(--bg-color);
color: var(--primary-text-color);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 8px 12px;
font-family: inherit;
}
.filter-item input[type="text"] {
width: 200px;
}
.time-range-group {
display: flex;
gap: 10px;
}
.btn {
padding: 9px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: background-color 0.3s ease, transform 0.1s ease;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background-color: var(--accent-color); color: #0a192f; }
.btn-primary:hover { background-color: var(--accent-color-hover); }
.btn-secondary { background-color: var(--secondary-text-color); color: var(--primary-text-color); }
.btn-secondary:hover { background-color: #a8b2d1; }
.btn-danger { background-color: #e94b3cff; color: white; }
.btn-danger:hover { background-color: #f06a5d; }
/* --- 表格样式 (与v2.0相同) --- */
.report-table { width: 100%; border-collapse: collapse; text-align: left; }
.report-table th, .report-table td { padding: 12px 15px; border-bottom: 1px solid var(--border-color); }
.report-table thead { background-color: var(--table-header-bg); }
.report-table th { font-weight: 500; color: var(--primary-text-color); }
.report-table tbody tr:hover { background-color: var(--table-row-hover-bg); }
.report-table .actions .action-btn { color: var(--accent-color); background: none; border: none; cursor: pointer; padding: 5px; margin-right: 10px; font-family: inherit; font-size: 14px; }
.report-table .actions .action-btn:hover { text-decoration: underline; color: var(--accent-color-hover); }
/* --- 弹窗样式 (与v2.0相同) --- */
.modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(10, 25, 47, 0.85); display: none; justify-content: center; align-items: center; z-index: 1000; backdrop-filter: blur(5px); }
.modal-content { background-color: var(--modal-bg-color); padding: 25px 30px; border-radius: 8px; border: 1px solid var(--border-color); box-shadow: 0 10px 30px -15px rgba(2,12,27,0.7); width: 100%; max-width: 550px; }
.modal-header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-color); padding-bottom: 15px; margin-bottom: 20px; }
.modal-header h2 { margin: 0; font-size: 18px; font-weight: 500; }
.modal-close-btn { background: none; border: none; color: var(--secondary-text-color); font-size: 24px; cursor: pointer; }
.modal-body .form-group { margin-bottom: 20px; }
.modal-body label { display: block; margin-bottom: 8px; color: var(--secondary-text-color); font-weight: 500; }
.modal-body select, .modal-body input[type="text"] { width: 100%; padding: 10px; background-color: var(--bg-color); border: 1px solid var(--border-color); border-radius: 4px; color: var(--primary-text-color); box-sizing: border-box; }
.modal-footer { display: flex; justify-content: flex-end; gap: 10px; padding-top: 10px; }
.confirm-modal-body p { font-size: 16px; line-height: 1.6; text-align: center; margin: 10px 0 25px; }
select[multiple] { height: 120px; padding: 10px; }
select[multiple] option { padding: 5px; }
select[multiple] option:checked { background-color: var(--accent-color); color: var(--bg-color); }
.multiselect-note { font-size: 12px; color: var(--secondary-text-color); margin-top: 5px; }
</style>
</head>
<body>
<div class="container">
<div class="top-controls">
<h1>报告管理</h1>
<button id="addReportBtn" class="btn btn-primary">[+] 新增报告</button>
</div>
<div class="filters-container">
<div class="filter-item">
<label for="filterName">报告名称:</label>
<input type="text" id="filterName" placeholder="输入关键词搜索">
</div>
<div class="filter-item">
<label>关联任务:</label>
<select id="filterTask">
<option value="">全部任务</option>
<option value="二号文任务盘点">二号文任务盘点</option>
<option value="环保合规性评价">环保合规性评价</option>
</select>
</div>
<div class="filter-item">
<label>时间范围:</label>
<div class="time-range-group">
<select id="filterYearSelect">
<option value="">全部年份</option>
<option>2025</option>
<option>2024</option>
</select>
<select id="filterPeriodTypeSelect">
<option value="">全部周期</option>
<option value="year">全年</option>
<option value="quarter">季度</option>
<option value="month">月度</option>
</select>
<select id="filterPeriodValueSelect" style="display: none;">
</select>
</div>
</div>
<div class="filter-item">
<button class="btn btn-secondary">查询</button>
<button class="btn btn-secondary">重置</button>
</div>
</div>
<table class="report-table">
<thead>
<tr>
<th>报告名称</th>
<th>关联任务</th>
<th>时间范围</th>
<th>生成时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="reportTbody">
<tr>
<td>环保合规性评价-2025年第二季度分析报告</td>
<td>环保合规性评价</td>
<td>2025年第二季度</td>
<td>2025-07-05 10:22:15</td>
<td class="actions">
<button class="action-btn">查看</button>
<button class="action-btn">下载</button>
<button class="action-btn">删除</button>
</td>
</tr>
<tr>
<td>二号文任务盘点-2025年6月分析报告</td>
<td>二号文任务盘点</td>
<td>2025年6月</td>
<td>2025-07-02 14:05:30</td>
<td class="actions">
<button class="action-btn">查看</button>
<button class="action-btn">下载</button>
<button class="action-btn">删除</button>
</td>
</tr>
<tr>
<td>二号文任务盘点-2025年第二季度分析报告</td>
<td>二号文任务盘点</td>
<td>2025年第二季度</td>
<td>2025-07-01 09:00:00</td>
<td class="actions">
<button class="action-btn">查看</button>
<button class="action-btn">下载</button>
<button class="action-btn">删除</button>
</td>
</tr>
<tr>
<td>安全生产标准化评审-2025年第二季度分析报告</td>
<td>安全生产标准化评审</td>
<td>2025年第二季度</td>
<td>2025-06-28 11:45:00</td>
<td class="actions">
<button class="action-btn">查看</button>
<button class="action-btn">下载</button>
<button class="action-btn">删除</button>
</td>
</tr>
<tr>
<td>环保合规性评价-2025年第一季度分析报告</td>
<td>环保合规性评价</td>
<td>2025年第一季度</td>
<td>2025-04-08 16:30:11</td>
<td class="actions">
<button class="action-btn">查看</button>
<button class="action-btn">下载</button>
<button class="action-btn">删除</button>
</td>
</tr>
<tr data-key="二号文任务盘点-2025年第一季度">
<td>二号文任务盘点-2025年第一季度分析报告</td>
<td>二号文任务盘点</td>
<td>2025年第一季度</td>
<td>2025-04-05 09:18:45</td>
<td class="actions">
<button class="action-btn">查看</button>
<button class="action-btn">下载</button>
<button class="action-btn">删除</button>
</td>
</tr>
<tr>
<td>二号文任务盘点-2024年年度分析报告</td>
<td>二号文任务盘点</td>
<td>2024年全年</td>
<td>2025-01-15 17:00:00</td>
<td class="actions">
<button class="action-btn">查看</button>
<button class="action-btn">下载</button>
<button class="action-btn">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 新增报告弹窗 -->
<div id="addReportModal" class="modal-overlay">
<div class="modal-content">
<div class="modal-header">
<h2>生成新报告</h2>
<button class="modal-close-btn">×</button>
</div>
<div class="modal-body">
<form id="addReportForm">
<div class="form-group">
<label for="taskSelect">关联任务</label>
<select id="taskSelect" required>
<option value="二号文任务盘点">二号文任务盘点</option>
<option value="环保合规性评价">环保合规性评价</option>
<option value="安全生产标准化评审">安全生产标准化评审</option>
<option value="月度安全检查">月度安全检查</option>
</select>
</div>
<div class="form-group">
<label>时间范围</label>
<div class="time-range-group">
<select id="yearSelect">
<option>2026</option><option selected>2025</option><option>2024</option>
</select>
<select id="periodTypeSelect">
<option value="year">全年</option>
<option value="quarter" selected>季度</option>
<option value="month">月度</option>
</select>
</div>
<div id="periodValueContainer" class="form-group" style="margin-top: 10px;">
<select id="periodValueSelect" multiple>
</select>
<p class="multiselect-note">提示:按住 Ctrl/Command 键可选择多个</p>
</div>
</div>
<div class="form-group">
<label for="reportNameInput">报告名称</label>
<input type="text" id="reportNameInput" placeholder="选择任务和时间后自动生成" required>
</div>
</form>
</div>
<div class="modal-footer">
<button id="cancelAddBtn" class="btn btn-secondary">取消</button>
<button id="confirmAddBtn" type="submit" form="addReportForm" class="btn btn-primary">确定</button>
</div>
</div>
</div>
<!-- 确认覆盖弹窗 -->
<div id="confirmOverwriteModal" class="modal-overlay">
<div class="modal-content" style="max-width: 400px;">
<div class="modal-header"><h2>确认覆盖</h2><button class="modal-close-btn">×</button></div>
<div class="modal-body confirm-modal-body"><p>部分报告已存在,确认要生成新报告并覆盖旧报告吗?</p></div>
<div class="modal-footer">
<button id="cancelOverwriteBtn" class="btn btn-secondary">取消</button>
<button id="confirmOverwriteBtn" class="btn btn-danger">确认覆盖</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// --- DOM Element References ---
const addReportBtn = document.getElementById('addReportBtn');
const addReportModal = document.getElementById('addReportModal');
const confirmOverwriteModal = document.getElementById('confirmOverwriteModal');
const closeModalBtns = document.querySelectorAll('.modal-close-btn');
const cancelAddBtn = document.getElementById('cancelAddBtn');
const confirmAddBtn = document.getElementById('confirmAddBtn');
const cancelOverwriteBtn = document.getElementById('cancelOverwriteBtn');
const confirmOverwriteBtn = document.getElementById('confirmOverwriteBtn');
const addReportForm = document.getElementById('addReportForm');
const taskSelect = document.getElementById('taskSelect');
const yearSelect = document.getElementById('yearSelect');
const periodTypeSelect = document.getElementById('periodTypeSelect');
const periodValueSelect = document.getElementById('periodValueSelect');
const periodValueContainer = document.getElementById('periodValueContainer');
const reportNameInput = document.getElementById('reportNameInput');
const reportTbody = document.getElementById('reportTbody');
let pendingReports = [];
// --- Time Range Selector Logic ---
const periodOptions = {
year: [],
quarter: ['第一季度', '第二季度', '第三季度', '第四季度'],
month: Array.from({ length: 12 }, (_, i) => `${i + 1}月`)
};
function setupTimeRangeSelector(yearEl, typeEl, valueEl, containerEl) {
function update() {
const type = typeEl.value;
if (!valueEl) return;
valueEl.innerHTML = '';
const isMultiSelect = type === 'quarter' || type === 'month';
// *** BUG FIX START ***
// The visibility of the container is the only thing we need to control.
// If the container is hidden, its children (the select box) will be hidden too.
if (containerEl) {
containerEl.style.display = isMultiSelect ? 'block' : 'none';
}
// *** BUG FIX END ***
if (isMultiSelect) {
periodOptions[type].forEach((optionText) => {
const optionEl = document.createElement('option');
optionEl.textContent = optionText;
optionEl.value = optionText;
valueEl.appendChild(optionEl);
});
}
}
typeEl.addEventListener('change', update);
update(); // Initial call
}
// Setup for modal
setupTimeRangeSelector(yearSelect, periodTypeSelect, periodValueSelect, periodValueContainer);
// Setup for filter
const filterPeriodTypeSelect = document.getElementById('filterPeriodTypeSelect');
const filterPeriodValueSelect = document.getElementById('filterPeriodValueSelect');
setupTimeRangeSelector(
document.getElementById('filterYearSelect'),
filterPeriodTypeSelect,
filterPeriodValueSelect,
null // No container for filter, so we handle visibility directly
);
filterPeriodTypeSelect.addEventListener('change', function(){
filterPeriodValueSelect.style.display = (this.value === 'quarter' || this.value === 'month') ? 'block' : 'none';
});
filterPeriodValueSelect.style.display = 'none'; // Initial state for filter
// --- Auto-generate Report Name ---
function updateReportName() {
const taskName = taskSelect.value;
const year = yearSelect.value;
const periodType = periodTypeSelect.value;
const selectedValues = Array.from(periodValueSelect.selectedOptions).map(opt => opt.value);
let timeRangeText = "";
if (periodType === 'year') {
timeRangeText = `${year}年全年`;
} else if (selectedValues.length === 1) {
timeRangeText = `${year}年${selectedValues[0]}`;
} else if (selectedValues.length > 1) {
timeRangeText = `${year}年批量报告`;
} else {
reportNameInput.value = "";
return;
}
reportNameInput.value = `${taskName}-${timeRangeText}分析报告`;
}
[taskSelect, yearSelect, periodTypeSelect, periodValueSelect].forEach(el => el.addEventListener('change', updateReportName));
// --- Modal Control ---
const openModal = (modal) => modal.style.display = 'flex';
const closeModal = (modal) => modal.style.display = 'none';
addReportBtn.addEventListener('click', () => {
updateReportName(); // Initialize name on open
openModal(addReportModal);
});
closeModalBtns.forEach(btn => btn.addEventListener('click', () => {
closeModal(addReportModal);
closeModal(confirmOverwriteModal);
}));
cancelAddBtn.addEventListener('click', () => closeModal(addReportModal));
cancelOverwriteBtn.addEventListener('click', () => closeModal(confirmOverwriteModal));
// --- Core Logic ---
addReportForm.addEventListener('submit', (e) => {
e.preventDefault();
const taskName = taskSelect.value;
const year = yearSelect.value;
const periodType = periodTypeSelect.value;
const selectedPeriods = periodType === 'year' ? ['全年'] : Array.from(periodValueSelect.selectedOptions).map(opt => opt.value);
if (selectedPeriods.length === 0 && periodType !== 'year') {
alert('请至少选择一个季度或月份!');
return;
}
pendingReports = selectedPeriods.map(period => ({
taskName,
timeRangeText: `${year}年${period}`,
reportName: (selectedPeriods.length > 1) ? `${taskName}-${year}年${period}分析报告` : reportNameInput.value,
isExisting: doesReportExist(taskName, `${year}年${period}`)
}));
const hasExisting = pendingReports.some(r => r.isExisting);
if (hasExisting) {
openModal(confirmOverwriteModal);
} else {
processReports(pendingReports);
closeModal(addReportModal);
}
});
confirmOverwriteBtn.addEventListener('click', () => {
processReports(pendingReports);
closeModal(confirmOverwriteModal);
closeModal(addReportModal);
});
function doesReportExist(taskName, timeRangeText) {
let found = false;
reportTbody.querySelectorAll('tr').forEach(row => {
if (row.cells[1].textContent === taskName && row.cells[2].textContent === timeRangeText) {
found = true;
}
});
return found;
}
function processReports(reports) {
reports.forEach(reportData => {
if (reportData.isExisting) {
overwriteReport(reportData);
} else {
addNewReport(reportData);
}
});
}
function getCurrentTimestamp() {
return new Date().toLocaleString('sv-SE').replace(' ', ' ').slice(0, 19);
}
function addNewReport(data) {
const newRow = document.createElement('tr');
newRow.innerHTML = `
<td>${data.reportName}</td>
<td>${data.taskName}</td>
<td>${data.timeRangeText}</td>
<td>${getCurrentTimestamp()}</td>
<td class="actions">
<button class="action-btn">查看</button>
<button class="action-btn">下载</button>
<button class="action-btn">删除</button>
</td>
`;
reportTbody.prepend(newRow);
}
function overwriteReport(data) {
let rowToUpdate = null;
reportTbody.querySelectorAll('tr').forEach(row => {
if(row.cells[1].textContent === data.taskName && row.cells[2].textContent === data.timeRangeText) {
rowToUpdate = row;
}
});
if (rowToUpdate) {
rowToUpdate.style.transition = 'background-color 0.5s ease';
rowToUpdate.style.backgroundColor = 'rgba(0, 188, 212, 0.3)';
rowToUpdate.cells[0].textContent = data.reportName;
rowToUpdate.cells[3].textContent = getCurrentTimestamp();
setTimeout(() => { rowToUpdate.style.backgroundColor = ''; }, 1500);
}
}
});
</script>
</body>
</html>
index.html
style.css
index.js
index.html