duqing2026 commited on
Commit
7d6d57d
·
1 Parent(s): dd323d7
__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, send_file, request
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
- // Initial State
207
- const checklist = ref({
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
- checklist.value = JSON.parse(saved);
 
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('${data}'));
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');