Browse Source

11.16

还差导出
daijiajun/feature-20251107115823-股票知识测评
chenzhen 2 months ago
parent
commit
7f36c66891
  1. 243
      src/components/Question/QuestionTable.vue
  2. 29
      src/components/UserStatistics/UserStatisticsSearch.vue
  3. 69
      src/components/UserStatistics/UserStatisticsTable.vue
  4. 20
      src/components/WrongQuestion/WrongQuestionSearch.vue
  5. 134
      src/components/WrongQuestion/WrongQuestionTable.vue
  6. 6
      src/views/UserStatistics.vue

243
src/components/Question/QuestionTable.vue

@ -1,78 +1,27 @@
<!-- src/components/Question/QuestionTable.vue --> <!-- src/components/Question/QuestionTable.vue -->
<template> <template>
<div class="question-table-container"> <div class="question-table-container">
<!-- 表格容器 -->
<div class="table-container"> <div class="table-container">
<!-- 题目数据表格 -->
<table> <table>
<!-- 表头部分 -->
<thead> <thead>
<tr> <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>题目类型</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>
<!-- 操作列 -->
<th>操作</th> <th>操作</th>
</tr> </tr>
</thead> </thead>
<!-- 表格主体部分 -->
<tbody> <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.stem }}</td>
<td>{{ item.questionTypeName }}</td> <td>{{ item.questionTypeName }}</td>
<td>{{ item.errorCount }}</td> <td>{{ item.errorCount }}</td>
<td>{{ item.errorRate }}%</td> <td>{{ item.errorRate }}%</td>
<td>{{ item.CrName }}</td> <td>{{ item.CrName }}</td>
<!-- 操作按钮 -->
<td> <td>
<button class="btn-red small" @click="viewQuestion(item)">查看</button> <button class="btn-red small" @click="viewQuestion(item)">查看</button>
<button class="btn-red small" @click="editQuestion(item)">修改</button> <button class="btn-red small" @click="editQuestion(item)">修改</button>
@ -82,16 +31,11 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- 分页控件 -->
<div class="pagination-container"> <div class="pagination-container">
<!-- 分页信息显示 -->
<div class="pagination-info"> <div class="pagination-info">
{{ total }} 条记录 {{ page }} {{ total }} 条记录 {{ page }}
</div> </div>
<!-- 分页操作按钮 -->
<div class="pagination-controls"> <div class="pagination-controls">
<!-- 首页按钮 -->
<button <button
class="btn-pagination" class="btn-pagination"
:disabled="page <= 1" :disabled="page <= 1"
@ -99,7 +43,6 @@
> >
首页 首页
</button> </button>
<!-- 上一页按钮 -->
<button <button
class="btn-pagination" class="btn-pagination"
:disabled="page <= 1" :disabled="page <= 1"
@ -107,40 +50,17 @@
> >
上一页 上一页
</button> </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> <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>
<!-- 末页按钮 -->
<button
class="btn-pagination"
:disabled="page >= totalPages"
@click="changePage(totalPages)"
>
<button class="btn-pagination" :disabled="page >= totalPages" @click="changePage(totalPages)">
末页 末页
</button> </button>
</div> </div>
</div> </div>
<!-- 查看题目详情弹窗 -->
<div v-if="showViewModal" class="modal-overlay" @click.self="closeViewModal"> <div v-if="showViewModal" class="modal-overlay" @click.self="closeViewModal">
<div class="view-modal-content" @click.stop> <div class="view-modal-content" @click.stop>
<div class="modal-header"> <div class="modal-header">
@ -148,17 +68,9 @@
<button class="close-btn" @click="closeViewModal">×</button> <button class="close-btn" @click="closeViewModal">×</button>
</div> </div>
<div class="view-modal-body"> <div class="view-modal-body">
<!-- 题目文本 -->
<p class="question-text">{{ currentQuestion.stem }}</p> <p class="question-text">{{ currentQuestion.stem }}</p>
<!-- 选项容器 -->
<div class="options-container"> <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}`]}` }} {{ `${option}. ${currentQuestion[`option${option}`]}` }}
</div> </div>
</div> </div>
@ -174,7 +86,7 @@
<div class="modal-content" @click.stop> <div class="modal-content" @click.stop>
<div class="modal-header"> <div class="modal-header">
<h3>编辑题目</h3> <h3>编辑题目</h3>
<!-- <button class="close-btn" @click="closeEditModal">×</button> -->
<button class="close-btn" @click="closeEditModal">×</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-row"> <div class="form-row">
@ -194,11 +106,7 @@
<div class="form-row-options"> <div class="form-row-options">
<div class="option-group"> <div class="option-group">
<label>选项A</label> <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>
<div class="option-group"> <div class="option-group">
<label>选项B</label> <label>选项B</label>
@ -241,13 +149,11 @@
</div> </div>
</div> </div>
<!-- 删除确认对话框 -->
<div v-if="showDeleteModal" class="modal-overlay" @click.self="closeDeleteModal"> <div v-if="showDeleteModal" class="modal-overlay" @click.self="closeDeleteModal">
<div class="delete-modal-content" @click.stop> <div class="delete-modal-content" @click.stop>
<div class="modal-header"> <div class="modal-header">
<h3>您确定要删除吗</h3> <h3>您确定要删除吗</h3>
<!-- <button class="close-btn" @click="closeDeleteModal">×</button> -->
<button class="close-btn" @click="closeDeleteModal">×</button>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn-red" @click="confirmDelete">确定</button> <button class="btn-red" @click="confirmDelete">确定</button>
@ -268,10 +174,7 @@ export default {
name: 'QuestionTable', name: 'QuestionTable',
data() { data() {
return { return {
sortField: '', //
sortDirection: 'asc', // (/)
items: [], // items: [], //
sortedItems: [], //
showViewModal: false, // showViewModal: false, //
currentQuestion: {}, // currentQuestion: {}, //
page: 1, // page: 1, //
@ -296,30 +199,11 @@ export default {
await this.fetchQuestions() await this.fetchQuestions()
}, },
watch: {
//
sortField: {
handler() {
this.updateSortedItems()
},
immediate: true
},
//
sortDirection: {
handler() {
this.updateSortedItems()
},
immediate: true
}
},
methods: { methods: {
// //
// setData
setData(data) { setData(data) {
this.items = data.list || data; this.items = data.list || data;
this.total = data.total || (data.list ? data.list.length : data.length); this.total = data.total || (data.list ? data.list.length : data.length);
this.updateSortedItems();
}, },
@ -338,56 +222,13 @@ export default {
if (response.data.code === 200) { if (response.data.code === 200) {
this.items = response.data.data.list this.items = response.data.data.list
this.total = response.data.data.total || 0 this.total = response.data.data.total || 0
this.updateSortedItems()
} }
} catch (error) { } catch (error) {
console.error('获取题目数据失败:', 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) { changePage(newPage) {
if (newPage >= 1 && newPage <= this.totalPages) { if (newPage >= 1 && newPage <= this.totalPages) {
this.page = newPage; this.page = newPage;
@ -397,7 +238,6 @@ export default {
}, },
// //
// jumpToPage
jumpToPage(event) { jumpToPage(event) {
const targetPage = parseInt(event.target.value); const targetPage = parseInt(event.target.value);
if (targetPage >= 1 && targetPage <= this.totalPages) { if (targetPage >= 1 && targetPage <= this.totalPages) {
@ -512,19 +352,12 @@ export default {
this.editingQuestion.recommendedCourse === '价格破译' ? 2 : 3); 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, const response = await axios.post('http://192.168.40.41:8000/admin/questions/update',params,
{ {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
}); });
console.log(response.data);
if (response.data.code === 200) { if (response.data.code === 200) {
this.closeEditModal(); this.closeEditModal();
@ -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 { .table-container {
width: 100%; width: 100%;
@ -684,30 +499,6 @@ th {
background-color: #f2f2f2; background-color: #f2f2f2;
font-weight: normal; font-weight: normal;
color: #333; 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; overflow-y: auto;
} }
/* 删除确认对话框样式 */ /* 删除确认对话框样式 */
.delete-modal-content { .delete-modal-content {
background-color: white; background-color: white;
@ -793,18 +583,16 @@ tr:hover {
gap: 16px; gap: 16px;
} }
.modal-content { .modal-content {
background-color: white; background-color: white;
border-radius: 8px; border-radius: 8px;
width: 600px; /* 从500px改为620px */
height: 760px; /* 增加高度 */
width: 600px;
height: 760px;
max-width: 90%; max-width: 90%;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
overflow: hidden; overflow: hidden;
} }
.modal-header { .modal-header {
padding: 20px; padding: 20px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
@ -874,7 +662,6 @@ tr:hover {
font-weight: bold; font-weight: bold;
} }
.modal-footer { .modal-footer {
padding: 20px; padding: 20px;
border-top: 1px solid #eee; border-top: 1px solid #eee;
@ -901,7 +688,7 @@ tr:hover {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
margin-right: 20px; /* 明确设置右侧间距 */
margin-right: 20px;
} }
.btn-pagination { .btn-pagination {

29
src/components/UserStatistics/UserStatisticsSearch.vue

@ -7,6 +7,7 @@
<select v-model="filters.type"> <select v-model="filters.type">
<option value="">全部</option> <option value="">全部</option>
<option value="股票知识">股票知识</option> <option value="股票知识">股票知识</option>
<option value="企业文化">企业文化</option>
</select> </select>
</div> </div>
@ -39,7 +40,7 @@
<!-- 身份 --> <!-- 身份 -->
<div class="search-item"> <div class="search-item">
<label>身份</label> <label>身份</label>
<select v-model="filters.role">
<select v-model="filters.user_identity">
<option value="">全部</option> <option value="">全部</option>
<option value="非网">非网</option> <option value="非网">非网</option>
<option value="半年">半年</option> <option value="半年">半年</option>
@ -67,34 +68,14 @@ export default {
endDate: '', endDate: '',
userName: '', userName: '',
jingwangId: '', jingwangId: '',
role: '',
questionType: ''
user_identity: ''
} }
} }
}, },
methods: { methods: {
async searchUserStatistics() {
searchUserStatistics() {
// //
try{
const params = new URLSearchParams();
const questionType = this.filters.questionType || '';
if(questionType){
console.log(questionType);
params.append('questionType', questionType);
}
const role = {
'非网':1,
'半年':2,
'终身':3
}
if(this.filters.role){
console.log(role[this.filters.role]);
params.append('role', role[this.filters.role]);
}
}catch (error) {
console.error('搜索用户统计失败:', error);
alert('搜索失败!');
}
this.$emit('search', { ...this.filters });
}, },
async exportToExcel() { async exportToExcel() {
try{ try{

69
src/components/UserStatistics/UserStatisticsTable.vue

@ -15,12 +15,12 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="user in users" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.userName }}</td>
<tr v-for="(user, index) in users" :key="user.id">
<td>{{ (page - 1) * pageSize + index + 1 }}</td>
<td>{{ user.user_name }}</td>
<td>{{ user.user_identity }}</td> <td>{{ user.user_identity }}</td>
<td>{{ user.jwcode }}</td> <td>{{ user.jwcode }}</td>
<td>{{ user.questionTypeName }}</td>
<td>股票知识</td>
<td>{{ user.score }}</td> <td>{{ user.score }}</td>
<td>{{ user.createdAt }}</td> <td>{{ user.createdAt }}</td>
<td> <td>
@ -28,6 +28,7 @@
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<!-- 分页控件 --> <!-- 分页控件 -->
@ -197,8 +198,7 @@ export default {
} else { } else {
throw new Error(response.message || '获取错题失败'); throw new Error(response.message || '获取错题失败');
} }
}
,
},
// //
setRecommendationText() { setRecommendationText() {
@ -225,24 +225,45 @@ export default {
// //
async fetchUserStatistics(page = 1, filters = {}) { async fetchUserStatistics(page = 1, filters = {}) {
try { try {
const params = {
page: page,
page_size: this.pageSize,
...filters
//
const params = new URLSearchParams();
params.append('page', page);
params.append('page_size', this.pageSize);
//
if (filters.type) {
params.append('question_type', filters.type);
}
//
if (filters.startDate) {
params.append('start_time', filters.startDate);
}
if (filters.endDate) {
params.append('end_time', filters.endDate);
}
//
if (filters.userName) {
params.append('user_name', filters.userName);
}
//
if (filters.jingwangId) {
params.append('jwcode', filters.jingwangId);
}
//
if (filters.user_identity) {
params.append('user_identity', filters.user_identity);
console.log(filters.user_identity);
} }
const response = await getUserStatistics(params) const response = await getUserStatistics(params)
console.log(response);
if (response.code === 200) { if (response.code === 200) {
const formattedData = response.data.list.map(item => ({
id: item.id,
userName: item.user_name,
user_identity: item.user_identity,
jwcode: item.jwcode,
questionTypeName: item.question_type_name || '股票知识',
score: item.score,
createdAt: item.createdAt
}))
const formattedData = response.data.list
this.page = page; this.page = page;
this.total = response.data.total || response.data.list.length; this.total = response.data.total || response.data.list.length;
@ -254,9 +275,19 @@ export default {
}); });
} else { } else {
console.error('接口返回错误:', response.message) console.error('接口返回错误:', response.message)
//
this.$emit('data-loaded', {
list: [],
total: 0
});
} }
} catch (error) { } catch (error) {
console.error('获取用户数据失败:', error) console.error('获取用户数据失败:', error)
//
this.$emit('data-loaded', {
list: [],
total: 0
});
} }
}, },

20
src/components/WrongQuestion/WrongQuestionSearch.vue

@ -4,17 +4,17 @@
<!-- 题目类型筛选项 --> <!-- 题目类型筛选项 -->
<div class="search-item"> <div class="search-item">
<label>题目类型</label> <label>题目类型</label>
<select v-model="filters.type">
<select v-model="filters.questionType">
<option value="">全部</option> <option value="">全部</option>
<option value="1">股票知识</option>
<option value="2">企业文化</option>
<option>股票知识</option>
<option>企业文化</option>
</select> </select>
</div> </div>
<!-- 题干关键词搜索项 --> <!-- 题干关键词搜索项 -->
<div class="search-item"> <div class="search-item">
<label>题干查找</label> <label>题干查找</label>
<input type="text" v-model="filters.questionText" placeholder="请输入题干关键词" />
<input type="text" v-model="filters.keyword" placeholder="请输入题干关键词" />
</div> </div>
<!-- 推荐课程筛选项 --> <!-- 推荐课程筛选项 -->
@ -22,9 +22,9 @@
<label>推荐课程</label> <label>推荐课程</label>
<select v-model="filters.course"> <select v-model="filters.course">
<option value="">全部</option> <option value="">全部</option>
<option value="1">量能擒牛</option>
<option value="2">价格破译</option>
<option value="3">量价时空综合</option>
<option>量能擒牛</option>
<option>价格破译</option>
<option>量价时空综合</option>
</select> </select>
</div> </div>
@ -42,9 +42,9 @@ export default {
data() { data() {
return { return {
filters: { filters: {
type: '', // ID
questionText: '', //
course: '' // ID
questionType: '', //
keyword: '', //
course: '' //
}, },
currentPage: 1, // currentPage: 1, //
total: 0 // total: 0 //

134
src/components/WrongQuestion/WrongQuestionTable.vue

@ -4,50 +4,18 @@
<table> <table>
<thead> <thead>
<tr> <tr>
<th @click="sort('id')" class="sortable">
<div class="sort-header">
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>题目类型</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>
<th>操作</th> <th>操作</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="item in sortedWrongQuestions" :key="item.id">
<td>{{ item.id }}</td>
<tr v-for="(item,index) in wrongQuestions" :key="item.id">
<td>{{ (page- 1) * pageSize + index + 1 }}</td>
<td>{{ item.stem }}</td> <td>{{ item.stem }}</td>
<td>{{ item.questionTypeName }}</td> <td>{{ item.questionTypeName }}</td>
<td>{{ item.errorCount }}</td> <td>{{ item.errorCount }}</td>
@ -163,7 +131,6 @@
</template> </template>
<script> <script>
import { getQuestions } from '@/api/question.js'; import { getQuestions } from '@/api/question.js';
export default { export default {
@ -174,8 +141,6 @@ export default {
total: 0, total: 0,
page: 1, page: 1,
pageSize: 20, pageSize: 20,
sortField: '',
sortDirection: 'asc',
showUserModal: false, showUserModal: false,
errorUsers: [], errorUsers: [],
showViewModal: false, showViewModal: false,
@ -184,41 +149,15 @@ export default {
} }
}, },
computed: { computed: {
sortedWrongQuestions() {
if (!this.sortField) {
return [...this.wrongQuestions].sort((a, b) => a.id - b.id)
}
return [...this.wrongQuestions].sort((a, b) => {
const aValue = a[this.sortField]
const bValue = b[this.sortField]
if (this.sortField === 'errorRate') {
const numA = parseFloat(String(aValue).replace('%', '')) || 0
const numB = parseFloat(String(bValue).replace('%', '')) || 0
return this.sortDirection === 'asc' ? numA - numB : numB - numA
}
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)
}
})
},
totalPages() { totalPages() {
return Math.ceil(this.total / this.pageSize) return Math.ceil(this.total / this.pageSize)
} }
}, },
methods: { methods: {
setData(data) {
this.wrongQuestions = data.list || [];
this.total = data.total || 0;
this.page = data.page || 1;
setFilters(filters) {
this.currentFilters = filters;
this.page = 1;
this.fetchWrongQuestions();
}, },
async fetchWrongQuestions() { async fetchWrongQuestions() {
@ -249,23 +188,10 @@ export default {
params.append('stem', this.currentFilters.keyword); params.append('stem', this.currentFilters.keyword);
} }
//
if (this.sortField) {
params.append('sort_field', this.sortField);
params.append('sort_direction', this.sortDirection);
}
const response = await getQuestions(params); const response = await getQuestions(params);
if (response.data.code === 200) { if (response.data.code === 200) {
this.wrongQuestions = response.data.data.list.map(item => ({
id: item.id,
stem: item.stem,
questionTypeName: item.questionTypeName,
errorCount: item.errorCount,
errorRate: item.errorRate, // %
CrName: item.CrName
}))
this.wrongQuestions = response.data.data.list
this.total = response.data.data.total || 0; this.total = response.data.data.total || 0;
} else { } else {
console.error('接口返回错误:', response.data.msg) console.error('接口返回错误:', response.data.msg)
@ -346,16 +272,6 @@ export default {
this.showViewModal = false this.showViewModal = false
}, },
sort(field) {
if (this.sortField === field) {
this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc'
} else {
this.sortField = field
this.sortDirection = 'asc'
}
this.fetchWrongQuestions();
},
changePage(newPage) { changePage(newPage) {
if (newPage >= 1 && newPage <= this.totalPages) { if (newPage >= 1 && newPage <= this.totalPages) {
this.page = newPage; this.page = newPage;
@ -397,8 +313,7 @@ table {
background-color: white; background-color: white;
} }
th,
td {
th,td {
padding: 12px; padding: 12px;
text-align: left; text-align: left;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
@ -408,8 +323,6 @@ th {
background-color: #f2f2f2; background-color: #f2f2f2;
font-weight: normal; font-weight: normal;
color: #333; color: #333;
cursor: pointer;
user-select: none;
display: table-cell !important; display: table-cell !important;
vertical-align: middle !important; vertical-align: middle !important;
} }
@ -423,31 +336,6 @@ tr:hover {
gap: 16px; gap: 16px;
} }
.sort-header {
display: flex;
align-items: center;
gap: 6px;
}
.sortable {
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;
}
th:hover {
background-color: #e0e0e0;
}
/* 分页样式 */ /* 分页样式 */
.pagination-container { .pagination-container {
display: flex; display: flex;

6
src/views/UserStatistics.vue

@ -32,7 +32,7 @@ export default {
endDate: '', endDate: '',
userName: '', userName: '',
jingwangId: '', jingwangId: '',
role: ''
user_identity: ''
}, },
page: 1, page: 1,
pageSize: 10, pageSize: 10,
@ -42,8 +42,8 @@ export default {
methods: { methods: {
// //
handleSearch(filters) { handleSearch(filters) {
this.filters = filters
this.page = 1
this.filters = { ...filters }; //
this.page = 1;
this.$refs.userStatisticsTable.fetchUserStatistics(1, filters); this.$refs.userStatisticsTable.fetchUserStatistics(1, filters);
}, },
// //

Loading…
Cancel
Save