zydx-plus 1.33.448 → 1.34.450

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zydx-plus",
3
- "version": "1.33.448",
3
+ "version": "1.34.450",
4
4
  "description": "Vue.js",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -0,0 +1,6 @@
1
+ import main from './src/chat';
2
+
3
+ main.install = function(Vue) {
4
+ Vue.component(main.name, main);
5
+ };
6
+ export default main;
Binary file
@@ -0,0 +1,560 @@
1
+ <template>
2
+ <div class="chat-content">
3
+ <div class="chat-header">
4
+ <span class="chat-header-title"
5
+ @mousedown.stop="chatMousedown"
6
+ @mouseup.stop="chatMouseup">
7
+ AI助手
8
+ </span>
9
+ <i class="iconfont zydx-guanbi" @click.stop="aiClose"></i>
10
+ </div>
11
+ <div class="chat-in" ref="content">
12
+ <div class="chat-in-cont" ref="contentLi">
13
+ <div class="chat-in-li" :class="{'left': item.role === 'assistant', 'right': item.role === 'user'}" v-for="(item,index) in messages">
14
+ <div class="ai-avatar" v-if="item.role === 'assistant'">
15
+ <img src="./ai.png" alt="" />
16
+ </div>
17
+ <div class="text-cont">{{ item.content }}</div>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ <div class="chat-input">
22
+ <div class="chat-text">
23
+ <textarea :disabled="disabled" @keydown="handleKeydown" v-model="userText" placeholder="请输入问题..."></textarea>
24
+ </div>
25
+ <div class="chat-send">
26
+ <i @click="speak" :class="{'speak': microphone}" class="iconfont zydx-luyinhuatongmai"></i>
27
+ <button @click="send" :disabled="disabled" :class="{'disabled': disabled}">{{ disabled? '发送中...': '发送' }}</button>
28
+ </div>
29
+ </div>
30
+ <div class="microphone" v-if="microphone">
31
+ <div class="microphone-cont mic-anim">
32
+ <div class="microphone1">
33
+ <div class="microphone2">
34
+ <div class="microphone3" @click="stopRecording">
35
+ <div>
36
+ <svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 38 68" width="26" height="48">
37
+ <path id="并集_轮廓_" data-name="并集 (轮廓)" fill="#ccc"
38
+ d="M10,44a3,3,0,0,1-3-3V3a3,3,0,0,1,3-3H28a3,3,0,0,1,3,3V41a3,3,0,0,1-3,3Zm17-4V4H11V40ZM0,47V24a2,2,0,0,1,4,0V45a2,2,0,0,0,2,2H32a2,2,0,0,0,2-2V24a2,2,0,0,1,4,0V47a4,4,0,0,1-4,4H21v9c7.34.26,13,1.93,13,4,0,2.21-6.72,4-15,4S4,66.21,4,64c0-2,5.66-3.7,13-4V51H4A4,4,0,0,1,0,47ZM19,64h.79L19,64l-.79,0Z"/>
39
+ <rect fill="#fff" x="11" y="4" width="16" height="36"/>
40
+ <rect class="volume" :height="volume(vol)" width="12" :y="volume2(vol)" x="13" fill="#43cf7c"/>
41
+ </svg>
42
+ </div>
43
+ <p>{{ voiceLoading? '语音转换中...': `正在说话:${countdown}s` }}</p>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ </template>
51
+
52
+ <script>
53
+ import Recorder from 'js-audio-recorder'
54
+ import axios from 'axios'
55
+ export default {
56
+ name: 'zydx-chat',
57
+ data() {
58
+ return {
59
+ token: '',
60
+ messages: [],
61
+ userText: '',
62
+ disabled: false,
63
+ x: 0,
64
+ y: 0,
65
+ left: 0,
66
+ top: 0,
67
+ isMove: false,
68
+ microphone: false,
69
+ recorder: null,
70
+ countdown: 59,
71
+ fileSize: 0,
72
+ vol: 0,
73
+ base64: '',
74
+ voiceLoading: false
75
+ }
76
+ },
77
+ beforeUnmount() {
78
+ this.recorder.destroy()
79
+ },
80
+ mounted() {
81
+ const _this = this
82
+ this.init()
83
+ this.recorder.onprogress = function (params) {
84
+ if(_this.countdown <= 0) {
85
+ _this.stopRecording()
86
+ }else {
87
+ _this.countdown = 59 - parseInt(params.duration)
88
+ _this.vol = params.vol
89
+ }
90
+ }
91
+ document.addEventListener('mousemove', () => {
92
+ if (!this.isMove) return
93
+ this.x = event.clientX - this.left;
94
+ this.y = event.clientY - this.top;
95
+ // 超出边界
96
+ const w = document.documentElement.clientWidth;
97
+ const h = document.documentElement.clientHeight;
98
+ if (this.x < 0) this.x = 0;
99
+ if (this.y < 0) this.y = 0;
100
+ if (this.x > w - 600) this.x = w - 600;
101
+ if (this.y > h - 650) this.y = h - 650;
102
+ this.$emit('move', {x: this.x, y: this.y})
103
+ })
104
+ document.addEventListener('mouseup', (event) => {
105
+ this.isMove = false
106
+ })
107
+ },
108
+ watch: {
109
+ messages: {
110
+ handler: function (e, oldVal) {
111
+ sessionStorage.setItem('messages', JSON.stringify(e)) // 保存聊天记录
112
+ },
113
+ deep: true
114
+ },
115
+ },
116
+ methods: {
117
+ speak() {
118
+ if(this.disabled) return
119
+ this.microphone = !this.microphone
120
+ if(this.microphone) {
121
+ this.recorder.start().then(() => { // 开始录音
122
+ this.countdown = 59
123
+ this.fileSize = 0
124
+ this.vol = 0
125
+ this.base64 = ''
126
+ }, (error) => {
127
+ this.microphone = false
128
+ this.$message({
129
+ type: 'text',
130
+ cancelShow: false,
131
+ promptContent: '录音失败,请检查麦克风是否可用'
132
+ })
133
+ });
134
+ }else {
135
+ this.microphone = true
136
+ this.stopRecording()
137
+ }
138
+ },
139
+ stopRecording() {
140
+ this.recorder.stop() // 停止录音
141
+ // 获取录音文件
142
+ const pcm = this.recorder.getPCMBlob()
143
+ const file = new File([pcm], 'audio.pcm', {type: 'audio/pcm'})
144
+ let reader = new FileReader();
145
+ reader.readAsDataURL(file)
146
+ reader.onload = async () => {
147
+ this.base64 = reader.result.split('base64,')[1]
148
+ await this.getSpeech()
149
+ }
150
+ },
151
+ async getSpeech() {
152
+ const _this = this
153
+ const token = await this.getAccessToken('voice')
154
+ const data = {
155
+ format: "pcm",
156
+ rate: 16000,
157
+ channel: 1,
158
+ cuid: "Ukvpuzv27RdJhltQBlpITVLNTcxs5ctM",
159
+ token: token.data.data,
160
+ speech: this.base64,
161
+ len: this.recorder.fileSize,
162
+ };
163
+ _this.voiceLoading = true
164
+ axios.post('/server_api', data)
165
+ .then(function(data){
166
+ _this.voiceLoading = false
167
+ _this.microphone = false
168
+ for(let i=0; i< data.data.result.length; i++) {
169
+ _this.userText += data.data.result[i]
170
+ }
171
+ _this.recorder.destroy()
172
+ })
173
+ .catch(function(err){
174
+ _this.recorder.destroy()
175
+ });
176
+ },
177
+ volume(v) {
178
+ if(v === 0) {
179
+ return 32
180
+ }else {
181
+ return v/3 > 32 ? 32 : v/5
182
+ }
183
+ },
184
+ volume2(v) {
185
+ if(v === 0) {
186
+ return 6
187
+ }else {
188
+ return 6 + 32 - this.volume(v)
189
+ }
190
+ },
191
+ chatMousedown(event) {
192
+ event.preventDefault()
193
+ //鼠标按下事件
194
+ this.left = event.offsetX;
195
+ this.top = event.offsetY;
196
+ this.isMove = true
197
+ },
198
+ chatMouseup() {
199
+ this.isMove = false
200
+ },
201
+ // 监听键盘事件
202
+ handleKeydown(e) {
203
+ if(e.keyCode === 32){
204
+ e.preventDefault();
205
+ return;
206
+ }
207
+ if(e.keyCode === 13){
208
+ e.preventDefault();
209
+ this.send()
210
+ }
211
+ },
212
+ // 滚动到底部
213
+ scrollToBottom() {
214
+ this.$nextTick(() => {
215
+ this.$refs.content.scrollTo(0,this.$refs.contentLi.scrollHeight)
216
+ })
217
+ },
218
+ // 发送消息
219
+ send() {
220
+ const _this = this
221
+ // 去掉换行和空格
222
+ let text = _this.userText.replace(/[\r\n]/g, '').replace(/\s*/g, '')
223
+ if(!text) return
224
+ _this.messages.push({
225
+ role: 'user',
226
+ content: text
227
+ })
228
+ _this.userText = ''
229
+ _this.disabled = true
230
+ _this.messages.push({
231
+ role: 'assistant',
232
+ content: '努力思考中...'
233
+ })
234
+ _this.scrollToBottom()
235
+ let mess = JSON.parse(JSON.stringify(_this.messages)) // 拷贝一份记录
236
+ mess.pop()
237
+ axios.post(`/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token=${_this.token}`,{
238
+ messages: mess
239
+ }).then(function(data){
240
+ _this.messages.pop()
241
+ if(data.data.error_code){
242
+ _this.messages.push({
243
+ role: 'assistant',
244
+ content: '这个问题太难了!'
245
+ })
246
+ _this.disabled = false
247
+ _this.scrollToBottom()
248
+ return
249
+ }
250
+ _this.messages.push({
251
+ role: 'assistant',
252
+ content: data.data.result
253
+ })
254
+ _this.scrollToBottom()
255
+ _this.disabled = false
256
+ }).catch(function(err){
257
+ _this.disabled = false
258
+ this.$message({promptContent: err, cancelShow: false})
259
+ });
260
+ },
261
+ // 初始化
262
+ async init() {
263
+ this.messages = JSON.parse(sessionStorage.getItem('messages')) || []
264
+ this.scrollToBottom()
265
+ // 初始化录音
266
+ this.recorder = new Recorder({
267
+ sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
268
+ sampleRate: 16000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
269
+ numChannels: 1, // 声道,支持 1 或 2, 默认是1
270
+ })
271
+ const token = await this.getAccessToken('ai')
272
+ this.token = token.data.data
273
+ },
274
+ async getAccessToken(v) {
275
+ return axios({
276
+ url: '/zydx/voice/getBaiduToken',
277
+ method: 'post',
278
+ headers: {
279
+ 'Authorization': localStorage.getItem('sessionId'),
280
+ 'Content-Type': 'application/x-www-form-urlencoded'
281
+ },
282
+ params: {
283
+ type: v,
284
+ }
285
+ }).then(function(data){
286
+ return data
287
+ })
288
+ .catch(function(err){
289
+ console.log(err)
290
+ });
291
+ // return await getBaiduToken({type: v})
292
+ },
293
+ // 关闭
294
+ aiClose() {
295
+ this.$emit('close')
296
+ }
297
+ }
298
+ }
299
+ </script>
300
+
301
+ <style scoped>
302
+ .disabled{
303
+ background: #ccc !important;
304
+ }
305
+ .chat-content {
306
+ width: 600px;
307
+ height: 650px;
308
+ background: #fff;
309
+ box-shadow: 0 0 10px rgba(0, 0, 0, .2);
310
+ position: relative;
311
+ }
312
+
313
+ .chat-header {
314
+ height: 30px;
315
+ background: rgba(67, 207, 124, 1);
316
+ display: flex;
317
+ line-height: 30px;
318
+ padding: 0 10px;
319
+ box-sizing: border-box;
320
+ color: #fff;
321
+ font-size: 14px;
322
+ position: relative;
323
+ }
324
+ .chat-header-title{
325
+ cursor: move;
326
+ display: inline-block;
327
+ width: calc(100% - 30px);
328
+ margin-right: 30px;
329
+ }
330
+
331
+ .chat-header i {
332
+ cursor: pointer;
333
+ font-size: 18px;
334
+ position: absolute;
335
+ top: 0;
336
+ right: 10px;
337
+ z-index: 1;
338
+ }
339
+
340
+ .chat-in {
341
+ height: calc(100% - 112px);
342
+ overflow-y: auto;
343
+ transition: all 0.5s;
344
+ }
345
+
346
+ .chat-input {
347
+ height: 82px;
348
+ background: rgba(242, 242, 242, 1);
349
+ }
350
+
351
+ .chat-text {
352
+ width: 100%;
353
+ height: 55px;
354
+ padding: 5px;
355
+ box-sizing: border-box;
356
+ }
357
+
358
+ .chat-text textarea {
359
+ width: 100%;
360
+ height: 100%;
361
+ font-family: Helvetica, sans-serif;
362
+ border: none;
363
+ outline: none;
364
+ resize: none;
365
+ background: transparent;
366
+ font-size: 14px;
367
+ }
368
+
369
+ .chat-send {
370
+ padding: 0 5px 5px 5px;
371
+ height: 24px;
372
+ display: flex;
373
+ justify-content: space-between;
374
+ }
375
+
376
+ .chat-send i {
377
+ width: 24px;
378
+ display: inline-block;
379
+ height: 24px;
380
+ box-sizing: border-box;
381
+ border-radius: 50%;
382
+ border: 1px solid #ccc;
383
+ text-align: center;
384
+ line-height: 23px;
385
+ font-size: 14px;
386
+ background: #fff;
387
+ cursor: pointer;
388
+ }
389
+
390
+ .chat-send button {
391
+ background: rgba(67, 207, 124, 1);
392
+ border: none;
393
+ outline: none;
394
+ width: 70px;
395
+ height: 24px;
396
+ color: #fff;
397
+ font-size: 12px;
398
+ cursor: pointer;
399
+ border-radius: 3px;
400
+ text-align-last: justify;
401
+ word-break: break-all;
402
+ text-justify: distribute;
403
+ padding: 0 10px;
404
+ box-sizing: border-box;
405
+ }
406
+ .chat-in-cont{
407
+ padding: 5px 15px;
408
+ }
409
+ .chat-in-li{
410
+ padding: 10px 0;
411
+ }
412
+ .left{
413
+ position: relative;
414
+ min-height: 50px;
415
+ }
416
+ .left .text-cont{
417
+ box-shadow: 0 0 10px rgba(0,0,0,.2);
418
+ background: #fff;
419
+ margin-left: 60px;
420
+ }
421
+ .right{
422
+ text-align: right;
423
+ }
424
+ .right .text-cont{
425
+ background: #fff;
426
+ background: rgba(149, 236, 105, 1);
427
+ }
428
+ .text-cont{
429
+ padding: 10px;
430
+ border-radius: 5px;
431
+ display: inline-block;
432
+ text-align: justify !important;
433
+ max-width: 60%;
434
+ font-size: 14px;
435
+ line-height: 24px;
436
+ white-space: pre-line;
437
+ }
438
+ .ai-avatar{
439
+ position: absolute;
440
+ left: 0;
441
+ top: 10px;
442
+ width: 50px;
443
+ height: 50px;
444
+ border-radius: 50%;
445
+ overflow: hidden;
446
+ box-shadow: 0 0 10px rgba(0,0,0,.2);
447
+ background: #fff;
448
+ padding: 5px;
449
+ box-sizing: border-box;
450
+ }
451
+ .ai-avatar img{
452
+ width: 100%;
453
+ height: 100%;
454
+ }
455
+ .speak{
456
+ background: #43cf7c !important;
457
+ color: #fff !important;
458
+ border: 1px solid #43cf7c !important;
459
+ }
460
+ .microphone{
461
+ position: absolute;
462
+ left: 50%;
463
+ top: 50%;
464
+ z-index: 50;
465
+ width: 150px;
466
+ height: 150px;
467
+ transform: translate(-50%,-50%);
468
+ }
469
+ .microphone-cont{
470
+ width: 100%;
471
+ height: 100%;
472
+ border-radius: 50%;
473
+ position: relative;
474
+ }
475
+ .microphone1{
476
+ width: 100%;
477
+ height: 100%;
478
+ border-radius: 50%;
479
+ background: rgba(67, 207, 124, 0.3);
480
+ }
481
+ .microphone2{
482
+ width: calc(100% - 30px);
483
+ height: calc(100% - 30px);
484
+ border-radius: 50%;
485
+ background: rgba(67, 207, 124, 0.5);
486
+ position: absolute;
487
+ top: 50%;
488
+ left: 50%;
489
+ z-index: 1;
490
+ transform: translate(-50%,-50%);
491
+ }
492
+ .microphone3{
493
+ width: calc(100% - 30px);
494
+ height: calc(100% - 30px);
495
+ border-radius: 50%;
496
+ background: rgba(67, 207, 124, 0.8);
497
+ position: absolute;
498
+ top: 50%;
499
+ left: 50%;
500
+ z-index: 1;
501
+ transform: translate(-50%,-50%);
502
+ text-align: center;
503
+ padding-top: 10px;
504
+ box-sizing: border-box;
505
+ cursor: pointer;
506
+ }
507
+ .microphone3 p{
508
+ font-size: 12px;
509
+ color: #fff;
510
+ line-height: 18px;
511
+ }
512
+ .mic-anim .microphone1{
513
+ animation: mic-anim 1.5s infinite;
514
+ }
515
+ @keyframes mic-anim{
516
+ 0%{
517
+ background: rgba(67, 207, 124, 0.2);
518
+ }
519
+ 80%{
520
+ background: rgba(67, 207, 124, 0.4);
521
+ }
522
+ 100%{
523
+ background: rgba(67, 207, 124, 0.2);
524
+ }
525
+ }
526
+ .mic-anim .microphone2{
527
+ animation: mic-anim2 1.5s infinite;
528
+ }
529
+ @keyframes mic-anim2{
530
+ 0%{
531
+ background: rgba(67, 207, 124, 0.4);
532
+ }
533
+ 40%{
534
+ background: rgba(67, 207, 124, 0.6);
535
+ }
536
+ 80%{
537
+ background: rgba(67, 207, 124, 0.4);
538
+ }
539
+ 100%{
540
+ background: rgba(67, 207, 124, 0.4);
541
+ }
542
+ }
543
+ .mic-anim .microphone3{
544
+ animation: mic-anim3 1.5s infinite;
545
+ }
546
+ @keyframes mic-anim3{
547
+ 0%{
548
+ background: rgba(67, 207, 124, 0.8);
549
+ }
550
+ 20%{
551
+ background: rgba(67, 207, 124, 1);
552
+ }
553
+ 40%{
554
+ background: rgba(67, 207, 124, 0.8);
555
+ }
556
+ 100%{
557
+ background: rgba(67, 207, 124, 0.8);
558
+ }
559
+ }
560
+ </style>
@@ -19,8 +19,10 @@
19
19
  <button v-if="voiceShow" class="buts" @mousedown.stop="speechDown" @click="voice()"
20
20
  :style="{color: (voiceStatus)? '#4B0C77' :'#000'}">{{ voiceStatus ? '关闭语音' : '语音输入' }}
21
21
  </button>
22
+ <button v-if='isShowAiTool' class="buts" @click="showAi">AI助手</button>
22
23
  </div>
23
24
  </div>
25
+ <zydx-chat v-if="isShowAi" @close='isShowAi = false'></zydx-chat>
24
26
  <div class="ed-cont" :style="heightStyleCont">
25
27
  <div :style="{'border': (borderShow)? '1px solid #ccc': '0'}" v-if="editorShow">
26
28
  <editor-content @paste.native.capture.prevent="preventPaste" ref="editorRef" class="editor" :editor="editor"
@@ -162,13 +164,16 @@ import alignmentModeCenter from './img/center.png'
162
164
  import alignmentModeLeft from './img/left.png'
163
165
  import alignmentModeRight from './img/right.png'
164
166
 
167
+ import zydxChat from '../../chat/src/chat.vue'
168
+
165
169
  export default {
166
170
  name: 'zydx-xiao-editor',
167
171
  components: {
168
172
  EditorContent,
169
173
  BubbleMenu,
170
174
  svgs,
171
- Paint
175
+ Paint,
176
+ zydxChat
172
177
  },
173
178
  data() {
174
179
  return {
@@ -202,7 +207,8 @@ export default {
202
207
  editorRef: null,
203
208
  imgSizeVal: '',
204
209
  currentImgDom: {},
205
- paintShow: false
210
+ paintShow: false,
211
+ isShowAi: false,
206
212
  }
207
213
  },
208
214
  beforeUnmount() {
@@ -213,6 +219,10 @@ export default {
213
219
  this.ws = null
214
220
  },
215
221
  props: {
222
+ isShowAiTool: {
223
+ type: Boolean,
224
+ default: true
225
+ },
216
226
  titleVertical: {
217
227
  type: Boolean,
218
228
  default: false
@@ -419,6 +429,9 @@ export default {
419
429
  })
420
430
  },
421
431
  methods: {
432
+ showAi() {
433
+ this.isShowAi = !this.isShowAi
434
+ },
422
435
  exportImg(e) {
423
436
  this.uploadFile(e, this.uploadImage).then(r => {
424
437
  this.paintShow = false
@@ -3,7 +3,7 @@
3
3
  <template #content>
4
4
  <div class="paint" :id="ids">
5
5
  <div class="paint-title">
6
- <span>{{ title }}</span>
6
+ <span>插入白板</span>
7
7
  <div></div>
8
8
  </div>
9
9
  <div class="paint-cont">
@@ -236,6 +236,7 @@ export default {
236
236
  },
237
237
  emits: ['cancel','exportImg'],
238
238
  created() {
239
+ stayTime = 0
239
240
  this.ids = this.randomId()
240
241
  },
241
242
  mounted() {
@@ -551,8 +552,11 @@ export default {
551
552
  this.top = e.clientY - this.topOffset;
552
553
  },
553
554
  mouseup(e) {
554
- console.log(stayTime)
555
555
  clearInterval(timer)
556
+ if (!this.pressDown) {
557
+ stayTime = 0
558
+ return
559
+ }
556
560
  if (stayTime <= 100 && this.pressDown) { // 如果小于100就是点击事件
557
561
  if (!this.toolArr[this.toolIndex]) {
558
562
  stayTime = 0
@@ -583,6 +587,7 @@ export default {
583
587
  return id.replace(/[0-9]/g, '')
584
588
  },
585
589
  cancel() {
590
+ stayTime = 0
586
591
  document.body.style.overflow = 'visible'
587
592
  this.$emit('cancel')
588
593
  }
package/src/index.js CHANGED
@@ -35,6 +35,7 @@ import question from './components/question/index';
35
35
  import tagging from './components/tagging/index';
36
36
  import seek from './components/seek/index';
37
37
  import sketchpad from './components/sketchpad/index';
38
+ import chat from './components/chat/index';
38
39
 
39
40
  const components = [
40
41
  Calendar,
@@ -70,7 +71,8 @@ const components = [
70
71
  question,
71
72
  tagging,
72
73
  seek,
73
- sketchpad
74
+ sketchpad,
75
+ chat
74
76
  ];
75
77
 
76
78
  function install(app) {
@@ -83,7 +85,7 @@ function install(app) {
83
85
  }
84
86
 
85
87
  export default {
86
- version: '1.33.448',
88
+ version: '1.34.450',
87
89
  install,
88
90
  Calendar,
89
91
  Message,
@@ -121,6 +123,7 @@ export default {
121
123
  question,
122
124
  tagging,
123
125
  seek,
124
- sketchpad
126
+ sketchpad,
127
+ chat
125
128
  };
126
129