Commit ·
7d6d57d
1
Parent(s): dd323d7
优化
Browse files- __pycache__/app.cpython-314.pyc +0 -0
- app.py +1 -2
- templates/index.html +51 -8
__pycache__/app.cpython-314.pyc
ADDED
|
Binary file (805 Bytes). View file
|
|
|
app.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
-
from flask import Flask, render_template
|
| 2 |
-
import os
|
| 3 |
|
| 4 |
app = Flask(__name__)
|
| 5 |
app.jinja_env.variable_start_string = '[['
|
|
|
|
| 1 |
+
from flask import Flask, render_template
|
|
|
|
| 2 |
|
| 3 |
app = Flask(__name__)
|
| 4 |
app.jinja_env.variable_start_string = '[['
|
templates/index.html
CHANGED
|
@@ -94,6 +94,9 @@
|
|
| 94 |
<button @click="exportHTML" class="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 rounded-md border-t border-gray-100 mt-1 pt-2">
|
| 95 |
<i class="fa-solid fa-file-export mr-2 text-gray-400"></i> 导出独立 HTML
|
| 96 |
</button>
|
|
|
|
|
|
|
|
|
|
| 97 |
</div>
|
| 98 |
</div>
|
| 99 |
<input type="file" ref="fileInput" @change="importJSON" class="hidden" accept=".json">
|
|
@@ -134,6 +137,10 @@
|
|
| 134 |
|
| 135 |
<!-- Checklist Items -->
|
| 136 |
<div class="space-y-8">
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
<div v-for="(section, sIndex) in checklist.sections" :key="sIndex" class="group">
|
| 138 |
<!-- Section Header -->
|
| 139 |
<div class="flex items-center justify-between mb-4 pb-2 border-b border-gray-100">
|
|
@@ -203,8 +210,8 @@
|
|
| 203 |
const mode = ref('edit'); // edit, preview
|
| 204 |
const fileInput = ref(null);
|
| 205 |
|
| 206 |
-
//
|
| 207 |
-
const
|
| 208 |
title: '新产品发布检查清单',
|
| 209 |
description: '这是一个示例清单,帮助你按部就班地完成产品发布。你可以点击上方的“编辑”按钮来修改内容。',
|
| 210 |
sections: [
|
|
@@ -227,12 +234,42 @@
|
|
| 227 |
]
|
| 228 |
});
|
| 229 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
// Persistence
|
| 231 |
onMounted(() => {
|
| 232 |
const saved = localStorage.getItem('checklist-data');
|
| 233 |
if (saved) {
|
| 234 |
try {
|
| 235 |
-
|
|
|
|
| 236 |
} catch(e) { console.error('Load failed', e); }
|
| 237 |
}
|
| 238 |
});
|
|
@@ -303,7 +340,7 @@
|
|
| 303 |
reader.onload = (e) => {
|
| 304 |
try {
|
| 305 |
const data = JSON.parse(e.target.result);
|
| 306 |
-
checklist.value = data;
|
| 307 |
alert('导入成功!');
|
| 308 |
} catch (err) {
|
| 309 |
alert('导入失败:文件格式不正确');
|
|
@@ -317,8 +354,7 @@
|
|
| 317 |
// We will fetch the current page's HTML structure but inject the current data state
|
| 318 |
// and set the mode to 'preview' by default in the generated file.
|
| 319 |
// For simplicity, we'll create a template string here.
|
| 320 |
-
|
| 321 |
-
const data = JSON.stringify(checklist.value).replace(/'/g, "\\'");
|
| 322 |
|
| 323 |
const htmlContent = `<!DOCTYPE html>
|
| 324 |
<html lang="zh-CN">
|
|
@@ -408,7 +444,7 @@
|
|
| 408 |
const { createApp, ref, computed, watch, onMounted } = Vue;
|
| 409 |
createApp({
|
| 410 |
setup() {
|
| 411 |
-
const checklist = ref(JSON.parse('${
|
| 412 |
|
| 413 |
// Local Storage for the exported file too!
|
| 414 |
const storageKey = 'checklist-export-' + checklist.value.title.replace(/\\s+/g, '-').toLowerCase();
|
|
@@ -475,7 +511,14 @@
|
|
| 475 |
importJSON,
|
| 476 |
exportHTML,
|
| 477 |
fileInput,
|
| 478 |
-
triggerFileInput
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 479 |
};
|
| 480 |
}
|
| 481 |
}).mount('#app');
|
|
|
|
| 94 |
<button @click="exportHTML" class="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 rounded-md border-t border-gray-100 mt-1 pt-2">
|
| 95 |
<i class="fa-solid fa-file-export mr-2 text-gray-400"></i> 导出独立 HTML
|
| 96 |
</button>
|
| 97 |
+
<button @click="resetToExample" class="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 rounded-md border-t border-gray-100 mt-1 pt-2">
|
| 98 |
+
<i class="fa-solid fa-rotate-left mr-2 text-gray-400"></i> 重置为示例
|
| 99 |
+
</button>
|
| 100 |
</div>
|
| 101 |
</div>
|
| 102 |
<input type="file" ref="fileInput" @change="importJSON" class="hidden" accept=".json">
|
|
|
|
| 137 |
|
| 138 |
<!-- Checklist Items -->
|
| 139 |
<div class="space-y-8">
|
| 140 |
+
<div v-if="!checklist.sections || checklist.sections.length === 0" class="text-center text-gray-400 py-16">
|
| 141 |
+
<i class="fa-regular fa-rectangle-list text-4xl mb-3"></i>
|
| 142 |
+
<div class="text-sm">暂无章节,点击下方“添加新章节”开始编辑</div>
|
| 143 |
+
</div>
|
| 144 |
<div v-for="(section, sIndex) in checklist.sections" :key="sIndex" class="group">
|
| 145 |
<!-- Section Header -->
|
| 146 |
<div class="flex items-center justify-between mb-4 pb-2 border-b border-gray-100">
|
|
|
|
| 210 |
const mode = ref('edit'); // edit, preview
|
| 211 |
const fileInput = ref(null);
|
| 212 |
|
| 213 |
+
// Example Data Factory
|
| 214 |
+
const exampleData = () => ({
|
| 215 |
title: '新产品发布检查清单',
|
| 216 |
description: '这是一个示例清单,帮助你按部就班地完成产品发布。你可以点击上方的“编辑”按钮来修改内容。',
|
| 217 |
sections: [
|
|
|
|
| 234 |
]
|
| 235 |
});
|
| 236 |
|
| 237 |
+
// Normalize helper: 填充缺省字段并保证结构可用
|
| 238 |
+
const normalizeChecklist = (data) => {
|
| 239 |
+
const fallback = exampleData();
|
| 240 |
+
const out = typeof data === 'object' && data ? data : {};
|
| 241 |
+
out.title = typeof out.title === 'string' ? out.title : fallback.title;
|
| 242 |
+
out.description = typeof out.description === 'string' ? out.description : '';
|
| 243 |
+
if (!Array.isArray(out.sections)) out.sections = [];
|
| 244 |
+
out.sections = out.sections.map(sec => {
|
| 245 |
+
const s = typeof sec === 'object' && sec ? sec : {};
|
| 246 |
+
s.title = typeof s.title === 'string' ? s.title : '';
|
| 247 |
+
if (!Array.isArray(s.items)) s.items = [];
|
| 248 |
+
s.items = s.items.map(it => {
|
| 249 |
+
const i = typeof it === 'object' && it ? it : {};
|
| 250 |
+
i.text = typeof i.text === 'string' ? i.text : '';
|
| 251 |
+
i.checked = typeof i.checked === 'boolean' ? i.checked : false;
|
| 252 |
+
return i;
|
| 253 |
+
});
|
| 254 |
+
// 至少保留一个空项目,避免渲染异常
|
| 255 |
+
if (s.items.length === 0) s.items = [{ text: '', checked: false }];
|
| 256 |
+
return s;
|
| 257 |
+
});
|
| 258 |
+
// 如果完全没有章节,放入示例章节
|
| 259 |
+
if (out.sections.length === 0) out.sections = fallback.sections;
|
| 260 |
+
return out;
|
| 261 |
+
};
|
| 262 |
+
|
| 263 |
+
// Initial State
|
| 264 |
+
const checklist = ref(exampleData());
|
| 265 |
+
|
| 266 |
// Persistence
|
| 267 |
onMounted(() => {
|
| 268 |
const saved = localStorage.getItem('checklist-data');
|
| 269 |
if (saved) {
|
| 270 |
try {
|
| 271 |
+
const loaded = JSON.parse(saved);
|
| 272 |
+
checklist.value = normalizeChecklist(loaded);
|
| 273 |
} catch(e) { console.error('Load failed', e); }
|
| 274 |
}
|
| 275 |
});
|
|
|
|
| 340 |
reader.onload = (e) => {
|
| 341 |
try {
|
| 342 |
const data = JSON.parse(e.target.result);
|
| 343 |
+
checklist.value = normalizeChecklist(data);
|
| 344 |
alert('导入成功!');
|
| 345 |
} catch (err) {
|
| 346 |
alert('导入失败:文件格式不正确');
|
|
|
|
| 354 |
// We will fetch the current page's HTML structure but inject the current data state
|
| 355 |
// and set the mode to 'preview' by default in the generated file.
|
| 356 |
// For simplicity, we'll create a template string here.
|
| 357 |
+
const encoded = encodeURIComponent(JSON.stringify(checklist.value));
|
|
|
|
| 358 |
|
| 359 |
const htmlContent = `<!DOCTYPE html>
|
| 360 |
<html lang="zh-CN">
|
|
|
|
| 444 |
const { createApp, ref, computed, watch, onMounted } = Vue;
|
| 445 |
createApp({
|
| 446 |
setup() {
|
| 447 |
+
const checklist = ref(JSON.parse(decodeURIComponent('${encoded}')));
|
| 448 |
|
| 449 |
// Local Storage for the exported file too!
|
| 450 |
const storageKey = 'checklist-export-' + checklist.value.title.replace(/\\s+/g, '-').toLowerCase();
|
|
|
|
| 511 |
importJSON,
|
| 512 |
exportHTML,
|
| 513 |
fileInput,
|
| 514 |
+
triggerFileInput,
|
| 515 |
+
resetToExample: () => {
|
| 516 |
+
if (confirm('确定重置为示例数据?此操作会覆盖当前内容。')) {
|
| 517 |
+
const ex = exampleData();
|
| 518 |
+
checklist.value = normalizeChecklist(ex);
|
| 519 |
+
localStorage.setItem('checklist-data', JSON.stringify(checklist.value));
|
| 520 |
+
}
|
| 521 |
+
}
|
| 522 |
};
|
| 523 |
}
|
| 524 |
}).mount('#app');
|