vue-element-ui-x 0.1.0

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.
Files changed (91) hide show
  1. package/README.md +47 -0
  2. package/lib/attachments.js +3046 -0
  3. package/lib/bubble-list.js +13845 -0
  4. package/lib/bubble.js +13130 -0
  5. package/lib/conversations.js +18817 -0
  6. package/lib/files-card.js +2457 -0
  7. package/lib/index.common.js +19 -0
  8. package/lib/index.esm.js +19 -0
  9. package/lib/index.js +38282 -0
  10. package/lib/index.umd.js +19 -0
  11. package/lib/mixins.js +1016 -0
  12. package/lib/prompts.js +832 -0
  13. package/lib/sender.js +1901 -0
  14. package/lib/think.js +799 -0
  15. package/lib/thinking.js +809 -0
  16. package/lib/thought-chain.js +30396 -0
  17. package/lib/typewriter.js +12793 -0
  18. package/lib/welcome.js +755 -0
  19. package/package.json +42 -0
  20. package/src/components/Attachments/index.js +8 -0
  21. package/src/components/Attachments/src/main.vue +537 -0
  22. package/src/components/Bubble/index.js +6 -0
  23. package/src/components/Bubble/src/main.vue +299 -0
  24. package/src/components/BubbleList/index.js +8 -0
  25. package/src/components/BubbleList/src/loading.vue +75 -0
  26. package/src/components/BubbleList/src/main.vue +466 -0
  27. package/src/components/Conversations/index.js +8 -0
  28. package/src/components/Conversations/src/components/item.vue +371 -0
  29. package/src/components/Conversations/src/main.vue +635 -0
  30. package/src/components/FilesCard/index.js +8 -0
  31. package/src/components/FilesCard/src/fileSvg/audio.vue +38 -0
  32. package/src/components/FilesCard/src/fileSvg/changeFileName.bat +18 -0
  33. package/src/components/FilesCard/src/fileSvg/code.vue +35 -0
  34. package/src/components/FilesCard/src/fileSvg/database.vue +94 -0
  35. package/src/components/FilesCard/src/fileSvg/excel.vue +38 -0
  36. package/src/components/FilesCard/src/fileSvg/file.vue +40 -0
  37. package/src/components/FilesCard/src/fileSvg/image.vue +40 -0
  38. package/src/components/FilesCard/src/fileSvg/index.js +46 -0
  39. package/src/components/FilesCard/src/fileSvg/link.vue +54 -0
  40. package/src/components/FilesCard/src/fileSvg/mark.vue +38 -0
  41. package/src/components/FilesCard/src/fileSvg/pdf.vue +38 -0
  42. package/src/components/FilesCard/src/fileSvg/ppt.vue +38 -0
  43. package/src/components/FilesCard/src/fileSvg/three.vue +38 -0
  44. package/src/components/FilesCard/src/fileSvg/txt.vue +38 -0
  45. package/src/components/FilesCard/src/fileSvg/unknown.vue +54 -0
  46. package/src/components/FilesCard/src/fileSvg/video.vue +38 -0
  47. package/src/components/FilesCard/src/fileSvg/word.vue +38 -0
  48. package/src/components/FilesCard/src/fileSvg/zip.vue +38 -0
  49. package/src/components/FilesCard/src/main.vue +405 -0
  50. package/src/components/FilesCard/src/options.js +18 -0
  51. package/src/components/Prompts/index.js +8 -0
  52. package/src/components/Prompts/src/main.vue +248 -0
  53. package/src/components/Sender/index.js +8 -0
  54. package/src/components/Sender/src/components/ClearButton.vue +28 -0
  55. package/src/components/Sender/src/components/Loading.vue +53 -0
  56. package/src/components/Sender/src/components/LoadingButton.vue +39 -0
  57. package/src/components/Sender/src/components/SendButton.vue +26 -0
  58. package/src/components/Sender/src/components/SpeechButton.vue +24 -0
  59. package/src/components/Sender/src/components/SpeechLoading.vue +87 -0
  60. package/src/components/Sender/src/components/SpeechLoadingButton.vue +43 -0
  61. package/src/components/Sender/src/main.vue +758 -0
  62. package/src/components/Think/index.js +8 -0
  63. package/src/components/Think/src/main.vue +190 -0
  64. package/src/components/Thinking/index.js +8 -0
  65. package/src/components/Thinking/src/main.vue +195 -0
  66. package/src/components/ThoughtChain/index.js +8 -0
  67. package/src/components/ThoughtChain/src/main.vue +293 -0
  68. package/src/components/Typewriter/index.js +8 -0
  69. package/src/components/Typewriter/src/main.vue +251 -0
  70. package/src/components/Welcome/index.js +8 -0
  71. package/src/components/Welcome/src/main.vue +151 -0
  72. package/src/index.js +63 -0
  73. package/src/mixins/index.js +49 -0
  74. package/src/mixins/recordMixin.js +118 -0
  75. package/src/mixins/sendMixin.js +357 -0
  76. package/src/mixins/streamMixin.js +499 -0
  77. package/src/styles/Attachments.scss +236 -0
  78. package/src/styles/Bubble.scss +157 -0
  79. package/src/styles/BubbleList.scss +148 -0
  80. package/src/styles/Conversations.scss +260 -0
  81. package/src/styles/FilesCard.scss +221 -0
  82. package/src/styles/Prompts.scss +195 -0
  83. package/src/styles/Sender.scss +199 -0
  84. package/src/styles/Think.scss +134 -0
  85. package/src/styles/Thinking.scss +112 -0
  86. package/src/styles/ThoughtChain.scss +113 -0
  87. package/src/styles/Typewriter.scss +66 -0
  88. package/src/styles/Welcome.scss +283 -0
  89. package/src/theme/var.scss +72 -0
  90. package/src/utils/index.js +199 -0
  91. package/src/utils/scrollDetector.js +34 -0
package/lib/mixins.js ADDED
@@ -0,0 +1,1016 @@
1
+ module.exports =
2
+ /******/ (function(modules) { // webpackBootstrap
3
+ /******/ // The module cache
4
+ /******/ var installedModules = {};
5
+ /******/
6
+ /******/ // The require function
7
+ /******/ function __webpack_require__(moduleId) {
8
+ /******/
9
+ /******/ // Check if module is in cache
10
+ /******/ if(installedModules[moduleId]) {
11
+ /******/ return installedModules[moduleId].exports;
12
+ /******/ }
13
+ /******/ // Create a new module (and put it into the cache)
14
+ /******/ var module = installedModules[moduleId] = {
15
+ /******/ i: moduleId,
16
+ /******/ l: false,
17
+ /******/ exports: {}
18
+ /******/ };
19
+ /******/
20
+ /******/ // Execute the module function
21
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22
+ /******/
23
+ /******/ // Flag the module as loaded
24
+ /******/ module.l = true;
25
+ /******/
26
+ /******/ // Return the exports of the module
27
+ /******/ return module.exports;
28
+ /******/ }
29
+ /******/
30
+ /******/
31
+ /******/ // expose the modules object (__webpack_modules__)
32
+ /******/ __webpack_require__.m = modules;
33
+ /******/
34
+ /******/ // expose the module cache
35
+ /******/ __webpack_require__.c = installedModules;
36
+ /******/
37
+ /******/ // define getter function for harmony exports
38
+ /******/ __webpack_require__.d = function(exports, name, getter) {
39
+ /******/ if(!__webpack_require__.o(exports, name)) {
40
+ /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
41
+ /******/ }
42
+ /******/ };
43
+ /******/
44
+ /******/ // define __esModule on exports
45
+ /******/ __webpack_require__.r = function(exports) {
46
+ /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
47
+ /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
48
+ /******/ }
49
+ /******/ Object.defineProperty(exports, '__esModule', { value: true });
50
+ /******/ };
51
+ /******/
52
+ /******/ // create a fake namespace object
53
+ /******/ // mode & 1: value is a module id, require it
54
+ /******/ // mode & 2: merge all properties of value into the ns
55
+ /******/ // mode & 4: return value when already ns object
56
+ /******/ // mode & 8|1: behave like require
57
+ /******/ __webpack_require__.t = function(value, mode) {
58
+ /******/ if(mode & 1) value = __webpack_require__(value);
59
+ /******/ if(mode & 8) return value;
60
+ /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
61
+ /******/ var ns = Object.create(null);
62
+ /******/ __webpack_require__.r(ns);
63
+ /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
64
+ /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
65
+ /******/ return ns;
66
+ /******/ };
67
+ /******/
68
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
69
+ /******/ __webpack_require__.n = function(module) {
70
+ /******/ var getter = module && module.__esModule ?
71
+ /******/ function getDefault() { return module['default']; } :
72
+ /******/ function getModuleExports() { return module; };
73
+ /******/ __webpack_require__.d(getter, 'a', getter);
74
+ /******/ return getter;
75
+ /******/ };
76
+ /******/
77
+ /******/ // Object.prototype.hasOwnProperty.call
78
+ /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
79
+ /******/
80
+ /******/ // __webpack_public_path__
81
+ /******/ __webpack_require__.p = "";
82
+ /******/
83
+ /******/
84
+ /******/ // Load entry module and return exports
85
+ /******/ return __webpack_require__(__webpack_require__.s = 82);
86
+ /******/ })
87
+ /************************************************************************/
88
+ /******/ ({
89
+
90
+ /***/ 82:
91
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
92
+
93
+ "use strict";
94
+ // ESM COMPAT FLAG
95
+ __webpack_require__.r(__webpack_exports__);
96
+
97
+ // EXPORTS
98
+ __webpack_require__.d(__webpack_exports__, "createSendUtils", function() { return /* reexport */ createSendUtils; });
99
+ __webpack_require__.d(__webpack_exports__, "createStreamUtils", function() { return /* reexport */ createStreamUtils; });
100
+ __webpack_require__.d(__webpack_exports__, "DEFAULT_KV_SEPARATOR", function() { return /* reexport */ DEFAULT_KV_SEPARATOR; });
101
+ __webpack_require__.d(__webpack_exports__, "DEFAULT_PART_SEPARATOR", function() { return /* reexport */ DEFAULT_PART_SEPARATOR; });
102
+ __webpack_require__.d(__webpack_exports__, "DEFAULT_STREAM_SEPARATOR", function() { return /* reexport */ DEFAULT_STREAM_SEPARATOR; });
103
+ __webpack_require__.d(__webpack_exports__, "isValidString", function() { return /* reexport */ isValidString; });
104
+ __webpack_require__.d(__webpack_exports__, "recordMixin", function() { return /* reexport */ recordMixin; });
105
+ __webpack_require__.d(__webpack_exports__, "sendMixin", function() { return /* reexport */ sendMixin; });
106
+ __webpack_require__.d(__webpack_exports__, "splitPart", function() { return /* reexport */ splitPart; });
107
+ __webpack_require__.d(__webpack_exports__, "splitStream", function() { return /* reexport */ splitStream; });
108
+ __webpack_require__.d(__webpack_exports__, "streamMixin", function() { return /* reexport */ streamMixin; });
109
+ __webpack_require__.d(__webpack_exports__, "XRequest", function() { return /* reexport */ XRequest; });
110
+ __webpack_require__.d(__webpack_exports__, "XStream", function() { return /* reexport */ XStream; });
111
+
112
+ // CONCATENATED MODULE: ./src/mixins/recordMixin.js
113
+ /**
114
+ * @description 提供语音识别的混入,允许语音输入并处理开始、结束、结果及错误等各种事件。
115
+ *
116
+ * @mixin recordMixin
117
+ */
118
+
119
+ /* harmony default export */ var recordMixin = ({
120
+ data() {
121
+ return {
122
+ // 语音识别状态
123
+ recordLoading: false,
124
+ // 识别结果
125
+ recordValue: '',
126
+ // 识别实例
127
+ recordRecognition: null,
128
+ // 配置选项
129
+ recordOptions: {
130
+ onError: null,
131
+ onStart: null,
132
+ onEnd: null,
133
+ onResult: null
134
+ }
135
+ };
136
+ },
137
+ methods: {
138
+ /**
139
+ * 初始化语音识别配置
140
+ * @param {Object} options - 配置选项
141
+ * @param {Function} [options.onError] - 错误回调
142
+ * @param {Function} [options.onStart] - 开始回调
143
+ * @param {Function} [options.onEnd] - 结束回调
144
+ * @param {Function} [options.onResult] - 结果回调
145
+ */
146
+ initRecord(options = {}) {
147
+ this.recordOptions = {
148
+ ...this.recordOptions,
149
+ ...options
150
+ };
151
+ },
152
+ /**
153
+ * 开始语音识别
154
+ */
155
+ startRecord() {
156
+ if ('webkitSpeechRecognition' in window) {
157
+ this.recordRecognition = new window.webkitSpeechRecognition();
158
+ this.recordRecognition.continuous = true;
159
+ this.recordRecognition.interimResults = true;
160
+ this.recordRecognition.lang = 'zh-CN';
161
+ this.recordRecognition.onstart = () => {
162
+ this.recordLoading = true;
163
+ this.recordValue = '';
164
+ if (this.recordOptions.onStart) {
165
+ this.recordOptions.onStart();
166
+ }
167
+ };
168
+ this.recordRecognition.onend = () => {
169
+ this.recordLoading = false;
170
+ if (this.recordOptions.onEnd) {
171
+ this.recordOptions.onEnd(this.recordValue);
172
+ }
173
+ };
174
+ this.recordRecognition.onerror = e => {
175
+ this.recordLoading = false;
176
+ if (this.recordOptions.onError) {
177
+ this.recordOptions.onError(e);
178
+ }
179
+ };
180
+ this.recordRecognition.onresult = e => {
181
+ let results = '';
182
+ for (let i = 0; i <= e.resultIndex; i++) {
183
+ results += e.results[i][0].transcript;
184
+ }
185
+ this.recordValue = results;
186
+ if (this.recordOptions.onResult) {
187
+ this.recordOptions.onResult(results);
188
+ }
189
+ };
190
+ this.recordRecognition.start();
191
+ } else {
192
+ const error = {
193
+ code: -1,
194
+ message: 'The current browser does not support voice recognition'
195
+ };
196
+ if (this.recordOptions.onError) {
197
+ this.recordOptions.onError(error);
198
+ }
199
+ }
200
+ },
201
+ /**
202
+ * 停止语音识别
203
+ */
204
+ stopRecord() {
205
+ if (this.recordRecognition) {
206
+ this.recordRecognition.stop();
207
+ }
208
+ },
209
+ /**
210
+ * 清理语音识别资源
211
+ */
212
+ cleanupRecord() {
213
+ this.stopRecord();
214
+ this.recordRecognition = null;
215
+ }
216
+ },
217
+ beforeDestroy() {
218
+ this.cleanupRecord();
219
+ }
220
+ });
221
+ // CONCATENATED MODULE: ./src/mixins/sendMixin.js
222
+ /**
223
+ * XRequest 类 - 用于处理 SSE 和 Fetch 请求
224
+ */
225
+ class XRequest {
226
+ constructor(options = {}) {
227
+ const {
228
+ baseURL,
229
+ onAbort,
230
+ onMessage,
231
+ onError,
232
+ baseOptions,
233
+ transformer,
234
+ type,
235
+ onFinish,
236
+ onOpen
237
+ } = options;
238
+ this._instance = null;
239
+ this._transformer = transformer;
240
+ this._baseURL = baseURL || '';
241
+ this._baseOptions = baseOptions || {};
242
+ this._onAbort = onAbort;
243
+ this._onMessage = onMessage;
244
+ this._onError = onError;
245
+ this._onOpen = onOpen;
246
+ this._type = type || 'sse';
247
+ this._controller = null;
248
+ this._onFinish = onFinish;
249
+ this._messages = [];
250
+ // 添加结束标志和超时控制
251
+ this._isFinished = false;
252
+ this._finishTimeout = null;
253
+
254
+ // 绑定方法上下文
255
+ this.abort = this.abort.bind(this);
256
+ this.send = this.send.bind(this);
257
+ }
258
+ _sendWithFetch(url, options = {}) {
259
+ this._controller = new AbortController();
260
+ const signal = this._controller.signal;
261
+ const fetchOptions = {
262
+ ...options,
263
+ signal
264
+ };
265
+ return fetch(this._baseURL + url, fetchOptions).then(res => res.body).then(async body => {
266
+ if (!body) {
267
+ return Promise.reject(new Error('Response body is null in stream mode'));
268
+ }
269
+ const reader = body.getReader();
270
+ const decoder = new TextDecoder('utf-8');
271
+ let done = false;
272
+ while (!done) {
273
+ const {
274
+ value,
275
+ done: streamDone
276
+ } = await reader.read();
277
+ done = streamDone;
278
+ if (streamDone) {
279
+ this._onFinish && this._onFinish(this._messages);
280
+ return;
281
+ }
282
+ if (value) {
283
+ const chunk = decoder.decode(value, {
284
+ stream: true
285
+ });
286
+ const chunkUse = chunk.startsWith('data: ') ? chunk.slice(6) : chunk;
287
+ try {
288
+ const res = this._transformer ? this._transformer(chunkUse) : chunkUse;
289
+ this._messages.push(res);
290
+ this._onMessage && this._onMessage(res);
291
+ } catch (error) {
292
+ this._onError && this._onError(error);
293
+ this._controller && this._controller.abort();
294
+ return Promise.reject(error);
295
+ }
296
+ }
297
+ }
298
+ }).catch(err => {
299
+ if (err.name === 'AbortError') {
300
+ this._onAbort && this._onAbort(this._messages);
301
+ return;
302
+ }
303
+ this._onError && this._onError(err);
304
+ this._controller && this._controller.abort();
305
+ });
306
+ }
307
+ _sendWithSSE(url, options = {}) {
308
+ const es = new EventSource(this._baseURL + url, {
309
+ ...this._baseOptions,
310
+ ...options
311
+ });
312
+ es.onmessage = e => {
313
+ // 检查是否是结束消息
314
+ if (e.data === '[DONE]' || e.data === 'data: [DONE]') {
315
+ this._isFinished = true;
316
+ this._onFinish && this._onFinish(this._messages);
317
+ this.abort();
318
+ return;
319
+ }
320
+ const res = this._transformer ? this._transformer(e.data) : e;
321
+ this._messages.push(res);
322
+ this._onMessage && this._onMessage(res);
323
+
324
+ // 重置超时定时器
325
+ if (this._finishTimeout) {
326
+ clearTimeout(this._finishTimeout);
327
+ }
328
+
329
+ // 设置超时检测:如果 10 秒内没有新消息,认为连接可能已结束
330
+ this._finishTimeout = setTimeout(() => {
331
+ if (!this._isFinished && es.readyState !== EventSource.CONNECTING) {
332
+ this._isFinished = true;
333
+ this._onFinish && this._onFinish(this._messages);
334
+ this.abort();
335
+ }
336
+ }, 10000);
337
+ };
338
+ es.onopen = () => {
339
+ this._onOpen && this._onOpen();
340
+ };
341
+ es.onerror = ev => {
342
+ // 清除超时定时器
343
+ if (this._finishTimeout) {
344
+ clearTimeout(this._finishTimeout);
345
+ this._finishTimeout = null;
346
+ }
347
+
348
+ // 如果已经标记为结束,直接返回
349
+ if (this._isFinished) {
350
+ return;
351
+ }
352
+
353
+ // 更可靠的结束检测逻辑
354
+ if (es.readyState === EventSource.CLOSED || this._messages.length > 0 && es.readyState !== EventSource.CONNECTING) {
355
+ // 有消息且连接状态不是正在连接,很可能是正常结束
356
+ this._isFinished = true;
357
+ this._onFinish && this._onFinish(this._messages);
358
+ } else {
359
+ // 真正的错误情况
360
+ this._onError && this._onError(es, ev);
361
+ }
362
+ this.abort();
363
+ };
364
+ this._instance = es;
365
+ return es;
366
+ }
367
+ send(url, options = {}) {
368
+ switch (this._type) {
369
+ case 'fetch':
370
+ this._sendWithFetch(url, options);
371
+ break;
372
+ default:
373
+ this._sendWithSSE(url, options);
374
+ }
375
+ return this;
376
+ }
377
+ abort() {
378
+ // 清除超时定时器
379
+ if (this._finishTimeout) {
380
+ clearTimeout(this._finishTimeout);
381
+ this._finishTimeout = null;
382
+ }
383
+ if (this._instance && this._instance.close) {
384
+ this._instance.close();
385
+ }
386
+ this._instance = null;
387
+ if (this._controller) {
388
+ this._controller.abort();
389
+ }
390
+ this._controller = null;
391
+
392
+ // 只有在未正常结束时才调用 onAbort
393
+ if (!this._isFinished) {
394
+ this._onAbort && this._onAbort(this._messages);
395
+ }
396
+ this._messages = [];
397
+ this._isFinished = false;
398
+ }
399
+ }
400
+
401
+ /**
402
+ * sendMixin - Vue2 版本
403
+ * 用于处理发送操作管理请求状态的 mixin,支持可选的中止功能,同时支持 Promise 和 SSE(服务端事件)
404
+ */
405
+ const sendMixin = {
406
+ data() {
407
+ return {
408
+ loading: false,
409
+ _sendPromise: null,
410
+ _sendController: null
411
+ };
412
+ },
413
+ methods: {
414
+ /**
415
+ * 初始化发送配置
416
+ * @param {Object} options - 配置选项
417
+ * @param {Function} options.onAbort - 中止回调
418
+ * @param {Function} options.sendHandler - 发送处理器
419
+ * @param {Function} options.abortHandler - 中止处理器
420
+ * @param {Function} options.finishHandler - 完成处理器
421
+ */
422
+ initSend(options = {}) {
423
+ this._sendOptions = {
424
+ onAbort: options.onAbort,
425
+ sendHandler: options.sendHandler,
426
+ abortHandler: options.abortHandler,
427
+ finishHandler: options.finishHandler
428
+ };
429
+ },
430
+ /**
431
+ * 发送操作
432
+ * @param {...any} args - 传递给 sendHandler 的参数
433
+ */
434
+ handleSend(...args) {
435
+ if (this.loading) {
436
+ return;
437
+ }
438
+ if (this._sendOptions && this._sendOptions.sendHandler) {
439
+ this._sendOptions.sendHandler(...args);
440
+ }
441
+ this.loading = true;
442
+ },
443
+ /**
444
+ * 中止操作
445
+ */
446
+ handleAbort() {
447
+ this.loading = false;
448
+ if (this._sendOptions && this._sendOptions.abortHandler) {
449
+ this._sendOptions.abortHandler();
450
+ }
451
+ if (this._sendOptions && this._sendOptions.onAbort) {
452
+ this._sendOptions.onAbort();
453
+ }
454
+ },
455
+ /**
456
+ * 完成操作
457
+ */
458
+ handleFinish() {
459
+ this.loading = false;
460
+ if (this._sendOptions && this._sendOptions.finishHandler) {
461
+ this._sendOptions.finishHandler();
462
+ }
463
+ },
464
+ /**
465
+ * 创建 XRequest 实例的便捷方法
466
+ * @param {Object} options - XRequest 配置选项
467
+ * @returns {XRequest} XRequest 实例
468
+ */
469
+ createXRequest(options = {}) {
470
+ return new XRequest({
471
+ ...options,
472
+ onAbort: (...args) => {
473
+ this.handleAbort();
474
+ if (options.onAbort) {
475
+ options.onAbort(...args);
476
+ }
477
+ },
478
+ onFinish: (...args) => {
479
+ this.handleFinish();
480
+ if (options.onFinish) {
481
+ options.onFinish(...args);
482
+ }
483
+ }
484
+ });
485
+ }
486
+ },
487
+ beforeDestroy() {
488
+ // 组件销毁时清理资源
489
+ if (this._sendController) {
490
+ this._sendController.abort();
491
+ }
492
+ }
493
+ };
494
+
495
+ /**
496
+ * 工具函数版本 - 用于非组件场景
497
+ * @param {Object} options - 配置选项
498
+ * @returns {Object} 包含发送相关方法的对象
499
+ */
500
+ function createSendUtils(options = {}) {
501
+ const state = {
502
+ loading: false
503
+ };
504
+ const handleSend = (...args) => {
505
+ if (state.loading) {
506
+ return;
507
+ }
508
+ if (options.sendHandler) {
509
+ options.sendHandler(...args);
510
+ }
511
+ state.loading = true;
512
+ };
513
+ const handleAbort = () => {
514
+ state.loading = false;
515
+ if (options.abortHandler) {
516
+ options.abortHandler();
517
+ }
518
+ if (options.onAbort) {
519
+ options.onAbort();
520
+ }
521
+ };
522
+ const handleFinish = () => {
523
+ state.loading = false;
524
+ if (options.finishHandler) {
525
+ options.finishHandler();
526
+ }
527
+ };
528
+ return {
529
+ state,
530
+ send: handleSend,
531
+ abort: handleAbort,
532
+ finish: handleFinish
533
+ };
534
+ }
535
+ /* harmony default export */ var mixins_sendMixin = (sendMixin);
536
+ // CONCATENATED MODULE: ./src/mixins/streamMixin.js
537
+ /**
538
+ * @fileoverview Vue2 流式数据处理 Mixin
539
+ * 从 Vue3 useXStream hooks 转换而来,支持 SSE 数据解析和中断功能
540
+ * @author Element UI X Team
541
+ * @version 1.0.0
542
+ */
543
+
544
+ /**
545
+ * 默认流分隔符
546
+ * @type {string}
547
+ * @constant
548
+ */
549
+ const DEFAULT_STREAM_SEPARATOR = '\n\n';
550
+
551
+ /**
552
+ * 默认部分分隔符
553
+ * @type {string}
554
+ * @constant
555
+ */
556
+ const DEFAULT_PART_SEPARATOR = '\n';
557
+
558
+ /**
559
+ * 默认键值分隔符
560
+ * @type {string}
561
+ * @constant
562
+ */
563
+ const DEFAULT_KV_SEPARATOR = ':';
564
+
565
+ /**
566
+ * SSE 字段类型定义
567
+ * @typedef {'data'|'event'|'id'|'retry'} SSEFields
568
+ */
569
+
570
+ /**
571
+ * SSE 输出对象类型
572
+ * @typedef {Object} SSEOutput
573
+ * @property {string} [data] - 数据字段
574
+ * @property {string} [event] - 事件字段
575
+ * @property {string} [id] - ID字段
576
+ * @property {string} [retry] - 重试字段
577
+ */
578
+
579
+ /**
580
+ * 流配置选项
581
+ * @typedef {Object} XStreamOptions
582
+ * @property {ReadableStream<Uint8Array>} readableStream - 可读流
583
+ * @property {TransformStream<string, *>} [transformStream] - 可选的转换流
584
+ */
585
+
586
+ /**
587
+ * 流状态对象
588
+ * @typedef {Object} StreamState
589
+ * @property {Array<*>} data - 流数据数组
590
+ * @property {Error|null} error - 错误信息
591
+ * @property {boolean} loading - 加载状态
592
+ */
593
+
594
+ /**
595
+ * 流回调函数配置
596
+ * @typedef {Object} StreamCallbacks
597
+ * @property {function(*): void} [onData] - 数据回调函数
598
+ * @property {function(Array<*>): void} [onComplete] - 完成回调函数
599
+ * @property {function(Error): void} [onError] - 错误回调函数
600
+ * @property {function(): void} [onCancel] - 取消回调函数
601
+ * @property {function(): void} [onFinish] - 结束回调函数
602
+ */
603
+
604
+ /**
605
+ * 流工具函数返回对象
606
+ * @typedef {Object} StreamUtils
607
+ * @property {StreamState} state - 流状态
608
+ * @property {function(XStreamOptions): Promise<void>} startStream - 启动流处理
609
+ * @property {function(): void} cancel - 取消流处理
610
+ * @property {function(): void} reset - 重置流状态
611
+ */
612
+
613
+ /**
614
+ * 验证字符串是否有效(非空且非空白)
615
+ * @param {string} str - 待验证的字符串
616
+ * @returns {boolean} 是否为有效字符串
617
+ */
618
+ const isValidString = str => (str === null || str === undefined ? '' : str).trim() !== '';
619
+
620
+ /**
621
+ * 创建流分割转换器
622
+ * 按照默认分隔符分割流数据
623
+ * @returns {TransformStream<string, string>} 流分割转换器
624
+ */
625
+ function splitStream() {
626
+ let buffer = '';
627
+ return new TransformStream({
628
+ /**
629
+ * 转换函数
630
+ * @param {string} chunk - 数据块
631
+ * @param {TransformStreamDefaultController<string>} controller - 控制器
632
+ */
633
+ transform(chunk, controller) {
634
+ buffer += chunk;
635
+ const parts = buffer.split(DEFAULT_STREAM_SEPARATOR);
636
+ parts.slice(0, -1).forEach(part => {
637
+ if (isValidString(part)) controller.enqueue(part);
638
+ });
639
+ buffer = parts[parts.length - 1];
640
+ },
641
+ /**
642
+ * 刷新函数
643
+ * @param {TransformStreamDefaultController<string>} controller - 控制器
644
+ */
645
+ flush(controller) {
646
+ if (isValidString(buffer)) controller.enqueue(buffer);
647
+ }
648
+ });
649
+ }
650
+
651
+ /**
652
+ * 创建 SSE 数据解析转换器
653
+ * 将文本数据解析为 SSE 格式对象
654
+ * @returns {TransformStream<string, SSEOutput>} SSE 解析转换器
655
+ */
656
+ function splitPart() {
657
+ return new TransformStream({
658
+ /**
659
+ * 转换函数
660
+ * @param {string} partChunk - 数据块
661
+ * @param {TransformStreamDefaultController<SSEOutput>} controller - 控制器
662
+ */
663
+ transform(partChunk, controller) {
664
+ const lines = partChunk.split(DEFAULT_PART_SEPARATOR);
665
+ const sseEvent = lines.reduce((acc, line) => {
666
+ const sepIndex = line.indexOf(DEFAULT_KV_SEPARATOR);
667
+ if (sepIndex === -1) return acc;
668
+ const key = line.slice(0, sepIndex);
669
+ if (!isValidString(key)) return acc;
670
+ const value = line.slice(sepIndex + 1);
671
+ return {
672
+ ...acc,
673
+ [key]: value
674
+ };
675
+ }, {});
676
+ if (Object.keys(sseEvent).length > 0) controller.enqueue(sseEvent);
677
+ }
678
+ });
679
+ }
680
+
681
+ /**
682
+ * 核心流处理函数(支持中断)
683
+ * @param {XStreamOptions} options - 配置选项
684
+ * @param {ReadableStream<Uint8Array>} options.readableStream - 可读流
685
+ * @param {TransformStream<string, *>} [options.transformStream] - 可选的转换流
686
+ * @param {AbortSignal} [signal] - 中断信号
687
+ * @returns {ReadableStream<*>} 处理后的流,支持异步迭代
688
+ * @throws {TypeError} 当 readableStream 不是 ReadableStream 实例时抛出
689
+ */
690
+ function XStream(options, signal) {
691
+ const {
692
+ readableStream,
693
+ transformStream
694
+ } = options;
695
+ if (!(readableStream instanceof ReadableStream)) {
696
+ throw new TypeError('options.readableStream 必须是 ReadableStream 的实例。');
697
+ }
698
+ const decoderStream = new TextDecoderStream();
699
+ const processedStream = transformStream ? readableStream.pipeThrough(decoderStream).pipeThrough(transformStream) : readableStream.pipeThrough(decoderStream).pipeThrough(splitStream()).pipeThrough(splitPart());
700
+
701
+ // 为流添加异步迭代器并处理中断信号
702
+ processedStream[Symbol.asyncIterator] = async function* () {
703
+ const reader = this.getReader();
704
+ this.reader = reader; // 保存读取器引用
705
+ try {
706
+ while (true) {
707
+ if (signal && signal.aborted) {
708
+ await reader.cancel(); // 主动取消 reader
709
+ break;
710
+ }
711
+ const {
712
+ done,
713
+ value
714
+ } = await reader.read();
715
+ if (done) break;
716
+ if (value) yield value;
717
+ }
718
+ } finally {
719
+ reader.releaseLock(); // 释放锁
720
+ }
721
+ };
722
+ return processedStream;
723
+ }
724
+
725
+ /**
726
+ * streamMixin - Vue2 版本
727
+ * 用于处理流式数据的 mixin,支持 SSE 数据解析和中断功能
728
+ * @namespace streamMixin
729
+ * @type {Object}
730
+ */
731
+ const streamMixin = {
732
+ /**
733
+ * 组件数据
734
+ * @returns {Object} 响应式数据对象
735
+ * @property {Array<*>} streamData - 流数据数组
736
+ * @property {Error|null} streamError - 流错误信息
737
+ * @property {boolean} streamLoading - 流处理状态
738
+ * @property {AbortController|null} _abortController - 私有:中断控制器
739
+ * @property {ReadableStream|null} _currentStream - 私有:当前流引用
740
+ */
741
+ data() {
742
+ return {
743
+ streamData: [],
744
+ streamError: null,
745
+ streamLoading: false,
746
+ _abortController: null,
747
+ _currentStream: null
748
+ };
749
+ },
750
+ methods: {
751
+ /**
752
+ * 启动流式请求
753
+ * @async
754
+ * @param {XStreamOptions} options - 流配置选项
755
+ * @param {ReadableStream<Uint8Array>} options.readableStream - 可读流
756
+ * @param {TransformStream<string, *>} [options.transformStream] - 可选的转换流
757
+ * @returns {Promise<void>} 流处理完成的 Promise
758
+ * @fires streamMixin#stream-data - 收到新数据时触发
759
+ * @fires streamMixin#stream-complete - 流完成时触发
760
+ * @fires streamMixin#stream-error - 流错误时触发
761
+ * @fires streamMixin#stream-finish - 流处理结束时触发
762
+ * @example
763
+ * // 基础使用
764
+ * const response = await fetch('/api/stream')
765
+ * await this.startStream({ readableStream: response.body })
766
+ *
767
+ * // 使用自定义转换流
768
+ * const customTransform = new TransformStream({ ... })
769
+ * await this.startStream({
770
+ * readableStream: response.body,
771
+ * transformStream: customTransform
772
+ * })
773
+ */
774
+ async startStream(options) {
775
+ this.streamLoading = true;
776
+ this.streamError = null;
777
+ this.streamData = [];
778
+ this._abortController = new AbortController();
779
+ this._currentStream = XStream(options, this._abortController.signal);
780
+ try {
781
+ for await (const item of this._currentStream) {
782
+ this.streamData.push(item);
783
+ // 触发数据更新事件
784
+ /**
785
+ * 流数据事件
786
+ * @event streamMixin#stream-data
787
+ * @type {*} 流数据项
788
+ */
789
+ this.$emit('stream-data', item);
790
+ }
791
+ // 流完成事件
792
+ /**
793
+ * 流完成事件
794
+ * @event streamMixin#stream-complete
795
+ * @type {Array<*>} 所有流数据
796
+ */
797
+ this.$emit('stream-complete', this.streamData);
798
+ } catch (err) {
799
+ if (err instanceof Error) {
800
+ this.streamError = err;
801
+ /**
802
+ * 流错误事件
803
+ * @event streamMixin#stream-error
804
+ * @type {Error} 错误对象
805
+ */
806
+ this.$emit('stream-error', err);
807
+ }
808
+ } finally {
809
+ this.streamLoading = false;
810
+ this._currentStream = null; // 释放流引用
811
+ this._abortController = null; // 释放控制器
812
+ /**
813
+ * 流处理结束事件
814
+ * @event streamMixin#stream-finish
815
+ */
816
+ this.$emit('stream-finish');
817
+ }
818
+ },
819
+ /**
820
+ * 中断流式请求(强制关闭流)
821
+ * @returns {void}
822
+ * @fires streamMixin#stream-cancel - 流被中断时触发
823
+ * @example
824
+ * this.cancelStream()
825
+ */
826
+ cancelStream() {
827
+ if (this._abortController) {
828
+ this._abortController.abort();
829
+ /**
830
+ * 流取消事件
831
+ * @event streamMixin#stream-cancel
832
+ */
833
+ this.$emit('stream-cancel');
834
+ }
835
+ },
836
+ /**
837
+ * 重置流状态
838
+ * 清空所有数据和状态,但不触发事件
839
+ * @returns {void}
840
+ * @example
841
+ * this.resetStream()
842
+ */
843
+ resetStream() {
844
+ this.streamData = [];
845
+ this.streamError = null;
846
+ this.streamLoading = false;
847
+ this._abortController = null;
848
+ this._currentStream = null;
849
+ },
850
+ /**
851
+ * 创建流处理器的便捷方法
852
+ * @param {ReadableStream<Uint8Array>} readableStream - 可读流
853
+ * @param {TransformStream<string, *>} [transformStream] - 可选的转换流
854
+ * @returns {XStreamOptions} 流处理器配置对象
855
+ * @example
856
+ * const processor = this.createStreamProcessor(response.body)
857
+ * await this.startStream(processor)
858
+ */
859
+ createStreamProcessor(readableStream, transformStream) {
860
+ return {
861
+ readableStream,
862
+ transformStream
863
+ };
864
+ }
865
+ },
866
+ /**
867
+ * 组件销毁前的生命周期钩子
868
+ * 自动清理流资源
869
+ * @returns {void}
870
+ */
871
+ beforeDestroy() {
872
+ // 组件销毁时清理资源
873
+ this.cancelStream();
874
+ }
875
+ };
876
+
877
+ /**
878
+ * 工具函数版本 - 用于非组件场景
879
+ * @param {StreamCallbacks} [callbacks={}] - 回调函数配置
880
+ * @param {function(*): void} [callbacks.onData] - 数据回调函数
881
+ * @param {function(Array<*>): void} [callbacks.onComplete] - 完成回调函数
882
+ * @param {function(Error): void} [callbacks.onError] - 错误回调函数
883
+ * @param {function(): void} [callbacks.onCancel] - 取消回调函数
884
+ * @param {function(): void} [callbacks.onFinish] - 结束回调函数
885
+ * @returns {StreamUtils} 包含流处理相关方法的对象
886
+ * @example
887
+ * // 基础使用
888
+ * const streamUtils = createStreamUtils({
889
+ * onData: (item) => console.log('数据:', item),
890
+ * onComplete: (allData) => console.log('完成:', allData),
891
+ * onError: (error) => console.error('错误:', error)
892
+ * })
893
+ *
894
+ * const response = await fetch('/api/stream')
895
+ * await streamUtils.startStream({ readableStream: response.body })
896
+ */
897
+ function createStreamUtils(callbacks = {}) {
898
+ /**
899
+ * 流状态对象
900
+ * @type {StreamState}
901
+ */
902
+ const state = {
903
+ data: [],
904
+ error: null,
905
+ loading: false
906
+ };
907
+ let abortController = null;
908
+ let currentStream = null;
909
+
910
+ /**
911
+ * 启动流处理
912
+ * @async
913
+ * @param {XStreamOptions} options - 流配置选项
914
+ * @returns {Promise<void>} 流处理完成的 Promise
915
+ */
916
+ const startStream = async options => {
917
+ state.loading = true;
918
+ state.error = null;
919
+ state.data = [];
920
+ abortController = new AbortController();
921
+ currentStream = XStream(options, abortController.signal);
922
+ try {
923
+ for await (const item of currentStream) {
924
+ state.data.push(item);
925
+ if (callbacks.onData) {
926
+ callbacks.onData(item);
927
+ }
928
+ }
929
+ if (callbacks.onComplete) {
930
+ callbacks.onComplete(state.data);
931
+ }
932
+ } catch (err) {
933
+ if (err instanceof Error) {
934
+ state.error = err;
935
+ if (callbacks.onError) {
936
+ callbacks.onError(err);
937
+ }
938
+ }
939
+ } finally {
940
+ state.loading = false;
941
+ currentStream = null;
942
+ abortController = null;
943
+ if (callbacks.onFinish) {
944
+ callbacks.onFinish();
945
+ }
946
+ }
947
+ };
948
+
949
+ /**
950
+ * 取消流处理
951
+ * @returns {void}
952
+ */
953
+ const cancel = () => {
954
+ if (abortController) {
955
+ abortController.abort();
956
+ if (callbacks.onCancel) {
957
+ callbacks.onCancel();
958
+ }
959
+ }
960
+ };
961
+
962
+ /**
963
+ * 重置流状态
964
+ * @returns {void}
965
+ */
966
+ const reset = () => {
967
+ state.data = [];
968
+ state.error = null;
969
+ state.loading = false;
970
+ abortController = null;
971
+ currentStream = null;
972
+ };
973
+ return {
974
+ state,
975
+ startStream,
976
+ cancel,
977
+ reset
978
+ };
979
+ }
980
+
981
+ // 导出常量和工具函数
982
+
983
+
984
+ /**
985
+ * 默认导出 streamMixin
986
+ * @type {Object}
987
+ */
988
+ /* harmony default export */ var mixins_streamMixin = (streamMixin);
989
+ // CONCATENATED MODULE: ./src/mixins/index.js
990
+ /**
991
+ * Mixins 入口文件
992
+ */
993
+
994
+
995
+
996
+
997
+
998
+ /* harmony default export */ var mixins = __webpack_exports__["default"] = ({
999
+ recordMixin: recordMixin,
1000
+ sendMixin: sendMixin,
1001
+ XRequest: XRequest,
1002
+ createSendUtils: createSendUtils,
1003
+ streamMixin: streamMixin,
1004
+ createStreamUtils: createStreamUtils,
1005
+ XStream: XStream,
1006
+ splitStream: splitStream,
1007
+ splitPart: splitPart,
1008
+ isValidString: isValidString,
1009
+ DEFAULT_STREAM_SEPARATOR: DEFAULT_STREAM_SEPARATOR,
1010
+ DEFAULT_PART_SEPARATOR: DEFAULT_PART_SEPARATOR,
1011
+ DEFAULT_KV_SEPARATOR: DEFAULT_KV_SEPARATOR
1012
+ });
1013
+
1014
+ /***/ })
1015
+
1016
+ /******/ });