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