byt-lingxiao-ai 0.3.28 → 0.3.30

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