vue-element-ui-x 1.0.4 → 1.0.5
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/Sender/index.js +1 -1
- package/lib/components/ThoughtChain/index.js +30 -30
- package/lib/index.common.js +1 -1
- package/lib/index.esm.js +1 -1
- package/lib/index.js +774 -774
- package/lib/index.umd.js +1 -1
- package/package.json +5 -10
- package/src/components/Attachments/index.js +0 -8
- package/src/components/Attachments/src/main.vue +0 -529
- package/src/components/Bubble/index.js +0 -6
- package/src/components/Bubble/src/main.vue +0 -288
- package/src/components/BubbleList/index.js +0 -8
- package/src/components/BubbleList/src/loading.vue +0 -75
- package/src/components/BubbleList/src/main.vue +0 -444
- package/src/components/Conversations/index.js +0 -8
- package/src/components/Conversations/src/components/item.vue +0 -350
- package/src/components/Conversations/src/main.vue +0 -587
- package/src/components/FilesCard/index.js +0 -8
- package/src/components/FilesCard/src/fileSvg/audio.vue +0 -38
- package/src/components/FilesCard/src/fileSvg/changeFileName.bat +0 -18
- package/src/components/FilesCard/src/fileSvg/code.vue +0 -35
- package/src/components/FilesCard/src/fileSvg/database.vue +0 -94
- package/src/components/FilesCard/src/fileSvg/excel.vue +0 -38
- package/src/components/FilesCard/src/fileSvg/file.vue +0 -40
- package/src/components/FilesCard/src/fileSvg/image.vue +0 -40
- package/src/components/FilesCard/src/fileSvg/index.js +0 -46
- package/src/components/FilesCard/src/fileSvg/link.vue +0 -54
- package/src/components/FilesCard/src/fileSvg/mark.vue +0 -38
- package/src/components/FilesCard/src/fileSvg/pdf.vue +0 -38
- package/src/components/FilesCard/src/fileSvg/ppt.vue +0 -38
- package/src/components/FilesCard/src/fileSvg/three.vue +0 -38
- package/src/components/FilesCard/src/fileSvg/txt.vue +0 -38
- package/src/components/FilesCard/src/fileSvg/unknown.vue +0 -54
- package/src/components/FilesCard/src/fileSvg/video.vue +0 -38
- package/src/components/FilesCard/src/fileSvg/word.vue +0 -38
- package/src/components/FilesCard/src/fileSvg/zip.vue +0 -38
- package/src/components/FilesCard/src/main.vue +0 -403
- package/src/components/FilesCard/src/options.js +0 -18
- package/src/components/Prompts/index.js +0 -8
- package/src/components/Prompts/src/main.vue +0 -248
- package/src/components/Sender/index.js +0 -8
- package/src/components/Sender/src/components/ClearButton.vue +0 -28
- package/src/components/Sender/src/components/Loading.vue +0 -53
- package/src/components/Sender/src/components/LoadingButton.vue +0 -37
- package/src/components/Sender/src/components/SendButton.vue +0 -26
- package/src/components/Sender/src/components/SpeechButton.vue +0 -24
- package/src/components/Sender/src/components/SpeechLoading.vue +0 -87
- package/src/components/Sender/src/components/SpeechLoadingButton.vue +0 -41
- package/src/components/Sender/src/main.vue +0 -803
- package/src/components/Thinking/index.js +0 -8
- package/src/components/Thinking/src/main.vue +0 -199
- package/src/components/ThoughtChain/index.js +0 -8
- package/src/components/ThoughtChain/src/main.vue +0 -291
- package/src/components/Typewriter/index.js +0 -8
- package/src/components/Typewriter/src/main.vue +0 -255
- package/src/components/Welcome/index.js +0 -8
- package/src/components/Welcome/src/main.vue +0 -151
- package/src/index.js +0 -104
- package/src/locale/index.js +0 -97
- package/src/locale/lang/ar.js +0 -18
- package/src/locale/lang/de.js +0 -18
- package/src/locale/lang/en.js +0 -18
- package/src/locale/lang/es.js +0 -18
- package/src/locale/lang/fr.js +0 -18
- package/src/locale/lang/index.js +0 -62
- package/src/locale/lang/it.js +0 -18
- package/src/locale/lang/ja.js +0 -18
- package/src/locale/lang/ko.js +0 -18
- package/src/locale/lang/pt-br.js +0 -18
- package/src/locale/lang/ru-RU.js +0 -18
- package/src/locale/lang/zh-CN.js +0 -18
- package/src/locale/lang/zh-TW.js +0 -18
- package/src/locale/mixin.js +0 -9
- package/src/mixins/index.js +0 -49
- package/src/mixins/recordMixin.js +0 -117
- package/src/mixins/sendMixin.js +0 -450
- package/src/mixins/streamMixin.js +0 -497
- package/src/styles/Attachments.scss +0 -236
- package/src/styles/Bubble.scss +0 -158
- package/src/styles/BubbleList.scss +0 -148
- package/src/styles/Conversations.scss +0 -283
- package/src/styles/FilesCard.scss +0 -222
- package/src/styles/Prompts.scss +0 -197
- package/src/styles/Sender.scss +0 -207
- package/src/styles/Thinking.scss +0 -142
- package/src/styles/ThoughtChain.scss +0 -113
- package/src/styles/Typewriter.scss +0 -66
- package/src/styles/Welcome.scss +0 -283
- package/src/theme/var.scss +0 -183
- package/src/utils/index.js +0 -199
- package/src/utils/scrollDetector.js +0 -34
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview 流式数据处理 Mixin
|
|
3
|
-
* 支持 SSE 数据解析和中断功能
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 默认流分隔符
|
|
8
|
-
* @type {string}
|
|
9
|
-
* @constant
|
|
10
|
-
*/
|
|
11
|
-
const DEFAULT_STREAM_SEPARATOR = '\n\n';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* 默认部分分隔符
|
|
15
|
-
* @type {string}
|
|
16
|
-
* @constant
|
|
17
|
-
*/
|
|
18
|
-
const DEFAULT_PART_SEPARATOR = '\n';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* 默认键值分隔符
|
|
22
|
-
* @type {string}
|
|
23
|
-
* @constant
|
|
24
|
-
*/
|
|
25
|
-
const DEFAULT_KV_SEPARATOR = ':';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* SSE 字段类型定义
|
|
29
|
-
* @typedef {'data'|'event'|'id'|'retry'} SSEFields
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* SSE 输出对象类型
|
|
34
|
-
* @typedef {Object} SSEOutput
|
|
35
|
-
* @property {string} [data] - 数据字段
|
|
36
|
-
* @property {string} [event] - 事件字段
|
|
37
|
-
* @property {string} [id] - ID字段
|
|
38
|
-
* @property {string} [retry] - 重试字段
|
|
39
|
-
*/
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 流配置选项
|
|
43
|
-
* @typedef {Object} XStreamOptions
|
|
44
|
-
* @property {ReadableStream<Uint8Array>} readableStream - 可读流
|
|
45
|
-
* @property {TransformStream<string, *>} [transformStream] - 可选的转换流
|
|
46
|
-
*/
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* 流状态对象
|
|
50
|
-
* @typedef {Object} StreamState
|
|
51
|
-
* @property {Array<*>} data - 流数据数组
|
|
52
|
-
* @property {Error|null} error - 错误信息
|
|
53
|
-
* @property {boolean} loading - 加载状态
|
|
54
|
-
*/
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* 流回调函数配置
|
|
58
|
-
* @typedef {Object} StreamCallbacks
|
|
59
|
-
* @property {function(*): void} [onData] - 数据回调函数
|
|
60
|
-
* @property {function(Array<*>): void} [onComplete] - 完成回调函数
|
|
61
|
-
* @property {function(Error): void} [onError] - 错误回调函数
|
|
62
|
-
* @property {function(): void} [onCancel] - 取消回调函数
|
|
63
|
-
* @property {function(): void} [onFinish] - 结束回调函数
|
|
64
|
-
*/
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* 流工具函数返回对象
|
|
68
|
-
* @typedef {Object} StreamUtils
|
|
69
|
-
* @property {StreamState} state - 流状态
|
|
70
|
-
* @property {function(XStreamOptions): Promise<void>} startStream - 启动流处理
|
|
71
|
-
* @property {function(): void} cancel - 取消流处理
|
|
72
|
-
* @property {function(): void} reset - 重置流状态
|
|
73
|
-
*/
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* 验证字符串是否有效(非空且非空白)
|
|
77
|
-
* @param {string} str - 待验证的字符串
|
|
78
|
-
* @returns {boolean} 是否为有效字符串
|
|
79
|
-
*/
|
|
80
|
-
const isValidString = str => (str === null || str === undefined ? '' : str).trim() !== '';
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* 创建流分割转换器
|
|
84
|
-
* 按照默认分隔符分割流数据
|
|
85
|
-
* @returns {TransformStream<string, string>} 流分割转换器
|
|
86
|
-
*/
|
|
87
|
-
function splitStream() {
|
|
88
|
-
let buffer = '';
|
|
89
|
-
|
|
90
|
-
return new TransformStream({
|
|
91
|
-
/**
|
|
92
|
-
* 转换函数
|
|
93
|
-
* @param {string} chunk - 数据块
|
|
94
|
-
* @param {TransformStreamDefaultController<string>} controller - 控制器
|
|
95
|
-
*/
|
|
96
|
-
transform(chunk, controller) {
|
|
97
|
-
buffer += chunk;
|
|
98
|
-
const parts = buffer.split(DEFAULT_STREAM_SEPARATOR);
|
|
99
|
-
parts.slice(0, -1).forEach(part => {
|
|
100
|
-
if (isValidString(part)) controller.enqueue(part);
|
|
101
|
-
});
|
|
102
|
-
buffer = parts[parts.length - 1];
|
|
103
|
-
},
|
|
104
|
-
/**
|
|
105
|
-
* 刷新函数
|
|
106
|
-
* @param {TransformStreamDefaultController<string>} controller - 控制器
|
|
107
|
-
*/
|
|
108
|
-
flush(controller) {
|
|
109
|
-
if (isValidString(buffer)) controller.enqueue(buffer);
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* 创建 SSE 数据解析转换器
|
|
116
|
-
* 将文本数据解析为 SSE 格式对象
|
|
117
|
-
* @returns {TransformStream<string, SSEOutput>} SSE 解析转换器
|
|
118
|
-
*/
|
|
119
|
-
function splitPart() {
|
|
120
|
-
return new TransformStream({
|
|
121
|
-
/**
|
|
122
|
-
* 转换函数
|
|
123
|
-
* @param {string} partChunk - 数据块
|
|
124
|
-
* @param {TransformStreamDefaultController<SSEOutput>} controller - 控制器
|
|
125
|
-
*/
|
|
126
|
-
transform(partChunk, controller) {
|
|
127
|
-
const lines = partChunk.split(DEFAULT_PART_SEPARATOR);
|
|
128
|
-
const sseEvent = lines.reduce((acc, line) => {
|
|
129
|
-
const sepIndex = line.indexOf(DEFAULT_KV_SEPARATOR);
|
|
130
|
-
if (sepIndex === -1) return acc;
|
|
131
|
-
|
|
132
|
-
const key = line.slice(0, sepIndex);
|
|
133
|
-
if (!isValidString(key)) return acc;
|
|
134
|
-
|
|
135
|
-
const value = line.slice(sepIndex + 1);
|
|
136
|
-
return {
|
|
137
|
-
...acc,
|
|
138
|
-
[key]: value,
|
|
139
|
-
};
|
|
140
|
-
}, {});
|
|
141
|
-
|
|
142
|
-
if (Object.keys(sseEvent).length > 0) controller.enqueue(sseEvent);
|
|
143
|
-
},
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* 核心流处理函数(支持中断)
|
|
149
|
-
* @param {XStreamOptions} options - 配置选项
|
|
150
|
-
* @param {ReadableStream<Uint8Array>} options.readableStream - 可读流
|
|
151
|
-
* @param {TransformStream<string, *>} [options.transformStream] - 可选的转换流
|
|
152
|
-
* @param {AbortSignal} [signal] - 中断信号
|
|
153
|
-
* @returns {ReadableStream<*>} 处理后的流,支持异步迭代
|
|
154
|
-
* @throws {TypeError} 当 readableStream 不是 ReadableStream 实例时抛出
|
|
155
|
-
*/
|
|
156
|
-
function XStream(options, signal) {
|
|
157
|
-
const { readableStream, transformStream } = options;
|
|
158
|
-
if (!(readableStream instanceof ReadableStream)) {
|
|
159
|
-
throw new TypeError('options.readableStream 必须是 ReadableStream 的实例。');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const decoderStream = new TextDecoderStream();
|
|
163
|
-
const processedStream = transformStream
|
|
164
|
-
? readableStream.pipeThrough(decoderStream).pipeThrough(transformStream)
|
|
165
|
-
: readableStream.pipeThrough(decoderStream).pipeThrough(splitStream()).pipeThrough(splitPart());
|
|
166
|
-
|
|
167
|
-
// 为流添加异步迭代器并处理中断信号
|
|
168
|
-
processedStream[Symbol.asyncIterator] = async function* () {
|
|
169
|
-
const reader = this.getReader();
|
|
170
|
-
this.reader = reader; // 保存读取器引用
|
|
171
|
-
try {
|
|
172
|
-
while (true) {
|
|
173
|
-
if (signal && signal.aborted) {
|
|
174
|
-
await reader.cancel(); // 主动取消 reader
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
177
|
-
const { done, value } = await reader.read();
|
|
178
|
-
if (done) break;
|
|
179
|
-
if (value) yield value;
|
|
180
|
-
}
|
|
181
|
-
} finally {
|
|
182
|
-
reader.releaseLock(); // 释放锁
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
return processedStream;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* streamMixin
|
|
191
|
-
* 用于处理流式数据的 mixin,支持 SSE 数据解析和中断功能
|
|
192
|
-
* @namespace streamMixin
|
|
193
|
-
* @type {Object}
|
|
194
|
-
*/
|
|
195
|
-
export const streamMixin = {
|
|
196
|
-
/**
|
|
197
|
-
* 组件数据
|
|
198
|
-
* @returns {Object} 响应式数据对象
|
|
199
|
-
* @property {Array<*>} streamData - 流数据数组
|
|
200
|
-
* @property {Error|null} streamError - 流错误信息
|
|
201
|
-
* @property {boolean} streamLoading - 流处理状态
|
|
202
|
-
* @property {AbortController|null} _abortController - 私有:中断控制器
|
|
203
|
-
* @property {ReadableStream|null} _currentStream - 私有:当前流引用
|
|
204
|
-
*/
|
|
205
|
-
data() {
|
|
206
|
-
return {
|
|
207
|
-
streamData: [],
|
|
208
|
-
streamError: null,
|
|
209
|
-
streamLoading: false,
|
|
210
|
-
_abortController: null,
|
|
211
|
-
_currentStream: null,
|
|
212
|
-
};
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
methods: {
|
|
216
|
-
/**
|
|
217
|
-
* 启动流式请求
|
|
218
|
-
* @async
|
|
219
|
-
* @param {XStreamOptions} options - 流配置选项
|
|
220
|
-
* @param {ReadableStream<Uint8Array>} options.readableStream - 可读流
|
|
221
|
-
* @param {TransformStream<string, *>} [options.transformStream] - 可选的转换流
|
|
222
|
-
* @returns {Promise<void>} 流处理完成的 Promise
|
|
223
|
-
* @fires streamMixin#stream-data - 收到新数据时触发
|
|
224
|
-
* @fires streamMixin#stream-complete - 流完成时触发
|
|
225
|
-
* @fires streamMixin#stream-error - 流错误时触发
|
|
226
|
-
* @fires streamMixin#stream-finish - 流处理结束时触发
|
|
227
|
-
* @example
|
|
228
|
-
* // 基础使用
|
|
229
|
-
* const response = await fetch('/api/stream')
|
|
230
|
-
* await this.startStream({ readableStream: response.body })
|
|
231
|
-
*
|
|
232
|
-
* // 使用自定义转换流
|
|
233
|
-
* const customTransform = new TransformStream({ ... })
|
|
234
|
-
* await this.startStream({
|
|
235
|
-
* readableStream: response.body,
|
|
236
|
-
* transformStream: customTransform
|
|
237
|
-
* })
|
|
238
|
-
*/
|
|
239
|
-
async startStream(options) {
|
|
240
|
-
this.streamLoading = true;
|
|
241
|
-
this.streamError = null;
|
|
242
|
-
this.streamData = [];
|
|
243
|
-
this._abortController = new AbortController();
|
|
244
|
-
this._currentStream = XStream(options, this._abortController.signal);
|
|
245
|
-
|
|
246
|
-
try {
|
|
247
|
-
for await (const item of this._currentStream) {
|
|
248
|
-
this.streamData.push(item);
|
|
249
|
-
// 触发数据更新事件
|
|
250
|
-
/**
|
|
251
|
-
* 流数据事件
|
|
252
|
-
* @event streamMixin#stream-data
|
|
253
|
-
* @type {*} 流数据项
|
|
254
|
-
*/
|
|
255
|
-
this.$emit('stream-data', item);
|
|
256
|
-
}
|
|
257
|
-
// 流完成事件
|
|
258
|
-
/**
|
|
259
|
-
* 流完成事件
|
|
260
|
-
* @event streamMixin#stream-complete
|
|
261
|
-
* @type {Array<*>} 所有流数据
|
|
262
|
-
*/
|
|
263
|
-
this.$emit('stream-complete', this.streamData);
|
|
264
|
-
} catch (err) {
|
|
265
|
-
if (err instanceof Error) {
|
|
266
|
-
this.streamError = err;
|
|
267
|
-
/**
|
|
268
|
-
* 流错误事件
|
|
269
|
-
* @event streamMixin#stream-error
|
|
270
|
-
* @type {Error} 错误对象
|
|
271
|
-
*/
|
|
272
|
-
this.$emit('stream-error', err);
|
|
273
|
-
}
|
|
274
|
-
} finally {
|
|
275
|
-
this.streamLoading = false;
|
|
276
|
-
this._currentStream = null; // 释放流引用
|
|
277
|
-
this._abortController = null; // 释放控制器
|
|
278
|
-
/**
|
|
279
|
-
* 流处理结束事件
|
|
280
|
-
* @event streamMixin#stream-finish
|
|
281
|
-
*/
|
|
282
|
-
this.$emit('stream-finish');
|
|
283
|
-
}
|
|
284
|
-
},
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* 中断流式请求(强制关闭流)
|
|
288
|
-
* @returns {void}
|
|
289
|
-
* @fires streamMixin#stream-cancel - 流被中断时触发
|
|
290
|
-
* @example
|
|
291
|
-
* this.cancelStream()
|
|
292
|
-
*/
|
|
293
|
-
cancelStream() {
|
|
294
|
-
if (this._abortController) {
|
|
295
|
-
this._abortController.abort();
|
|
296
|
-
/**
|
|
297
|
-
* 流取消事件
|
|
298
|
-
* @event streamMixin#stream-cancel
|
|
299
|
-
*/
|
|
300
|
-
this.$emit('stream-cancel');
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* 重置流状态
|
|
306
|
-
* 清空所有数据和状态,但不触发事件
|
|
307
|
-
* @returns {void}
|
|
308
|
-
* @example
|
|
309
|
-
* this.resetStream()
|
|
310
|
-
*/
|
|
311
|
-
resetStream() {
|
|
312
|
-
this.streamData = [];
|
|
313
|
-
this.streamError = null;
|
|
314
|
-
this.streamLoading = false;
|
|
315
|
-
this._abortController = null;
|
|
316
|
-
this._currentStream = null;
|
|
317
|
-
},
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* 创建流处理器的便捷方法
|
|
321
|
-
* @param {ReadableStream<Uint8Array>} readableStream - 可读流
|
|
322
|
-
* @param {TransformStream<string, *>} [transformStream] - 可选的转换流
|
|
323
|
-
* @returns {XStreamOptions} 流处理器配置对象
|
|
324
|
-
* @example
|
|
325
|
-
* const processor = this.createStreamProcessor(response.body)
|
|
326
|
-
* await this.startStream(processor)
|
|
327
|
-
*/
|
|
328
|
-
createStreamProcessor(readableStream, transformStream) {
|
|
329
|
-
return {
|
|
330
|
-
readableStream,
|
|
331
|
-
transformStream,
|
|
332
|
-
};
|
|
333
|
-
},
|
|
334
|
-
},
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* 组件销毁前的生命周期钩子
|
|
338
|
-
* 自动清理流资源
|
|
339
|
-
* @returns {void}
|
|
340
|
-
*/
|
|
341
|
-
beforeDestroy() {
|
|
342
|
-
// 组件销毁时清理资源
|
|
343
|
-
this.cancelStream();
|
|
344
|
-
},
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* 工具函数版本 - 用于非组件场景
|
|
349
|
-
* @param {StreamCallbacks} [callbacks={}] - 回调函数配置
|
|
350
|
-
* @param {function(*): void} [callbacks.onData] - 数据回调函数
|
|
351
|
-
* @param {function(Array<*>): void} [callbacks.onComplete] - 完成回调函数
|
|
352
|
-
* @param {function(Error): void} [callbacks.onError] - 错误回调函数
|
|
353
|
-
* @param {function(): void} [callbacks.onCancel] - 取消回调函数
|
|
354
|
-
* @param {function(): void} [callbacks.onFinish] - 结束回调函数
|
|
355
|
-
* @returns {StreamUtils} 包含流处理相关方法的对象
|
|
356
|
-
* @example
|
|
357
|
-
* // 基础使用
|
|
358
|
-
* const streamUtils = createStreamUtils({
|
|
359
|
-
* onData: (item) => console.log('数据:', item),
|
|
360
|
-
* onComplete: (allData) => console.log('完成:', allData),
|
|
361
|
-
* onError: (error) => console.error('错误:', error)
|
|
362
|
-
* })
|
|
363
|
-
*
|
|
364
|
-
* const response = await fetch('/api/stream')
|
|
365
|
-
* await streamUtils.startStream({ readableStream: response.body })
|
|
366
|
-
*/
|
|
367
|
-
export function createStreamUtils(callbacks = {}) {
|
|
368
|
-
/**
|
|
369
|
-
* 流状态对象
|
|
370
|
-
* @type {StreamState}
|
|
371
|
-
*/
|
|
372
|
-
const state = {
|
|
373
|
-
data: [],
|
|
374
|
-
error: null,
|
|
375
|
-
loading: false,
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
let abortController = null;
|
|
379
|
-
let currentStream = null;
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* 启动流处理
|
|
383
|
-
* @async
|
|
384
|
-
* @param {XStreamOptions} options - 流配置选项
|
|
385
|
-
* @returns {Promise<void>} 流处理完成的 Promise
|
|
386
|
-
*/
|
|
387
|
-
const startStream = async options => {
|
|
388
|
-
state.loading = true;
|
|
389
|
-
state.error = null;
|
|
390
|
-
state.data = [];
|
|
391
|
-
abortController = new AbortController();
|
|
392
|
-
currentStream = XStream(options, abortController.signal);
|
|
393
|
-
|
|
394
|
-
try {
|
|
395
|
-
for await (const item of currentStream) {
|
|
396
|
-
state.data.push(item);
|
|
397
|
-
if (callbacks.onData) {
|
|
398
|
-
callbacks.onData(item);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
if (callbacks.onComplete) {
|
|
402
|
-
callbacks.onComplete(state.data);
|
|
403
|
-
}
|
|
404
|
-
} catch (err) {
|
|
405
|
-
if (err instanceof Error) {
|
|
406
|
-
state.error = err;
|
|
407
|
-
if (callbacks.onError) {
|
|
408
|
-
callbacks.onError(err);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
} finally {
|
|
412
|
-
state.loading = false;
|
|
413
|
-
currentStream = null;
|
|
414
|
-
abortController = null;
|
|
415
|
-
if (callbacks.onFinish) {
|
|
416
|
-
callbacks.onFinish();
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* 取消流处理
|
|
423
|
-
* @returns {void}
|
|
424
|
-
*/
|
|
425
|
-
const cancel = () => {
|
|
426
|
-
if (abortController) {
|
|
427
|
-
abortController.abort();
|
|
428
|
-
if (callbacks.onCancel) {
|
|
429
|
-
callbacks.onCancel();
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* 重置流状态
|
|
436
|
-
* @returns {void}
|
|
437
|
-
*/
|
|
438
|
-
const reset = () => {
|
|
439
|
-
state.data = [];
|
|
440
|
-
state.error = null;
|
|
441
|
-
state.loading = false;
|
|
442
|
-
abortController = null;
|
|
443
|
-
currentStream = null;
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
return {
|
|
447
|
-
state,
|
|
448
|
-
startStream,
|
|
449
|
-
cancel,
|
|
450
|
-
reset,
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// 导出常量和工具函数
|
|
455
|
-
export {
|
|
456
|
-
/**
|
|
457
|
-
* 默认键值分隔符
|
|
458
|
-
* @type {string}
|
|
459
|
-
*/
|
|
460
|
-
DEFAULT_KV_SEPARATOR,
|
|
461
|
-
/**
|
|
462
|
-
* 默认部分分隔符
|
|
463
|
-
* @type {string}
|
|
464
|
-
*/
|
|
465
|
-
DEFAULT_PART_SEPARATOR,
|
|
466
|
-
/**
|
|
467
|
-
* 默认流分隔符
|
|
468
|
-
* @type {string}
|
|
469
|
-
*/
|
|
470
|
-
DEFAULT_STREAM_SEPARATOR,
|
|
471
|
-
/**
|
|
472
|
-
* 字符串验证函数
|
|
473
|
-
* @type {function(string): boolean}
|
|
474
|
-
*/
|
|
475
|
-
isValidString,
|
|
476
|
-
/**
|
|
477
|
-
* SSE 数据解析转换器
|
|
478
|
-
* @type {function(): TransformStream<string, SSEOutput>}
|
|
479
|
-
*/
|
|
480
|
-
splitPart,
|
|
481
|
-
/**
|
|
482
|
-
* 流分割转换器
|
|
483
|
-
* @type {function(): TransformStream<string, string>}
|
|
484
|
-
*/
|
|
485
|
-
splitStream,
|
|
486
|
-
/**
|
|
487
|
-
* 核心流处理函数
|
|
488
|
-
* @type {function(XStreamOptions, AbortSignal=): ReadableStream}
|
|
489
|
-
*/
|
|
490
|
-
XStream,
|
|
491
|
-
};
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* 默认导出 streamMixin
|
|
495
|
-
* @type {Object}
|
|
496
|
-
*/
|
|
497
|
-
export default streamMixin;
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
@import '../theme/var.scss';
|
|
2
|
-
|
|
3
|
-
/* CSS 动画样式调整 */
|
|
4
|
-
.card-motion {
|
|
5
|
-
&-enter-active,
|
|
6
|
-
&-move,
|
|
7
|
-
&-leave-active {
|
|
8
|
-
transition: all 0.3s ease;
|
|
9
|
-
opacity: 1;
|
|
10
|
-
transform: translateX(0);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
&-enter,
|
|
14
|
-
&-leave-to {
|
|
15
|
-
opacity: 0;
|
|
16
|
-
/* 从左侧外进入(进场)/ 移动到右侧外(退场) */
|
|
17
|
-
transform: translateX(-100%);
|
|
18
|
-
/* 进场初始位置在左侧外 */
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
&-leave-active {
|
|
22
|
-
/* 退场时从当前位置移动到右侧外 */
|
|
23
|
-
transform: translateX(100%);
|
|
24
|
-
opacity: 0;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.el-x-attachments {
|
|
29
|
-
&-file-card-wrap {
|
|
30
|
-
display: flex;
|
|
31
|
-
height: 100%;
|
|
32
|
-
align-items: center;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
&-upload-placeholder {
|
|
36
|
-
display: inline-block;
|
|
37
|
-
width: fit-content;
|
|
38
|
-
align-self: center;
|
|
39
|
-
margin: 6px;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
&-card {
|
|
43
|
-
display: inline-block;
|
|
44
|
-
vertical-align: top;
|
|
45
|
-
|
|
46
|
-
&-item {
|
|
47
|
-
margin: 6px;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
&-prev-btn,
|
|
52
|
-
&-next-btn {
|
|
53
|
-
position: absolute;
|
|
54
|
-
top: 50%;
|
|
55
|
-
transform: translateY(-50%);
|
|
56
|
-
z-index: 10;
|
|
57
|
-
background-color: rgba(0, 0, 0, 0.3);
|
|
58
|
-
color: white;
|
|
59
|
-
border: none;
|
|
60
|
-
padding: 4px 0px;
|
|
61
|
-
border-radius: 3px;
|
|
62
|
-
transition: background-color 0.3s ease;
|
|
63
|
-
|
|
64
|
-
&:hover {
|
|
65
|
-
background-color: rgba(0, 0, 0, 0.5);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
&:active {
|
|
69
|
-
background-color: rgba(0, 0, 0, 0.7);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
&-prev-btn {
|
|
74
|
-
left: 8px;
|
|
75
|
-
border-top-left-radius: 0px;
|
|
76
|
-
border-bottom-left-radius: 0px;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
&-next-btn {
|
|
80
|
-
right: 8px;
|
|
81
|
-
border-top-right-radius: 0px;
|
|
82
|
-
border-bottom-right-radius: 0px;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
&-background {
|
|
86
|
-
position: absolute;
|
|
87
|
-
top: 0;
|
|
88
|
-
left: 0;
|
|
89
|
-
right: 0;
|
|
90
|
-
bottom: 0;
|
|
91
|
-
width: 100%;
|
|
92
|
-
pointer-events: none;
|
|
93
|
-
|
|
94
|
-
&-start {
|
|
95
|
-
position: absolute;
|
|
96
|
-
top: 0;
|
|
97
|
-
left: 0;
|
|
98
|
-
bottom: 0;
|
|
99
|
-
width: 50px;
|
|
100
|
-
background: linear-gradient(to right, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
|
|
101
|
-
z-index: 5;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
&-end {
|
|
105
|
-
position: absolute;
|
|
106
|
-
top: 0;
|
|
107
|
-
right: 0;
|
|
108
|
-
bottom: 0;
|
|
109
|
-
width: 50px;
|
|
110
|
-
background: linear-gradient(to left, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
|
|
111
|
-
z-index: 5;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
&-overflow-scrollX {
|
|
116
|
-
height: 100%;
|
|
117
|
-
|
|
118
|
-
&::-webkit-scrollbar {
|
|
119
|
-
display: none;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
scrollbar-width: none;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
&-overflow-scrollY {
|
|
126
|
-
width: 100%;
|
|
127
|
-
height: 100%;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
&-wrapper {
|
|
131
|
-
position: relative;
|
|
132
|
-
display: block;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
&-upload-btn {
|
|
136
|
-
display: flex;
|
|
137
|
-
|
|
138
|
-
::v-deep .el-upload {
|
|
139
|
-
border: 1px dashed $--border-color-base;
|
|
140
|
-
border-radius: 6px;
|
|
141
|
-
cursor: pointer;
|
|
142
|
-
position: relative;
|
|
143
|
-
overflow: hidden;
|
|
144
|
-
transition: $--all-transition;
|
|
145
|
-
box-sizing: border-box;
|
|
146
|
-
text-align: center;
|
|
147
|
-
|
|
148
|
-
&:hover {
|
|
149
|
-
border-color: $--color-primary;
|
|
150
|
-
|
|
151
|
-
.uploader-icon {
|
|
152
|
-
color: $--color-primary;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
.uploader-icon {
|
|
158
|
-
font-size: 28px;
|
|
159
|
-
color: #8c939d;
|
|
160
|
-
text-align: center;
|
|
161
|
-
width: var(--el-x-attachments-upload-icon-size);
|
|
162
|
-
height: var(--el-x-attachments-upload-icon-size);
|
|
163
|
-
line-height: var(--el-x-attachments-upload-icon-size);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
::v-deep .el-upload-dragger {
|
|
167
|
-
padding: 0;
|
|
168
|
-
width: auto !important;
|
|
169
|
-
height: auto !important;
|
|
170
|
-
background-color: transparent;
|
|
171
|
-
|
|
172
|
-
&:hover .el-icon {
|
|
173
|
-
color: $--color-primary;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/* 设置拖放区域的样式 */
|
|
179
|
-
&-drop-area {
|
|
180
|
-
position: absolute !important;
|
|
181
|
-
top: 0 !important;
|
|
182
|
-
left: 0 !important;
|
|
183
|
-
width: calc(100% - 4px) !important;
|
|
184
|
-
height: calc(100% - 4px) !important;
|
|
185
|
-
border-radius: 15px !important;
|
|
186
|
-
border: 2px dashed $--color-primary !important;
|
|
187
|
-
z-index: 9999 !important;
|
|
188
|
-
display: flex !important;
|
|
189
|
-
align-items: center !important;
|
|
190
|
-
justify-content: center !important;
|
|
191
|
-
flex-direction: column !important;
|
|
192
|
-
background: rgba(225, 225, 225, 0.8) !important; // 增加透明度
|
|
193
|
-
backdrop-filter: blur(4px) !important; // 增加模糊效果
|
|
194
|
-
animation: dragAreaShow 0.3s ease-in-out !important; // 添加显示动画
|
|
195
|
-
pointer-events: none !important; // 防止拖拽区域本身阻止事件
|
|
196
|
-
|
|
197
|
-
&-icon {
|
|
198
|
-
font-size: 50px !important;
|
|
199
|
-
color: $--color-primary !important;
|
|
200
|
-
animation: bounce 1s infinite alternate !important; // 添加跳动动画
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
&-text {
|
|
204
|
-
font-size: 16px !important;
|
|
205
|
-
color: $--color-primary !important;
|
|
206
|
-
margin-top: 10px !important;
|
|
207
|
-
text-align: center !important;
|
|
208
|
-
width: 100% !important;
|
|
209
|
-
max-width: 300px !important;
|
|
210
|
-
font-weight: bold !important;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// 添加动画关键帧
|
|
215
|
-
@keyframes dragAreaShow {
|
|
216
|
-
from {
|
|
217
|
-
opacity: 0;
|
|
218
|
-
transform: scale(0.9);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
to {
|
|
222
|
-
opacity: 1;
|
|
223
|
-
transform: scale(1);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
@keyframes bounce {
|
|
228
|
-
from {
|
|
229
|
-
transform: translateY(0);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
to {
|
|
233
|
-
transform: translateY(-10px);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|