miaofuhao 2 weeks ago
parent
commit
6d6601e9d6

+ 1 - 2
apps/web-ele/src/router/routes/local.ts

@@ -150,7 +150,7 @@ const localRoutes: RouteRecordStringComponent[] = [
150 150
     component: '/knowledge/edit/index',
151 151
     meta: {
152 152
       activePath: '/knowledge/edit',
153
-      icon: 'carbon:data-base', 
153
+      icon: 'carbon:data-base',
154 154
       title: '知识库编辑',
155 155
       hideInMenu: true,
156 156
     },
@@ -228,4 +228,3 @@ export const localMenuList: RouteRecordStringComponent[] = [
228 228
   // },
229 229
   ...localRoutes,
230 230
 ];
231
-

+ 0 - 0
apps/web-ele/src/views/examManage/examPaper/cpns/examEdit.vue


+ 499 - 0
apps/web-ele/src/views/examManage/examPaper/cpns/examEdit/index.vue

@@ -0,0 +1,499 @@
1
+<script setup lang="ts">
2
+import { computed, ref } from 'vue';
3
+
4
+// 试卷表单数据
5
+const examForm = ref({
6
+  examName: '',
7
+  examCategory: '',
8
+});
9
+
10
+// 当前选中的题目索引
11
+const currentQuestionIndex = ref(0);
12
+
13
+// 新题目标题
14
+const newQuestionTitle = ref('');
15
+
16
+// 试卷题目数据
17
+const examQuestions = ref([
18
+  {
19
+    title: '第一题 选择题',
20
+    fixedQuestions: [
21
+      { type: '填空题', count: 120, score: 0 },
22
+      { type: '简答题', count: 120, score: 0 },
23
+      { type: '单选题', count: 120, score: 0 },
24
+      { type: '多选题', count: 120, score: 0 },
25
+    ],
26
+    randomQuestions: [
27
+      { type: '填空题', count: 120, score: 0 },
28
+      { type: '简答题', count: 120, score: 0 },
29
+      { type: '单选题', count: 120, score: 0 },
30
+      { type: '多选题', count: 120, score: 0 },
31
+    ],
32
+  },
33
+  {
34
+    title: '第二题 填空题',
35
+    fixedQuestions: [],
36
+    randomQuestions: [],
37
+  },
38
+  {
39
+    title: '第三题 判断题',
40
+    fixedQuestions: [],
41
+    randomQuestions: [],
42
+  },
43
+]);
44
+
45
+// 计算总题数
46
+const totalQuestions = computed(() => examQuestions.value.length);
47
+
48
+// 计算小题总数
49
+const totalSubQuestions = computed(() => {
50
+  return examQuestions.value.reduce((total, question) => {
51
+    return (
52
+      total + question.fixedQuestions.length + question.randomQuestions.length
53
+    );
54
+  }, 0);
55
+});
56
+
57
+// 计算总分
58
+const totalScore = computed(() => {
59
+  let score = 0;
60
+  examQuestions.value.forEach((question) => {
61
+    question.fixedQuestions.forEach((item) => {
62
+      score += item.score;
63
+    });
64
+    question.randomQuestions.forEach((item) => {
65
+      score += item.score;
66
+    });
67
+  });
68
+  return score;
69
+});
70
+
71
+// 获取单个题目总分
72
+const getQuestionTotalScore = (index: number) => {
73
+  const question = examQuestions.value[index];
74
+  let score = 0;
75
+  question.fixedQuestions.forEach((item) => {
76
+    score += item.score;
77
+  });
78
+  question.randomQuestions.forEach((item) => {
79
+    score += item.score;
80
+  });
81
+  return score;
82
+};
83
+
84
+// 计算分数变化
85
+const calculateScore = () => {
86
+  // 分数计算逻辑已通过computed属性实现
87
+};
88
+
89
+// 添加大题
90
+const addBigQuestion = () => {
91
+  examQuestions.value.push({
92
+    title: `第${examQuestions.value.length + 1}题 新题目`,
93
+    fixedQuestions: [],
94
+    randomQuestions: [],
95
+  });
96
+  currentQuestionIndex.value = examQuestions.value.length - 1;
97
+};
98
+</script>
99
+
100
+<template>
101
+  <div class="exam-edit-container">
102
+    <!-- 顶部区域 -->
103
+    <div class="top-section">
104
+      <div class="left-section">
105
+        <div class="form-item">
106
+          <label class="label">试卷名称:</label>
107
+          <el-input
108
+            v-model="examForm.examName"
109
+            placeholder="请输入试卷名称"
110
+            :maxlength="30"
111
+            show-word-limit
112
+            class="input"
113
+          />
114
+        </div>
115
+        <div class="form-item">
116
+          <label class="label">试卷分类:</label>
117
+          <el-select
118
+            v-model="examForm.examCategory"
119
+            placeholder="请选择"
120
+            class="select"
121
+          >
122
+            <el-option label="选择题" value="1" />
123
+            <el-option label="填空题" value="2" />
124
+            <el-option label="简答题" value="3" />
125
+            <el-option label="判断题" value="4" />
126
+          </el-select>
127
+        </div>
128
+      </div>
129
+      <div class="right-section">
130
+        <div class="stats">
131
+          <span>{{ totalQuestions }}大题</span>
132
+          <span class="separator">|</span>
133
+          <span>{{ totalSubQuestions }}小题</span>
134
+          <span class="separator">|</span>
135
+          <span>总分:{{ totalScore }}</span>
136
+        </div>
137
+        <el-button type="primary" size="default" class="save-btn">
138
+          保存
139
+        </el-button>
140
+      </div>
141
+    </div>
142
+
143
+    <!-- 主体区域 -->
144
+    <div class="main-section">
145
+      <!-- 左侧导航 -->
146
+      <div class="left-nav">
147
+        <div class="nav-title">大题导航</div>
148
+        <div
149
+          v-for="(question, index) in examQuestions"
150
+          :key="index"
151
+          class="nav-item"
152
+          :class="{ active: currentQuestionIndex === index }"
153
+          @click="currentQuestionIndex = index"
154
+        >
155
+          {{ question.title }}
156
+        </div>
157
+        <el-input
158
+          v-model="newQuestionTitle"
159
+          placeholder="大题名称"
160
+          class="new-question-input"
161
+        />
162
+      </div>
163
+
164
+      <!-- 右侧内容 -->
165
+      <div class="right-content">
166
+        <!-- 题目编辑区域 -->
167
+        <div
168
+          v-for="(question, index) in examQuestions"
169
+          :key="index"
170
+          class="question-block"
171
+          v-show="currentQuestionIndex === index"
172
+        >
173
+          <div class="question-header">
174
+            <span class="question-title">{{ question.title }}</span>
175
+            <el-button type="text" icon="EditPen" class="edit-btn">
176
+              编辑
177
+            </el-button>
178
+          </div>
179
+
180
+          <!-- 固定试题 -->
181
+          <div class="question-type-section">
182
+            <div class="section-title">固定试题</div>
183
+            <el-table :data="question.fixedQuestions" style="width: 100%">
184
+              <el-table-column prop="type" label="试题类型" width="120" />
185
+              <el-table-column prop="count" label="试题数" width="100" />
186
+              <el-table-column prop="score" label="每道题分数" width="120">
187
+                <template #default="scope">
188
+                  <el-input-number
189
+                    v-model="scope.row.score"
190
+                    :min="0"
191
+                    :max="100"
192
+                    size="small"
193
+                    @change="calculateScore"
194
+                  />
195
+                </template>
196
+              </el-table-column>
197
+              <el-table-column label="操作" width="120">
198
+                <template #default="scope">
199
+                  <el-button type="text" size="small" class="preview-btn">
200
+                    预览
201
+                  </el-button>
202
+                  <el-button type="text" size="small" class="delete-btn">
203
+                    删除
204
+                  </el-button>
205
+                </template>
206
+              </el-table-column>
207
+            </el-table>
208
+            <el-button type="text" icon="Plus" class="add-question-btn">
209
+              + 试题库中选题
210
+            </el-button>
211
+          </div>
212
+
213
+          <!-- 随机试题 -->
214
+          <div class="question-type-section">
215
+            <div class="section-title">随机试题</div>
216
+            <el-table :data="question.randomQuestions" style="width: 100%">
217
+              <el-table-column prop="type" label="试题类型" width="120" />
218
+              <el-table-column prop="count" label="试题数" width="100" />
219
+              <el-table-column prop="score" label="每道题分数" width="120">
220
+                <template #default="scope">
221
+                  <el-input-number
222
+                    v-model="scope.row.score"
223
+                    :min="0"
224
+                    :max="100"
225
+                    size="small"
226
+                    @change="calculateScore"
227
+                  />
228
+                </template>
229
+              </el-table-column>
230
+              <el-table-column label="操作" width="120">
231
+                <template #default="scope">
232
+                  <el-button type="text" size="small" class="preview-btn">
233
+                    预览
234
+                  </el-button>
235
+                  <el-button type="text" size="small" class="delete-btn">
236
+                    删除
237
+                  </el-button>
238
+                </template>
239
+              </el-table-column>
240
+            </el-table>
241
+            <el-button type="text" icon="Plus" class="add-question-btn">
242
+              + 试题库中选题
243
+            </el-button>
244
+          </div>
245
+
246
+          <div class="question-footer">
247
+            <span class="question-score"
248
+              >合计:{{ getQuestionTotalScore(index) }}分</span
249
+            >
250
+          </div>
251
+        </div>
252
+
253
+        <!-- 添加大题按钮 -->
254
+        <el-button
255
+          type="dashed"
256
+          icon="Plus"
257
+          class="add-big-question-btn"
258
+          @click="addBigQuestion"
259
+        >
260
+          + 添加大题
261
+        </el-button>
262
+      </div>
263
+    </div>
264
+  </div>
265
+</template>
266
+
267
+<style scoped>
268
+.exam-edit-container {
269
+  padding: 20px;
270
+  background-color: #f5f7fa;
271
+  min-height: 100vh;
272
+}
273
+
274
+/* 顶部区域 */
275
+.top-section {
276
+  display: flex;
277
+  justify-content: space-between;
278
+  align-items: flex-start;
279
+  margin-bottom: 20px;
280
+  background-color: white;
281
+  padding: 20px;
282
+  border-radius: 8px;
283
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
284
+}
285
+
286
+.left-section {
287
+  display: flex;
288
+  flex-direction: column;
289
+  gap: 20px;
290
+  flex: 1;
291
+}
292
+
293
+.form-item {
294
+  display: flex;
295
+  align-items: center;
296
+  gap: 10px;
297
+}
298
+
299
+.label {
300
+  font-size: 14px;
301
+  color: #606266;
302
+  min-width: 80px;
303
+  text-align: right;
304
+}
305
+
306
+.input {
307
+  width: 300px;
308
+}
309
+
310
+.select {
311
+  width: 300px;
312
+}
313
+
314
+.right-section {
315
+  display: flex;
316
+  flex-direction: column;
317
+  align-items: flex-end;
318
+  gap: 15px;
319
+}
320
+
321
+.stats {
322
+  display: flex;
323
+  align-items: center;
324
+  gap: 15px;
325
+  font-size: 14px;
326
+  color: #303133;
327
+}
328
+
329
+.separator {
330
+  color: #dcdfe6;
331
+  font-size: 16px;
332
+}
333
+
334
+.save-btn {
335
+  width: 100px;
336
+}
337
+
338
+/* 主体区域 */
339
+.main-section {
340
+  display: flex;
341
+  gap: 20px;
342
+  background-color: white;
343
+  padding: 20px;
344
+  border-radius: 8px;
345
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
346
+}
347
+
348
+/* 左侧导航 */
349
+.left-nav {
350
+  width: 250px;
351
+  border-right: 1px solid #e6e8eb;
352
+  padding-right: 20px;
353
+}
354
+
355
+.nav-title {
356
+  font-size: 16px;
357
+  font-weight: bold;
358
+  margin-bottom: 15px;
359
+  color: #303133;
360
+}
361
+
362
+.nav-item {
363
+  padding: 10px 15px;
364
+  margin-bottom: 10px;
365
+  border-radius: 4px;
366
+  cursor: pointer;
367
+  transition: all 0.3s;
368
+  background-color: #f5f7fa;
369
+}
370
+
371
+.nav-item:hover {
372
+  background-color: #ecf5ff;
373
+  color: #409eff;
374
+}
375
+
376
+.nav-item.active {
377
+  background-color: #ecf5ff;
378
+  color: #409eff;
379
+  border-left: 3px solid #409eff;
380
+}
381
+
382
+.new-question-input {
383
+  margin-top: 10px;
384
+}
385
+
386
+/* 右侧内容 */
387
+.right-content {
388
+  flex: 1;
389
+  padding-left: 20px;
390
+}
391
+
392
+/* 题目块 */
393
+.question-block {
394
+  margin-bottom: 30px;
395
+  padding: 20px;
396
+  background-color: #fafafa;
397
+  border-radius: 8px;
398
+}
399
+
400
+.question-header {
401
+  display: flex;
402
+  justify-content: space-between;
403
+  align-items: center;
404
+  margin-bottom: 20px;
405
+}
406
+
407
+.question-title {
408
+  font-size: 18px;
409
+  font-weight: bold;
410
+  color: #303133;
411
+}
412
+
413
+.edit-btn {
414
+  color: #409eff;
415
+}
416
+
417
+/* 题目类型区块 */
418
+.question-type-section {
419
+  margin-bottom: 20px;
420
+  padding: 15px;
421
+  background-color: white;
422
+  border-radius: 6px;
423
+}
424
+
425
+.section-title {
426
+  font-size: 16px;
427
+  font-weight: bold;
428
+  margin-bottom: 15px;
429
+  color: #303133;
430
+}
431
+
432
+.add-question-btn {
433
+  color: #409eff;
434
+  margin-top: 10px;
435
+}
436
+
437
+.question-footer {
438
+  text-align: right;
439
+  margin-top: 15px;
440
+  font-size: 14px;
441
+  color: #606266;
442
+}
443
+
444
+.question-score {
445
+  font-weight: bold;
446
+  color: #303133;
447
+}
448
+
449
+/* 添加大题按钮 */
450
+.add-big-question-btn {
451
+  width: 100%;
452
+  margin-top: 20px;
453
+}
454
+
455
+/* 操作按钮样式 */
456
+.preview-btn {
457
+  color: #409eff;
458
+  margin-right: 10px;
459
+}
460
+
461
+.delete-btn {
462
+  color: #f56c6c;
463
+}
464
+
465
+/* 响应式设计 */
466
+@media (max-width: 1200px) {
467
+  .input,
468
+  .select {
469
+    width: 250px;
470
+  }
471
+}
472
+
473
+@media (max-width: 768px) {
474
+  .top-section {
475
+    flex-direction: column;
476
+  }
477
+
478
+  .right-section {
479
+    align-items: flex-start;
480
+  }
481
+
482
+  .main-section {
483
+    flex-direction: column;
484
+  }
485
+
486
+  .left-nav {
487
+    width: 100%;
488
+    border-right: none;
489
+    border-bottom: 1px solid #e6e8eb;
490
+    padding-right: 0;
491
+    padding-bottom: 20px;
492
+    margin-bottom: 20px;
493
+  }
494
+
495
+  .right-content {
496
+    padding-left: 0;
497
+  }
498
+}
499
+</style>

+ 47 - 44
apps/web-ele/src/views/examManage/examPaper/index.vue

@@ -1,30 +1,33 @@
1 1
 <script lang="ts" setup>
2 2
 import type { VbenFormProps } from '@vben/common-ui';
3
+
3 4
 import type { VxeGridProps } from '#/adapter/vxe-table';
4 5
 
5
-import { ref, onMounted } from 'vue';
6
+import { onMounted, ref } from 'vue';
6 7
 import { useRoute, useRouter } from 'vue-router';
8
+
7 9
 import { Page } from '@vben/common-ui';
8
-import { useVbenVxeGrid } from '#/adapter/vxe-table';
9 10
 
10 11
 import {
11
-  Plus,
12
-  Edit,
13
-  Delete,
14
-  DArrowLeft,
15
-  Expand,
16 12
   CopyDocument,
13
+  DArrowLeft,
14
+  Delete,
17 15
   Download,
16
+  Edit,
17
+  Expand,
18
+  Plus,
18 19
 } from '@element-plus/icons-vue';
19 20
 import {
20
-  ElTree,
21 21
   ElButton,
22 22
   ElIcon,
23 23
   ElMessage,
24
-  ElSpace,
25 24
   ElMessageBox,
25
+  ElSpace,
26
+  ElTree,
26 27
 } from 'element-plus';
27 28
 
29
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
30
+
28 31
 // 路由实例
29 32
 const router = useRouter();
30 33
 const route = useRoute();
@@ -192,26 +195,26 @@ const gridOptions: VxeGridProps = {
192 195
       query: async ({ page }, formValues = {}) => {
193 196
         // 模拟API请求
194 197
         console.log('查询参数:', formValues, page);
195
-        
198
+
196 199
         // 模拟搜索过滤
197 200
         let filteredData = [...mockExamData.value];
198
-        
201
+
199 202
         if (formValues.name) {
200
-          filteredData = filteredData.filter(item => 
201
-            item.name.includes(formValues.name)
203
+          filteredData = filteredData.filter((item) =>
204
+            item.name.includes(formValues.name),
202 205
           );
203 206
         }
204
-        
207
+
205 208
         // 模拟分页
206 209
         const start = (page.currentPage - 1) * page.pageSize;
207 210
         const end = start + page.pageSize;
208 211
         const paginatedData = filteredData.slice(start, end);
209 212
         console.log('分页数据:', paginatedData);
210
-        
213
+
211 214
         // 确保返回格式正确,使用 items 作为数据键
212
-        return { 
213
-          items: paginatedData, 
214
-          total: filteredData.length 
215
+        return {
216
+          items: paginatedData,
217
+          total: filteredData.length,
215 218
         };
216 219
       },
217 220
     },
@@ -260,7 +263,7 @@ const deleteChapter = (node) => {
260 263
 // 新增试卷
261 264
 const addExam = () => {
262 265
   // 跳转到新页面
263
-  router.push('/examManage/examEdit');
266
+  router.push('/examManage/examEdit/0');
264 267
 };
265 268
 
266 269
 // 添加时间
@@ -280,7 +283,7 @@ const deleteHandle = () => {
280 283
   if (ids.length <= 0) {
281 284
     return;
282 285
   }
283
-  
286
+
284 287
   ElMessageBox.confirm(`确认删除选中的${ids.length}条数据吗?`, '提示', {
285 288
     confirmButtonText: '确定',
286 289
     cancelButtonText: '取消',
@@ -326,16 +329,15 @@ onMounted(() => {
326 329
 <template>
327 330
   <Page :auto-content-height="true">
328 331
     <div class="knowledge-detail">
329
-
330 332
       <!-- 主体内容区 -->
331 333
       <div class="main-content">
332 334
         <!-- 左侧目录 -->
333
-        <div class="left-sidebar" :class="{ 'collapsed': isCollapse }">
335
+        <div class="left-sidebar" :class="{ collapsed: isCollapse }">
334 336
           <!-- 折叠按钮 -->
335 337
           <div class="collapse-btn" @click="isCollapse = true">
336 338
             <ElIcon><DArrowLeft /></ElIcon>
337 339
           </div>
338
-          
340
+
339 341
           <div class="sidebar-content">
340 342
             <!-- 章节树 -->
341 343
             <div class="chapter-tree">
@@ -387,18 +389,31 @@ onMounted(() => {
387 389
 
388 390
         <!-- 中间内容 -->
389 391
         <div class="center-content">
390
-          <ElIcon v-if="isCollapse" :size="20" class="collapse-control-btn" @click="isCollapse = false"><Expand /></ElIcon>
391
-          
392
+          <ElIcon
393
+            v-if="isCollapse"
394
+            :size="20"
395
+            class="collapse-control-btn"
396
+            @click="isCollapse = false"
397
+          >
398
+            <Expand />
399
+          </ElIcon>
400
+
392 401
           <!-- 试卷列表区域 -->
393 402
           <div class="question-list">
394 403
             <!-- 试卷列表 -->
395 404
             <BasicTable table-title="试卷管理列表">
396 405
               <template #toolbar-tools>
397 406
                 <ElSpace>
398
-                  <ElButton type="danger" :disabled=" !(basicTableApi?.grid?.getCheckboxRecords?.()?.length > 0) " @click="deleteHandle" >
407
+                  <ElButton
408
+                    type="danger"
409
+                    :disabled="
410
+                      !(basicTableApi?.grid?.getCheckboxRecords?.()?.length > 0)
411
+                    "
412
+                    @click="deleteHandle"
413
+                  >
399 414
                     批量删除
400 415
                   </ElButton>
401
-                  <ElButton type="primary" @click="addExam" >
416
+                  <ElButton type="primary" @click="addExam">
402 417
                     新增试卷
403 418
                   </ElButton>
404 419
                 </ElSpace>
@@ -409,7 +424,7 @@ onMounted(() => {
409 424
                     type="text"
410 425
                     size="small"
411 426
                     @click="previewExam(row)"
412
-                    style="margin-left: 0px;"
427
+                    style="margin-left: 0px"
413 428
                   >
414 429
                     预览
415 430
                   </ElButton>
@@ -417,7 +432,7 @@ onMounted(() => {
417 432
                     type="text"
418 433
                     size="small"
419 434
                     @click="copyExam(row)"
420
-                    style="margin-left: 0px;"
435
+                    style="margin-left: 0px"
421 436
                   >
422 437
                     <ElIcon><CopyDocument /></ElIcon>
423 438
                     复制
@@ -426,7 +441,7 @@ onMounted(() => {
426 441
                     type="text"
427 442
                     size="small"
428 443
                     @click="downloadExam(row)"
429
-                    style="margin-left: 0px;"
444
+                    style="margin-left: 0px"
430 445
                   >
431 446
                     <ElIcon><Download /></ElIcon>
432 447
                     下载
@@ -435,7 +450,7 @@ onMounted(() => {
435 450
                     type="text"
436 451
                     size="small"
437 452
                     @click="editExam(row)"
438
-                    style="margin-left: 0px;"
453
+                    style="margin-left: 0px"
439 454
                   >
440 455
                     <ElIcon><Edit /></ElIcon>
441 456
                     编辑
@@ -445,7 +460,7 @@ onMounted(() => {
445 460
                     size="small"
446 461
                     @click="deleteExam(row)"
447 462
                     class="text-red-500"
448
-                    style="margin-left: 0px;"
463
+                    style="margin-left: 0px"
449 464
                   >
450 465
                     <ElIcon><Delete /></ElIcon>
451 466
                     删除
@@ -591,15 +606,3 @@ onMounted(() => {
591 606
   display: flex;
592 607
 }
593 608
 </style>
594
-
595
-
596
-
597
-
598
-
599
-
600
-
601
-
602
-
603
-
604
-
605
-