byt-lingxiao-ai 0.2.4 → 0.2.6

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.
@@ -3746,7 +3746,7 @@ if (typeof window !== 'undefined') {
3746
3746
  var es_iterator_constructor = __webpack_require__(8111);
3747
3747
  // EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.for-each.js
3748
3748
  var es_iterator_for_each = __webpack_require__(7588);
3749
- ;// ./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=43521054&scoped=true
3749
+ ;// ./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=d604373c&scoped=true
3750
3750
  var render = function render() {
3751
3751
  var _vm = this,
3752
3752
  _c = _vm._self._c;
@@ -3764,27 +3764,289 @@ var render = function render() {
3764
3764
  "src": _vm.audioSrc,
3765
3765
  "type": "audio/mpeg"
3766
3766
  }
3767
- }), _vm._v(" 您的浏览器不支持音频元素。 ")]), _vm.robotStatus !== 'leaving' ? _c('div', {
3768
- class: ['chat-robot', _vm.robotStatus]
3769
- }) : _c('div', {
3770
- staticClass: "chat-ai",
3767
+ }), _vm._v(" 您的浏览器不支持音频元素。 ")]), _vm.robotStatus !== 'leaving' ? _c('ChatRobot', {
3768
+ attrs: {
3769
+ "status": _vm.robotStatus
3770
+ }
3771
+ }) : _c('ChatAvatar', {
3772
+ attrs: {
3773
+ "status": _vm.avaterStatus
3774
+ },
3771
3775
  on: {
3772
3776
  "click": _vm.toggleWindow
3773
3777
  }
3778
+ }), _c('ChatWindowDialog', {
3779
+ attrs: {
3780
+ "messages": _vm.messages,
3781
+ "input-message": _vm.inputMessage,
3782
+ "think-status": _vm.thinkStatus
3783
+ },
3784
+ on: {
3785
+ "update:inputMessage": function ($event) {
3786
+ _vm.inputMessage = $event;
3787
+ },
3788
+ "send": _vm.handleSend,
3789
+ "thinking-click": _vm.handleThinkingClick,
3790
+ "overlay-click": _vm.handleOverlayClick
3791
+ },
3792
+ model: {
3793
+ value: _vm.visible,
3794
+ callback: function ($$v) {
3795
+ _vm.visible = $$v;
3796
+ },
3797
+ expression: "visible"
3798
+ }
3799
+ })], 1);
3800
+ };
3801
+ var staticRenderFns = [];
3802
+
3803
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.array.push.js
3804
+ var es_array_push = __webpack_require__(4114);
3805
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.difference.v2.js
3806
+ var es_set_difference_v2 = __webpack_require__(7642);
3807
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.intersection.v2.js
3808
+ var es_set_intersection_v2 = __webpack_require__(8004);
3809
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.is-disjoint-from.v2.js
3810
+ var es_set_is_disjoint_from_v2 = __webpack_require__(3853);
3811
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.is-subset-of.v2.js
3812
+ var es_set_is_subset_of_v2 = __webpack_require__(5876);
3813
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.is-superset-of.v2.js
3814
+ var es_set_is_superset_of_v2 = __webpack_require__(2475);
3815
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.symmetric-difference.v2.js
3816
+ var es_set_symmetric_difference_v2 = __webpack_require__(5024);
3817
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.union.v2.js
3818
+ var es_set_union_v2 = __webpack_require__(1698);
3819
+ ;// ./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/ChatRobot.vue?vue&type=template&id=3d936cb2&scoped=true
3820
+ var ChatRobotvue_type_template_id_3d936cb2_scoped_true_render = function render() {
3821
+ var _vm = this,
3822
+ _c = _vm._self._c;
3823
+ return _c('div', {
3824
+ class: ['chat-robot', _vm.status]
3825
+ });
3826
+ };
3827
+ var ChatRobotvue_type_template_id_3d936cb2_scoped_true_staticRenderFns = [];
3828
+
3829
+ ;// ./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/ChatRobot.vue?vue&type=script&lang=js
3830
+ /* harmony default export */ var ChatRobotvue_type_script_lang_js = ({
3831
+ name: 'ChatRobot',
3832
+ props: {
3833
+ status: {
3834
+ type: String,
3835
+ required: true,
3836
+ validator: value => ['entering', 'waiting', 'speaking'].includes(value)
3837
+ }
3838
+ }
3839
+ });
3840
+ ;// ./components/ChatRobot.vue?vue&type=script&lang=js
3841
+ /* harmony default export */ var components_ChatRobotvue_type_script_lang_js = (ChatRobotvue_type_script_lang_js);
3842
+ ;// ./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/ChatRobot.vue?vue&type=style&index=0&id=3d936cb2&prod&scoped=true&lang=css
3843
+ // extracted by mini-css-extract-plugin
3844
+
3845
+ ;// ./components/ChatRobot.vue?vue&type=style&index=0&id=3d936cb2&prod&scoped=true&lang=css
3846
+
3847
+ ;// ./node_modules/@vue/vue-loader-v15/lib/runtime/componentNormalizer.js
3848
+ /* globals __VUE_SSR_CONTEXT__ */
3849
+
3850
+ // IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
3851
+ // This module is a runtime utility for cleaner component module output and will
3852
+ // be included in the final webpack user bundle.
3853
+
3854
+ function normalizeComponent(
3855
+ scriptExports,
3856
+ render,
3857
+ staticRenderFns,
3858
+ functionalTemplate,
3859
+ injectStyles,
3860
+ scopeId,
3861
+ moduleIdentifier /* server only */,
3862
+ shadowMode /* vue-cli only */
3863
+ ) {
3864
+ // Vue.extend constructor export interop
3865
+ var options =
3866
+ typeof scriptExports === 'function' ? scriptExports.options : scriptExports
3867
+
3868
+ // render functions
3869
+ if (render) {
3870
+ options.render = render
3871
+ options.staticRenderFns = staticRenderFns
3872
+ options._compiled = true
3873
+ }
3874
+
3875
+ // functional template
3876
+ if (functionalTemplate) {
3877
+ options.functional = true
3878
+ }
3879
+
3880
+ // scopedId
3881
+ if (scopeId) {
3882
+ options._scopeId = 'data-v-' + scopeId
3883
+ }
3884
+
3885
+ var hook
3886
+ if (moduleIdentifier) {
3887
+ // server build
3888
+ hook = function (context) {
3889
+ // 2.3 injection
3890
+ context =
3891
+ context || // cached call
3892
+ (this.$vnode && this.$vnode.ssrContext) || // stateful
3893
+ (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
3894
+ // 2.2 with runInNewContext: true
3895
+ if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
3896
+ context = __VUE_SSR_CONTEXT__
3897
+ }
3898
+ // inject component styles
3899
+ if (injectStyles) {
3900
+ injectStyles.call(this, context)
3901
+ }
3902
+ // register component module identifier for async chunk inferrence
3903
+ if (context && context._registeredComponents) {
3904
+ context._registeredComponents.add(moduleIdentifier)
3905
+ }
3906
+ }
3907
+ // used by ssr in case component is cached and beforeCreate
3908
+ // never gets called
3909
+ options._ssrRegister = hook
3910
+ } else if (injectStyles) {
3911
+ hook = shadowMode
3912
+ ? function () {
3913
+ injectStyles.call(
3914
+ this,
3915
+ (options.functional ? this.parent : this).$root.$options.shadowRoot
3916
+ )
3917
+ }
3918
+ : injectStyles
3919
+ }
3920
+
3921
+ if (hook) {
3922
+ if (options.functional) {
3923
+ // for template-only hot-reload because in that case the render fn doesn't
3924
+ // go through the normalizer
3925
+ options._injectStyles = hook
3926
+ // register for functional component in vue file
3927
+ var originalRender = options.render
3928
+ options.render = function renderWithStyleInjection(h, context) {
3929
+ hook.call(context)
3930
+ return originalRender(h, context)
3931
+ }
3932
+ } else {
3933
+ // inject component registration as beforeCreate hook
3934
+ var existing = options.beforeCreate
3935
+ options.beforeCreate = existing ? [].concat(existing, hook) : [hook]
3936
+ }
3937
+ }
3938
+
3939
+ return {
3940
+ exports: scriptExports,
3941
+ options: options
3942
+ }
3943
+ }
3944
+
3945
+ ;// ./components/ChatRobot.vue
3946
+
3947
+
3948
+
3949
+ ;
3950
+
3951
+
3952
+ /* normalize component */
3953
+
3954
+ var component = normalizeComponent(
3955
+ components_ChatRobotvue_type_script_lang_js,
3956
+ ChatRobotvue_type_template_id_3d936cb2_scoped_true_render,
3957
+ ChatRobotvue_type_template_id_3d936cb2_scoped_true_staticRenderFns,
3958
+ false,
3959
+ null,
3960
+ "3d936cb2",
3961
+ null
3962
+
3963
+ )
3964
+
3965
+ /* harmony default export */ var ChatRobot = (component.exports);
3966
+ ;// ./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/ChatAvatar.vue?vue&type=template&id=7b09a9c8&scoped=true
3967
+ var ChatAvatarvue_type_template_id_7b09a9c8_scoped_true_render = function render() {
3968
+ var _vm = this,
3969
+ _c = _vm._self._c;
3970
+ return _c('div', {
3971
+ staticClass: "chat-ai",
3972
+ on: {
3973
+ "click": function ($event) {
3974
+ return _vm.$emit('click');
3975
+ }
3976
+ }
3774
3977
  }, [_c('div', {
3775
- staticClass: "chat-ai-avater"
3978
+ class: ['chat-ai-avater', _vm.status]
3776
3979
  }), _c('div', {
3777
3980
  staticClass: "chat-ai-text"
3778
- }, [_vm._v("凌霄AI")])]), _c('div', {
3981
+ }, [_vm._v(_vm._s(_vm.statusText))])]);
3982
+ };
3983
+ var ChatAvatarvue_type_template_id_7b09a9c8_scoped_true_staticRenderFns = [];
3984
+
3985
+ ;// ./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/ChatAvatar.vue?vue&type=script&lang=js
3986
+ /* harmony default export */ var ChatAvatarvue_type_script_lang_js = ({
3987
+ name: 'ChatAvatar',
3988
+ props: {
3989
+ status: {
3990
+ type: String,
3991
+ required: true,
3992
+ validator: value => ['normal', 'thinking', 'output'].includes(value)
3993
+ }
3994
+ },
3995
+ computed: {
3996
+ statusText() {
3997
+ const textMap = {
3998
+ 'normal': '凌霄AI',
3999
+ 'thinking': '思考中',
4000
+ 'output': '语音中'
4001
+ };
4002
+ return textMap[this.status];
4003
+ }
4004
+ }
4005
+ });
4006
+ ;// ./components/ChatAvatar.vue?vue&type=script&lang=js
4007
+ /* harmony default export */ var components_ChatAvatarvue_type_script_lang_js = (ChatAvatarvue_type_script_lang_js);
4008
+ ;// ./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/ChatAvatar.vue?vue&type=style&index=0&id=7b09a9c8&prod&scoped=true&lang=css
4009
+ // extracted by mini-css-extract-plugin
4010
+
4011
+ ;// ./components/ChatAvatar.vue?vue&type=style&index=0&id=7b09a9c8&prod&scoped=true&lang=css
4012
+
4013
+ ;// ./components/ChatAvatar.vue
4014
+
4015
+
4016
+
4017
+ ;
4018
+
4019
+
4020
+ /* normalize component */
4021
+
4022
+ var ChatAvatar_component = normalizeComponent(
4023
+ components_ChatAvatarvue_type_script_lang_js,
4024
+ ChatAvatarvue_type_template_id_7b09a9c8_scoped_true_render,
4025
+ ChatAvatarvue_type_template_id_7b09a9c8_scoped_true_staticRenderFns,
4026
+ false,
4027
+ null,
4028
+ "7b09a9c8",
4029
+ null
4030
+
4031
+ )
4032
+
4033
+ /* harmony default export */ var ChatAvatar = (ChatAvatar_component.exports);
4034
+ ;// ./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/ChatWindowDialog.vue?vue&type=template&id=5eca994a&scoped=true
4035
+ var ChatWindowDialogvue_type_template_id_5eca994a_scoped_true_render = function render() {
4036
+ var _vm = this,
4037
+ _c = _vm._self._c;
4038
+ return _c('div', {
3779
4039
  directives: [{
3780
4040
  name: "show",
3781
4041
  rawName: "v-show",
3782
- value: _vm.visible,
3783
- expression: "visible"
4042
+ value: _vm.value,
4043
+ expression: "value"
3784
4044
  }],
3785
4045
  staticClass: "chat-overlay",
3786
4046
  on: {
3787
- "click": _vm.handleOverlayClick
4047
+ "click": function ($event) {
4048
+ return _vm.$emit('overlay-click');
4049
+ }
3788
4050
  }
3789
4051
  }, [_c('div', {
3790
4052
  staticClass: "chat-window",
@@ -3793,15 +4055,97 @@ var render = function render() {
3793
4055
  $event.stopPropagation();
3794
4056
  }
3795
4057
  }
3796
- }, [_c('div', {
4058
+ }, [_c('ChatWindowHeader', {
4059
+ on: {
4060
+ "close": function ($event) {
4061
+ return _vm.$emit('input', false);
4062
+ }
4063
+ }
4064
+ }), _c('ChatMessageList', {
4065
+ ref: "messageList",
4066
+ attrs: {
4067
+ "messages": _vm.messages,
4068
+ "think-status": _vm.thinkStatus
4069
+ },
4070
+ on: {
4071
+ "thinking-click": function ($event) {
4072
+ return _vm.$emit('thinking-click');
4073
+ }
4074
+ }
4075
+ }), _c('ChatInputBox', {
4076
+ attrs: {
4077
+ "value": _vm.inputMessage
4078
+ },
4079
+ on: {
4080
+ "input": function ($event) {
4081
+ return _vm.$emit('update:inputMessage', $event);
4082
+ },
4083
+ "send": function ($event) {
4084
+ return _vm.$emit('send');
4085
+ }
4086
+ }
4087
+ })], 1)]);
4088
+ };
4089
+ var ChatWindowDialogvue_type_template_id_5eca994a_scoped_true_staticRenderFns = [];
4090
+
4091
+ ;// ./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/ChatWindowHeader.vue?vue&type=template&id=6518e4ca&scoped=true
4092
+ var ChatWindowHeadervue_type_template_id_6518e4ca_scoped_true_render = function render() {
4093
+ var _vm = this,
4094
+ _c = _vm._self._c;
4095
+ return _c('div', {
3797
4096
  staticClass: "chat-window-header"
3798
4097
  }, [_c('div', {
3799
4098
  staticClass: "chat-window-header-title"
3800
- }, [_vm._v("凌霄大模型AI对话")]), _c('div', {
4099
+ }, [_vm._v("凌霄大模型AI123对话")]), _c('div', {
4100
+ staticClass: "chat-window-header-open",
4101
+ on: {
4102
+ "click": _vm.handleOpen
4103
+ }
4104
+ }, [_c('svg', {
4105
+ attrs: {
4106
+ "xmlns": "http://www.w3.org/2000/svg",
4107
+ "width": "24",
4108
+ "height": "24",
4109
+ "viewBox": "0 0 24 24",
4110
+ "fill": "none"
4111
+ }
4112
+ }, [_c('path', {
4113
+ attrs: {
4114
+ "d": "M14.5 4.5H19.5V9.5",
4115
+ "stroke": "#4E5969",
4116
+ "stroke-width": "1.66667",
4117
+ "stroke-linecap": "round",
4118
+ "stroke-linejoin": "round"
4119
+ }
4120
+ }), _c('path', {
4121
+ attrs: {
4122
+ "d": "M9.5 19.5H4.5V14.5",
4123
+ "stroke": "#4E5969",
4124
+ "stroke-width": "1.66667",
4125
+ "stroke-linecap": "round",
4126
+ "stroke-linejoin": "round"
4127
+ }
4128
+ }), _c('path', {
4129
+ attrs: {
4130
+ "d": "M19.5 4.5L14.0833 9.91667",
4131
+ "stroke": "#4E5969",
4132
+ "stroke-width": "1.66667",
4133
+ "stroke-linecap": "round",
4134
+ "stroke-linejoin": "round"
4135
+ }
4136
+ }), _c('path', {
4137
+ attrs: {
4138
+ "d": "M9.91667 14.083L4.5 19.4997",
4139
+ "stroke": "#4E5969",
4140
+ "stroke-width": "1.66667",
4141
+ "stroke-linecap": "round",
4142
+ "stroke-linejoin": "round"
4143
+ }
4144
+ })])]), _c('div', {
3801
4145
  staticClass: "chat-window-header-close",
3802
4146
  on: {
3803
4147
  "click": function ($event) {
3804
- _vm.visible = false;
4148
+ return _vm.$emit('close');
3805
4149
  }
3806
4150
  }
3807
4151
  }, [_c('svg', {
@@ -3828,23 +4172,268 @@ var render = function render() {
3828
4172
  "stroke-linecap": "round",
3829
4173
  "stroke-linejoin": "round"
3830
4174
  }
3831
- })])])]), _c('div', {
4175
+ })])])]);
4176
+ };
4177
+ var ChatWindowHeadervue_type_template_id_6518e4ca_scoped_true_staticRenderFns = [];
4178
+
4179
+ ;// ./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/ChatWindowHeader.vue?vue&type=script&lang=js
4180
+ /* harmony default export */ var ChatWindowHeadervue_type_script_lang_js = ({
4181
+ name: 'ChatWindowHeader',
4182
+ methods: {
4183
+ handleOpen() {
4184
+ const path = this.$router.resolve({
4185
+ name: '/chat'
4186
+ });
4187
+ window.open(path.href, '_blank');
4188
+ }
4189
+ }
4190
+ });
4191
+ ;// ./components/ChatWindowHeader.vue?vue&type=script&lang=js
4192
+ /* harmony default export */ var components_ChatWindowHeadervue_type_script_lang_js = (ChatWindowHeadervue_type_script_lang_js);
4193
+ ;// ./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/ChatWindowHeader.vue?vue&type=style&index=0&id=6518e4ca&prod&scoped=true&lang=css
4194
+ // extracted by mini-css-extract-plugin
4195
+
4196
+ ;// ./components/ChatWindowHeader.vue?vue&type=style&index=0&id=6518e4ca&prod&scoped=true&lang=css
4197
+
4198
+ ;// ./components/ChatWindowHeader.vue
4199
+
4200
+
4201
+
4202
+ ;
4203
+
4204
+
4205
+ /* normalize component */
4206
+
4207
+ var ChatWindowHeader_component = normalizeComponent(
4208
+ components_ChatWindowHeadervue_type_script_lang_js,
4209
+ ChatWindowHeadervue_type_template_id_6518e4ca_scoped_true_render,
4210
+ ChatWindowHeadervue_type_template_id_6518e4ca_scoped_true_staticRenderFns,
4211
+ false,
4212
+ null,
4213
+ "6518e4ca",
4214
+ null
4215
+
4216
+ )
4217
+
4218
+ /* harmony default export */ var ChatWindowHeader = (ChatWindowHeader_component.exports);
4219
+ ;// ./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/ChatMessageList.vue?vue&type=template&id=ba7356ea&scoped=true
4220
+ var ChatMessageListvue_type_template_id_ba7356ea_scoped_true_render = function render() {
4221
+ var _vm = this,
4222
+ _c = _vm._self._c;
4223
+ return _c('div', {
3832
4224
  ref: "chatArea",
3833
4225
  staticClass: "chat-window-content scrollbar-hide"
3834
4226
  }, _vm._l(_vm.messages, function (message) {
3835
4227
  return _c('div', {
3836
4228
  key: message.id,
3837
4229
  staticClass: "chat-window-message"
3838
- }, [message.type === 'user' ? _c('div', {
3839
- staticClass: "chat-window-message-user"
3840
- }, [_c('div', {
3841
- staticClass: "user-message"
3842
- }, [_vm._v(_vm._s(message.content))])]) : _c('div', {
3843
- staticClass: "chat-window-message-ai"
3844
- }, [_c('div', {
3845
- staticClass: "ai-message"
3846
- }, [_vm._v(_vm._s(message.content))])])]);
3847
- }), 0), _c('div', {
4230
+ }, [message.type === 'user' ? _c('UserMessage', {
4231
+ attrs: {
4232
+ "content": message.content
4233
+ }
4234
+ }) : _c('AiMessage', {
4235
+ attrs: {
4236
+ "message": message,
4237
+ "think-status": _vm.thinkStatus
4238
+ },
4239
+ on: {
4240
+ "thinking-click": function ($event) {
4241
+ return _vm.$emit('thinking-click');
4242
+ }
4243
+ }
4244
+ })], 1);
4245
+ }), 0);
4246
+ };
4247
+ var ChatMessageListvue_type_template_id_ba7356ea_scoped_true_staticRenderFns = [];
4248
+
4249
+ ;// ./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/UserMessage.vue?vue&type=template&id=6a2b6167&scoped=true
4250
+ var UserMessagevue_type_template_id_6a2b6167_scoped_true_render = function render() {
4251
+ var _vm = this,
4252
+ _c = _vm._self._c;
4253
+ return _c('div', {
4254
+ staticClass: "chat-window-message-user"
4255
+ }, [_c('div', {
4256
+ staticClass: "user-message"
4257
+ }, [_vm._v(_vm._s(_vm.content))])]);
4258
+ };
4259
+ var UserMessagevue_type_template_id_6a2b6167_scoped_true_staticRenderFns = [];
4260
+
4261
+ ;// ./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/UserMessage.vue?vue&type=script&lang=js
4262
+ /* harmony default export */ var UserMessagevue_type_script_lang_js = ({
4263
+ name: 'UserMessage',
4264
+ props: {
4265
+ content: {
4266
+ type: String,
4267
+ required: true
4268
+ }
4269
+ }
4270
+ });
4271
+ ;// ./components/UserMessage.vue?vue&type=script&lang=js
4272
+ /* harmony default export */ var components_UserMessagevue_type_script_lang_js = (UserMessagevue_type_script_lang_js);
4273
+ ;// ./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/UserMessage.vue?vue&type=style&index=0&id=6a2b6167&prod&scoped=true&lang=css
4274
+ // extracted by mini-css-extract-plugin
4275
+
4276
+ ;// ./components/UserMessage.vue?vue&type=style&index=0&id=6a2b6167&prod&scoped=true&lang=css
4277
+
4278
+ ;// ./components/UserMessage.vue
4279
+
4280
+
4281
+
4282
+ ;
4283
+
4284
+
4285
+ /* normalize component */
4286
+
4287
+ var UserMessage_component = normalizeComponent(
4288
+ components_UserMessagevue_type_script_lang_js,
4289
+ UserMessagevue_type_template_id_6a2b6167_scoped_true_render,
4290
+ UserMessagevue_type_template_id_6a2b6167_scoped_true_staticRenderFns,
4291
+ false,
4292
+ null,
4293
+ "6a2b6167",
4294
+ null
4295
+
4296
+ )
4297
+
4298
+ /* harmony default export */ var UserMessage = (UserMessage_component.exports);
4299
+ ;// ./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/AiMessage.vue?vue&type=template&id=3347daf6&scoped=true
4300
+ var AiMessagevue_type_template_id_3347daf6_scoped_true_render = function render() {
4301
+ var _vm = this,
4302
+ _c = _vm._self._c;
4303
+ return _c('div', {
4304
+ staticClass: "chat-window-message-ai"
4305
+ }, [_c('div', {
4306
+ staticClass: "ai-render"
4307
+ }, [_c('div', {
4308
+ staticClass: "ai-thinking",
4309
+ on: {
4310
+ "click": function ($event) {
4311
+ return _vm.$emit('thinking-click');
4312
+ }
4313
+ }
4314
+ }, [_c('div', {
4315
+ staticClass: "ai-thinking-time"
4316
+ }, [_vm._v("思考用时" + _vm._s(_vm.message.time) + "秒")]), _vm.thinkStatus ? _c('div', {
4317
+ staticClass: "ai-thinking-content"
4318
+ }, [_vm._v(_vm._s(_vm.message.thinking))]) : _vm._e()]), _c('div', {
4319
+ staticClass: "ai-content"
4320
+ }, [_vm._v(_vm._s(_vm.message.content))])])]);
4321
+ };
4322
+ var AiMessagevue_type_template_id_3347daf6_scoped_true_staticRenderFns = [];
4323
+
4324
+ ;// ./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/AiMessage.vue?vue&type=script&lang=js
4325
+ /* harmony default export */ var AiMessagevue_type_script_lang_js = ({
4326
+ name: 'AiMessage',
4327
+ props: {
4328
+ message: {
4329
+ type: Object,
4330
+ required: true
4331
+ },
4332
+ thinkStatus: {
4333
+ type: Boolean,
4334
+ default: true
4335
+ }
4336
+ }
4337
+ });
4338
+ ;// ./components/AiMessage.vue?vue&type=script&lang=js
4339
+ /* harmony default export */ var components_AiMessagevue_type_script_lang_js = (AiMessagevue_type_script_lang_js);
4340
+ ;// ./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/AiMessage.vue?vue&type=style&index=0&id=3347daf6&prod&scoped=true&lang=css
4341
+ // extracted by mini-css-extract-plugin
4342
+
4343
+ ;// ./components/AiMessage.vue?vue&type=style&index=0&id=3347daf6&prod&scoped=true&lang=css
4344
+
4345
+ ;// ./components/AiMessage.vue
4346
+
4347
+
4348
+
4349
+ ;
4350
+
4351
+
4352
+ /* normalize component */
4353
+
4354
+ var AiMessage_component = normalizeComponent(
4355
+ components_AiMessagevue_type_script_lang_js,
4356
+ AiMessagevue_type_template_id_3347daf6_scoped_true_render,
4357
+ AiMessagevue_type_template_id_3347daf6_scoped_true_staticRenderFns,
4358
+ false,
4359
+ null,
4360
+ "3347daf6",
4361
+ null
4362
+
4363
+ )
4364
+
4365
+ /* harmony default export */ var AiMessage = (AiMessage_component.exports);
4366
+ ;// ./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/ChatMessageList.vue?vue&type=script&lang=js
4367
+
4368
+
4369
+ /* harmony default export */ var ChatMessageListvue_type_script_lang_js = ({
4370
+ name: 'ChatMessageList',
4371
+ components: {
4372
+ UserMessage: UserMessage,
4373
+ AiMessage: AiMessage
4374
+ },
4375
+ props: {
4376
+ messages: {
4377
+ type: Array,
4378
+ required: true
4379
+ },
4380
+ thinkStatus: {
4381
+ type: Boolean,
4382
+ default: true
4383
+ }
4384
+ },
4385
+ methods: {
4386
+ scrollToBottom() {
4387
+ this.$nextTick(() => {
4388
+ const chatArea = this.$refs.chatArea;
4389
+ if (chatArea) {
4390
+ chatArea.scrollTop = chatArea.scrollHeight;
4391
+ }
4392
+ });
4393
+ }
4394
+ },
4395
+ watch: {
4396
+ messages: {
4397
+ handler() {
4398
+ this.scrollToBottom();
4399
+ },
4400
+ deep: true
4401
+ }
4402
+ }
4403
+ });
4404
+ ;// ./components/ChatMessageList.vue?vue&type=script&lang=js
4405
+ /* harmony default export */ var components_ChatMessageListvue_type_script_lang_js = (ChatMessageListvue_type_script_lang_js);
4406
+ ;// ./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/ChatMessageList.vue?vue&type=style&index=0&id=ba7356ea&prod&scoped=true&lang=css
4407
+ // extracted by mini-css-extract-plugin
4408
+
4409
+ ;// ./components/ChatMessageList.vue?vue&type=style&index=0&id=ba7356ea&prod&scoped=true&lang=css
4410
+
4411
+ ;// ./components/ChatMessageList.vue
4412
+
4413
+
4414
+
4415
+ ;
4416
+
4417
+
4418
+ /* normalize component */
4419
+
4420
+ var ChatMessageList_component = normalizeComponent(
4421
+ components_ChatMessageListvue_type_script_lang_js,
4422
+ ChatMessageListvue_type_template_id_ba7356ea_scoped_true_render,
4423
+ ChatMessageListvue_type_template_id_ba7356ea_scoped_true_staticRenderFns,
4424
+ false,
4425
+ null,
4426
+ "ba7356ea",
4427
+ null
4428
+
4429
+ )
4430
+
4431
+ /* harmony default export */ var ChatMessageList = (ChatMessageList_component.exports);
4432
+ ;// ./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/ChatInputBox.vue?vue&type=template&id=6f416073&scoped=true
4433
+ var ChatInputBoxvue_type_template_id_6f416073_scoped_true_render = function render() {
4434
+ var _vm = this,
4435
+ _c = _vm._self._c;
4436
+ return _c('div', {
3848
4437
  staticClass: "chat-window-footer"
3849
4438
  }, [_c('div', {
3850
4439
  staticClass: "chat-window-textarea"
@@ -3854,24 +4443,27 @@ var render = function render() {
3854
4443
  "type": "textarea",
3855
4444
  "placeholder": "有什么我能帮您的吗?",
3856
4445
  "rows": "2",
3857
- "resize": "none"
4446
+ "resize": "none",
4447
+ "value": _vm.value
3858
4448
  },
3859
4449
  on: {
3860
- "keydown": _vm.handleKeyDown
4450
+ "input": function ($event) {
4451
+ return _vm.$emit('input', $event);
4452
+ }
3861
4453
  },
3862
- model: {
3863
- value: _vm.inputMessage,
3864
- callback: function ($$v) {
3865
- _vm.inputMessage = $$v;
3866
- },
3867
- expression: "inputMessage"
4454
+ nativeOn: {
4455
+ "keydown": function ($event) {
4456
+ return _vm.handleKeyDown.apply(null, arguments);
4457
+ }
3868
4458
  }
3869
4459
  }), _c('div', {
3870
4460
  staticClass: "chat-window-bar"
3871
4461
  }, [_c('div', {
3872
4462
  staticClass: "chat-window-send",
3873
4463
  on: {
3874
- "click": _vm.handleSend
4464
+ "click": function ($event) {
4465
+ return _vm.$emit('send');
4466
+ }
3875
4467
  }
3876
4468
  }, [_c('svg', {
3877
4469
  attrs: {
@@ -3910,187 +4502,202 @@ var render = function render() {
3910
4502
  "height": "20",
3911
4503
  "fill": "white"
3912
4504
  }
3913
- })])])])])])], 1)])])])]);
4505
+ })])])])])])], 1)]);
3914
4506
  };
3915
- var staticRenderFns = [];
4507
+ var ChatInputBoxvue_type_template_id_6f416073_scoped_true_staticRenderFns = [];
3916
4508
 
3917
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.array.push.js
3918
- var es_array_push = __webpack_require__(4114);
3919
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.array-buffer.detached.js
3920
- var es_array_buffer_detached = __webpack_require__(6573);
3921
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.array-buffer.transfer.js
3922
- var es_array_buffer_transfer = __webpack_require__(8100);
3923
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.array-buffer.transfer-to-fixed-length.js
3924
- var es_array_buffer_transfer_to_fixed_length = __webpack_require__(7936);
3925
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.difference.v2.js
3926
- var es_set_difference_v2 = __webpack_require__(7642);
3927
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.intersection.v2.js
3928
- var es_set_intersection_v2 = __webpack_require__(8004);
3929
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.is-disjoint-from.v2.js
3930
- var es_set_is_disjoint_from_v2 = __webpack_require__(3853);
3931
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.is-subset-of.v2.js
3932
- var es_set_is_subset_of_v2 = __webpack_require__(5876);
3933
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.is-superset-of.v2.js
3934
- var es_set_is_superset_of_v2 = __webpack_require__(2475);
3935
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.symmetric-difference.v2.js
3936
- var es_set_symmetric_difference_v2 = __webpack_require__(5024);
3937
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.set.union.v2.js
3938
- var es_set_union_v2 = __webpack_require__(1698);
3939
- // EXTERNAL MODULE: ./node_modules/core-js/modules/es.typed-array.with.js
3940
- var es_typed_array_with = __webpack_require__(9577);
3941
- ;// ./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
4509
+ ;// ./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/ChatInputBox.vue?vue&type=script&lang=js
4510
+ /* harmony default export */ var ChatInputBoxvue_type_script_lang_js = ({
4511
+ name: 'ChatInputBox',
4512
+ props: {
4513
+ value: {
4514
+ type: String,
4515
+ default: ''
4516
+ }
4517
+ },
4518
+ methods: {
4519
+ handleKeyDown(e) {
4520
+ if (e.key === 'Enter' && !e.shiftKey) {
4521
+ e.preventDefault();
4522
+ this.$emit('send');
4523
+ }
4524
+ }
4525
+ }
4526
+ });
4527
+ ;// ./components/ChatInputBox.vue?vue&type=script&lang=js
4528
+ /* harmony default export */ var components_ChatInputBoxvue_type_script_lang_js = (ChatInputBoxvue_type_script_lang_js);
4529
+ ;// ./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/ChatInputBox.vue?vue&type=style&index=0&id=6f416073&prod&scoped=true&lang=css
4530
+ // extracted by mini-css-extract-plugin
3942
4531
 
4532
+ ;// ./components/ChatInputBox.vue?vue&type=style&index=0&id=6f416073&prod&scoped=true&lang=css
3943
4533
 
4534
+ ;// ./components/ChatInputBox.vue
3944
4535
 
3945
4536
 
3946
4537
 
4538
+ ;
3947
4539
 
3948
4540
 
4541
+ /* normalize component */
3949
4542
 
4543
+ var ChatInputBox_component = normalizeComponent(
4544
+ components_ChatInputBoxvue_type_script_lang_js,
4545
+ ChatInputBoxvue_type_template_id_6f416073_scoped_true_render,
4546
+ ChatInputBoxvue_type_template_id_6f416073_scoped_true_staticRenderFns,
4547
+ false,
4548
+ null,
4549
+ "6f416073",
4550
+ null
4551
+
4552
+ )
3950
4553
 
4554
+ /* harmony default export */ var ChatInputBox = (ChatInputBox_component.exports);
4555
+ ;// ./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/ChatWindowDialog.vue?vue&type=script&lang=js
3951
4556
 
3952
4557
 
3953
4558
 
3954
- const SAMPLE_RATE = 16000;
3955
- const FRAME_SIZE = 512;
3956
- /* harmony default export */ var ChatWindowvue_type_script_lang_js = ({
3957
- name: 'ChatWindow',
4559
+ /* harmony default export */ var ChatWindowDialogvue_type_script_lang_js = ({
4560
+ name: 'ChatWindowDialog',
4561
+ components: {
4562
+ ChatWindowHeader: ChatWindowHeader,
4563
+ ChatMessageList: ChatMessageList,
4564
+ ChatInputBox: ChatInputBox
4565
+ },
3958
4566
  props: {
3959
- appendToBody: {
4567
+ value: {
4568
+ type: Boolean,
4569
+ required: true
4570
+ },
4571
+ messages: {
4572
+ type: Array,
4573
+ required: true
4574
+ },
4575
+ inputMessage: {
4576
+ type: String,
4577
+ default: ''
4578
+ },
4579
+ thinkStatus: {
3960
4580
  type: Boolean,
3961
4581
  default: true
3962
4582
  }
3963
- },
4583
+ }
4584
+ });
4585
+ ;// ./components/ChatWindowDialog.vue?vue&type=script&lang=js
4586
+ /* harmony default export */ var components_ChatWindowDialogvue_type_script_lang_js = (ChatWindowDialogvue_type_script_lang_js);
4587
+ ;// ./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/ChatWindowDialog.vue?vue&type=style&index=0&id=5eca994a&prod&scoped=true&lang=css
4588
+ // extracted by mini-css-extract-plugin
4589
+
4590
+ ;// ./components/ChatWindowDialog.vue?vue&type=style&index=0&id=5eca994a&prod&scoped=true&lang=css
4591
+
4592
+ ;// ./components/ChatWindowDialog.vue
4593
+
4594
+
4595
+
4596
+ ;
4597
+
4598
+
4599
+ /* normalize component */
4600
+
4601
+ var ChatWindowDialog_component = normalizeComponent(
4602
+ components_ChatWindowDialogvue_type_script_lang_js,
4603
+ ChatWindowDialogvue_type_template_id_5eca994a_scoped_true_render,
4604
+ ChatWindowDialogvue_type_template_id_5eca994a_scoped_true_staticRenderFns,
4605
+ false,
4606
+ null,
4607
+ "5eca994a",
4608
+ null
4609
+
4610
+ )
4611
+
4612
+ /* harmony default export */ var ChatWindowDialog = (ChatWindowDialog_component.exports);
4613
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.array-buffer.detached.js
4614
+ var es_array_buffer_detached = __webpack_require__(6573);
4615
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.array-buffer.transfer.js
4616
+ var es_array_buffer_transfer = __webpack_require__(8100);
4617
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.array-buffer.transfer-to-fixed-length.js
4618
+ var es_array_buffer_transfer_to_fixed_length = __webpack_require__(7936);
4619
+ // EXTERNAL MODULE: ./node_modules/core-js/modules/es.typed-array.with.js
4620
+ var es_typed_array_with = __webpack_require__(9577);
4621
+ ;// ./components/mixins/audioMixin.js
4622
+
4623
+
4624
+
4625
+
4626
+ /* harmony default export */ var audioMixin = ({
3964
4627
  data() {
3965
4628
  return {
3966
- audioSrc: '/minio/lingxiaoai/byt.mp3',
3967
- // 音频URL
3968
- inputMessage: '',
3969
- // 输入消息
3970
- visible: false,
3971
- // 窗口是否可见
3972
- messages: [{
3973
- id: 1,
3974
- type: 'user',
3975
- sender: '',
3976
- time: '',
3977
- content: '你好,欢迎来到凌霄大模型AI对话。'
3978
- }, {
3979
- id: 2,
3980
- type: 'ai',
3981
- sender: 'AI',
3982
- time: '',
3983
- content: '欢迎来到凌霄大模型AI对话。'
3984
- }, {
3985
- id: 3,
3986
- type: 'ai',
3987
- sender: 'AI',
3988
- time: '',
3989
- content: '请输入您的问题。'
3990
- }, {
3991
- id: 4,
3992
- type: 'user',
3993
- sender: '用户',
3994
- time: '',
3995
- content: '你好,欢迎来到凌霄大模型AI对话。'
3996
- }],
3997
- // 消息列表
3998
4629
  isRecording: false,
3999
- // 正在录制
4000
4630
  isMicAvailable: false,
4001
- // 麦克风是否可用
4002
- mediaRecorder: null,
4003
- // 媒体录制器
4004
- ws: null,
4005
- // WebSocket连接
4006
- // wsUrl: 'wss://mes.shnfonline.top:8316/ai_model/ws/voice-stream', // WebSocket URL
4007
- wsUrl: 'ws://192.168.8.9:9999/ai_model/ws/voice-stream',
4008
- // WebSocket URL
4009
- isConnected: false,
4010
- // WebSocket是否已连接
4011
4631
  audioContext: null,
4012
- // 音频上下文
4013
4632
  microphone: null,
4014
- // 麦克风输入节点
4015
4633
  processor: null,
4016
- // 音频处理节点
4017
- robotStatus: 'leaving',
4018
- // 机器人状态 waiting, speaking, leaving, entering
4019
- audioBuffer: new Float32Array(0) // 音频缓冲区
4634
+ audioBuffer: new Float32Array(0)
4020
4635
  };
4021
4636
  },
4022
- mounted() {
4023
- this.initWebSocket();
4024
-
4025
- // 处理append-to-body逻辑
4026
- if (this.appendToBody) {
4027
- this.appendToBodyHandler();
4028
- }
4029
- },
4030
- unmounted() {
4031
- this.closeWebSocket();
4032
- this.stopRecording();
4033
- },
4034
- beforeDestroy() {
4035
- // 组件销毁前,如果元素被移动到body中,需要移除
4036
- if (this.appendToBody && this.$el.parentElement === document.body) {
4037
- document.body.removeChild(this.$el);
4038
- }
4039
- this.closeWebSocket();
4040
- this.stopRecording();
4041
- },
4042
4637
  methods: {
4043
- // 处理音频播放结束事件
4044
- onAudioEnded() {
4045
- this.robotStatus = 'leaving';
4046
- this.jumpedTimePoints.clear();
4047
- },
4048
- // 处理音频播放时间更新事件
4049
- onTimeUpdate() {
4050
- const audio = this.$refs.audioPlayer;
4051
- const currentTime = audio.currentTime;
4052
- if (!this.jumpedTimePoints) {
4053
- this.jumpedTimePoints = new Set();
4638
+ async initAudio() {
4639
+ if (this.isRecording) return;
4640
+ try {
4641
+ this.isMicAvailable = true;
4642
+ const stream = await navigator.mediaDevices.getUserMedia({
4643
+ audio: {
4644
+ sampleRate: this.SAMPLE_RATE,
4645
+ channelCount: 1,
4646
+ noiseSuppression: true,
4647
+ echoCancellation: true
4648
+ }
4649
+ });
4650
+ this.audioContext = new AudioContext({
4651
+ sampleRate: this.SAMPLE_RATE
4652
+ });
4653
+ this.microphone = this.audioContext.createMediaStreamSource(stream);
4654
+ this.processor = this.audioContext.createScriptProcessor(this.FRAME_SIZE, 1, 1);
4655
+ this.processor.onaudioprocess = this.processAudio;
4656
+ this.microphone.connect(this.processor);
4657
+ this.processor.connect(this.audioContext.destination);
4658
+ this.isRecording = true;
4659
+ console.log(`录音中 (采样率: ${this.audioContext.sampleRate}Hz)`);
4660
+ } catch (error) {
4661
+ console.error("音频初始化失败:", error);
4662
+ this.isRecording = false;
4663
+ this.isMicAvailable = false;
4054
4664
  }
4055
- const timeJumpPoints = [{
4056
- time: 40,
4057
- url: '/permission/user',
4058
- name: 'permission_user',
4059
- title: '用户管理'
4060
- }, {
4061
- time: 65,
4062
- url: '/permission/menu',
4063
- name: 'permission_menu',
4064
- title: '菜单管理'
4065
- }, {
4066
- time: 75,
4067
- url: '/permission/role',
4068
- name: 'permission_role',
4069
- title: '角色管理'
4070
- }];
4071
- // 检查当前时间是否达到跳转点
4072
- timeJumpPoints.forEach(point => {
4073
- // 使用一定的误差范围,确保不会因为播放进度的微小差异而错过跳转点
4074
- if (currentTime >= point.time && currentTime < point.time + 0.5 && !this.jumpedTimePoints.has(point.time)) {
4075
- this.jumpedTimePoints.add(point.time);
4076
- console.log(`到达时间点 ${point.time} 秒,跳转到 ${point.title}`);
4077
- this.$appOptions.store.dispatch('tags/addTagview', {
4078
- path: point.url,
4079
- fullPath: point.url,
4080
- label: point.title,
4081
- name: point.title,
4082
- meta: {
4083
- title: point.title
4084
- },
4085
- query: {},
4086
- params: {}
4087
- });
4088
- this.$appOptions.router.push({
4089
- path: point.url
4090
- });
4665
+ },
4666
+ processAudio(event) {
4667
+ if (!this.isRecording) return;
4668
+ const inputData = event.inputBuffer.getChannelData(0);
4669
+ const tempBuffer = new Float32Array(this.audioBuffer.length + inputData.length);
4670
+ tempBuffer.set(this.audioBuffer, 0);
4671
+ tempBuffer.set(inputData, this.audioBuffer.length);
4672
+ this.audioBuffer = tempBuffer;
4673
+ while (this.audioBuffer.length >= this.FRAME_SIZE) {
4674
+ const frame = this.audioBuffer.slice(0, this.FRAME_SIZE);
4675
+ this.audioBuffer = this.audioBuffer.slice(this.FRAME_SIZE);
4676
+ const pcmData = this.floatTo16BitPCM(frame);
4677
+ if (this.ws && this.ws.readyState === this.ws.OPEN) {
4678
+ this.ws.send(pcmData);
4091
4679
  }
4092
- });
4093
- console.log('当前播放时间:', currentTime);
4680
+ }
4681
+ },
4682
+ floatTo16BitPCM(input) {
4683
+ const output = new Int16Array(input.length);
4684
+ for (let i = 0; i < input.length; i++) {
4685
+ const s = Math.max(-1, Math.min(1, input[i]));
4686
+ output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
4687
+ }
4688
+ return output.buffer;
4689
+ },
4690
+ stopRecording() {
4691
+ if (!this.isRecording) return;
4692
+ if (this.microphone) this.microphone.disconnect();
4693
+ if (this.processor) this.processor.disconnect();
4694
+ if (this.analyser) this.analyser.disconnect();
4695
+ if (this.audioContext) {
4696
+ this.audioContext.close().catch(e => {
4697
+ console.error("关闭音频上下文失败:", e);
4698
+ });
4699
+ }
4700
+ this.isRecording = false;
4094
4701
  },
4095
4702
  play() {
4096
4703
  this.robotStatus = 'speaking';
@@ -4106,40 +4713,62 @@ const FRAME_SIZE = 512;
4106
4713
  this.$refs.audioPlayer.currentTime = 0;
4107
4714
  this.jumpedTimePoints.clear();
4108
4715
  },
4716
+ analyzeAudioCommand(command) {
4717
+ console.log('分析音频命令:', command);
4718
+ if (command === 'C5') {
4719
+ this.robotStatus = 'entering';
4720
+ setTimeout(() => {
4721
+ this.robotStatus = 'speaking';
4722
+ this.play();
4723
+ }, 3000);
4724
+ } else if (command === 'C8') {
4725
+ this.robotStatus = 'speaking';
4726
+ this.play();
4727
+ } else if (command === 'C7') {
4728
+ this.robotStatus = 'waiting';
4729
+ this.pause();
4730
+ } else if (command === 'C6') {
4731
+ this.robotStatus = 'leaving';
4732
+ this.stop();
4733
+ }
4734
+ }
4735
+ }
4736
+ });
4737
+ ;// ./components/mixins/webSocketMixin.js
4738
+
4739
+
4740
+
4741
+ /* harmony default export */ var webSocketMixin = ({
4742
+ data() {
4743
+ return {
4744
+ ws: null,
4745
+ wsUrl: 'ws://192.168.8.9:9999/ai_model/ws/voice-stream',
4746
+ isConnected: false
4747
+ };
4748
+ },
4749
+ methods: {
4109
4750
  initWebSocket() {
4110
4751
  try {
4111
4752
  this.ws = new WebSocket(this.wsUrl);
4112
4753
  this.ws.binaryType = 'arraybuffer';
4113
4754
  this.ws.onopen = async () => {
4114
- console.log('连接成功');
4755
+ console.log('WebSocket 连接成功');
4115
4756
  this.isConnected = true;
4116
4757
  this.initAudio();
4117
4758
  };
4118
4759
  this.ws.onmessage = event => {
4119
4760
  try {
4120
4761
  console.log("收到原始消息:", event.data);
4121
- // 二进制数据直接返回
4122
4762
  if (event.data instanceof ArrayBuffer) {
4123
4763
  console.log("收到二进制音频数据");
4124
4764
  return;
4125
4765
  }
4126
- // 解析JSON数据
4127
4766
  const data = JSON.parse(event.data);
4128
4767
  console.log("解析后的数据:", data);
4129
4768
  if (data.type === 'config') {
4130
4769
  console.log('配置信息:', data);
4131
4770
  } else if (data.code === 0) {
4132
- if (data.data.type === 'detection') {
4133
- console.log('检测到唤醒词...');
4134
- } else if (data.data.type === 'Collecting') {
4135
- console.log('状态: 采集中...');
4136
- } else if (data.data.type === 'command') {
4137
- // 根据指令改变机器人的状态
4138
- console.log('状态: 处理中...');
4139
- this.analyzeAudioCommand(data.data.category);
4140
- } else {
4141
- console.log('状态: 其他...');
4142
- }
4771
+ this.handleWebSocketMessage(data.data);
4143
4772
  } else {
4144
4773
  console.error("服务器返回错误:", data.msg);
4145
4774
  }
@@ -4148,7 +4777,7 @@ const FRAME_SIZE = 512;
4148
4777
  }
4149
4778
  };
4150
4779
  this.ws.onclose = () => {
4151
- console.log('连接关闭');
4780
+ console.log('WebSocket 连接关闭');
4152
4781
  this.isConnected = false;
4153
4782
  if (this.isRecording) {
4154
4783
  this.stopRecording();
@@ -4156,76 +4785,367 @@ const FRAME_SIZE = 512;
4156
4785
  setTimeout(() => {
4157
4786
  console.log('尝试重新建立连接');
4158
4787
  if (!this.isConnected) {
4159
- // 尝试重连
4160
4788
  this.initWebSocket();
4161
4789
  }
4162
4790
  }, 3000);
4163
4791
  };
4792
+ this.ws.onerror = error => {
4793
+ console.error('WebSocket 错误:', error);
4794
+ };
4164
4795
  } catch (error) {
4165
4796
  console.error('WebSocket连接失败:', error);
4166
4797
  }
4167
4798
  },
4799
+ handleWebSocketMessage(data) {
4800
+ if (data.type === 'detection') {
4801
+ console.log('检测到唤醒词');
4802
+ this.avaterStatus = 'normal';
4803
+ } else if (data.type === 'Collecting') {
4804
+ console.log('状态: 采集中');
4805
+ this.avaterStatus = 'thinking';
4806
+ } else if (data.type === 'command') {
4807
+ console.log('状态: 处理中');
4808
+ this.analyzeAudioCommand(data.category);
4809
+ } else {
4810
+ console.log('状态: 其他');
4811
+ }
4812
+ },
4168
4813
  closeWebSocket() {
4169
4814
  if (this.ws) {
4170
4815
  this.ws.close();
4171
4816
  }
4172
- },
4173
- async initAudio() {
4174
- if (this.isRecording) return;
4175
- try {
4176
- this.isMicAvailable = true;
4177
- // 2. 获取麦克风权限
4178
- const stream = await navigator.mediaDevices.getUserMedia({
4179
- audio: {
4180
- sampleRate: SAMPLE_RATE,
4181
- // 请求指定采样率
4182
- channelCount: 1,
4183
- // 单声道
4184
- noiseSuppression: true,
4185
- echoCancellation: true
4817
+ }
4818
+ }
4819
+ });
4820
+ ;// ./components/utils/StreamParser.js
4821
+
4822
+
4823
+ /**
4824
+ * SSE (Server-Sent Events) 流解析器
4825
+ * 优化点:
4826
+ * 1. 使用状态机模式处理标签解析
4827
+ * 2. 批量更新减少DOM操作
4828
+ * 3. 内存优化:避免频繁字符串拼接
4829
+ * 4. 可配置的更新频率
4830
+ */
4831
+ class StreamParser {
4832
+ constructor(options = {}) {
4833
+ this.options = {
4834
+ updateInterval: 16,
4835
+ // 更新间隔(ms),默认约60fps
4836
+ batchSize: 100,
4837
+ // 批处理大小
4838
+ debug: false,
4839
+ // 是否开启调试
4840
+ ...options
4841
+ };
4842
+ this.reset();
4843
+ }
4844
+
4845
+ /**
4846
+ * 重置解析器状态
4847
+ */
4848
+ reset() {
4849
+ this.buffer = '';
4850
+ this.contentBuffer = []; // 使用数组缓冲,避免频繁字符串拼接
4851
+ this.thinkingBuffer = [];
4852
+ this.inTag = false;
4853
+ this.tagBuffer = '';
4854
+ this.updateTimer = null;
4855
+ this.status = 'output'; // thinking | output
4856
+ this.metrics = {
4857
+ startTime: Date.now(),
4858
+ chunks: 0,
4859
+ events: 0,
4860
+ chars: 0
4861
+ };
4862
+ }
4863
+
4864
+ /**
4865
+ * 处理流数据块
4866
+ * @param {string} chunk - 接收到的数据块
4867
+ * @param {Function} callback - 更新回调函数
4868
+ */
4869
+ processChunk(chunk, callback) {
4870
+ this.metrics.chunks++;
4871
+ this.buffer += chunk;
4872
+
4873
+ // SSE 格式:事件由双换行符分隔
4874
+ const events = this.buffer.split('\n\n');
4875
+
4876
+ // 保留最后一个可能不完整的事件
4877
+ this.buffer = events.pop() || '';
4878
+
4879
+ // 处理完整的事件
4880
+ for (const event of events) {
4881
+ if (event.trim()) {
4882
+ this.metrics.events++;
4883
+ this.processEvent(event, callback);
4884
+ }
4885
+ }
4886
+ }
4887
+
4888
+ /**
4889
+ * 处理单个SSE事件
4890
+ */
4891
+ processEvent(eventStr, callback) {
4892
+ const lines = eventStr.split('\n');
4893
+ for (const line of lines) {
4894
+ // 解析 data: 行
4895
+ if (line.startsWith('data:')) {
4896
+ const data = line.substring(5).trim();
4897
+ if (data === '[DONE]') {
4898
+ this.flush(callback);
4899
+ return;
4900
+ }
4901
+ try {
4902
+ const parsed = JSON.parse(data);
4903
+ const content = parsed?.choices?.[0]?.delta?.content;
4904
+ if (content) {
4905
+ this.metrics.chars += content.length;
4906
+ this.parseContent(content, callback);
4186
4907
  }
4187
- });
4908
+ } catch (error) {
4909
+ if (this.options.debug) {
4910
+ console.warn('[StreamParser] JSON解析失败:', data);
4911
+ }
4912
+ }
4913
+ }
4914
+ }
4915
+ }
4188
4916
 
4189
- // 3. 创建音频处理环境
4190
- this.audioContext = new AudioContext({
4191
- sampleRate: SAMPLE_RATE
4192
- });
4193
- const actualSampleRate = this.audioContext.sampleRate;
4194
- this.microphone = this.audioContext.createMediaStreamSource(stream);
4917
+ /**
4918
+ * 使用状态机解析内容和标签
4919
+ */
4920
+ parseContent(content, callback) {
4921
+ let i = 0;
4922
+ while (i < content.length) {
4923
+ if (this.inTag) {
4924
+ // 在标签内部,查找标签结束
4925
+ const endIndex = content.indexOf('>', i);
4926
+ if (endIndex !== -1) {
4927
+ // 找到标签结束
4928
+ this.tagBuffer += content.substring(i, endIndex + 1);
4929
+ this.handleTag(this.tagBuffer);
4930
+ this.tagBuffer = '';
4931
+ this.inTag = false;
4932
+ i = endIndex + 1;
4933
+ } else {
4934
+ // 标签未结束,缓存剩余部分
4935
+ this.tagBuffer += content.substring(i);
4936
+ break;
4937
+ }
4938
+ } else {
4939
+ // 不在标签内,查找标签开始
4940
+ const startIndex = content.indexOf('<', i);
4941
+ if (startIndex !== -1) {
4942
+ // 找到标签开始,先处理前面的文本
4943
+ if (startIndex > i) {
4944
+ this.appendText(content.substring(i, startIndex));
4945
+ }
4195
4946
 
4196
- // 4. 创建音频处理器
4197
- this.processor = this.audioContext.createScriptProcessor(FRAME_SIZE, 1, 1);
4198
- this.processor.onaudioprocess = this.processAudio;
4199
- // 连接处理链
4200
- this.microphone.connect(this.processor);
4201
- this.processor.connect(this.audioContext.destination);
4202
- this.isRecording = true;
4203
- console.log(`状态: 录音中 (采样率: ${actualSampleRate}Hz)`);
4204
- } catch (error) {
4205
- console.error("音频初始化失败:", error);
4206
- this.isRecording = false;
4207
- this.isMicAvailable = false;
4947
+ // 检查是否是完整标签
4948
+ const endIndex = content.indexOf('>', startIndex);
4949
+ if (endIndex !== -1) {
4950
+ // 完整标签
4951
+ const tag = content.substring(startIndex, endIndex + 1);
4952
+ this.handleTag(tag);
4953
+ i = endIndex + 1;
4954
+ } else {
4955
+ // 不完整标签,标记进入标签状态
4956
+ this.inTag = true;
4957
+ this.tagBuffer = content.substring(startIndex);
4958
+ break;
4959
+ }
4960
+ } else {
4961
+ // 没有标签,全部是文本
4962
+ this.appendText(content.substring(i));
4963
+ break;
4964
+ }
4208
4965
  }
4966
+ }
4967
+
4968
+ // 定时批量更新
4969
+ this.scheduleUpdate(callback);
4970
+ }
4971
+
4972
+ /**
4973
+ * 处理标签
4974
+ */
4975
+ handleTag(tag) {
4976
+ const tagName = tag.toLowerCase();
4977
+ if (tagName === '<think>') {
4978
+ this.status = 'thinking';
4979
+ } else if (tagName === '</think>') {
4980
+ this.status = 'output';
4981
+ }
4982
+ // 可扩展:支持更多标签类型
4983
+ // else if (tagName.startsWith('<code')) {
4984
+ // this.status = 'code';
4985
+ // }
4986
+ }
4987
+
4988
+ /**
4989
+ * 添加文本到缓冲区
4990
+ */
4991
+ appendText(text) {
4992
+ if (!text) return;
4993
+ if (this.status === 'thinking') {
4994
+ this.thinkingBuffer.push(text);
4995
+ } else {
4996
+ this.contentBuffer.push(text);
4997
+ }
4998
+ }
4999
+
5000
+ /**
5001
+ * 计划更新(防抖)
5002
+ */
5003
+ scheduleUpdate(callback) {
5004
+ if (this.updateTimer) return;
5005
+ this.updateTimer = setTimeout(() => {
5006
+ this.flush(callback);
5007
+ this.updateTimer = null;
5008
+ }, this.options.updateInterval);
5009
+ }
5010
+
5011
+ /**
5012
+ * 立即刷新缓冲区
5013
+ */
5014
+ flush(callback) {
5015
+ if (!callback) return;
5016
+ const hasThinking = this.thinkingBuffer.length > 0;
5017
+ const hasContent = this.contentBuffer.length > 0;
5018
+ if (!hasThinking && !hasContent) return;
5019
+
5020
+ // 使用 join 比字符串拼接性能更好
5021
+ const result = {
5022
+ thinking: hasThinking ? this.thinkingBuffer.join('') : null,
5023
+ content: hasContent ? this.contentBuffer.join('') : null,
5024
+ status: this.status
5025
+ };
5026
+
5027
+ // 清空缓冲区
5028
+ this.thinkingBuffer = [];
5029
+ this.contentBuffer = [];
5030
+
5031
+ // 回调更新
5032
+ callback(result);
5033
+ }
5034
+
5035
+ /**
5036
+ * 完成解析
5037
+ */
5038
+ finish(callback) {
5039
+ this.flush(callback);
5040
+ if (this.updateTimer) {
5041
+ clearTimeout(this.updateTimer);
5042
+ this.updateTimer = null;
5043
+ }
5044
+ if (this.options.debug) {
5045
+ const duration = Date.now() - this.metrics.startTime;
5046
+ console.log('[StreamParser] 解析完成:', {
5047
+ 耗时: `${duration}ms`,
5048
+ 数据块: this.metrics.chunks,
5049
+ 事件数: this.metrics.events,
5050
+ 字符数: this.metrics.chars,
5051
+ 平均速度: `${(this.metrics.chars / duration * 1000).toFixed(0)} chars/s`
5052
+ });
5053
+ }
5054
+ }
5055
+
5056
+ /**
5057
+ * 销毁解析器
5058
+ */
5059
+ destroy() {
5060
+ if (this.updateTimer) {
5061
+ clearTimeout(this.updateTimer);
5062
+ }
5063
+ this.reset();
5064
+ }
5065
+ }
5066
+
5067
+ /**
5068
+ * 使用示例:
5069
+ *
5070
+ * const parser = new StreamParser({ debug: true });
5071
+ *
5072
+ * // 处理流
5073
+ * for await (const chunk of stream) {
5074
+ * parser.processChunk(chunk, (result) => {
5075
+ * if (result.thinking) {
5076
+ * message.thinking += result.thinking;
5077
+ * }
5078
+ * if (result.content) {
5079
+ * message.content += result.content;
5080
+ * }
5081
+ * this.$forceUpdate();
5082
+ * });
5083
+ * }
5084
+ *
5085
+ * // 完成
5086
+ * parser.finish();
5087
+ */
5088
+ ;// ./components/mixins/messageMixin.js
5089
+
5090
+
5091
+ /* harmony default export */ var messageMixin = ({
5092
+ data() {
5093
+ return {
5094
+ streamParser: null
5095
+ };
5096
+ },
5097
+ created() {
5098
+ // 初始化流解析器
5099
+ this.streamParser = new StreamParser({
5100
+ updateInterval: 16,
5101
+ // 约60fps
5102
+ debug: "production" === 'development'
5103
+ });
5104
+ },
5105
+ methods: {
5106
+ createAiMessage() {
5107
+ const message = {
5108
+ id: this.messages.length + 1,
5109
+ type: 'ai',
5110
+ sender: 'AI',
5111
+ time: '',
5112
+ thinking: '',
5113
+ charts: [],
5114
+ content: ''
5115
+ };
5116
+ this.messages.push(message);
5117
+ this.currentMessage = message;
5118
+ return message;
5119
+ },
5120
+ createUserMessage(content) {
5121
+ const message = {
5122
+ id: this.messages.length + 1,
5123
+ type: 'user',
5124
+ sender: '用户',
5125
+ time: '',
5126
+ content
5127
+ };
5128
+ this.messages.push(message);
5129
+ this.inputMessage = '';
5130
+ return message;
4209
5131
  },
4210
5132
  async handleSend() {
4211
5133
  if (!this.inputMessage.trim()) {
4212
5134
  return;
4213
5135
  }
4214
5136
  const message = this.inputMessage.trim();
4215
- // 发送消息
4216
- this.messages.push({
4217
- id: this.messages.length + 1,
4218
- type: 'user',
4219
- sender: '用户',
4220
- time: new Date().toLocaleTimeString(),
4221
- content: this.inputMessage
4222
- });
4223
- this.inputMessage = '';
5137
+ this.createUserMessage(message);
5138
+ this.createAiMessage();
5139
+
5140
+ // 重置解析器
5141
+ this.streamParser.reset();
4224
5142
  try {
4225
- const token = `Bearer 24ab99b4-4b59-42a0-84df-1d73a96e70cd`;
5143
+ const startTime = Date.now();
5144
+ const controller = new AbortController();
5145
+ const token = `Bearer ac627d0a-8346-4ae9-b93a-f37ff6210adc`;
4226
5146
  const response = await fetch('/bytserver/api-model/chat/stream', {
4227
- timeout: 30000,
4228
5147
  method: 'POST',
5148
+ signal: controller.signal,
4229
5149
  headers: {
4230
5150
  'Content-Type': 'application/json',
4231
5151
  'Authorization': token
@@ -4235,243 +5155,236 @@ const FRAME_SIZE = 512;
4235
5155
  })
4236
5156
  });
4237
5157
  if (!response.ok) {
4238
- throw new Error(`${response.status}`);
5158
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
5159
+ }
5160
+
5161
+ // 使用优化的流处理
5162
+ await this.consumeStream(response.body);
5163
+
5164
+ // 完成解析
5165
+ this.streamParser.finish(this.handleStreamUpdate);
5166
+
5167
+ // 记录耗时
5168
+ const duration = Date.now() - startTime;
5169
+ if (this.currentMessage) {
5170
+ this.currentMessage.time = (duration / 1000).toFixed(2);
4239
5171
  }
4240
- console.log('响应状态:', response.status);
4241
- // 解析响应流
4242
- this.parseResponseStream(response.body);
5172
+ console.log(`流处理完成,总耗时: ${duration}ms`);
4243
5173
  } catch (error) {
4244
5174
  console.error('发送消息失败:', error);
5175
+ if (this.currentMessage) {
5176
+ this.currentMessage.content = '抱歉,发生了错误,请重试。';
5177
+ this.$forceUpdate();
5178
+ }
4245
5179
  }
4246
5180
  },
4247
- // 分析音频命令
4248
- analyzeAudioCommand(command) {
4249
- console.log('分析音频命令:', command);
4250
- // 解析到开始导览,执行机器人进入动画
4251
- if (command === 'C5') {
4252
- this.robotStatus = 'entering';
4253
- setTimeout(() => {
4254
- this.robotStatus = 'speaking';
4255
- this.play();
4256
- }, 3000);
5181
+ /**
5182
+ * 消费流数据
5183
+ */
5184
+ async consumeStream(readableStream) {
5185
+ const reader = readableStream.getReader();
5186
+ const decoder = new TextDecoder('utf-8');
5187
+ try {
5188
+ // eslint-disable-next-line no-constant-condition
5189
+ while (true) {
5190
+ const {
5191
+ done,
5192
+ value
5193
+ } = await reader.read();
5194
+ if (done) break;
5195
+ const chunk = decoder.decode(value, {
5196
+ stream: true
5197
+ });
5198
+
5199
+ // 使用解析器处理数据块
5200
+ this.streamParser.processChunk(chunk, this.handleStreamUpdate);
5201
+ }
5202
+ } finally {
5203
+ reader.releaseLock();
4257
5204
  }
4258
- // 继续导览
4259
- else if (command === 'C8') {
4260
- this.robotStatus = 'speaking';
4261
- this.play();
5205
+ },
5206
+ /**
5207
+ * 处理流更新回调
5208
+ */
5209
+ handleStreamUpdate(result) {
5210
+ if (!this.currentMessage) return;
5211
+
5212
+ // 更新思考内容
5213
+ if (result.thinking) {
5214
+ this.currentMessage.thinking += result.thinking;
4262
5215
  }
4263
- // 解析到暂停导览,执行机器人暂停动画
4264
- else if (command === 'C7') {
4265
- this.robotStatus = 'waiting';
4266
- this.pause();
5216
+
5217
+ // 更新回复内容
5218
+ if (result.content) {
5219
+ this.currentMessage.content += result.content;
4267
5220
  }
4268
- // 解析到结束导览,执行机器人离开动画
4269
- else if (command === 'C6') {
4270
- this.robotStatus = 'leaving';
4271
- this.stop();
5221
+
5222
+ // 更新状态
5223
+ if (result.status === 'thinking') {
5224
+ this.avaterStatus = 'thinking';
5225
+ } else {
5226
+ this.avaterStatus = 'output';
4272
5227
  }
4273
- },
4274
- // 解析响应流
4275
- parseResponseStream(body) {
4276
- console.log(body);
4277
- },
4278
- processAudio(event) {
4279
- if (!this.isRecording) return;
4280
- // 5. 获取音频数据并处理
4281
- const inputData = event.inputBuffer.getChannelData(0);
4282
5228
 
4283
- // 累积音频数据
4284
- const tempBuffer = new Float32Array(this.audioBuffer.length + inputData.length);
4285
- tempBuffer.set(this.audioBuffer, 0);
4286
- tempBuffer.set(inputData, this.audioBuffer.length);
4287
- this.audioBuffer = tempBuffer;
5229
+ // 触发视图更新
5230
+ this.$forceUpdate();
5231
+ }
5232
+ },
5233
+ beforeDestroy() {
5234
+ // 清理解析器
5235
+ if (this.streamParser) {
5236
+ this.streamParser.destroy();
5237
+ }
5238
+ }
5239
+ });
5240
+ ;// ./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
4288
5241
 
4289
- // 当累积足够一帧时发送
4290
- while (this.audioBuffer.length >= FRAME_SIZE) {
4291
- const frame = this.audioBuffer.slice(0, FRAME_SIZE);
4292
- this.audioBuffer = this.audioBuffer.slice(FRAME_SIZE);
4293
5242
 
4294
- // 转换为16位PCM
4295
- const pcmData = this.floatTo16BitPCM(frame);
4296
5243
 
4297
- // 通过WebSocket发送
4298
- if (this.ws && this.ws.readyState === this.ws.OPEN) {
4299
- this.ws.send(pcmData);
4300
- }
4301
- }
4302
- },
4303
- floatTo16BitPCM(input) {
4304
- const output = new Int16Array(input.length);
4305
- for (let i = 0; i < input.length; i++) {
4306
- const s = Math.max(-1, Math.min(1, input[i]));
4307
- output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
4308
- }
4309
- return output.buffer;
4310
- },
5244
+
5245
+
5246
+
5247
+
5248
+
5249
+
5250
+
5251
+
5252
+
5253
+
5254
+
5255
+ const SAMPLE_RATE = 16000;
5256
+ const FRAME_SIZE = 512;
5257
+ /* harmony default export */ var ChatWindowvue_type_script_lang_js = ({
5258
+ name: 'ChatWindow',
5259
+ components: {
5260
+ ChatRobot: ChatRobot,
5261
+ ChatAvatar: ChatAvatar,
5262
+ ChatWindowDialog: ChatWindowDialog
5263
+ },
5264
+ mixins: [audioMixin, webSocketMixin, messageMixin],
5265
+ props: {
5266
+ appendToBody: {
5267
+ type: Boolean,
5268
+ default: true
5269
+ }
5270
+ },
5271
+ data() {
5272
+ return {
5273
+ audioSrc: '/minio/lingxiaoai/byt.mp3',
5274
+ inputMessage: '',
5275
+ visible: false,
5276
+ messages: [{
5277
+ id: 1,
5278
+ type: 'user',
5279
+ sender: '',
5280
+ time: '',
5281
+ content: '你好,欢迎来到凌霄大模型AI对话。'
5282
+ }, {
5283
+ id: 2,
5284
+ type: 'ai',
5285
+ sender: 'AI',
5286
+ time: '',
5287
+ thinking: '嗯,用户问的是回转窑的工业应用。首先,我需要回忆一下之前对话的内容。用户之前让我解释了水泥的制作流程,特别是提到了回转窑在高温煅烧熟料中的作用。',
5288
+ charts: [{
5289
+ title: '',
5290
+ options: {}
5291
+ }],
5292
+ content: '回转窑(Rotary Kiln)是一种长筒形旋转煅烧设备(类似倾斜安装的大管子),因其独特的旋转运动和高温耐火衬里设计,在多个工业领域都有广泛应用'
5293
+ }],
5294
+ robotStatus: 'leaving',
5295
+ avaterStatus: 'normal',
5296
+ currentMessage: null,
5297
+ thinkStatus: true,
5298
+ jumpedTimePoints: new Set(),
5299
+ SAMPLE_RATE,
5300
+ FRAME_SIZE
5301
+ };
5302
+ },
5303
+ mounted() {
5304
+ this.initWebSocket();
5305
+ if (this.appendToBody) {
5306
+ this.appendToBodyHandler();
5307
+ }
5308
+ },
5309
+ beforeDestroy() {
5310
+ if (this.appendToBody && this.$el.parentElement === document.body) {
5311
+ document.body.removeChild(this.$el);
5312
+ }
5313
+ this.closeWebSocket();
5314
+ this.stopRecording();
5315
+ },
5316
+ methods: {
4311
5317
  toggleWindow() {
4312
5318
  this.visible = !this.visible;
4313
5319
  },
4314
- showWindow() {
4315
- this.visible = true;
5320
+ handleThinkingClick() {
5321
+ this.thinkStatus = !this.thinkStatus;
4316
5322
  },
4317
- hideWindow() {
5323
+ handleOverlayClick() {
4318
5324
  this.visible = false;
4319
5325
  },
4320
- scrollToBottom() {
5326
+ appendToBodyHandler() {
4321
5327
  this.$nextTick(() => {
4322
- const chatArea = this.$refs.chatArea;
4323
- if (chatArea) {
4324
- chatArea.scrollTop = chatArea.scrollHeight;
5328
+ if (this.$el.parentElement !== document.body) {
5329
+ document.body.appendChild(this.$el);
4325
5330
  }
4326
5331
  });
4327
5332
  },
4328
- handleKeyDown(e) {
4329
- if (e.key === 'Enter' && !e.shiftKey) {
4330
- e.preventDefault();
4331
- this.handleSend();
4332
- }
4333
- },
4334
- stopRecording() {
4335
- if (!this.isRecording) return;
4336
- if (this.microphone) this.microphone.disconnect();
4337
- if (this.processor) this.processor.disconnect();
4338
- if (this.analyser) this.analyser.disconnect();
4339
- if (this.audioContext) {
4340
- this.audioContext.close().catch(e => {
4341
- console.error("关闭音频上下文失败:", e);
4342
- });
5333
+ // 音频时间更新处理
5334
+ onTimeUpdate() {
5335
+ const audio = this.$refs.audioPlayer;
5336
+ const currentTime = audio.currentTime;
5337
+ if (!this.jumpedTimePoints) {
5338
+ this.jumpedTimePoints = new Set();
4343
5339
  }
4344
- this.isRecording = false;
4345
- },
4346
- // 添加到body的处理函数
4347
- appendToBodyHandler() {
4348
- // 确保DOM已经渲染完成
4349
- this.$nextTick(() => {
4350
- // 检查元素是否已经在body中
4351
- if (this.$el.parentElement !== document.body) {
4352
- // 将组件的根元素移动到body中
4353
- document.body.appendChild(this.$el);
5340
+ const timeJumpPoints = [{
5341
+ time: 40,
5342
+ url: '/permission/user',
5343
+ name: 'permission_user',
5344
+ title: '用户管理'
5345
+ }, {
5346
+ time: 65,
5347
+ url: '/permission/menu',
5348
+ name: 'permission_menu',
5349
+ title: '菜单管理'
5350
+ }, {
5351
+ time: 75,
5352
+ url: '/permission/role',
5353
+ name: 'permission_role',
5354
+ title: '角色管理'
5355
+ }];
5356
+ timeJumpPoints.forEach(point => {
5357
+ if (currentTime >= point.time && currentTime < point.time + 0.5 && !this.jumpedTimePoints.has(point.time)) {
5358
+ this.jumpedTimePoints.add(point.time);
5359
+ this.$appOptions.store.dispatch('tags/addTagview', {
5360
+ path: point.url,
5361
+ fullPath: point.url,
5362
+ label: point.title,
5363
+ name: point.title,
5364
+ meta: {
5365
+ title: point.title
5366
+ },
5367
+ query: {},
5368
+ params: {}
5369
+ });
5370
+ this.$appOptions.router.push({
5371
+ path: point.url
5372
+ });
4354
5373
  }
4355
5374
  });
4356
5375
  },
4357
- // 处理点击遮罩层事件
4358
- handleOverlayClick() {
4359
- this.visible = false;
4360
- }
4361
- },
4362
- watch: {
4363
- messages: {
4364
- handler() {
4365
- this.scrollToBottom();
4366
- },
4367
- deep: true
5376
+ onAudioEnded() {
5377
+ this.robotStatus = 'leaving';
5378
+ this.jumpedTimePoints.clear();
4368
5379
  }
4369
5380
  }
4370
5381
  });
4371
5382
  ;// ./components/ChatWindow.vue?vue&type=script&lang=js
4372
5383
  /* harmony default export */ var components_ChatWindowvue_type_script_lang_js = (ChatWindowvue_type_script_lang_js);
4373
- ;// ./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=43521054&prod&scoped=true&lang=css
5384
+ ;// ./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=d604373c&prod&scoped=true&lang=css
4374
5385
  // extracted by mini-css-extract-plugin
4375
5386
 
4376
- ;// ./components/ChatWindow.vue?vue&type=style&index=0&id=43521054&prod&scoped=true&lang=css
4377
-
4378
- ;// ./node_modules/@vue/vue-loader-v15/lib/runtime/componentNormalizer.js
4379
- /* globals __VUE_SSR_CONTEXT__ */
4380
-
4381
- // IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
4382
- // This module is a runtime utility for cleaner component module output and will
4383
- // be included in the final webpack user bundle.
4384
-
4385
- function normalizeComponent(
4386
- scriptExports,
4387
- render,
4388
- staticRenderFns,
4389
- functionalTemplate,
4390
- injectStyles,
4391
- scopeId,
4392
- moduleIdentifier /* server only */,
4393
- shadowMode /* vue-cli only */
4394
- ) {
4395
- // Vue.extend constructor export interop
4396
- var options =
4397
- typeof scriptExports === 'function' ? scriptExports.options : scriptExports
4398
-
4399
- // render functions
4400
- if (render) {
4401
- options.render = render
4402
- options.staticRenderFns = staticRenderFns
4403
- options._compiled = true
4404
- }
4405
-
4406
- // functional template
4407
- if (functionalTemplate) {
4408
- options.functional = true
4409
- }
4410
-
4411
- // scopedId
4412
- if (scopeId) {
4413
- options._scopeId = 'data-v-' + scopeId
4414
- }
4415
-
4416
- var hook
4417
- if (moduleIdentifier) {
4418
- // server build
4419
- hook = function (context) {
4420
- // 2.3 injection
4421
- context =
4422
- context || // cached call
4423
- (this.$vnode && this.$vnode.ssrContext) || // stateful
4424
- (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
4425
- // 2.2 with runInNewContext: true
4426
- if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
4427
- context = __VUE_SSR_CONTEXT__
4428
- }
4429
- // inject component styles
4430
- if (injectStyles) {
4431
- injectStyles.call(this, context)
4432
- }
4433
- // register component module identifier for async chunk inferrence
4434
- if (context && context._registeredComponents) {
4435
- context._registeredComponents.add(moduleIdentifier)
4436
- }
4437
- }
4438
- // used by ssr in case component is cached and beforeCreate
4439
- // never gets called
4440
- options._ssrRegister = hook
4441
- } else if (injectStyles) {
4442
- hook = shadowMode
4443
- ? function () {
4444
- injectStyles.call(
4445
- this,
4446
- (options.functional ? this.parent : this).$root.$options.shadowRoot
4447
- )
4448
- }
4449
- : injectStyles
4450
- }
4451
-
4452
- if (hook) {
4453
- if (options.functional) {
4454
- // for template-only hot-reload because in that case the render fn doesn't
4455
- // go through the normalizer
4456
- options._injectStyles = hook
4457
- // register for functional component in vue file
4458
- var originalRender = options.render
4459
- options.render = function renderWithStyleInjection(h, context) {
4460
- hook.call(context)
4461
- return originalRender(h, context)
4462
- }
4463
- } else {
4464
- // inject component registration as beforeCreate hook
4465
- var existing = options.beforeCreate
4466
- options.beforeCreate = existing ? [].concat(existing, hook) : [hook]
4467
- }
4468
- }
4469
-
4470
- return {
4471
- exports: scriptExports,
4472
- options: options
4473
- }
4474
- }
5387
+ ;// ./components/ChatWindow.vue?vue&type=style&index=0&id=d604373c&prod&scoped=true&lang=css
4475
5388
 
4476
5389
  ;// ./components/ChatWindow.vue
4477
5390
 
@@ -4482,18 +5395,18 @@ function normalizeComponent(
4482
5395
 
4483
5396
  /* normalize component */
4484
5397
 
4485
- var component = normalizeComponent(
5398
+ var ChatWindow_component = normalizeComponent(
4486
5399
  components_ChatWindowvue_type_script_lang_js,
4487
5400
  render,
4488
5401
  staticRenderFns,
4489
5402
  false,
4490
5403
  null,
4491
- "43521054",
5404
+ "d604373c",
4492
5405
  null
4493
5406
 
4494
5407
  )
4495
5408
 
4496
- /* harmony default export */ var ChatWindow = (component.exports);
5409
+ /* harmony default export */ var ChatWindow = (ChatWindow_component.exports);
4497
5410
  ;// ./components/index.js
4498
5411
 
4499
5412