@darkchest/wck 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/demo/Todo.vue +664 -0
- package/package.json +36 -0
- package/readme.md +774 -0
- package/src/compiler-helper.js +34 -0
- package/src/compiler-template.js +199 -0
- package/src/compilers/compileScriptDefault.js +333 -0
- package/src/compilers/compileScriptSetup.js +5 -0
- package/src/compilers/compileStyles.js +29 -0
- package/src/compilers/compileTemplate.js +62 -0
- package/src/component.js +2 -0
- package/src/index.js +50 -0
- package/src/petite-vue/petite-vue.es.js +1 -0
- package/src/petite-vue/petite-vue.iife.js +1 -0
- package/src/petite-vue/petite-vue.umd.js +1 -0
- package/src/utils/Logger.js +7 -0
- package/src/utils/index.js +2 -0
- package/src/utils/isComp.js +29 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2025 @darkchest
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/demo/Todo.vue
ADDED
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div id="app">
|
|
3
|
+
<div class="todo-container">
|
|
4
|
+
<!-- Header Slot -->
|
|
5
|
+
<header>
|
|
6
|
+
<slot name="header">
|
|
7
|
+
<h1 class="app-title">{{ appTitle }}</h1>
|
|
8
|
+
<p class="app-description">使用 Vue2 实现的 Todo List 应用</p>
|
|
9
|
+
</slot>
|
|
10
|
+
</header>
|
|
11
|
+
|
|
12
|
+
<!-- Add Todo Form -->
|
|
13
|
+
<div class="add-todo">
|
|
14
|
+
<input
|
|
15
|
+
type="text"
|
|
16
|
+
v-model="newTodo"
|
|
17
|
+
placeholder="请输入待办事项..."
|
|
18
|
+
@keyup.enter="addTodo"
|
|
19
|
+
class="todo-input"
|
|
20
|
+
/>
|
|
21
|
+
<button @click="addTodo" class="add-btn">添加</button>
|
|
22
|
+
<button @click="clearCompleted" class="clear-btn">清除已完成</button>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<!-- Filter Controls -->
|
|
26
|
+
<div class="filter-controls">
|
|
27
|
+
<button
|
|
28
|
+
@click="filter = 'all'"
|
|
29
|
+
:class="{ active: filter === 'all' }"
|
|
30
|
+
class="filter-btn"
|
|
31
|
+
>
|
|
32
|
+
全部 ({{ totalTodos }})
|
|
33
|
+
</button>
|
|
34
|
+
<button
|
|
35
|
+
@click="filter = 'active'"
|
|
36
|
+
:class="{ active: filter === 'active' }"
|
|
37
|
+
class="filter-btn"
|
|
38
|
+
>
|
|
39
|
+
未完成 ({{ activeTodosCount }})
|
|
40
|
+
</button>
|
|
41
|
+
<button
|
|
42
|
+
@click="filter = 'completed'"
|
|
43
|
+
:class="{ active: filter === 'completed' }"
|
|
44
|
+
class="filter-btn"
|
|
45
|
+
>
|
|
46
|
+
已完成 ({{ completedTodosCount }})
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<!-- Todo List -->
|
|
51
|
+
<div class="todo-list">
|
|
52
|
+
<div v-if="filteredTodos.length === 0" class="empty-state">
|
|
53
|
+
<p v-if="todos.length === 0">暂无待办事项,请添加一个吧!</p>
|
|
54
|
+
<p v-else>没有{{ filter === 'active' ? '未完成' : '已完成' }}的事项</p>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div
|
|
58
|
+
v-for="todo in filteredTodos"
|
|
59
|
+
:key="todo.id"
|
|
60
|
+
class="todo-item"
|
|
61
|
+
:class="{ completed: todo.completed }"
|
|
62
|
+
>
|
|
63
|
+
<div class="todo-content">
|
|
64
|
+
<input
|
|
65
|
+
type="checkbox"
|
|
66
|
+
v-model="todo.completed"
|
|
67
|
+
class="todo-checkbox"
|
|
68
|
+
:id="'todo-' + todo.id"
|
|
69
|
+
/>
|
|
70
|
+
<label :for="'todo-' + todo.id" class="todo-text">
|
|
71
|
+
<span>{{ todo.text }}</span>
|
|
72
|
+
<span class="todo-date">{{ formatDate(todo.createdAt) }}</span>
|
|
73
|
+
</label>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="todo-actions">
|
|
76
|
+
<button @click="editTodo(todo)" class="edit-btn">编辑</button>
|
|
77
|
+
<button @click="deleteTodo(todo.id)" class="delete-btn">删除</button>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<!-- Stats -->
|
|
83
|
+
<div class="stats">
|
|
84
|
+
<p v-if="todos.length > 0">
|
|
85
|
+
已完成 {{ completedTodosCount }} / 总共 {{ totalTodos }} 个待办事项
|
|
86
|
+
<span v-if="hasCompletedTodos" class="completion-rate">
|
|
87
|
+
(完成率: {{ completionRate }}%)
|
|
88
|
+
</span>
|
|
89
|
+
</p>
|
|
90
|
+
|
|
91
|
+
<!-- Progress Bar Slot -->
|
|
92
|
+
<slot name="progress">
|
|
93
|
+
<div class="progress-container">
|
|
94
|
+
<div class="progress-bar" :style="{ width: completionRate + '%' }"></div>
|
|
95
|
+
</div>
|
|
96
|
+
</slot>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<!-- Footer Slot -->
|
|
100
|
+
<footer>
|
|
101
|
+
<slot name="footer">
|
|
102
|
+
<p class="footer-text">双击事项可标记为完成/未完成</p>
|
|
103
|
+
<p class="footer-text">使用 Vue2 实现 - 包含 Props, Data, Methods, Slots, Watch, Computed, 生命周期</p>
|
|
104
|
+
</slot>
|
|
105
|
+
</footer>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<!-- Edit Modal -->
|
|
109
|
+
<div v-if="editingTodo" class="modal-overlay" @click="cancelEdit">
|
|
110
|
+
<div class="modal" @click.stop>
|
|
111
|
+
<h3>编辑待办事项</h3>
|
|
112
|
+
<input
|
|
113
|
+
type="text"
|
|
114
|
+
v-model="editingTodo.text"
|
|
115
|
+
@keyup.enter="saveEdit"
|
|
116
|
+
class="edit-input"
|
|
117
|
+
/>
|
|
118
|
+
<div class="modal-actions">
|
|
119
|
+
<button @click="saveEdit" class="save-btn">保存</button>
|
|
120
|
+
<button @click="cancelEdit" class="cancel-btn">取消</button>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</template>
|
|
126
|
+
|
|
127
|
+
<script>
|
|
128
|
+
export default {
|
|
129
|
+
name: 'TodoApp',
|
|
130
|
+
|
|
131
|
+
// Props 示例
|
|
132
|
+
props: {
|
|
133
|
+
initialTodos: {
|
|
134
|
+
type: [Array, Object],
|
|
135
|
+
default: () => []
|
|
136
|
+
},
|
|
137
|
+
appTitle: {
|
|
138
|
+
type: String,
|
|
139
|
+
default: '我的待办清单'
|
|
140
|
+
},
|
|
141
|
+
enableLocalStorage: {
|
|
142
|
+
type: Boolean,
|
|
143
|
+
default: true
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
// Data 示例
|
|
148
|
+
data() {
|
|
149
|
+
return {
|
|
150
|
+
newTodo: '',
|
|
151
|
+
todos: [],
|
|
152
|
+
filter: 'all',
|
|
153
|
+
editingTodo: null,
|
|
154
|
+
lastTodoId: 0
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
// Computed 示例
|
|
159
|
+
computed: {
|
|
160
|
+
// 计算过滤后的待办事项
|
|
161
|
+
filteredTodos() {
|
|
162
|
+
switch (this.filter) {
|
|
163
|
+
case 'active':
|
|
164
|
+
return this.todos.filter(todo => !todo.completed);
|
|
165
|
+
case 'completed':
|
|
166
|
+
return this.todos.filter(todo => todo.completed);
|
|
167
|
+
default:
|
|
168
|
+
return this.todos;
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
// 计算未完成事项数量
|
|
173
|
+
activeTodosCount() {
|
|
174
|
+
return this.todos.filter(todo => !todo.completed).length;
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// 计算已完成事项数量
|
|
178
|
+
completedTodosCount() {
|
|
179
|
+
return this.todos.filter(todo => todo.completed).length;
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
// 计算总事项数量
|
|
183
|
+
totalTodos() {
|
|
184
|
+
return this.todos.length;
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
// 计算完成率
|
|
188
|
+
completionRate() {
|
|
189
|
+
if (this.totalTodos === 0) return 0;
|
|
190
|
+
return Math.round((this.completedTodosCount / this.totalTodos) * 100);
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
// 检查是否有已完成事项
|
|
194
|
+
hasCompletedTodos() {
|
|
195
|
+
return this.completedTodosCount > 0;
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
// Watch 示例
|
|
200
|
+
watch: {
|
|
201
|
+
// 监听 todos 变化并保存到 localStorage
|
|
202
|
+
todos: {
|
|
203
|
+
handler(newTodos, oldTodos) {
|
|
204
|
+
if (this.enableLocalStorage) {
|
|
205
|
+
localStorage.setItem('vue-todos', JSON.stringify(newTodos));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 发送事件给父组件(如果需要)
|
|
209
|
+
this.$emit('todos-updated', newTodos);
|
|
210
|
+
},
|
|
211
|
+
deep: true
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
// 监听 filter 变化
|
|
215
|
+
filter(newFilter, oldFilter) {
|
|
216
|
+
console.log('过滤器已更改为: '+newFilter);
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
// Methods 示例
|
|
221
|
+
methods: {
|
|
222
|
+
// 添加新的待办事项
|
|
223
|
+
addTodo() {
|
|
224
|
+
if (this.newTodo.trim() === '') return;
|
|
225
|
+
|
|
226
|
+
this.todos.push({
|
|
227
|
+
id: ++this.lastTodoId,
|
|
228
|
+
text: this.newTodo.trim(),
|
|
229
|
+
completed: false,
|
|
230
|
+
createdAt: new Date()
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
this.newTodo = '';
|
|
234
|
+
console.log('已添加新的待办事项');
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
// 删除待办事项
|
|
238
|
+
deleteTodo(id) {
|
|
239
|
+
const index = this.todos.findIndex(todo => todo.id === id);
|
|
240
|
+
if (index !== -1) {
|
|
241
|
+
this.todos.splice(index, 1);
|
|
242
|
+
console.log('已删除待办事项 ID: '+id);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
// 编辑待办事项
|
|
247
|
+
editTodo(todo) {
|
|
248
|
+
this.editingTodo = { ...todo };
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
// 保存编辑
|
|
252
|
+
saveEdit() {
|
|
253
|
+
if (this.editingTodo && this.editingTodo.text.trim() !== '') {
|
|
254
|
+
const index = this.todos.findIndex(todo => todo.id === this.editingTodo.id);
|
|
255
|
+
if (index !== -1) {
|
|
256
|
+
this.$set(this.todos, index, { ...this.editingTodo });
|
|
257
|
+
console.log('已更新待办事项 ID: '+this.editingTodo.id);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
this.editingTodo = null;
|
|
261
|
+
// 已知这里会报错, 这是petite-vue内部的bug, 因为并不影响使用, 所以我不打算修复(没有petite-vue源码).
|
|
262
|
+
// 感觉这是因为v-if的优先级没有v-model高导致的, 所以我们可以通过额外设置一个开关变量来绕开这个报错, 只要不设置对象为null就行.
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
// 取消编辑
|
|
266
|
+
cancelEdit() {
|
|
267
|
+
this.editingTodo = null;
|
|
268
|
+
// 已知这里会报错, 这是petite-vue内部的bug, 因为并不影响使用, 所以我不打算修复(没有petite-vue源码).
|
|
269
|
+
// 感觉这是因为v-if的优先级没有v-model高导致的, 所以我们可以通过额外设置一个开关变量来绕开这个报错, 只要不设置对象为null就行.
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
// 清除已完成事项
|
|
273
|
+
clearCompleted() {
|
|
274
|
+
const originalLength = this.todos.length;
|
|
275
|
+
this.todos = this.todos.filter(todo => !todo.completed);
|
|
276
|
+
const clearedCount = originalLength - this.todos.length;
|
|
277
|
+
console.log('已清除 '+clearedCount+' 个已完成事项');
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
// 格式化日期
|
|
281
|
+
formatDate(date) {
|
|
282
|
+
return new Date(date).toLocaleDateString('zh-CN', {
|
|
283
|
+
month: 'short',
|
|
284
|
+
day: 'numeric',
|
|
285
|
+
hour: '2-digit',
|
|
286
|
+
minute: '2-digit'
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
// 生命周期钩子示例
|
|
292
|
+
created() {
|
|
293
|
+
console.log('TodoApp 组件已创建');
|
|
294
|
+
|
|
295
|
+
// 从 props 或 localStorage 初始化 todos
|
|
296
|
+
if (this.initialTodos && this.initialTodos.length > 0) {
|
|
297
|
+
this.todos = [...this.initialTodos];
|
|
298
|
+
this.lastTodoId = Math.max(...this.initialTodos.map(todo => todo.id), 0);
|
|
299
|
+
} else if (this.enableLocalStorage) {
|
|
300
|
+
const savedTodos = localStorage.getItem('vue-todos');
|
|
301
|
+
if (savedTodos) {
|
|
302
|
+
try {
|
|
303
|
+
this.todos = JSON.parse(savedTodos);
|
|
304
|
+
this.lastTodoId = Math.max(...this.todos.map(todo => todo.id), 0);
|
|
305
|
+
console.log('从 localStorage 加载待办事项');
|
|
306
|
+
} catch (e) {
|
|
307
|
+
console.error('解析保存的待办事项失败:', e);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
mounted() {
|
|
314
|
+
console.log('TodoApp 组件已挂载到 DOM');
|
|
315
|
+
|
|
316
|
+
// 为每个待办事项添加双击切换完成状态的功能
|
|
317
|
+
this.$nextTick(() => {
|
|
318
|
+
const todoItems = document.querySelectorAll('.todo-item');
|
|
319
|
+
todoItems.forEach(item => {
|
|
320
|
+
item.addEventListener('dblclick', () => {
|
|
321
|
+
const todoId = parseInt(item.dataset.id);
|
|
322
|
+
const todoIndex = this.todos.findIndex(todo => todo.id === todoId);
|
|
323
|
+
if (todoIndex !== -1) {
|
|
324
|
+
this.todos[todoIndex].completed = !this.todos[todoIndex].completed;
|
|
325
|
+
console.log('双击切换待办事项状态 ID: '+todoId);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
beforeUpdate() {
|
|
333
|
+
console.log('TodoApp 组件即将更新');
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
updated() {
|
|
337
|
+
console.log('TodoApp 组件已更新');
|
|
338
|
+
},
|
|
339
|
+
|
|
340
|
+
beforeDestroy() {
|
|
341
|
+
console.log('TodoApp 组件即将销毁');
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
destroyed() {
|
|
345
|
+
console.log('TodoApp 组件已销毁');
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
</script>
|
|
349
|
+
|
|
350
|
+
<style lang="scss" scoped>
|
|
351
|
+
#app {
|
|
352
|
+
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
|
353
|
+
-webkit-font-smoothing: antialiased;
|
|
354
|
+
-moz-osx-font-smoothing: grayscale;
|
|
355
|
+
color: #2c3e50;
|
|
356
|
+
max-width: 800px;
|
|
357
|
+
margin: 0 auto;
|
|
358
|
+
padding: 20px;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.todo-container {
|
|
362
|
+
background: white;
|
|
363
|
+
border-radius: 10px;
|
|
364
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
|
365
|
+
padding: 30px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
header {
|
|
369
|
+
text-align: center;
|
|
370
|
+
margin-bottom: 30px;
|
|
371
|
+
border-bottom: 1px solid #eee;
|
|
372
|
+
padding-bottom: 20px;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.app-title {
|
|
376
|
+
color: #42b983;
|
|
377
|
+
font-size: 2.5rem;
|
|
378
|
+
margin-bottom: 10px;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.app-description {
|
|
382
|
+
color: #7f8c8d;
|
|
383
|
+
font-size: 1rem;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.add-todo {
|
|
387
|
+
display: flex;
|
|
388
|
+
gap: 10px;
|
|
389
|
+
margin-bottom: 20px;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.todo-input {
|
|
393
|
+
flex: 1;
|
|
394
|
+
padding: 12px 15px;
|
|
395
|
+
border: 2px solid #e0e0e0;
|
|
396
|
+
border-radius: 6px;
|
|
397
|
+
font-size: 16px;
|
|
398
|
+
transition: border-color 0.3s;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.todo-input:focus {
|
|
402
|
+
border-color: #42b983;
|
|
403
|
+
outline: none;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.add-btn, .clear-btn {
|
|
407
|
+
padding: 12px 20px;
|
|
408
|
+
border: none;
|
|
409
|
+
border-radius: 6px;
|
|
410
|
+
font-weight: bold;
|
|
411
|
+
cursor: pointer;
|
|
412
|
+
transition: all 0.3s;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.add-btn {
|
|
416
|
+
background-color: #42b983;
|
|
417
|
+
color: white;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.add-btn:hover {
|
|
421
|
+
background-color: #3aa876;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.clear-btn {
|
|
425
|
+
background-color: #f0f0f0;
|
|
426
|
+
color: #333;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.clear-btn:hover {
|
|
430
|
+
background-color: #e0e0e0;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.filter-controls {
|
|
434
|
+
display: flex;
|
|
435
|
+
gap: 10px;
|
|
436
|
+
margin-bottom: 25px;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.filter-btn {
|
|
440
|
+
flex: 1;
|
|
441
|
+
padding: 10px;
|
|
442
|
+
background-color: #f8f9fa;
|
|
443
|
+
border: 1px solid #dee2e6;
|
|
444
|
+
border-radius: 6px;
|
|
445
|
+
cursor: pointer;
|
|
446
|
+
transition: all 0.3s;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.filter-btn.active {
|
|
450
|
+
background-color: #42b983;
|
|
451
|
+
color: white;
|
|
452
|
+
border-color: #42b983;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.filter-btn:hover:not(.active) {
|
|
456
|
+
background-color: #e9ecef;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.todo-list {
|
|
460
|
+
margin-bottom: 25px;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.empty-state {
|
|
464
|
+
text-align: center;
|
|
465
|
+
padding: 40px 20px;
|
|
466
|
+
color: #95a5a6;
|
|
467
|
+
font-style: italic;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.todo-item {
|
|
471
|
+
display: flex;
|
|
472
|
+
justify-content: space-between;
|
|
473
|
+
align-items: center;
|
|
474
|
+
padding: 15px;
|
|
475
|
+
border-bottom: 1px solid #f0f0f0;
|
|
476
|
+
transition: all 0.3s;
|
|
477
|
+
border-radius: 6px;
|
|
478
|
+
margin-bottom: 8px;
|
|
479
|
+
background-color: #f9f9f9;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.todo-item:hover {
|
|
483
|
+
background-color: #f0f0f0;
|
|
484
|
+
transform: translateY(-2px);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.todo-item.completed {
|
|
488
|
+
opacity: 0.8;
|
|
489
|
+
background-color: #f0f9f0;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.todo-content {
|
|
493
|
+
display: flex;
|
|
494
|
+
align-items: center;
|
|
495
|
+
gap: 15px;
|
|
496
|
+
flex: 1;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.todo-checkbox {
|
|
500
|
+
width: 20px;
|
|
501
|
+
height: 20px;
|
|
502
|
+
cursor: pointer;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.todo-text {
|
|
506
|
+
flex: 1;
|
|
507
|
+
cursor: pointer;
|
|
508
|
+
display: flex;
|
|
509
|
+
justify-content: space-between;
|
|
510
|
+
align-items: center;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.todo-item.completed .todo-text span:first-child {
|
|
514
|
+
text-decoration: line-through;
|
|
515
|
+
color: #95a5a6;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.todo-date {
|
|
519
|
+
font-size: 0.8rem;
|
|
520
|
+
color: #95a5a6;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.todo-actions {
|
|
524
|
+
display: flex;
|
|
525
|
+
gap: 8px;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.edit-btn, .delete-btn {
|
|
529
|
+
padding: 6px 12px;
|
|
530
|
+
border: none;
|
|
531
|
+
border-radius: 4px;
|
|
532
|
+
font-size: 0.9rem;
|
|
533
|
+
cursor: pointer;
|
|
534
|
+
transition: all 0.2s;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.edit-btn {
|
|
538
|
+
background-color: #f0f0f0;
|
|
539
|
+
color: #333;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.edit-btn:hover {
|
|
543
|
+
background-color: #e0e0e0;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.delete-btn {
|
|
547
|
+
background-color: #ff6b6b;
|
|
548
|
+
color: white;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.delete-btn:hover {
|
|
552
|
+
background-color: #ff5252;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.stats {
|
|
556
|
+
margin-top: 25px;
|
|
557
|
+
padding-top: 20px;
|
|
558
|
+
border-top: 1px solid #eee;
|
|
559
|
+
color: #7f8c8d;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.completion-rate {
|
|
563
|
+
color: #42b983;
|
|
564
|
+
font-weight: bold;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.progress-container {
|
|
568
|
+
height: 10px;
|
|
569
|
+
background-color: #f0f0f0;
|
|
570
|
+
border-radius: 5px;
|
|
571
|
+
margin-top: 10px;
|
|
572
|
+
overflow: hidden;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.progress-bar {
|
|
576
|
+
height: 100%;
|
|
577
|
+
background-color: #42b983;
|
|
578
|
+
border-radius: 5px;
|
|
579
|
+
transition: width 0.5s ease;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
footer {
|
|
583
|
+
margin-top: 30px;
|
|
584
|
+
padding-top: 20px;
|
|
585
|
+
border-top: 1px solid #eee;
|
|
586
|
+
text-align: center;
|
|
587
|
+
color: #95a5a6;
|
|
588
|
+
font-size: 0.9rem;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.footer-text {
|
|
592
|
+
margin: 5px 0;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/* 编辑模态框样式 */
|
|
596
|
+
.modal-overlay {
|
|
597
|
+
position: fixed;
|
|
598
|
+
top: 0;
|
|
599
|
+
left: 0;
|
|
600
|
+
right: 0;
|
|
601
|
+
bottom: 0;
|
|
602
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
603
|
+
display: flex;
|
|
604
|
+
justify-content: center;
|
|
605
|
+
align-items: center;
|
|
606
|
+
z-index: 1000;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.modal {
|
|
610
|
+
background-color: white;
|
|
611
|
+
padding: 30px;
|
|
612
|
+
border-radius: 10px;
|
|
613
|
+
width: 90%;
|
|
614
|
+
max-width: 500px;
|
|
615
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.modal h3 {
|
|
619
|
+
margin-top: 0;
|
|
620
|
+
margin-bottom: 20px;
|
|
621
|
+
color: #2c3e50;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.edit-input {
|
|
625
|
+
width: 100%;
|
|
626
|
+
padding: 12px 15px;
|
|
627
|
+
border: 2px solid #42b983;
|
|
628
|
+
border-radius: 6px;
|
|
629
|
+
font-size: 16px;
|
|
630
|
+
margin-bottom: 20px;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.modal-actions {
|
|
634
|
+
display: flex;
|
|
635
|
+
gap: 10px;
|
|
636
|
+
justify-content: flex-end;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
.save-btn, .cancel-btn {
|
|
640
|
+
padding: 10px 20px;
|
|
641
|
+
border: none;
|
|
642
|
+
border-radius: 6px;
|
|
643
|
+
font-weight: bold;
|
|
644
|
+
cursor: pointer;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.save-btn {
|
|
648
|
+
background-color: #42b983;
|
|
649
|
+
color: white;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
.save-btn:hover {
|
|
653
|
+
background-color: #3aa876;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.cancel-btn {
|
|
657
|
+
background-color: #f0f0f0;
|
|
658
|
+
color: #333;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.cancel-btn:hover {
|
|
662
|
+
background-color: #e0e0e0;
|
|
663
|
+
}
|
|
664
|
+
</style>
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@darkchest/wck",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "@darkchest/wck是一个通过包装 Vue 单文件组件 (SFC) 并将它转换为通用的web-component组件的解决方案。",
|
|
5
|
+
"private": false,
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "src/index.js",
|
|
10
|
+
"./component": "src/component.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"vue2",
|
|
14
|
+
"web-component"
|
|
15
|
+
],
|
|
16
|
+
"author": "destiny.michael",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public",
|
|
19
|
+
"registry": "https://registry.npmjs.org/"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@babel/core": "^7.28.5",
|
|
24
|
+
"@babel/generator": "^7.28.5",
|
|
25
|
+
"@babel/parser": "^7.28.5",
|
|
26
|
+
"@babel/traverse": "^7.28.5",
|
|
27
|
+
"@babel/types": "^7.28.5",
|
|
28
|
+
"@vue/compiler-sfc": "^3.5.26",
|
|
29
|
+
"sass": "^1.94.2"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"dev": "node src/vite.js",
|
|
33
|
+
"serve": "npm run dev",
|
|
34
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
35
|
+
}
|
|
36
|
+
}
|