该文本描述了一个任务管理工具的HTML结构和功能,包括任务输入、进度条、任务列表、拖放功能以及导出链接的功能。工具使用了现代的CSS样式来创建一个科技感的界面,并通过JavaScript实现任务的添加、删除、标记完成和进度更新。所有任务数据存储在本地存储中,用户可以通过链接导出任务。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>任务管理工具</title>
<style>
/* 科技风背景和全局样式 */
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
background: linear-gradient(135deg, #1e1e2f, #2a2a40);
color: #e0e0e0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
/* 任务管理容器 */
.task-manager {
position: relative;
background: rgba(30, 30, 47, 0.8);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
border-radius: 12px;
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
border: 1px solid rgba(255, 255, 255, 0.1);
}
/* 进度条区域 */
.progress {
position: sticky;
top: 0;
background: rgba(30, 30, 47, 0.9);
z-index: 10;
padding: 15px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
text-align: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.progress-bar {
height: 10px;
width: 100%;
background: rgba(255, 255, 255, 0.1);
border-radius: 5px;
margin-top: 10px;
overflow: hidden;
}
.progress-bar span {
display: block;
height: 100%;
background: linear-gradient(90deg, #00c6ff, #0072ff);
border-radius: 5px;
transition: width 0.3s ease;
}
/* 标题 */
.task-manager h1 {
font-size: 24px;
color: #ffffff;
margin: 20px 0 10px;
text-align: center;
text-shadow: 0 0 10px rgba(0, 194, 255, 0.7);
}
/* 任务输入区域 */
.task-manager .task-input-container {
display: flex;
gap: 10px;
padding: 15px;
background: rgba(40, 40, 60, 0.8);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.task-manager input[type="text"] {
flex: 1;
padding: 10px;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
font-size: 14px;
outline: none;
background: rgba(255, 255, 255, 0.1);
color: #e0e0e0;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.task-manager input[type="number"] {
width: 100px;
padding: 10px;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
font-size: 14px;
outline: none;
background: rgba(255, 255, 255, 0.1);
color: #e0e0e0;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.task-manager input[type="text"]:focus,
.task-manager input[type="number"]:focus {
border-color: #00c6ff;
box-shadow: 0 0 8px rgba(0, 198, 255, 0.6);
}
.task-manager button {
padding: 10px 20px;
background: linear-gradient(90deg, #00c6ff, #0072ff);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.task-manager button:hover {
transform: scale(1.05);
box-shadow: 0 0 10px rgba(0, 198, 255, 0.8);
}
.task-manager button:active {
transform: scale(0.95);
}
/* 任务列表 */
.tasks {
list-style: none;
padding: 15px;
margin: 0;
}
.tasks li {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 6px;
margin-bottom: 10px;
background: rgba(40, 40, 60, 0.8);
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
.tasks li:hover {
background: rgba(50, 50, 70, 0.9);
box-shadow: 0 0 10px rgba(0, 198, 255, 0.4);
}
.tasks li span {
flex: 1;
margin-right: 10px;
word-break: break-word;
font-size: 14px;
color: #e0e0e0;
}
.tasks li.completed span {
text-decoration: line-through;
color: #888;
}
.tasks li div {
display: flex;
gap: 10px;
}
.tasks li button {
padding: 5px 10px;
font-size: 14px;
width: 80px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.tasks li button:hover {
transform: scale(1.05);
box-shadow: 0 0 8px rgba(0, 198, 255, 0.6);
}
.tasks li button:active {
transform: scale(0.95);
}
.tasks li .complete-btn {
background: linear-gradient(90deg, #00d2ff, #3a7bd5);
color: white;
}
.tasks li .delete-btn {
background: linear-gradient(90deg, #ff416c, #ff4b2b);
color: white;
}
/* 导出区域 */
.export-container {
padding: 15px;
text-align: center;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.export-container button {
margin: 5px;
}
</style>
</head>
<body>
<div class="task-manager">
<div class="progress">
<p id="progressText">任务进度:0%</p>
<div class="progress-bar">
<span id="progressBar" style="width: 0%;"></span>
</div>
</div>
<h1>任务管理工具</h1>
<div class="task-input-container">
<input type="text" id="taskInput" placeholder="请输入任务名称">
<input type="number" id="taskDuration" placeholder="预估时间(分钟)" min="0">
<button onclick="addTask()">添加任务</button>
</div>
<ul class="tasks" id="taskList"></ul>
<div class="export-container">
<button onclick="exportTasks()">导出为链接</button>
<button id="copyLinkButton" style="display: none;" onclick="copyLink()">复制链接</button>
</div>
</div>
<script>
const taskList = document.getElementById('taskList');
const progressText = document.getElementById('progressText');
const progressBar = document.getElementById('progressBar');
const copyLinkButton = document.getElementById('copyLinkButton');
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
function addTask() {
const taskInput = document.getElementById('taskInput');
const taskDurationInput = document.getElementById('taskDuration');
const taskName = taskInput.value.trim();
const taskDuration = parseFloat(taskDurationInput.value.trim());
if (taskName === '') {
alert('任务名称不能为空!');
return;
}
if (isNaN(taskDuration) || taskDuration < 0) {
alert('预估时长必须是一个有效的非负数!');
return;
}
tasks.push({ name: taskName, duration: taskDuration, completed: false });
taskInput.value = '';
taskDurationInput.value = '';
saveTasks();
renderTasks();
}
function toggleComplete(index) {
tasks[index].completed = !tasks[index].completed;
saveTasks();
renderTasks();
}
function deleteTask(index) {
tasks.splice(index, 1);
saveTasks();
renderTasks();
}
function saveTasks() {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
function renderTasks() {
taskList.innerHTML = '';
tasks.forEach((task, index) => {
const taskItem = document.createElement('li');
taskItem.className = task.completed ? 'completed' : '';
taskItem.draggable = true;
taskItem.dataset.index = index;
taskItem.innerHTML = `
<span>${index + 1}. ${task.name} (预估时长: ${task.duration} 分钟)</span>
<div>
<button class="complete-btn" onclick="toggleComplete(${index})">
${task.completed ? '撤销完成' : '标记完成'}
</button>
<button class="delete-btn" onclick="deleteTask(${index})">删除</button>
</div>
`;
taskItem.addEventListener('dragstart', handleDragStart);
taskItem.addEventListener('dragover', handleDragOver);
taskItem.addEventListener('drop', handleDrop);
taskItem.addEventListener('dragend', handleDragEnd);
taskList.appendChild(taskItem);
});
updateProgress();
}
function handleDragStart(event) {
event.target.classList.add('dragging');
event.dataTransfer.setData('text/plain', event.target.dataset.index);
}
function handleDragOver(event) {
event.preventDefault();
const draggingElement = document.querySelector('.dragging');
const targetElement = event.target.closest('li');
if (targetElement && targetElement !== draggingElement) {
const targetIndex = parseInt(targetElement.dataset.index);
const draggingIndex = parseInt(draggingElement.dataset.index);
if (draggingIndex < targetIndex) {
taskList.insertBefore(draggingElement, targetElement.nextSibling);
} else {
taskList.insertBefore(draggingElement, targetElement);
}
}
}
function handleDrop(event) {
event.preventDefault();
const draggingIndex = parseInt(event.dataTransfer.getData('text/plain'));
const targetIndex = Array.from(taskList.children).indexOf(event.target.closest('li'));
if (draggingIndex !== targetIndex) {
const [draggedTask] = tasks.splice(draggingIndex, 1);
tasks.splice(targetIndex, 0, draggedTask);
saveTasks();
renderTasks();
}
}
function handleDragEnd(event) {
event.target.classList.remove('dragging');
}
function updateProgress() {
const totalTasks = tasks.length;
const completedTasks = tasks.filter(task => task.completed).length;
const progress = totalTasks === 0 ? 0 : Math.round((completedTasks / totalTasks) * 100);
progressText.innerText = `任务进度:${progress}%`;
progressBar.style.width = `${progress}%`;
}
function exportTasks() {
const encodedTasks = btoa(encodeURIComponent(JSON.stringify(tasks)));
const link = `${window.location.origin}${window.location.pathname}?tasks=${encodedTasks}`;
navigator.clipboard.writeText(link).then(() => {
alert('链接已复制到剪贴板!');
});
}
function importTasksFromURL() {
const urlParams = new URLSearchParams(window.location.search);
const encodedTasks = urlParams.get('tasks');
if (encodedTasks) {
tasks = JSON.parse(decodeURIComponent(atob(encodedTasks)));
saveTasks();
renderTasks();
}
}
importTasksFromURL();
renderTasks();
</script>
</body>
</html>