uu531 commited on
Commit
343b47b
·
verified ·
1 Parent(s): badbbb8

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +233 -24
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>任务清单</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <style>
@@ -36,6 +36,50 @@
36
  @keyframes strike {
37
  to { transform: scaleX(1); }
38
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  </style>
40
  </head>
41
  <body class="bg-gray-50 min-h-screen">
@@ -58,6 +102,27 @@
58
  <i class="fas fa-plus"></i>
59
  </button>
60
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  </header>
62
 
63
  <!-- 任务控制 -->
@@ -66,6 +131,7 @@
66
  <button id="filterAll" class="filter-btn active px-4 py-2 rounded-lg bg-gray-900 text-white font-medium">全部</button>
67
  <button id="filterActive" class="filter-btn px-4 py-2 rounded-lg border border-gray-300 hover:bg-gray-100 text-gray-700">待办</button>
68
  <button id="filterCompleted" class="filter-btn px-4 py-2 rounded-lg border border-gray-300 hover:bg-gray-100 text-gray-700">已完成</button>
 
69
  </div>
70
  <button id="clearCompleted" class="text-gray-700 hover:text-gray-900 text-sm">
71
  <i class="fas fa-trash-alt mr-1"></i> 清除已完成
@@ -86,6 +152,10 @@
86
  <span class="text-gray-600">待完成:</span>
87
  <span id="remainingTasks" class="font-bold ml-1">0</span>
88
  </div>
 
 
 
 
89
  </div>
90
 
91
  <!-- 任务列表 -->
@@ -104,15 +174,19 @@
104
  // DOM元素
105
  const newTaskInput = document.getElementById('newTaskInput');
106
  const addTaskBtn = document.getElementById('addTaskBtn');
 
 
107
  const taskList = document.getElementById('taskList');
108
  const emptyState = document.getElementById('emptyState');
109
  const filterAll = document.getElementById('filterAll');
110
  const filterActive = document.getElementById('filterActive');
111
  const filterCompleted = document.getElementById('filterCompleted');
 
112
  const clearCompleted = document.getElementById('clearCompleted');
113
  const totalTasks = document.getElementById('totalTasks');
114
  const completedTasks = document.getElementById('completedTasks');
115
  const remainingTasks = document.getElementById('remainingTasks');
 
116
 
117
  // 状态
118
  let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
@@ -131,6 +205,7 @@
131
  filterAll.addEventListener('click', () => setFilter('all'));
132
  filterActive.addEventListener('click', () => setFilter('active'));
133
  filterCompleted.addEventListener('click', () => setFilter('completed'));
 
134
 
135
  clearCompleted.addEventListener('click', clearCompletedTasks);
136
 
@@ -139,18 +214,27 @@
139
  const taskText = newTaskInput.value.trim();
140
  if (taskText === '') return;
141
 
 
 
 
142
  const task = {
143
  id: Date.now().toString(),
144
  text: taskText,
145
  completed: false,
146
- createdAt: new Date().toISOString()
 
 
147
  };
148
 
149
  tasks.unshift(task);
150
  saveTasks();
151
  renderTasks();
152
  updateStats();
 
 
153
  newTaskInput.value = '';
 
 
154
  }
155
 
156
  function renderTasks() {
@@ -164,6 +248,9 @@
164
  case 'completed':
165
  filteredTasks = tasks.filter(task => task.completed);
166
  break;
 
 
 
167
  default:
168
  filteredTasks = [...tasks];
169
  }
@@ -176,6 +263,31 @@
176
  emptyState.classList.add('hidden');
177
  taskList.innerHTML = '';
178
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  filteredTasks.forEach(task => {
180
  const taskElement = createTaskElement(task);
181
  taskList.appendChild(taskElement);
@@ -185,19 +297,58 @@
185
 
186
  function createTaskElement(task) {
187
  const taskElement = document.createElement('div');
188
- taskElement.className = `task-item bg-white rounded-lg p-4 border border-gray-200 flex items-center justify-between`;
189
  taskElement.dataset.id = task.id;
190
 
191
  if (task.completed) {
192
  taskElement.classList.add('completed');
193
  }
194
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  taskElement.innerHTML = `
196
  <div class="flex items-center space-x-3 flex-1">
197
- <button class="complete-btn w-5 h-5 rounded border ${task.completed ? 'bg-gray-900 border-gray-900 text-white' : 'border-gray-400'} flex items-center justify-center">
198
- ${task.completed ? '<i class="fas fa-check text-xs"></i>' : ''}
199
  </button>
200
- <span class="task-text ${task.completed ? 'text-gray-400' : 'text-gray-800'} flex-1">${task.text}</span>
 
 
 
 
 
 
 
 
 
 
 
201
  </div>
202
  <div class="flex space-x-2">
203
  <button class="edit-btn text-gray-500 hover:text-gray-700">
@@ -238,27 +389,62 @@
238
  const taskElement = document.querySelector(`.task-item[data-id="${taskId}"]`);
239
  const taskTextElement = taskElement.querySelector('.task-text');
240
 
241
- const input = document.createElement('input');
242
- input.type = 'text';
243
- input.value = task.text;
244
- input.className = 'border border-gray-300 px-2 py-1 rounded flex-1';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
- taskTextElement.replaceWith(input);
247
- input.focus();
248
 
249
- const handleEdit = (e) => {
250
- if (e.key === 'Enter' || e.type === 'blur') {
251
- const newText = input.value.trim();
252
- if (newText !== '' && newText !== task.text) {
253
- task.text = newText;
254
- saveTasks();
255
- }
256
- renderTasks();
 
 
 
 
 
 
 
257
  }
 
258
  };
259
 
260
- input.addEventListener('keypress', handleEdit);
261
- input.addEventListener('blur', handleEdit);
 
 
 
 
 
 
 
262
  }
263
 
264
  function deleteTask(taskId) {
@@ -285,8 +471,10 @@
285
  });
286
 
287
  const activeBtn = document.getElementById(`filter${filter.charAt(0).toUpperCase() + filter.slice(1)}`);
288
- activeBtn.classList.add('active', 'bg-gray-900', 'text-white');
289
- activeBtn.classList.remove('border', 'hover:bg-gray-100', 'text-gray-700');
 
 
290
 
291
  renderTasks();
292
  }
@@ -295,15 +483,36 @@
295
  const total = tasks.length;
296
  const completed = tasks.filter(task => task.completed).length;
297
  const remaining = total - completed;
 
298
 
299
  totalTasks.textContent = total;
300
  completedTasks.textContent = completed;
301
  remainingTasks.textContent = remaining;
 
302
  }
303
 
304
  function saveTasks() {
305
  localStorage.setItem('tasks', JSON.stringify(tasks));
306
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  });
308
  </script>
309
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=uu531/web" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>待办</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <style>
 
36
  @keyframes strike {
37
  to { transform: scaleX(1); }
38
  }
39
+
40
+ /* 自定义日期输入样式 */
41
+ input[type="date"]::-webkit-calendar-picker-indicator {
42
+ filter: invert(0.5);
43
+ }
44
+
45
+ input[type="date"]::-webkit-datetime-edit {
46
+ color: #4b5563;
47
+ }
48
+
49
+ /* 优先级通过待办框颜色表示 */
50
+ .priority-urgent {
51
+ background-color: #fee2e2; /* 红色背景 */
52
+ border-left: 4px solid #ef4444;
53
+ }
54
+
55
+ .priority-high {
56
+ background-color: #fef3c7; /* 黄色背景 */
57
+ border-left: 4px solid #f59e0b;
58
+ }
59
+
60
+ .priority-normal {
61
+ background-color: white; /* 白色背景 */
62
+ border-left: 4px solid #d1d5db;
63
+ }
64
+
65
+ /* 完成按钮颜色 */
66
+ .complete-btn-urgent {
67
+ border-color: #ef4444;
68
+ }
69
+
70
+ .complete-btn-high {
71
+ border-color: #f59e0b;
72
+ }
73
+
74
+ .complete-btn-normal {
75
+ border-color: #d1d5db;
76
+ }
77
+
78
+ /* 完成状态的按钮颜色 */
79
+ .complete-btn-done {
80
+ background-color: #10b981;
81
+ border-color: #10b981;
82
+ }
83
  </style>
84
  </head>
85
  <body class="bg-gray-50 min-h-screen">
 
102
  <i class="fas fa-plus"></i>
103
  </button>
104
  </div>
105
+ <div class="mt-3 flex items-center justify-between">
106
+ <div class="flex-1 mr-2">
107
+ <label for="dueDate" class="block text-sm text-gray-600 mb-1">截止日期</label>
108
+ <input
109
+ type="date"
110
+ id="dueDate"
111
+ class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:border-gray-500 bg-white text-gray-900"
112
+ >
113
+ </div>
114
+ <div class="flex-1 ml-2">
115
+ <label for="priority" class="block text-sm text-gray-600 mb-1">优先级</label>
116
+ <select
117
+ id="priority"
118
+ class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:border-gray-500 bg-white text-gray-900"
119
+ >
120
+ <option value="normal">普通</option>
121
+ <option value="high">高</option>
122
+ <option value="urgent">紧急</option>
123
+ </select>
124
+ </div>
125
+ </div>
126
  </header>
127
 
128
  <!-- 任务控制 -->
 
131
  <button id="filterAll" class="filter-btn active px-4 py-2 rounded-lg bg-gray-900 text-white font-medium">全部</button>
132
  <button id="filterActive" class="filter-btn px-4 py-2 rounded-lg border border-gray-300 hover:bg-gray-100 text-gray-700">待办</button>
133
  <button id="filterCompleted" class="filter-btn px-4 py-2 rounded-lg border border-gray-300 hover:bg-gray-100 text-gray-700">已完成</button>
134
+ <button id="filterDueToday" class="filter-btn px-4 py-2 rounded-lg border border-gray-300 hover:bg-gray-100 text-gray-700">今日到期</button>
135
  </div>
136
  <button id="clearCompleted" class="text-gray-700 hover:text-gray-900 text-sm">
137
  <i class="fas fa-trash-alt mr-1"></i> 清除已完成
 
152
  <span class="text-gray-600">待完成:</span>
153
  <span id="remainingTasks" class="font-bold ml-1">0</span>
154
  </div>
155
+ <div>
156
+ <span class="text-gray-600">今日到期:</span>
157
+ <span id="dueTodayTasks" class="font-bold ml-1">0</span>
158
+ </div>
159
  </div>
160
 
161
  <!-- 任务列表 -->
 
174
  // DOM元素
175
  const newTaskInput = document.getElementById('newTaskInput');
176
  const addTaskBtn = document.getElementById('addTaskBtn');
177
+ const dueDateInput = document.getElementById('dueDate');
178
+ const prioritySelect = document.getElementById('priority');
179
  const taskList = document.getElementById('taskList');
180
  const emptyState = document.getElementById('emptyState');
181
  const filterAll = document.getElementById('filterAll');
182
  const filterActive = document.getElementById('filterActive');
183
  const filterCompleted = document.getElementById('filterCompleted');
184
+ const filterDueToday = document.getElementById('filterDueToday');
185
  const clearCompleted = document.getElementById('clearCompleted');
186
  const totalTasks = document.getElementById('totalTasks');
187
  const completedTasks = document.getElementById('completedTasks');
188
  const remainingTasks = document.getElementById('remainingTasks');
189
+ const dueTodayTasks = document.getElementById('dueTodayTasks');
190
 
191
  // 状态
192
  let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
 
205
  filterAll.addEventListener('click', () => setFilter('all'));
206
  filterActive.addEventListener('click', () => setFilter('active'));
207
  filterCompleted.addEventListener('click', () => setFilter('completed'));
208
+ filterDueToday.addEventListener('click', () => setFilter('dueToday'));
209
 
210
  clearCompleted.addEventListener('click', clearCompletedTasks);
211
 
 
214
  const taskText = newTaskInput.value.trim();
215
  if (taskText === '') return;
216
 
217
+ const dueDate = dueDateInput.value;
218
+ const priority = prioritySelect.value;
219
+
220
  const task = {
221
  id: Date.now().toString(),
222
  text: taskText,
223
  completed: false,
224
+ createdAt: new Date().toISOString(),
225
+ dueDate: dueDate,
226
+ priority: priority
227
  };
228
 
229
  tasks.unshift(task);
230
  saveTasks();
231
  renderTasks();
232
  updateStats();
233
+
234
+ // 重置输入
235
  newTaskInput.value = '';
236
+ dueDateInput.value = '';
237
+ prioritySelect.value = 'normal';
238
  }
239
 
240
  function renderTasks() {
 
248
  case 'completed':
249
  filteredTasks = tasks.filter(task => task.completed);
250
  break;
251
+ case 'dueToday':
252
+ filteredTasks = tasks.filter(task => isDueToday(task.dueDate) && !task.completed);
253
+ break;
254
  default:
255
  filteredTasks = [...tasks];
256
  }
 
263
  emptyState.classList.add('hidden');
264
  taskList.innerHTML = '';
265
 
266
+ // 按优先级和截止日期排序
267
+ filteredTasks.sort((a, b) => {
268
+ // 先按完成状态排序(未完成的在前)
269
+ if (a.completed !== b.completed) {
270
+ return a.completed ? 1 : -1;
271
+ }
272
+
273
+ // 然后按优先级排序
274
+ const priorityOrder = { 'urgent': 0, 'high': 1, 'normal': 2 };
275
+ if (a.priority !== b.priority) {
276
+ return priorityOrder[a.priority] - priorityOrder[b.priority];
277
+ }
278
+
279
+ // 最后按截止日期排序(近期的在前)
280
+ if (a.dueDate && b.dueDate) {
281
+ return new Date(a.dueDate) - new Date(b.dueDate);
282
+ } else if (a.dueDate) {
283
+ return -1;
284
+ } else if (b.dueDate) {
285
+ return 1;
286
+ }
287
+
288
+ return 0;
289
+ });
290
+
291
  filteredTasks.forEach(task => {
292
  const taskElement = createTaskElement(task);
293
  taskList.appendChild(taskElement);
 
297
 
298
  function createTaskElement(task) {
299
  const taskElement = document.createElement('div');
300
+ taskElement.className = `task-item rounded-lg p-4 border border-gray-200 flex items-center justify-between priority-${task.priority}`;
301
  taskElement.dataset.id = task.id;
302
 
303
  if (task.completed) {
304
  taskElement.classList.add('completed');
305
  }
306
 
307
+ // 格式化日期显示
308
+ let dueDateDisplay = '';
309
+ if (task.dueDate) {
310
+ const dueDate = new Date(task.dueDate);
311
+ const today = new Date();
312
+ today.setHours(0, 0, 0, 0);
313
+
314
+ const timeDiff = dueDate - today;
315
+ const daysDiff = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
316
+
317
+ if (daysDiff === 0) {
318
+ dueDateDisplay = '<span class="text-red-500">今天到期</span>';
319
+ } else if (daysDiff === 1) {
320
+ dueDateDisplay = '<span class="text-orange-500">明天到期</span>';
321
+ } else if (daysDiff < 0) {
322
+ dueDateDisplay = '<span class="text-red-500">已过期</span>';
323
+ } else {
324
+ dueDateDisplay = `${daysDiff}天后到期`;
325
+ }
326
+
327
+ dueDateDisplay = `<span class="text-xs ml-2">${dueDateDisplay}</span>`;
328
+ }
329
+
330
+ // 完成按钮类名
331
+ const completeBtnClass = task.completed
332
+ ? 'complete-btn-done'
333
+ : `complete-btn-${task.priority}`;
334
+
335
  taskElement.innerHTML = `
336
  <div class="flex items-center space-x-3 flex-1">
337
+ <button class="complete-btn w-5 h-5 rounded border ${completeBtnClass} flex items-center justify-center">
338
+ ${task.completed ? '<i class="fas fa-check text-xs text-white"></i>' : ''}
339
  </button>
340
+ <div class="flex-1">
341
+ <div class="flex items-center">
342
+ <span class="task-text ${task.completed ? 'text-gray-400' : 'text-gray-800'}">${task.text}</span>
343
+ </div>
344
+ ${task.dueDate ? `
345
+ <div class="text-xs text-gray-500 mt-1 ml-7">
346
+ <i class="far fa-calendar-alt mr-1"></i>
347
+ ${formatDate(task.dueDate)}
348
+ ${dueDateDisplay}
349
+ </div>
350
+ ` : ''}
351
+ </div>
352
  </div>
353
  <div class="flex space-x-2">
354
  <button class="edit-btn text-gray-500 hover:text-gray-700">
 
389
  const taskElement = document.querySelector(`.task-item[data-id="${taskId}"]`);
390
  const taskTextElement = taskElement.querySelector('.task-text');
391
 
392
+ const editForm = document.createElement('div');
393
+ editForm.className = 'w-full';
394
+ editForm.innerHTML = `
395
+ <div class="mb-2">
396
+ <input type="text" value="${task.text}" class="task-edit-input w-full px-3 py-2 border border-gray-300 rounded">
397
+ </div>
398
+ <div class="flex space-x-2">
399
+ <div class="flex-1">
400
+ <label class="block text-xs text-gray-600 mb-1">截止日期</label>
401
+ <input type="date" value="${task.dueDate || ''}" class="due-date-edit w-full px-3 py-1 border border-gray-300 rounded">
402
+ </div>
403
+ <div class="flex-1">
404
+ <label class="block text-xs text-gray-600 mb-1">优先级</label>
405
+ <select class="priority-edit w-full px-3 py-1 border border-gray-300 rounded">
406
+ <option value="normal" ${task.priority === 'normal' ? 'selected' : ''}>普通</option>
407
+ <option value="high" ${task.priority === 'high' ? 'selected' : ''}>高</option>
408
+ <option value="urgent" ${task.priority === 'urgent' ? 'selected' : ''}>紧急</option>
409
+ </select>
410
+ </div>
411
+ </div>
412
+ <div class="mt-2 flex justify-end space-x-2">
413
+ <button class="save-edit px-3 py-1 bg-gray-900 text-white text-sm rounded">保存</button>
414
+ <button class="cancel-edit px-3 py-1 border border-gray-300 text-gray-700 text-sm rounded">取消</button>
415
+ </div>
416
+ `;
417
 
418
+ taskTextElement.parentElement.parentElement.replaceWith(editForm);
 
419
 
420
+ const saveBtn = editForm.querySelector('.save-edit');
421
+ const cancelBtn = editForm.querySelector('.cancel-edit');
422
+ const editInput = editForm.querySelector('.task-edit-input');
423
+ const dueDateEdit = editForm.querySelector('.due-date-edit');
424
+ const priorityEdit = editForm.querySelector('.priority-edit');
425
+
426
+ editInput.focus();
427
+
428
+ const saveChanges = () => {
429
+ const newText = editInput.value.trim();
430
+ if (newText !== '' && (newText !== task.text || dueDateEdit.value !== task.dueDate || priorityEdit.value !== task.priority)) {
431
+ task.text = newText;
432
+ task.dueDate = dueDateEdit.value;
433
+ task.priority = priorityEdit.value;
434
+ saveTasks();
435
  }
436
+ renderTasks();
437
  };
438
 
439
+ saveBtn.addEventListener('click', saveChanges);
440
+
441
+ cancelBtn.addEventListener('click', () => {
442
+ renderTasks();
443
+ });
444
+
445
+ editInput.addEventListener('keypress', function(e) {
446
+ if (e.key === 'Enter') saveChanges();
447
+ });
448
  }
449
 
450
  function deleteTask(taskId) {
 
471
  });
472
 
473
  const activeBtn = document.getElementById(`filter${filter.charAt(0).toUpperCase() + filter.slice(1)}`);
474
+ if (activeBtn) {
475
+ activeBtn.classList.add('active', 'bg-gray-900', 'text-white');
476
+ activeBtn.classList.remove('border', 'hover:bg-gray-100', 'text-gray-700');
477
+ }
478
 
479
  renderTasks();
480
  }
 
483
  const total = tasks.length;
484
  const completed = tasks.filter(task => task.completed).length;
485
  const remaining = total - completed;
486
+ const dueToday = tasks.filter(task => isDueToday(task.dueDate) && !task.completed).length;
487
 
488
  totalTasks.textContent = total;
489
  completedTasks.textContent = completed;
490
  remainingTasks.textContent = remaining;
491
+ dueTodayTasks.textContent = dueToday;
492
  }
493
 
494
  function saveTasks() {
495
  localStorage.setItem('tasks', JSON.stringify(tasks));
496
  }
497
+
498
+ // 辅助函数
499
+ function formatDate(dateString) {
500
+ if (!dateString) return '';
501
+ const date = new Date(dateString);
502
+ return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`;
503
+ }
504
+
505
+ function isDueToday(dateString) {
506
+ if (!dateString) return false;
507
+
508
+ const today = new Date();
509
+ today.setHours(0, 0, 0, 0);
510
+
511
+ const dueDate = new Date(dateString);
512
+ dueDate.setHours(0, 0, 0, 0);
513
+
514
+ return dueDate.getTime() === today.getTime();
515
+ }
516
  });
517
  </script>
518
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=uu531/web" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>