ai-error-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.
Files changed (34) hide show
  1. package/README.md +33 -0
  2. package/components/demo/api/index.js +84 -0
  3. package/components/demo/index.js +7 -0
  4. package/components/demo/plugins/cache.js +77 -0
  5. package/components/demo/src/chat-tools.vue +289 -0
  6. package/components/demo/src/chat.vue +342 -0
  7. package/components/demo/src/error-chat.vue +247 -0
  8. package/components/demo/src/main.vue +221 -0
  9. package/components/demo/static/bg-img.png +0 -0
  10. package/components/demo/static/cai-active.png +0 -0
  11. package/components/demo/static/cai.png +0 -0
  12. package/components/demo/static/correct.png +0 -0
  13. package/components/demo/static/error.png +0 -0
  14. package/components/demo/static/logo.png +0 -0
  15. package/components/demo/static/robot.png +0 -0
  16. package/components/demo/static/send-icon.png +0 -0
  17. package/components/demo/static/zan-active.png +0 -0
  18. package/components/demo/static/zan.png +0 -0
  19. package/components/demo/utils/aes-utils.js +35 -0
  20. package/components/demo/utils/config.js +88 -0
  21. package/components/demo/utils/constants.js +13 -0
  22. package/components/demo/utils/request.js +69 -0
  23. package/components/index.js +15 -0
  24. package/dist/ai-error-assistant-pro.common.js +12065 -0
  25. package/dist/ai-error-assistant-pro.common.js.map +1 -0
  26. package/dist/ai-error-assistant-pro.css +1 -0
  27. package/dist/ai-error-assistant-pro.umd.js +12084 -0
  28. package/dist/ai-error-assistant-pro.umd.js.map +1 -0
  29. package/dist/ai-error-assistant-pro.umd.min.js +18 -0
  30. package/dist/ai-error-assistant-pro.umd.min.js.map +1 -0
  31. package/dist/demo.html +1 -0
  32. package/dist/img/bg-img.9391d6da.png +0 -0
  33. package/dist/img/robot.7ad12cd4.png +0 -0
  34. package/package.json +44 -0
@@ -0,0 +1,342 @@
1
+ <template>
2
+ <div class="chat-contain" ref="chatContainer">
3
+ <error-chat :question-stem="analyExercise" @on-init="errorChatInit"/>
4
+ <div v-for="(list, index) in messageData" :key="index"
5
+ :class="{'user-list': list.type === 'user'}"
6
+ class="message">
7
+ <div v-if="list.type === 'robot'" class="robot">
8
+ <div class="robot-image-div"></div>
9
+ <!-- <img alt="" src="../static/robot.png" style="width: 48px; height: 48px">-->
10
+ </div>
11
+ <div v-else class="robot user">user</div>
12
+ <div :class="{'user-info': list.type === 'user'}" class="robot-message">
13
+ <div v-html="list.message"></div>
14
+ <div v-if="list.type === 'robot' && list.links.length > 0" class="link">
15
+ <div class="link-title">相关链接</div>
16
+ <div class="link-content">
17
+ <div class="links" v-for="item in list.links">
18
+ <a :href="'https://ai-yuliao.hep.com.cn'+item.documentUrl" target="_blank">
19
+ <span>{{ item.title }}</span>
20
+ </a>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ <chat-tools v-if="list.type === 'robot'"
25
+ :chatId="chatId"
26
+ :detail-data="list"
27
+ @on-reanswer="(list) => reanswer(list, index)"
28
+ @on-stop-chat="onStopChat"/>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ </template>
33
+ <script>
34
+ import { errorList } from '../utils/config';
35
+ import ChatTools from './chat-tools.vue';
36
+ import ErrorChat from './error-chat.vue';
37
+ import { chartClear, sendMessageEventSource } from "../api";
38
+ const controller = new AbortController();
39
+ const signal = controller.signal;
40
+
41
+ export default {
42
+ name: 'Chat',
43
+ components: {
44
+ ErrorChat,
45
+ ChatTools
46
+ },
47
+ props: {
48
+ analyExercise: {},
49
+ keyWord: {
50
+ type: String,
51
+ default: '',
52
+ },
53
+ resId: {
54
+ type: String,
55
+ default: '',
56
+ },
57
+ xtId: {
58
+ type: String,
59
+ default: '',
60
+ },
61
+ },
62
+ data() {
63
+ return {
64
+ popover: false,
65
+ input: '',
66
+ errors: [],
67
+ errorList: errorList,
68
+ cacheKeyWord: '', // 由于外部需要快速清掉 keyword 但是存在重新发送请求的情况,因此需要一个混存记录
69
+ messageData: [],
70
+ chatId: '', // 对话id
71
+ }
72
+ },
73
+ computed: {
74
+ canSubmit() {
75
+ return this.errors.length === 0 && this.input === "";
76
+ },
77
+ },
78
+ watch: {
79
+ keyWord: {
80
+ handler() {
81
+ if (this.keyWord) {
82
+ this.cacheKeyWord = this.keyWord;
83
+ this.postMessage();
84
+ }
85
+ },
86
+ immediate: true
87
+ }
88
+ },
89
+ methods: {
90
+ cancelPopover() {
91
+ this.popover = false;
92
+ },
93
+ openPopover() {
94
+ this.popover = true;
95
+ },
96
+ scrollToBottom() {
97
+ this.$refs.chatContainer.scrollTop = this.$refs.chatContainer.scrollHeight;
98
+ },
99
+ async postMessage(list, commonKey) {
100
+ let reanswerParams = {};
101
+ if (list) {
102
+ reanswerParams = {
103
+ reStatus: 1,
104
+ parentMsgId: list.parentMsgId,
105
+ msgId: list.msgId,
106
+ content: this.messageData[list.index - 1].message,
107
+ }
108
+ this.messageData[list.index].message = '';
109
+ }
110
+ const params = {
111
+ messageList: [
112
+ {
113
+ content: this.keyWord || this.cacheKeyWord,
114
+ resId: this.resId,
115
+ chatId: this.chatId || undefined,
116
+ commonKey: commonKey || false,
117
+ ...reanswerParams,
118
+ }
119
+ ]
120
+ };
121
+ // list 存在说明是重新生成
122
+ if (!list && !commonKey) {
123
+ this.messageData.push({ type: 'user', message: this.keyWord });
124
+ setTimeout(() => this.messageData.push(
125
+ {
126
+ type: 'robot',
127
+ message: '',
128
+ zan: false,
129
+ cai: false,
130
+ stop: false,
131
+ finish: false,
132
+ links: [],
133
+ reAnswerCount: 0
134
+ }
135
+ ));
136
+ }
137
+ await sendMessageEventSource(params, signal, res => {
138
+ this.makeMessageLine(res, list);
139
+ });
140
+ },
141
+ makeMessageLine(res, list) {
142
+ const { content, status, chatId, msgId, parentMsgId, reference, commonKey } = JSON.parse(res.data);
143
+ let index = list ? list.index : this.messageData.length - 1;
144
+ const currentList = this.messageData[index];
145
+ // 结束标识
146
+ if (status === 2 || status === 1) {
147
+ if (commonKey) {
148
+ this.postMessage(list, commonKey);
149
+ return;
150
+ }
151
+ currentList.finish = true;
152
+ if (reference) {
153
+ currentList.links = JSON.parse(reference);
154
+ }
155
+ this.cacheKeyWord = '';
156
+ this.$emit('on-message-finish', chatId);
157
+ return;
158
+ }
159
+ currentList.message += content;
160
+ currentList.msgId = msgId;
161
+ currentList.parentMsgId = parentMsgId;
162
+ currentList.chatId = chatId;
163
+ setTimeout(() => {
164
+ if (!list) this.scrollToBottom();
165
+ }, 100)
166
+ },
167
+ onStopChat(list){
168
+ this.$emit('on-message-finish', list.chatId)
169
+ },
170
+ async clearChat() {
171
+ if (this.messageData.length < 1) {
172
+ this.$message.warning('当前对话为最新对话');
173
+ return;
174
+ }
175
+ await chartClear(this.chatId);
176
+ this.messageData = []
177
+ this.$message.success('对话清除成功!');
178
+ },
179
+ reanswer(list, index) {
180
+ list.index = index;
181
+ list.stop = false;
182
+ list.finish = false;
183
+ list.links = [];
184
+ this.postMessage(list);
185
+ this.$emit('on-reanser');
186
+ },
187
+ errorChatInit(list, chatId) {
188
+ if (list && list[0].contents) {
189
+ const cacheArray = [];
190
+ list[0].contents.reverse().map((list) => {
191
+ const obj = {
192
+ type: list.role === 'assistant' ? 'robot' : 'user',
193
+ message: list.content,
194
+ zan: list.type === 1,
195
+ cai: list.type === 2,
196
+ stop: list.status === 1,
197
+ finish: true,
198
+ links: list.references || [],
199
+ reAnswerCount: 0,
200
+ msgId: list.contentId,
201
+ parentMsgId: list.parentMsgId
202
+ };
203
+ cacheArray.push(obj);
204
+ })
205
+ this.messageData = cacheArray;
206
+ this.chatId = chatId;
207
+ }else {
208
+ this.messageData = [];
209
+ }
210
+ }
211
+ },
212
+ }
213
+ </script>
214
+ <style lang="scss" scoped>
215
+ .chat-contain {
216
+ height: 100%;
217
+ overflow-y: auto;
218
+ padding-right: 32px;
219
+ /* 滚动条轨道样式 */
220
+ &::-webkit-scrollbar {
221
+ width: 8px;
222
+ /* 设置滚动条宽度 */
223
+ // background-color: #f1f1f1;
224
+ border-radius: 10px;
225
+ }
226
+
227
+ /* 滚动条轨道 */
228
+ &::-webkit-scrollbar-track {
229
+ background: transparent; /* 设置轨道背景为透明 */
230
+ }
231
+
232
+ /* 滚动条滑块样式 */
233
+ &::-webkit-scrollbar-thumb {
234
+ background: rgba(96, 128, 240, 0.6); /* 设置拖动块颜色 */
235
+ border-radius: 4px;
236
+ -webkit-box-shadow: inset 0 0 2px transparent;
237
+ -webkit-transition: all 0.2s linear;
238
+ transition: all 0.2s linear;
239
+ }
240
+
241
+ /* 滚动条滑块hover状态样式 */
242
+ &::-webkit-scrollbar-thumb:hover {
243
+ background: rgba(96, 128, 240, 0.5); /* 加深颜色 */
244
+ }
245
+ }
246
+
247
+ .message {
248
+ display: flex;
249
+ margin: 24px;
250
+ }
251
+
252
+ .robot {
253
+ min-width: 48px;
254
+ height: 48px;
255
+ }
256
+
257
+ .user {
258
+ color: #fff;
259
+ line-height: 48px;
260
+ border-radius: 20px;
261
+ text-align: center;
262
+ box-shadow: 3px 3px 6px rgba(0, 3, 6, 0.16);
263
+ background: linear-gradient(90deg, rgba(0, 144, 240, 1) 0%, rgba(144, 48, 240, 1) 100%);
264
+ }
265
+
266
+ .user-list {
267
+ display: flex;
268
+ justify-content: end;
269
+ flex-direction: row-reverse;
270
+
271
+ .robot-message {
272
+ margin-left: 0;
273
+ margin-right: 16px;
274
+ padding: 12px 24px;
275
+ color: #fff;
276
+ }
277
+ }
278
+
279
+ .robot-message {
280
+ width: 100%;
281
+ background-color: rgba(255, 255, 255, 1);
282
+ border-radius: 12px;
283
+ border-top-left-radius: 0;
284
+ padding: 24px 24px;
285
+ margin-left: 16px;
286
+ line-height: 24px;
287
+ }
288
+
289
+ .user-info {
290
+ width: auto;
291
+ border-radius: 12px;
292
+ border-top-right-radius: 0;
293
+ background: linear-gradient(90deg, rgba(130, 136, 220, 1) 0%, rgba(48, 90, 220, 1) 100%);
294
+ }
295
+
296
+ .link {
297
+ width: 100%;
298
+ margin-bottom: 16px;
299
+
300
+ .link-title {
301
+ color: #878aab;
302
+ font-size: 14px;
303
+ margin: 8px 0;
304
+ text-align: left;
305
+ }
306
+
307
+ .link-content {
308
+ background: #fafafd;
309
+ border-radius: 8px;
310
+ padding: 8px;
311
+ max-height: 170px;
312
+ overflow: auto;
313
+
314
+ .links {
315
+ position: relative;
316
+ height: 22px;
317
+ line-height: 22px;
318
+ margin: 8px 0;
319
+ margin-left: 10px;
320
+
321
+ a {
322
+ color: #1890ff;
323
+
324
+ &:hover {
325
+ color: #615ced;
326
+ }
327
+ }
328
+
329
+ span {
330
+ display: inline-block;
331
+ font-size: 14px;
332
+ }
333
+ }
334
+ }
335
+ }
336
+ .robot-image-div {
337
+ width: 48px;
338
+ height: 48px;
339
+ background: url("../static/robot.png");
340
+ background-size: 100%;
341
+ }
342
+ </style>
@@ -0,0 +1,247 @@
1
+ <template>
2
+ <div class="error-chat-contain">
3
+ <div class="exercises-answer">
4
+ <div class="exercises-answer-bg"></div>
5
+ <p class="question-stem">
6
+ {{ questionStem.title }}
7
+ </p>
8
+ <div class="answer-part" v-if="questionStem.type === 'single' || questionStem.type === 'multiple'">
9
+ <div class="answer-list" v-for="(item, index) in questionStem.options" :key="index">
10
+ <div class="check-content" style="width: 24px">
11
+ <img src="../static/correct.png"
12
+ v-if="questionStem.answer.indexOf(item) > -1"
13
+ alt=""
14
+ style="width: 24px">
15
+ </div>
16
+ <div class="answer-list">{{ item }}</div>
17
+ </div>
18
+ </div>
19
+ <div class="answer-part"
20
+ style="padding-bottom: 24px"
21
+ v-if="questionStem.type === 'judge'">
22
+ 正确答案: <span style="margin-left: 8px">{{ questionStem.answer.join(',') }}</span>
23
+ </div>
24
+ <div class="answer-part"
25
+ style="padding-bottom: 24px"
26
+ v-if="questionStem.type === 'gapfilling'">
27
+ 正确答案:
28
+ <span style="margin-left: 8px"
29
+ v-for="(item, index) in questionStem.answer"
30
+ :key="index">
31
+ {{ item }}<span v-if="index !== questionStem.answer.length - 1">,</span>
32
+ </span>
33
+ </div>
34
+ <div class="answer-part"
35
+ style="padding-bottom: 24px"
36
+ v-if="questionStem.type === 'shortanswer'">
37
+ 正确答案: <span style="margin-left: 8px">{{ questionStem.answer.join(',') }}</span>
38
+ </div>
39
+ <div class="student-answer-part">
40
+ <div class="check-content" style="width: 24px; margin-right: 16px">
41
+ <img src="../static/error.png" style="width: 24px">
42
+ </div>
43
+ <div v-if="questionStem.type === 'single' || questionStem.type === 'multiple'">
44
+ <span v-for="(item, index) in questionStem.studentAnswer"
45
+ style="margin-right: 16px"
46
+ :key="index">
47
+ {{ item }}
48
+ </span>
49
+ </div>
50
+ <div v-if="questionStem.type === 'judge'">
51
+ <span style="margin-left: 8px">{{ questionStem.studentAnswer.join(',') }}</span>
52
+ </div>
53
+ <span style="margin-left: 8px"
54
+ v-if="questionStem.type === 'gapfilling'"
55
+ v-for="(item, index) in questionStem.studentAnswer"
56
+ :key="index">
57
+ {{ item }}<span v-if="index !== questionStem.studentAnswer.length - 1">,</span>
58
+ </span>
59
+ <div v-if="questionStem.type === 'shortanswer'">
60
+ <span style="margin-left: 8px">{{ questionStem.studentAnswer.join(',') }}</span>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ <div class="ai-analyze-content"
65
+ element-loading-text="分析中,请稍后"
66
+ v-loading="!Object.keys(analysisData).length > 0">
67
+ <div class="title-label">题目解析</div>
68
+ <div class="answer-content" v-html="analysisData.analysis"></div>
69
+ <div class="title-label">错因分析</div>
70
+ <div class="answer-content" v-html="analysisData.reason"></div>
71
+ <div class="title-label">推荐资料</div>
72
+ <div class="answer-content">
73
+ <div v-for="(list,index) in analysisData.recommend" :key="index" style="margin-bottom: 8px">
74
+ <a :href="'https://ai-yuliao.hep.com.cn' + list.url" target="_blank">{{ list.title }}</a>
75
+ </div>
76
+ </div>
77
+ <chat-tools type="error"
78
+ :chatId="chatId"
79
+ :detailData="detailData"
80
+ @on-reanswer="reanswer" />
81
+ </div>
82
+ </div>
83
+ </template>
84
+ <script>
85
+ import ChatTools from './chat-tools.vue';
86
+ import { erranalysis, retryAnalysis } from '../api/index'
87
+ export default {
88
+ name: 'ErrorChart',
89
+ components: {
90
+ ChatTools
91
+ },
92
+ props: {
93
+ questionStem: {},
94
+ },
95
+ data() {
96
+ return {
97
+ detailData: {}, // 记录页面点赞 踩情况
98
+ analysisData: {}, // 记录页面点赞 踩情况
99
+ chatId: '' // 对话id
100
+ }
101
+ },
102
+ watch: {
103
+ questionStem: {
104
+ handler() {
105
+ if (this.questionStem) this.getErrorAnalysis();
106
+ },
107
+ immediate: true
108
+ }
109
+ },
110
+ methods: {
111
+ async getErrorAnalysis() {
112
+ try {
113
+ this.analysisData = {};
114
+ const analysisRes = await erranalysis({
115
+ courseId: this.questionStem.courseId,
116
+ busId: this.questionStem.busId,
117
+ questionType: this.questionStem.type,
118
+ title: this.questionStem.title,
119
+ answer: this.questionStem.answer.join(','),
120
+ studentAnswer: this.questionStem.studentAnswer.join(','),
121
+ optionList: this.questionStem.options,
122
+ });
123
+ const {
124
+ eaAnalysisDtoList: [{ content, errId, chatId, contentId, type }],
125
+ xhModelDetailVoList
126
+ } = analysisRes;
127
+ // 传入工具中的值
128
+ this.chatId = chatId;
129
+ this.detailData = {
130
+ msgId: contentId,
131
+ zan: type === 1,
132
+ cai: type === 2
133
+ }
134
+ this.$emit('on-init', xhModelDetailVoList, chatId);
135
+ try {
136
+ this.analysisData = JSON.parse(content);
137
+ this.detailData.errId = errId;
138
+ } catch (e) {
139
+ this.$message.warning('解析失败' + e);
140
+ this.analysisData = { key: true };
141
+ console.log(e);
142
+ }
143
+ } catch (e) {
144
+ return new Error(e);
145
+ }
146
+ },
147
+ async reanswer(list) {
148
+ const cache = {...this.analysisData};
149
+ this.analysisData = {};
150
+ const analysisRes = await retryAnalysis(list.errId);
151
+ if (!analysisRes) {
152
+ this.analysisData = { ...cache };
153
+ return;
154
+ }
155
+ const { eaAnalysisDtoList: [{ content, errId, contentId,type }] } = analysisRes;
156
+ this.detailData = {
157
+ msgId: contentId,
158
+ zan: type === 1,
159
+ cai: type === 2
160
+ }
161
+ try {
162
+ this.analysisData = JSON.parse(content);
163
+ this.detailData.errId = errId;
164
+ } catch (e) {
165
+ this.$message.warn('解析失败');
166
+ this.analysisData = { key: true };
167
+ console.log(e);
168
+ }
169
+ }
170
+ }
171
+ }
172
+ </script>
173
+ <style lang="scss" scoped>
174
+ .exercises-answer {
175
+ position: relative;
176
+ overflow: hidden;
177
+ padding: 24px 32px;
178
+ border-radius: 12px;
179
+ background: linear-gradient(131.57282055732597deg, rgba(255, 255, 255, 1) 36%, rgba(238, 238, 255, 1) 100%);
180
+ }
181
+ .exercises-answer-bg {
182
+ position: absolute;
183
+ right: 0;
184
+ width: 416px;
185
+ height: 304px;
186
+ transform: rotate(90deg);
187
+ background: url("../static/bg-img.png") no-repeat;
188
+ background-size: 226px 394px;
189
+ background-position: 34px -76px;
190
+ opacity: 0.6;
191
+ }
192
+ .question-stem {
193
+ position: relative;
194
+ z-index: 1;
195
+ font-size: 16px;
196
+ color: #202840;
197
+ line-height: 24px;
198
+ font-weight: 700;
199
+ margin: 0;
200
+ }
201
+ .answer-part {
202
+ margin-top: 24px;
203
+ margin-left: 8px;
204
+ padding-bottom: 8px;
205
+ border-bottom: 1px solid #d3d4e1;
206
+ .check-content {
207
+ margin-right: 16px;
208
+ }
209
+ }
210
+ .answer-list {
211
+ display: flex;
212
+ margin-bottom: 8px;
213
+ }
214
+ .student-answer-part {
215
+ display: flex;
216
+ padding-top: 16px;
217
+ padding-left: 8px;
218
+ }
219
+ .ai-analyze-content {
220
+ border-radius: 12px;
221
+ background: #fff;
222
+ padding: 24px 32px;
223
+ margin-top: 24px;
224
+ .title-label {
225
+ width: 80px;
226
+ height: 32px;
227
+ font-weight: 700;
228
+ font-size: 14px;
229
+ color: rgba(96, 96, 224, 1);
230
+ border: 1px solid rgba(96, 96, 224, 1);
231
+ text-align: center;
232
+ line-height: 32px;
233
+ border-radius: 8px;
234
+ border-bottom-left-radius: 0;
235
+ }
236
+ .answer-content {
237
+ margin: 16px 0;
238
+ a {
239
+ color: #1890ff;
240
+
241
+ &:hover {
242
+ color: #615ced;
243
+ }
244
+ }
245
+ }
246
+ }
247
+ </style>