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