vue-element-ui-x 0.1.8-beta → 0.1.10-beta
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/lib/components/Attachments/index.js +229 -237
- package/lib/components/Bubble/index.js +47 -34
- package/lib/components/BubbleList/index.js +57 -49
- package/lib/components/Conversations/index.js +32 -69
- package/lib/components/FilesCard/index.js +207 -211
- package/lib/components/Prompts/index.js +21 -21
- package/lib/components/Sender/index.js +18 -18
- package/lib/components/Think/index.js +1 -1
- package/lib/components/Thinking/index.js +1 -1
- package/lib/components/ThoughtChain/index.js +60 -47
- package/lib/components/Typewriter/index.js +46 -33
- package/lib/components/Welcome/index.js +1 -1
- package/lib/index.common.js +1 -1
- package/lib/index.esm.js +1 -1
- package/lib/index.js +479 -427
- package/lib/index.umd.js +1 -1
- package/lib/mixins/index.js +105 -16
- package/package.json +1 -1
- package/src/components/Attachments/index.js +8 -8
- package/src/components/Attachments/src/main.vue +10 -10
- package/src/components/Bubble/index.js +6 -6
- package/src/components/Bubble/src/main.vue +299 -299
- package/src/components/BubbleList/index.js +8 -8
- package/src/components/BubbleList/src/loading.vue +75 -75
- package/src/components/BubbleList/src/main.vue +461 -466
- package/src/components/Conversations/index.js +8 -8
- package/src/components/Conversations/src/components/item.vue +13 -34
- package/src/components/Conversations/src/main.vue +622 -635
- package/src/components/FilesCard/index.js +8 -8
- package/src/components/FilesCard/src/fileSvg/audio.vue +38 -38
- package/src/components/FilesCard/src/fileSvg/code.vue +35 -35
- package/src/components/FilesCard/src/fileSvg/database.vue +94 -94
- package/src/components/FilesCard/src/fileSvg/excel.vue +38 -38
- package/src/components/FilesCard/src/fileSvg/file.vue +40 -40
- package/src/components/FilesCard/src/fileSvg/image.vue +40 -40
- package/src/components/FilesCard/src/fileSvg/index.js +46 -46
- package/src/components/FilesCard/src/fileSvg/link.vue +54 -54
- package/src/components/FilesCard/src/fileSvg/mark.vue +38 -38
- package/src/components/FilesCard/src/fileSvg/pdf.vue +38 -38
- package/src/components/FilesCard/src/fileSvg/ppt.vue +38 -38
- package/src/components/FilesCard/src/fileSvg/three.vue +38 -38
- package/src/components/FilesCard/src/fileSvg/txt.vue +38 -38
- package/src/components/FilesCard/src/fileSvg/unknown.vue +54 -54
- package/src/components/FilesCard/src/fileSvg/video.vue +38 -38
- package/src/components/FilesCard/src/fileSvg/word.vue +38 -38
- package/src/components/FilesCard/src/fileSvg/zip.vue +38 -38
- package/src/components/FilesCard/src/main.vue +4 -8
- package/src/components/FilesCard/src/options.js +18 -18
- package/src/components/Prompts/index.js +8 -8
- package/src/components/Prompts/src/main.vue +248 -248
- package/src/components/Sender/index.js +8 -8
- package/src/components/Sender/src/components/ClearButton.vue +28 -28
- package/src/components/Sender/src/components/Loading.vue +53 -53
- package/src/components/Sender/src/components/LoadingButton.vue +39 -39
- package/src/components/Sender/src/components/SendButton.vue +26 -26
- package/src/components/Sender/src/components/SpeechButton.vue +24 -24
- package/src/components/Sender/src/components/SpeechLoading.vue +87 -87
- package/src/components/Sender/src/components/SpeechLoadingButton.vue +43 -43
- package/src/components/Sender/src/main.vue +4 -3
- package/src/components/Think/index.js +8 -8
- package/src/components/Think/src/main.vue +190 -190
- package/src/components/Thinking/index.js +8 -8
- package/src/components/ThoughtChain/index.js +8 -8
- package/src/components/ThoughtChain/src/main.vue +293 -293
- package/src/components/Typewriter/index.js +8 -8
- package/src/components/Typewriter/src/main.vue +10 -2
- package/src/components/Welcome/index.js +8 -8
- package/src/components/Welcome/src/main.vue +151 -151
- package/src/mixins/recordMixin.js +0 -1
- package/src/mixins/sendMixin.js +104 -11
- package/src/mixins/streamMixin.js +3 -5
- package/src/styles/Attachments.scss +236 -236
- package/src/styles/Bubble.scss +157 -157
- package/src/styles/BubbleList.scss +148 -148
- package/src/styles/Conversations.scss +260 -260
- package/src/styles/FilesCard.scss +221 -221
- package/src/styles/Prompts.scss +195 -195
- package/src/styles/Sender.scss +199 -199
- package/src/styles/Think.scss +134 -134
- package/src/styles/ThoughtChain.scss +113 -113
- package/src/styles/Typewriter.scss +66 -66
- package/src/theme/var.scss +72 -72
- package/lib/attachments.js +0 -3082
- package/lib/bubble-list.js +0 -13840
- package/lib/bubble.js +0 -13125
- package/lib/conversations.js +0 -18825
- package/lib/files-card.js +0 -2471
- package/lib/mixins.js +0 -1016
- package/lib/prompts.js +0 -832
- package/lib/sender.js +0 -1911
- package/lib/think.js +0 -799
- package/lib/thinking.js +0 -809
- package/lib/thought-chain.js +0 -30391
- package/lib/typewriter.js +0 -12788
- package/lib/welcome.js +0 -755
- package/src/styles/button.scss +0 -302
- package/src/styles/var.scss +0 -1052
package/lib/mixins.js
DELETED
|
@@ -1,1016 +0,0 @@
|
|
|
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
|
-
/******/ });
|