Browse Source

提测结束,进行复测

daijiajun/feature-20251107115823-股票知识测评
chenzhen 2 months ago
parent
commit
b4f1269a1c
  1. 13
      src/App.vue
  2. 60
      src/components/Question/QuestionSearch.vue
  3. 24
      src/components/Question/QuestionTable.vue
  4. 4
      src/components/Tabs/TabNavigation.vue
  5. 59
      src/components/UserStatistics/UserStatisticsSearch.vue
  6. 49
      src/components/UserStatistics/UserStatisticsTable.vue
  7. 58
      src/components/WrongQuestion/WrongQuestionSearch.vue
  8. 15
      src/components/WrongQuestion/WrongQuestionTable.vue
  9. 12
      src/router/index.js

13
src/App.vue

@ -16,11 +16,6 @@
<!-- 路由视图出口根据路由显示对应组件 -->
<router-view />
<!-- 底部应用按钮 -->
<div class="footer-btn">
<button class="btn-red">应用</button>
</div>
</div>
</div>
</div>
@ -139,14 +134,6 @@ body {
opacity: 0.5;
}
/* 修改后的底部按钮定位样式 */
.footer-btn {
position: fixed; /* 改为 fixed 定位 */
bottom: 20px; /* 距离底部 20px */
right: 20px; /* 距离右侧 20px */
z-index: 1000; /* 确保在其他元素之上 */
}
/* 搜索区域样式 */
.search-area {

60
src/components/Question/QuestionSearch.vue

@ -1,11 +1,14 @@
<!-- src/components/Question/QuestionSearch.vue -->
<template>
<div class="question-search-container">
<div class="top">
<h2>题库管理</h2>
</div>
<!-- 搜索区域容器 -->
<div class="search-area">
<!-- 题目类型筛选项 -->
<div class="search-item">
<label>题目类型</label>
<h3>题目类型</h3>
<select v-model="searchForm.questionType">
<option value="">全部</option>
<option>股票知识</option>
@ -15,13 +18,13 @@
<!-- 题干关键词搜索项 -->
<div class="search-item">
<label>题干查找</label>
<h3>题干查找</h3>
<input type="text" placeholder="请输入题干关键词" v-model="searchForm.keyword" />
</div>
<!-- 课程推荐筛选项 -->
<div class="search-item">
<label>推荐系列</label>
<h3>推荐系列</h3>
<select v-model="searchForm.course">
<option value="">全部</option>
<option>量能擒牛</option>
@ -39,7 +42,7 @@
</div>
<!-- 新增题目弹窗 -->
<div v-if="showAddModal" class="modal-overlay" @click.self="closeModal">
<div v-if="showAddModal" class="modal-overlay">
<div class="modal-content" @click.stop>
<div class="modal-header">
<h3>新增题目</h3>
@ -98,7 +101,6 @@
<div class="form-row">
<label>推荐系列</label>
<select v-model="newQuestion.recommendedCourse">
<option value="">请选择</option>
<option value="量能擒牛">量能擒牛</option>
<option value="价格破译">价格破译</option>
<option value="量价时空综合">量价时空综合</option>
@ -137,7 +139,7 @@ export default {
optionD: '',
correctAnswer: 'A',
questionTypeName: '股票知识',
recommendedCourse: ''
recommendedCourse: '量能擒牛'
},
currentPage: 1, //
total: 0 //
@ -192,7 +194,7 @@ export default {
const list = response.data.data.list || [];
const totalRaw = response.data.data.total;
const total = Number.isFinite(Number(totalRaw)) ? Number(totalRaw) : 0;
const total = Number.isFinite(Number(totalRaw)) ? Number(totalRaw) : 1;
const resultData = { list, total };
@ -210,7 +212,7 @@ export default {
async addQuestion() {
//
if (!this.newQuestion.stem || !this.newQuestion.optionA || !this.newQuestion.optionB ||
!this.newQuestion.optionC || !this.newQuestion.optionD || !this.newQuestion.correctAnswer) {
!this.newQuestion.optionC || !this.newQuestion.optionD || !this.newQuestion.correctAnswer || !this.newQuestion.recommendedCourse) {
alert('请填写所有必填项!');
return;
}
@ -228,8 +230,7 @@ export default {
params.append('question_type_id', this.newQuestion.questionTypeName === '股票知识' ? 1 : 2);
params.append('course_recommendation_id',
this.newQuestion.recommendedCourse === '量能擒牛' ? 1 :
this.newQuestion.recommendedCourse === '价格破译' ? 2 :
this.newQuestion.recommendedCourse === '量价时空综合' ? 3 : '');
this.newQuestion.recommendedCourse === '价格破译' ? 2 : 3 );
//
// const response = await axios.post('/admin/questions/update', params, {
@ -267,7 +268,39 @@ export default {
async exportExcel() {
try{
const response = await axios.post('http://192.168.40.41:8000/admin/questions/export',{},{responseType: 'blob'});
//
const exportParams = {};
//
const questionTypeIdMap = {
'股票知识': 1,
'企业文化': 2
};
if (this.searchForm.questionType) {
exportParams.question_type_id = questionTypeIdMap[this.searchForm.questionType];
}
//
const courseRecommendationIdMap = {
'量能擒牛': 1,
'价格破译': 2,
'量价时空综合': 3
};
if (this.searchForm.course) {
exportParams.course_recommendation_id = courseRecommendationIdMap[this.searchForm.course];
}
//
if (this.searchForm.keyword) {
exportParams.stem = this.searchForm.keyword;
}
//
const response = await axios.post(
'http://192.168.40.41:8000/admin/questions/export',
exportParams,
{ responseType: 'blob' }
);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
@ -297,7 +330,7 @@ export default {
optionD: '',
correctAnswer: 'A',
questionTypeName: '股票知识',
recommendedCourse: ''
recommendedCourse: '量能擒牛'
};
}
}
@ -305,6 +338,9 @@ export default {
</script>
<style scoped>
.top{
padding: 20px 0px;
}
/* 弹窗样式 */
.modal-overlay {
position: fixed;

24
src/components/Question/QuestionTable.vue

@ -56,7 +56,7 @@
下一页
</button>
<button class="btn-pagination" :disabled="page >= totalPages" @click="changePage(totalPages)">
</button>
</div>
</div>
@ -80,6 +80,9 @@
</div>
</div>
</div>
<div class="footer-btn">
<button class="btn-red">应用</button>
</div>
<div v-if="showEditModal" class="modal-overlay" @click.self="closeEditModal">
@ -135,7 +138,6 @@
<div class="form-row">
<label>推荐课程</label>
<select v-model="editingQuestion.recommendedCourse">
<option value="">请选择</option>
<option value="量能擒牛">量能擒牛</option>
<option value="价格破译">价格破译</option>
<option value="量价时空综合">量价时空综合</option>
@ -188,12 +190,15 @@ export default {
},
computed: {
//
totalPages() {
if (this.total === 0) {
return 1;
}
return Math.ceil(this.total / this.pageSize);
}
},
async mounted() {
//
await this.fetchQuestions()
@ -201,9 +206,12 @@ export default {
methods: {
//
setData(data) {
setData(data, resetPage = false) {
this.items = data.list || data;
this.total = data.total || (data.list ? data.list.length : data.length);
if (resetPage || this.page > this.totalPages) {
this.page = 1;
}
},
@ -221,7 +229,7 @@ export default {
//
if (response.data.code === 200) {
this.items = response.data.data.list
this.total = response.data.data.total || 0
this.total = response.data.data.total !== 0 ? response.data.data.total : 1
}
} catch (error) {
console.error('获取题目数据失败:', error)
@ -724,4 +732,10 @@ tr:hover {
font-size: 14px;
color: #666;
}
.footer-btn {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
}
</style>

4
src/components/Tabs/TabNavigation.vue

@ -22,8 +22,8 @@
<!-- 题库管理标签按钮 -->
<button
class="tab-btn"
:class="{ active: $route.path === '/questions' }"
@click="$router.push('/questions')"
:class="{ active: $route.path === '/' }"
@click="$router.push('/')"
>
题库管理
</button>

59
src/components/UserStatistics/UserStatisticsSearch.vue

@ -1,33 +1,36 @@
<!-- UserStatisticsSearch.vue -->
<template>
<div>
<div class="top">
<h2>用户数据</h2>
</div>
<div class="search-area">
<!-- 题目类型 -->
<div class="search-item">
<label>题目类型</label>
<h3>题目类型</h3>
<select v-model="filters.type">
<option value="">全部</option>
<option value="股票知识">股票知识</option>
<option value="企业文化">企业文化</option>
</select>
</div>
<!-- 时间选择 -->
<div class="search-item date-range">
<label>时间选择</label>
<h3>时间选择</h3>
<div class="date-input-wrapper">
<div class="date-box">
<input type="date" v-model="filters.startDate" />
<input type="datetime-local" v-model="filters.startDate" step="1" />
</div>
<span class="separator"></span>
<div class="date-box">
<input type="date" v-model="filters.endDate" />
<input type="datetime-local" v-model="filters.endDate" step="1" />
</div>
</div>
</div>
<!-- 用户名称 -->
<div class="search-item">
<label>用户名称</label>
<h3>用户名称</h3>
<input type="text" v-model="filters.userName" placeholder="请输入用户名称" />
</div>
@ -54,6 +57,7 @@
<button class="btn-red" @click="exportToExcel">Excel导出</button>
</div>
</div>
</div>
</template>
<script>
@ -79,7 +83,45 @@ export default {
},
async exportToExcel() {
try{
const response = await axios.post('http://192.168.40.41:8000/admin/user/export',{},{responseType: 'blob'});
const params = new URLSearchParams();
const questionTypeIdMap = {
'股票知识':1,
'企业文化':2
};
//
if (this.filters.type) {
params.append('question_type_id', questionTypeIdMap[this.filters.type]);
}
//
if (this.filters.startDate) {
params.append('start_time', this.filters.startDate);
}
if (this.filters.endDate) {
params.append('end_time', this.filters.endDate);
}
if(this.filters.startDate>this.filters.endDate){
alert('开始时间不能晚于结束时间');
return;
}
//
if (this.filters.userName) {
params.append('user_name', this.filters.userName);
}
//
if (this.filters.jingwangId) {
params.append('jingwang_id', this.filters.jingwangId);
}
//
if (this.filters.user_identity) {
params.append('user_identity', this.filters.user_identity);
}
console.log(params);
const response = await axios.post('http://192.168.40.41:8000/admin/user/export',params,{responseType: 'blob'});
console.log(response);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
@ -103,7 +145,6 @@ export default {
display: flex;
flex-wrap: wrap;
gap: 20px;
padding: 20px;
background-color: white;
border-radius: 4px;
}
@ -111,7 +152,7 @@ export default {
.search-item {
display: flex;
flex-direction: column;
min-width: 200px;
min-width: 100px;
}
.search-item label {

49
src/components/UserStatistics/UserStatisticsTable.vue

@ -28,11 +28,10 @@
</td>
</tr>
</tbody>
</table>
<!-- 分页控件 -->
<div class="pagination-container" v-if="total > pageSize">
<div class="pagination-container">
<div class="pagination-info">
{{ total }} 条记录 {{ page }}
</div>
@ -75,7 +74,7 @@
:disabled="page >= totalPages"
@click="changePage(totalPages)"
>
</button>
</div>
</div>
@ -144,16 +143,19 @@ export default {
showWrongQuestionsModal: false,
wrongQuestions: [],
currentUserId: null,
recommendationText: '基金操作入门'
recommendationText: '基金操作入门',
currentFilters: {}
}
},
computed: {
totalPages() {
return Math.ceil(this.total / this.pageSize);
console.log(this.total);
const totalPages = this.total !== 0 ? Math.ceil(this.total / this.pageSize) : 1;
console.log(totalPages);
return totalPages;
}
},
methods: {
//
async viewUser(user) {
// ID
this.currentUserId = user.id;
@ -168,8 +170,6 @@ export default {
alert('获取用户错题数据失败,请检查网络连接!');
}
},
//
async loadWrongQuestions(jwcode) {
const params = {
jwcode: jwcode,
@ -225,15 +225,15 @@ export default {
//
async fetchUserStatistics(page = 1, filters = {}) {
try {
//
this.page=page;
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.type=='') {
// params.append('user_identity', '');
// }
//
if (filters.startDate) {
@ -242,6 +242,10 @@ export default {
if (filters.endDate) {
params.append('end_time', filters.endDate);
}
if(filters.startDate>filters.endDate){
alert('开始时间不能晚于结束时间');
return;
}
//
if (filters.userName) {
@ -256,17 +260,18 @@ export default {
//
if (filters.user_identity) {
params.append('user_identity', filters.user_identity);
console.log(filters.user_identity);
}
const response = await getUserStatistics(params)
console.log(response);
if (response.code === 200) {
const formattedData = response.data.list
const formattedData = response.data.list||[];
this.page = page;
this.total = response.data.total || response.data.list.length;
const totalRaw = response.data.total;
this.total = Number.isFinite(Number(totalRaw)) ? Number(totalRaw) : (formattedData.length > 0 ? formattedData.length : 0);
//
this.$emit('data-loaded', {
@ -274,7 +279,10 @@ export default {
total: this.total
});
} else {
console.error('接口返回错误:', response.message)
// console.error(':', response.message)
alert('输入内容错误')
this.total = 0;
this.page = 1;
//
this.$emit('data-loaded', {
list: [],
@ -283,7 +291,8 @@ export default {
}
} catch (error) {
console.error('获取用户数据失败:', error)
//
this.total = 0;
this.page = 1;
this.$emit('data-loaded', {
list: [],
total: 0
@ -306,7 +315,11 @@ export default {
event.target.value = this.page;
}
}
}
},
onMounted() {
this.fetchQuestions();
this.totalPages();
},
}
</script>

58
src/components/WrongQuestion/WrongQuestionSearch.vue

@ -1,9 +1,13 @@
<!-- src/components/WrongQuestion/WrongQuestionSearch.vue -->
<template>
<div>
<div class="top">
<h2>错题统计</h2>
</div>
<div class="search-area">
<!-- 题目类型筛选项 -->
<div class="search-item">
<label>题目类型</label>
<h3>题目类型</h3>
<select v-model="filters.questionType">
<option value="">全部</option>
<option>股票知识</option>
@ -13,13 +17,13 @@
<!-- 题干关键词搜索项 -->
<div class="search-item">
<label>题干查找</label>
<h3>题干查找</h3>
<input type="text" v-model="filters.keyword" placeholder="请输入题干关键词" />
</div>
<!-- 推荐课程筛选项 -->
<div class="search-item">
<label>推荐课程</label>
<h3>推荐课程</h3>
<select v-model="filters.course">
<option value="">全部</option>
<option>量能擒牛</option>
@ -31,11 +35,14 @@
<!-- 操作按钮组 -->
<div class="btn-group">
<button class="btn-red" @click="searchWrongQuestions">查找</button>
<button class="btn-red" @click="exportExcel">Excel导出</button>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'WrongQuestionSearch',
data() {
@ -67,7 +74,52 @@ export default {
//
resetPage() {
this.currentPage = 1;
},
async exportExcel() {
try{
const Params = {};
const questionTypeIdMap = {
'股票知识': 1,
'企业文化': 2
};
if (this.filters.questionType) {
Params.question_type_id = questionTypeIdMap[this.filters.questionType];
}
const courseRecommendationIdMap = {
'量能擒牛': 1,
'价格破译': 2,
'量价时空综合': 3
};
if (this.filters.course) {
Params.course_recommendation_id = courseRecommendationIdMap[this.filters.course];
}
if (this.filters.keyword) {
Params.stem = this.filters.keyword;
}
const response = await axios.post(
'http://192.168.40.41:8000/admin/questions/export',
Params,
{ responseType: 'blob' }
);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', '错题统计表.xlsx');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
alert('导出成功!');
}catch (error) {
console.error('导出 Excel 失败:', error);
alert('网络错误,请检查连接!');
}
}
}
}
</script>
<style>
.top{
margin-bottom: 10px;
}
</style>

15
src/components/WrongQuestion/WrongQuestionTable.vue

@ -19,7 +19,7 @@
<td>{{ item.stem }}</td>
<td>{{ item.questionTypeName }}</td>
<td>{{ item.errorCount }}</td>
<td>{{ item.errorRate }}</td>
<td>{{ item.errorRate }}%</td>
<td>{{ item.CrName }}</td>
<td class="operation-cell">
<button class="btn-red small" @click="viewUser(item)">出错用户</button>
@ -73,7 +73,7 @@
:disabled="page >= totalPages"
@click="changePage(totalPages)"
>
</button>
</div>
</div>
@ -150,7 +150,12 @@ export default {
},
computed: {
totalPages() {
return Math.ceil(this.total / this.pageSize)
try {
return this.total !== 0 ? Math.ceil(this.total / this.pageSize) : 1;
} catch(error) {
console.error('计算总页数时出错:', error);
return 1;
}
}
},
methods: {
@ -192,7 +197,7 @@ export default {
if (response.data.code === 200) {
this.wrongQuestions = response.data.data.list
this.total = response.data.data.total || 0;
this.total = response.data.data.total;
} else {
console.error('接口返回错误:', response.data.msg)
}
@ -295,7 +300,7 @@ export default {
}
},
mounted() {
this.fetchWrongQuestions()
this.fetchWrongQuestions();
}
}
</script>

12
src/router/index.js

@ -4,7 +4,7 @@ import Vue from 'vue'
// 导入Vue Router插件
import VueRouter from 'vue-router'
// 导入主页面组件
import MainPage from '@/views/MainPage.vue'
// import MainPage from '@/views/MainPage.vue'
// 导入题库管理页面组件
import QuestionManage from '@/views/QuestionManage.vue'
// 导入用户统计数据页面组件
@ -17,12 +17,12 @@ Vue.use(VueRouter)
// 定义路由配置数组
const routes = [
// {
// path: '/', // 根路径
// component: MainPage // 使用主页面作为默认页面
// },
{
path: '/', // 根路径
component: MainPage // 使用主页面作为默认页面
},
{
path: '/questions', // 题库管理路径
path: '/', // 题库管理路径
name: 'QuestionManage', // 路由名称
component: QuestionManage // 对应的组件
},

Loading…
Cancel
Save