From d744e00c6652c963588f5cfb22131f36e39a35b7 Mon Sep 17 00:00:00 2001 From: jinchao <383321154@qq.com> Date: Wed, 7 May 2025 16:02:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9EQ&A=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Release/database/XNSim.db | Bin 163840 -> 172032 bytes XNSimHtml/assets/icons/png/question.png | Bin 0 -> 5573 bytes XNSimHtml/assets/icons/png/question_b.png | Bin 0 -> 6307 bytes XNSimHtml/components/content-area.js | 3 + XNSimHtml/components/qa-component.js | 846 ++++++++++++++++++++++ XNSimHtml/components/sub-toolbar.js | 4 + XNSimHtml/main.html | 9 + XNSimHtml/routes/qa.js | 102 +++ XNSimHtml/server.js | 5 +- XNSimHtml/utils/db-utils.js | 207 +++++- 10 files changed, 1174 insertions(+), 2 deletions(-) create mode 100644 XNSimHtml/assets/icons/png/question.png create mode 100644 XNSimHtml/assets/icons/png/question_b.png create mode 100644 XNSimHtml/components/qa-component.js create mode 100644 XNSimHtml/routes/qa.js diff --git a/Release/database/XNSim.db b/Release/database/XNSim.db index 55db87ac3826d3dc3b54d42ac533f20b035b9a11..39e9d65b2e360b65a9bb3c0576b61eb8cab09119 100644 GIT binary patch delta 1617 zcmcJPUu@e%9LIfe^UsNW6rn{`m~sdqlF}8&aS|3Zp)9$k5vdzFj>cXVvN?>1B^`;Y zJs^>$n>Hkbv{maeC_-!u4TRP$l&NJ}ftS5(F9@Ux?Gd%vow7AW;sMzw zpMSsK=Xbx~S9dox#Z3LgoOSn)Q4}>u#%?l9WQ?C13V^ZZo0nc@;W|abdldW&-h=lS z`q47XvL@OC-kmH|wcYT?`5SA?v#rMGt%cdO4^D4fy4kw;!}^t5>z|z3Xv}Y1zWATw zZ3M`xrDA#EVRFFYbiy+fd<6f1Gw{p;mp%a<3_SogD|4Ql$t}+0KsmkG&x2KX?(Fks zK$?Hn_L^dowx{WR-+bge$BSlzFA zY}5O+O7ZB?LS^!}n%4?BL&}_1ED^!;rQ?RB8k2EU!AOyli3FC>jsRk!BMJ$~rsM7S zaS3%(F#B;LDKQAukm2t7PzEb{QZ<*?&^{E)$}*OeoRS>F86`TFMh8Z*j8Rb~P6;0v zE~>9GSc*^c8LP|f2B*SW{_wGazBTmEpiy>oe+nbrh%8=G1VW9cuY(tAovBerj$}_t z3U0S3jVnmXrcy(lh$9natx(pGg5MzK$6WbI?d^$5FVB%mfymlvsI5l36%@xK(QHaF z`m1dj6(dizJMrnt>{2??bnR%HHd;|TR_NK?|0GFQkx+V5lFiNbuQGO-G~-*0 ztpLSjvi;up_&1R3gLlA6v;GTMT)GcNzkG%z=f=X_rr=$89v*^T?oaM^v;GxV6B#Q7 zp(_vwg*wM_>pDPuI1&^h{3|_7qFtwg!CsU<5)3^-wN0W;r$o`9YWm&NS&YQ++c93W z>R2FPV2@K`cpD{#MT<@e!VZ*3DEWy@qG;AJzkdfz*uI$1mKVSk3a-Gb970>Ox|cMy2Z77BP=0c-2p7pqI3wk~|%YxTVmf9P+> CxaBSY delta 587 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 R$b6Rtpo?hp8dJA~{{zPDQ%V2; literal 0 HcmV?d00001 diff --git a/XNSimHtml/assets/icons/png/question_b.png b/XNSimHtml/assets/icons/png/question_b.png new file mode 100644 index 0000000000000000000000000000000000000000..c3d7e5b52a4ce5064de5544faa2743052e8c2fc8 GIT binary patch literal 6307 zcmb7J`9DH8 z+Fd=j>lJMPJ;H0N1C>KOKLLQ#28+3QE5LT$=6t%1W`^=|Tb2*Nw&X;?vUlAf&s7`= z>~wsRxWfrO(CC0#N9fZ36UbBJX_K7q#S73m5F;KxCNe)2U<&7za7Gg%+Mwpb*Sa}FZuh@eVLMn*wkPkF_wJFZsqeYS%TgVXgkC^W z^&=o9AxVc~ZBx&V6oXL2QyUr@UW`}Sse)iv9yVl0-UJix3^sW$j*HuneVnGtF(Ryj z6ji#88bBhF_*PT|h~Vh<^iJ#8V;k&H1gc|*9Ur@f_D}r`^R?KWREn?PWo?e1K1#oL>Fj-7a7- zml{PDm`75#Hy+vS`#VUn9Jn8% zQq_fK1N@i<(A^H;Rr~o+psBgB(SN8v?bA`ucWWUS6lKID#WJ;eZu>t(->zXXARYOQfQ6}`?}q7a5wq+u^afG zq`k%X4!(3}-sm`%Jr3%~1n_*H0QQ~0k{un}xYDT5I1HmV2>9m@e*6QP9r+4kU6M|A zIl4JDjXf9L`I6Yg4pY6z{^CfA?yg$|(N?IS;bHT?tO}>ETC^juizQ|iDVy)*ag}6% z;N)4~nSmJgzK-X;cv?)9f%tkL&JmINnJ)DiIFd_L&Hd&yMnUl^#_zFQjHw#-;8jNe zcB+9ph|0(OVF!3@F9oefy@PqzEC(jD8Y{wAXPh^iD$B0AJQ$FpqdA`9{?5*?jlfyH zOqWn*z<@4d(Ge0HhhMd8gkg1c2W*?IJt?Ju8qDoDjBFC0a?tFbrGYs~=O_o0-iSrH zL5zl$!Xckb|h-hj*hu&hDgfxUV6E?CdZ%#4MZJ?+SYKIXI} zeF59gNE{+F3DRrAgkZd-@iY0*g*D*!qYqlLJG;B}mF6`I!XNxN+d!P00fuo4mLt(Z z8|ppYmGFX7>Aq_)eSaJO-iw(c>ymzcTn~7bMd3l6&V;CK;Y07WVv#jB!3AYJPF%o2 z^4Wza*9f2DVTqx;@B&5aiQ}TM=^zN8HJxLs%{;3?iRvsLu#CN~B<%!&DEYPCdBC^i zO&Y7QL=1rJ?KJ*{+97=t1@FhWfYliDtE$Q_7!T*C=N9lc&Z<+C^*FbbHai|RmuCzm za!GVXKpn*ZC8smbiQ~=thT*hIKRj^+-G}ADi;g6!&w9 ze!9U)jd6<$J~>y5dPeO2nb)7g&CmCD#zua5$p#Z*b}CX}`dTY7sI9K<(G50vX*zYH z6=>d??H}hwCiqY)e%85jOWL8~?b{HgtbzJ75h_v-RWbJ~LT(%sze2CrJj!(Wd90#~ z8nFEG^=m1W`TJgSee9*R=vt9p2{Bgc_7Ey-pk3ly0JlUm^a`j00?2hmv|k1fou|db z$TC>Q=PLU-yE@dGCmVuh5XQSn_S7(aHIZy=#U%8mAdGsZgXZY`L8?X?GE)u78We3k zdaqju;K7}1Za1?T$E1s(>Qt$piZLf#PLs0|f9u}8dsnr-x@yUvcKqdI?!V41c&SuH zm!tP3>UJ9oP`64r=N@ntZi^&IldXkNZXY@wQV3nny>fJA#k;wUbm}q8bQ`(-?GctO z7lzFdeY$nRgii}il7W>Tiz#i-Ld=l$@NZ$`>wNKrp65Q$aenbXPV0wkUh34i5qs;a z2bl+!d_4o_{?Q_iKSz@_ocU0hQ#7;Y)4YPLNs9Q;^7g2Lg2IVszuI&YDz!-p0B9>~ zSq=afgPJ#&1t@6vM{2Svklq7V3|}hArC-$U+zB;DabOYBEFP4G^=4v#ZGt$# zBgj-dOA+P1)@F`yuoF?)@+l=*ix*$j1KTF7jAE>3Ht@I?7qh(C{45Y6=DU%1r}p}Y z*?kKaiE{e5()KZzf*Ku1+-s=%_3Ze%J9T}%jkm5y-$jVs=fK|5^K$E%Pa4dIPrm=f zg)6la?#c=JXP+|m!IszQvKr#LC~0!_l_*-n0)ZwKfG`C)gOnM9W+xoo*U{2{MyEAc z@^hjltH~kvcY{A5K-x@­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 + :host { + display: block; + height: 100%; + overflow: auto; + padding: 20px; + box-sizing: border-box; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + background-color: #f5f7fa; + --primary-color: #1890ff; + --primary-hover: #40a9ff; + --danger-color: #ff4d4f; + --danger-hover: #ff7875; + --success-color: #52c41a; + --success-hover: #73d13d; + } + + .qa-container { + background-color: white; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + padding: 24px; + min-height: calc(100% - 40px); + box-sizing: border-box; + display: flex; + flex-direction: column; + } + + .qa-header { + display: flex; + justify-content: flex-end; + align-items: center; + margin-bottom: 24px; + } + + .ask-question-btn { + background-color: var(--primary-color); + color: white; + border: none; + padding: 10px 20px; + border-radius: 8px; + cursor: pointer; + font-size: 15px; + display: flex; + align-items: center; + gap: 8px; + transition: all 0.3s ease; + box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2); + } + + .ask-question-btn:hover { + background-color: var(--primary-hover); + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3); + } + + .qa-item { + background-color: white; + border-radius: 10px; + padding: 20px; + box-shadow: 0 2px 12px rgba(0,0,0,0.06); + transition: all 0.3s ease; + border: 1px solid #eef2f7; + } + + .qa-item:hover { + transform: translateY(-2px); + box-shadow: 0 4px 16px rgba(0,0,0,0.08); + } + + .qa-question { + font-weight: 600; + color: #2c3e50; + margin-bottom: 12px; + font-size: 18px; + display: flex; + justify-content: space-between; + align-items: center; + } + + .qa-answer { + color: #4a5568; + line-height: 1.7; + margin-top: 15px; + padding-top: 15px; + border-top: 1px solid #edf2f7; + } + + .answer-btn { + background-color: var(--primary-color); + color: white; + border: none; + padding: 8px 16px; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + margin-top: 12px; + transition: all 0.3s ease; + } + + .answer-btn:hover { + background-color: var(--primary-hover); + transform: translateY(-1px); + } + + .delete-btn { + background-color: var(--danger-color); + color: white; + border: none; + padding: 6px 12px; + border-radius: 6px; + cursor: pointer; + font-size: 13px; + transition: all 0.3s ease; + } + + .delete-btn:hover { + background-color: var(--danger-hover); + transform: translateY(-1px); + } + + .question-meta { + font-size: 13px; + color: #718096; + margin-top: 8px; + display: flex; + justify-content: space-between; + align-items: center; + } + + .form-group input, + .form-group textarea { + width: 100%; + padding: 12px; + border: 1px solid #e2e8f0; + border-radius: 8px; + font-size: 15px; + transition: all 0.3s ease; + box-sizing: border-box; + } + + .form-group input:focus, + .form-group textarea:focus { + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.1); + outline: none; + } + + .form-group textarea { + min-height: 120px; + max-height: 300px; + resize: vertical; + } + + .submit-btn { + background-color: var(--success-color); + color: white; + padding: 10px 20px; + border-radius: 8px; + transition: all 0.3s ease; + } + + .submit-btn:hover { + background-color: var(--success-hover); + transform: translateY(-1px); + } + + .cancel-btn { + background-color: var(--danger-color); + color: white; + padding: 10px 20px; + border-radius: 8px; + transition: all 0.3s ease; + } + + .cancel-btn:hover { + background-color: var(--danger-hover); + transform: translateY(-1px); + } + + .qa-content { + flex: 1; + display: flex; + flex-direction: column; + } + + .qa-list { + flex: 1; + display: flex; + flex-direction: column; + gap: 20px; + } + + .question-form { + display: none; + background-color: #f8f9fa; + border-radius: 8px; + padding: 20px; + margin-bottom: 20px; + } + + .question-form.active { + display: block; + } + + .form-actions { + display: flex; + justify-content: flex-end; + gap: 12px; + margin-top: 24px; + } + + .form-actions button { + padding: 10px 20px; + border: none; + border-radius: 8px; + cursor: pointer; + font-size: 14px; + transition: all 0.3s ease; + outline: none; + } + + .form-actions .submit-btn { + background-color: var(--primary-color); + color: white; + } + + .form-actions .submit-btn:hover { + background-color: var(--primary-hover); + transform: translateY(-1px); + } + + .form-actions .cancel-btn { + background-color: var(--danger-color); + color: white; + } + + .form-actions .cancel-btn:hover { + background-color: var(--danger-hover); + transform: translateY(-1px); + } + + .answer-form { + display: none; + margin-top: 10px; + } + + .answer-form.active { + display: block; + } + + .answer-btn.hidden { + display: none; + } + + .modal-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + z-index: 1000; + justify-content: center; + align-items: center; + } + + .modal-overlay.active { + display: flex; + } + + .modal-content { + background-color: white; + border-radius: 12px; + padding: 24px; + width: 90%; + max-width: 500px; + max-height: 90vh; + overflow-y: auto; + position: relative; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); + } + + .modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + } + + .modal-title { + font-size: 20px; + font-weight: 600; + color: #2c3e50; + } + + .modal-close { + background: none; + border: none; + font-size: 24px; + color: #718096; + cursor: pointer; + padding: 4px; + line-height: 1; + transition: color 0.3s ease; + outline: none; + } + + .modal-close:hover { + color: var(--primary-color); + } + + .question-form { + display: block; + background-color: transparent; + padding: 0; + margin: 0; + } + + .form-group { + margin-bottom: 20px; + } + + .form-group label { + display: block; + margin-bottom: 8px; + color: #4a5568; + font-weight: 500; + } + + .confirm-modal { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + z-index: 1001; + justify-content: center; + align-items: center; + } + + .confirm-modal.active { + display: flex; + } + + .confirm-content { + background-color: white; + border-radius: 12px; + padding: 24px; + width: 90%; + max-width: 400px; + position: relative; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); + } + + .confirm-title { + font-size: 18px; + font-weight: 600; + color: #2c3e50; + margin-bottom: 16px; + } + + .confirm-message { + color: #4a5568; + margin-bottom: 24px; + line-height: 1.5; + } + + .confirm-actions { + display: flex; + justify-content: flex-end; + gap: 12px; + } + + .confirm-actions button { + padding: 8px 16px; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + transition: all 0.3s ease; + outline: none; + } + + .confirm-actions .confirm-btn { + background-color: var(--danger-color); + color: white; + } + + .confirm-actions .confirm-btn:hover { + background-color: var(--danger-hover); + } + + .confirm-actions .cancel-btn { + background-color: #e2e8f0; + color: #4a5568; + } + + .confirm-actions .cancel-btn:hover { + background-color: #cbd5e0; + } + + .pagination { + display: flex; + justify-content: center; + align-items: center; + margin-top: 30px; + gap: 15px; + padding: 15px 0; + } + + .pagination-btn { + padding: 8px 16px; + border: 1px solid #ddd; + border-radius: 6px; + background-color: white; + color: #2d3748; + cursor: pointer; + transition: all 0.3s ease; + min-width: 80px; + } + + .pagination-btn:hover:not(:disabled) { + background-color: #f8f9fa; + border-color: var(--primary-color); + color: var(--primary-color); + } + + .pagination-btn:disabled { + background-color: #f8f9fa; + color: #a0aec0; + cursor: not-allowed; + } + + .page-info { + color: #4a5568; + font-size: 14px; + } + +
+
+ +
+
+
+ +
+ +
+
+ + + +
+
+
确认删除
+
确定要删除这个内容吗?此操作不可恢复。
+
+ + +
+
+
+ `; + } + + setupEventListeners() { + const askQuestionBtn = this.shadowRoot.querySelector('.ask-question-btn'); + const modal = this.shadowRoot.querySelector('.modal-overlay'); + const modalClose = this.shadowRoot.querySelector('.modal-close'); + const cancelBtn = this.shadowRoot.querySelector('.cancel-btn'); + const submitBtn = this.shadowRoot.querySelector('.submit-btn'); + + const closeModal = () => { + modal.classList.remove('active'); + this.shadowRoot.getElementById('questionTitle').value = ''; + this.shadowRoot.getElementById('questionContent').value = ''; + }; + + askQuestionBtn.addEventListener('click', () => { + modal.classList.add('active'); + }); + + modalClose.addEventListener('click', closeModal); + cancelBtn.addEventListener('click', closeModal); + + // 点击模态框外部关闭 + modal.addEventListener('click', (e) => { + if (e.target === modal) { + closeModal(); + } + }); + + submitBtn.addEventListener('click', async () => { + const title = this.shadowRoot.getElementById('questionTitle').value.trim(); + const content = this.shadowRoot.getElementById('questionContent').value.trim(); + + if (!title || !content) { + alert('请填写完整的问题信息'); + return; + } + + try { + const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}'); + const response = await fetch('/api/qa/questions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + title, + content, + userInfo + }) + }); + + const data = await response.json(); + if (data.success) { + closeModal(); + await this.loadQuestions(); + } else { + alert(data.message || '创建问题失败'); + } + } catch (error) { + console.error('创建问题失败:', error); + alert('创建问题失败,请稍后重试'); + } + }); + } + + async addAnswer(questionId, content) { + try { + const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}'); + const response = await fetch(`/api/qa/questions/${questionId}/answers`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + content, + userInfo + }) + }); + + const data = await response.json(); + if (data.success) { + await this.loadQuestions(); + } else { + alert(data.message || '添加回答失败'); + } + } catch (error) { + console.error('添加回答失败:', error); + alert('添加回答失败,请稍后重试'); + } + } + + async deleteQuestion(questionId) { + try { + const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}'); + const response = await fetch(`/api/qa/questions/${questionId}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ userInfo }) + }); + + const data = await response.json(); + if (data.success) { + await this.loadQuestions(); + } else { + alert(data.message || '删除问题失败'); + } + } catch (error) { + console.error('删除问题失败:', error); + alert('删除问题失败,请稍后重试'); + } + } + + async deleteAnswer(answerId) { + try { + const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}'); + const response = await fetch(`/api/qa/answers/${answerId}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ userInfo }) + }); + + const data = await response.json(); + if (data.success) { + await this.loadQuestions(); + } else { + alert(data.message || '删除回答失败'); + } + } catch (error) { + console.error('删除回答失败:', error); + alert('删除回答失败,请稍后重试'); + } + } + + showConfirmDialog(type, id) { + const modal = this.shadowRoot.querySelector('.confirm-modal'); + const confirmBtn = modal.querySelector('.confirm-btn'); + const cancelBtn = modal.querySelector('.cancel-btn'); + const message = modal.querySelector('.confirm-message'); + + message.textContent = type === 'question' ? + '确定要删除这个问题吗?此操作不可恢复。' : + '确定要删除这个回答吗?此操作不可恢复。'; + + const closeModal = () => { + modal.classList.remove('active'); + }; + + const handleConfirm = async () => { + if (type === 'question') { + await this.deleteQuestion(id); + } else { + await this.deleteAnswer(id); + } + closeModal(); + }; + + // 移除旧的事件监听器 + const newConfirmBtn = confirmBtn.cloneNode(true); + const newCancelBtn = cancelBtn.cloneNode(true); + confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn); + cancelBtn.parentNode.replaceChild(newCancelBtn, cancelBtn); + + // 添加新的事件监听器 + newConfirmBtn.addEventListener('click', handleConfirm); + newCancelBtn.addEventListener('click', closeModal); + + modal.classList.add('active'); + } + + renderQuestions() { + const qaList = this.shadowRoot.getElementById('qaList'); + + if (this.questions.length === 0) { + qaList.innerHTML = '
暂无问题
'; + return; + } + + this.totalPages = Math.ceil(this.questions.length / this.pageSize); + const startIndex = (this.currentPage - 1) * this.pageSize; + const endIndex = Math.min(startIndex + this.pageSize, this.questions.length); + const currentPageQuestions = this.questions.slice(startIndex, endIndex); + + qaList.innerHTML = currentPageQuestions.map(question => ` +
+
+ ${question.title} + +
+
+ 提问者:${question.author} | 时间:${question.created_at} +
+
${question.content}
+ ${question.answers.map(answer => ` +
+
${answer.content}
+
+ 回答者:${answer.author} | 时间:${answer.created_at} + +
+
+ `).join('')} + +
+
+ +
+
+ + +
+
+
+ `).join(''); + + // 添加分页 + const pagination = this.shadowRoot.getElementById('pagination'); + pagination.innerHTML = ` + + 第 ${this.currentPage} 页 / 共 ${this.totalPages} 页 + + `; + + // 添加分页按钮事件监听 + this.addPaginationListeners(); + + // 重新绑定回答按钮的事件监听器 + const answerBtns = qaList.querySelectorAll('.answer-btn'); + answerBtns.forEach(btn => { + btn.addEventListener('click', () => { + const questionId = parseInt(btn.dataset.questionId); + const answerForm = qaList.querySelector(`.answer-form[data-question-id="${questionId}"]`); + answerForm.classList.add('active'); + }); + }); + + // 绑定回答表单的事件监听器 + const answerForms = qaList.querySelectorAll('.answer-form'); + answerForms.forEach(form => { + const questionId = parseInt(form.dataset.questionId); + const cancelBtn = form.querySelector('.cancel-btn'); + const submitBtn = form.querySelector('.submit-btn'); + const textarea = form.querySelector('textarea'); + + cancelBtn.addEventListener('click', () => { + form.classList.remove('active'); + textarea.value = ''; + }); + + submitBtn.addEventListener('click', () => { + const content = textarea.value.trim(); + if (!content) { + alert('请输入回答内容'); + return; + } + this.addAnswer(questionId, content); + form.classList.remove('active'); + textarea.value = ''; + }); + }); + } + + // 添加分页按钮事件监听 + addPaginationListeners() { + const prevButton = this.shadowRoot.getElementById('prevPage'); + const nextButton = this.shadowRoot.getElementById('nextPage'); + + if (prevButton) { + prevButton.addEventListener('click', () => { + if (this.currentPage > 1) { + this.currentPage--; + this.renderQuestions(); + } + }); + } + + if (nextButton) { + nextButton.addEventListener('click', () => { + if (this.currentPage < this.totalPages) { + this.currentPage++; + this.renderQuestions(); + } + }); + } + } +} + +customElements.define('qa-component', QAComponent); \ No newline at end of file diff --git a/XNSimHtml/components/sub-toolbar.js b/XNSimHtml/components/sub-toolbar.js index ddfd19b..0bc1a4b 100644 --- a/XNSimHtml/components/sub-toolbar.js +++ b/XNSimHtml/components/sub-toolbar.js @@ -166,6 +166,10 @@ class SubToolbar extends HTMLElement { 帮助 帮助 +
+ Q&A + Q&A +
+