byt-lingxiao-ai 0.3.27 → 0.3.29

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/dist/index.umd.js CHANGED
@@ -66369,7 +66369,7 @@ if (typeof window !== 'undefined') {
66369
66369
  var es_iterator_constructor = __webpack_require__(8111);
66370
66370
  // EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.for-each.js
66371
66371
  var es_iterator_for_each = __webpack_require__(7588);
66372
- ;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-82.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/templateLoader.js??ruleSet[1].rules[3]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/ChatWindow.vue?vue&type=template&id=65473704&scoped=true
66372
+ ;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-82.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/templateLoader.js??ruleSet[1].rules[3]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/ChatWindow.vue?vue&type=template&id=959a9f66&scoped=true
66373
66373
  var render = function render() {
66374
66374
  var _vm = this,
66375
66375
  _c = _vm._self._c;
@@ -66379,16 +66379,16 @@ var render = function render() {
66379
66379
  }, [_c('audio', {
66380
66380
  ref: "audioPlayer",
66381
66381
  staticClass: "hidden-audio",
66382
+ attrs: {
66383
+ "src": _vm.audioSrc,
66384
+ "controls": "",
66385
+ "preload": "auto"
66386
+ },
66382
66387
  on: {
66383
66388
  "timeupdate": _vm.onTimeUpdate,
66384
66389
  "ended": _vm.onAudioEnded
66385
66390
  }
66386
- }, [_c('source', {
66387
- attrs: {
66388
- "src": _vm.audioSrc,
66389
- "type": "audio/mpeg"
66390
- }
66391
- }), _vm._v(" 您的浏览器不支持音频元素。 ")]), _vm.robotStatus !== 'leaving' ? _c('ChatRobot', {
66391
+ }), _vm.robotStatus !== 'leaving' ? _c('ChatRobot', {
66392
66392
  attrs: {
66393
66393
  "status": _vm.robotStatus
66394
66394
  }
@@ -66397,7 +66397,9 @@ var render = function render() {
66397
66397
  "status": _vm.avaterStatus
66398
66398
  },
66399
66399
  on: {
66400
- "mousedown": _vm.startDrag
66400
+ "mousedown": function ($event) {
66401
+ return _vm.startDrag($event);
66402
+ }
66401
66403
  }
66402
66404
  }), _c('ChatWindowDialog', {
66403
66405
  attrs: {
@@ -66422,6 +66424,11 @@ var render = function render() {
66422
66424
  },
66423
66425
  expression: "visible"
66424
66426
  }
66427
+ }), _c('AudioSubtitle', {
66428
+ ref: "audioSubtitle",
66429
+ attrs: {
66430
+ "current-subtitle": _vm.currentSubtitle
66431
+ }
66425
66432
  })], 1);
66426
66433
  };
66427
66434
  var staticRenderFns = [];
@@ -69553,6 +69560,55 @@ var ChatWindowDialog_component = normalizeComponent(
69553
69560
  )
69554
69561
 
69555
69562
  /* harmony default export */ var ChatWindowDialog = (ChatWindowDialog_component.exports);
69563
+ ;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-82.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/templateLoader.js??ruleSet[1].rules[3]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/AudioSubtitle.vue?vue&type=template&id=a3a42172&scoped=true
69564
+ var AudioSubtitlevue_type_template_id_a3a42172_scoped_true_render = function render() {
69565
+ var _vm = this,
69566
+ _c = _vm._self._c;
69567
+ return _c('div', {
69568
+ staticClass: "subtitle-container"
69569
+ }, [_c('div', {
69570
+ staticClass: "subtitle-text"
69571
+ }, [_vm._v(_vm._s(_vm.currentSubtitle))])]);
69572
+ };
69573
+ var AudioSubtitlevue_type_template_id_a3a42172_scoped_true_staticRenderFns = [];
69574
+
69575
+ ;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-82.use[1]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/AudioSubtitle.vue?vue&type=script&lang=js
69576
+ /* harmony default export */ var AudioSubtitlevue_type_script_lang_js = ({
69577
+ props: {
69578
+ currentSubtitle: {
69579
+ type: String,
69580
+ default: ''
69581
+ }
69582
+ }
69583
+ });
69584
+ ;// ./components/AudioSubtitle.vue?vue&type=script&lang=js
69585
+ /* harmony default export */ var components_AudioSubtitlevue_type_script_lang_js = (AudioSubtitlevue_type_script_lang_js);
69586
+ ;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-54.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-54.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-54.use[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/AudioSubtitle.vue?vue&type=style&index=0&id=a3a42172&prod&scoped=true&lang=css
69587
+ // extracted by mini-css-extract-plugin
69588
+
69589
+ ;// ./components/AudioSubtitle.vue?vue&type=style&index=0&id=a3a42172&prod&scoped=true&lang=css
69590
+
69591
+ ;// ./components/AudioSubtitle.vue
69592
+
69593
+
69594
+
69595
+ ;
69596
+
69597
+
69598
+ /* normalize component */
69599
+
69600
+ var AudioSubtitle_component = normalizeComponent(
69601
+ components_AudioSubtitlevue_type_script_lang_js,
69602
+ AudioSubtitlevue_type_template_id_a3a42172_scoped_true_render,
69603
+ AudioSubtitlevue_type_template_id_a3a42172_scoped_true_staticRenderFns,
69604
+ false,
69605
+ null,
69606
+ "a3a42172",
69607
+ null
69608
+
69609
+ )
69610
+
69611
+ /* harmony default export */ var AudioSubtitle = (AudioSubtitle_component.exports);
69556
69612
  // EXTERNAL MODULE: ./node_modules/core-js/modules/es.array-buffer.detached.js
69557
69613
  var es_array_buffer_detached = __webpack_require__(6573);
69558
69614
  // EXTERNAL MODULE: ./node_modules/core-js/modules/es.array-buffer.transfer.js
@@ -69566,23 +69622,92 @@ var es_typed_array_with = __webpack_require__(9577);
69566
69622
 
69567
69623
 
69568
69624
 
69625
+
69626
+
69627
+
69628
+
69629
+
69569
69630
  /* harmony default export */ var audioMixin = ({
69570
69631
  data() {
69571
69632
  return {
69633
+ audioContext: null,
69634
+ workletNode: null,
69635
+ mediaStreamSource: null,
69572
69636
  isRecording: false,
69573
69637
  isMicAvailable: false,
69574
- audioContext: null,
69575
- microphone: null,
69576
- processor: null,
69577
- audioBuffer: new Float32Array(0)
69638
+ audioData: new Float32Array(128)
69578
69639
  };
69579
69640
  },
69580
69641
  methods: {
69642
+ async initAudioWorklet() {
69643
+ if (this.audioContext) return;
69644
+ const AudioContext = window.AudioContext || window.webkitAudioContext;
69645
+ this.audioContext = new AudioContext({
69646
+ sampleRate: this.SAMPLE_RATE
69647
+ });
69648
+ const processorCode = `
69649
+ class RecorderProcessor extends AudioWorkletProcessor {
69650
+ constructor() {
69651
+ super();
69652
+ this.buffer = [];
69653
+ this.frameSize = ${this.FRAME_SIZE};
69654
+ }
69655
+
69656
+ process(inputs) {
69657
+ const input = inputs[0];
69658
+ if (!input || !input[0]) return true;
69659
+
69660
+ const channel = input[0];
69661
+
69662
+ for (let i = 0; i < channel.length; i++) {
69663
+ this.buffer.push(channel[i]);
69664
+ }
69665
+
69666
+ while (this.buffer.length >= this.frameSize) {
69667
+ const frame = this.buffer.splice(0, this.frameSize);
69668
+
69669
+ // PCM
69670
+ const pcm = this.floatTo16BitPCM(frame);
69671
+ this.port.postMessage({
69672
+ type: 'audio',
69673
+ payload: pcm
69674
+ });
69675
+
69676
+ // Visual(下采样)
69677
+ this.port.postMessage({
69678
+ type: 'visual',
69679
+ payload: new Float32Array(frame.slice(0, 128))
69680
+ });
69681
+ }
69682
+ return true;
69683
+ }
69684
+
69685
+ floatTo16BitPCM(float32Array) {
69686
+ const pcm = new Int16Array(float32Array.length);
69687
+ for (let i = 0; i < float32Array.length; i++) {
69688
+ let s = Math.max(-1, Math.min(1, float32Array[i]));
69689
+ pcm[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
69690
+ }
69691
+ return pcm.buffer;
69692
+ }
69693
+ }
69694
+
69695
+ registerProcessor('recorder-processor', RecorderProcessor);
69696
+ `;
69697
+ const blob = new Blob([processorCode], {
69698
+ type: 'application/javascript'
69699
+ });
69700
+ const moduleUrl = URL.createObjectURL(blob);
69701
+ await this.audioContext.audioWorklet.addModule(moduleUrl);
69702
+ this.workletNode = new AudioWorkletNode(this.audioContext, 'recorder-processor');
69703
+ this.workletNode.port.onmessage = this.handleWorkletMessage;
69704
+ },
69581
69705
  async initAudio() {
69582
69706
  if (this.isRecording) return;
69583
69707
  try {
69584
69708
  this.isMicAvailable = true;
69585
- const stream = await navigator.mediaDevices.getUserMedia({
69709
+ await this.initAudioWorklet();
69710
+ this.stream = await navigator.mediaDevices.getUserMedia({
69586
69711
  audio: {
69587
69712
  sampleRate: this.SAMPLE_RATE,
69588
69713
  channelCount: 1,
@@ -69591,103 +69716,95 @@ var es_typed_array_with = __webpack_require__(9577);
69591
69716
  autoGainControl: false
69592
69717
  }
69593
69718
  });
69594
- this.audioContext = new AudioContext({
69595
- sampleRate: this.SAMPLE_RATE
69596
- });
69597
- this.microphone = this.audioContext.createMediaStreamSource(stream);
69598
- this.processor = this.audioContext.createScriptProcessor(this.FRAME_SIZE, 1, 1);
69599
- this.processor.onaudioprocess = this.processAudio;
69600
- this.microphone.connect(this.processor);
69601
- this.processor.connect(this.audioContext.destination);
69719
+ this.mediaStreamSource = this.audioContext.createMediaStreamSource(this.stream);
69720
+ this.mediaStreamSource.connect(this.workletNode);
69721
+ if (this.audioContext.state === 'suspended') {
69722
+ await this.audioContext.resume();
69723
+ }
69602
69724
  this.isRecording = true;
69603
- console.log(`录音中 (采样率: ${this.audioContext.sampleRate}Hz)`);
69604
- } catch (error) {
69605
- console.error("音频初始化失败:", error);
69725
+ console.log(`AudioWorklet 录音中 (${this.SAMPLE_RATE}Hz)`);
69726
+ } catch (e) {
69727
+ console.error('音频初始化失败:', e);
69606
69728
  this.isRecording = false;
69607
69729
  this.isMicAvailable = false;
69608
69730
  }
69609
69731
  },
69610
- processAudio(event) {
69611
- if (!this.isRecording) return;
69612
- const inputData = event.inputBuffer.getChannelData(0);
69613
- const tempBuffer = new Float32Array(this.audioBuffer.length + inputData.length);
69614
- tempBuffer.set(this.audioBuffer, 0);
69615
- tempBuffer.set(inputData, this.audioBuffer.length);
69616
- this.audioBuffer = tempBuffer;
69617
- while (this.audioBuffer.length >= this.FRAME_SIZE) {
69618
- const frame = this.audioBuffer.slice(0, this.FRAME_SIZE);
69619
- this.audioBuffer = this.audioBuffer.slice(this.FRAME_SIZE);
69620
- const pcmData = this.floatTo16BitPCM(frame);
69621
- if (this.ws && this.ws.readyState === this.ws.OPEN) {
69622
- this.ws.send(pcmData);
69732
+ handleWorkletMessage(e) {
69733
+ const {
69734
+ type,
69735
+ payload
69736
+ } = e.data;
69737
+ if (type === 'audio') {
69738
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
69739
+ this.ws.send(payload);
69623
69740
  }
69624
69741
  }
69625
69742
  },
69626
- floatTo16BitPCM(input) {
69627
- const output = new Int16Array(input.length);
69628
- for (let i = 0; i < input.length; i++) {
69629
- const s = Math.max(-1, Math.min(1, input[i]));
69630
- output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
69631
- }
69632
- return output.buffer;
69633
- },
69634
69743
  stopRecording() {
69635
69744
  if (!this.isRecording) return;
69636
- if (this.microphone) this.microphone.disconnect();
69637
- if (this.processor) this.processor.disconnect();
69638
- if (this.analyser) this.analyser.disconnect();
69745
+ if (this.mediaStreamSource) this.mediaStreamSource.disconnect();
69746
+ if (this.workletNode) this.workletNode.disconnect();
69747
+ if (this.stream) {
69748
+ this.stream.getTracks().forEach(t => t.stop());
69749
+ }
69639
69750
  if (this.audioContext) {
69640
- this.audioContext.close().catch(e => {
69641
- console.error("关闭音频上下文失败:", e);
69642
- });
69751
+ this.audioContext.close();
69752
+ this.audioContext = null;
69643
69753
  }
69644
69754
  this.isRecording = false;
69755
+ console.log('录音已停止');
69756
+ },
69757
+ handleWebSocketMessage(data) {
69758
+ if (data.type === 'detection') {
69759
+ if (this.robotStatus === 'speaking') {
69760
+ this.robotStatus = 'waiting';
69761
+ }
69762
+ this.avaterStatus = 'normal';
69763
+ this.startTime = Date.now();
69764
+ }
69765
+ if (data.type === 'Collecting') {
69766
+ this.avaterStatus = 'thinking';
69767
+ }
69768
+ if (data.type === 'command') {
69769
+ if (this.startTime) {
69770
+ console.log(`[Latency] ${Date.now() - this.startTime}ms`);
69771
+ this.startTime = null;
69772
+ }
69773
+ this.analyzeAudioCommand(data.category);
69774
+ }
69645
69775
  },
69646
69776
  play() {
69647
- this.robotStatus = 'speaking';
69648
- this.$refs.audioPlayer.play();
69777
+ this.$refs.audioPlayer?.play();
69649
69778
  },
69650
69779
  pause() {
69651
- console.log('暂停播放');
69652
- this.robotStatus = 'waiting';
69653
- this.$refs.audioPlayer.pause();
69780
+ this.$refs.audioPlayer?.pause();
69654
69781
  },
69655
69782
  stop() {
69656
- this.robotStatus = 'leaving';
69657
- this.$refs.audioPlayer.pause();
69658
- this.$refs.audioPlayer.currentTime = 0;
69659
- this.jumpedTimePoints.clear();
69660
- },
69661
- analyzeAudioCommand(command) {
69662
- console.log('分析音频命令:', command);
69663
- if (command === 'C5') {
69664
- this.robotStatus = 'entering';
69665
- setTimeout(() => {
69666
- this.robotStatus = 'speaking';
69667
- this.play();
69668
- }, 3000);
69669
- } else if (command === 'C8') {
69670
- this.robotStatus = 'speaking';
69671
- this.play();
69672
- } else if (command === 'C7') {
69673
- this.robotStatus = 'waiting';
69674
- this.pause();
69675
- } else if (command === 'C6') {
69676
- this.robotStatus = 'leaving';
69677
- this.avaterStatus = 'normal';
69678
- this.stop();
69679
- }
69783
+ const p = this.$refs.audioPlayer;
69784
+ if (!p) return;
69785
+ p.pause();
69786
+ p.currentTime = 0;
69680
69787
  }
69681
69788
  }
69682
69789
  });
69683
69790
  // EXTERNAL MODULE: ./node_modules/core-js/modules/esnext.json.parse.js
69684
69791
  var esnext_json_parse = __webpack_require__(8335);
69685
69792
  ;// ./components/config/index.js
69686
- const baseUrl = window.location.host;
69793
+ // const baseUrl = window.location.host;
69794
+
69687
69795
  const API_URL = `/lingxiao-byt/api/v1/mcp/ask`; // 对话
69688
- const WS_URL = `ws://${baseUrl}/audio/ws/`; // 语音
69689
- const AUDIO_URL = '/minio/lingxiaoai/byt.mp3'; // 导览
69796
+ // export const WS_URL = `ws://${baseUrl}/audio/ws/`; // 语音
69797
+ const WS_URL = `ws://220.189.237.146:8312/audio/ws/`; // 测试语音
69798
+
69799
+ const AUDIO_URL = '/minio/lingxiaoai/byt.mp3'; // 导览 铜梁
69800
+
69801
+ const COLLECTION = '/minio/lingxiaoai/collection.mp3';
69802
+ const AREA_BOARD = '/minio/lingxiaoai/area_board.mp3'; // 区域看板
69803
+ const ENTERPRISE_APPLICATION = '/minio/lingxiaoai/enterprise_application.mp3'; // 企业应用
69804
+ const REGIONAL_APPLICATION = '/minio/lingxiaoai/regional_application.mp3'; // 区域应用
69805
+
69690
69806
  const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json'; // 语音url跳转节点
69807
+ const SUBTITLE_POINTS_URL = '/minio/lingxiaoai/subTitlePoints.json'; // 字幕跳转节点
69691
69808
  ;// ./components/mixins/webSocketMixin.js
69692
69809
 
69693
69810
 
@@ -69707,11 +69824,7 @@ const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json'; // 语音u
69707
69824
  methods: {
69708
69825
  initWebSocket() {
69709
69826
  try {
69710
- // this.ws = new WebSocket('ws://10.2.233.41:9999');
69711
- // 测试
69712
- // console.log('WS_URL:', WS_URL)
69713
69827
  this.ws = new WebSocket(WS_URL);
69714
- // this.ws = new WebSocket('ws://192.168.8.87/audio/ws/');
69715
69828
  this.ws.binaryType = 'arraybuffer';
69716
69829
  this.ws.onopen = async () => {
69717
69830
  console.log('WebSocket 连接成功');
@@ -69781,12 +69894,15 @@ const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json'; // 语音u
69781
69894
  console.log('状态: 处理中');
69782
69895
 
69783
69896
  // 性能检测终点
69784
- if (this.startTime) {
69785
- const latency = Date.now() - this.startTime;
69786
- console.log(`[Latency] 完整命令处理耗时: ${latency}ms`); // 记录端到端延迟
69787
- this.startTime = null;
69788
- }
69897
+ // if (this.startTime) {
69898
+ // const latency = Date.now() - this.startTime;
69899
+ // console.log(`[Latency] 完整命令处理耗时: ${latency}ms`); // 记录端到端延迟
69900
+ // this.startTime = null;
69901
+ // }
69902
+
69789
69903
  this.analyzeAudioCommand(data.category);
69904
+ } else if (data.type === 'voice') {
69905
+ this.analyzeVoiceCommand(data.category);
69790
69906
  } else {
69791
69907
  console.log('状态: 其他');
69792
69908
  this.avaterStatus = 'normal';
@@ -70104,6 +70220,7 @@ function generateUuid() {
70104
70220
  // 记录耗时
70105
70221
  const duration = Date.now() - startTime;
70106
70222
  if (this.currentMessage) {
70223
+ this.messageLoading = false;
70107
70224
  this.currentMessage.time = (duration / 1000).toFixed(2);
70108
70225
  }
70109
70226
  console.log(`流处理完成,总耗时: ${duration}ms`);
@@ -70111,10 +70228,9 @@ function generateUuid() {
70111
70228
  } catch (error) {
70112
70229
  console.error('发送消息失败:', error);
70113
70230
  if (this.currentMessage) {
70231
+ this.messageLoading = false;
70114
70232
  this.currentMessage.content = '抱歉,发生了错误,请重试。';
70115
70233
  }
70116
- } finally {
70117
- this.messageLoading = false;
70118
70234
  }
70119
70235
  },
70120
70236
  /**
@@ -70180,6 +70296,9 @@ function generateUuid() {
70180
70296
  },
70181
70297
  beforeDestroy() {}
70182
70298
  });
70299
+ ;// ./components/config/blacklist.js
70300
+ const BLACKLIST = ['81010400' // 南方水泥
70301
+ ];
70183
70302
  ;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-82.use[1]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/ChatWindow.vue?vue&type=script&lang=js
70184
70303
 
70185
70304
 
@@ -70199,15 +70318,30 @@ function generateUuid() {
70199
70318
 
70200
70319
 
70201
70320
 
70321
+
70202
70322
  const SAMPLE_RATE = 16000;
70203
70323
  const FRAME_SIZE = 512;
70204
- const startTime = null;
70324
+ const ROBOT_STATUS = {
70325
+ ENTERING: 'entering',
70326
+ // 进入中
70327
+ SPEAKING: 'speaking',
70328
+ // 说话中
70329
+ WAITING: 'waiting',
70330
+ // 等待中
70331
+ LEAVING: 'leaving' // 离开中
70332
+ };
70333
+ const AVATAR_STATUS = {
70334
+ NORMAL: 'normal',
70335
+ // 正常
70336
+ THINKING: 'thinking' // 思考中
70337
+ };
70205
70338
  /* harmony default export */ var ChatWindowvue_type_script_lang_js = ({
70206
70339
  name: 'ChatWindow',
70207
70340
  components: {
70208
70341
  ChatRobot: ChatRobot,
70209
70342
  ChatAvatar: ChatAvatar,
70210
- ChatWindowDialog: ChatWindowDialog
70343
+ ChatWindowDialog: ChatWindowDialog,
70344
+ AudioSubtitle: AudioSubtitle
70211
70345
  },
70212
70346
  mixins: [audioMixin, webSocketMixin, messageMixin],
70213
70347
  props: {
@@ -70218,32 +70352,67 @@ const startTime = null;
70218
70352
  },
70219
70353
  data() {
70220
70354
  return {
70355
+ lastTimeUpdate: null,
70356
+ // 上次更新时间
70221
70357
  chatId: generateUuid(),
70358
+ // 唯一标识当前聊天会话
70222
70359
  audioSrc: AUDIO_URL,
70360
+ // 音频文件URL
70223
70361
  inputMessage: '',
70362
+ // 当前输入的消息
70224
70363
  visible: false,
70364
+ // 是否显示聊天窗口
70225
70365
  messages: [],
70366
+ // 聊天消息数组
70226
70367
  messageLoading: true,
70368
+ // 是否正在加载消息
70227
70369
  robotStatus: 'leaving',
70370
+ // 机器人状态 entering: 进入中, waiting: 等待中, speaking: 说话中, leaving: 离开中
70228
70371
  avaterStatus: 'normal',
70372
+ // 头像状态 normal: 正常, thinking: 思考中
70229
70373
  currentMessage: null,
70374
+ // 当前正在处理的消息
70230
70375
  thinkStatus: true,
70376
+ // 是否思考中
70231
70377
  jumpedTimePoints: new Set(),
70378
+ // 已跳转的时间点集合
70232
70379
  SAMPLE_RATE,
70380
+ // 采样率 16000Hz
70233
70381
  FRAME_SIZE,
70234
- startTime,
70235
- // 检查性能使用
70382
+ // 帧大小 512
70236
70383
  dragThreshold: 5,
70237
70384
  // 拖拽阈值
70238
70385
  isDragging: false,
70386
+ // 是否正在拖拽
70239
70387
  dragStartX: 0,
70388
+ // 拖拽开始时的X坐标
70240
70389
  dragStartY: 0,
70390
+ // 拖拽开始时的Y坐标
70241
70391
  currentX: 10,
70392
+ // 当前X坐标
70242
70393
  currentY: 20,
70394
+ // 当前Y坐标
70243
70395
  initialX: 10,
70396
+ // 初始X坐标
70244
70397
  initialY: 20,
70398
+ // 初始Y坐标
70245
70399
  hasMoved: false,
70246
- timeJumpPoints: []
70400
+ // 是否已移动
70401
+ timeJumpPoints: null,
70402
+ // 跳转
70403
+ subTitlePoints: null,
70404
+ // 字幕
70405
+ currentJumpPoints: [],
70406
+ // 当前跳转点数组
70407
+ currentSubtitles: [],
70408
+ // 当前字幕数组
70409
+ currentSubtitle: '',
70410
+ // 当前字幕,
70411
+ jumpIndex: 0,
70412
+ // 当前跳转索引
70413
+ subtitleIndex: 0,
70414
+ // 当前字幕索引
70415
+ isTourRunning: false // 是否正在导览
70247
70416
  };
70248
70417
  },
70249
70418
  computed: {
@@ -70261,12 +70430,12 @@ const startTime = null;
70261
70430
  };
70262
70431
  }
70263
70432
  },
70264
- mounted() {
70433
+ async mounted() {
70265
70434
  this.initWebSocket();
70266
70435
  if (this.appendToBody) {
70267
70436
  this.appendToBodyHandler();
70268
70437
  }
70269
- this.fetchTimeJumpPoints();
70438
+ await Promise.all([this.fetchTimeJumpPoints(), this.fetchSubTitlePoints()]);
70270
70439
  this.$nextTick(() => {
70271
70440
  const chatEl = this.$el;
70272
70441
  const style = window.getComputedStyle(chatEl);
@@ -70286,16 +70455,27 @@ const startTime = null;
70286
70455
  document.removeEventListener('mouseup', this.stopDrag);
70287
70456
  },
70288
70457
  methods: {
70458
+ initGuide() {
70459
+ this.jumpIndex = 0;
70460
+ this.subtitleIndex = 0;
70461
+ this.jumpedTimePoints.clear();
70462
+ this.setRobotStatus(ROBOT_STATUS.LEAVING);
70463
+ this.setAvatarStatus(AVATAR_STATUS.NORMAL);
70464
+ },
70465
+ setRobotStatus(status) {
70466
+ this.robotStatus = status || ROBOT_STATUS.LEAVING;
70467
+ },
70468
+ setAvatarStatus(status) {
70469
+ this.avaterStatus = status || AVATAR_STATUS.NORMAL;
70470
+ },
70289
70471
  toggleWindow() {
70290
- if (this.avaterStatus === 'thinking') return;
70291
70472
  this.visible = !this.visible;
70292
70473
  if (this.visible) {
70293
70474
  this.currentX = this.initialX;
70294
70475
  this.currentY = this.initialY;
70295
70476
  }
70296
70477
  },
70297
- startDrag() {
70298
- console.log('startDrag');
70478
+ startDrag(event) {
70299
70479
  if (this.robotStatus !== 'leaving' && this.visible) return;
70300
70480
  this.isDragging = true;
70301
70481
  this.hasMoved = false;
@@ -70347,9 +70527,18 @@ const startTime = null;
70347
70527
  document.removeEventListener('mousemove', this.onDrag);
70348
70528
  document.removeEventListener('mouseup', this.stopDrag);
70349
70529
  if (!this.hasMoved) {
70350
- this.toggleWindow();
70530
+ this.handleClick();
70351
70531
  }
70352
70532
  },
70533
+ handleClick() {
70534
+ if (this.avaterStatus === 'thinking') return;
70535
+ if (this.isInBlacklist()) return;
70536
+ this.toggleWindow();
70537
+ },
70538
+ isInBlacklist() {
70539
+ const tenantId = getCookie('bonyear-tenantId');
70540
+ return tenantId && BLACKLIST.includes(tenantId);
70541
+ },
70353
70542
  handleThinkingClick() {
70354
70543
  this.thinkStatus = !this.thinkStatus;
70355
70544
  },
@@ -70365,54 +70554,274 @@ const startTime = null;
70365
70554
  },
70366
70555
  async fetchTimeJumpPoints() {
70367
70556
  try {
70368
- const res = await fetch(TIME_JUMP_POINTS_URL);
70557
+ const res = await fetch(TIME_JUMP_POINTS_URL + '?timestamp=' + Date.now());
70369
70558
  const data = await res.json();
70370
- this.timeJumpPoints = Array.isArray(data) ? data : [];
70371
- console.log('时间跳转点加载完成:', this.timeJumpPoints);
70559
+ this.timeJumpPoints = data;
70372
70560
  } catch (err) {
70373
70561
  console.error('获取时间跳转点失败:', err);
70374
- this.timeJumpPoints = [];
70562
+ this.timeJumpPoints = null;
70563
+ }
70564
+ },
70565
+ async fetchSubTitlePoints() {
70566
+ try {
70567
+ const res = await fetch(SUBTITLE_POINTS_URL + '?timestamp=' + Date.now());
70568
+ const data = await res.json();
70569
+ this.subTitlePoints = data;
70570
+ } catch (err) {
70571
+ console.error('获取字幕跳转点失败');
70572
+ this.subTitlePoints = null;
70573
+ }
70574
+ },
70575
+ normalizeCommand(cmd = '') {
70576
+ return cmd.trim() // 去掉 \n \r 空格
70577
+ .replace(/[\u200B-\u200D]/g, '') // 去零宽字符
70578
+ .replace(/[。!?,、,.!?]/g, ''); // 去标点
70579
+ },
70580
+ // 分析音频语音指令
70581
+ analyzeVoiceCommand(name) {
70582
+ console.log('===== analyzeVoiceCommand =====');
70583
+ console.log('typeof name:', typeof name);
70584
+ console.log('name === "结束导览":', name === '结束导览');
70585
+ console.log('Object.prototype.toString:', Object.prototype.toString.call(name));
70586
+ const normalized = this.normalizeCommand(name);
70587
+ const commandMap = {
70588
+ '开始导览': this.startTheTour,
70589
+ '开始区域看板导览': this.startAreaBoardTour,
70590
+ '开始企业应用导览': this.startEnterpriseApplicationTour,
70591
+ '开始区域应用导览': this.startRegionalApplicationTour,
70592
+ '暂停导览': this.pauseTheTour,
70593
+ '继续导览': this.resumeTheTour,
70594
+ '结束导览': this.offTheTour,
70595
+ '返回首页': this.returnToHome,
70596
+ '未知指令': this.offTheTour
70597
+ };
70598
+ const handler = commandMap[normalized];
70599
+ if (!handler) {
70600
+ console.warn('未定义的指令:', name);
70601
+ return;
70602
+ }
70603
+ handler();
70604
+ },
70605
+ // 启动导览
70606
+ async onTheTour() {
70607
+ if (this.isTourRunning) return;
70608
+ this.isTourRunning = true;
70609
+ const audio = this.$refs.audioPlayer;
70610
+ if (!audio) return;
70611
+ this.setRobotStatus(ROBOT_STATUS.ENTERING);
70612
+ await new Promise(resolve => setTimeout(resolve, 3000));
70613
+ this.setRobotStatus(ROBOT_STATUS.SPEAKING);
70614
+ try {
70615
+ await audio.play();
70616
+ } catch (e) {
70617
+ console.error('播放音频失败:', e);
70618
+ } finally {
70619
+ this.isTourRunning = false;
70620
+ }
70621
+ },
70622
+ // 暂停导览
70623
+ pauseTheTour() {
70624
+ this.pause();
70625
+ this.setRobotStatus(ROBOT_STATUS.WAITING);
70626
+ },
70627
+ // 继续导览
70628
+ resumeTheTour() {
70629
+ this.play();
70630
+ this.setRobotStatus(ROBOT_STATUS.SPEAKING);
70631
+ },
70632
+ // 结束导览
70633
+ offTheTour() {
70634
+ this.stop();
70635
+ this.currentSubtitle = '';
70636
+ this.setRobotStatus(ROBOT_STATUS.LEAVING);
70637
+ this.setAvatarStatus(AVATAR_STATUS.NORMAL);
70638
+ },
70639
+ // 返回首页
70640
+ returnToHome() {
70641
+ this.setRobotStatus(ROBOT_STATUS.LEAVING);
70642
+ this.setAvatarStatus(AVATAR_STATUS.NORMAL);
70643
+ this.$appOptions.router.push({
70644
+ path: '/'
70645
+ });
70646
+ },
70647
+ initJumpPoints(name) {
70648
+ // 初始化跳转点
70649
+ if (name) {
70650
+ this.currentJumpPoints = this.timeJumpPoints[name] || [];
70651
+ console.log('currentJumpPoints:', this.currentJumpPoints);
70652
+ } else {
70653
+ this.currentJumpPoints = this.timeJumpPoints;
70654
+ }
70655
+ },
70656
+ initSubtitles(name) {
70657
+ console.log('name:', name);
70658
+ if (name) {
70659
+ this.currentSubtitles = this.subTitlePoints[name] || [];
70660
+ } else {
70661
+ this.currentSubtitles = this.subTitlePoints;
70662
+ }
70663
+ },
70664
+ // 开始导览
70665
+ async startTheTour() {
70666
+ await this.setAudio(COLLECTION); // 重置音频源
70667
+
70668
+ // 重置指针
70669
+ this.initGuide();
70670
+ // 重置跳转点指针
70671
+ this.initJumpPoints('开始导览');
70672
+ // 重置字幕指针
70673
+ this.initSubtitles('开始导览');
70674
+ await this.onTheTour();
70675
+ },
70676
+ // 开始区域看板导览
70677
+ async startAreaBoardTour() {
70678
+ // 重置audio
70679
+ await this.setAudio(AREA_BOARD);
70680
+
70681
+ // 重置指针
70682
+ this.initGuide();
70683
+ // 重置跳转点指针
70684
+ this.initJumpPoints('开始区域看板导览');
70685
+ // 重置字幕指针
70686
+ this.initSubtitles('开始区域看板导览');
70687
+ await this.onTheTour();
70688
+ },
70689
+ // 开始企业应用导览
70690
+ async startEnterpriseApplicationTour() {
70691
+ await this.setAudio(ENTERPRISE_APPLICATION);
70692
+
70693
+ // 重置指针
70694
+ this.initGuide();
70695
+ // 重置跳转点指针
70696
+ this.initJumpPoints('开始企业应用导览');
70697
+ // 重置字幕指针
70698
+ this.initSubtitles('开始企业应用导览');
70699
+ await this.onTheTour();
70700
+ },
70701
+ // 开始区域应用导览
70702
+ async startRegionalApplicationTour() {
70703
+ await this.setAudio(REGIONAL_APPLICATION);
70704
+
70705
+ // 重置指针
70706
+ this.initGuide();
70707
+ // 重置跳转点指针
70708
+ this.initJumpPoints('开始区域应用导览');
70709
+ // 重置字幕指针
70710
+ this.initSubtitles('开始区域应用导览');
70711
+ await this.onTheTour();
70712
+ },
70713
+ // 分析音频命令
70714
+ analyzeAudioCommand(command) {
70715
+ console.log('分析音频命令:', command);
70716
+ if (command === 'C5') {
70717
+ this.setRobotStatus(ROBOT_STATUS.ENTERING);
70718
+ setTimeout(() => {
70719
+ this.setRobotStatus(ROBOT_STATUS.SPEAKING);
70720
+ this.play();
70721
+ }, 3000);
70722
+ }
70723
+ if (command === 'C8') {
70724
+ this.setRobotStatus(ROBOT_STATUS.SPEAKING);
70725
+ this.play();
70726
+ }
70727
+ if (command === 'C7') {
70728
+ this.setRobotStatus(ROBOT_STATUS.WAITING);
70729
+ this.pause();
70730
+ }
70731
+ if (command === 'C6') {
70732
+ this.setRobotStatus(ROBOT_STATUS.LEAVING);
70733
+ this.stop();
70375
70734
  }
70376
70735
  },
70736
+ // 处理点击跳转时间
70737
+ handleJumpPoint(currentTime) {
70738
+ console.log('jumpIndex:', this.jumpIndex);
70739
+ const point = this.currentJumpPoints[this.jumpIndex];
70740
+ if (!point) return;
70741
+ console.log('currentTime:', currentTime);
70742
+ console.log('point.time:', point.time);
70743
+ if (currentTime >= point.time && !this.jumpedTimePoints.has(point.time)) {
70744
+ this.jumpedTimePoints.add(point.time);
70745
+ this.jumpIndex++;
70746
+ this.$appOptions.store.dispatch('tags/addTagview', {
70747
+ path: point.url,
70748
+ fullPath: point.url,
70749
+ label: point.title,
70750
+ name: point.title,
70751
+ meta: {
70752
+ title: point.title
70753
+ },
70754
+ query: {},
70755
+ params: {}
70756
+ });
70757
+ this.$appOptions.router.push({
70758
+ path: point.url
70759
+ });
70760
+ }
70761
+ },
70762
+ // 处理播放字幕
70763
+ handlePlaySubtitles(currentTime) {
70764
+ const subtitle = this.currentSubtitles[this.subtitleIndex];
70765
+ if (!subtitle) return;
70766
+ if (currentTime < subtitle.start) return;
70767
+
70768
+ // 正在播放当前字幕
70769
+ if (currentTime >= subtitle.start && currentTime <= subtitle.end) {
70770
+ if (this.currentSubtitle !== subtitle.text) {
70771
+ this.currentSubtitle = subtitle.text;
70772
+ }
70773
+ return;
70774
+ }
70775
+
70776
+ // 超过当前字幕,推进游标
70777
+ if (currentTime > subtitle.end) {
70778
+ this.subtitleIndex++;
70779
+ this.currentSubtitle = '';
70780
+ }
70781
+ },
70782
+ async setAudio(src) {
70783
+ return new Promise((resolve, reject) => {
70784
+ const audio = this.$refs.audioPlayer;
70785
+ if (!audio) return reject('未找到音频元素');
70786
+ audio.pause();
70787
+ audio.currentTime = 0;
70788
+
70789
+ // ✅ 用响应式数据
70790
+ this.audioSrc = src;
70791
+ this.$nextTick(() => {
70792
+ audio.load();
70793
+ const onCanPlay = () => {
70794
+ audio.removeEventListener('canplay', onCanPlay);
70795
+ resolve();
70796
+ };
70797
+ audio.addEventListener('canplay', onCanPlay);
70798
+ });
70799
+ });
70800
+ },
70377
70801
  // 音频时间更新处理
70378
70802
  onTimeUpdate() {
70803
+ const now = performance.now();
70804
+ if (this.lastTimeUpdate && now - this.lastTimeUpdate < 200) return;
70805
+ this.lastTimeUpdate = now;
70379
70806
  const audio = this.$refs.audioPlayer;
70380
70807
  const currentTime = audio.currentTime;
70381
- if (!this.timeJumpPoints.length) return;
70382
- this.timeJumpPoints.forEach(point => {
70383
- if (currentTime >= point.time && currentTime < point.time + 1 && !this.jumpedTimePoints.has(point.time)) {
70384
- console.log('触发跳转:', point.url);
70385
- this.jumpedTimePoints.add(point.time);
70386
- this.$appOptions.store.dispatch('tags/addTagview', {
70387
- path: point.url,
70388
- fullPath: point.url,
70389
- label: point.title,
70390
- name: point.title,
70391
- meta: {
70392
- title: point.title
70393
- },
70394
- query: {},
70395
- params: {}
70396
- });
70397
- this.$appOptions.router.push({
70398
- path: point.url
70399
- });
70400
- }
70401
- });
70808
+ this.handleJumpPoint(currentTime);
70809
+ this.handlePlaySubtitles(currentTime);
70402
70810
  },
70403
70811
  onAudioEnded() {
70404
- this.robotStatus = 'leaving';
70405
- this.avaterStatus = 'normal';
70812
+ this.currentSubtitle = '';
70813
+ this.setRobotStatus(ROBOT_STATUS.LEAVING);
70814
+ this.setAvatarStatus(AVATAR_STATUS.NORMAL);
70406
70815
  this.jumpedTimePoints.clear();
70407
70816
  }
70408
70817
  }
70409
70818
  });
70410
70819
  ;// ./components/ChatWindow.vue?vue&type=script&lang=js
70411
70820
  /* harmony default export */ var components_ChatWindowvue_type_script_lang_js = (ChatWindowvue_type_script_lang_js);
70412
- ;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-54.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-54.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-54.use[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/ChatWindow.vue?vue&type=style&index=0&id=65473704&prod&scoped=true&lang=css
70821
+ ;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-54.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-54.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-54.use[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/ChatWindow.vue?vue&type=style&index=0&id=959a9f66&prod&scoped=true&lang=css
70413
70822
  // extracted by mini-css-extract-plugin
70414
70823
 
70415
- ;// ./components/ChatWindow.vue?vue&type=style&index=0&id=65473704&prod&scoped=true&lang=css
70824
+ ;// ./components/ChatWindow.vue?vue&type=style&index=0&id=959a9f66&prod&scoped=true&lang=css
70416
70825
 
70417
70826
  ;// ./components/ChatWindow.vue
70418
70827
 
@@ -70429,7 +70838,7 @@ var ChatWindow_component = normalizeComponent(
70429
70838
  staticRenderFns,
70430
70839
  false,
70431
70840
  null,
70432
- "65473704",
70841
+ "959a9f66",
70433
70842
  null
70434
70843
 
70435
70844
  )