Docfile commited on
Commit
5064228
·
verified ·
1 Parent(s): d083d33

Update templates/philosophie.html

Browse files
Files changed (1) hide show
  1. templates/philosophie.html +224 -219
templates/philosophie.html CHANGED
@@ -1,9 +1,9 @@
1
  <!DOCTYPE html>
2
  <html lang="fr">
3
  <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width,initial-scale=1" />
6
- <title>Assistant - Télécharger PDF sans afficher</title>
7
 
8
  <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
9
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
@@ -13,272 +13,277 @@
13
  <link href="https://fonts.googleapis.com/css2?family=Kalam&family=Lato:wght@400;700&display=swap" rel="stylesheet">
14
 
15
  <style>
16
- body { font-family: 'Lato', sans-serif; margin: 20px; background:#f4f4f9; color:#333; }
17
- .container { max-width:700px; margin:0 auto; background:white; padding:20px; border-radius:8px; box-shadow:0 6px 18px rgba(0,0,0,0.06); }
18
- textarea { width:100%; min-height:80px; padding:10px; margin-bottom:10px; font-size:15px; }
19
- button { width:100%; padding:12px; font-size:16px; background:#2980b9; color:white; border:0; border-radius:6px; cursor:pointer; }
20
- button:disabled { background:#95a5a6; cursor:not-allowed; }
21
- .loader { margin:12px auto; border:6px solid #eee; border-top:6px solid #2980b9; border-radius:50%; width:36px; height:36px; animation:spin 1s linear infinite; }
22
- @keyframes spin { to { transform:rotate(360deg);} }
23
- .dissertation-paper { /* styles utilisés pour le rendu PDF (réutilisés dans l'élément hors-écran) */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  font-family: 'Kalam', cursive;
25
- font-size:18px;
26
- color:#1a2a4c;
27
- background-color:#fdfaf4;
28
- line-height:1.6;
29
  background-image: linear-gradient(transparent 97%, #d8e2ee 98%);
30
- background-size:100% 36px;
31
- border-left:2px solid #ffaaab;
32
- padding:28px;
33
- box-sizing:border-box;
 
 
 
 
 
34
  }
35
- .dissertation-paper h2 { text-align:center; margin-bottom:18px; }
36
- .dissertation-paper .indented { text-indent:3em; text-align:justify; margin:0 0 8px 0; }
37
- .prof { text-align:center; font-style:italic; margin-bottom:12px; }
38
- .transition { margin-top:12px; font-style:italic; color:#4a6a9c; }
39
- /* évite que l'élément hors-écran soit pris en compte dans l'affichage */
40
- #pdf-hidden { position:absolute; left:-9999px; top:0; width:210mm; background:#fff; }
 
 
 
 
41
  </style>
42
  </head>
43
  <body>
44
  <div id="app" class="container">
45
- <h1>Générer & télécharger le PDF (sans affichage)</h1>
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- <!-- Garde le textarea si tu veux fournir le sujet ; sinon supprime ce bloc -->
48
- <textarea v-model="question" placeholder="Entrez votre sujet de dissertation ici..."></textarea>
49
 
50
- <!-- Ce bouton déclenche la génération + téléchargement sans afficher la réponse -->
51
- <!-- data-html2canvas-ignore empêche le bouton d'être capturé par html2canvas -->
52
- <button @click="generateAndDownloadPDF" :disabled="isLoading" data-html2canvas-ignore="true">
53
- [[ isLoading ? 'Génération et téléchargement en cours...' : 'Générer et télécharger le PDF' ]]
54
- </button>
55
 
56
- <div v-if="isLoading" class="loader" aria-hidden="true"></div>
57
- <p v-if="errorMessage" style="color:#c0392b;margin-top:12px;">[[ errorMessage ]]</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  </div>
59
 
60
  <script>
61
  const { createApp } = Vue;
62
 
63
- createApp({
64
  data() {
65
  return {
66
  question: '',
67
  isLoading: false,
68
- errorMessage: null
 
69
  }
70
  },
71
  methods: {
72
- // petit utilitaire pour échapper HTML (sécurité de base)
73
- escapeHtml(str) {
74
- if (!str) return '';
75
- return String(str)
76
- .replace(/&/g, '&amp;')
77
- .replace(/</g, '&lt;')
78
- .replace(/>/g, '&gt;')
79
- .replace(/"/g, '&quot;')
80
- .replace(/'/g, '&#39;');
81
- },
82
-
83
- // construit le HTML (mêmes classes que .dissertation-paper)
84
- buildDissertationHTML(data) {
85
- const e = this.escapeHtml.bind(this);
86
- let html = '';
87
- html += `<h2>Sujet : ${e(data.sujet || '')}</h2>`;
88
- html += `<p class="prof">Prof : ${e(data.prof || '')}</p>`;
89
- html += `<h3>Introduction</h3>`;
90
- html += `<p class="indented">${e(data.introduction || '')}</p>`;
91
-
92
- (data.parties || []).forEach(partie => {
93
- html += `<div class="development-block">`;
94
- html += `<p class="indented">${e(partie.chapeau || '')}</p>`;
95
- (partie.arguments || []).forEach(arg => {
96
- html += `<p class="indented">${e(arg.paragraphe_argumentatif || '')}</p>`;
97
- });
98
- html += `</div>`;
99
- if (partie.transition) {
100
- html += `<p class="indented transition">${e(partie.transition)}</p>`;
101
- }
102
- });
103
-
104
- html += `<h3>Conclusion</h3>`;
105
- html += `<p class="indented">${e(data.conclusion || '')}</p>`;
106
- return html;
107
- },
108
-
109
- async generateAndDownloadPDF() {
110
  if (!this.question.trim()) {
111
  this.errorMessage = "Veuillez entrer un sujet de dissertation.";
112
  return;
113
  }
114
  this.isLoading = true;
115
  this.errorMessage = null;
116
-
117
  try {
118
- // 1) Appel API pour récupérer la dissertation (même structure JSON qu'avant)
119
- const res = await fetch('/api/generate_dissertation', {
120
  method: 'POST',
121
  headers: { 'Content-Type': 'application/json' },
122
  body: JSON.stringify({ question: this.question })
123
  });
124
- const data = await res.json();
125
- if (!res.ok) throw new Error(data.error || 'Erreur serveur lors de la génération.');
126
-
127
- // 2) construire le HTML à insérer dans l'élément hors-écran
128
- const inner = this.buildDissertationHTML(data);
129
-
130
- // 3) créer l'élément hors-écran (important : pas display:none)
131
- let hidden = document.getElementById('pdf-hidden');
132
- if (hidden) hidden.remove(); // nettoyage si présent
133
- hidden = document.createElement('div');
134
- hidden.id = 'pdf-hidden';
135
- hidden.className = 'dissertation-paper';
136
- hidden.style.position = 'absolute';
137
- hidden.style.left = '-9999px';
138
- hidden.style.top = '0';
139
- hidden.style.width = '210mm'; // correspond à la largeur A4
140
- hidden.style.backgroundColor = '#ffffff';
141
- hidden.innerHTML = inner;
142
- document.body.appendChild(hidden);
143
 
144
- // 4) attendre le chargement des polices web (important)
 
 
 
 
 
 
145
  if (document.fonts && document.fonts.ready) {
146
  await document.fonts.ready;
147
  }
 
148
 
149
- // 5) config html2pdf et génération
150
  const options = {
151
- margin: [10, 10, 10, 10], // mm
152
  filename: 'dissertation-philosophie.pdf',
153
  image: { type: 'jpeg', quality: 0.98 },
154
- html2canvas: { scale: 2, useCORS: true, backgroundColor: '#ffffff' },
 
 
 
 
 
155
  jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
156
  };
157
 
158
- await html2pdf().set(options).from(hidden).save();
159
-
160
- // 6) nettoyage
161
- hidden.remove();
162
-
163
  } catch (err) {
164
- console.error('Erreur génération PDF:', err);
165
- this.errorMessage = err.message || 'Erreur inconnue lors de la génération du PDF.';
166
- } finally {
167
- this.isLoading = false;
168
  }
169
  }
170
  }
171
- })
172
- // éviter conflit Jinja2
173
- .config && (Vue.createApp ? null : null); // noop to keep linter tranquille
174
-
175
- const app = createApp ? createApp() : null; // sécurité
176
- // actually create and mount app with custom delimiters
177
- const mountedApp = (function() {
178
- const a = Vue.createApp ? Vue.createApp({
179
- data: () => ({ /* unused - actual instance created above via createApp in chain */ })
180
- }) : null;
181
- // Instead we will create using the previously-defined createApp call:
182
- // To avoid confusion, re-create properly:
183
- const realApp = Vue.createApp({
184
- data() {
185
- return {
186
- question: '',
187
- isLoading: false,
188
- errorMessage: null
189
- };
190
- }
191
- });
192
- // set delimiters to avoid Jinja conflicts
193
- realApp.config.compilerOptions = realApp.config.compilerOptions || {};
194
- realApp.config.compilerOptions.delimiters = ['[[', ']]'];
195
 
196
- // mount the app and replace the placeholder methods with those defined earlier
197
- // We'll reuse the methods from the earlier createApp block by redefining them here:
198
- realApp.mixin({
199
- methods: {
200
- escapeHtml(str) {
201
- if (!str) return '';
202
- return String(str)
203
- .replace(/&/g, '&amp;')
204
- .replace(/</g, '&lt;')
205
- .replace(/>/g, '&gt;')
206
- .replace(/"/g, '&quot;')
207
- .replace(/'/g, '&#39;');
208
- },
209
- buildDissertationHTML(data) {
210
- const e = this.escapeHtml.bind(this);
211
- let html = '';
212
- html += `<h2>Sujet : ${e(data.sujet || '')}</h2>`;
213
- html += `<p class="prof">Prof : ${e(data.prof || '')}</p>`;
214
- html += `<h3>Introduction</h3>`;
215
- html += `<p class="indented">${e(data.introduction || '')}</p>`;
216
- (data.parties || []).forEach(partie => {
217
- html += `<div class="development-block">`;
218
- html += `<p class="indented">${e(partie.chapeau || '')}</p>`;
219
- (partie.arguments || []).forEach(arg => {
220
- html += `<p class="indented">${e(arg.paragraphe_argumentatif || '')}</p>`;
221
- });
222
- html += `</div>`;
223
- if (partie.transition) html += `<p class="indented transition">${e(partie.transition)}</p>`;
224
- });
225
- html += `<h3>Conclusion</h3>`;
226
- html += `<p class="indented">${e(data.conclusion || '')}</p>`;
227
- return html;
228
- },
229
- async generateAndDownloadPDF() {
230
- if (!this.question.trim()) {
231
- this.errorMessage = "Veuillez entrer un sujet de dissertation.";
232
- return;
233
- }
234
- this.isLoading = true;
235
- this.errorMessage = null;
236
- try {
237
- const res = await fetch('/api/generate_dissertation', {
238
- method: 'POST',
239
- headers: { 'Content-Type': 'application/json' },
240
- body: JSON.stringify({ question: this.question })
241
- });
242
- const data = await res.json();
243
- if (!res.ok) throw new Error(data.error || 'Erreur serveur.');
244
-
245
- const inner = this.buildDissertationHTML(data);
246
- let hidden = document.getElementById('pdf-hidden');
247
- if (hidden) hidden.remove();
248
- hidden = document.createElement('div');
249
- hidden.id = 'pdf-hidden';
250
- hidden.className = 'dissertation-paper';
251
- hidden.style.position = 'absolute';
252
- hidden.style.left = '-9999px';
253
- hidden.style.top = '0';
254
- hidden.style.width = '210mm';
255
- hidden.style.backgroundColor = '#ffffff';
256
- hidden.innerHTML = inner;
257
- document.body.appendChild(hidden);
258
-
259
- if (document.fonts && document.fonts.ready) await document.fonts.ready;
260
-
261
- const options = {
262
- margin: [10,10,10,10],
263
- filename: 'dissertation-philosophie.pdf',
264
- image: { type: 'jpeg', quality: 0.98 },
265
- html2canvas: { scale: 2, useCORS: true, backgroundColor: '#ffffff' },
266
- jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
267
- };
268
- await html2pdf().set(options).from(hidden).save();
269
- hidden.remove();
270
- } catch (err) {
271
- console.error(err);
272
- this.errorMessage = err.message || 'Erreur lors de la génération du PDF.';
273
- } finally {
274
- this.isLoading = false;
275
- }
276
- }
277
- }
278
- });
279
 
280
- return realApp.mount('#app');
281
- })();
282
  </script>
283
  </body>
284
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="fr">
3
  <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <title>Assistant de Philosophie (Vue.js)</title>
7
 
8
  <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
9
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
 
13
  <link href="https://fonts.googleapis.com/css2?family=Kalam&family=Lato:wght@400;700&display=swap" rel="stylesheet">
14
 
15
  <style>
16
+ :root {
17
+ --primary-color: #4f46e5; /* Indigo */
18
+ --primary-hover: #4338ca;
19
+ --text-color: #374151; /* Gris foncé */
20
+ --background-color: #f8f9fa;
21
+ --container-bg: #ffffff;
22
+ --border-color: #e5e7eb;
23
+ }
24
+
25
+ body {
26
+ font-family: 'Lato', sans-serif;
27
+ background-color: var(--background-color);
28
+ margin: 0;
29
+ padding: 2rem;
30
+ color: var(--text-color);
31
+ -webkit-font-smoothing: antialiased;
32
+ -moz-osx-font-smoothing: grayscale;
33
+ }
34
+
35
+ .container {
36
+ max-width: 960px;
37
+ margin: 0 auto;
38
+ background: var(--container-bg);
39
+ padding: 40px;
40
+ border-radius: 12px;
41
+ box-shadow: 0 6px 20px rgba(0,0,0,0.08);
42
+ }
43
+
44
+ h1 {
45
+ text-align: center;
46
+ color: #111827;
47
+ margin-bottom: 0.5em;
48
+ }
49
+
50
+ .type-indicator {
51
+ text-align: center;
52
+ color: #6b7280;
53
+ font-size: 1.1em;
54
+ margin-bottom: 2em;
55
+ }
56
+
57
+ textarea {
58
+ width: 100%;
59
+ padding: 15px;
60
+ border-radius: 8px;
61
+ border: 1px solid var(--border-color);
62
+ min-height: 100px;
63
+ margin-bottom: 15px;
64
+ font-size: 16px;
65
+ line-height: 1.6;
66
+ transition: border-color 0.3s, box-shadow 0.3s;
67
+ }
68
+
69
+ textarea:focus {
70
+ outline: none;
71
+ border-color: var(--primary-color);
72
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.2);
73
+ }
74
+
75
+ button {
76
+ display: block;
77
+ box-sizing: border-box;
78
+ width: 100%;
79
+ padding: 16px;
80
+ background-color: var(--primary-color);
81
+ color: white;
82
+ border: none;
83
+ border-radius: 8px;
84
+ font-size: 18px;
85
+ font-weight: 700;
86
+ cursor: pointer;
87
+ transition: background-color 0.3s, transform 0.2s;
88
+ }
89
+
90
+ button:hover:not(:disabled) {
91
+ background-color: var(--primary-hover);
92
+ transform: translateY(-2px);
93
+ }
94
+
95
+ button:disabled {
96
+ background-color: #9ca3af;
97
+ cursor: not-allowed;
98
+ }
99
+
100
+ /* Style pour le bouton de téléchargement */
101
+ .download-container {
102
+ margin-top: 15px;
103
+ }
104
+ .secondary-button {
105
+ background-color: #f3f4f6; /* Gris clair */
106
+ color: #374151; /* Texte foncé */
107
+ border: 1px solid #d1d5db; /* Bordure subtile */
108
+ }
109
+ .secondary-button:hover:not(:disabled) {
110
+ background-color: #e5e7eb; /* Gris plus foncé au survol */
111
+ border-color: #9ca3af;
112
+ }
113
+
114
+ .loader {
115
+ display: block;
116
+ border: 8px solid #f3f3f3;
117
+ border-top: 8px solid var(--primary-color);
118
+ border-radius: 50%;
119
+ width: 50px;
120
+ height: 50px;
121
+ animation: spin 1s linear infinite;
122
+ margin: 30px auto;
123
+ }
124
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
125
+
126
+ .error {
127
+ color: #ef4444;
128
+ background-color: #fee2e2;
129
+ text-align: center;
130
+ margin-top: 20px;
131
+ padding: 15px;
132
+ border-radius: 8px;
133
+ font-weight: bold;
134
+ }
135
+
136
+ /* Styles de la feuille de dissertation */
137
+ .dissertation-paper {
138
  font-family: 'Kalam', cursive;
139
+ font-size: 20px;
140
+ color: #1a2a4c;
141
+ background-color: #fdfaf4;
142
+ line-height: 2;
143
  background-image: linear-gradient(transparent 97%, #d8e2ee 98%);
144
+ background-size: 100% 40px;
145
+ border-left: 3px solid #ffaaab;
146
+ padding-left: 4em;
147
+ margin: 40px -40px -40px -40px;
148
+ padding-top: 30px;
149
+ padding-bottom: 40px;
150
+ padding-right: 30px;
151
+ -webkit-print-color-adjust: exact;
152
+ print-color-adjust: exact;
153
  }
154
+ .dissertation-paper h2 { font-size: 1.5em; text-align: center; margin-bottom: 1.5em; }
155
+ .dissertation-paper h3 { font-size: 1.2em; margin-top: 3em; margin-bottom: 1.5em; text-transform: uppercase; text-decoration: underline; }
156
+ .dissertation-paper .development-block { margin-top: 3em; }
157
+ .dissertation-paper p { text-align: justify; margin: 0; padding: 0; }
158
+ .dissertation-paper .prof { text-align: center; font-style: italic; margin-bottom: 2em; }
159
+ .dissertation-paper .indented { text-indent: 3em; }
160
+ .dissertation-paper .transition { margin-top: 2em; margin-bottom: 2em; font-style: italic; color: #4a6a9c; }
161
+ .dissertation-paper, .dissertation-paper * { box-sizing: border-box; }
162
+
163
+ .avoid-page-break { page-break-inside: avoid; break-inside: avoid; }
164
  </style>
165
  </head>
166
  <body>
167
  <div id="app" class="container">
168
+ <h1>Assistant de Dissertation Philosophique</h1>
169
+ <p class="type-indicator">Type 1</p>
170
+
171
+ <form @submit.prevent="generateDissertation">
172
+ <textarea v-model="question" placeholder="Entrez votre sujet de dissertation ici..."></textarea>
173
+ <button type="submit" :disabled="isLoading">
174
+ [[ isLoading ? 'Génération en cours...' : 'Générer la dissertation' ]]
175
+ </button>
176
+ </form>
177
+
178
+ <!-- NOUVEL EMPLACEMENT DU BOUTON PDF -->
179
+ <div v-if="dissertation" class="download-container" data-html2canvas-ignore="true">
180
+ <button class="secondary-button" @click="generatePDF">Télécharger la dissertation en PDF</button>
181
+ </div>
182
 
183
+ <div v-if="isLoading" class="loader"></div>
184
+ <p v-if="errorMessage" class="error">[[ errorMessage ]]</p>
185
 
186
+ <div v-if="dissertation" id="dissertation-content" class="dissertation-paper">
187
+
188
+ <h2>Sujet : [[ dissertation.sujet ]]</h2>
189
+ <p class="prof">Prof : [[ dissertation.prof ]]</p>
 
190
 
191
+ <h3>Introduction</h3>
192
+ <p class="indented">[[ dissertation.introduction ]]</p>
193
+
194
+ <div v-for="partie in dissertation.parties" :key="partie.chapeau" class="avoid-page-break">
195
+ <div class="development-block">
196
+ <p class="indented">[[ partie.chapeau ]]</p>
197
+ <p v-for="(arg, idx) in partie.arguments" :key="idx" class="indented">
198
+ [[ arg.paragraphe_argumentatif ]]
199
+ </p>
200
+ </div>
201
+ <p v-if="partie.transition" class="indented transition">[[ partie.transition ]]</p>
202
+ </div>
203
+
204
+ <h3>Conclusion</h3>
205
+ <p class="indented">[[ dissertation.conclusion ]]</p>
206
+
207
+ </div>
208
  </div>
209
 
210
  <script>
211
  const { createApp } = Vue;
212
 
213
+ const app = createApp({
214
  data() {
215
  return {
216
  question: '',
217
  isLoading: false,
218
+ errorMessage: null,
219
+ dissertation: null
220
  }
221
  },
222
  methods: {
223
+ async generateDissertation() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  if (!this.question.trim()) {
225
  this.errorMessage = "Veuillez entrer un sujet de dissertation.";
226
  return;
227
  }
228
  this.isLoading = true;
229
  this.errorMessage = null;
230
+ this.dissertation = null;
231
  try {
232
+ const response = await fetch('/api/generate_dissertation', {
 
233
  method: 'POST',
234
  headers: { 'Content-Type': 'application/json' },
235
  body: JSON.stringify({ question: this.question })
236
  });
237
+ const data = await response.json();
238
+ if (!response.ok) {
239
+ throw new Error(data.error || "Une erreur inconnue est survenue.");
240
+ }
241
+ this.dissertation = data;
242
+ } catch (error) {
243
+ this.errorMessage = error.message;
244
+ } finally {
245
+ this.isLoading = false;
246
+ }
247
+ },
 
 
 
 
 
 
 
 
248
 
249
+ async generatePDF() {
250
+ const element = document.getElementById('dissertation-content');
251
+ if (!element) {
252
+ this.errorMessage = "Contenu introuvable pour le PDF.";
253
+ return;
254
+ }
255
+ try {
256
  if (document.fonts && document.fonts.ready) {
257
  await document.fonts.ready;
258
  }
259
+ window.scrollTo(0, 0);
260
 
 
261
  const options = {
262
+ margin: [15, 15, 15, 15],
263
  filename: 'dissertation-philosophie.pdf',
264
  image: { type: 'jpeg', quality: 0.98 },
265
+ html2canvas: {
266
+ scale: 2,
267
+ useCORS: true,
268
+ logging: true,
269
+ backgroundColor: null,
270
+ },
271
  jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
272
  };
273
 
274
+ await html2pdf().set(options).from(element).save();
 
 
 
 
275
  } catch (err) {
276
+ console.error('Erreur html2pdf:', err);
277
+ this.errorMessage = "Erreur lors de la génération du PDF. Voir la console pour les détails.";
 
 
278
  }
279
  }
280
  }
281
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
 
283
+ // ⚠️ Changer les délimiteurs pour éviter le conflit avec Jinja2
284
+ app.config.compilerOptions.delimiters = ['[[', ']]'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
 
286
+ app.mount('#app');
 
287
  </script>
288
  </body>
289
  </html>