You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

465 lines
13 KiB

2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
  1. <!-- src/components/Question/QuestionSearch.vue -->
  2. <template>
  3. <div class="question-search-container">
  4. <div class="top">
  5. <h2>题库管理</h2>
  6. </div>
  7. <!-- 搜索区域容器 -->
  8. <div class="search-area">
  9. <!-- 题目类型筛选项 -->
  10. <div class="search-item">
  11. <h3>题目类型</h3>
  12. <select v-model="searchForm.questionType">
  13. <option value="">全部</option>
  14. <option>股票知识</option>
  15. <option>企业文化</option>
  16. </select>
  17. </div>
  18. <!-- 题干关键词搜索项 -->
  19. <div class="search-item">
  20. <h3>题干查找</h3>
  21. <input type="text" placeholder="请输入题干关键词" v-model="searchForm.keyword" />
  22. </div>
  23. <!-- 课程推荐筛选项 -->
  24. <div class="search-item">
  25. <h3>推荐系列</h3>
  26. <select v-model="searchForm.course">
  27. <option value="">全部</option>
  28. <option>量能擒牛</option>
  29. <option>价格破译</option>
  30. <option>量价时空综合</option>
  31. </select>
  32. </div>
  33. <!-- 操作按钮组 -->
  34. <div class="btn-group">
  35. <button class="btn-red" @click="handleSearch">查找</button>
  36. <button class="btn-red" @click="showAddModal = true">新增题目</button>
  37. <button class="btn-red" @click="exportExcel">Excel导出</button>
  38. </div>
  39. </div>
  40. <!-- 新增题目弹窗 -->
  41. <div v-if="showAddModal" class="modal-overlay">
  42. <div class="modal-content" @click.stop>
  43. <div class="modal-header">
  44. <h3>新增题目</h3>
  45. <button class="close-btn" @click="closeModal">×</button>
  46. </div>
  47. <div class="modal-body">
  48. <div class="form-row">
  49. <label>题目类型</label>
  50. <select v-model="newQuestion.questionTypeName">
  51. <option value="股票知识">股票知识</option>
  52. <option value="企业文化">企业文化</option>
  53. </select>
  54. </div>
  55. <div class="form-row">
  56. <label>题干</label>
  57. <textarea v-model="newQuestion.stem" placeholder="请输入题目内容" rows="4"
  58. style="width: 545px; height: 120px;"
  59. ></textarea>
  60. </div>
  61. <div class="form-row-options">
  62. <div class="option-group">
  63. <label>选项A</label>
  64. <input
  65. type="text"
  66. v-model="newQuestion.optionA"
  67. placeholder="请输入选项A"
  68. style="width: 280px; height: 40px;"
  69. />
  70. </div>
  71. <div class="option-group">
  72. <label>选项B</label>
  73. <input type="text" v-model="newQuestion.optionB" placeholder="请输入选项B" style="width: 280px; height: 40px;"/>
  74. </div>
  75. <div class="option-group">
  76. <label>选项C</label>
  77. <input type="text" v-model="newQuestion.optionC" placeholder="请输入选项C" style="width: 280px; height: 40px;"/>
  78. </div>
  79. <div class="option-group">
  80. <label>选项D</label>
  81. <input type="text" v-model="newQuestion.optionD" placeholder="请输入选项D" style="width: 280px; height: 40px;"/>
  82. </div>
  83. </div>
  84. <div class="form-row">
  85. <label>正确答案</label>
  86. <select v-model="newQuestion.correctAnswer">
  87. <option value="A">A</option>
  88. <option value="B">B</option>
  89. <option value="C">C</option>
  90. <option value="D">D</option>
  91. </select>
  92. </div>
  93. <div class="form-row">
  94. <label>推荐系列</label>
  95. <select v-model="newQuestion.recommendedCourse">
  96. <option value="量能擒牛">量能擒牛</option>
  97. <option value="价格破译">价格破译</option>
  98. <option value="量价时空综合">量价时空综合</option>
  99. </select>
  100. </div>
  101. </div>
  102. <div class="modal-footer">
  103. <button class="btn-red" @click="addQuestion">确定</button>
  104. <button class="btn-red" @click="closeModal">取消</button>
  105. </div>
  106. </div>
  107. </div>
  108. </div>
  109. </template>
  110. <script>
  111. import { getQuestions } from '@/api/question.js';
  112. import axios from 'axios';
  113. // import { Message } from 'element-ui'
  114. export default {
  115. name: 'QuestionSearch',
  116. data() {
  117. return {
  118. searchForm: {
  119. questionType: '',
  120. keyword: '',
  121. course: ''
  122. },
  123. showAddModal: false,
  124. newQuestion: {
  125. id: 0,
  126. stem: '',
  127. optionA: '',
  128. optionB: '',
  129. optionC: '',
  130. optionD: '',
  131. correctAnswer: 'A',
  132. questionTypeName: '股票知识',
  133. recommendedCourse: '量能擒牛'
  134. },
  135. currentPage: 1, // 当前页码
  136. total: 0 // 总记录数
  137. };
  138. },
  139. methods: {
  140. async handleSearch(page = 1) {
  141. try {
  142. this.currentPage = page; // 更新当前页码
  143. const params = new URLSearchParams();
  144. params.append('page', page); // 使用传入的页码
  145. params.append('page_size', 20);
  146. // 题目类型映射为 id
  147. const questionTypeIdMap = {
  148. '股票知识': 1,
  149. '企业文化': 2
  150. };
  151. if (this.searchForm.questionType) {
  152. params.append('question_type_id', questionTypeIdMap[this.searchForm.questionType]);
  153. }
  154. // 推荐系列映射为 id
  155. const courseRecommendationIdMap = {
  156. '量能擒牛': 1,
  157. '价格破译': 2,
  158. '量价时空综合': 3
  159. };
  160. if (this.searchForm.course) {
  161. params.append('course_recommendation_id', courseRecommendationIdMap[this.searchForm.course]);
  162. }
  163. // 题干关键词模糊查询
  164. if (this.searchForm.keyword) {
  165. params.append('stem', this.searchForm.keyword);
  166. }
  167. const response = await getQuestions(params);
  168. // if (response.data.code === 200) {
  169. // // 包装数据以便传递分页信息
  170. // const resultData = {
  171. // list: response.data.data.list,
  172. // total: response.data.data.total || []
  173. // };
  174. // this.$emit('search-result', resultData);
  175. // this.total = response.data.data.total || 0;
  176. // } else {
  177. // alert('搜索失败:' + response.data.msg);
  178. // }
  179. if (response.data.code === 200) {
  180. const list = response.data.data.list || [];
  181. const totalRaw = response.data.data.total;
  182. const total = Number.isFinite(Number(totalRaw)) ? Number(totalRaw) : 1;
  183. const resultData = { list, total };
  184. this.$emit('search-result', resultData);
  185. this.total = total;
  186. } else {
  187. alert('搜索失败:' + response.data.msg);
  188. }
  189. } catch (error) {
  190. console.error('搜索失败:', error);
  191. alert('网络错误,请检查连接!');
  192. }
  193. },
  194. async addQuestion() {
  195. // 表单验证
  196. if (!this.newQuestion.stem || !this.newQuestion.optionA || !this.newQuestion.optionB ||
  197. !this.newQuestion.optionC || !this.newQuestion.optionD || !this.newQuestion.correctAnswer || !this.newQuestion.recommendedCourse) {
  198. alert('请填写所有必填项!');
  199. return;
  200. }
  201. try {
  202. // 构造请求参数
  203. const params = new URLSearchParams();
  204. params.append('id', this.newQuestion.id);
  205. params.append('stem', this.newQuestion.stem);
  206. params.append('A', this.newQuestion.optionA);
  207. params.append('B', this.newQuestion.optionB);
  208. params.append('C', this.newQuestion.optionC);
  209. params.append('D', this.newQuestion.optionD);
  210. params.append('correct_answer', this.newQuestion.correctAnswer);
  211. params.append('question_type_id', this.newQuestion.questionTypeName === '股票知识' ? 1 : 2);
  212. params.append('course_recommendation_id',
  213. this.newQuestion.recommendedCourse === '量能擒牛' ? 1 :
  214. this.newQuestion.recommendedCourse === '价格破译' ? 2 : 3 );
  215. // 发送请求
  216. // const response = await axios.post('/admin/questions/update', params, {
  217. // headers: {
  218. // 'Content-Type': 'application/x-www-form-urlencoded'
  219. // }
  220. // });
  221. //发送请求
  222. console.log(params);
  223. const response = await axios.post('http://192.168.40.41:8000/admin/questions/update',params,
  224. {
  225. headers: {
  226. 'Content-Type': 'application/json'
  227. }
  228. });
  229. console.log(response.data);
  230. if (response.data.code === 200) {
  231. this.closeModal();
  232. console.log('第二步');
  233. this.$emit('question-added');
  234. console.log('第三步');
  235. this.$message({
  236. message: '添加题目成功!',
  237. type: 'success'
  238. });
  239. console.log('第四步');
  240. } else {
  241. alert('添加题目失败:' + response.data.msg);
  242. }
  243. } catch (error) {
  244. console.error('添加题目失败:', error);
  245. alert('网络错误,请检查连接!');
  246. }
  247. },
  248. // 新增:处理分页搜索
  249. async handlePageChange(page) {
  250. await this.handleSearch(page);
  251. },
  252. async exportExcel() {
  253. try{
  254. // 构造包含筛选条件的导出参数
  255. const exportParams = {};
  256. // 添加题目类型筛选条件
  257. const questionTypeIdMap = {
  258. '股票知识': 1,
  259. '企业文化': 2
  260. };
  261. if (this.searchForm.questionType) {
  262. exportParams.question_type_id = questionTypeIdMap[this.searchForm.questionType];
  263. }
  264. // 添加推荐系列筛选条件
  265. const courseRecommendationIdMap = {
  266. '量能擒牛': 1,
  267. '价格破译': 2,
  268. '量价时空综合': 3
  269. };
  270. if (this.searchForm.course) {
  271. exportParams.course_recommendation_id = courseRecommendationIdMap[this.searchForm.course];
  272. }
  273. // 添加题干关键词筛选条件
  274. if (this.searchForm.keyword) {
  275. exportParams.stem = this.searchForm.keyword;
  276. }
  277. // 发送导出请求,包含筛选条件
  278. const response = await axios.post(
  279. 'http://192.168.40.41:8000/admin/questions/export',
  280. exportParams,
  281. { responseType: 'blob' }
  282. );
  283. const url = window.URL.createObjectURL(new Blob([response.data]));
  284. const link = document.createElement('a');
  285. link.href = url;
  286. link.setAttribute('download', '题库详细数据表.xlsx');
  287. document.body.appendChild(link);
  288. link.click();
  289. document.body.removeChild(link);
  290. window.URL.revokeObjectURL(url);
  291. alert('导出成功!');
  292. }catch (error) {
  293. console.error('导出 Excel 失败:', error);
  294. alert('网络错误,请检查连接!');
  295. }
  296. },
  297. closeModal() {
  298. this.showAddModal = false;
  299. console.log('关闭弹窗 第一步');
  300. },
  301. resetForm() {
  302. this.newQuestion = {
  303. id: 0,
  304. stem: '',
  305. optionA: '',
  306. optionB: '',
  307. optionC: '',
  308. optionD: '',
  309. correctAnswer: 'A',
  310. questionTypeName: '股票知识',
  311. recommendedCourse: '量能擒牛'
  312. };
  313. }
  314. }
  315. };
  316. </script>
  317. <style scoped>
  318. .top{
  319. padding: 20px 0px;
  320. }
  321. /* 弹窗样式 */
  322. .modal-overlay {
  323. position: fixed;
  324. top: 0;
  325. left: 0;
  326. right: 0;
  327. bottom: 0;
  328. background-color: rgba(0, 0, 0, 0.5);
  329. display: flex;
  330. justify-content: center;
  331. align-items: center;
  332. z-index: 1000;
  333. }
  334. .modal-content {
  335. background-color: white;
  336. border-radius: 8px;
  337. width: 620px;
  338. height: 760px;
  339. max-width: 90%;
  340. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  341. overflow: hidden;
  342. }
  343. .modal-header {
  344. padding: 20px;
  345. border-bottom: 1px solid #eee;
  346. display: flex;
  347. justify-content: space-between;
  348. align-items: center;
  349. }
  350. .modal-header h3 {
  351. margin: 0;
  352. font-size: 18px;
  353. color: #333;
  354. }
  355. .close-btn {
  356. background: none;
  357. border: none;
  358. font-size: 24px;
  359. cursor: pointer;
  360. color: #666;
  361. padding: 5px;
  362. border-radius: 50%;
  363. transition: color 0.2s;
  364. }
  365. .close-btn:hover {
  366. color: #e74c3c;
  367. }
  368. .modal-body {
  369. padding: 20px;
  370. max-height: 600px;
  371. overflow-y: auto;
  372. }
  373. .form-row {
  374. margin-bottom: 16px;
  375. }
  376. .form-row label {
  377. display: block;
  378. margin-bottom: 8px;
  379. font-weight: 500;
  380. color: #333;
  381. }
  382. .form-row select,
  383. .form-row input[type="text"],
  384. .form-row textarea {
  385. width: 100%;
  386. padding: 10px;
  387. border: 1px solid #ddd;
  388. border-radius: 4px;
  389. box-sizing: border-box;
  390. }
  391. .form-row textarea {
  392. resize: vertical;
  393. }
  394. .form-row-options {
  395. display: grid;
  396. grid-template-columns: 1fr 1fr;
  397. gap: 16px;
  398. margin-bottom: 16px;
  399. }
  400. .option-group {
  401. display: flex;
  402. flex-direction: column;
  403. }
  404. .modal-footer {
  405. padding: 20px;
  406. border-top: 1px solid #eee;
  407. display: flex;
  408. justify-content: flex-end;
  409. gap: 16px;
  410. }
  411. /* 响应式设计 */
  412. @media (max-width: 768px) {
  413. .modal-content {
  414. width: 90%;
  415. }
  416. .form-row-options {
  417. grid-template-columns: 1fr;
  418. }
  419. }
  420. </style>