Kaynağa Gözat

对话界面

lamphua 2 gün önce
ebeveyn
işleme
a43ab30e8b

+ 2
- 8
llm-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/llm/CmcTopicController.java Dosyayı Görüntüle

@@ -2,7 +2,7 @@ package com.ruoyi.web.controller.llm;
2 2
 
3 3
 import java.util.List;
4 4
 import javax.servlet.http.HttpServletResponse;
5
-import org.springframework.security.access.prepost.PreAuthorize;
5
+
6 6
 import org.springframework.beans.factory.annotation.Autowired;
7 7
 import org.springframework.web.bind.annotation.GetMapping;
8 8
 import org.springframework.web.bind.annotation.PostMapping;
@@ -37,7 +37,6 @@ public class CmcTopicController extends BaseController
37 37
     /**
38 38
      * 查询cmc聊天主题列表
39 39
      */
40
-    @PreAuthorize("@ss.hasPermi('llm:topic:list')")
41 40
     @GetMapping("/list")
42 41
     public TableDataInfo list(CmcTopic cmcTopic)
43 42
     {
@@ -49,7 +48,6 @@ public class CmcTopicController extends BaseController
49 48
     /**
50 49
      * 导出cmc聊天主题列表
51 50
      */
52
-    @PreAuthorize("@ss.hasPermi('llm:topic:export')")
53 51
     @Log(title = "cmc聊天主题", businessType = BusinessType.EXPORT)
54 52
     @PostMapping("/export")
55 53
     public void export(HttpServletResponse response, CmcTopic cmcTopic)
@@ -62,7 +60,6 @@ public class CmcTopicController extends BaseController
62 60
     /**
63 61
      * 获取cmc聊天主题详细信息
64 62
      */
65
-    @PreAuthorize("@ss.hasPermi('llm:topic:query')")
66 63
     @GetMapping(value = "/{topicId}")
67 64
     public AjaxResult getInfo(@PathVariable("topicId") String topicId)
68 65
     {
@@ -72,7 +69,6 @@ public class CmcTopicController extends BaseController
72 69
     /**
73 70
      * 新增cmc聊天主题
74 71
      */
75
-    @PreAuthorize("@ss.hasPermi('llm:topic:add')")
76 72
     @Log(title = "cmc聊天主题", businessType = BusinessType.INSERT)
77 73
     @PostMapping
78 74
     public AjaxResult add(@RequestBody CmcTopic cmcTopic)
@@ -83,7 +79,6 @@ public class CmcTopicController extends BaseController
83 79
     /**
84 80
      * 修改cmc聊天主题
85 81
      */
86
-    @PreAuthorize("@ss.hasPermi('llm:topic:edit')")
87 82
     @Log(title = "cmc聊天主题", businessType = BusinessType.UPDATE)
88 83
     @PutMapping
89 84
     public AjaxResult edit(@RequestBody CmcTopic cmcTopic)
@@ -94,11 +89,10 @@ public class CmcTopicController extends BaseController
94 89
     /**
95 90
      * 删除cmc聊天主题
96 91
      */
97
-    @PreAuthorize("@ss.hasPermi('llm:topic:remove')")
98 92
     @Log(title = "cmc聊天主题", businessType = BusinessType.DELETE)
99 93
 	@DeleteMapping("/{topicIds}")
100 94
     public AjaxResult remove(@PathVariable String[] topicIds)
101 95
     {
102
-        return toAjax(cmcTopicService.deleteCmcTopicByTopicIds(topicIds));
96
+        return success(cmcTopicService.deleteCmcTopicByTopicIds(topicIds));
103 97
     }
104 98
 }

+ 44
- 0
llm-back/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java Dosyayı Görüntüle

@@ -8,6 +8,7 @@ import java.time.LocalDateTime;
8 8
 import java.time.LocalTime;
9 9
 import java.time.ZoneId;
10 10
 import java.time.ZonedDateTime;
11
+import java.util.Calendar;
11 12
 import java.util.Date;
12 13
 import org.apache.commons.lang3.time.DateFormatUtils;
13 14
 
@@ -188,4 +189,47 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
188 189
         ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
189 190
         return Date.from(zdt.toInstant());
190 191
     }
192
+
193
+    /**
194
+     * 判断日期为今天、昨天、前天、7天内、30天内,其余按yyyy-MM格式返回
195
+     */
196
+    public static String getRelativeDate(Date inputDate) {
197
+        if (inputDate == null) {
198
+            return "";
199
+        }
200
+
201
+        // 获取当前日期和时间
202
+        Calendar today = Calendar.getInstance();
203
+        today.set(Calendar.HOUR_OF_DAY, 0);
204
+        today.set(Calendar.MINUTE, 0);
205
+        today.set(Calendar.SECOND, 0);
206
+        today.set(Calendar.MILLISECOND, 0);
207
+
208
+        // 处理输入日期
209
+        Calendar inputCal = Calendar.getInstance();
210
+        inputCal.setTime(inputDate);
211
+        inputCal.set(Calendar.HOUR_OF_DAY, 0);
212
+        inputCal.set(Calendar.MINUTE, 0);
213
+        inputCal.set(Calendar.SECOND, 0);
214
+        inputCal.set(Calendar.MILLISECOND, 0);
215
+
216
+        long diffInMillis = today.getTimeInMillis() - inputCal.getTimeInMillis();
217
+        long diffInDays = diffInMillis / (24 * 60 * 60 * 1000);
218
+
219
+        if (diffInDays == 0) {
220
+            return "今天";
221
+        } else if (diffInDays == 1) {
222
+            return "昨天";
223
+        } else if (diffInDays == 2) {
224
+            return "前天";
225
+        } else if (diffInDays > 2 && diffInDays <= 7) {
226
+            return "7天内";
227
+        } else if (diffInDays > 7 && diffInDays <= 30) {
228
+            return "30天内";
229
+        } else {
230
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
231
+            return sdf.format(inputDate);
232
+        }
233
+    }
234
+
191 235
 }

+ 1
- 1
llm-back/ruoyi-system/src/main/resources/mapper/llm/CmcChatMapper.xml Dosyayı Görüntüle

@@ -2,7 +2,7 @@
2 2
 <!DOCTYPE mapper
3 3
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4 4
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5
-<mapper namespace="com.ruoyi.system.mapper.CmcChatMapper">
5
+<mapper namespace="com.ruoyi.llm.mapper.CmcChatMapper">
6 6
     
7 7
     <resultMap type="CmcChat" id="CmcChatResult">
8 8
         <result property="chatId"    column="chat_id"    />

+ 1
- 1
llm-back/ruoyi-system/src/main/resources/mapper/llm/CmcDocumentMapper.xml Dosyayı Görüntüle

@@ -2,7 +2,7 @@
2 2
 <!DOCTYPE mapper
3 3
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4 4
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5
-<mapper namespace="com.ruoyi.system.mapper.CmcDocumentMapper">
5
+<mapper namespace="com.ruoyi.llm.mapper.CmcDocumentMapper">
6 6
     
7 7
     <resultMap type="CmcDocument" id="CmcDocumentResult">
8 8
         <result property="documentId"    column="document_id"    />

+ 2
- 1
llm-back/ruoyi-system/src/main/resources/mapper/llm/CmcTopicMapper.xml Dosyayı Görüntüle

@@ -2,7 +2,7 @@
2 2
 <!DOCTYPE mapper
3 3
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4 4
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5
-<mapper namespace="com.ruoyi.system.mapper.CmcTopicMapper">
5
+<mapper namespace="com.ruoyi.llm.mapper.CmcTopicMapper">
6 6
     
7 7
     <resultMap type="CmcTopic" id="CmcTopicResult">
8 8
         <result property="topicId"    column="topic_id"    />
@@ -19,6 +19,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
19 19
         <where>  
20 20
             <if test="topic != null  and topic != ''"> and topic = #{topic}</if>
21 21
         </where>
22
+        order by create_time desc
22 23
     </select>
23 24
     
24 25
     <select id="selectCmcTopicByTopicId" parameterType="String" resultMap="CmcTopicResult">

+ 1948
- 0
llm-back/sql/cmc_llm.sql
Dosya farkı çok büyük olduğundan ihmal edildi
Dosyayı Görüntüle


+ 5
- 5
llm-ui/src/api/llm/document.js Dosyayı Görüntüle

@@ -3,7 +3,7 @@ import request from '@/utils/request'
3 3
 // 查询cmc聊天附件列表
4 4
 export function listDocument(query) {
5 5
   return request({
6
-    url: '/system/document/list',
6
+    url: '/llm/document/list',
7 7
     method: 'get',
8 8
     params: query
9 9
   })
@@ -12,7 +12,7 @@ export function listDocument(query) {
12 12
 // 查询cmc聊天附件详细
13 13
 export function getDocument(documentId) {
14 14
   return request({
15
-    url: '/system/document/' + documentId,
15
+    url: '/llm/document/' + documentId,
16 16
     method: 'get'
17 17
   })
18 18
 }
@@ -20,7 +20,7 @@ export function getDocument(documentId) {
20 20
 // 新增cmc聊天附件
21 21
 export function addDocument(data) {
22 22
   return request({
23
-    url: '/system/document',
23
+    url: '/llm/document',
24 24
     method: 'post',
25 25
     data: data
26 26
   })
@@ -29,7 +29,7 @@ export function addDocument(data) {
29 29
 // 修改cmc聊天附件
30 30
 export function updateDocument(data) {
31 31
   return request({
32
-    url: '/system/document',
32
+    url: '/llm/document',
33 33
     method: 'put',
34 34
     data: data
35 35
   })
@@ -38,7 +38,7 @@ export function updateDocument(data) {
38 38
 // 删除cmc聊天附件
39 39
 export function delDocument(documentId) {
40 40
   return request({
41
-    url: '/system/document/' + documentId,
41
+    url: '/llm/document/' + documentId,
42 42
     method: 'delete'
43 43
   })
44 44
 }

+ 5
- 5
llm-ui/src/api/llm/topic.js Dosyayı Görüntüle

@@ -3,7 +3,7 @@ import request from '@/utils/request'
3 3
 // 查询cmc聊天主题列表
4 4
 export function listTopic(query) {
5 5
   return request({
6
-    url: '/system/topic/list',
6
+    url: '/llm/topic/list',
7 7
     method: 'get',
8 8
     params: query
9 9
   })
@@ -12,7 +12,7 @@ export function listTopic(query) {
12 12
 // 查询cmc聊天主题详细
13 13
 export function getTopic(topicId) {
14 14
   return request({
15
-    url: '/system/topic/' + topicId,
15
+    url: '/llm/topic/' + topicId,
16 16
     method: 'get'
17 17
   })
18 18
 }
@@ -20,7 +20,7 @@ export function getTopic(topicId) {
20 20
 // 新增cmc聊天主题
21 21
 export function addTopic(data) {
22 22
   return request({
23
-    url: '/system/topic',
23
+    url: '/llm/topic',
24 24
     method: 'post',
25 25
     data: data
26 26
   })
@@ -29,7 +29,7 @@ export function addTopic(data) {
29 29
 // 修改cmc聊天主题
30 30
 export function updateTopic(data) {
31 31
   return request({
32
-    url: '/system/topic',
32
+    url: '/llm/topic',
33 33
     method: 'put',
34 34
     data: data
35 35
   })
@@ -38,7 +38,7 @@ export function updateTopic(data) {
38 38
 // 删除cmc聊天主题
39 39
 export function delTopic(topicId) {
40 40
   return request({
41
-    url: '/system/topic/' + topicId,
41
+    url: '/llm/topic/' + topicId,
42 42
     method: 'delete'
43 43
   })
44 44
 }

+ 30
- 3
llm-ui/src/router/index.js Dosyayı Görüntüle

@@ -1,3 +1,9 @@
1
+/*
2
+ * @Author: wrh
3
+ * @Date: 2025-04-07 12:47:46
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2025-06-11 11:21:06
6
+ */
1 7
 import { createWebHistory, createRouter } from 'vue-router'
2 8
 /* Layout */
3 9
 import Layout from '@/layout'
@@ -85,9 +91,30 @@ export const constantRoutes = [
85 91
     ]
86 92
   },
87 93
   {
88
-    path: '/chat',
89
-    component: () => import('@/views/chat'),
90
-    hidden: true
94
+    path: '/llm/chat',
95
+    component: Layout,
96
+    hidden: true,
97
+    children: [
98
+      {
99
+        path: '',
100
+        component: () => import('@/views/llm/chat/index'),
101
+        name: 'Chat',
102
+        meta: { title: '对话', icon: '' }
103
+      }
104
+    ]
105
+  },
106
+  {
107
+    path: '/llm/knowledge',
108
+    component: Layout,
109
+    hidden: true,
110
+    children: [
111
+      {
112
+        path: '',
113
+        component: () => import('@/views/llm/knowledge/index'),
114
+        name: 'Knowledge',
115
+        meta: { title: '知识库', icon: '' }
116
+      }
117
+    ]
91 118
   }
92 119
 ]
93 120
 

+ 0
- 185
llm-ui/src/views/chat.vue Dosyayı Görüntüle

@@ -1,185 +0,0 @@
1
-<!--
2
- * @Author: wrh
3
- * @Date: 2025-04-07 14:14:05
4
- * @LastEditors: wrh
5
- * @LastEditTime: 2025-04-07 18:00:58
6
--->
7
-<template>
8
-  <!-- 最外层页面于窗口同宽,使聊天面板居中 -->
9
-  <div class="home-view">
10
-    <!-- 整个聊天面板 -->
11
-    <div class="chat-panel">
12
-      <!-- 左侧的会话列表 -->
13
-      <div class="session-panel">
14
-        <div class="title">办公助手</div>
15
-        <div class="description">构建你的AI助手</div>
16
-      </div>
17
-      <!-- 右侧的消息记录 -->
18
-
19
-      <div class="message-panel">
20
-        <div class="navbar">
21
-          <div class="right-menu">
22
-            <div class="avatar-container">
23
-              <span class="right-menu-item">当前用户:{{ userStore.name }}</span>
24
-              <el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
25
-                <div class="avatar-wrapper">
26
-                  <img :src="userStore.avatar" class="user-avatar" />
27
-                  <el-icon><caret-bottom /></el-icon>
28
-                </div>
29
-                <template #dropdown>
30
-                  <el-dropdown-menu>
31
-                    <router-link to="/user/profile">
32
-                      <el-dropdown-item>个人中心</el-dropdown-item>
33
-                    </router-link>
34
-                    <el-dropdown-item divided command="logout">
35
-                      <span>退出登录</span>
36
-                    </el-dropdown-item>
37
-                  </el-dropdown-menu>
38
-                </template>
39
-              </el-dropdown>
40
-            </div>
41
-          </div>
42
-        </div>
43
-      </div>
44
-    </div>
45
-  </div>
46
-</template>
47
-
48
-<script setup name=''>
49
-import { reactive, toRefs, onBeforeMount, onMounted } from 'vue'
50
-import useUserStore from '@/store/modules/user'
51
-import useSettingsStore from '@/store/modules/settings'
52
-const userStore = useUserStore()
53
-const settingsStore = useSettingsStore()
54
-function handleCommand(command) {
55
-  switch (command) {
56
-    case "logout":
57
-      logout();
58
-      break;
59
-    default:
60
-      break;
61
-  }
62
-}
63
-
64
-function logout() {
65
-  ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
66
-    confirmButtonText: '确定',
67
-    cancelButtonText: '取消',
68
-    type: 'warning'
69
-  }).then(() => {
70
-    userStore.logOut().then(() => {
71
-      location.href = '/index';
72
-    })
73
-  }).catch(() => { });
74
-}
75
-
76
-</script>
77
-<style lang="scss" scoped>
78
-.home-view {
79
-  display: flex;
80
-  /* 与窗口同宽 */
81
-  width: 100vw;
82
-  /* 水平方向上剧中 */
83
-  justify-content: center;
84
-
85
-  .chat-panel {
86
-    /* 聊天面板flex布局,让会话列表和聊天记录左右展示 */
87
-    display: flex;
88
-    /* 让聊天面板圆润一些 */
89
-    border-radius: 20px;
90
-    background-color: white;
91
-    /* 给一些阴影 */
92
-    box-shadow: 0 0 20px 20px rgba(black, 0.05);
93
-    /* 与上方增加一些间距 */
94
-    margin-top: 70px;
95
-
96
-    /* 左侧聊天会话面板*/
97
-    .session-panel {
98
-      background-color: rgb(231, 248, 255);
99
-      width: 300px;
100
-      border-top-left-radius: 20px;
101
-      border-bottom-left-radius: 20px;
102
-      padding: 20px;
103
-      position: relative;
104
-      border-right: 1px solid rgba(black, 0.07);
105
-
106
-      /* 标题*/
107
-      .title {
108
-        margin-top: 20px;
109
-        font-size: 20px;
110
-      }
111
-
112
-      /* 描述*/
113
-      .description {
114
-        color: rgba(black, 0.7);
115
-        font-size: 10px;
116
-        margin-top: 10px;
117
-      }
118
-    }
119
-
120
-    /* 右侧消息记录面板*/
121
-    .message-panel {
122
-      width: 700px;
123
-      height: 800px;
124
-    }
125
-  }
126
-
127
-}
128
-
129
-.navbar {
130
-  height: 50px;
131
-  overflow: hidden;
132
-  position: relative;
133
-  background: var(--navbar-bg);
134
-  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
135
-
136
-  .right-menu {
137
-    float: right;
138
-    height: 100%;
139
-    line-height: 50px;
140
-    display: flex;
141
-
142
-    .right-menu-item {
143
-      display: inline-block;
144
-      padding: 0 8px;
145
-      height: 100%;
146
-      font-size: 14px;
147
-      color: var(--navbar-text);
148
-      vertical-align: text-bottom;
149
-
150
-      &.hover-effect {
151
-        cursor: pointer;
152
-        transition: background 0.3s;
153
-
154
-        &:hover {
155
-          background: rgba(0, 0, 0, 0.025);
156
-        }
157
-      }
158
-
159
-      .avatar-container {
160
-        margin-right: 30px;
161
-
162
-        .avatar-wrapper {
163
-          margin-top: 5px;
164
-          position: relative;
165
-
166
-          .user-avatar {
167
-            cursor: pointer;
168
-            width: 40px;
169
-            height: 40px;
170
-            border-radius: 10px;
171
-          }
172
-
173
-          i {
174
-            cursor: pointer;
175
-            position: absolute;
176
-            right: -20px;
177
-            top: 25px;
178
-            font-size: 12px;
179
-          }
180
-        }
181
-      }
182
-    }
183
-  }
184
-}
185
-</style>

+ 349
- 0
llm-ui/src/views/llm/chat/index.vue Dosyayı Görüntüle

@@ -0,0 +1,349 @@
1
+<!--
2
+ * @Author: wrh
3
+ * @Date: 2025-04-07 14:14:05
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2025-06-12 17:01:55
6
+-->
7
+<template>
8
+  <div class="app-container">
9
+    <!-- 最外层页面于窗口同宽,使聊天面板居中 -->
10
+    <div class="home-view">
11
+      <!-- 整个聊天面板 -->
12
+      <div class="chat-panel">
13
+        <!-- 左侧的会话列表 -->
14
+        <div class="session-panel">
15
+          <el-row :gutter="20">
16
+            <el-col :span="12" :offset="3">
17
+              <div class="top-plus-button">
18
+                <el-button :icon="Plus" type="primary">开启新对话</el-button>
19
+              </div>
20
+            </el-col>
21
+          </el-row>
22
+          <el-divider />
23
+          <el-scrollbar max-height="800px">
24
+            <div id="recent-containers">
25
+              <div v-if="classifiedRecent.today.length > 0">
26
+                <div class="category-title">今天</div>
27
+                <div v-for="item in classifiedRecent.today" :key="item.topicId"
28
+                  class="record">
29
+                  <span @click="enableEdit(item)" v-if="!item.isEditing">{{ item.topic }}</span>
30
+                  <el-input v-else v-model="item.topic" @blur="disableEdit(item)"
31
+                    @keyup.enter="disableEdit(item)" />
32
+                  <el-button class="right-delete-button" :icon="Delete" style="border: none;"></el-button>
33
+                </div>
34
+              </div>
35
+              <div class="spacer" v-if="classifiedRecent.today.length > 0"></div>
36
+
37
+              <div v-if="classifiedRecent.yesterday.length > 0">
38
+                <div class="category-title">昨天</div>
39
+                <div v-for="item in classifiedRecent.yesterday" :key="item.topicId"
40
+                  class="record">
41
+                  <span @click="enableEdit(item)" v-if="!item.isEditing">{{ item.topic }}</span>
42
+                  <el-input v-else v-model="item.topic" @blur="disableEdit(item)"
43
+                    @keyup.enter="disableEdit(item)" />
44
+                  <el-button class="right-delete-button" :icon="Delete" style="border: none;"></el-button>
45
+                </div>
46
+              </div>
47
+              <div class="spacer" v-if="classifiedRecent.yesterday.length > 0"></div>
48
+
49
+              <div v-if="classifiedRecent.dayBeforeYesterday.length > 0">
50
+                <div class="category-title">前天</div>
51
+                <div  v-for="item in classifiedRecent.dayBeforeYesterday" :key="item.topicId"
52
+                  class="record">
53
+                  <span @click="enableEdit(item)" v-if="!item.isEditing">{{ item.topic }}</span>
54
+                  <el-input v-else v-model="item.topic" @blur="disableEdit(item)"
55
+                    @keyup.enter="disableEdit(item)" />
56
+                  <el-button class="right-delete-button" :icon="Delete" style="border: none;"></el-button>
57
+                </div>
58
+              </div>
59
+              <div class="spacer" v-if="classifiedRecent.dayBeforeYesterday.length > 0"></div>
60
+
61
+              <div v-if="classifiedRecent.within7Days.length > 0">
62
+                <div class="category-title">7天内</div>
63
+                <div  v-for="item in classifiedRecent.within7Days" :key="item.topicId"
64
+                  class="record">
65
+                  <span @click="enableEdit(item)" v-if="!item.isEditing">{{ item.topic }}</span>
66
+                  <el-input v-else v-model="item.topic" @blur="disableEdit(item)"
67
+                    @keyup.enter="disableEdit(item)" />
68
+                  <el-button class="right-delete-button" :icon="Delete" style="border: none;"></el-button>
69
+                </div>
70
+              </div>
71
+              <div class="spacer" v-if="classifiedRecent.within7Days.length > 0"></div>
72
+
73
+              <div v-if="classifiedRecent.within30Days.length > 0">
74
+                <div class="category-title">30天内</div>
75
+                <div  v-for="item in classifiedRecent.within30Days" :key="item.topicId"
76
+                  class="record">
77
+                  <span @click="enableEdit(item)" v-if="!item.isEditing">{{ item.topic }}</span>
78
+                  <el-input v-else v-model="item.topic" @blur="disableEdit(item)"
79
+                    @keyup.enter="disableEdit(item)" />
80
+                  <el-button class="right-delete-button" :icon="Delete" style="border: none;"></el-button>
81
+                </div>
82
+              </div>
83
+              <div class="spacer" v-if="classifiedRecent.within30Days.length > 0"></div>
84
+            </div>
85
+
86
+            <!-- 按月记录容器 -->
87
+            <div v-if="sortedMonthlyKeys.length > 0">
88
+              <div v-for="monthKey in sortedMonthlyKeys" :key="monthKey">
89
+                <div class="category-title">{{ formatMonth(monthKey) }}</div>
90
+                <div v-for="item in classifiedMonthly[monthKey]" :key="item.topicId"
91
+                  class="record">
92
+                  <span @click="enableEdit(item)" v-if="!item.isEditing">{{ item.topic }}</span>
93
+                  <el-input v-else v-model="item.topic" @blur="disableEdit(item)"
94
+                    @keyup.enter="disableEdit(item)" />
95
+                  <el-button class="right-delete-button" :icon="Delete" style="border: none;"></el-button>
96
+                </div>
97
+              </div>
98
+              <div class="spacer"></div>
99
+            </div>
100
+
101
+            <!-- 空状态 -->
102
+            <div v-if="!hasRecords" class="empty">
103
+              暂无记录
104
+            </div>
105
+          </el-scrollbar>
106
+        </div>
107
+        <!-- 右侧的消息记录 -->
108
+
109
+        <div class="message-panel">
110
+
111
+        </div>
112
+      </div>
113
+    </div>
114
+  </div>
115
+</template>
116
+
117
+<script setup name=''>
118
+import { reactive, toRefs, onBeforeMount, onMounted } from 'vue'
119
+import { Plus, Delete } from '@element-plus/icons-vue'
120
+import { listTopic, getTopic, delTopic, addTopic, updateTopic } from "@/api/llm/topic";
121
+const { proxy } = getCurrentInstance();
122
+
123
+const topicList = ref([]);
124
+const open = ref(false);
125
+const loading = ref(true);
126
+const showSearch = ref(true);
127
+const ids = ref([]);
128
+const single = ref(true);
129
+const multiple = ref(true);
130
+const total = ref(0);
131
+const title = ref("");
132
+const hasRecords = ref(false);
133
+const classifiedRecent = ref({
134
+  today: [],
135
+  yesterday: [],
136
+  dayBeforeYesterday: [],
137
+  within7Days: [],
138
+  within30Days: []
139
+});
140
+const classifiedMonthly = ref({});
141
+
142
+const enableEdit = (item) => {
143
+  item.isEditing = true;
144
+};
145
+
146
+const disableEdit = (item) => {
147
+  item.isEditing = false;
148
+};
149
+const data = reactive({
150
+  form: {},
151
+  queryParams: {
152
+    pageNum: 1,
153
+    pageSize: 99999,
154
+    topic: null,
155
+  },
156
+  rules: {
157
+  }
158
+});
159
+
160
+const { queryParams, form, rules } = toRefs(data);
161
+
162
+/** 查询cmc聊天主题列表 */
163
+const getList = async () => {
164
+  try {
165
+    loading.value = true;
166
+    let response = await listTopic(queryParams.value);
167
+    if (response.total > 0) {
168
+      topicList.value = response.rows;
169
+      total.value = response.total;
170
+      loading.value = false;
171
+      hasRecords.value = true;
172
+      // 分类近期记录
173
+
174
+      const today = new Date()
175
+      today.setHours(0, 0, 0, 0)
176
+
177
+      const yesterday = new Date(today)
178
+      yesterday.setDate(yesterday.getDate() - 1)
179
+
180
+      const dayBeforeYesterday = new Date(today)
181
+      dayBeforeYesterday.setDate(dayBeforeYesterday.getDate() - 2)
182
+
183
+      const sevenDaysAgo = new Date(today)
184
+      sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7)
185
+
186
+      const thirtyDaysAgo = new Date(today)
187
+      thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30)
188
+
189
+      topicList.value.forEach(record => {
190
+        const recordDate = new Date(record.createTime)
191
+        recordDate.setHours(0, 0, 0, 0)
192
+
193
+        if (recordDate.getTime() === today.getTime()) {
194
+          classifiedRecent.value.today.push(record)
195
+        } else if (recordDate.getTime() === yesterday.getTime()) {
196
+          classifiedRecent.value.yesterday.push(record)
197
+        } else if (recordDate.getTime() === dayBeforeYesterday.getTime()) {
198
+          classifiedRecent.value.dayBeforeYesterday.push(record)
199
+        } else if (recordDate > sevenDaysAgo) {
200
+          classifiedRecent.value.within7Days.push(record)
201
+        } else if (recordDate > thirtyDaysAgo) {
202
+          classifiedRecent.value.within30Days.push(record)
203
+        } else if (recordDate <= thirtyDaysAgo) {
204
+          const monthKey = getMonthKey(recordDate)
205
+
206
+          if (!classifiedMonthly.value[monthKey]) {
207
+            classifiedMonthly.value[monthKey] = []
208
+          }
209
+
210
+          classifiedMonthly.value[monthKey].push(record)
211
+        }
212
+      })
213
+    }
214
+  } catch (err) {
215
+    console.error('获取数据失败:', err)
216
+  } finally {
217
+    loading.value = false
218
+  }
219
+}
220
+// 获取排序后的月份键
221
+const sortedMonthlyKeys = computed(() => {
222
+  return Object.keys(classifiedMonthly.value).sort((a, b) => new Date(b) - new Date(a))
223
+})
224
+
225
+// 获取月份键 (yyyy-MM)
226
+const getMonthKey = (date) => {
227
+  const year = date.getFullYear()
228
+  const month = (date.getMonth() + 1).toString().padStart(2, '0')
229
+  return `${year}-${month}`
230
+}
231
+
232
+// 格式化月份显示
233
+const formatMonth = (monthKey) => {
234
+  const [year, month] = monthKey.split('-')
235
+  return `${year}-${month}`
236
+}
237
+
238
+// 日期格式化
239
+const formatDate = (date) => {
240
+  const d = new Date(date)
241
+  return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`
242
+}
243
+onMounted(() => {
244
+  getList();
245
+})
246
+</script>
247
+<style lang="scss" scoped>
248
+.home-view {
249
+  display: flex;
250
+  /* 与窗口同宽 */
251
+  width: 100vw;
252
+
253
+  .chat-panel {
254
+    /* 聊天面板flex布局,让会话列表和聊天记录左右展示 */
255
+    display: flex;
256
+    /* 让聊天面板圆润一些 */
257
+    border-radius: 4px;
258
+    background-color: white;
259
+    /* 给一些阴影 */
260
+    box-shadow: 0 0 4px 4px rgba(black, 0.05);
261
+    /* 与上方增加一些间距 */
262
+    margin-top: 4px;
263
+
264
+    /* 左侧聊天会话面板*/
265
+    .session-panel {
266
+      background-color: rgb(249 251 255);
267
+      width: 12vw;
268
+      border-top-left-radius: 4px;
269
+      border-bottom-left-radius: 4px;
270
+      position: relative;
271
+      border-right: 1px solid rgba(black, 0.07);
272
+    }
273
+
274
+    /* 右侧消息记录面板*/
275
+    .message-panel {
276
+      width: 88vw;
277
+      height: 90vh;
278
+    }
279
+  }
280
+}
281
+
282
+.category-title {
283
+  padding: 0 10px;
284
+  font-size: 12px;
285
+  font-weight: bold;
286
+  line-height: 30px;
287
+  color: #333;
288
+  background-color: rgb(249 251 255);
289
+}
290
+
291
+.record {
292
+  z-index: 1;
293
+  white-space: nowrap;
294
+  line-height: 30px;
295
+  color: #4c4c4c;
296
+  background-color: rgb(249 251 255);
297
+  cursor: pointer;
298
+  border-radius: 12px;
299
+  align-items: center;
300
+  padding: 0 10px;
301
+  font-size: 12px;
302
+  display: flex;
303
+  position: relative;
304
+}
305
+
306
+.spacer {
307
+  height: 20px;
308
+  clear: both;
309
+}
310
+
311
+.empty {
312
+  padding: 0 10px;
313
+  color: #909399;
314
+}
315
+
316
+.top-plus-button {
317
+  margin-top: 20px;
318
+}
319
+
320
+.right-delete-button {
321
+  opacity: 0;
322
+  z-index: 2;
323
+  justify-content: left;
324
+  align-items: center;
325
+  width: 12px;
326
+  height: 12px;
327
+  display: flex;
328
+  position: absolute;
329
+  top: 50%;
330
+  right: 0px;
331
+  transform: translateY(-50%);
332
+}
333
+
334
+.right-delete-button:hover {
335
+  opacity: 1;
336
+  z-index: 2;
337
+  background: linear-gradient(90deg, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 1), rgba(255, 255, 255, 1));
338
+  color: rgb(70, 70, 70);
339
+  justify-content: left;
340
+  align-items: center;
341
+  width: 12px;
342
+  height: 12px;
343
+  display: flex;
344
+  position: absolute;
345
+  top: 50%;
346
+  right: 0px;
347
+  transform: translateY(-50%);
348
+}
349
+</style>

+ 3
- 0
llm-ui/src/views/llm/knowledge/index.vue Dosyayı Görüntüle

@@ -0,0 +1,3 @@
1
+<template></template>
2
+<script></script>
3
+<style></style>

Loading…
İptal
Kaydet