845 lines
29 KiB
JavaScript
845 lines
29 KiB
JavaScript
|
class QAComponent extends HTMLElement {
|
|||
|
constructor() {
|
|||
|
super();
|
|||
|
this.attachShadow({ mode: 'open' });
|
|||
|
this.accessLevel = 0;
|
|||
|
this.questions = [];
|
|||
|
this.currentPage = 1;
|
|||
|
this.pageSize = 5; // 每页显示5个问题
|
|||
|
this.totalPages = 1;
|
|||
|
}
|
|||
|
|
|||
|
connectedCallback() {
|
|||
|
this.checkUserAccess();
|
|||
|
this.loadQuestions();
|
|||
|
this.render();
|
|||
|
this.setupEventListeners();
|
|||
|
}
|
|||
|
|
|||
|
async checkUserAccess() {
|
|||
|
try {
|
|||
|
const userInfo = localStorage.getItem('userInfo');
|
|||
|
if (userInfo) {
|
|||
|
const user = JSON.parse(userInfo);
|
|||
|
this.accessLevel = user.access_level || 0;
|
|||
|
}
|
|||
|
} catch (error) {
|
|||
|
console.error('获取用户权限失败:', error);
|
|||
|
this.accessLevel = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
async loadQuestions() {
|
|||
|
try {
|
|||
|
const response = await fetch(`/api/qa/questions?sort=desc`);
|
|||
|
const data = await response.json();
|
|||
|
if (data.success) {
|
|||
|
this.questions = data.questions;
|
|||
|
this.renderQuestions();
|
|||
|
} else {
|
|||
|
console.error('加载问题失败:', data.message);
|
|||
|
}
|
|||
|
} catch (error) {
|
|||
|
console.error('加载问题失败:', error);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
render() {
|
|||
|
this.shadowRoot.innerHTML = `
|
|||
|
<style>
|
|||
|
: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;
|
|||
|
}
|
|||
|
</style>
|
|||
|
<div class="qa-container">
|
|||
|
<div class="qa-header">
|
|||
|
<button class="ask-question-btn">
|
|||
|
<img src="assets/icons/png/plus.png" alt="提问" width="16" height="16">
|
|||
|
提问
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
<div class="qa-content">
|
|||
|
<div class="qa-list" id="qaList">
|
|||
|
<!-- 问题列表将通过JavaScript动态添加 -->
|
|||
|
</div>
|
|||
|
<div class="pagination" id="pagination">
|
|||
|
<!-- 分页按钮将通过JavaScript动态添加 -->
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="modal-overlay" id="questionModal">
|
|||
|
<div class="modal-content">
|
|||
|
<div class="modal-header">
|
|||
|
<div class="modal-title">提问</div>
|
|||
|
<button class="modal-close">×</button>
|
|||
|
</div>
|
|||
|
<div class="question-form">
|
|||
|
<div class="form-group">
|
|||
|
<label for="questionTitle">问题标题</label>
|
|||
|
<input type="text" id="questionTitle" placeholder="请输入问题标题">
|
|||
|
</div>
|
|||
|
<div class="form-group">
|
|||
|
<label for="questionContent">问题内容</label>
|
|||
|
<textarea id="questionContent" placeholder="请详细描述您的问题"></textarea>
|
|||
|
</div>
|
|||
|
<div class="form-actions">
|
|||
|
<button class="cancel-btn">取消</button>
|
|||
|
<button class="submit-btn">提交问题</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="confirm-modal" id="confirmModal">
|
|||
|
<div class="confirm-content">
|
|||
|
<div class="confirm-title">确认删除</div>
|
|||
|
<div class="confirm-message">确定要删除这个内容吗?此操作不可恢复。</div>
|
|||
|
<div class="confirm-actions">
|
|||
|
<button class="cancel-btn">取消</button>
|
|||
|
<button class="confirm-btn">确认删除</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
`;
|
|||
|
}
|
|||
|
|
|||
|
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 = '<div class="error">暂无问题</div>';
|
|||
|
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 => `
|
|||
|
<div class="qa-item">
|
|||
|
<div class="qa-question">
|
|||
|
<span>${question.title}</span>
|
|||
|
<button class="delete-btn ${this.accessLevel >= 3 ? '' : 'hidden'}"
|
|||
|
onclick="this.getRootNode().host.showConfirmDialog('question', ${question.id})">
|
|||
|
删除
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
<div class="question-meta">
|
|||
|
提问者:${question.author} | 时间:${question.created_at}
|
|||
|
</div>
|
|||
|
<div class="qa-content">${question.content}</div>
|
|||
|
${question.answers.map(answer => `
|
|||
|
<div class="qa-answer">
|
|||
|
<div class="answer-content">${answer.content}</div>
|
|||
|
<div class="question-meta">
|
|||
|
<span>回答者:${answer.author} | 时间:${answer.created_at}</span>
|
|||
|
<button class="delete-btn ${this.accessLevel >= 3 ? '' : 'hidden'}"
|
|||
|
onclick="this.getRootNode().host.showConfirmDialog('answer', ${answer.id})">
|
|||
|
删除
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
`).join('')}
|
|||
|
<button class="answer-btn ${this.accessLevel >= 2 ? '' : 'hidden'}" data-question-id="${question.id}">
|
|||
|
回答
|
|||
|
</button>
|
|||
|
<div class="answer-form" data-question-id="${question.id}">
|
|||
|
<div class="form-group">
|
|||
|
<textarea placeholder="请输入您的回答"></textarea>
|
|||
|
</div>
|
|||
|
<div class="form-actions">
|
|||
|
<button class="cancel-btn">取消</button>
|
|||
|
<button class="submit-btn">提交回答</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
`).join('');
|
|||
|
|
|||
|
// 添加分页
|
|||
|
const pagination = this.shadowRoot.getElementById('pagination');
|
|||
|
pagination.innerHTML = `
|
|||
|
<button class="pagination-btn" id="prevPage" ${this.currentPage === 1 ? 'disabled' : ''}>
|
|||
|
上一页
|
|||
|
</button>
|
|||
|
<span class="page-info">第 ${this.currentPage} 页 / 共 ${this.totalPages} 页</span>
|
|||
|
<button class="pagination-btn" id="nextPage" ${this.currentPage === this.totalPages ? 'disabled' : ''}>
|
|||
|
下一页
|
|||
|
</button>
|
|||
|
`;
|
|||
|
|
|||
|
// 添加分页按钮事件监听
|
|||
|
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);
|