ai-assistant-pro 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,307 @@
1
+ <template>
2
+ <div class="chat-contain" ref="chatContainer">
3
+ <div v-for="(list, index) in messageData" :key="index"
4
+ :class="{'user-list': list.type === 'user'}"
5
+ class="message">
6
+ <div v-if="list.type === 'robot'" class="robot">
7
+ <div class="robot-image-div"></div>
8
+ <!-- <img alt="" src="../static/robot.png" style="width: 48px; height: 48px">-->
9
+ </div>
10
+ <div v-else class="robot user">user</div>
11
+ <div :class="{'user-info': list.type === 'user'}" class="robot-message">
12
+ <div v-html="list.message"></div>
13
+ <div v-if="list.type === 'robot' && list.links.length > 0" class="link">
14
+ <div class="link-title">相关链接</div>
15
+ <div class="link-content">
16
+ <div class="links" v-for="item in list.links">
17
+ <a :href="'https://ai-yuliao.hep.com.cn'+item.documentUrl" target="_blank">
18
+ <span>{{ item.title }}</span>
19
+ </a>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ <chat-tools v-if="list.type === 'robot'"
24
+ :chatId="chatId"
25
+ :detail-data="list"
26
+ @on-reanswer="(list) => reanswer(list, index)"
27
+ @on-stop-chat="onStopChat"/>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </template>
32
+ <script>
33
+ import { errorList, cacheMessageList } from '../utils/config';
34
+ import ChatTools from './chat-tools.vue';
35
+ import { sendMessageEventSource } from "../api";
36
+ const controller = new AbortController();
37
+ const signal = controller.signal;
38
+
39
+ export default {
40
+ name: 'Chat',
41
+ components: {
42
+ ChatTools
43
+ },
44
+ props: {
45
+ keyWord: {
46
+ type: String,
47
+ default: '',
48
+ },
49
+ resId: {
50
+ type: String,
51
+ default: '',
52
+ },
53
+ chatId: {
54
+ type: String,
55
+ default: '',
56
+ },
57
+ messageData: {
58
+ type: Array,
59
+ default: cacheMessageList
60
+ }
61
+ },
62
+ data() {
63
+ return {
64
+ popover: false,
65
+ input: "",
66
+ errors: [],
67
+ errorList: errorList,
68
+ cacheKeyWord: '' // 由于外部需要快速清掉 keyword 但是存在重新发送请求的情况,因此需要一个混存记录
69
+ }
70
+ },
71
+ computed: {
72
+ canSubmit() {
73
+ return this.errors.length === 0 && this.input === "";
74
+ },
75
+ },
76
+ watch: {
77
+ keyWord: {
78
+ handler() {
79
+ if (this.keyWord) {
80
+ this.cacheKeyWord = this.keyWord;
81
+ this.postMessage();
82
+ }
83
+ },
84
+ immediate: true
85
+ }
86
+ },
87
+ methods: {
88
+ cancelPopover() {
89
+ this.popover = false;
90
+ },
91
+ openPopover() {
92
+ this.popover = true;
93
+ },
94
+ scrollToBottom() {
95
+ this.$refs.chatContainer.scrollTop = this.$refs.chatContainer.scrollHeight;
96
+ },
97
+ async postMessage(list, commonKey) {
98
+ let reanswerParams = {};
99
+ if (list) {
100
+ reanswerParams = {
101
+ reStatus: 1,
102
+ parentMsgId: list.parentMsgId,
103
+ msgId: list.msgId,
104
+ content: this.messageData[list.index - 1].message,
105
+ }
106
+ this.messageData[list.index].message = '';
107
+ }
108
+ const params = {
109
+ messageList: [
110
+ {
111
+ content: this.keyWord || this.cacheKeyWord,
112
+ resId: this.resId,
113
+ chatId: this.chatId || undefined,
114
+ commonKey: commonKey || false,
115
+ ...reanswerParams,
116
+ }
117
+ ]
118
+ };
119
+ // list 存在说明是重新生成
120
+ if (!list && !commonKey) {
121
+ this.messageData.push({ type: 'user', message: this.keyWord });
122
+ setTimeout(() => this.messageData.push(
123
+ {
124
+ type: 'robot',
125
+ message: '',
126
+ zan: false,
127
+ cai: false,
128
+ stop: false,
129
+ finish: false,
130
+ links: [],
131
+ reAnswerCount: 0
132
+ }
133
+ ));
134
+ }
135
+ await sendMessageEventSource(params, signal, res => {
136
+ this.makeMessageLine(res, list);
137
+ });
138
+ },
139
+ makeMessageLine(res, list) {
140
+ const { content, status, chatId, msgId, parentMsgId, reference, commonKey } = JSON.parse(res.data);
141
+ let index = list ? list.index : this.messageData.length - 1;
142
+ const currentList = this.messageData[index];
143
+ // 结束标识
144
+ if (status === 2 || status === 1) {
145
+ if (commonKey) {
146
+ this.postMessage(list, commonKey);
147
+ return;
148
+ }
149
+ currentList.finish = true;
150
+ if (reference) {
151
+ currentList.links = JSON.parse(reference);
152
+ }
153
+ this.cacheKeyWord = '';
154
+ this.$emit('on-message-finish', chatId);
155
+ return;
156
+ }
157
+ currentList.message += content;
158
+ currentList.msgId = msgId;
159
+ currentList.parentMsgId = parentMsgId;
160
+ currentList.chatId = chatId;
161
+ setTimeout(() => {
162
+ if (!list) this.scrollToBottom();
163
+ }, 100)
164
+ },
165
+ onStopChat(list){
166
+ this.$emit('on-message-finish', list.chatId)
167
+ },
168
+ reanswer(list, index) {
169
+ list.index = index;
170
+ list.stop = false;
171
+ list.finish = false;
172
+ list.links = [];
173
+ this.postMessage(list);
174
+ this.$emit('on-reanser');
175
+ }
176
+ },
177
+ }
178
+ </script>
179
+ <style lang="scss" scoped>
180
+ .chat-contain {
181
+ height: 100%;
182
+ overflow-y: auto;
183
+ padding-right: 32px;
184
+ /* 滚动条轨道样式 */
185
+ &::-webkit-scrollbar {
186
+ width: 8px;
187
+ /* 设置滚动条宽度 */
188
+ // background-color: #f1f1f1;
189
+ border-radius: 10px;
190
+ }
191
+
192
+ /* 滚动条轨道 */
193
+ &::-webkit-scrollbar-track {
194
+ background: transparent; /* 设置轨道背景为透明 */
195
+ }
196
+
197
+ /* 滚动条滑块样式 */
198
+ &::-webkit-scrollbar-thumb {
199
+ background: rgba(96, 128, 240, 0.6); /* 设置拖动块颜色 */
200
+ border-radius: 4px;
201
+ -webkit-box-shadow: inset 0 0 2px transparent;
202
+ -webkit-transition: all 0.2s linear;
203
+ transition: all 0.2s linear;
204
+ }
205
+
206
+ /* 滚动条滑块hover状态样式 */
207
+ &::-webkit-scrollbar-thumb:hover {
208
+ background: rgba(96, 128, 240, 0.5); /* 加深颜色 */
209
+ }
210
+ }
211
+
212
+ .message {
213
+ display: flex;
214
+ margin-bottom: 16px;
215
+ }
216
+
217
+ .robot {
218
+ min-width: 48px;
219
+ height: 48px;
220
+ }
221
+
222
+ .user {
223
+ color: #fff;
224
+ line-height: 48px;
225
+ border-radius: 20px;
226
+ text-align: center;
227
+ box-shadow: 3px 3px 6px rgba(0, 3, 6, 0.16);
228
+ background: linear-gradient(90deg, rgba(0, 144, 240, 1) 0%, rgba(144, 48, 240, 1) 100%);
229
+ }
230
+
231
+ .user-list {
232
+ display: flex;
233
+ justify-content: end;
234
+ flex-direction: row-reverse;
235
+
236
+ .robot-message {
237
+ margin-left: 0;
238
+ margin-right: 16px;
239
+ padding: 12px 24px;
240
+ color: #fff;
241
+ }
242
+ }
243
+
244
+ .robot-message {
245
+ width: 100%;
246
+ background-color: rgba(241, 243, 245, 1);
247
+ border-radius: 12px;
248
+ border-top-left-radius: 0;
249
+ padding: 24px 24px;
250
+ margin-left: 16px;
251
+ line-height: 24px;
252
+ }
253
+
254
+ .user-info {
255
+ width: auto;
256
+ border-radius: 12px;
257
+ border-top-right-radius: 0;
258
+ background: linear-gradient(90deg, rgba(130, 136, 220, 1) 0%, rgba(48, 90, 220, 1) 100%);
259
+ }
260
+
261
+ .link {
262
+ width: 100%;
263
+ margin-bottom: 16px;
264
+
265
+ .link-title {
266
+ color: #878aab;
267
+ font-size: 14px;
268
+ margin: 8px 0;
269
+ text-align: left;
270
+ }
271
+
272
+ .link-content {
273
+ background: #fafafd;
274
+ border-radius: 8px;
275
+ padding: 8px;
276
+ max-height: 170px;
277
+ overflow: auto;
278
+
279
+ .links {
280
+ position: relative;
281
+ height: 22px;
282
+ line-height: 22px;
283
+ margin: 8px 0;
284
+ margin-left: 10px;
285
+
286
+ a {
287
+ color: #1890ff;
288
+
289
+ &:hover {
290
+ color: #615ced;
291
+ }
292
+ }
293
+
294
+ span {
295
+ display: inline-block;
296
+ font-size: 14px;
297
+ }
298
+ }
299
+ }
300
+ }
301
+ .robot-image-div {
302
+ width: 48px;
303
+ height: 48px;
304
+ background: url("../static/robot.png");
305
+ background-size: 100%;
306
+ }
307
+ </style>
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <div class="hot-search-contain">
3
+ <div class="robot">
4
+ <img src="../static/robot.png" alt="" style="width: 48px; height: 48px">
5
+ </div>
6
+ <div class="robot-message">
7
+ <p>Hi,我是学习助手。</p>
8
+ <p>在这里你可以获得专业、准确的课程讲解,包括对公式的深入理解和细腻的解答。</p>
9
+ <!-- <div class="hot-list" v-for="list in aiMessage" :key="list.id" @click="selectModule(list)">-->
10
+ <!-- {{ list.value }}-->
11
+ <!-- <i class="el-icon-arrow-right go-icon"></i>-->
12
+ <!-- </div>-->
13
+ </div>
14
+ </div>
15
+ </template>
16
+ <script>
17
+
18
+ export default {
19
+ name: 'HotSearch',
20
+ data() {
21
+ return {
22
+ aiMessage: [
23
+ {
24
+ value: '请解释一下牛顿第二定律的公式和物理意义。',
25
+ id: 1,
26
+ },
27
+ {
28
+ value: '电磁感应定律是如何描述的?请给出相关公式并解释。',
29
+ id: 2,
30
+ },
31
+ {
32
+ value: '在光学中,光的干涉现象是如何产生的?能否用公式来表示?',
33
+ id: 3,
34
+ }
35
+ ],
36
+ }
37
+ },
38
+ methods: {
39
+ selectModule (list) {
40
+ this.$emit('select-module', list);
41
+ }
42
+ }
43
+ }
44
+ </script>
45
+ <style lang="scss" scoped>
46
+ .hot-search-contain {
47
+ display: flex;
48
+ }
49
+ .robot {
50
+ min-width: 48px;
51
+ height: 48px;
52
+ }
53
+ .robot-message {
54
+ width: 100%;
55
+ background-color: rgba(241, 243, 245, 1);
56
+ border-radius: 12px;
57
+ border-top-left-radius: 0;
58
+ padding: 24px 24px;
59
+ margin-left: 16px;
60
+ }
61
+ .hot-list {
62
+ background-color: rgba(255, 255, 255, 1);
63
+ border-radius: 8px;
64
+ font-size: 16px;
65
+ padding: 10px 24px 10px 16px;
66
+ margin-top: 16px;
67
+ cursor: pointer;
68
+ &:hover {
69
+ background-color: rgba(96, 96, 224, 0.24);
70
+ }
71
+ }
72
+ p {
73
+ margin: 0;
74
+ font-size: 18px;
75
+ color: rgba(32, 40, 64, 1);
76
+ }
77
+ .go-icon {
78
+ position: relative;
79
+ right: -16px;
80
+ float: right;
81
+ }
82
+ </style>
@@ -0,0 +1,292 @@
1
+ <template>
2
+ <div class="ai-contain" v-if="visible">
3
+ <div class="bg-image"></div>
4
+ <div class="ai-contain-header">
5
+ <div class="logo-title" @click="showHot">
6
+ <img src="../static/logo.png" alt="" style="width: 48px; height: 48px">
7
+ <span class="title-font">学习助手</span>
8
+ </div>
9
+ <div class="icon-close" @click="closeMessage">
10
+ <i class="el-icon-close"></i>
11
+ </div>
12
+ </div>
13
+ <div class="ai-main-content">
14
+ <hot-search v-if="!chatShow" @select-module="selectModule"></hot-search>
15
+ <chat v-if="chatShow"
16
+ ref="chatRef"
17
+ :resId="resId"
18
+ :chatId="chatId"
19
+ :key-word="aiMessageKeyWord"
20
+ :message-data="messageList"
21
+ @on-reanser="reanserMethod"
22
+ @on-message-finish="messageFinish"
23
+ />
24
+ </div>
25
+ <div class="tools-wrap">
26
+ <span @click="addNewChat">新增对话</span>
27
+ <span @click="clearChat">清空对话</span>
28
+ </div>
29
+ <div class="ai-message-send" @keyup.enter="goChat">
30
+ <el-input v-model="aiMessage"
31
+ placeholder="可以提出问题...">
32
+ </el-input>
33
+ <div class="ai-send-icon" @click="goChat">
34
+ <img src="../static/send-icon.png" alt="" style="width: 24px; height: 24px">
35
+ </div>
36
+ </div>
37
+ <p class="disclaimer">
38
+ 此功能公测期间对正式版用户开放
39
+ </p>
40
+ </div>
41
+ </template>
42
+ <script>
43
+ import HotSearch from './hot-search.vue';
44
+ import Chat from './chat.vue';
45
+ import Cookies from "js-cookie";
46
+ import { getUserInfo } from '../utils/config';
47
+ import { newChat, chartClear, getDetailList, countAccess } from '../api/index';
48
+ export default {
49
+ name: 'AiAssistant',
50
+ components: {
51
+ HotSearch,
52
+ Chat
53
+ },
54
+ props: {
55
+ visible: true,
56
+ resId: {
57
+ type: String,
58
+ default: ''
59
+ }
60
+ },
61
+ watch: {
62
+ visible() {
63
+ if (this.visible) this.queryList();
64
+ },
65
+ },
66
+ data() {
67
+ return {
68
+ aiMessage: '', // 页面显示值
69
+ aiMessageKeyWord: '', // 传入组件值
70
+ chatShow: false,
71
+ messageList: [],
72
+ chatId: undefined, // 对话id
73
+ stop: false
74
+ }
75
+ },
76
+ mounted() {
77
+ if (!Cookies.get("token")) {
78
+ return this.$message.warning('未获取到登录信息,请重新登录');
79
+ }
80
+ this.messageList = [];
81
+ getUserInfo(Cookies.get("token"));
82
+ },
83
+ methods: {
84
+ closeMessage() {
85
+ this.$emit('close');
86
+ },
87
+ selectModule() {
88
+ this.chatShow = true;
89
+ },
90
+ showHot() {
91
+ this.chatShow = false;
92
+ },
93
+ async queryList() {
94
+ countAccess(this.resId);
95
+ const listRes = await getDetailList(this.resId);
96
+ if (!listRes || listRes.length < 1) return;
97
+ let currentList = listRes[0].contents.reverse();
98
+ const cacheArray = [];
99
+ currentList.map((list) => {
100
+ const obj = {
101
+ type: list.role === 'assistant' ? 'robot' : 'user',
102
+ message: list.content,
103
+ zan: list.type === 1,
104
+ cai: list.type === 2,
105
+ stop: list.status === 1,
106
+ finish: true,
107
+ links: list.references || [],
108
+ reAnswerCount: 0,
109
+ msgId: list.contentId,
110
+ parentMsgId: list.parentMsgId
111
+ };
112
+ cacheArray.push(obj);
113
+ })
114
+ if (currentList[0]) this.chatId = currentList[0].chatId;
115
+ this.messageList = cacheArray;
116
+ if (this.messageList.length > 0) {
117
+ this.chatShow = true;
118
+ setTimeout(() => {
119
+ this.$refs.chatRef.scrollToBottom();
120
+ });
121
+ }
122
+ },
123
+ async addNewChat() {
124
+ if (!this.stopMessage()) {
125
+ return;
126
+ }
127
+ const res = await newChat(this.resId);
128
+ this.chatId = res.chatId;
129
+ this.resizeChat();
130
+ this.$message.success('创建新对话成功!');
131
+ },
132
+ async clearChat() {
133
+ if (!this.stopMessage()) {
134
+ return;
135
+ }
136
+ if (!this.chatId || this.messageList.length < 1) {
137
+ this.$message.warning('当前对话为最新对话');
138
+ return;
139
+ }
140
+ await chartClear(this.chatId);
141
+ this.resizeChat();
142
+ this.$message.success('对话清除成功!');
143
+ this.chatId = undefined;
144
+ },
145
+ goChat() {
146
+ if (!this.stopMessage()) {
147
+ return;
148
+ }
149
+ this.aiMessageKeyWord = this.aiMessage;
150
+ this.chatShow = true;
151
+ this.stop = true;
152
+ setTimeout(() => {
153
+ this.aiMessageKeyWord = this.aiMessage = '';
154
+ }, 300);
155
+ },
156
+ reanserMethod(){
157
+ this.stop = true;
158
+ },
159
+ stopMessage() {
160
+ if (this.stop) {
161
+ this.$message.warning('对话正在进行中,请稍后重试');
162
+ return false;
163
+ }
164
+ return true;
165
+ },
166
+ resizeChat() {
167
+ this.messageList = [];
168
+ this.chatShow = false;
169
+ },
170
+ messageFinish(chatId) {
171
+ this.stop = false;
172
+ this.chatId = chatId;
173
+ }
174
+ }
175
+ }
176
+ </script>
177
+ <style lang="scss" scoped>
178
+ .ai-contain {
179
+ width: 1000px;
180
+ height: 100%;
181
+ border-radius: 16px;
182
+ box-shadow: 0 1px 8px rgba(32, 40, 64, 0.32);
183
+ padding: 16px 16px 16px 16px;
184
+ box-sizing: border-box;
185
+ position: relative;
186
+ background: #fff;
187
+ }
188
+ .ai-contain-header {
189
+ position: relative;
190
+ z-index: 1;
191
+ display: flex;
192
+ justify-content: space-between;
193
+ }
194
+ .logo-title {
195
+ font-weight: 700;
196
+ color: #202840;
197
+ cursor: pointer;
198
+ font-size: 18px;
199
+ }
200
+ .title-font {
201
+ display: inline-block;
202
+ vertical-align: top;
203
+ margin-top: 12px;
204
+ margin-left: 12px;
205
+ color: rgb(40, 128, 240);
206
+ }
207
+ .icon-close {
208
+ cursor: pointer;
209
+ position: relative;
210
+ right: 0;
211
+ i {
212
+ font-weight: bold !important;
213
+ }
214
+ }
215
+ .ai-message-send-linear {
216
+ padding: 2px;
217
+ border-radius: 40px;
218
+ }
219
+ .tools-wrap {
220
+ width: calc(100% - 68px - 48px - 24px);
221
+ margin-left: 80px;
222
+ display: flex;
223
+ justify-content: space-between;
224
+ font-size: 13px;
225
+ margin-bottom: 8px;
226
+ color: #08d;
227
+ span {
228
+ cursor: pointer;
229
+ &:hover {
230
+ color: #0ad;
231
+ }
232
+ }
233
+ }
234
+ .ai-message-send {
235
+ width: calc(100% - 68px - 48px - 17px);
236
+ background: linear-gradient(to right, #33cc88, #6699ff, #ff9933);
237
+ padding: 3px;
238
+ border-radius: 40px;
239
+ position: relative;
240
+ margin-left: 68px;
241
+ }
242
+ .ai-send-icon {
243
+ position: absolute;
244
+ right: 14px;
245
+ bottom: 14px;
246
+ width: 40px;
247
+ height: 40px;
248
+ text-align: center;
249
+ line-height: 52px;
250
+ border-radius: 20px;
251
+ cursor: pointer;
252
+ background: linear-gradient(90deg, rgba(0, 144, 240, 1) 0%, rgba(144, 48, 240, 1) 100%);
253
+ }
254
+ .ai-main-content {
255
+ position: relative;
256
+ z-index: 1;
257
+ height: calc(100% - 53px - 86px - 32px - 17px);
258
+ padding: 16px 0 16px 8px;
259
+ }
260
+ .disclaimer {
261
+ margin: 0;
262
+ font-size: 14px;
263
+ color: #cfcfcf;
264
+ text-align: center;
265
+ margin-top: 6px;
266
+ }
267
+ .bg-image {
268
+ position: absolute;
269
+ top: 0;
270
+ left: 0;
271
+ width: 100%;
272
+ height: 248px;
273
+ background: linear-gradient(to bottom, rgba(96, 96, 244, .1), rgba(44, 144, 240, 0));
274
+ border-radius: 16px 0;
275
+ }
276
+ :deep {
277
+ .ai-message-send {
278
+ .el-input__inner {
279
+ height: 60px;
280
+ font-size: 16px;
281
+ color: rgba(51,51,51,.75);
282
+ border: 0;
283
+ border-radius: 40px;
284
+ background: linear-gradient(to bottom, #fff, #d8f0fb);
285
+ padding: 0 24px;
286
+ &::placeholder {
287
+ color: rgba(51,51,51,.75);
288
+ }
289
+ }
290
+ }
291
+ }
292
+ </style>
@@ -0,0 +1,11 @@
1
+ <svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1" width="712px" height="248px">
2
+ <defs>
3
+ <linearGradient gradientUnits="userSpaceOnUse" x1="1376" y1="44" x2="1376" y2="292" id="LinearGradient311">
4
+ <stop id="Stop312" stop-color="#6060e0" stop-opacity="0.156862745098039" offset="0"/>
5
+ <stop id="Stop313" stop-color="#3090f0" stop-opacity="0" offset="1"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <g transform="matrix(1 0 0 1 -1020 -44 )">
9
+ <path d="M 1020 52 A 8 8 0 0 1 1028 44 L 1724 44 A 8 8 0 0 1 1732 52 L 1732 284 A 8 8 0 0 1 1724 292 L 1028 292 A 8 8 0 0 1 1020 284 L 1020 52 Z " fill-rule="nonzero" fill="url(#LinearGradient311)" stroke="none"/>
10
+ </g>
11
+ </svg>
Binary file
Binary file
Binary file
Binary file