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