2 Revīzijas cb5afca6f8 ... a0c2d755b2

Autors SHA1 Ziņojums Datums
  闪电 a0c2d755b2 Merge branch 'master' of http://39.164.159.226:3000/hnsh-smart-steward/smart-steward-admin 5 dienas atpakaļ
  闪电 b0e6738a14 mod: 试题管理相关交互 5 dienas atpakaļ

+ 2 - 1
apps/web-ele/.env.development

4
 VITE_BASE=/
4
 VITE_BASE=/
5
 
5
 
6
 # 接口地址
6
 # 接口地址
7
-VITE_GLOB_API_URL=http://39.164.159.226:8088
7
+# VITE_GLOB_API_URL=http://39.164.159.226:8088
8
+VITE_GLOB_API_URL=http://192.168.1.7:8080
8
 # VITE_GLOB_API_URL=http://192.168.1.170:8080
9
 # VITE_GLOB_API_URL=http://192.168.1.170:8080
9
 # VITE_GLOB_FLOW_URL=http://192.168.31.160:8082
10
 # VITE_GLOB_FLOW_URL=http://192.168.31.160:8082
10
 VITE_GLOB_FLOW_URL=http://192.168.1.21:8082
11
 VITE_GLOB_FLOW_URL=http://192.168.1.21:8082

+ 64 - 0
apps/web-ele/src/api/exam/question/question.ts

1
+import { requestClient } from '#/api/request';
2
+
3
+enum Api {
4
+  base = '/examQuestion/question',
5
+  list = '/examQuestion/question/list',
6
+  randomList = '/examQuestion/question/random-list',
7
+}
8
+
9
+export interface Question {
10
+  id: number;
11
+  categoryId: number;
12
+  questionType: string;
13
+  questionContent: string;
14
+  options: string;
15
+  correctAnswer: string;
16
+  answerAnalysis: string;
17
+  analysisReference: string;
18
+  questionStatus: string;
19
+  difficultyLevel: string;
20
+  score: number;
21
+  delFlag: string;
22
+}
23
+
24
+/**
25
+ * 获取试题列表
26
+ */
27
+async function questionList(params?: any) {
28
+  return requestClient.get<Question>(Api.list, { params });
29
+}
30
+
31
+async function questionDetail(id: number) {
32
+  return requestClient.get<Question>(`${Api.base}/${id}`);
33
+}
34
+// 新增试题分类
35
+function addQuestion(question: Question) {
36
+  return requestClient.post(Api.base, question, {
37
+    successMessageMode: 'message',
38
+  });
39
+}
40
+// 修改试题分类
41
+function updateQuestion(question: Question) {
42
+  return requestClient.put(Api.base, question, {
43
+    successMessageMode: 'message',
44
+  });
45
+}
46
+// 删除试题分类
47
+function deleteQuestion(ids: number[]) {
48
+  return requestClient.delete(`${Api.base}/${ids.join(',')}`, {
49
+    successMessageMode: 'message',
50
+  });
51
+}
52
+
53
+function randomQuestionList(params?: any) {
54
+  return requestClient.get<Question>(Api.randomList, { params });
55
+}
56
+
57
+export {
58
+  addQuestion,
59
+  deleteQuestion,
60
+  questionDetail,
61
+  questionList,
62
+  randomQuestionList,
63
+  updateQuestion,
64
+};

+ 1 - 0
apps/web-ele/src/components/questionPreview/index.ts

1
+export { default, default as QuestionPreview } from './src/index.vue';

+ 182 - 0
apps/web-ele/src/components/questionPreview/src/index.vue

1
+<script setup lang="ts">
2
+/**
3
+ * 试题预览组件
4
+ * 支持单选、多选、判断、填空、简答等题型
5
+ */
6
+import { isNumber } from 'lodash-es';
7
+
8
+interface QuestionItem {
9
+  id: number;
10
+  questionType: string;
11
+  questionContent: string;
12
+  options: null | string;
13
+  correctAnswer: string;
14
+  answerAnalysis: string;
15
+}
16
+
17
+interface Props {
18
+  questions: QuestionItem[]; // 试题数组
19
+}
20
+
21
+withDefaults(defineProps<Props>(), {
22
+  questions: () => [],
23
+});
24
+
25
+/**
26
+ * 计算每个试题的选项
27
+ */
28
+const getOptions = (question: QuestionItem): string[] => {
29
+  try {
30
+    const options = question.options ? JSON.parse(question.options) : [];
31
+    // 确保返回的是字符串数组
32
+    return Array.isArray(options) ? options.map(String) : [];
33
+  } catch {
34
+    return [];
35
+  }
36
+};
37
+
38
+/**
39
+ * 计算每个试题的正确答案
40
+ */
41
+const getCorrectAnswer = (question: QuestionItem): number[] => {
42
+  try {
43
+    const answer = JSON.parse(question.correctAnswer);
44
+    // 确保返回的是数字数组
45
+    return Array.isArray(answer)
46
+      ? answer.map((item: any) =>
47
+          isNumber(item) ? Number.parseInt(item.toString(), 10) : item,
48
+        )
49
+      : [];
50
+  } catch {
51
+    return [];
52
+  }
53
+};
54
+
55
+/**
56
+ * 获取选项字母标识(A、B、C...)
57
+ */
58
+const getOptionLabel = (index: number | string) => {
59
+  return String.fromCodePoint(65 + Number(index));
60
+};
61
+</script>
62
+
63
+<template>
64
+  <div class="question-preview">
65
+    <div
66
+      v-for="(question, index) in questions"
67
+      :key="question.id"
68
+      class="question-item mb-6"
69
+    >
70
+      <!-- 题目序号和内容 -->
71
+      <div class="question-header mb-3 flex items-start">
72
+        <span class="question-number mr-2 font-medium">{{ index + 1 }}.</span>
73
+        <div
74
+          class="question-content flex-1"
75
+          v-html="question.questionContent"
76
+        ></div>
77
+      </div>
78
+
79
+      <!-- 选项(单选、多选、判断) -->
80
+      <div
81
+        v-if="
82
+          ['single_choice', 'multiple_choice', 'judgment'].includes(
83
+            question.questionType,
84
+          )
85
+        "
86
+        class="question-options pl-6"
87
+      >
88
+        <div
89
+          v-for="(option, optionIndex) in getOptions(question)"
90
+          :key="optionIndex"
91
+          class="option-item mb-2 flex items-center"
92
+        >
93
+          <!-- 单选题 -->
94
+          <div
95
+            v-if="question.questionType === 'single_choice'"
96
+            class="flex w-full items-center"
97
+          >
98
+            <el-radio
99
+              :value="optionIndex"
100
+              :model-value="getCorrectAnswer(question)[0]"
101
+              disabled
102
+              class="mr-1 flex-shrink-0"
103
+            >
104
+              <span class="option-label mr-2 flex-shrink-0">
105
+                {{ getOptionLabel(optionIndex) }}.
106
+              </span>
107
+              <span class="option-content flex-1" v-html="option"></span>
108
+            </el-radio>
109
+          </div>
110
+
111
+          <!-- 多选题 -->
112
+          <div
113
+            v-else-if="question.questionType === 'multiple_choice'"
114
+            class="flex w-full items-center"
115
+          >
116
+            <el-checkbox
117
+              :value="optionIndex"
118
+              :checked="getCorrectAnswer(question).includes(optionIndex)"
119
+              disabled
120
+              class="mr-1 flex-shrink-0"
121
+            >
122
+              <span class="option-label mr-2 flex-shrink-0">
123
+                {{ getOptionLabel(optionIndex) }}.
124
+              </span>
125
+              <span class="option-content flex-1" v-html="option"></span>
126
+            </el-checkbox>
127
+          </div>
128
+
129
+          <!-- 判断题 -->
130
+          <div
131
+            v-else-if="question.questionType === 'judgment'"
132
+            class="flex w-full items-center"
133
+          >
134
+            <el-radio
135
+              :value="optionIndex"
136
+              :model-value="getCorrectAnswer(question)[0]"
137
+              disabled
138
+              class="mr-1 flex-shrink-0"
139
+            >
140
+              <span class="option-content flex-1" v-html="option"></span>
141
+            </el-radio>
142
+          </div>
143
+        </div>
144
+      </div>
145
+
146
+      <!-- 填空题 -->
147
+      <div
148
+        v-else-if="question.questionType === 'fill_blank'"
149
+        class="question-blanks flex items-start pl-6"
150
+      >
151
+        <div class="blank-label mr-2 flex-shrink-0 font-medium">答案:</div>
152
+        <div class="blank-answers flex-1 text-[#4E5969]">
153
+          {{ getCorrectAnswer(question).join('、') }}
154
+        </div>
155
+      </div>
156
+
157
+      <!-- 简答题 -->
158
+      <div
159
+        v-else-if="question.questionType === 'essay'"
160
+        class="question-essay flex items-start pl-6"
161
+      >
162
+        <div class="blank-label mr-2 flex-shrink-0 font-medium">答案:</div>
163
+        <div
164
+          class="essay-answer flex-1 text-[#4E5969]"
165
+          v-html="getCorrectAnswer(question)[0] || ''"
166
+        ></div>
167
+      </div>
168
+
169
+      <!-- 答案解析 -->
170
+      <div
171
+        v-if="question.answerAnalysis"
172
+        class="question-analysis mt-3 flex items-start pl-6"
173
+      >
174
+        <div class="analysis-label mr-2 flex-shrink-0 font-medium">解析:</div>
175
+        <div
176
+          class="analysis-content flex-1 text-[#4E5969]"
177
+          v-html="question.answerAnalysis"
178
+        ></div>
179
+      </div>
180
+    </div>
181
+  </div>
182
+</template>

+ 3 - 3
apps/web-ele/src/components/tinymce/src/editor.vue

153
     contextmenu: 'link image table',
153
     contextmenu: 'link image table',
154
     default_link_target: '_blank',
154
     default_link_target: '_blank',
155
     height,
155
     height,
156
-    image_advtab: true, // 图片高级选项
157
-    image_caption: true,
156
+    image_advtab: false, // 图片高级选项
157
+    image_caption: false,
158
     importcss_append: true,
158
     importcss_append: true,
159
     language: langName.value,
159
     language: langName.value,
160
     link_title: false,
160
     link_title: false,
164
      * 允许粘贴图片 默认base64格式
164
      * 允许粘贴图片 默认base64格式
165
      * images_upload_handler启用时为上传
165
      * images_upload_handler启用时为上传
166
      */
166
      */
167
-    paste_data_images: true,
167
+    paste_data_images: false,
168
     plugins,
168
     plugins,
169
     quickbars_selection_toolbar:
169
     quickbars_selection_toolbar:
170
       'bold italic | quicklink h2 h3 blockquote quickimage quicktable',
170
       'bold italic | quicklink h2 h3 blockquote quickimage quicktable',

+ 2 - 2
apps/web-ele/src/components/tinymce/src/img-upload.vue

89
       @change="handleChange"
89
       @change="handleChange"
90
     >
90
     >
91
       <!-- 这里要改成i18n -->
91
       <!-- 这里要改成i18n -->
92
-      <ElButton type="primary" v-bind="{ ...getButtonProps }">
92
+      <!-- <ElButton type="primary" v-bind="{ ...getButtonProps }">
93
         图片上传
93
         图片上传
94
-      </ElButton>
94
+      </ElButton> -->
95
     </ElUpload>
95
     </ElUpload>
96
   </div>
96
   </div>
97
 </template>
97
 </template>

+ 8 - 2
apps/web-ele/src/components/tinymce/src/tinymce.ts

4
 // colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration
4
 // colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration
5
 
5
 
6
 // quickbars 快捷栏
6
 // quickbars 快捷栏
7
+// export const plugins =
8
+//   'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap emoticons accordion';
9
+
7
 export const plugins =
10
 export const plugins =
8
-  'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap emoticons accordion';
11
+  'preview importcss searchreplace autolink autosave save directionality visualblocks visualchars fullscreen image link charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount charmap emoticons accordion';
12
+
13
+// export const toolbar =
14
+//   'undo redo | accordion accordionremove | blocks fontfamily fontsize | bold italic underline strikethrough | align numlist bullist | link image | table media | lineheight outdent indent| forecolor backcolor removeformat | charmap emoticons | code fullscreen preview | save print | pagebreak anchor codesample | ltr rtl';
9
 
15
 
10
 export const toolbar =
16
 export const toolbar =
11
-  'undo redo | accordion accordionremove | blocks fontfamily fontsize | bold italic underline strikethrough | align numlist bullist | link image | table media | lineheight outdent indent| forecolor backcolor removeformat | charmap emoticons | code fullscreen preview | save print | pagebreak anchor codesample | ltr rtl';
17
+  'undo redo | accordion accordionremove | blocks fontfamily fontsize | bold italic underline strikethrough | align numlist bullist | link image | lineheight outdent indent| forecolor backcolor removeformat | charmap emoticons | fullscreen preview';

+ 113 - 0
apps/web-ele/src/views/examManage/questionBank/config-data.tsx

1
+import type { FormSchemaGetter } from '#/adapter/form';
2
+import type { VxeGridProps } from '#/adapter/vxe-table';
3
+
4
+import { h } from 'vue';
5
+
6
+// 题型选项
7
+// single_choice-单选题、multiple_choice-多选题、judgment-判断题、essay-简答题、fill_blank-填空题
8
+export const questionTypeOptions = [
9
+  { label: '单选题', value: 'single_choice' },
10
+  { label: '多选题', value: 'multiple_choice' },
11
+  { label: '判断题', value: 'judgment' },
12
+  { label: '填空题', value: 'fill_blank' },
13
+  { label: '简答题', value: 'essay' },
14
+];
15
+
16
+// 试题难度选项 easy-简单、medium-中等、hard-困难
17
+export const difficultyOptions = [
18
+  { label: '简单', value: 'easy', color: '' },
19
+  { label: '中等', value: 'medium', color: 'yellow' },
20
+  { label: '困难', value: 'hard', color: 'red' },
21
+];
22
+
23
+export const queryFormSchema: FormSchemaGetter = () => [
24
+  {
25
+    component: 'Input',
26
+    fieldName: 'content',
27
+    label: '试题内容',
28
+    componentProps: {
29
+      placeholder: '请输入试题内容',
30
+    },
31
+  },
32
+  {
33
+    component: 'Select',
34
+    fieldName: 'questionType',
35
+    label: '题型',
36
+    componentProps: {
37
+      placeholder: '请选择题型',
38
+      options: questionTypeOptions,
39
+    },
40
+  },
41
+  {
42
+    component: 'Select',
43
+    fieldName: 'difficultyLevel',
44
+    label: '试题难度',
45
+    componentProps: {
46
+      placeholder: '请选择试题难度',
47
+      options: difficultyOptions,
48
+    },
49
+  },
50
+];
51
+
52
+export const tableColumns: VxeGridProps['columns'] = [
53
+  {
54
+    type: 'checkbox',
55
+    width: 80,
56
+  },
57
+  {
58
+    field: 'questionContent',
59
+    title: '试题内容',
60
+    minWidth: 300,
61
+    slots: {
62
+      default: ({ row }) => {
63
+        return h('div', { innerHTML: row.questionContent });
64
+      },
65
+    },
66
+  },
67
+  {
68
+    field: 'questionType',
69
+    title: '题型',
70
+    minWidth: 100,
71
+    formatter: ({ row }) => {
72
+      const questionType = questionTypeOptions.find(
73
+        (item) => item.value === row.questionType,
74
+      );
75
+      return questionType?.label || '-';
76
+    },
77
+  },
78
+  {
79
+    field: 'categoryName',
80
+    title: '类别',
81
+    minWidth: 120,
82
+  },
83
+  {
84
+    field: 'createBy',
85
+    title: '创建人',
86
+    minWidth: 120,
87
+  },
88
+  {
89
+    field: 'difficultyName',
90
+    title: '试题难度',
91
+    minWidth: 100,
92
+    slots: {
93
+      default: ({ row }) => {
94
+        const difficulty = difficultyOptions.find(
95
+          (item) => item.value === row.difficultyLevel,
96
+        );
97
+        // 字体颜色,返回 h函数
98
+        return h(
99
+          'span',
100
+          { style: { color: difficulty?.color || 'black' } },
101
+          difficulty?.label || '简单',
102
+        );
103
+      },
104
+    },
105
+  },
106
+  {
107
+    field: 'action',
108
+    fixed: 'right',
109
+    slots: { default: 'action' },
110
+    title: '操作',
111
+    width: 200,
112
+  },
113
+];

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 520 - 507
apps/web-ele/src/views/examManage/questionBank/cpn/questionEdit/index.vue


+ 54 - 165
apps/web-ele/src/views/examManage/questionBank/index.vue

6
 import { onMounted, ref } from 'vue';
6
 import { onMounted, ref } from 'vue';
7
 import { useRoute, useRouter } from 'vue-router';
7
 import { useRoute, useRouter } from 'vue-router';
8
 
8
 
9
-import { Page, useVbenDrawer } from '@vben/common-ui';
9
+import { Page, useVbenDrawer, useVbenModal } from '@vben/common-ui';
10
 
10
 
11
 import {
11
 import {
12
   DArrowLeft,
12
   DArrowLeft,
29
   deleteQuestionCategory,
29
   deleteQuestionCategory,
30
   questionCategoryTree,
30
   questionCategoryTree,
31
 } from '#/api/exam/question/category';
31
 } from '#/api/exam/question/category';
32
+import { deleteQuestion, questionList } from '#/api/exam/question/question';
33
+// 导入预览组件
34
+import QuestionPreview from '#/components/questionPreview';
32
 
35
 
36
+import { queryFormSchema, tableColumns } from './config-data';
33
 import ChapterDrawer from './cpn/chapter/drawer.vue';
37
 import ChapterDrawer from './cpn/chapter/drawer.vue';
34
 
38
 
35
 // 路由实例
39
 // 路由实例
44
 const chapterData = ref([]);
48
 const chapterData = ref([]);
45
 
49
 
46
 // 选中的章节
50
 // 选中的章节
47
-const selectedChapter = ref({});
48
-
49
-// 模拟试题数据
50
-const mockQuestionData = ref([
51
-  {
52
-    id: 1,
53
-    content: '下列关于JavaScript的说法,错误的是?',
54
-    questionType: 'single_choice',
55
-    questionTypeName: '单选题',
56
-    category: '基础知识',
57
-    creator: '管理员',
58
-    difficulty: 'medium',
59
-    difficultyName: '中等',
60
-    reminder: '重要',
61
-  },
62
-  {
63
-    id: 2,
64
-    content: 'Vue.js的核心特性有哪些?',
65
-    questionType: 'multiple_choice',
66
-    questionTypeName: '多选题',
67
-    category: '框架知识',
68
-    creator: '教师1',
69
-    difficulty: 'hard',
70
-    difficultyName: '困难',
71
-    reminder: '一般',
72
-  },
73
-  {
74
-    id: 3,
75
-    content: '简述CSS盒模型的概念',
76
-    questionType: 'essay',
77
-    questionTypeName: '简答题',
78
-    category: '前端基础',
79
-    creator: '教师2',
80
-    difficulty: 'easy',
81
-    difficultyName: '简单',
82
-    reminder: '重要',
83
-  },
84
-]);
85
-
86
-// 提醒选项
87
-const reminderOptions = [
88
-  { label: '重要', value: '重要' },
89
-  { label: '一般', value: '一般' },
90
-  { label: '普通', value: '普通' },
91
-];
92
-
93
-// 试题难度选项
94
-const difficultyOptions = [
95
-  { label: '简单', value: 'easy' },
96
-  { label: '中等', value: 'medium' },
97
-  { label: '困难', value: 'hard' },
98
-];
51
+const selectedChapter: any = ref({});
99
 
52
 
100
 // 表单配置
53
 // 表单配置
101
 const formOptions: VbenFormProps = {
54
 const formOptions: VbenFormProps = {
105
       allowClear: true,
58
       allowClear: true,
106
     },
59
     },
107
   },
60
   },
108
-  schema: [
109
-    {
110
-      component: 'Input',
111
-      fieldName: 'content',
112
-      label: '试题内容',
113
-      componentProps: {
114
-        placeholder: '请输入试题内容',
115
-      },
116
-    },
117
-    {
118
-      component: 'Select',
119
-      fieldName: 'reminder',
120
-      label: '提醒',
121
-      componentProps: {
122
-        placeholder: '请选择提醒等级',
123
-        options: reminderOptions,
124
-      },
125
-    },
126
-    {
127
-      component: 'Select',
128
-      fieldName: 'difficulty',
129
-      label: '试题难度',
130
-      componentProps: {
131
-        placeholder: '请选择试题难度',
132
-        options: difficultyOptions,
133
-      },
134
-    },
135
-  ],
61
+  schema: queryFormSchema(),
136
   wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4',
62
   wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4',
137
 };
63
 };
138
 
64
 
143
     reserve: true,
69
     reserve: true,
144
     trigger: 'default',
70
     trigger: 'default',
145
   },
71
   },
146
-  columns: [
147
-    {
148
-      type: 'checkbox',
149
-      width: 80,
150
-    },
151
-    {
152
-      field: 'content',
153
-      title: '试题内容',
154
-      minWidth: 300,
155
-    },
156
-    {
157
-      field: 'questionTypeName',
158
-      title: '题型',
159
-      minWidth: 100,
160
-    },
161
-    {
162
-      field: 'category',
163
-      title: '类别',
164
-      minWidth: 120,
165
-    },
166
-    {
167
-      field: 'creator',
168
-      title: '创建人',
169
-      minWidth: 120,
170
-    },
171
-    {
172
-      field: 'difficultyName',
173
-      title: '试题难度',
174
-      minWidth: 100,
175
-    },
176
-    {
177
-      field: 'action',
178
-      fixed: 'right',
179
-      slots: { default: 'action' },
180
-      title: '操作',
181
-      width: 150,
182
-    },
183
-  ],
72
+  columns: tableColumns,
184
   size: 'medium',
73
   size: 'medium',
185
   // 固定表格高度,避免高度不断增加
74
   // 固定表格高度,避免高度不断增加
186
   height: 800,
75
   height: 800,
201
     },
90
     },
202
     ajax: {
91
     ajax: {
203
       query: async ({ page }, formValues = {}) => {
92
       query: async ({ page }, formValues = {}) => {
204
-        // 模拟API请求
205
-        console.log('查询参数:', formValues, page);
206
-
207
-        // 模拟搜索过滤
208
-        let filteredData = [...mockQuestionData.value];
209
-
210
-        if (formValues.content) {
211
-          filteredData = filteredData.filter((item) =>
212
-            item.content.includes(formValues.content),
213
-          );
214
-        }
215
-
216
-        if (formValues.reminder) {
217
-          filteredData = filteredData.filter(
218
-            (item) => item.reminder === formValues.reminder,
219
-          );
220
-        }
221
-
222
-        if (formValues.difficulty) {
223
-          filteredData = filteredData.filter(
224
-            (item) => item.difficulty === formValues.difficulty,
225
-          );
226
-        }
227
-
228
-        // 模拟分页
229
-        const start = (page.currentPage - 1) * page.pageSize;
230
-        const end = start + page.pageSize;
231
-        const paginatedData = filteredData.slice(start, end);
232
-        console.log('分页数据:', paginatedData);
93
+        // 调用API获取试题列表
94
+        // 当前选择的章节ID
95
+        const chapterId = selectedChapter.value.id || 0;
96
+        const res: any = await questionList({
97
+          ...formValues,
98
+          categoryId: chapterId,
99
+          currentPage: page.currentPage,
100
+          pageSize: page.pageSize,
101
+        });
233
 
102
 
234
         // 确保返回格式正确,使用 items 作为数据键
103
         // 确保返回格式正确,使用 items 作为数据键
235
         return {
104
         return {
236
-          items: paginatedData,
237
-          total: filteredData.length,
105
+          items: res.rows || [],
106
+          total: res.total || 0,
238
         };
107
         };
239
       },
108
       },
240
     },
109
     },
306
 };
175
 };
307
 
176
 
308
 // 编辑试题
177
 // 编辑试题
309
-const editQuestion = (row) => {
178
+const editQuestion = (row: any) => {
310
   // 跳转到编辑页面,并传递试题ID
179
   // 跳转到编辑页面,并传递试题ID
311
   router.push(`/examManage/questionBank/questionEdit?id=${row.id}`);
180
   router.push(`/examManage/questionBank/questionEdit?id=${row.id}`);
312
 };
181
 };
313
-
182
+const previewQuestions: any = ref([]);
314
 // 预览试题
183
 // 预览试题
315
-const previewQuestion = (row) => {
316
-  // 跳转到预览页面,并传递试题ID
317
-  router.push(`/examManage/questionBank/questionPreview?id=${row.id}`);
184
+const previewQuestion = (row: any) => {
185
+  // 打开预览模态框,并传递试题数据
186
+  previewQuestions.value = [row];
187
+  previewModalApi.open();
318
 };
188
 };
319
 
189
 
320
 // 删除试题
190
 // 删除试题
321
-const deleteQuestion = (row) => {
322
-  ElMessage.info('删除试题功能待实现');
191
+const deleteQuestionEvent = async (row: any) => {
192
+  // 二次确认
193
+  ElMessageBox.confirm('确认删除该试题吗?', '提示', {
194
+    confirmButtonText: '确定',
195
+    cancelButtonText: '取消',
196
+    type: 'warning',
197
+  }).then(async () => {
198
+    await deleteQuestion([row.id]);
199
+    await basicTableApi.reload();
200
+  });
323
 };
201
 };
324
 
202
 
325
 // 批量删除试题
203
 // 批量删除试题
335
     cancelButtonText: '取消',
213
     cancelButtonText: '取消',
336
     type: 'warning',
214
     type: 'warning',
337
   }).then(async () => {
215
   }).then(async () => {
338
-    // 模拟批量删除操作
339
-    console.log('批量删除试题:', ids);
216
+    await deleteQuestion(ids);
340
     await basicTableApi.reload();
217
     await basicTableApi.reload();
341
   });
218
   });
342
 };
219
 };
345
   connectedComponent: ChapterDrawer,
222
   connectedComponent: ChapterDrawer,
346
 });
223
 });
347
 
224
 
225
+// 预览试题模态框
226
+const [PreviewModal, previewModalApi] = useVbenModal({
227
+  // connectedComponent: QuestionPreview,
228
+  title: '预览',
229
+  width: 800,
230
+  height: 600,
231
+  showCancelBtn: false,
232
+});
233
+
348
 const getChapterTree = async () => {
234
 const getChapterTree = async () => {
349
-  const { rows } = await questionCategoryTree();
350
-  chapterData.value = rows;
235
+  const data: any = await questionCategoryTree();
236
+  chapterData.value = data.rows || [];
351
 };
237
 };
352
 
238
 
353
 // 页面加载时获取数据
239
 // 页面加载时获取数据
457
                   <ElButton type="primary" @click="importQuestions">
343
                   <ElButton type="primary" @click="importQuestions">
458
                     导入
344
                     导入
459
                   </ElButton>
345
                   </ElButton>
460
-                  <ElButton type="primary" @click="exportQuestions">
346
+                  <!-- <ElButton type="primary" @click="exportQuestions">
461
                     导出
347
                     导出
462
-                  </ElButton>
348
+                  </ElButton> -->
463
                 </ElSpace>
349
                 </ElSpace>
464
               </template>
350
               </template>
465
               <template #action="{ row }">
351
               <template #action="{ row }">
478
                   <ElButton
364
                   <ElButton
479
                     type="text"
365
                     type="text"
480
                     size="small"
366
                     size="small"
481
-                    @click="deleteQuestion(row)"
367
+                    @click="deleteQuestionEvent(row)"
482
                     class="text-red-500"
368
                     class="text-red-500"
483
                   >
369
                   >
484
                     <ElIcon><Delete /></ElIcon>
370
                     <ElIcon><Delete /></ElIcon>
492
       </div>
378
       </div>
493
     </div>
379
     </div>
494
     <ChapterVbenDrawer @reload="getChapterTree" />
380
     <ChapterVbenDrawer @reload="getChapterTree" />
381
+    <PreviewModal>
382
+      <QuestionPreview :questions="previewQuestions" />
383
+    </PreviewModal>
495
   </Page>
384
   </Page>
496
 </template>
385
 </template>
497
 
386
 
624
 
513
 
625
 .action-buttons {
514
 .action-buttons {
626
   display: flex;
515
   display: flex;
627
-  gap: 8px;
516
+  //gap: 8px;
628
 }
517
 }
629
 </style>
518
 </style>