|
|
|
@ -1,78 +1,27 @@ |
|
|
|
<!-- src/components/Question/QuestionTable.vue --> |
|
|
|
<template> |
|
|
|
<div class="question-table-container"> |
|
|
|
<!-- 表格容器 --> |
|
|
|
<div class="table-container"> |
|
|
|
<!-- 题目数据表格 --> |
|
|
|
<table> |
|
|
|
<!-- 表头部分 --> |
|
|
|
<thead> |
|
|
|
<tr> |
|
|
|
<!-- ID列,支持排序 --> |
|
|
|
<th @click="sort('id')" class="sortable"> |
|
|
|
<div class="sort-header"> |
|
|
|
ID |
|
|
|
<!-- 排序图标,当按照ID排序时显示 --> |
|
|
|
<span v-if="sortField === 'id'" class="sort-icon"> |
|
|
|
<svg :style="{ transform: sortDirection === 'desc' ? 'rotate(180deg)' : 'rotate(0deg)' }" |
|
|
|
width="12" height="12" viewBox="0 0 24 24" fill="none"> |
|
|
|
<path d="M7 10L12 15L17 10" stroke="#e74c3c" stroke-width="2" /> |
|
|
|
<path d="M12 15V3" stroke="#e74c3c" stroke-width="2" /> |
|
|
|
</svg> |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
</th> |
|
|
|
|
|
|
|
<!-- 题干列 --> |
|
|
|
<th>ID</th> |
|
|
|
<th>题干</th> |
|
|
|
<!-- 题目类型列 --> |
|
|
|
<th>题目类型</th> |
|
|
|
|
|
|
|
<!-- 出错次数列,支持排序 --> |
|
|
|
<th @click="sort('errorCount')" class="sortable"> |
|
|
|
<div class="sort-header"> |
|
|
|
出错次数 |
|
|
|
<!-- 排序图标,当按照出错次数排序时显示 --> |
|
|
|
<span v-if="sortField === 'errorCount'" class="sort-icon"> |
|
|
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|
|
|
<path d="M7 10L12 15L17 10" stroke="#e74c3c" stroke-width="2" /> |
|
|
|
<path d="M12 15V3" stroke="#e74c3c" stroke-width="2" /> |
|
|
|
</svg> |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
</th> |
|
|
|
|
|
|
|
<!-- 出错率列,支持排序 --> |
|
|
|
<th @click="sort('errorRate')" class="sortable"> |
|
|
|
<div class="sort-header"> |
|
|
|
出错率 |
|
|
|
<!-- 排序图标,当按照出错率排序时显示 --> |
|
|
|
<span v-if="sortField === 'errorRate'" class="sort-icon"> |
|
|
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|
|
|
<path d="M7 10L12 15L17 10" stroke="#e74c3c" stroke-width="2" /> |
|
|
|
<path d="M12 15V3" stroke="#e74c3c" stroke-width="2" /> |
|
|
|
</svg> |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
</th> |
|
|
|
|
|
|
|
<!-- 推荐系列列 --> |
|
|
|
<th>出错次数</th> |
|
|
|
<th>出错率</th> |
|
|
|
<th>推荐系列</th> |
|
|
|
<!-- 操作列 --> |
|
|
|
<th>操作</th> |
|
|
|
</tr> |
|
|
|
</thead> |
|
|
|
<!-- 表格主体部分 --> |
|
|
|
<tbody> |
|
|
|
<!-- 数据行,遍历sortedItems数组渲染每一条题目数据 --> |
|
|
|
<tr v-for="item in sortedItems" :key="item.id" :data-id="item.id"> |
|
|
|
<td>{{ item.id }}</td> |
|
|
|
<tr v-for="(item,index) in items" :key="item.id" :data-id="item.id"> |
|
|
|
<td>{{ (page- 1) * pageSize + index + 1 }}</td> |
|
|
|
<td>{{ item.stem }}</td> |
|
|
|
<td>{{ item.questionTypeName }}</td> |
|
|
|
<td>{{ item.errorCount }}</td> |
|
|
|
<td>{{ item.errorRate }}%</td> |
|
|
|
<td>{{ item.CrName }}</td> |
|
|
|
<!-- 操作按钮 --> |
|
|
|
<td> |
|
|
|
<button class="btn-red small" @click="viewQuestion(item)">查看</button> |
|
|
|
<button class="btn-red small" @click="editQuestion(item)">修改</button> |
|
|
|
@ -82,16 +31,11 @@ |
|
|
|
</tbody> |
|
|
|
</table> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 分页控件 --> |
|
|
|
<div class="pagination-container"> |
|
|
|
<!-- 分页信息显示 --> |
|
|
|
<div class="pagination-info"> |
|
|
|
共 {{ total }} 条记录,第 {{ page }} 页 |
|
|
|
</div> |
|
|
|
<!-- 分页操作按钮 --> |
|
|
|
<div class="pagination-controls"> |
|
|
|
<!-- 首页按钮 --> |
|
|
|
<button |
|
|
|
class="btn-pagination" |
|
|
|
:disabled="page <= 1" |
|
|
|
@ -99,7 +43,6 @@ |
|
|
|
> |
|
|
|
首页 |
|
|
|
</button> |
|
|
|
<!-- 上一页按钮 --> |
|
|
|
<button |
|
|
|
class="btn-pagination" |
|
|
|
:disabled="page <= 1" |
|
|
|
@ -107,40 +50,17 @@ |
|
|
|
> |
|
|
|
上一页 |
|
|
|
</button> |
|
|
|
|
|
|
|
<!-- 页码输入框 --> |
|
|
|
<input |
|
|
|
type="number" |
|
|
|
class="page-input" |
|
|
|
:value="page" |
|
|
|
@keyup.enter="jumpToPage" |
|
|
|
min="1" |
|
|
|
:max="totalPages" |
|
|
|
/> |
|
|
|
|
|
|
|
<!-- 总页数显示 --> |
|
|
|
<input type="number" class="page-input" :value="page" @keyup.enter="jumpToPage" min="1" :max="totalPages"/> |
|
|
|
<span class="page-info">/ {{ totalPages }}</span> |
|
|
|
|
|
|
|
<!-- 下一页按钮 --> |
|
|
|
<button |
|
|
|
class="btn-pagination" |
|
|
|
:disabled="page >= totalPages" |
|
|
|
@click="changePage(page + 1)" |
|
|
|
> |
|
|
|
<button class="btn-pagination" :disabled="page >= totalPages" @click="changePage(page + 1)"> |
|
|
|
下一页 |
|
|
|
</button> |
|
|
|
<!-- 末页按钮 --> |
|
|
|
<button |
|
|
|
class="btn-pagination" |
|
|
|
:disabled="page >= totalPages" |
|
|
|
@click="changePage(totalPages)" |
|
|
|
> |
|
|
|
<button class="btn-pagination" :disabled="page >= totalPages" @click="changePage(totalPages)"> |
|
|
|
末页 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 查看题目详情弹窗 --> |
|
|
|
<div v-if="showViewModal" class="modal-overlay" @click.self="closeViewModal"> |
|
|
|
<div class="view-modal-content" @click.stop> |
|
|
|
<div class="modal-header"> |
|
|
|
@ -148,17 +68,9 @@ |
|
|
|
<button class="close-btn" @click="closeViewModal">×</button> |
|
|
|
</div> |
|
|
|
<div class="view-modal-body"> |
|
|
|
<!-- 题目文本 --> |
|
|
|
<p class="question-text">{{ currentQuestion.stem }}</p> |
|
|
|
|
|
|
|
<!-- 选项容器 --> |
|
|
|
<div class="options-container"> |
|
|
|
<!-- 遍历ABCD四个选项 --> |
|
|
|
<div |
|
|
|
v-for="option in ['A', 'B', 'C', 'D']" |
|
|
|
:key="option" |
|
|
|
:class="[option === currentQuestion.correctAnswer ? 'option-item-correct' : 'option-item']" |
|
|
|
> |
|
|
|
<div v-for="option in ['A', 'B', 'C', 'D']" :key="option" :class="[option === currentQuestion.correctAnswer ? 'option-item-correct' : 'option-item']"> |
|
|
|
{{ `${option}. ${currentQuestion[`option${option}`]}` }} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
@ -174,7 +86,7 @@ |
|
|
|
<div class="modal-content" @click.stop> |
|
|
|
<div class="modal-header"> |
|
|
|
<h3>编辑题目</h3> |
|
|
|
<!-- <button class="close-btn" @click="closeEditModal">×</button> --> |
|
|
|
<button class="close-btn" @click="closeEditModal">×</button> |
|
|
|
</div> |
|
|
|
<div class="modal-body"> |
|
|
|
<div class="form-row"> |
|
|
|
@ -194,11 +106,7 @@ |
|
|
|
<div class="form-row-options"> |
|
|
|
<div class="option-group"> |
|
|
|
<label>选项A</label> |
|
|
|
<input |
|
|
|
type="text" |
|
|
|
v-model="editingQuestion.optionA" |
|
|
|
placeholder="请输入选项A" style="width: 280px; height: 40px;" |
|
|
|
/> |
|
|
|
<input type="text" v-model="editingQuestion.optionA" placeholder="请输入选项A" style="width: 280px; height: 40px;"/> |
|
|
|
</div> |
|
|
|
<div class="option-group"> |
|
|
|
<label>选项B</label> |
|
|
|
@ -241,13 +149,11 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 删除确认对话框 --> |
|
|
|
<div v-if="showDeleteModal" class="modal-overlay" @click.self="closeDeleteModal"> |
|
|
|
<div class="delete-modal-content" @click.stop> |
|
|
|
<div class="modal-header"> |
|
|
|
<h3>您确定要删除吗</h3> |
|
|
|
<!-- <button class="close-btn" @click="closeDeleteModal">×</button> --> |
|
|
|
<button class="close-btn" @click="closeDeleteModal">×</button> |
|
|
|
</div> |
|
|
|
<div class="modal-footer"> |
|
|
|
<button class="btn-red" @click="confirmDelete">确定</button> |
|
|
|
@ -268,19 +174,16 @@ export default { |
|
|
|
name: 'QuestionTable', |
|
|
|
data() { |
|
|
|
return { |
|
|
|
sortField: '', // 当前排序字段 |
|
|
|
sortDirection: 'asc', // 排序方向(升序/降序) |
|
|
|
items: [], // 从后端获取的原始题目数据 |
|
|
|
sortedItems: [], // 排序后的题目数据 |
|
|
|
showViewModal: false, // 是否显示查看弹窗 |
|
|
|
currentQuestion: {}, // 当前查看的题目详情 |
|
|
|
page: 1, // 当前页码 |
|
|
|
pageSize: 20, // 每页显示条数 |
|
|
|
total: 0 , // 总记录数 |
|
|
|
showEditModal: false, // 控制编辑弹窗显示 |
|
|
|
editingQuestion: {}, // 正在编辑的题目 |
|
|
|
showDeleteModal: false, // 控制删除确认对话框显示 |
|
|
|
deleteId: null, // 要删除的题目ID |
|
|
|
total: 0, // 总记录数 |
|
|
|
showEditModal: false, // 控制编辑弹窗显示 |
|
|
|
editingQuestion: {}, // 正在编辑的题目 |
|
|
|
showDeleteModal: false, // 控制删除确认对话框显示 |
|
|
|
deleteId: null, // 要删除的题目ID |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
@ -296,30 +199,11 @@ export default { |
|
|
|
await this.fetchQuestions() |
|
|
|
}, |
|
|
|
|
|
|
|
watch: { |
|
|
|
// 监听排序字段变化,更新排序结果 |
|
|
|
sortField: { |
|
|
|
handler() { |
|
|
|
this.updateSortedItems() |
|
|
|
}, |
|
|
|
immediate: true |
|
|
|
}, |
|
|
|
// 监听排序方向变化,更新排序结果 |
|
|
|
sortDirection: { |
|
|
|
handler() { |
|
|
|
this.updateSortedItems() |
|
|
|
}, |
|
|
|
immediate: true |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
methods: { |
|
|
|
// 新增:外部设置数据的方法 |
|
|
|
// 修改setData方法,同时设置分页信息 |
|
|
|
setData(data) { |
|
|
|
this.items = data.list || data; |
|
|
|
this.total = data.total || (data.list ? data.list.length : data.length); |
|
|
|
this.updateSortedItems(); |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
@ -338,56 +222,13 @@ export default { |
|
|
|
if (response.data.code === 200) { |
|
|
|
this.items = response.data.data.list |
|
|
|
this.total = response.data.data.total || 0 |
|
|
|
this.updateSortedItems() |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('获取题目数据失败:', error) |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 更新排序后的数据 |
|
|
|
updateSortedItems() { |
|
|
|
// 如果没有指定排序字段,则按ID升序排列 |
|
|
|
if (!this.sortField) { |
|
|
|
this.sortedItems = [...this.items].sort((a, b) => a.id - b.id) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 根据当前排序字段和方向进行排序 |
|
|
|
const sorted = [...this.items].sort((a, b) => { |
|
|
|
const aValue = a[this.sortField] |
|
|
|
const bValue = b[this.sortField] |
|
|
|
|
|
|
|
// 数字类型排序 |
|
|
|
if (typeof aValue === 'number' && typeof bValue === 'number') { |
|
|
|
return this.sortDirection === 'asc' ? aValue - bValue : bValue - aValue |
|
|
|
} else { |
|
|
|
// 字符串类型排序 |
|
|
|
const strA = String(aValue).toLowerCase() |
|
|
|
const strB = String(bValue).toLowerCase() |
|
|
|
return this.sortDirection === 'asc' |
|
|
|
? strA.localeCompare(strB) |
|
|
|
: strB.localeCompare(strA) |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
this.sortedItems = sorted |
|
|
|
}, |
|
|
|
|
|
|
|
// 处理列排序点击事件 |
|
|
|
sort(field) { |
|
|
|
if (this.sortField === field) { |
|
|
|
// 切换排序方向 |
|
|
|
this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc' |
|
|
|
} else { |
|
|
|
// 设置新的排序字段,默认升序 |
|
|
|
this.sortField = field |
|
|
|
this.sortDirection = 'asc' |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 切换页面 |
|
|
|
// 修改changePage方法 |
|
|
|
changePage(newPage) { |
|
|
|
if (newPage >= 1 && newPage <= this.totalPages) { |
|
|
|
this.page = newPage; |
|
|
|
@ -397,7 +238,6 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
// 跳转到指定页码 |
|
|
|
// 修改jumpToPage方法 |
|
|
|
jumpToPage(event) { |
|
|
|
const targetPage = parseInt(event.target.value); |
|
|
|
if (targetPage >= 1 && targetPage <= this.totalPages) { |
|
|
|
@ -410,7 +250,7 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 查看题目详情 |
|
|
|
// 查看题目详情 |
|
|
|
async viewQuestion(item) { |
|
|
|
// 1. 滚动到对应行位置 |
|
|
|
const row = document.querySelector(`[data-id="${item.id}"]`); |
|
|
|
@ -467,7 +307,7 @@ export default { |
|
|
|
this.showViewModal = false; |
|
|
|
}, |
|
|
|
|
|
|
|
// 编辑题目 |
|
|
|
// 编辑题目 |
|
|
|
editQuestion(item) { |
|
|
|
// 将要编辑的题目数据填充到编辑表单中 |
|
|
|
this.editingQuestion = { |
|
|
|
@ -512,19 +352,12 @@ export default { |
|
|
|
this.editingQuestion.recommendedCourse === '价格破译' ? 2 : 3); |
|
|
|
|
|
|
|
// 发送请求 |
|
|
|
// const response = await axios.post('/admin/questions/update', params, { |
|
|
|
// headers: { |
|
|
|
// 'Content-Type': 'application/x-www-form-urlencoded' |
|
|
|
// } |
|
|
|
// }); |
|
|
|
console.log(params); |
|
|
|
const response = await axios.post('http://192.168.40.41:8000/admin/questions/update',params, |
|
|
|
{ |
|
|
|
headers: { |
|
|
|
'Content-Type': 'application/json' |
|
|
|
} |
|
|
|
}); |
|
|
|
console.log(response.data); |
|
|
|
|
|
|
|
if (response.data.code === 200) { |
|
|
|
this.closeEditModal(); |
|
|
|
@ -546,7 +379,7 @@ export default { |
|
|
|
this.editingQuestion = {}; |
|
|
|
}, |
|
|
|
|
|
|
|
// 删除题目(点击删除按钮) |
|
|
|
// 删除题目(点击删除按钮) |
|
|
|
async deleteQuestion(item) { |
|
|
|
this.deleteId = item.id; |
|
|
|
this.showDeleteModal = true; |
|
|
|
@ -639,24 +472,6 @@ th { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* sort-header 样式 */ |
|
|
|
.sort-header { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
gap: 6px; |
|
|
|
} |
|
|
|
|
|
|
|
/* 排序图标样式 */ |
|
|
|
.sort-icon svg { |
|
|
|
width: 12px; |
|
|
|
height: 12px; |
|
|
|
fill: none; |
|
|
|
stroke: #e74c3c; |
|
|
|
stroke-width: 2; |
|
|
|
transition: transform 0.2s; |
|
|
|
} |
|
|
|
|
|
|
|
/* 表格容器样式 */ |
|
|
|
.table-container { |
|
|
|
width: 100%; |
|
|
|
@ -684,30 +499,6 @@ th { |
|
|
|
background-color: #f2f2f2; |
|
|
|
font-weight: normal; |
|
|
|
color: #333; |
|
|
|
cursor: pointer; /* 添加鼠标指针 */ |
|
|
|
user-select: none; /* 防止文字选中 */ |
|
|
|
} |
|
|
|
|
|
|
|
/* 可排序列样式 */ |
|
|
|
.sortable { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
gap: 6px; |
|
|
|
} |
|
|
|
|
|
|
|
/* 排序图标样式 */ |
|
|
|
.sortable svg { |
|
|
|
width: 12px; |
|
|
|
height: 12px; |
|
|
|
fill: none; |
|
|
|
stroke: #e74c3c; |
|
|
|
stroke-width: 2; |
|
|
|
transition: transform 0.2s; |
|
|
|
} |
|
|
|
|
|
|
|
/* 悬停效果 */ |
|
|
|
th:hover { |
|
|
|
background-color: #e0e0e0; |
|
|
|
} |
|
|
|
|
|
|
|
/* 表格行悬停效果 */ |
|
|
|
@ -745,7 +536,6 @@ tr:hover { |
|
|
|
overflow-y: auto; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 删除确认对话框样式 */ |
|
|
|
.delete-modal-content { |
|
|
|
background-color: white; |
|
|
|
@ -793,18 +583,16 @@ tr:hover { |
|
|
|
gap: 16px; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.modal-content { |
|
|
|
background-color: white; |
|
|
|
border-radius: 8px; |
|
|
|
width: 600px; /* 从500px改为620px */ |
|
|
|
height: 760px; /* 增加高度 */ |
|
|
|
width: 600px; |
|
|
|
height: 760px; |
|
|
|
max-width: 90%; |
|
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); |
|
|
|
overflow: hidden; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.modal-header { |
|
|
|
padding: 20px; |
|
|
|
border-bottom: 1px solid #eee; |
|
|
|
@ -874,7 +662,6 @@ tr:hover { |
|
|
|
font-weight: bold; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.modal-footer { |
|
|
|
padding: 20px; |
|
|
|
border-top: 1px solid #eee; |
|
|
|
@ -901,7 +688,7 @@ tr:hover { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
gap: 10px; |
|
|
|
margin-right: 20px; /* 明确设置右侧间距 */ |
|
|
|
margin-right: 20px; |
|
|
|
} |
|
|
|
|
|
|
|
.btn-pagination { |
|
|
|
@ -937,4 +724,4 @@ tr:hover { |
|
|
|
font-size: 14px; |
|
|
|
color: #666; |
|
|
|
} |
|
|
|
</style> |
|
|
|
</style> |