<!DOCTYPE html>
<html lang=
"zh-CN"
>
<head>
<meta charset=
"UTF-8"
>
<meta
name
=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<
title
>项目完成情况管理系统</
title
>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family:
'Segoe UI'
,
'Microsoft YaHei'
, sans-serif;
}
body {
background: linear-gradient(135deg, #f0f5ff 0%, #e6f7ff 100%);
min-height: 100vh;
padding: 20px;
color: #333;
}
.container {
max-
width
: 1400px;
margin: 0 auto;
background: white;
border-radius: 15px;
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.12);
overflow: hidden;
position: relative;
}
header {
background: linear-gradient(90deg, #1a6bc4, #0d4a9e);
color: white;
padding: 25px 30px;
position: relative;
overflow: hidden;
display: flex;
justify-content: space-between;
align
-items: center;
flex-wrap: wrap;
}
header::before {
content:
""
;
position:
absolute
;
top: -50%;
left: -50%;
width
: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%);
transform: rotate(30deg);
}
.header-content {
max-
width
: 65%;
}
h1 {
font-
size
: 2.5rem;
letter-spacing: 1px;
text-shadow: 0 3px 6px rgba(0, 0, 0, 0.25);
position: relative;
margin-bottom: 10px;
}
.
subtitle
{
font-
size
: 1.1rem;
opacity: 0.9;
font-weight: 300;
}
.header-controls {
display: flex;
gap: 15px;
flex-wrap: wrap;
justify-content: flex-
end
;
}
.control-btn {
background: rgba(255, 255, 255, 0.15);
color: white;
border:
none
;
padding: 12px 18px;
border-radius: 8px;
font-
size
: 1rem;
cursor: pointer;
display: flex;
align
-items: center;
gap: 8px;
transition:
all
0.25s ease;
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
min-
width
: 120px;
}
.control-btn:hover {
background: rgba(255, 255, 255, 0.25);
transform: translateY(-2px);
}
.stats {
display: flex;
justify-content: center;
background: #0d4a9e;
padding: 18px;
color: white;
font-
size
: 1.25rem;
gap: 50px;
position: relative;
z-index: 2;
}
.stat-item {
display: flex;
align
-items: center;
gap: 12px;
}
.stat-value {
font-weight: bold;
font-
size
: 1.5rem;
background: rgba(255, 255, 255, 0.15);
padding: 6px 22px;
border-radius: 25px;
min-
width
: 80px;
text-
align
: center;
backdrop-filter: blur(4px);
}
.table-container {
padding: 25px;
overflow: auto;
max-height: 680px;
position: relative;
}
table {
width
: 100%;
border-collapse: separate;
border-spacing: 0;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05);
border-radius: 12px;
overflow: hidden;
}
th {
background: #1a6bc4;
color: white;
padding: 18px 15px;
text-
align
: left;
position: sticky;
top: 0;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.8px;
font-
size
: 1.05rem;
}
th:first-child {
border-top-left-radius: 12px;
}
th:last-child {
border-top-right-radius: 12px;
}
td {
padding: 16px 15px;
border-bottom: 1px solid #e0e8f0;
transition: background 0.2s;
}
tr:nth-child(
even
) {
background-color: #f8fbfe;
}
tr:hover {
background-color: #f0f7ff;
}
.completed-row {
background-color: #edf7ed !important;
border-left: 5px solid #4caf50;
}
.completed-row:hover {
background-color: #e0f0e0 !important;
}
input {
width
: 100%;
padding: 10px 12px;
border: 1px solid #d0dde9;
border-radius: 6px;
font-
size
: 1rem;
transition:
all
0.3s;
background: rgba(255, 255, 255, 0.7);
}
input:focus {
outline:
none
;
border-color: #1a6bc4;
box-shadow: 0 0 0 3px rgba(26, 107, 196, 0.2);
background: white;
}
input[
type
=
"date"
] {
padding: 9px 12px;
}
.status-badge {
display: inline-block;
padding: 7px 15px;
border-radius: 25px;
font-
size
: 0.9rem;
font-weight: 600;
text-
align
: center;
min-
width
: 90px;
}
.status-completed {
background: linear-gradient(135deg, #4CAF50, #2E7D32);
color: white;
box-shadow: 0 3px 6px rgba(76, 175, 80, 0.2);
}
.status-pending {
background: linear-gradient(135deg, #FF9800, #F57C00);
color: white;
box-shadow: 0 3px 6px rgba(255, 152, 0, 0.2);
}
footer {
text-
align
: center;
padding: 25px;
color: #6c7a89;
font-
size
: 0.95rem;
border-top: 1px solid #edf2f7;
background: #f9fbfd;
}
.file-input-wrapper {
position:
absolute
;
width
: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.notification {
position: fixed;
top: 30px;
right: 30px;
padding: 18px 25px;
border-radius: 10px;
background: #4CAF50;
color: white;
font-weight: 500;
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
transform: translateX(150%);
transition: transform 0.4s ease;
z-index: 1000;
}
.notification.show {
transform: translateX(0);
}
.notification.
error
{
background: #f44336;
}
.modal {
display:
none
;
position: fixed;
top: 0;
left: 0;
width
: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
z-index: 2000;
align
-items: center;
justify-content: center;
}
.modal-content {
background: white;
border-radius: 15px;
width
: 90%;
max-
width
: 500px;
padding: 30px;
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
transform: translateY(-50px);
opacity: 0;
transition:
all
0.4s ease;
}
.modal.show {
display: flex;
}
.modal.show .modal-content {
transform: translateY(0);
opacity: 1;
}
.modal-header {
display: flex;
justify-content: space-between;
align
-items: center;
margin-bottom: 25px;
}
.modal-
title
{
font-
size
: 1.8rem;
color: #1a6bc4;
}
.close-modal {
background:
none
;
border:
none
;
font-
size
: 2rem;
cursor: pointer;
color: #999;
transition: color 0.2s;
}
.close-modal:hover {
color: #f44336;
}
.modal-body {
margin-bottom: 30px;
}
.modal-text {
font-
size
: 1.1rem;
line-height: 1.6;
margin-bottom: 20px;
color: #555;
}
.modal-footer {
display: flex;
gap: 15px;
justify-content: flex-
end
;
}
.modal-btn {
padding: 12px 25px;
border:
none
;
border-radius: 8px;
font-
size
: 1.05rem;
cursor: pointer;
transition:
all
0.2s;
}
.btn-confirm {
background: #1a6bc4;
color: white;
}
.btn-confirm:hover {
background: #0d4a9e;
}
.btn-cancel {
background: #f5f5f5;
color: #666;
}
.btn-cancel:hover {
background: #e0e0e0;
}
[url=home.php
?
mod
=space&uid=945662]@media[/url] (max-
width
: 1100px) {
.header-content {
max-
width
: 100%;
text-
align
: center;
margin-bottom: 20px;
}
.header-controls {
width
: 100%;
justify-content: center;
}
header {
padding: 20px 15px;
}
}
@media (max-
width
: 768px) {
h1 {
font-
size
: 2rem;
}
.stats {
flex-direction: column;
gap: 15px;
padding: 15px;
}
.stat-item {
justify-content: center;
}
.control-btn {
padding: 10px 15px;
font-
size
: 0.9rem;
min-
width
: 100px;
}
}
@media (max-
width
: 480px) {
.header-controls {
gap: 10px;
}
.control-btn {
min-
width
: auto;
padding: 8px 12px;
}
.control-btn span:last-child {
display:
none
;
}
}
</style>
</head>
<body>
<
div
class=
"container"
>
<header>
<
div
class=
"header-content"
>
<h1>项目完成情况一览表</h1>
<p class=
"subtitle"
>高效管理项目进度,实时跟踪完成情况,提升团队协作效率</p>
</
div
>
<
div
class=
"header-controls"
>
<button class=
"control-btn"
id=
"import-btn"
>
<span>📥</span> <span>导入CSV</span>
</button>
<button class=
"control-btn"
id=
"export-btn"
>
<span>📤</span> <span>导出CSV</span>
</button>
<button class=
"control-btn"
id=
"new-btn"
>
<span>➕</span> <span>新建项目</span>
</button>
<button class=
"control-btn"
id=
"reset-btn"
>
<span>🔄</span> <span>重置数据</span>
</button>
</
div
>
</header>
<
div
class=
"stats"
>
<
div
class=
"stat-item"
>
<span>项目总数:</span>
<span class=
"stat-value"
id=
"total-count"
>0</span>
</
div
>
<
div
class=
"stat-item"
>
<span>已完成数:</span>
<span class=
"stat-value"
id=
"completed-count"
>0</span>
</
div
>
<
div
class=
"stat-item"
>
<span>未完成数:</span>
<span class=
"stat-value"
id=
"pending-count"
>0</span>
</
div
>
</
div
>
<
div
class=
"table-container"
>
<table id=
"project-table"
>
<thead>
<tr>
<th style=
"width: 60px;"
>序号</th>
<th>项目名称</th>
<th>责任人</th>
<th>项目标题</th>
<th>项目完成人</th>
<th style=
"width: 150px;"
>完成时间</th>
<th style=
"width: 130px;"
>项目编号</th>
<th style=
"width: 110px;"
>完成情况</th>
</tr>
</thead>
<tbody id=
"table-body"
>
<!-- 表格数据将通过JavaScript动态生成 -->
</tbody>
</table>
</
div
>
<footer>
<p>项目完成情况管理系统 © 2023 | 数据已本地存储 | 支持CSV导入导出</p>
</footer>
</
div
>
<input
type
=
"file"
id=
"csv-file-input"
class=
"file-input-wrapper"
accept=
".csv"
>
<
div
class=
"notification"
id=
"notification"
>操作成功!</
div
>
<
div
class=
"modal"
id=
"reset-modal"
>
<
div
class=
"modal-content"
>
<
div
class=
"modal-header"
>
<h2 class=
"modal-title"
>确认重置数据</h2>
<button class=
"close-modal"
>×</button>
</
div
>
<
div
class=
"modal-body"
>
<p class=
"modal-text"
>您确定要重置所有项目数据吗?此操作将永久删除当前所有项目记录,且不可恢复。</p>
<p class=
"modal-text"
><strong>请注意:</strong> 重置操作将清除所有本地存储数据,包括已完成的项目信息。</p>
</
div
>
<
div
class=
"modal-footer"
>
<button class=
"modal-btn btn-cancel"
id=
"cancel-reset"
>取消</button>
<button class=
"modal-btn btn-confirm"
id=
"confirm-reset"
>确认重置</button>
</
div
>
</
div
>
</
div
>
<script>
// 示例项目数据
const sampleProjects = [
{ id:
"PRJ-2023-001"
,
name
:
"网站重构项目"
, owner:
"张伟"
},
{ id:
"PRJ-2023-002"
,
name
:
"移动应用开发"
, owner:
"李娜"
},
{ id:
"PRJ-2023-003"
,
name
:
"数据库迁移"
, owner:
"王强"
},
{ id:
"PRJ-2023-004"
,
name
:
"安全系统升级"
, owner:
"赵敏"
},
{ id:
"PRJ-2023-005"
,
name
:
"客户关系管理"
, owner:
"陈明"
},
{ id:
"PRJ-2023-006"
,
name
:
"市场推广活动"
, owner:
"刘芳"
},
{ id:
"PRJ-2023-007"
,
name
:
"产品UI设计"
, owner:
"杨光"
},
{ id:
"PRJ-2023-008"
,
name
:
"服务器部署"
, owner:
"徐静"
},
{ id:
"PRJ-2023-009"
,
name
:
"API接口开发"
, owner:
"黄涛"
},
{ id:
"PRJ-2023-010"
,
name
:
"数据分析报告"
, owner:
"周华"
},
{ id:
"PRJ-2023-011"
,
name
:
"内部培训系统"
, owner:
"吴刚"
},
{ id:
"PRJ-2023-012"
,
name
:
"财务系统升级"
, owner:
"郑琳"
},
{ id:
"PRJ-2023-013"
,
name
:
"云服务迁移"
, owner:
"钱勇"
},
{ id:
"PRJ-2023-014"
,
name
:
"用户调研分析"
, owner:
"孙悦"
},
{ id:
"PRJ-2023-015"
,
name
:
"产品测试计划"
, owner:
"朱华"
}
];
// 初始化项目数据
let projects = [];
// DOM元素
const tableBody = document.getElementById(
'table-body'
);
const importBtn = document.getElementById(
'import-btn'
);
const exportBtn = document.getElementById(
'export-btn'
);
const newBtn = document.getElementById(
'new-btn'
);
const resetBtn = document.getElementById(
'reset-btn'
);
const totalCountEl = document.getElementById(
'total-count'
);
const completedCountEl = document.getElementById(
'completed-count'
);
const pendingCountEl = document.getElementById(
'pending-count'
);
const csvFileInput = document.getElementById(
'csv-file-input'
);
const notification = document.getElementById(
'notification'
);
const resetModal = document.getElementById(
'reset-modal'
);
const closeModal = document.querySelector(
'.close-modal'
);
const cancelResetBtn = document.getElementById(
'cancel-reset'
);
const confirmResetBtn = document.getElementById(
'confirm-reset'
);
// 页面加载时初始化
document.addEventListener(
'DOMContentLoaded'
, () => {
loadFromLocalStorage();
renderTable();
updateStats();
// 添加事件监听器
importBtn.addEventListener(
'click'
, triggerFileInput);
csvFileInput.addEventListener(
'change'
, handleFileImport);
exportBtn.addEventListener(
'click'
, exportToCSV);
newBtn.addEventListener(
'click'
, addNewProject);
resetBtn.addEventListener(
'click'
, showResetModal);
closeModal.addEventListener(
'click'
, hideResetModal);
cancelResetBtn.addEventListener(
'click'
, hideResetModal);
confirmResetBtn.addEventListener(
'click'
, resetData);
});
// 触发文件选择
function triggerFileInput() {
csvFileInput.click();
}
// 处理文件导入
function handleFileImport(event) {
const file = event.target.files[0];
if
(!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
const csvData = e.target.result;
const parsedData = parseCSV(csvData);
if
(parsedData.
length
> 0) {
projects = parsedData.map((row, index) => ({
name
: row[0] || `项目${index + 1}`,
owner: row[1] ||
'待分配'
,
title
: row[2] ||
''
,
completer: row[3] ||
''
,
date: row[4] ||
''
,
id: row[5] || `PRJ-${new Date().getFullYear()}-${String(index + 1).padStart(3,
'0'
)}`,
completed: !!row[4]
}));
saveToLocalStorage();
renderTable();
updateStats();
showNotification(
'成功导入 '
+ parsedData.
length
+
' 个项目数据'
);
}
else
{
showNotification(
'导入失败:CSV文件格式不正确'
, true);
}
} catch (
error
) {
console.
error
(
'CSV导入错误:'
,
error
);
showNotification(
'导入失败:'
+
error
.message, true);
}
};
reader.readAsText(file);
// 重置input,允许再次选择同一文件
event.target.value =
''
;
}
// 解析CSV数据
function parseCSV(csv) {
const lines = csv.split(
'\n'
);
const result = [];
// 检查是否有标题行
const hasHeader = lines[0].includes(
'项目名称'
) && lines[0].includes(
'责任人'
);
for
(let i = hasHeader ? 1 : 0; i < lines.
length
; i++) {
const line = lines[i].trim();
if
(!line) continue;
// 简单处理带逗号的值
const values = [];
let current =
''
;
let inQuotes = false;
for
(let j = 0; j < line.
length
; j++) {
const char = line[j];
if
(char ===
'"'
) {
inQuotes = !inQuotes;
}
else
if
(char ===
','
&& !inQuotes) {
values.
push
(current);
current =
''
;
}
else
{
current += char;
}
}
values.
push
(current);
// 确保每行有6列
if
(values.
length
>= 6) {
result.
push
(values.slice(0, 6));
}
}
return result;
}
// 导出为CSV
function exportToCSV() {
if
(projects.
length
=== 0) {
showNotification(
'没有数据可导出'
, true);
return;
}
// 创建CSV内容
let csvContent =
'项目名称,责任人,项目标题,项目完成人,项目完成时间,项目编号\n'
;
projects.forEach(project => {
const row = [
`
"${project.name}"
`,
`
"${project.owner}"
`,
`
"${project.title}"
`,
`
"${project.completer}"
`,
`
"${project.date}"
`,
`
"${project.id}"
`
];
csvContent += row.join(
','
) +
'\n'
;
});
// 创建下载链接
const blob = new Blob([csvContent], {
type
:
'text/csv;charset=utf-8;'
});
const url = URL.createObjectURL(blob);
const link = document.createElement(
'a'
);
link.setAttribute(
'href'
, url);
link.setAttribute(
'download'
, `项目完成情况_${new Date().toLocaleDateString().replace(/\//g,
'-'
)}.csv`);
link.style.visibility =
'hidden'
;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showNotification(
'成功导出 '
+ projects.
length
+
' 个项目数据'
);
}
// 渲染表格
function renderTable() {
tableBody.innerHTML =
''
;
projects.forEach((project, index) => {
const row = document.createElement(
'tr'
);
if
(project.completed) {
row.classList.
add
(
'completed-row'
);
}
row.innerHTML = `
<td>${index + 1}</td>
<td>${project.
name
}</td>
<td>${project.owner}</td>
<td>
<input
type
=
"text"
class=
"project-title"
value=
"${project.title || ''}"
data-index=
"${index}"
>
</td>
<td>
<input
type
=
"text"
class=
"project-completer"
value=
"${project.completer || ''}"
data-index=
"${index}"
>
</td>
<td>
<input
type
=
"date"
class=
"project-date"
value=
"${project.date || ''}"
data-index=
"${index}"
>
</td>
<td>
<input
type
=
"text"
class=
"project-id"
value=
"${project.id || ''}"
data-index=
"${index}"
>
</td>
<td>
<span class=
"status-badge ${project.completed ? 'status-completed' : 'status-pending'}"
>
${project.completed ?
'已完成'
:
'未完成'
}
</span>
</td>
`;
tableBody.appendChild(row);
});
// 添加事件监听
document.querySelectorAll(
'.project-title'
).forEach(input => {
input.addEventListener(
'input'
, handleInputChange);
});
document.querySelectorAll(
'.project-completer'
).forEach(input => {
input.addEventListener(
'input'
, handleInputChange);
});
document.querySelectorAll(
'.project-date'
).forEach(input => {
input.addEventListener(
'change'
, handleDateChange);
});
document.querySelectorAll(
'.project-id'
).forEach(input => {
input.addEventListener(
'input'
, handleInputChange);
});
}
// 处理输入变化
function handleInputChange(e) {
const index = e.target.dataset.index;
const field = e.target.classList[0].split(
'-'
)[1];
projects[index][field] = e.target.value;
saveToLocalStorage();
}
// 处理日期变化
function handleDateChange(e) {
const index = e.target.dataset.index;
projects[index].date = e.target.value;
projects[index].completed = !!e.target.value;
saveToLocalStorage();
renderTable();
updateStats();
}
// 更新统计数据
function updateStats() {
totalCountEl.textContent = projects.
length
;
const completed = projects.filter(p => p.completed).
length
;
completedCountEl.textContent = completed;
pendingCountEl.textContent = projects.
length
- completed;
}
// 添加新项目
function addNewProject() {
if
(projects.
length
>= 65) {
showNotification(
'已达到最大项目数(65个)'
, true);
return;
}
const newProject = {
name
: `新项目 ${projects.
length
+ 1}`,
owner:
'待分配'
,
title
:
''
,
completer:
''
,
date:
''
,
id: `PRJ-${new Date().getFullYear()}-${String(projects.
length
+ 1).padStart(3,
'0'
)}`,
completed: false
};
projects.
push
(newProject);
saveToLocalStorage();
renderTable();
updateStats();
showNotification(
'已添加新项目'
);
}
// 显示重置确认模态框
function showResetModal() {
resetModal.classList.
add
(
'show'
);
}
// 隐藏重置确认模态框
function hideResetModal() {
resetModal.classList.remove(
'show'
);
}
// 重置数据
function resetData() {
projects = [];
localStorage.removeItem(
'projectData'
);
renderTable();
updateStats();
hideResetModal();
showNotification(
'数据已重置'
);
}
// 显示通知
function showNotification(message, isError = false) {
notification.textContent = message;
notification.classList.remove(
'error'
);
if
(isError) {
notification.classList.
add
(
'error'
);
}
notification.classList.
add
(
'show'
);
setTimeout(() => {
notification.classList.remove(
'show'
);
}, 3000);
}
// 保存到本地存储
function saveToLocalStorage() {
localStorage.setItem(
'projectData'
, JSON.stringify(projects));
}
// 从本地存储加载
function loadFromLocalStorage() {
const savedData = localStorage.getItem(
'projectData'
);
if
(savedData) {
projects = JSON.
parse
(savedData);
}
else
{
// 初始加载一些示例数据
projects = sampleProjects.slice(0, 10).map(proj => ({
...proj,
title
:
''
,
completer:
''
,
date:
''
,
id: proj.id,
completed: false
}));
// 标记前四个为已完成
projects[0].
title
=
"企业网站重构"
;
projects[0].completer =
"张伟"
;
projects[0].date =
"2023-08-15"
;
projects[0].completed = true;
projects[1].
title
=
"移动端应用开发"
;
projects[1].completer =
"李娜"
;
projects[1].date =
"2023-09-01"
;
projects[1].completed = true;
projects[2].
title
=
"数据库迁移"
;
projects[2].completer =
"王强"
;
projects[2].date =
"2023-09-15"
;
projects[2].completed = true;
projects[3].
title
=
"安全系统升级"
;
projects[3].completer =
"赵敏"
;
projects[3].date =
"2023-10-01"
;
projects[3].completed = true;
}
}
</script>
</body>
</html>