瀏覽代碼

Merge branch 'master' of http://192.168.1.222:3000/hnsh-smart-steward/smart-steward-admin

miaofuhao 3 周之前
父節點
當前提交
8b858aa9aa

二進制
apps/web-ele/public/AIimgs.gif


+ 4 - 0
apps/web-ele/src/api/knowledge/category.ts

@@ -13,6 +13,10 @@ export function addKnowledgeCategory(data: any) {
13 13
 export function queryKnowledgeCategory(params: any) {
14 14
   return requestClient.get(Api.queryknowledgeCategory, { params });
15 15
 }
16
+//详情
17
+export function queryKnowledgeCategoryDetail(ids: any) {
18
+  return requestClient.get(`${Api.baseknowledgeCategory}/${ids}`);
19
+}
16 20
 //修改知识库分类
17 21
 export function editKnowledgeCategory(data: any) {
18 22
   return requestClient.put(Api.baseknowledgeCategory, data, {

+ 6 - 0
apps/web-ele/src/api/schedule/index.ts

@@ -5,6 +5,7 @@ enum Api {
5 5
   executorApi = '/system/user/stationList',
6 6
   // queryrgasstation = '/system/user/stationList',
7 7
   queryrtodoaskslist = '/scheduleTasks/tasks/allList',
8
+  setPlanTime = '/scheduleTasks/tasks/setPlanTime',
8 9
 }
9 10
 
10 11
 export function transferTask(data: any) {
@@ -41,6 +42,11 @@ export function queryExecutorList(params: any) {
41 42
 export function queryGasStationList(params: any) {
42 43
   return requestClient.get(Api.executorApi, { params });
43 44
 }
45
+
46
+//修改计划时间
47
+export function setPlanTimeAPI(params: any) {
48
+  return requestClient.get(`${Api.setPlanTime}/${params.taskId}`, {params});
49
+}
44 50
 // //查询知识库查看记录列表
45 51
 // export function queryrecord(params: any) {
46 52
 //   return requestClient.get(Api.queryrecord, params);

+ 37 - 2
apps/web-ele/src/app.vue

@@ -1,17 +1,52 @@
1 1
 <script lang="ts" setup>
2 2
 import { useElementPlusDesignTokens } from '@vben/hooks';
3
-
4 3
 import { ElConfigProvider } from 'element-plus';
5
-
6 4
 import { elementLocale } from '#/locales';
5
+// @ts-ignore
6
+import { useVbenModal } from '@vben/common-ui';
7 7
 
8 8
 defineOptions({ name: 'App' });
9 9
 
10 10
 useElementPlusDesignTokens();
11
+
12
+const [AIModal, AIModalApi] = useVbenModal({
13
+  title: 'AI Assistant',
14
+  showCancelBtn: false,
15
+});
16
+const handleAIModal = () => {
17
+  AIModalApi.open();
18
+};
11 19
 </script>
12 20
 
13 21
 <template>
14 22
   <ElConfigProvider :locale="elementLocale">
15 23
     <RouterView />
24
+    <div class="ai-icon" @click="handleAIModal">
25
+      <img src="/AIimgs.gif" alt="AI Assistant" />
26
+    </div>
27
+    <AIModal />
16 28
   </ElConfigProvider>
17 29
 </template>
30
+
31
+<style scoped lang="scss">
32
+.ai-icon {
33
+  position: fixed;
34
+  bottom: 30px;
35
+  right: 30px;
36
+  z-index: 9999;
37
+  width: 100px;
38
+  height: 100px;
39
+  border-radius: 50%;
40
+  overflow: hidden;
41
+  display: flex;
42
+  align-items: center;
43
+  justify-content: center;
44
+  cursor: pointer;
45
+}
46
+
47
+.ai-icon img {
48
+  width: 100%;
49
+  height: 100%;
50
+  object-fit: contain;
51
+}
52
+</style>

File diff suppressed because it is too large
+ 1232 - 0
apps/web-ele/src/components/knowledgemain/index.vue


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

@@ -111,7 +111,7 @@ const localRoutes: RouteRecordStringComponent[] = [
111 111
       hideInMenu: true,
112 112
     },
113 113
     name: 'KnowledgeDetail',
114
-    path: '/knowledge/detail/:classId/:classname/:classicon',
114
+    path: '/knowledge/detail/:classId',
115 115
   },
116 116
   {
117 117
     component: '/knowledge/edit/index',

+ 24 - 1
apps/web-ele/src/views/examManage/questionBank/cpn/questionEdit/index.vue

@@ -1,6 +1,8 @@
1 1
 <script setup lang="ts">
2 2
 import { onMounted, ref } from 'vue';
3 3
 import { useRoute } from 'vue-router';
4
+// @ts-ignore
5
+import { useVbenModal } from '@vben/common-ui';
4 6
 
5 7
 // @ts-ignore
6 8
 import { useTabs } from '@vben/hooks';
@@ -15,6 +17,7 @@ import {
15 17
 } from '#/api/exam/question/question';
16 18
 // @ts-ignore
17 19
 import TinymceEditor from '#/components/tinymce/src/editor.vue';
20
+import knowledgemain from '#/components/knowledgemain/index.vue';
18 21
 // @ts-ignore
19 22
 const { closeCurrentTab } = useTabs();
20 23
 
@@ -262,6 +265,18 @@ onMounted(() => {
262 265
   loadQuestionData();
263 266
   fetchCategories();
264 267
 });
268
+
269
+const [quoteModal, quoteModalApi] = useVbenModal({
270
+  title: '引用试题',
271
+  showCancelBtn: false,
272
+  class: 'quote-modal',
273
+  // onConfirm:()=>{
274
+  // }
275
+});
276
+
277
+const handleQuote = () => {
278
+  quoteModalApi.open();
279
+};
265 280
 </script>
266 281
 
267 282
 <template>
@@ -511,7 +526,9 @@ onMounted(() => {
511 526
           </h3>
512 527
           <div class="mb-2 flex items-center gap-2">
513 528
             <!--TODO: 引用功能未实现-->
514
-            <el-button type="default" plain size="small"> 引用 </el-button>
529
+            <el-button type="default" plain size="small" @click="handleQuote">
530
+              引用
531
+            </el-button>
515 532
           </div>
516 533
           <TinymceEditor
517 534
             v-model="formData.analysis"
@@ -531,6 +548,9 @@ onMounted(() => {
531 548
         </div>
532 549
       </div>
533 550
     </div>
551
+    <quoteModal>
552
+      <knowledgemain :classId="id" />
553
+    </quoteModal>
534 554
   </div>
535 555
 </template>
536 556
 
@@ -557,4 +577,7 @@ onMounted(() => {
557 577
 :deep(.el-textarea__inner:focus) {
558 578
   box-shadow: none;
559 579
 }
580
+:global(.quote-modal) {
581
+  min-width: 70% !important;
582
+}
560 583
 </style>

+ 33 - 21
apps/web-ele/src/views/knowledge/detail/index.vue

@@ -44,6 +44,7 @@ import {
44 44
   getKnowledgeContentDetail,
45 45
   queryKnowledgeContentlist,
46 46
 } from '#/api/knowledge/content';
47
+import { queryKnowledgeCategoryDetail } from '#/api/knowledge/category';
47 48
 import {
48 49
   addrecord,
49 50
   queryPublishRecordlist,
@@ -60,9 +61,7 @@ const userId = userStore.userInfo?.userId;
60 61
 // 路由实例
61 62
 const router = useRouter();
62 63
 const route = useRoute();
63
-const { classId, classname, classicon } = route.params;
64
-console.log(route.params);
65
-
64
+const { classId } = route.params;
66 65
 // 菜单折叠状态
67 66
 const isCollapse = ref(false);
68 67
 
@@ -78,6 +77,7 @@ const commentsloading = ref(true);
78 77
 const centerloading = ref(false);
79 78
 const isEdit = ref(false);
80 79
 const ChapterId = ref(null);
80
+const KnowledgeDetails = ref({}) as any;
81 81
 // 查询章节数据
82 82
 const chapterDatafn = async () => {
83 83
   chapterDataloading.value = true;
@@ -85,7 +85,6 @@ const chapterDatafn = async () => {
85 85
     const res = await queryKnowledgeContentlist(classId);
86 86
     if (res.code === 200) {
87 87
       console.log('章节数据', res.data);
88
-
89 88
       chapterData.value = res.data || [];
90 89
       handleChapterClick({ id: chapterData.value[0]?.id });
91 90
     }
@@ -134,7 +133,18 @@ const queryrecordfn = async () => {
134 133
     commentsloading.value = false;
135 134
   }
136 135
 };
136
+// 查询知识分类详情
137
+const queryKnowledgeCategoryDetailfn = async () => {
138
+  try {
139
+    const res = await queryKnowledgeCategoryDetail(classId);
140
+    KnowledgeDetails.value = res;
141
+  } catch (error) {
142
+    console.log(error);
143
+  }
144
+};
145
+
137 146
 onMounted(() => {
147
+  queryKnowledgeCategoryDetailfn();
138 148
   chapterDatafn();
139 149
   querycommentfn();
140 150
   addrecordfn();
@@ -169,7 +179,7 @@ const toggleCollapse = () => {
169 179
 // 提醒弹框
170 180
 const notificationDialogVisible = ref(false);
171 181
 const notificationContent = ref('');
172
-const notificationRecipients = ref([]);
182
+const notificationRecipients = ref([]) as any;
173 183
 
174 184
 // 打开提醒弹框
175 185
 const openNotificationDialog = () => {
@@ -237,7 +247,7 @@ const formatTime = (d: Date) =>
237 247
   d.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
238 248
 const computedEditHistoryData = computed(() => {
239 249
   const dayMap = new Map<string, any[]>();
240
-  editHistoryData.value.forEach((item:any) => {
250
+  editHistoryData.value.forEach((item: any) => {
241 251
     const day = formatDayOffset(item.createTime);
242 252
     if (!dayMap.has(day)) dayMap.set(day, []);
243 253
     dayMap.get(day)!.push({
@@ -258,7 +268,7 @@ const queryPublishRecordlistfn = async () => {
258 268
   try {
259 269
     const res = await queryPublishRecordlist({
260 270
       categoryId: classId,
261
-      contentId:ChapterId.value
271
+      contentId: ChapterId.value,
262 272
     });
263 273
     editHistoryData.value = res?.rows || [];
264 274
   } catch (error) {
@@ -299,7 +309,7 @@ const selectHistoryRecord = (record: any) => {
299 309
 };
300 310
 
301 311
 // 点击章节
302
-const handleChapterClick = async (node:any) => {
312
+const handleChapterClick = async (node: any) => {
303 313
   centerloading.value = true;
304 314
   ChapterId.value = node.id;
305 315
   if (!isEdit.value) isEdit.value = true;
@@ -338,7 +348,7 @@ const addEditDialogVisible = ref(false);
338 348
 const addiseditTitle = ref('');
339 349
 const editvaloraddId = ref({});
340 350
 // 添加章节
341
-const addChapter = (data) => {
351
+const addChapter = (data: any) => {
342 352
   addEditDialogVisible.value = true;
343 353
   addiseditTitle.value = '新增章节';
344 354
   editvaloraddId.value = {
@@ -348,14 +358,14 @@ const addChapter = (data) => {
348 358
   };
349 359
 };
350 360
 // 编辑章节
351
-const editChapter = (node) => {
361
+const editChapter = (node: any) => {
352 362
   addEditDialogVisible.value = true;
353 363
   addiseditTitle.value = '编辑章节';
354 364
   editvaloraddId.value = node;
355 365
 };
356 366
 
357 367
 // 删除章节
358
-const deleteChapter = async (node) => {
368
+const deleteChapter = async (node: any) => {
359 369
   try {
360 370
     await deleteKnowledgeContent(node.id);
361 371
     await chapterDatafn();
@@ -374,7 +384,9 @@ const submitComment = async () => {
374 384
     await addComment({
375 385
       targetId: Number(classId),
376 386
       targetType: 'knowledge',
377
-      attachments: commentImages.value.map((item) => item.fileName).join(','),
387
+      attachments: commentImages.value
388
+        .map((item: any) => item.fileName)
389
+        .join(','),
378 390
       content: newComment.value,
379 391
     });
380 392
     newComment.value = '';
@@ -446,11 +458,11 @@ const handleUploadSuccess = (file: any) => {
446 458
   }
447 459
 };
448 460
 // 移除上传的图片
449
-const removeImage = (index: number) => {
461
+const removeImage = (index: any) => {
450 462
   commentImages.value = [];
451 463
   uploadRef?.value?.clearFiles();
452 464
 };
453
-const navigateToEdit = (id: number) => {
465
+const navigateToEdit = (id: any) => {
454 466
   router.push(`/knowledge/detail/edit/${id}/${ChapterId.value}`);
455 467
 };
456 468
 const addMenu = () => {
@@ -522,7 +534,7 @@ const isImage = (url: string) => {
522 534
   if (!url) return false;
523 535
   try {
524 536
     const cleanUrl = url.split('?')[0];
525
-    const ext = cleanUrl.split('.').pop()?.toLowerCase() ?? '';
537
+    const ext = cleanUrl?.split('.').pop()?.toLowerCase() ?? '';
526 538
     return imageExt.has(ext);
527 539
   } catch {
528 540
     return false;
@@ -537,7 +549,7 @@ const nonImageList = computed(() => {
537 549
     : selectedChapter.value.attachmentUrl
538 550
       ? [selectedChapter.value.attachmentUrl]
539 551
       : [];
540
-  const result = urls.filter((u) => !isImage(u));
552
+  const result = urls.filter((u: any) => !isImage(u));
541 553
   return result;
542 554
 });
543 555
 </script>
@@ -597,16 +609,16 @@ const nonImageList = computed(() => {
597 609
             <div class="knowledge-info">
598 610
               <div class="knowledge-icon">
599 611
                 <span
600
-                  v-if="classicon?.includes('iconfont')"
601
-                  :class="classicon?.split(',')[0] || ''"
612
+                  v-if="KnowledgeDetails?.icon?.includes('iconfont')"
613
+                  :class="KnowledgeDetails?.icon?.split(',')[0] || ''"
602 614
                   :style="{
603
-                    color: `#${classicon?.split(',')[1] || ''}`,
615
+                    color: `${KnowledgeDetails?.icon?.split(',')[1] || ''}`,
604 616
                     fontSize: '30px',
605 617
                   }"
606 618
                 ></span>
607
-                <img v-else :src="classicon" alt="" />
619
+                <img v-else :src="KnowledgeDetails?.iconUrl?.[0] || ''" alt="" />
608 620
               </div>
609
-              <div class="knowledge-title">{{ classname }}</div>
621
+              <div class="knowledge-title">{{ KnowledgeDetails?.name }}</div>
610 622
             </div>
611 623
 
612 624
             <!-- 主页按钮 -->

+ 11 - 11
apps/web-ele/src/views/knowledge/type/index.vue

@@ -68,17 +68,17 @@ const selectedCategory = ref<FormData>({
68 68
 
69 69
 // 跳转到详情页
70 70
 const navigateToDetail = (id: number) => {
71
-  const category = categories.value.find((item: any) => item.id === id);
72
-  const cleaned = category?.icon?.replace(/#/g, '');
73
-  // console.log(cleaned);
74
-  if (cleaned.includes('iconfont')) {
75
-    router.push(`/knowledge/detail/${id}/${category.name}/${cleaned}`);
76
-  } else {
77
-    const iconUrl = category?.iconUrl?.[0] || '';
78
-    router.push(
79
-      `/knowledge/detail/${id}/${category.name}/${encodeURIComponent(iconUrl)}`,
80
-    );
81
-  }
71
+  router.push(`/knowledge/detail/${id}`);
72
+  // const category = categories.value.find((item: any) => item.id === id);
73
+  // const cleaned = category?.icon?.replace(/#/g, '');
74
+  // if (cleaned.includes('iconfont')) {
75
+  //   router.push(`/knowledge/detail/${id}/${category.name}/${cleaned}`);
76
+  // } else {
77
+  //   const iconUrl = category?.iconUrl?.[0] || '';
78
+  //   router.push(
79
+  //     `/knowledge/detail/${id}/${category.name}/${encodeURIComponent(iconUrl)}`,
80
+  //   );
81
+  // }
82 82
 };
83 83
 
84 84
 // 打开创建抽屉

+ 56 - 3
apps/web-ele/src/views/schedule/detail/index.vue

@@ -20,7 +20,12 @@ import {
20 20
 import { keyBy } from 'lodash-es';
21 21
 
22 22
 // @ts-ignore
23
-import { closeTask, getTaskDetail, transferTask } from '#/api/schedule';
23
+import {
24
+  closeTask,
25
+  getTaskDetail,
26
+  transferTask,
27
+  setPlanTimeAPI,
28
+} from '#/api/schedule';
24 29
 // @ts-ignore
25 30
 import { confirmDoneTask, handleLicense } from '#/api/task/confirm';
26 31
 // @ts-ignore
@@ -545,6 +550,38 @@ const [PageModal, pageModalApi] = useVbenModal({
545 550
 onMounted(async () => {
546 551
   await init();
547 552
 });
553
+const PlannedTimeValue = ref('') as any;
554
+const [PlannedTimeModal, plannedTimeModalApi] = useVbenModal({
555
+  title: '修改计划时间',
556
+  showCancelBtn: false,
557
+  contentClass:'plannedTimeModalclass',
558
+  onConfirm: async () => {
559
+    if (
560
+      new Date(PlannedTimeValue.value).getTime() <
561
+        new Date(taskInfo.value.startTime).getTime() ||
562
+      new Date(PlannedTimeValue.value).getTime() >
563
+        new Date(taskInfo.value.endTime).getTime()
564
+    ) {
565
+      ElMessage.error('计划时间必须在任务开始时间和结束时间之间');
566
+      return;
567
+    }
568
+    try {
569
+      await setPlanTimeAPI({
570
+        taskId: taskId.value,
571
+        planTime: PlannedTimeValue.value,
572
+      });
573
+      await init();
574
+      ElMessage.success('修改计划时间成功');
575
+      plannedTimeModalApi.close();
576
+    } catch {
577
+      ElMessage.error('修改计划时间失败');
578
+    }
579
+  },
580
+});
581
+const handlePlannedTimeClick = () => {
582
+  PlannedTimeValue.value = taskInfo.value.displayDate;
583
+  plannedTimeModalApi.open();
584
+};
548 585
 </script>
549 586
 
550 587
 <template>
@@ -665,7 +702,9 @@ onMounted(async () => {
665 702
               {{ taskInfo.executorName || '-' }}
666 703
             </ElDescriptionsItem>
667 704
             <ElDescriptionsItem label="计划时间:">
668
-              {{ taskInfo.displayDate || '-' }}
705
+              <el-button type="primary" text @click="handlePlannedTimeClick">
706
+                {{ taskInfo.displayDate || '-' }}
707
+              </el-button>
669 708
             </ElDescriptionsItem>
670 709
             <ElDescriptionsItem label="创建:">
671 710
               {{ taskInfo.createBy || '' }}
@@ -942,7 +981,17 @@ onMounted(async () => {
942 981
         }"
943 982
       />
944 983
     </ExecutorModal>
945
-
984
+    <PlannedTimeModal>
985
+      <div>
986
+        <span>计划时间:</span>
987
+        <el-date-picker
988
+          v-model="PlannedTimeValue"
989
+          type="datetime"
990
+          placeholder="请选择计划时间"
991
+          value-format="YYYY-MM-DD HH:mm:ss"
992
+        />
993
+      </div>
994
+    </PlannedTimeModal>
946 995
     <DealDrawer class="w-3/5" @reload="refreshTab()" />
947 996
     <PageModal>
948 997
       <!-- 应急防护月检 -->
@@ -975,6 +1024,10 @@ onMounted(async () => {
975 1024
   box-shadow: none !important;
976 1025
   // background: transparent !important;
977 1026
 }
1027
+
1028
+:global(.plannedTimeModalclass) {
1029
+  min-height: 0px !important;
1030
+}
978 1031
 .boxdev :deep(.el-card__header) {
979 1032
   padding-top: 18px !important;
980 1033
   padding-bottom: 4px !important;

+ 1 - 1
apps/web-ele/src/views/schedule/view/index.vue

@@ -525,7 +525,7 @@ const createWorkOrderDrawerEvent = () => {
525 525
               <ElOption
526 526
                 v-for="item in gasstationlist"
527 527
                 :key="`${item.userId}-${item.stationId}`"
528
-                :label="`${item.nickName}-${item.stationName}`"
528
+                :label="`${item.nickName}-${item.postName}-${item.stationName}`"
529 529
                 :value="`${item.userId}-${item.stationId}`"
530 530
               />
531 531
             </ElSelect>