jiajunchen 2 недель назад
Родитель
Сommit
9c06a75542

+ 16
- 0
src/router/index.ts Просмотреть файл

@@ -684,6 +684,22 @@ const router = createRouter({
684 684
 			name: '课程添加小节',
685 685
 			component: () => import('@/view/dati/courseManagement/section.vue')
686 686
 		},
687
+		{
688
+			path: '/sectionList',
689
+			name: '人员小节',
690
+			component: () => import('@/view/dati/classOne/sectionList.vue')
691
+		},
692
+		{
693
+			path: '/line',
694
+			name: '人员小节答题',
695
+			component: () => import('@/view/dati/classOne/line.vue')
696
+		},
697
+		{
698
+			path: '/learning1',
699
+			name: '人员学习',
700
+			component: () => import('@/view/dati/classOne/learning.vue')
701
+		},
702
+
687 703
 
688 704
 	]
689 705
 })

+ 15
- 4
src/view/dati/classOne/class2.vue Просмотреть файл

@@ -22,7 +22,7 @@
22 22
       </template>
23 23
     </van-nav-bar>
24 24
 
25
-    <van-search v-model="query.name" show-action placeholder="请输入检查任务名称" @search="onRefresh"
25
+    <van-search v-model="query.name" show-action placeholder="请输入学习任务名称" @search="onRefresh"
26 26
                 @cancel="handdelect" />
27 27
     <!-- 项目列表 -->
28 28
     <van-pull-refresh v-model="isRefreshing" success-text="刷新成功" @refresh="onRefresh">
@@ -31,14 +31,14 @@
31 31
           <van-swipe-cell title-style="color: #007aff" style="height: 80px;" :ref="el => getSwipeCellRef(el, idx)">
32 32
             <template #default>
33 33
               <div class="swipe-cell-default">
34
-                <van-cell style="height: 100%; display: flex; align-items: center;" @click="edits(item)">
34
+                <van-cell style="height: 100%; display: flex; align-items: center;">
35 35
                   <template #title>
36 36
                     <div class="cell-title">
37 37
                       {{ item.projectName }}
38 38
                     </div>
39 39
                   </template>
40 40
                   <template #label>
41
-                    <div> 培训学时:{{item.trainHours}}  ||培训类别:{{ item.projectType }}</div>
41
+                    <div> 培训学时:{{item.trainHours}}  ||培训类别:{{ getProjectTypeName(item.projectType)}}</div>
42 42
                     <div style="width: 112px" :class="getStatusClass(item.statusFlag)">
43 43
                       状态:
44 44
                       <span v-if="item.statusFlag === '1'" style="width: 200px">已完成</span>
@@ -107,6 +107,17 @@ const isAfter = (toDateStr) => {
107 107
   return toDate && now > toDate;
108 108
 };
109 109
 
110
+const getProjectTypeName = (code) => {
111
+  const map = {
112
+    'Gbm': '安全管理',
113
+    'Jja': '生产安全技术',
114
+    'Jjb': '消防安全技术',
115
+    'Jjc': '安全保卫技术',
116
+    'Jjd': '特种作业安全技术',
117
+
118
+  };
119
+  return map[code] || '未知类型';
120
+}
110 121
 const onClickLeft = () => {
111 122
   history.back();
112 123
 };
@@ -206,7 +217,7 @@ const handAdd =  () => {
206 217
 };
207 218
 const goaddPeo = (item) => {
208 219
   router.push({
209
-    path: '/addPeo',
220
+    path: '/sectionList',
210 221
     query: {
211 222
       data: JSON.stringify(item)
212 223
     }

+ 325
- 0
src/view/dati/classOne/learning.vue Просмотреть файл

@@ -0,0 +1,325 @@
1
+<template>
2
+  <div class="learning-page">
3
+    <!-- 倒计时区域 -->
4
+    <div class="countdown-timer">
5
+      <h3>⏳ 观看倒计时</h3>
6
+      <div class="time-display" :class="{ finished: remainingTime <= 0 }">
7
+        {{ formattedTime }}
8
+      </div>
9
+    </div>
10
+
11
+    <!-- 视频播放区域 -->
12
+    <div v-if="currentVideo.src" class="video-wrapper">
13
+      <h3>🎥 视频播放</h3>
14
+      <video
15
+        ref="videoRef"
16
+        :src="currentVideo.src"
17
+        controls
18
+        playsinline
19
+        webkit-playsinline
20
+        x5-playsinline
21
+        x5-video-player-type="h5"
22
+      x5-video-player-fullscreen="true"
23
+      preload="metadata"
24
+      @loadeddata="onVideoLoaded"
25
+      @error="onVideoError"
26
+      style="width: 100%; height: auto; aspect-ratio: 16/9; background: #000;"
27
+      >
28
+      您的设备不支持视频播放。
29
+      </video>
30
+
31
+      <!-- 多视频控制 -->
32
+      <div v-if="videoList.length > 1" class="video-controls">
33
+        <van-button size="small" @click="prevVideo" :disabled="currentVideoIndex === 0">
34
+          上一个
35
+        </van-button>
36
+        <span class="video-index">{{ currentVideoIndex + 1 }} / {{ videoList.length }}</span>
37
+        <van-button size="small" @click="nextVideo" :disabled="currentVideoIndex === videoList.length - 1">
38
+          下一个
39
+        </van-button>
40
+      </div>
41
+    </div>
42
+
43
+    <!-- 文档提示(H5 不支持内嵌预览,改为下载) -->
44
+    <div v-if="previewList.length > 0" class="doc-tip">
45
+      <van-notice-bar
46
+        left-icon="volume-o"
47
+        text="当前有文档资料,请点击下方按钮下载查看"
48
+        color="#1989fa"
49
+        background="#ecf9ff"
50
+      />
51
+      <div class="doc-list">
52
+        <van-button
53
+          v-for="item in previewList"
54
+          :key="item.id"
55
+          type="primary"
56
+          plain
57
+          block
58
+          style="margin-top: 8px"
59
+          @click="downloadFile(item)"
60
+        >
61
+          📄 下载 {{ item.fileName }}
62
+        </van-button>
63
+      </div>
64
+    </div>
65
+
66
+    <!-- 加载中 -->
67
+    <van-loading v-if="loading" size="24px" vertical>加载中...</van-loading>
68
+
69
+    <!-- 完成学习按钮 -->
70
+    <div v-if="remainingTime <= 0" class="completion-actions">
71
+      <van-button type="primary" block @click="goBackAndMarkComplete">
72
+        ✅ 完成学习
73
+      </van-button>
74
+    </div>
75
+  </div>
76
+</template>
77
+
78
+<script setup>
79
+import { ref, computed, onMounted, onUnmounted, nextTick, getCurrentInstance } from 'vue'
80
+import { useRoute, useRouter } from 'vue-router'
81
+import { showToast } from 'vant'
82
+import { Base64 } from 'js-base64'
83
+
84
+const route = useRoute()
85
+const router = useRouter()
86
+const { proxy } = getCurrentInstance()
87
+
88
+// ========== 响应式数据 ==========
89
+const loading = ref(false)
90
+const videoList = ref([])
91
+const previewList = ref([])
92
+const currentVideoIndex = ref(0)
93
+const watchTime = ref(0) // 单位:秒
94
+const remainingTime = ref(0)
95
+const timer = ref(null)
96
+const onVideoLoaded = () => {
97
+  loading.value = false // 视频元数据加载完成,可隐藏全局 loading
98
+}
99
+
100
+const onVideoError = () => {
101
+  loading.value = false
102
+  showToast('视频加载失败,请检查网络')
103
+}
104
+// 视频引用
105
+const videoRef = ref(null)
106
+
107
+// ========== 计算属性 ==========
108
+const currentVideo = computed(() => {
109
+  return videoList.value[currentVideoIndex.value] || {}
110
+})
111
+
112
+const formattedTime = computed(() => {
113
+  const mins = Math.floor(remainingTime.value / 60).toString().padStart(2, '0')
114
+  const secs = (remainingTime.value % 60).toString().padStart(2, '0')
115
+  return `${mins}:${secs}`
116
+})
117
+
118
+// ========== 倒计时逻辑 ==========
119
+const startCountdown = () => {
120
+  if (watchTime.value <= 0) return
121
+  remainingTime.value = watchTime.value
122
+  clearInterval(timer.value)
123
+  timer.value = setInterval(() => {
124
+    if (remainingTime.value > 0) {
125
+      remainingTime.value--
126
+    } else {
127
+      clearInterval(timer.value)
128
+      showToast('观看时间结束,学习已完成!')
129
+    }
130
+  }, 1000)
131
+}
132
+
133
+// ========== 视频控制 ==========
134
+const prevVideo = () => {
135
+  if (currentVideoIndex.value > 0) {
136
+    currentVideoIndex.value--
137
+    resetVideo()
138
+  }
139
+}
140
+
141
+const nextVideo = () => {
142
+  if (currentVideoIndex.value < videoList.value.length - 1) {
143
+    currentVideoIndex.value++
144
+    resetVideo()
145
+  }
146
+}
147
+
148
+const resetVideo = () => {
149
+  if (videoRef.value) {
150
+    videoRef.value.load()
151
+    videoRef.value.play().catch(() => {})
152
+  }
153
+}
154
+
155
+const onVideoEnded = () => {
156
+  // 可选:自动播放下一个
157
+}
158
+
159
+// ========== 文件处理 ==========
160
+const downloadFile = (file) => {
161
+  const link = document.createElement('a')
162
+  link.href = file.downloadUrl
163
+  link.download = file.fileName
164
+  link.target = '_blank'
165
+  link.click()
166
+}
167
+
168
+const createPreview = (file) => {
169
+  const { id, fileName, fileType } = file
170
+  const type = fileType?.toLowerCase()
171
+  const bucket = import.meta.env.VITE_BUCKET
172
+  const originUrl = `${import.meta.env.VITE_BASE_API}/framework/Common/downloadFileS3?bucket=${bucket}&id=${id}`
173
+  const downloadUrl = `${originUrl}&fullfilename=${encodeURIComponent(fileName)}`
174
+
175
+  if (type === 'mp4') {
176
+    videoList.value.push({
177
+      id,
178
+      fileName,
179
+      src: downloadUrl
180
+    })
181
+  } else if (['pdf', 'doc', 'docx'].includes(type)) {
182
+    previewList.value.push({
183
+      id,
184
+      fileName,
185
+      downloadUrl
186
+    })
187
+  }
188
+}
189
+
190
+// ========== 数据加载(保留 proxy.$axios)==========
191
+const getFile = async (fileId) => {
192
+  loading.value = true
193
+  previewList.value = []
194
+  videoList.value = []
195
+
196
+  try {
197
+    const response = await proxy.$axios.get('framework/Common/queryFileWithValues', {
198
+      fId: fileId
199
+    })
200
+
201
+    if (response.data.code === 0) {
202
+      const data = response.data.data
203
+      if (!data || !Array.isArray(data) || data.length === 0) {
204
+        showToast('暂无文件数据,请联系管理员')
205
+        return
206
+      }
207
+
208
+      data.forEach(file => createPreview(file))
209
+
210
+      await nextTick()
211
+      if (videoList.value.length > 0) {
212
+        resetVideo()
213
+      }
214
+    } else {
215
+      showToast('获取文件失败:' + response.data.msg)
216
+    }
217
+  } catch (error) {
218
+    console.error('文件加载异常:', error)
219
+    showToast('网络错误,请重试')
220
+  } finally {
221
+    loading.value = false
222
+  }
223
+}
224
+
225
+// ========== 页面初始化 ==========
226
+onMounted(async () => {
227
+  try {
228
+    const rowData = JSON.parse(route.query.data)
229
+    const fileId = rowData.fileId
230
+    const timeInMinutes = Number(rowData.watchTime)
231
+    console.log(timeInMinutes);
232
+    if (!fileId || isNaN(timeInMinutes) || timeInMinutes <= 0) {
233
+      showToast('参数无效')
234
+      return
235
+    }
236
+
237
+    watchTime.value = Math.floor(timeInMinutes * 60)
238
+    startCountdown()
239
+
240
+    await getFile(fileId)
241
+  } catch (error) {
242
+    console.error('路由参数解析失败:', error)
243
+    showToast('页面参数错误')
244
+  }
245
+})
246
+
247
+// ========== 完成学习 ==========
248
+const goBackAndMarkComplete = async () => {
249
+  try {
250
+    const rowData = JSON.parse(route.query.data)
251
+    const taskId = rowData.id
252
+
253
+    const res = await proxy.$axios.post('/sgsafe/Class/saveStatus', {
254
+      json: taskId
255
+    })
256
+
257
+    if (res.data.code === 0) {
258
+      showToast('学习记录已保存')
259
+    } else {
260
+      showToast(res.data.msg || '保存成功')
261
+    }
262
+  } catch (error) {
263
+    console.error('保存失败:', error)
264
+    showToast('保存学习状态失败')
265
+  }
266
+
267
+  router.back()
268
+}
269
+
270
+// ========== 清理定时器 ==========
271
+onUnmounted(() => {
272
+  if (timer.value) clearInterval(timer.value)
273
+})
274
+</script>
275
+
276
+<style scoped>
277
+.learning-page {
278
+  padding: 16px;
279
+  max-width: 100%;
280
+  box-sizing: border-box;
281
+}
282
+
283
+.countdown-timer {
284
+  text-align: center;
285
+  margin-bottom: 20px;
286
+}
287
+
288
+.time-display {
289
+  font-size: 28px;
290
+  font-weight: bold;
291
+  color: #333;
292
+}
293
+
294
+.time-display.finished {
295
+  color: #ee0a24;
296
+}
297
+
298
+.video-wrapper {
299
+  margin-bottom: 20px;
300
+}
301
+
302
+.video-controls {
303
+  display: flex;
304
+  justify-content: space-between;
305
+  align-items: center;
306
+  margin-top: 12px;
307
+}
308
+
309
+.video-index {
310
+  font-size: 14px;
311
+  color: #666;
312
+}
313
+
314
+.doc-tip {
315
+  margin-top: 20px;
316
+}
317
+
318
+.doc-list {
319
+  margin-top: 12px;
320
+}
321
+
322
+.completion-actions {
323
+  margin-top: 30px;
324
+}
325
+</style>

+ 641
- 0
src/view/dati/classOne/line.vue Просмотреть файл

@@ -0,0 +1,641 @@
1
+<template>
2
+  <van-sticky>
3
+    <van-nav-bar>
4
+      <!-- <template #left>
5
+        <van-icon name="arrow-left" size="18" @click="goBack" />
6
+      </template> -->
7
+      <template #title> 答题考试 </template>
8
+      <template #right>
9
+        <!-- 提交按钮 -->
10
+        <van-button @click="checkBeforeSubmit" round color="linear-gradient(to right, #18FFFF, #304FFE)"
11
+                    style="height: 30px; width: 80px">交卷</van-button>
12
+      </template>
13
+    </van-nav-bar>
14
+  </van-sticky>
15
+  <van-overlay :show="overlayloading">
16
+    <div class="wrapper">
17
+      <van-loading color="#0094ff"> 加载中... </van-loading>
18
+    </div>
19
+  </van-overlay>
20
+
21
+  <div class="quiz-page">
22
+    <div class="question-number">
23
+      {{ activeIndex + 1 }}/{{ questions.length }}
24
+    </div>
25
+    <div v-if="questions.length > 0" class="question-content">
26
+      <!-- 题干 -->
27
+      <p class="kong">
28
+        <img
29
+          :src="getQuestionTypeImage(currentQuestion.category)"
30
+          class="question-type-img"
31
+          alt=""
32
+        />
33
+        {{ currentQuestion.stem }}
34
+      </p>
35
+
36
+
37
+      <!-- 单选题 -->
38
+      <div v-if="currentQuestion.category === '单选'">
39
+        <van-radio-group v-model="userAnswers[currentQuestion.id]">
40
+          <van-radio :name="'A'" class="kong">A.{{ currentQuestion.optionA }}</van-radio>
41
+          <van-radio :name="'B'" class="kong">B.{{ currentQuestion.optionB }}</van-radio>
42
+          <van-radio :name="'C'" class="kong">C.{{ currentQuestion.optionC }}</van-radio>
43
+          <van-radio :name="'D'" class="kong" v-if="currentQuestion.optionD">D.
44
+            {{ currentQuestion.optionD }}</van-radio>
45
+          <van-radio :name="'E'" class="kong" v-if="currentQuestion.optionE">E.
46
+            {{ currentQuestion.optionE }}</van-radio>
47
+        </van-radio-group>
48
+      </div>
49
+
50
+      <!-- 多选题 -->
51
+      <div v-if="currentQuestion.category === '多选'">
52
+        <van-checkbox-group v-model="userAnswers[currentQuestion.id]" shape="square">
53
+          <van-checkbox :name="'A'" class="kong">A.{{ currentQuestion.optionA }}</van-checkbox>
54
+          <van-checkbox :name="'B'" class="kong">B.{{ currentQuestion.optionB }}</van-checkbox>
55
+          <van-checkbox :name="'C'" class="kong">C.{{ currentQuestion.optionC }}</van-checkbox>
56
+          <van-checkbox :name="'D'" class="kong"
57
+                        v-if="currentQuestion.optionD">D.{{ currentQuestion.optionD }}</van-checkbox>
58
+          <van-checkbox :name="'E'" class="kong"
59
+                        v-if="currentQuestion.optionE">E.{{ currentQuestion.optionE }}</van-checkbox>
60
+        </van-checkbox-group>
61
+      </div>
62
+
63
+      <!-- 判断题 -->
64
+      <div v-if="currentQuestion.category === '判断'">
65
+        <van-radio-group v-model="userAnswers[currentQuestion.id]">
66
+          <van-radio :name="'A'" class="kong">A.正确</van-radio>
67
+          <van-radio :name="'B'" class="kong">B.错误</van-radio>
68
+        </van-radio-group>
69
+      </div>
70
+    </div>
71
+
72
+    <!-- 底部固定栏 -->
73
+    <div class="footer">
74
+      <van-button @click="prevQuestion" :disabled="activeIndex === 0"
75
+                  style="height: 40px; width: 45%">上一题</van-button>
76
+      <van-button @click="nextQuestion" :disabled="activeIndex === questions.length - 1" style="
77
+          height: 40px;
78
+          width: 45%;
79
+          background-color: var(--van-radio-checked-icon-color);
80
+          border-color: var(--van-radio-checked-icon-color);
81
+          color: #fff;
82
+        ">下一题</van-button>
83
+    </div>
84
+
85
+    <!-- 提交前确认弹窗 -->
86
+    <van-dialog v-model:show="confirmSubmitDialog" title="确认交卷" show-cancel-button @confirm="submitForm">
87
+      <p :class="{ 'van-dialog__message': true, 'text-center': true }">
88
+        <span v-if="hasUnanswered">{{ unansweredText }}</span>
89
+        <span v-else>{{ completedText }}</span>
90
+      </p>
91
+    </van-dialog>
92
+
93
+    <!-- 结果弹窗 -->
94
+    <van-popup v-model:show="showResult" position="top" style="height: 100%">
95
+      <van-sticky>
96
+        <van-nav-bar title="答题结果" />
97
+      </van-sticky>
98
+      <div style="
99
+          margin-top: 10px;
100
+          margin-left: 20px;
101
+          margin-bottom: 20px;
102
+          font-weight: bold;
103
+        ">
104
+        本次得分:{{ totalScore }}
105
+        <!--取接口成绩-->
106
+      </div>
107
+      <van-divider />
108
+      <!-- 题干 -->
109
+      <div v-for="question in questions" :key="question.id" class="question">
110
+        <p>
111
+          <span v-if="question.category === '单选'">[单选]</span>
112
+          <span v-if="question.category === '多选'">[多选]</span>
113
+          <span v-if="question.category === '判断'">[判断]</span>
114
+          {{ question.stem }}
115
+        </p>
116
+        <!-- 显示提交答案 -->
117
+        <p>
118
+					<span :style="{
119
+              color: Array.isArray(userAnswers[question.id])
120
+                ? userAnswers[question.id].sort().join('') ===
121
+                  question.answer
122
+                  ? '#007aff'
123
+                  : 'red'
124
+                : userAnswers[question.id] === question.answer
125
+                ? '#007aff'
126
+                : 'red',
127
+            }">
128
+						提交答案:{{
129
+              Array.isArray(userAnswers[question.id])
130
+                ? userAnswers[question.id].sort().join("")
131
+                : userAnswers[question.id] || "未作答"
132
+            }}
133
+					</span>
134
+        </p>
135
+        <!-- 显示正确答案 -->
136
+        <p style="color: #007aff">正确答案:{{ question.answer }}</p>
137
+        <div v-if="question.category === '单选'" class="kong">
138
+          <div>A. {{ question.optionA }}</div>
139
+          <div>B. {{ question.optionB }}</div>
140
+          <div>C. {{ question.optionC }}</div>
141
+          <div v-if="question.optionD">D. {{ question.optionD }}</div>
142
+          <div v-if="question.optionE">E. {{ question.optionE }}</div>
143
+        </div>
144
+        <div v-if="question.category === '多选'" class="kong">
145
+          <div>A. {{ question.optionA }}</div>
146
+          <div>B. {{ question.optionB }}</div>
147
+          <div>C. {{ question.optionC }}</div>
148
+          <div v-if="question.optionD">D. {{ question.optionD }}</div>
149
+          <div v-if="question.optionE">E. {{ question.optionE }}</div>
150
+        </div>
151
+        <div v-if="question.category === '判断'" class="kong">
152
+          <div>A.正确</div>
153
+          <div>B.错误</div>
154
+        </div>
155
+
156
+        <!-- AI解析按钮和内容 -->
157
+        <div style="margin: 10px 0;">
158
+          <van-button
159
+            type="primary"
160
+            size="small"
161
+            :loading="analysisLoading[question.id]"
162
+            @click="generateAIAnalysis(question,true)"
163
+          >
164
+            {{ aiAnalysis[question.id] ? '重新解析' : 'AI解析' }}
165
+          </van-button>
166
+
167
+          <!-- 显示AI解析内容 -->
168
+          <div v-if="aiAnalysis[question.id]" class="ai-analysis-content">
169
+            <div v-html="renderAnalysis(aiAnalysis[question.id])"></div>
170
+          </div>
171
+        </div>
172
+
173
+        <van-divider />
174
+      </div>
175
+      <div style="margin-top: 20px; text-align: center; margin-bottom: 20px">
176
+        <van-button class="questionBtn" type="primary" @click="confirmResult">确定</van-button>
177
+      </div>
178
+    </van-popup>
179
+  </div>
180
+</template>
181
+
182
+<script setup>
183
+import {
184
+  onMounted,
185
+  ref,
186
+  getCurrentInstance,
187
+  computed
188
+} from "vue";
189
+import {
190
+  showConfirmDialog,
191
+  showSuccessToast,
192
+  showFailToast,
193
+  showDialog
194
+} from 'vant';
195
+import {
196
+  useRouter,
197
+  useRoute
198
+} from "vue-router";
199
+import {
200
+  examResult,
201
+  formNew,
202
+  myDefaultCourse,
203
+  saveScore,
204
+  sortData,
205
+} from "@/api/dati";
206
+const {
207
+  proxy
208
+} = getCurrentInstance()
209
+
210
+const router = useRouter();
211
+const route = useRoute();
212
+
213
+const courseId = route.query.courseId;
214
+const userId = route.query.userId;
215
+if (userId == '' || userId == 'undefined') userId = localStorage.getItem('userId')
216
+const todayStr = route.query.todayStr;
217
+
218
+const questions = ref([]);
219
+
220
+const userAnswers = ref({});
221
+const activeIndex = ref(0);
222
+const totalScore = ref(0);
223
+const showResult = ref(false);
224
+
225
+const confirmSubmitDialog = ref(false);
226
+const hasUnanswered = ref(false);
227
+const unansweredText = "有题目未完成,是否确认交卷?";
228
+const completedText = "已完成所有题目,是否确认交卷?";
229
+const overlayloading = ref(false);
230
+// 在组件挂载时获取试卷
231
+onMounted(async () => {
232
+  overlayloading.value = true;
233
+  await getForm();
234
+  overlayloading.value = false;
235
+});
236
+
237
+//获取试卷
238
+const getForm = async () => {
239
+  var url = '/sgsafe/ExamLine/query'
240
+  const query = ref({
241
+    headId: courseId
242
+  })
243
+  var param = {
244
+    params: JSON.stringify(query.value)
245
+  }
246
+  try {
247
+    const res = await proxy.$axios.post(url, param);
248
+    if (res.data.code === 0) {
249
+      questions.value = res.data.data
250
+    } else {
251
+      console.log('操作失败!' + res.data.msg);
252
+    }
253
+  } catch (error) {
254
+    console.log('请求出错:', questions);
255
+  }
256
+};
257
+
258
+// 获取当前题目
259
+const currentQuestion = computed(() => {
260
+  return questions.value[activeIndex.value];
261
+});
262
+
263
+// 获取题目类型对应的图片路径
264
+import danxuan from '@/assets/img/dx.svg'
265
+import duoxuanImg from '@/assets/img/ksdx.svg'
266
+import panduanImg from '@/assets/img/kspd.svg'
267
+const getQuestionTypeImage = (category) => {
268
+  switch (category) {
269
+    case "单选": // 单选
270
+      return danxuan;
271
+    case "多选": // 多选
272
+      return duoxuanImg;
273
+    case "判断": // 判断
274
+      return panduanImg;
275
+    default:
276
+      return "";
277
+  }
278
+};
279
+
280
+//返回答题首页
281
+const goBack = () => {
282
+  router.push({
283
+    path: "/dailyproblem"
284
+  });
285
+};
286
+
287
+// 切换到下一题
288
+const nextQuestion = () => {
289
+  if (activeIndex.value < questions.value.length - 1) {
290
+    activeIndex.value++;
291
+  }
292
+};
293
+
294
+// 切换到上一题
295
+const prevQuestion = () => {
296
+  if (activeIndex.value > 0) {
297
+    activeIndex.value--;
298
+  }
299
+};
300
+
301
+
302
+const getUserAnswers = () => {
303
+  let useranswers = [];
304
+  questions.value.forEach((question) => {
305
+    const userAnswer = userAnswers.value[question.id]; // 获取用户的答案
306
+    let userAnswerString;
307
+    if (Array.isArray(userAnswer)) {
308
+      // 多选题,将数组转换为字符串
309
+      userAnswerString = userAnswer.sort().join(""); // 排序并转换为字符串,如 "ABC"
310
+    } else {
311
+      // 单选题,直接是字符串
312
+      userAnswerString = userAnswer || ""; // 如果未选择答案,则设为空字符串
313
+    }
314
+    // 将答案保存到 answers 数组中
315
+    useranswers.push({
316
+      id: question.id, // 题目 ID
317
+      userAnswer: userAnswerString, // 用户的答案
318
+    });
319
+  });
320
+  return useranswers;
321
+};
322
+
323
+//交卷
324
+const submitForm = async () => {
325
+  overlayloading.value = true;
326
+  try {
327
+    // 1. 保存答案
328
+    const answers = getUserAnswers();
329
+    const saveRes = await proxy.$axios.post('/sgsafe/ExamLine/appSaveMyScore', {
330
+      json: JSON.stringify(answers)
331
+    });
332
+    if (saveRes.data.code !== 0) {
333
+      showFailToast("答案保存失败");
334
+      return;
335
+    }
336
+
337
+    // 2. 触发判卷
338
+    const gradeRes = await proxy.$axios.post('/sgsafe/Package/doProc', {
339
+      procName: 'safeplat.sxsp_grade_exam_class',
340
+      param: JSON.stringify([courseId])
341
+    });
342
+    if (gradeRes.data.code !== 0) {
343
+      showFailToast("判卷失败,请稍后重试");
344
+      return;
345
+    }
346
+    const query = ref({
347
+      headId: courseId
348
+    })
349
+
350
+    // 3. 调用 queryMistake 获取错题及得分明细
351
+    const mistakeRes = await proxy.$axios.post(
352
+      '/sgsafe/ExamLine/queryMistake',
353
+      {
354
+        params: JSON.stringify(query.value) // 注意:这里和后端参数名一致
355
+      }
356
+    );
357
+    console.log(mistakeRes.data);
358
+    if (mistakeRes.data.code != 0) {
359
+      console.log('获取答题结果失败!' + res.data.msg);
360
+      return;
361
+    }
362
+
363
+    const data = mistakeRes.data.data || [];
364
+
365
+    // 直接计算总分和用户得分(不要用 computed!)
366
+    // totalScore.value = data.reduce((sum, item) => sum + (Number(item.score) || 0), 0);
367
+   totalScore.value = data.reduce((sum, item) => sum + (Number(item.userScore) || 0), 0);
368
+
369
+    // (可选)如果后续页面需要错题列表,也可以存下来
370
+    // tableData2.value = data;
371
+
372
+    // 4. 弹出结果提示
373
+    showConfirmDialog({
374
+      message: "判卷完成",
375
+      confirmButtonText: "查看本次答题结果",
376
+      cancelButtonText: "退出答题"
377
+    })
378
+      .then(() => {
379
+        showResult.value = true; // 显示结果页(会用到 totalScore 和 userTotalScore)
380
+      })
381
+      .catch(() => {
382
+        router.back()
383
+      });
384
+
385
+  } catch (error) {
386
+    console.error("交卷过程出错:", error);
387
+    showFailToast("交卷失败,请重试");
388
+  } finally {
389
+    overlayloading.value = false;
390
+  }
391
+};
392
+// 确认结果并返回
393
+const confirmResult = () => {
394
+  showResult.value = false;
395
+  // router.back();
396
+  router.back()
397
+};
398
+
399
+// 检查是否所有题目都已作答
400
+const checkBeforeSubmit = () => {
401
+  hasUnanswered.value = questions.value.some((question) => {
402
+    const userAnswer = userAnswers.value[question.id];
403
+    return (
404
+      !userAnswer || (Array.isArray(userAnswer) && userAnswer.length === 0)
405
+    );
406
+  });
407
+  confirmSubmitDialog.value = true;
408
+};
409
+
410
+// AI解析功能
411
+// AI解析相关变量
412
+const aiAnalysis = ref({}); // 存储每道题的AI解析内容
413
+const analysisLoading = ref({}); // 存储每道题的解析加载状态
414
+import { fetchHuaweiResponse } from "@/tools/deepseek.js";
415
+// 动态导入依赖
416
+let marked, DOMPurify;
417
+
418
+const initMarkdownLibs = async () => {
419
+  try {
420
+    // 尝试导入marked
421
+    const markedModule = await import('marked');
422
+    marked = markedModule.marked || markedModule.default || markedModule;
423
+
424
+    // 尝试导入DOMPurify
425
+    const dompurifyModule = await import('dompurify');
426
+    DOMPurify = dompurifyModule.default || dompurifyModule;
427
+  } catch (error) {
428
+    console.warn('Markdown libraries not available, using plain text', error);
429
+    // 如果导入失败,使用基础功能
430
+    marked = {
431
+      parse: (text) => text
432
+    };
433
+    DOMPurify = {
434
+      sanitize: (html) => html
435
+    };
436
+  }
437
+};
438
+
439
+// 在组件挂载时初始化
440
+onMounted(() => {
441
+  initMarkdownLibs();
442
+});
443
+
444
+// 生成AI解析
445
+const generateAIAnalysis = async (question, force = false) => {
446
+  // 如果该题已有解析且不是强制重新生成,直接返回
447
+  if (aiAnalysis.value[question.id] && !force) {
448
+    return;
449
+  }
450
+
451
+  // 如果是重新解析,先清空之前的内容
452
+  if (force) {
453
+    aiAnalysis.value[question.id] = '';
454
+  }
455
+
456
+  // 确保依赖已加载
457
+  if (!marked || !DOMPurify) {
458
+    await initMarkdownLibs();
459
+  }
460
+
461
+  // 设置加载状态
462
+  analysisLoading.value[question.id] = true;
463
+
464
+  try {
465
+    // 构造提示词
466
+    let prompt = `请为以下题目提供详细解析:
467
+题目类型:${question.category}题干:${question.stem}`;
468
+
469
+    // 添加选项
470
+    if (question.optionA) prompt += `\nA. ${question.optionA}`;
471
+    if (question.optionB) prompt += `\nB. ${question.optionB}`;
472
+    if (question.optionC) prompt += `\nC. ${question.optionC}`;
473
+    if (question.optionD) prompt += `\nD. ${question.optionD}`;
474
+    if (question.optionE) prompt += `\nE. ${question.optionE}`;
475
+
476
+    prompt += `\n正确答案:${question.answer}`;
477
+
478
+    // 添加用户答案(如果已作答)
479
+    const userAnswer = userAnswers.value[question.id];
480
+    if (userAnswer) {
481
+      const userAnswerString = Array.isArray(userAnswer)
482
+        ? userAnswer.sort().join("")
483
+        : userAnswer;
484
+      prompt += `\n用户答案:${userAnswerString}`;
485
+    }
486
+
487
+    prompt += `\n\n请提供以下内容:
488
+1. 正确答案的解释
489
+2. 为什么其他选项不正确(如果用户答案错误)
490
+3. 相关知识点说明`;
491
+
492
+    // 构造消息对象
493
+    const messages = [
494
+      {
495
+        role: "user",
496
+        content: prompt
497
+      }
498
+    ];
499
+
500
+    // 调用AI接口
501
+    fetchHuaweiResponse(
502
+      messages,
503
+      (content, isThinking, isEnd) => {
504
+        // 实时更新解析内容
505
+        aiAnalysis.value[question.id] = content;
506
+
507
+        // 如果是最终结果,停止加载状态
508
+        if (isEnd) {
509
+          analysisLoading.value[question.id] = false;
510
+        }
511
+      },
512
+      null
513
+    );
514
+  } catch (error) {
515
+    console.error('AI解析生成失败:', error);
516
+    analysisLoading.value[question.id] = false;
517
+    aiAnalysis.value[question.id] = '解析生成失败';
518
+  }
519
+};
520
+
521
+// 解析内容转换为HTML
522
+const renderAnalysis = (content) => {
523
+  if (!content) return '';
524
+
525
+  try {
526
+    // 确保依赖已加载
527
+    if (!marked || !DOMPurify) {
528
+      return content.replace(/\n/g, '<br>');
529
+    }
530
+
531
+    const html = marked.parse ? marked.parse(content) : marked(content);
532
+    return DOMPurify.sanitize ? DOMPurify.sanitize(html) : html;
533
+  } catch (error) {
534
+    console.error('Markdown解析错误:', error);
535
+    return content.replace(/\n/g, '<br>');
536
+  }
537
+};
538
+
539
+
540
+
541
+
542
+</script>
543
+
544
+<style scoped>
545
+.quiz-page {
546
+  padding: 20px;
547
+}
548
+
549
+.question-type-img {
550
+  width: 54px;
551
+  height: 20px;
552
+}
553
+
554
+.question {
555
+  margin-left: 20px;
556
+  margin-right: 20px;
557
+}
558
+
559
+.kong {
560
+  margin-bottom: 20px;
561
+}
562
+
563
+.footer {
564
+  position: fixed;
565
+  bottom: 0;
566
+  left: 0;
567
+  width: 100%;
568
+  background-color: #fff;
569
+  display: flex;
570
+  justify-content: space-around;
571
+  align-items: center;
572
+  margin-bottom: 10px;
573
+}
574
+
575
+.van-dialog__message {
576
+  text-align: center;
577
+}
578
+
579
+.questionBtn {
580
+  width: 40%;
581
+}
582
+
583
+/* 遮罩 */
584
+.wrapper {
585
+  display: flex;
586
+  align-items: center;
587
+  justify-content: center;
588
+  height: 100%;
589
+}
590
+
591
+.van-overlay {
592
+  z-index: 2;
593
+  background-color: rgba(0, 0, 0, 0.5);
594
+}
595
+
596
+.ai-analysis-content {
597
+  margin-top: 10px;
598
+  padding: 10px;
599
+  background-color: #f5f5f5;
600
+  border-radius: 4px;
601
+  font-size: 14px;
602
+  line-height: 1.6;
603
+}
604
+
605
+.ai-analysis-content :deep(h1),
606
+.ai-analysis-content :deep(h2),
607
+.ai-analysis-content :deep(h3) {
608
+  margin: 10px 0;
609
+  font-weight: bold;
610
+  font-size: 16px;
611
+}
612
+
613
+.ai-analysis-content :deep(p) {
614
+  margin: 8px 0;
615
+}
616
+
617
+.ai-analysis-content :deep(ul),
618
+.ai-analysis-content :deep ol {
619
+  padding-left: 20px;
620
+  margin: 8px 0;
621
+}
622
+
623
+.ai-analysis-content :deep(li) {
624
+  margin: 4px 0;
625
+}
626
+
627
+.ai-analysis-content :deep(code) {
628
+  padding: 2px 4px;
629
+  background-color: #e0e0e0;
630
+  border-radius: 3px;
631
+  font-family: monospace;
632
+}
633
+
634
+.ai-analysis-content :deep(pre) {
635
+  padding: 10px;
636
+  background-color: #e0e0e0;
637
+  border-radius: 4px;
638
+  overflow-x: auto;
639
+}
640
+
641
+</style>

+ 830
- 0
src/view/dati/classOne/sectionList.vue Просмотреть файл

@@ -0,0 +1,830 @@
1
+<template>
2
+  <div class="h5-container">
3
+    <van-nav-bar title="学习课程小节">
4
+
5
+      <template #right>
6
+        <van-icon
7
+          name="add"
8
+          size="24"
9
+          color="var(--van-nav-bar-icon-color)"
10
+          @click="handAdd"
11
+        />
12
+        <van-popover
13
+          v-model:show="showPopover"
14
+          :actions="actions"
15
+          placement="bottom-end"
16
+          @select="onSelect"
17
+        >
18
+          <template #reference>
19
+            <van-icon name="filter-o" size="24px" />
20
+          </template>
21
+        </van-popover>
22
+      </template>
23
+    </van-nav-bar>
24
+
25
+
26
+    <!-- 项目列表 -->
27
+    <van-pull-refresh v-model="isRefreshing" success-text="刷新成功" @refresh="onRefresh">
28
+      <van-list v-model:loading="isLoading" :finished="isFinished" finished-text="没有更多了" offset="200" @load="onLoad">
29
+        <div v-for="(item, idx) in resultData" :key="item.id">
30
+          <van-swipe-cell title-style="color: #007aff" style="height: 80px;" :ref="el => getSwipeCellRef(el, idx)">
31
+            <template #default>
32
+              <div class="swipe-cell-default">
33
+                <van-cell style="height: 100%; display: flex; align-items: center;" >
34
+                  <template #title>
35
+                    <div class="cell-title">
36
+                      {{ item.sectionName }}
37
+                    </div>
38
+                  </template>
39
+                  <template #label>
40
+                    <div> 小节类型:{{ getProjectTypeName(item.studyorExam)}} </div>
41
+                    <div style="width: 112px" :class="getStatusClass(item.isFinish)">
42
+                      状态:
43
+                      <span v-if="item.isFinish == '1'" style="width: 200px">已完成</span>
44
+                      <span v-else-if="item.isFinish == '2'" style="width: 200px">不及格</span>
45
+                      <span v-else>未完成</span>
46
+                    </div>
47
+                  </template>
48
+                </van-cell>
49
+                <div class="swipe-cell-default-icon">
50
+                  <van-icon v-if="openStatus[idx]" name="arrow-double-left" @click.stop="openSwipe(idx)" />
51
+                  <van-icon v-else name="arrow-double-right" @click.stop="closeSwipe(idx)" />
52
+                </div>
53
+              </div>
54
+            </template>
55
+
56
+            <template #right>
57
+
58
+              <div style="display: flex; align-items: center; justify-content: flex-end; height: 100%;">
59
+                <van-button v-if="item.studyorExam =='study'" @click="goaddLearn(item)" class="red-rounded-box-wide" text="学习"/>
60
+                <van-button v-else-if="item.studyorExam =='exam'"  @click="goaddPeo(item)" class="red-rounded-box-wide" text="考试"/>
61
+
62
+              </div>
63
+
64
+            </template>
65
+          </van-swipe-cell>
66
+        </div>
67
+
68
+      </van-list>
69
+    </van-pull-refresh>
70
+
71
+  </div>
72
+</template>
73
+
74
+<script setup>
75
+import { ref, reactive, onMounted, getCurrentInstance, nextTick, toRaw } from 'vue';
76
+import { Dialog, showDialog, showSuccessToast, showToast, Toast } from 'vant';
77
+
78
+const { proxy } = getCurrentInstance();
79
+
80
+
81
+
82
+
83
+
84
+const route = useRoute()
85
+const rouData =ref({})
86
+rouData.value = JSON.parse(route.query.data)
87
+
88
+
89
+
90
+// 将字符串转为 Date 对象进行比较
91
+const parseDate = (str) => {
92
+  if (!str) return null;
93
+  // 支持 "YYYY-MM-DD HH:mm:ss" 或 ISO 格式
94
+  return new Date(str);
95
+};
96
+
97
+
98
+// 当前时间是否在 fromDate 之前(未开始)
99
+const isBefore = (fromDateStr) => {
100
+  const now = new Date();
101
+  const fromDate = parseDate(fromDateStr);
102
+  return fromDate && now < fromDate;
103
+};
104
+
105
+// 当前时间是否在 toDate 之后(已结束)
106
+const isAfter = (toDateStr) => {
107
+  const now = new Date();
108
+  const toDate = parseDate(toDateStr);
109
+  return toDate && now > toDate;
110
+};
111
+
112
+
113
+
114
+
115
+const getProjectTypeName = (code) => {
116
+  const map = {
117
+    'exam': '考试',
118
+    'study': '学习',
119
+
120
+
121
+  };
122
+  return map[code] || '未知类型';
123
+}
124
+const onClickLeft = () => {
125
+  history.back();
126
+};
127
+
128
+var userId = localStorage.getItem('userId')
129
+const switchIconState = (idx) => {
130
+  openStatus.value[idx] = !openStatus.value[idx]
131
+  openStatus.value = new Array(resultData.value.length).fill(true);
132
+}
133
+
134
+// const onClickRight = () =>{
135
+//   searchShow.value = !searchShow.value;
136
+// }
137
+
138
+const searchShow = ref(false);
139
+
140
+/**
141
+ 时间
142
+ */
143
+// 当前年份
144
+const currentYear = new Date().getFullYear();
145
+
146
+// 生成年份:往前3年,往后1年 → 共5年
147
+const yearRange = Array.from({ length: 5 }, (_, i) => currentYear - 3 + i);
148
+
149
+// 构造 actions(符合 van-popover 要求)
150
+const actions = yearRange.map(year => ({
151
+  text: `${year}年`
152
+}));
153
+
154
+// 默认选中的年份(用于请求)
155
+const selectedYear = ref(currentYear);
156
+
157
+// 控制 popover 显示(可选,你用了 v-model:show 就够了)
158
+const showPopover = ref(false);
159
+
160
+// 选择回调
161
+const onSelect = (action, index) => {
162
+  query.value.year = yearRange[index];
163
+  // 👇 触发刷新(带上 selectedYear)
164
+  console.log(selectedYear.value);
165
+  resetAndRefresh();
166
+};
167
+const query = ref({
168
+  year: currentYear, // ← 默认就是今年
169
+  name: '',
170
+});
171
+
172
+// 重置并刷新(你已有类似逻辑)
173
+const resetAndRefresh = () => {
174
+  isFinished.value = false;
175
+  isLoading.value = false;
176
+
177
+  resultData.value = [];
178
+  onRefresh();
179
+};
180
+
181
+function formatDate(date, format) {
182
+  const year = date.getFullYear();
183
+  const month = date.getMonth() + 1;
184
+  const day = date.getDate();
185
+  const hours = date.getHours();
186
+  const minutes = date.getMinutes();
187
+  const seconds = date.getSeconds();
188
+
189
+  return format
190
+    .replace('yyyy', year)
191
+    .replace('MM', month.toString().padStart(2, '0'))
192
+    .replace('dd', day.toString().padStart(2, '0'))
193
+    .replace('HH', hours.toString().padStart(2, '0'))
194
+    .replace('mm', minutes.toString().padStart(2, '0'))
195
+    .replace('ss', seconds.toString().padStart(2, '0'));
196
+}
197
+
198
+const tableData = ref([]);
199
+const selectedRows = ref([]);
200
+const dialogVisibleLook = ref(false);
201
+const deleteDialogVisible = ref(false);
202
+const currentDeleteItem = ref([]);
203
+const dialogVisible = ref(false);
204
+const dialogVisibleFile = ref(false);
205
+const date = ref(null);
206
+
207
+const kz = ref(true);
208
+import { useRoute, useRouter } from 'vue-router';
209
+const router = useRouter();
210
+
211
+const handAdd =  () => {
212
+
213
+  router.push({ path: "/checkList",
214
+    query: {
215
+      mark:-1
216
+    } });
217
+
218
+};
219
+const today = new Date();
220
+const year = today.getFullYear();
221
+const month = String(today.getMonth() + 1).padStart(2, '0');
222
+const day = String(today.getDate()).padStart(2, '0');
223
+const todayStr = `${year}-${month}-${day}`;
224
+const goaddPeo =async (item) => {
225
+  //在这的时候生成题目
226
+  await prepareQuizData(item.id)
227
+
228
+  router.push({
229
+    path: "/line",
230
+    query: {
231
+      courseId: item.id,
232
+      userId: userId,
233
+      todayStr: todayStr,
234
+
235
+    },
236
+  });
237
+}
238
+const goaddLearn =async (item) => {
239
+  router.push({
240
+    path: '/learning1',
241
+    query: {
242
+      data: JSON.stringify(item)
243
+    }
244
+  })
245
+}
246
+const  questionData=ref([])
247
+const prepareQuizData = async (sectionId) => {
248
+
249
+  var url = '/sgsafe/Class/getQuestionBysectionId'
250
+  var param = {
251
+
252
+    params: sectionId
253
+  }
254
+  const response = await proxy.$axios.get(url, param)
255
+  if (response.data.code == '0') {
256
+    questionData.value = response.data.data
257
+    console.log(questionData.value)
258
+
259
+  }
260
+
261
+}
262
+
263
+const edits = (row) => {
264
+  kz.value = true;
265
+  form.value = { ...row };
266
+  router.push({ path: "/checkList",
267
+    query: {
268
+      mark:1,
269
+      data:JSON.stringify(form.value)
270
+    } });
271
+};
272
+// 定义表单数据
273
+const form = ref({
274
+  hdPicId: '',
275
+  hdId: '',
276
+  hdType: '',
277
+  discoveryTime: '',
278
+  hdSubtype: '',
279
+  discoverer: '',
280
+  discovererOther: '',
281
+  hdDescription: '',
282
+  hdLevel: '',
283
+  bz: '',
284
+  hdLocation: '',
285
+  picBefore: '',
286
+
287
+  equipmentId: '',
288
+  hdmanageLevel: '',
289
+  handlingProcesses: '',
290
+  companyId: '',
291
+  repairLeader: '',
292
+  repairOther: '',
293
+  repairSuggest: '',
294
+  repairDdl: '',
295
+  repairDept: '',
296
+  acceptLeader: '',
297
+  acceptOther: '',
298
+  picAfter: '',
299
+  picTemp: '',
300
+  repairDescription: '',
301
+  discovererDept: '',
302
+  discovererDeptCode: '',
303
+  hdLocationCode: '',
304
+  hdLocationName: '',
305
+
306
+  status: '',
307
+
308
+
309
+  hdSelect: '正常登记',
310
+  id: ''
311
+});
312
+const resetForma = () => {
313
+  form.value = {
314
+    hdPicId: '',
315
+    hdId: '',
316
+    hdType: '',
317
+    discoveryTime: '',
318
+    hdSubtype: '',
319
+    discoverer: '',
320
+    discovererOther: '',
321
+    hdDescription: '',
322
+    hdLevel: '',
323
+    bz: '',
324
+    hdLocation: '',
325
+    picBefore: '',
326
+
327
+    equipmentId: '',
328
+    hdmanageLevel: '',
329
+    handlingProcesses: '',
330
+    companyId: '',
331
+    repairLeader: '',
332
+    repairOther: '',
333
+    repairSuggest: '',
334
+    repairDdl: '',
335
+    repairDept: '',
336
+    acceptLeader: '',
337
+    acceptOther: '',
338
+    picAfter: '',
339
+    picTemp: '',
340
+    repairDescription: '',
341
+    status: '',
342
+    discovererDept: '',
343
+    discovererDeptCode: '',
344
+    hdLocationCode: '',
345
+    hdLocationName: '',
346
+
347
+    hdSelect: '正常登记',
348
+    id: ''
349
+  };
350
+};
351
+
352
+const isRefreshing = ref(false);
353
+const isLoading = ref(false);
354
+const isFinished = ref(false);
355
+const currentPage = ref(1);
356
+const pageSize = ref(10);
357
+const totalRows = ref(0);
358
+const resultData = ref([]);
359
+
360
+const dept=localStorage.getItem("dept")[0].deptCode;
361
+const getTableData = async () => {
362
+
363
+  const url = '/sgsafe/Class/querySectionList'
364
+  const param = {
365
+    page: currentPage.value,
366
+    rows: pageSize.value,
367
+    params: rouData.value.id
368
+  };
369
+  const response = await proxy.$axios.get(url, param);
370
+  if (response.data.code === 0) {
371
+    tableData.value = response.data.data.records;
372
+    totalRows.value = response.data.data.total;
373
+  } else {
374
+    showToast({
375
+      type: 'error',
376
+      message: '操作失败!' + response.data.msg
377
+    });
378
+  }
379
+};
380
+const ruleIds = ref([]);
381
+const getRuleId = () => {
382
+  var url = '/sgsafe/ExamHead/getCheckRuleId'
383
+  var param = {}
384
+  proxy.$axios.get(url, param).then(response => {
385
+    if (response.data.code == '0') {
386
+      ruleIds.value = response.data.data
387
+    } else {
388
+      console.log("1111111");
389
+    }
390
+  })
391
+  console.log('ruleIds', ruleIds)
392
+}
393
+
394
+const onRefresh = () => {
395
+  basicReset();
396
+  onLoad();
397
+};
398
+
399
+const onLoad = async () => {
400
+  if (isRefreshing.value) {
401
+    resultData.value = [];
402
+    currentPage.value = 1;
403
+    isRefreshing.value = false;
404
+  }
405
+  try {
406
+    await getTableData();
407
+    await getRuleId()
408
+    if (pageSize.value * currentPage.value < totalRows.value) {
409
+      resultData.value = [...resultData.value, ...tableData.value];
410
+      openStatus.value = new Array(resultData.value.length).fill(true);
411
+      currentPage.value++;
412
+
413
+    } else {
414
+      resultData.value = [...resultData.value, ...tableData.value];
415
+      openStatus.value = new Array(resultData.value.length).fill(true);
416
+      isFinished.value = true;
417
+    }
418
+  } catch (error) {
419
+    console.log(error);
420
+    isFinished.value = true;
421
+  } finally {
422
+    isLoading.value = false;
423
+  }
424
+};
425
+/* 通用方法: 重置list数据 */
426
+const basicReset = () => {
427
+  isFinished.value = false;
428
+  isLoading.value = true;
429
+  currentPage.value = 1;
430
+  resultData.value = [];
431
+};
432
+
433
+/*onMounted(() => {
434
+  handleSearch();
435
+});
436
+
437
+const handleSearch = () => {
438
+/!*  currentPage.value = 1;
439
+  isFinished.value = false;
440
+  tableData.value = [];*!/
441
+  basicReset()
442
+  onLoad()
443
+};*/
444
+
445
+const handdelect = () => {
446
+  query.value.checkName = '';
447
+  onRefresh()
448
+};
449
+
450
+const handleDetailLook = (row) => {
451
+  form.value = { ...row };
452
+  proxy.$router.push({
453
+    name: 'taiZhang_detail',
454
+    query: {
455
+      form: form.value.id
456
+    }
457
+  });
458
+  // dialogVisibleLook.value = true;
459
+};
460
+const deleteData=ref({})
461
+
462
+const handleDelete = (item) => {
463
+  deleteData.value=item
464
+  deleteData.value.cancelFlag='1'
465
+  var url = '/sgsafe/EduCheckMaster/save';
466
+  var param = {
467
+    json: JSON.stringify(item)
468
+  };
469
+  proxy.$axios.post(url, param).then(response => {
470
+    if (response.data.code == '0') {
471
+      showSuccessToast("删除成功")
472
+      onRefresh();
473
+
474
+    } else {
475
+    }
476
+
477
+  })
478
+};
479
+
480
+
481
+
482
+
483
+
484
+
485
+
486
+const resetForm = () => {
487
+  form.value = {
488
+    projectName: '',
489
+    projectLeader: '',
490
+    phone: '',
491
+    dept: ''
492
+  };
493
+};
494
+
495
+
496
+
497
+//处理人员code
498
+const repairLL = ref('');
499
+const repairOO = ref('');
500
+const acceptLL = ref('');
501
+const orJsons = () => {
502
+  // console.log('forms',form.value)
503
+  if (form.value.hdSelect === '正常登记') {
504
+    nextTick(() => {
505
+      nextTick(() => {
506
+        repairLL.value = qq('repairLL', form.value.discoverer);//隐患发现人
507
+        nextTick(() => {
508
+          repairOO.value = qq('repairOO', form.value.discovererOther);//其他隐患发现人
509
+          nextTick(() => {
510
+            acceptLL.value = qq('acceptLL', form.value.discoverer);//隐患销号人
511
+          });
512
+        });
513
+      });
514
+
515
+      // acceptOO.value = qq('acceptOO', form.value.acceptOther)
516
+    });
517
+  } else {
518
+    // console.log('noiajdoifjpoewjfopjp')
519
+    nextTick(() => {
520
+      nextTick(() => {
521
+        repairLL.value = qq('repairLL', form.value.acceptLeader);//隐患发现人
522
+        nextTick(() => {
523
+          repairOO.value = qq('repairOO', form.value.acceptOther);//其他隐患发现人
524
+          nextTick(() => {
525
+            acceptLL.value = qq('acceptLL', form.value.discoverer);//隐患销号人
526
+          });
527
+        });
528
+      });
529
+
530
+      // acceptOO.value = qq('acceptOO', form.value.acceptOther)
531
+    });
532
+  }
533
+};
534
+
535
+const jsons = ref({});
536
+const qq = (a, val) => {
537
+  let aa = '';
538
+  var url = 'sgsafe/Hiddendanger/qqId';
539
+  var param = {
540
+    params: val
541
+  };
542
+  proxy.$axios.post(url, param).then(response => {
543
+    if (response.data.code == 0) {
544
+
545
+      aa = response.data.data;
546
+      switch (a) {
547
+        case 'repairLL':
548
+          repairLL.value = response.data.data;
549
+          // console.log('repairLL',repairLL.value);
550
+          break;
551
+        case 'repairOO':
552
+          repairOO.value = response.data.data;
553
+          // console.log('repairOO',repairLL.value);
554
+          break;
555
+        case 'acceptLL':
556
+          acceptLL.value = response.data.data;
557
+          // console.log('acceptLL',repairLL.value);
558
+          break;
559
+        default:
560
+          break;
561
+      }
562
+      jsons.value = {
563
+        hdConfirm: repairLL.value,
564
+        hdConfirmO: repairOO.value,
565
+        hdCancel: acceptLL.value
566
+      };
567
+
568
+      // 处理函数
569
+      function processValue(value) {
570
+        // 将逗号替换为分号
571
+        const replacedValue = value.replace(/,/g, ';');
572
+        // 分割值
573
+        const parts = replacedValue.split(';');
574
+        // 每个部分前加上 U_
575
+        const processedParts = parts.map(part => `U_${part.trim()}`);
576
+        // 重新组合
577
+        return processedParts.join(';');
578
+      }
579
+
580
+      // 处理整个对象
581
+      const processedData = {};
582
+      for (const key in jsons.value) {
583
+        if (jsons.value.hasOwnProperty(key)) {
584
+          processedData[key] = processValue(jsons.value[key]);
585
+        }
586
+      }
587
+
588
+      console.log('对象', toRaw(processedData));
589
+
590
+      let b = {
591
+        acceptL: processedData.hdConfirm,
592
+        acceptO: processedData.hdConfirmO,
593
+        id: form.value.id
594
+      };
595
+
596
+      if (form.value.hdSelect === '即查即改') {
597
+        b = {
598
+          hdFxr: processedData.hdCancel,
599
+          id: form.value.id
600
+        };
601
+      }
602
+
603
+      if (form.value.hdSelect === '正常登记') {
604
+        b = {
605
+          // hdConfirm: processedData.hdConfirm,
606
+          // hdConfirmO: processedData.hdConfirmO,
607
+          id: form.value.id
608
+        };
609
+      }
610
+
611
+      const aaa = JSON.stringify(toRaw(b));
612
+      sessionStorage.setItem('variables', aaa);
613
+      console.log('aaa', aaa);
614
+    } else {
615
+      showToast({
616
+        type: 'fail',
617
+        message: '操作失败!' + response.data.msg
618
+      });
619
+    }
620
+  });
621
+  return aa;
622
+};
623
+
624
+const reback = () => {
625
+  // 返回逻辑
626
+};
627
+
628
+const deleteRow = (row) => {
629
+  selectedRows.value = [row];
630
+  handleDelete(row);
631
+};
632
+
633
+const deleteRowa = (row) => {
634
+  deleteRow(row);
635
+};
636
+
637
+const bm = (val) => {
638
+  // 部门选择逻辑
639
+};
640
+
641
+//提交审批流程
642
+import { workflowSubmit, workflowCancel } from '@/tools/workflow.js';
643
+
644
+const flowId = ref('');
645
+flowId.value = 'hazardManagementFlowId';
646
+
647
+
648
+const getStatusClass = (status) => {
649
+  switch (status) {
650
+    case '1':
651
+      return 'status-rectifying';
652
+    case '2':
653
+      return 'status-closed';
654
+    default:
655
+      return 'status-analyzing';
656
+  }
657
+};
658
+
659
+/**
660
+ * 按钮实现swipe-cell滑动
661
+ */
662
+const openStatus = ref([])
663
+const swipeCellRefs = ref([])
664
+const getSwipeCellRef = (el, index) => {
665
+  if (el) {
666
+    swipeCellRefs.value[index] = el;
667
+  }
668
+}
669
+const openSwipe = (idx) => {
670
+  openStatus.value = new Array(resultData.value.length).fill(true);
671
+  if (idx >= 0 && idx < swipeCellRefs.value.length) {
672
+    openStatus.value[idx] = false
673
+    swipeCellRefs.value[idx].open('right')
674
+  }
675
+  document.addEventListener('click', handleDocumentClick)
676
+}
677
+/**
678
+ * 当点击滑动单元格时,开始监听点击事件
679
+ */
680
+const handleDocumentClick = (event) => {
681
+  openStatus.value = new Array(resultData.value.length).fill(true);
682
+}
683
+
684
+const closeSwipe = (idx) => {
685
+  if (idx >= 0 && idx < swipeCellRefs.value.length) {
686
+    openStatus.value[idx] = true
687
+    swipeCellRefs.value[idx].close()
688
+  }
689
+}
690
+
691
+</script>
692
+
693
+<style scoped>
694
+.h5-container {
695
+  width: 100%;
696
+  padding: 5px;
697
+  box-sizing: border-box;
698
+}
699
+
700
+.status-pending {
701
+  background-color: #fff3cd;
702
+  color: #856404;
703
+  padding: 2px 4px;
704
+  border-radius: 4px;
705
+}
706
+
707
+.status-registered {
708
+  background-color: #d1ecf1;
709
+  color: #0c5460;
710
+  padding: 2px 4px;
711
+  border-radius: 4px;
712
+}
713
+
714
+.status-analyzing {
715
+  background-color: #fff8e1;
716
+  color: #ff8f00;
717
+  padding: 2px 4px;
718
+  border-radius: 4px;
719
+}
720
+
721
+.status-rectifying {
722
+  background-color: #e8f5e9;
723
+  color: #2e7d32;
724
+  padding: 2px 4px;
725
+  border-radius: 4px;
726
+}
727
+
728
+.status-accepting {
729
+  background-color: #e3f2fd;
730
+  color: #1565c0;
731
+  padding: 2px 4px;
732
+  border-radius: 4px;
733
+}
734
+
735
+.status-closed {
736
+  background-color: #f8bbd0;
737
+  color: #b71c1c;
738
+  padding: 2px 4px;
739
+  border-radius: 4px;
740
+}
741
+
742
+.status-finished {
743
+  background-color: #e8eaf6;
744
+  color: #311b92;
745
+  padding: 2px 4px;
746
+  border-radius: 4px;
747
+}
748
+
749
+.status-unknown {
750
+  background-color: #efebe9;
751
+  color: #424242;
752
+  padding: 2px 4px;
753
+  border-radius: 4px;
754
+}
755
+
756
+.cell-title {
757
+  display: -webkit-box;
758
+  /* 旧版弹性盒子模型 */
759
+  -webkit-box-orient: vertical;
760
+  /* 内容垂直排列 */
761
+  -webkit-line-clamp: 2;
762
+  /* 限制显示行数 */
763
+  overflow: hidden;
764
+  /* 超出隐藏 */
765
+  text-overflow: ellipsis;
766
+  /* 省略号 */
767
+  line-height: 1.5;
768
+  /* 可选:设置行高 */
769
+  max-height: calc(1.5em * 2);
770
+  /* 可选:根据行高限制最大高度 */
771
+  font-size: 16px;
772
+  font-weight: bold;
773
+  color: #333;
774
+  /* 字号 */
775
+}
776
+
777
+.swipe-cell-default {
778
+  display: flex;
779
+  background-color: #ffffff;
780
+  justify-content: center;
781
+  align-items: center;
782
+}
783
+
784
+.swipe-cell-default-icon {
785
+  width: 60px;
786
+  display: flex;
787
+  justify-content: center;
788
+}
789
+
790
+.delete-button {
791
+  height: 100%;
792
+  border: none;
793
+  color: #ff0000;
794
+  background-image: url('@/assets/img/del.png');
795
+  background-size: auto 100%;
796
+  background-repeat: no-repeat;
797
+}
798
+
799
+.red-rounded-box-wide {
800
+  width: 80px;           /* 可容纳3个字 */
801
+  height: 36px;          /* 高度适中 */
802
+  border: 2px solid #51e74c;
803
+  border-radius: 18px;   /* 圆角 = 高度 / 2 → 完美胶囊形 */
804
+  display: flex;
805
+  align-items: center;
806
+  justify-content: center;
807
+  font-size: 14px;
808
+  color: #07c160;
809
+  background-color: transparent;
810
+  box-sizing: border-box;
811
+}
812
+
813
+.submit-button {
814
+  height: 100%;
815
+  border: none;
816
+  color: #07c160;
817
+  background-image: url('@/assets/img/sub.png');
818
+  background-size: auto 100%;
819
+  background-repeat: no-repeat;
820
+}
821
+
822
+.subsuccess {
823
+  height: 100%;
824
+  border: none;
825
+  color: #07c160;
826
+  background-image: url('@/assets/img/sub1.png');
827
+  background-size: auto 100%;
828
+  background-repeat: no-repeat;
829
+}
830
+</style>

Загрузка…
Отмена
Сохранить