@qy_better_lib/hooks 0.2.5 → 0.2.7
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/DOCUMENTATION.md +18 -61
- package/lib/index.mjs +935 -0
- package/lib/{use-chart.js → use-chart/index.mjs} +77 -5
- package/lib/use-chart/type.d.ts +0 -7
- package/package.json +5 -2
- package/lib/index.js +0 -16
- package/lib/node_modules/@qy_better_lib/core/lib/utils/echarts.js +0 -166
- package/lib/use-chart/config.js +0 -78
- package/lib/use-emit/extend.js +0 -42
- package/lib/use-emit/index.js +0 -27
- package/lib/use-fullscreen/index.js +0 -51
- package/lib/use-image/index.js +0 -192
- package/lib/use-image/type.js +0 -9
- package/lib/use-print/index.js +0 -301
- package/lib/use-watermark/index.js +0 -131
- package/lib/use-websocket/index.js +0 -205
- /package/lib/{use-layout-flow.js → use-layout-flow/index.mjs} +0 -0
- /package/lib/{use-mqtt.js → use-mqtt/index.mjs} +0 -0
package/lib/index.mjs
ADDED
|
@@ -0,0 +1,935 @@
|
|
|
1
|
+
import mitt from "mitt";
|
|
2
|
+
import { onMounted, onUnmounted, ref, shallowRef } from "vue";
|
|
3
|
+
import { guid, on, off, get_window_size, get_element, is_object, download_file, to_base64, compress_image } from "@qy_better_lib/core";
|
|
4
|
+
function createAsyncEmitter() {
|
|
5
|
+
if (typeof mitt === "undefined") {
|
|
6
|
+
return {};
|
|
7
|
+
}
|
|
8
|
+
const emitter2 = mitt();
|
|
9
|
+
emitter2.emitAsync = async function(type, ...args) {
|
|
10
|
+
const handlers = this.all?.get(type);
|
|
11
|
+
if (!handlers || handlers.length === 0) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const promises = handlers.map((handler) => {
|
|
15
|
+
try {
|
|
16
|
+
const result = handler(...args);
|
|
17
|
+
return result instanceof Promise ? result : Promise.resolve(result);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
return Promise.reject(error);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return Promise.all(promises);
|
|
23
|
+
};
|
|
24
|
+
emitter2.emit;
|
|
25
|
+
emitter2.emit = function(type, ...args) {
|
|
26
|
+
const handlers = this.all?.get(type);
|
|
27
|
+
if (!handlers || handlers.length === 0) {
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
let lastResult;
|
|
31
|
+
handlers.forEach((handler) => {
|
|
32
|
+
try {
|
|
33
|
+
lastResult = handler(...args);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error(`Error in event handler for ${type}:`, error);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return lastResult;
|
|
39
|
+
};
|
|
40
|
+
return emitter2;
|
|
41
|
+
}
|
|
42
|
+
const emitter = createAsyncEmitter();
|
|
43
|
+
function use_emit(event_list) {
|
|
44
|
+
if (typeof mitt === "undefined") return { emitter: {} };
|
|
45
|
+
onMounted(() => {
|
|
46
|
+
if (event_list && event_list.length > 0) {
|
|
47
|
+
for (const event of event_list) {
|
|
48
|
+
emitter.on(event.key, event.value);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
onUnmounted(() => {
|
|
53
|
+
if (event_list && event_list.length > 0) {
|
|
54
|
+
for (const event of event_list) {
|
|
55
|
+
emitter.off(event.key, event.value);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
emitter
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function use_print() {
|
|
64
|
+
let preview_container = null;
|
|
65
|
+
let print_in_progress = false;
|
|
66
|
+
function get_style(custom_style, options) {
|
|
67
|
+
const page_size = options?.page_size || "A4";
|
|
68
|
+
const page_orientation = options?.page_orientation || "portrait";
|
|
69
|
+
const margin = options?.margin || "1cm";
|
|
70
|
+
const print_background = options?.print_background ? "true" : "false";
|
|
71
|
+
const default_style = `#print-container {
|
|
72
|
+
display: none;
|
|
73
|
+
}
|
|
74
|
+
#preview-container {
|
|
75
|
+
position: fixed;
|
|
76
|
+
top: 0;
|
|
77
|
+
left: 0;
|
|
78
|
+
width: 100%;
|
|
79
|
+
height: 100%;
|
|
80
|
+
background: rgba(0, 0, 0, 0.8);
|
|
81
|
+
display: flex;
|
|
82
|
+
justify-content: center;
|
|
83
|
+
align-items: center;
|
|
84
|
+
z-index: 9999;
|
|
85
|
+
overflow: auto;
|
|
86
|
+
}
|
|
87
|
+
#preview-content {
|
|
88
|
+
background: white;
|
|
89
|
+
width: 80%;
|
|
90
|
+
max-width: 800px;
|
|
91
|
+
padding: 20px;
|
|
92
|
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
|
|
93
|
+
position: relative;
|
|
94
|
+
}
|
|
95
|
+
#preview-close {
|
|
96
|
+
position: absolute;
|
|
97
|
+
top: 10px;
|
|
98
|
+
right: 10px;
|
|
99
|
+
background: #333;
|
|
100
|
+
color: white;
|
|
101
|
+
border: none;
|
|
102
|
+
border-radius: 50%;
|
|
103
|
+
width: 30px;
|
|
104
|
+
height: 30px;
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
font-size: 16px;
|
|
107
|
+
display: flex;
|
|
108
|
+
justify-content: center;
|
|
109
|
+
align-items: center;
|
|
110
|
+
}
|
|
111
|
+
#preview-print {
|
|
112
|
+
margin-top: 20px;
|
|
113
|
+
padding: 10px 20px;
|
|
114
|
+
background: #007bff;
|
|
115
|
+
color: white;
|
|
116
|
+
border: none;
|
|
117
|
+
border-radius: 4px;
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
font-size: 16px;
|
|
120
|
+
}
|
|
121
|
+
@media print {
|
|
122
|
+
body > :not(#print-container) {
|
|
123
|
+
display: none;
|
|
124
|
+
}
|
|
125
|
+
html,
|
|
126
|
+
body {
|
|
127
|
+
display: block !important;
|
|
128
|
+
margin: 0;
|
|
129
|
+
padding: 0;
|
|
130
|
+
width: 100%;
|
|
131
|
+
height: 100%;
|
|
132
|
+
-webkit-print-color-adjust: ${print_background};
|
|
133
|
+
print-color-adjust: ${print_background};
|
|
134
|
+
}
|
|
135
|
+
#print-container {
|
|
136
|
+
display: block;
|
|
137
|
+
padding: 20px;
|
|
138
|
+
width: 100%;
|
|
139
|
+
height: 100%;
|
|
140
|
+
box-sizing: border-box;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
@page {
|
|
144
|
+
size: ${page_size} ${page_orientation};
|
|
145
|
+
margin: ${margin};
|
|
146
|
+
}`;
|
|
147
|
+
const style = document.createElement("style");
|
|
148
|
+
style.innerHTML = custom_style || default_style;
|
|
149
|
+
return style;
|
|
150
|
+
}
|
|
151
|
+
function clean_print() {
|
|
152
|
+
const existing_container = document.getElementById("print-container");
|
|
153
|
+
if (existing_container) {
|
|
154
|
+
try {
|
|
155
|
+
document.body.removeChild(existing_container);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error("清理打印容器失败:", error);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function clean_preview() {
|
|
162
|
+
const existing_preview = document.getElementById("preview-container");
|
|
163
|
+
if (existing_preview) {
|
|
164
|
+
try {
|
|
165
|
+
document.body.removeChild(existing_preview);
|
|
166
|
+
preview_container = null;
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error("清理预览容器失败:", error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function get_container(html_content) {
|
|
173
|
+
clean_print();
|
|
174
|
+
const container = document.createElement("div");
|
|
175
|
+
container.setAttribute("id", "print-container");
|
|
176
|
+
container.innerHTML = html_content;
|
|
177
|
+
return container;
|
|
178
|
+
}
|
|
179
|
+
function get_load_promise(container) {
|
|
180
|
+
if (!container || typeof container.querySelectorAll !== "function") {
|
|
181
|
+
return Promise.resolve();
|
|
182
|
+
}
|
|
183
|
+
const img_elements = container.querySelectorAll("img");
|
|
184
|
+
const imgs = Array.from(img_elements);
|
|
185
|
+
if (imgs.length === 0) {
|
|
186
|
+
return Promise.resolve();
|
|
187
|
+
}
|
|
188
|
+
let finished_count = 0;
|
|
189
|
+
return new Promise((resolve, reject) => {
|
|
190
|
+
function check() {
|
|
191
|
+
finished_count++;
|
|
192
|
+
if (finished_count === imgs.length) {
|
|
193
|
+
resolve();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
imgs.forEach((img) => {
|
|
197
|
+
if (img.complete) {
|
|
198
|
+
check();
|
|
199
|
+
} else {
|
|
200
|
+
img.addEventListener("load", check);
|
|
201
|
+
img.addEventListener("error", check);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
setTimeout(() => {
|
|
205
|
+
reject(new Error("图片加载超时"));
|
|
206
|
+
}, 1e4);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
async function print(html_content, options) {
|
|
210
|
+
if (print_in_progress) {
|
|
211
|
+
console.warn("打印已在进行中");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
print_in_progress = true;
|
|
216
|
+
const style = get_style(options?.custom_style, options);
|
|
217
|
+
const container = get_container(html_content);
|
|
218
|
+
document.body.appendChild(style);
|
|
219
|
+
document.body.appendChild(container);
|
|
220
|
+
await get_load_promise(container);
|
|
221
|
+
window.print();
|
|
222
|
+
setTimeout(() => {
|
|
223
|
+
try {
|
|
224
|
+
if (style.parentNode) {
|
|
225
|
+
style.parentNode.removeChild(style);
|
|
226
|
+
}
|
|
227
|
+
if (container.parentNode) {
|
|
228
|
+
container.parentNode.removeChild(container);
|
|
229
|
+
}
|
|
230
|
+
print_in_progress = false;
|
|
231
|
+
options?.on_complete?.();
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.error("清理打印元素失败:", error);
|
|
234
|
+
options?.on_error?.(error);
|
|
235
|
+
}
|
|
236
|
+
}, 100);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.error("打印失败:", error);
|
|
239
|
+
print_in_progress = false;
|
|
240
|
+
options?.on_error?.(error);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async function preview(html_content, options) {
|
|
244
|
+
try {
|
|
245
|
+
clean_preview();
|
|
246
|
+
const style = get_style(options?.custom_style, options);
|
|
247
|
+
const content = document.createElement("div");
|
|
248
|
+
content.innerHTML = html_content;
|
|
249
|
+
await get_load_promise(content);
|
|
250
|
+
const preview_wrapper = document.createElement("div");
|
|
251
|
+
preview_wrapper.setAttribute("id", "preview-container");
|
|
252
|
+
const preview_content = document.createElement("div");
|
|
253
|
+
preview_content.setAttribute("id", "preview-content");
|
|
254
|
+
preview_content.appendChild(content);
|
|
255
|
+
const close_button = document.createElement("button");
|
|
256
|
+
close_button.setAttribute("id", "preview-close");
|
|
257
|
+
close_button.textContent = "×";
|
|
258
|
+
close_button.addEventListener("click", () => {
|
|
259
|
+
clean_preview();
|
|
260
|
+
options?.on_cancel?.();
|
|
261
|
+
});
|
|
262
|
+
const print_button = document.createElement("button");
|
|
263
|
+
print_button.setAttribute("id", "preview-print");
|
|
264
|
+
print_button.textContent = "打印";
|
|
265
|
+
print_button.addEventListener("click", () => {
|
|
266
|
+
clean_preview();
|
|
267
|
+
print(html_content, options);
|
|
268
|
+
});
|
|
269
|
+
preview_content.appendChild(close_button);
|
|
270
|
+
preview_content.appendChild(print_button);
|
|
271
|
+
preview_wrapper.appendChild(preview_content);
|
|
272
|
+
document.body.appendChild(style);
|
|
273
|
+
document.body.appendChild(preview_wrapper);
|
|
274
|
+
preview_container = preview_wrapper;
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.error("预览失败:", error);
|
|
277
|
+
options?.on_error?.(error);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async function print_html(dom_selector, options) {
|
|
281
|
+
try {
|
|
282
|
+
const target_element = document.querySelector(dom_selector);
|
|
283
|
+
if (target_element) {
|
|
284
|
+
if (options?.preview) {
|
|
285
|
+
await preview(target_element.innerHTML, options);
|
|
286
|
+
} else {
|
|
287
|
+
await print(target_element.innerHTML, options);
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
throw new Error(`未找到DOM节点: ${dom_selector}`);
|
|
291
|
+
}
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.error("打印DOM元素失败:", error);
|
|
294
|
+
options?.on_error?.(error);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
async function print_html_element(element_array, options) {
|
|
298
|
+
try {
|
|
299
|
+
if (!element_array || element_array.length === 0) {
|
|
300
|
+
throw new Error("元素数组为空");
|
|
301
|
+
}
|
|
302
|
+
let html_content = "";
|
|
303
|
+
for (const element of element_array) {
|
|
304
|
+
html_content += element.outerHTML;
|
|
305
|
+
}
|
|
306
|
+
if (!html_content) {
|
|
307
|
+
throw new Error("元素内容为空");
|
|
308
|
+
}
|
|
309
|
+
if (options?.preview) {
|
|
310
|
+
await preview(html_content, options);
|
|
311
|
+
} else {
|
|
312
|
+
await print(html_content, options);
|
|
313
|
+
}
|
|
314
|
+
} catch (error) {
|
|
315
|
+
console.error("打印元素数组失败:", error);
|
|
316
|
+
options?.on_error?.(error);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function preview_html(dom_selector, options) {
|
|
320
|
+
try {
|
|
321
|
+
const target_element = document.querySelector(dom_selector);
|
|
322
|
+
if (target_element) {
|
|
323
|
+
preview(target_element.innerHTML, options);
|
|
324
|
+
} else {
|
|
325
|
+
throw new Error(`未找到DOM节点: ${dom_selector}`);
|
|
326
|
+
}
|
|
327
|
+
} catch (error) {
|
|
328
|
+
console.error("预览DOM元素失败:", error);
|
|
329
|
+
options?.on_error?.(error);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function preview_html_element(element_array, options) {
|
|
333
|
+
try {
|
|
334
|
+
if (!element_array || element_array.length === 0) {
|
|
335
|
+
throw new Error("元素数组为空");
|
|
336
|
+
}
|
|
337
|
+
let html_content = "";
|
|
338
|
+
for (const element of element_array) {
|
|
339
|
+
html_content += element.outerHTML;
|
|
340
|
+
}
|
|
341
|
+
if (!html_content) {
|
|
342
|
+
throw new Error("元素内容为空");
|
|
343
|
+
}
|
|
344
|
+
preview(html_content, options);
|
|
345
|
+
} catch (error) {
|
|
346
|
+
console.error("预览元素数组失败:", error);
|
|
347
|
+
options?.on_error?.(error);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function close_preview() {
|
|
351
|
+
clean_preview();
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
print_html,
|
|
355
|
+
print_html_element,
|
|
356
|
+
preview_html,
|
|
357
|
+
preview_html_element,
|
|
358
|
+
close_preview
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function use_water_mark() {
|
|
362
|
+
const id = guid();
|
|
363
|
+
let current_options = null;
|
|
364
|
+
let resize_handler = null;
|
|
365
|
+
function get_wrapper(wrapper) {
|
|
366
|
+
return wrapper ? get_element(wrapper) || document.body : document.body;
|
|
367
|
+
}
|
|
368
|
+
function create_watermark_canvas(options) {
|
|
369
|
+
const { text1, text2, font_size = 16, font_family = "Avenir,Helvetica,Arial,sans-serif", rotate = -15, text_color = "#999", opacity = 0.1, size = { width: 250, height: 150 } } = options;
|
|
370
|
+
try {
|
|
371
|
+
const canvas = document.createElement("canvas");
|
|
372
|
+
canvas.width = size.width;
|
|
373
|
+
canvas.height = size.height;
|
|
374
|
+
const ctx = canvas.getContext("2d");
|
|
375
|
+
if (ctx) {
|
|
376
|
+
ctx.font = `${font_size}px ${font_family}`;
|
|
377
|
+
ctx.fillStyle = text_color.replace(/\,\s(1|(0\.\d+)\))/, `, ${opacity}`);
|
|
378
|
+
ctx.textAlign = "left";
|
|
379
|
+
ctx.textBaseline = "middle";
|
|
380
|
+
ctx.rotate(rotate * Math.PI / 180);
|
|
381
|
+
ctx.fillText(text1, 0, canvas.height / 2);
|
|
382
|
+
if (text2) {
|
|
383
|
+
const text1_width = ctx.measureText(text1).width;
|
|
384
|
+
ctx.fillText(text2, text1_width + 16, canvas.height / 2);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return canvas;
|
|
388
|
+
} catch (error) {
|
|
389
|
+
console.error("创建水印画布失败:", error);
|
|
390
|
+
const canvas = document.createElement("canvas");
|
|
391
|
+
canvas.width = 0;
|
|
392
|
+
canvas.height = 0;
|
|
393
|
+
return canvas;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function create_watermark_element(options) {
|
|
397
|
+
const {
|
|
398
|
+
wrapper,
|
|
399
|
+
z_index = 1e5,
|
|
400
|
+
position = "fixed",
|
|
401
|
+
spacing = 0,
|
|
402
|
+
size = { width: 250, height: 150 }
|
|
403
|
+
} = options;
|
|
404
|
+
const wrapper_element = get_wrapper(wrapper);
|
|
405
|
+
const existing_element = document.getElementById(id);
|
|
406
|
+
if (existing_element) {
|
|
407
|
+
try {
|
|
408
|
+
wrapper_element.removeChild(existing_element);
|
|
409
|
+
} catch (error) {
|
|
410
|
+
console.error("移除旧水印失败:", error);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
const canvas = create_watermark_canvas(options);
|
|
414
|
+
const div = document.createElement("div");
|
|
415
|
+
div.id = id;
|
|
416
|
+
div.style.pointerEvents = "none";
|
|
417
|
+
div.style.top = "0px";
|
|
418
|
+
div.style.left = "0px";
|
|
419
|
+
div.style.position = position;
|
|
420
|
+
div.style.zIndex = z_index.toString();
|
|
421
|
+
const window_size = get_window_size();
|
|
422
|
+
div.style.width = window_size.width + "px";
|
|
423
|
+
div.style.height = window_size.height + "px";
|
|
424
|
+
div.style.background = `url(${canvas.toDataURL("image/png")}) left top repeat`;
|
|
425
|
+
if (spacing > 0) {
|
|
426
|
+
div.style.backgroundSize = `${size.width + spacing}px ${size.height + spacing}px`;
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
wrapper_element.appendChild(div);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
console.error("添加水印失败:", error);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function handle_resize() {
|
|
435
|
+
if (current_options) {
|
|
436
|
+
create_watermark_element(current_options);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function create_water_mark(options) {
|
|
440
|
+
current_options = options;
|
|
441
|
+
create_watermark_element(options);
|
|
442
|
+
if (options.responsive) {
|
|
443
|
+
if (!resize_handler) {
|
|
444
|
+
resize_handler = handle_resize;
|
|
445
|
+
on(window, "resize", resize_handler);
|
|
446
|
+
}
|
|
447
|
+
} else if (resize_handler) {
|
|
448
|
+
off(window, "resize", resize_handler);
|
|
449
|
+
resize_handler = null;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
function remove_water_mark() {
|
|
453
|
+
const existing_element = document.getElementById(id);
|
|
454
|
+
if (existing_element) {
|
|
455
|
+
try {
|
|
456
|
+
const parent = existing_element.parentElement;
|
|
457
|
+
if (parent) {
|
|
458
|
+
parent.removeChild(existing_element);
|
|
459
|
+
}
|
|
460
|
+
} catch (error) {
|
|
461
|
+
console.error("移除水印失败:", error);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (resize_handler) {
|
|
465
|
+
off(window, "resize", resize_handler);
|
|
466
|
+
resize_handler = null;
|
|
467
|
+
}
|
|
468
|
+
current_options = null;
|
|
469
|
+
}
|
|
470
|
+
function update_water_mark(options) {
|
|
471
|
+
if (current_options) {
|
|
472
|
+
const new_options = { ...current_options, ...options };
|
|
473
|
+
create_water_mark(new_options);
|
|
474
|
+
} else {
|
|
475
|
+
console.warn("水印未初始化,请先调用 create_water_mark");
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
function has_water_mark() {
|
|
479
|
+
return document.getElementById(id) !== null;
|
|
480
|
+
}
|
|
481
|
+
return {
|
|
482
|
+
create_water_mark,
|
|
483
|
+
remove_water_mark,
|
|
484
|
+
update_water_mark,
|
|
485
|
+
has_water_mark
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function use_web_socket(options) {
|
|
489
|
+
const {
|
|
490
|
+
server,
|
|
491
|
+
receive,
|
|
492
|
+
max_reconnect_attempts = 5,
|
|
493
|
+
reconnect_interval = 5e3,
|
|
494
|
+
heartbeat_interval = 3e4,
|
|
495
|
+
heartbeat_message = "ping",
|
|
496
|
+
on_open,
|
|
497
|
+
on_error,
|
|
498
|
+
on_close,
|
|
499
|
+
auto_reconnect = true,
|
|
500
|
+
auto_connect = true,
|
|
501
|
+
max_message_queue_size = 100
|
|
502
|
+
} = options;
|
|
503
|
+
let reconnect_count = 0;
|
|
504
|
+
let lock_reconnect = false;
|
|
505
|
+
let ws = void 0;
|
|
506
|
+
let reconnect_timer = null;
|
|
507
|
+
let heartbeat_timer = null;
|
|
508
|
+
let message_queue = [];
|
|
509
|
+
let last_heartbeat_time = 0;
|
|
510
|
+
const heartbeat_timeout = heartbeat_interval * 1.5;
|
|
511
|
+
if (typeof window !== "undefined") {
|
|
512
|
+
window.onbeforeunload = () => {
|
|
513
|
+
close();
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
function create() {
|
|
517
|
+
try {
|
|
518
|
+
console.log("正在连接WebSocket:", server);
|
|
519
|
+
ws = new WebSocket(server);
|
|
520
|
+
init_event_handlers();
|
|
521
|
+
} catch (error) {
|
|
522
|
+
console.error("WebSocket连接建立异常:", error);
|
|
523
|
+
on_error?.(error);
|
|
524
|
+
if (auto_reconnect) {
|
|
525
|
+
reconnect();
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
function init_event_handlers() {
|
|
530
|
+
if (!ws) return;
|
|
531
|
+
ws.onopen = () => {
|
|
532
|
+
console.log("WebSocket连接成功:", server);
|
|
533
|
+
reconnect_count = 0;
|
|
534
|
+
last_heartbeat_time = Date.now();
|
|
535
|
+
start_heartbeat();
|
|
536
|
+
flush_message_queue();
|
|
537
|
+
on_open?.();
|
|
538
|
+
};
|
|
539
|
+
ws.onerror = (error) => {
|
|
540
|
+
console.error("WebSocket连接错误:", error);
|
|
541
|
+
on_error?.(error);
|
|
542
|
+
};
|
|
543
|
+
ws.onclose = (event) => {
|
|
544
|
+
console.log("WebSocket连接关闭:", (/* @__PURE__ */ new Date()).toLocaleTimeString(), "原因:", event.code, event.reason);
|
|
545
|
+
stop_heartbeat();
|
|
546
|
+
on_close?.();
|
|
547
|
+
if (auto_reconnect && !event.wasClean) {
|
|
548
|
+
reconnect();
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
ws.onmessage = (event) => {
|
|
552
|
+
try {
|
|
553
|
+
last_heartbeat_time = Date.now();
|
|
554
|
+
receive(event);
|
|
555
|
+
} catch (error) {
|
|
556
|
+
console.error("WebSocket消息处理错误:", error);
|
|
557
|
+
console.log("原始消息:", event.data);
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function reconnect() {
|
|
562
|
+
if (reconnect_count >= max_reconnect_attempts) {
|
|
563
|
+
console.error("WebSocket重连失败次数过多,停止重连");
|
|
564
|
+
close();
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (lock_reconnect) return;
|
|
568
|
+
lock_reconnect = true;
|
|
569
|
+
console.log(`WebSocket尝试重连 ${reconnect_count + 1}/${max_reconnect_attempts}...`);
|
|
570
|
+
reconnect_timer = setTimeout(() => {
|
|
571
|
+
create();
|
|
572
|
+
reconnect_count++;
|
|
573
|
+
lock_reconnect = false;
|
|
574
|
+
clear_reconnect_timer();
|
|
575
|
+
}, reconnect_interval);
|
|
576
|
+
}
|
|
577
|
+
function clear_reconnect_timer() {
|
|
578
|
+
if (reconnect_timer) {
|
|
579
|
+
clearTimeout(reconnect_timer);
|
|
580
|
+
reconnect_timer = null;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function start_heartbeat() {
|
|
584
|
+
stop_heartbeat();
|
|
585
|
+
heartbeat_timer = setInterval(() => {
|
|
586
|
+
send_heartbeat();
|
|
587
|
+
check_heartbeat_timeout();
|
|
588
|
+
}, heartbeat_interval);
|
|
589
|
+
}
|
|
590
|
+
function stop_heartbeat() {
|
|
591
|
+
if (heartbeat_timer) {
|
|
592
|
+
clearInterval(heartbeat_timer);
|
|
593
|
+
heartbeat_timer = null;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
function send_heartbeat() {
|
|
597
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
598
|
+
try {
|
|
599
|
+
const heartbeat_msg = is_object(heartbeat_message) ? JSON.stringify(heartbeat_message) : heartbeat_message;
|
|
600
|
+
ws.send(heartbeat_msg);
|
|
601
|
+
} catch (error) {
|
|
602
|
+
console.error("发送心跳消息失败:", error);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function check_heartbeat_timeout() {
|
|
607
|
+
const now = Date.now();
|
|
608
|
+
if (now - last_heartbeat_time > heartbeat_timeout) {
|
|
609
|
+
console.error("WebSocket心跳超时,尝试重连");
|
|
610
|
+
reconnect();
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function send_message(msg) {
|
|
614
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
615
|
+
try {
|
|
616
|
+
const message = is_object(msg) ? JSON.stringify(msg) : msg;
|
|
617
|
+
ws.send(message);
|
|
618
|
+
return true;
|
|
619
|
+
} catch (error) {
|
|
620
|
+
console.error("发送消息失败:", error);
|
|
621
|
+
add_to_message_queue(msg);
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
} else {
|
|
625
|
+
console.warn("WebSocket未连接,消息已加入队列");
|
|
626
|
+
add_to_message_queue(msg);
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
function add_to_message_queue(msg) {
|
|
631
|
+
if (message_queue.length >= max_message_queue_size) {
|
|
632
|
+
const removed_msg = message_queue.shift();
|
|
633
|
+
console.warn("消息队列已满,移除最旧的消息:", removed_msg);
|
|
634
|
+
}
|
|
635
|
+
message_queue.push(msg);
|
|
636
|
+
console.log(`消息已加入队列,当前队列大小: ${message_queue.length}/${max_message_queue_size}`);
|
|
637
|
+
}
|
|
638
|
+
function flush_message_queue() {
|
|
639
|
+
if (ws?.readyState === WebSocket.OPEN && message_queue.length > 0) {
|
|
640
|
+
console.log(`发送队列中的 ${message_queue.length} 条消息`);
|
|
641
|
+
while (message_queue.length > 0) {
|
|
642
|
+
const msg = message_queue.shift();
|
|
643
|
+
send_message(msg);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
function close() {
|
|
648
|
+
stop_heartbeat();
|
|
649
|
+
clear_reconnect_timer();
|
|
650
|
+
if (ws) {
|
|
651
|
+
try {
|
|
652
|
+
ws.close();
|
|
653
|
+
} catch (error) {
|
|
654
|
+
console.error("关闭WebSocket失败:", error);
|
|
655
|
+
}
|
|
656
|
+
ws = void 0;
|
|
657
|
+
}
|
|
658
|
+
message_queue = [];
|
|
659
|
+
}
|
|
660
|
+
function get_status() {
|
|
661
|
+
if (!ws) return "CLOSED";
|
|
662
|
+
switch (ws.readyState) {
|
|
663
|
+
case WebSocket.CONNECTING:
|
|
664
|
+
return "CONNECTING";
|
|
665
|
+
case WebSocket.OPEN:
|
|
666
|
+
return "OPEN";
|
|
667
|
+
case WebSocket.CLOSING:
|
|
668
|
+
return "CLOSING";
|
|
669
|
+
case WebSocket.CLOSED:
|
|
670
|
+
return "CLOSED";
|
|
671
|
+
default:
|
|
672
|
+
return "UNKNOWN";
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
if (auto_connect) {
|
|
676
|
+
create();
|
|
677
|
+
}
|
|
678
|
+
return {
|
|
679
|
+
ws,
|
|
680
|
+
create,
|
|
681
|
+
close,
|
|
682
|
+
send_message,
|
|
683
|
+
get_status,
|
|
684
|
+
reconnect,
|
|
685
|
+
start_heartbeat,
|
|
686
|
+
stop_heartbeat
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
var EImageType = /* @__PURE__ */ ((EImageType2) => {
|
|
690
|
+
EImageType2["PNG"] = "image/png";
|
|
691
|
+
EImageType2["JPEG"] = "image/jpeg";
|
|
692
|
+
EImageType2["GIF"] = "image/gif";
|
|
693
|
+
return EImageType2;
|
|
694
|
+
})(EImageType || {});
|
|
695
|
+
function use_image() {
|
|
696
|
+
function check_image_type(type) {
|
|
697
|
+
return Object.values(EImageType).includes(type);
|
|
698
|
+
}
|
|
699
|
+
async function canvas_to_data_url(canvas, quality = 0.92, type = EImageType.JPEG) {
|
|
700
|
+
if (!check_image_type(type)) {
|
|
701
|
+
type = EImageType.JPEG;
|
|
702
|
+
}
|
|
703
|
+
return canvas.toDataURL(type, quality);
|
|
704
|
+
}
|
|
705
|
+
async function canvas_to_file(canvas, file_name = "image", quality = 0.92, type = EImageType.JPEG) {
|
|
706
|
+
const data_url = await canvas_to_data_url(canvas, quality, type);
|
|
707
|
+
return data_url_to_file(data_url, type, file_name);
|
|
708
|
+
}
|
|
709
|
+
async function data_url_to_file(data_url, type = EImageType.JPEG, file_name = "image") {
|
|
710
|
+
if (!check_image_type(type)) {
|
|
711
|
+
type = EImageType.JPEG;
|
|
712
|
+
}
|
|
713
|
+
const arr = data_url.split(",");
|
|
714
|
+
const mime = arr[0].match(/:(.*?);/)?.[1] || type;
|
|
715
|
+
const bstr = atob(arr[1]);
|
|
716
|
+
let n = bstr.length;
|
|
717
|
+
const u8arr = new Uint8Array(n);
|
|
718
|
+
while (n--) {
|
|
719
|
+
u8arr[n] = bstr.charCodeAt(n);
|
|
720
|
+
}
|
|
721
|
+
const extension = mime.split("/")[1];
|
|
722
|
+
const full_name = `${file_name}.${extension}`;
|
|
723
|
+
return new File([u8arr], full_name, { type: mime });
|
|
724
|
+
}
|
|
725
|
+
async function data_url_to_image(data_url) {
|
|
726
|
+
return new Promise((resolve, reject) => {
|
|
727
|
+
const image = new Image();
|
|
728
|
+
image.onload = () => resolve(image);
|
|
729
|
+
image.onerror = reject;
|
|
730
|
+
image.src = data_url;
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
function download_file$1(blob, file_name = "download") {
|
|
734
|
+
const url = URL.createObjectURL(blob);
|
|
735
|
+
download_file({ path: url, name: file_name });
|
|
736
|
+
URL.revokeObjectURL(url);
|
|
737
|
+
}
|
|
738
|
+
async function file_to_data_url(file) {
|
|
739
|
+
const result = await to_base64(file);
|
|
740
|
+
if (!result) {
|
|
741
|
+
throw new Error("转换文件为DataURL失败");
|
|
742
|
+
}
|
|
743
|
+
return result;
|
|
744
|
+
}
|
|
745
|
+
async function image_to_canvas(image, config = {}) {
|
|
746
|
+
const canvas = document.createElement("canvas");
|
|
747
|
+
const context = canvas.getContext("2d");
|
|
748
|
+
if (!context) {
|
|
749
|
+
throw new Error("创建Canvas上下文失败");
|
|
750
|
+
}
|
|
751
|
+
const width = config.width || image.width;
|
|
752
|
+
const height = config.height || image.height;
|
|
753
|
+
const scale = config.scale || 1;
|
|
754
|
+
canvas.width = width * scale;
|
|
755
|
+
canvas.height = height * scale;
|
|
756
|
+
context.scale(scale, scale);
|
|
757
|
+
context.drawImage(image, 0, 0, width, height);
|
|
758
|
+
return canvas;
|
|
759
|
+
}
|
|
760
|
+
async function url_to_blob(url) {
|
|
761
|
+
const response = await fetch(url);
|
|
762
|
+
if (!response.ok) {
|
|
763
|
+
throw new Error(`获取图片失败: ${response.statusText}`);
|
|
764
|
+
}
|
|
765
|
+
return response.blob();
|
|
766
|
+
}
|
|
767
|
+
async function url_to_image(url) {
|
|
768
|
+
return new Promise((resolve, reject) => {
|
|
769
|
+
const image = new Image();
|
|
770
|
+
image.onload = () => resolve(image);
|
|
771
|
+
image.onerror = reject;
|
|
772
|
+
image.src = url;
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
async function compress(file, config = {}) {
|
|
776
|
+
if (!(file instanceof Blob)) {
|
|
777
|
+
throw new Error("compress(): 第一个参数必须是Blob对象或File对象");
|
|
778
|
+
}
|
|
779
|
+
const quality = Math.max(0, Math.min(1, isNaN(Number(config.quality)) ? 0.92 : Number(config.quality)));
|
|
780
|
+
config.quality = quality;
|
|
781
|
+
const data_url = await file_to_data_url(file);
|
|
782
|
+
const original_mime = data_url.split(",")[0].match(/:(.*?);/)?.[1];
|
|
783
|
+
let mime = EImageType.JPEG;
|
|
784
|
+
if (check_image_type(config.type)) {
|
|
785
|
+
mime = config.type;
|
|
786
|
+
}
|
|
787
|
+
const image = await data_url_to_image(data_url);
|
|
788
|
+
const canvas = await image_to_canvas(image, config);
|
|
789
|
+
const compress_data_url = await canvas_to_data_url(canvas, config.quality, mime);
|
|
790
|
+
const compress_file = await data_url_to_file(compress_data_url, original_mime || mime);
|
|
791
|
+
if (compress_file.size > file.size) {
|
|
792
|
+
return file;
|
|
793
|
+
}
|
|
794
|
+
return compress_file;
|
|
795
|
+
}
|
|
796
|
+
async function compress_accurately(file, config = {}) {
|
|
797
|
+
if (!(file instanceof Blob)) {
|
|
798
|
+
throw new Error("compressAccurately(): 第一个参数必须是Blob对象或File对象");
|
|
799
|
+
}
|
|
800
|
+
const size = Math.max(0, isNaN(Number(config.size)) ? 0 : Number(config.size));
|
|
801
|
+
config.size = size;
|
|
802
|
+
const accuracy = Math.max(0.8, Math.min(0.99, isNaN(Number(config.accuracy)) ? 0.95 : Number(config.accuracy)));
|
|
803
|
+
config.accuracy = accuracy;
|
|
804
|
+
if (isNaN(config.size)) {
|
|
805
|
+
return file;
|
|
806
|
+
}
|
|
807
|
+
if (config.size * 1024 > file.size) {
|
|
808
|
+
return file;
|
|
809
|
+
}
|
|
810
|
+
if (!config.accuracy || config.accuracy < 0.8 || config.accuracy > 0.99) {
|
|
811
|
+
config.accuracy = 0.95;
|
|
812
|
+
}
|
|
813
|
+
const result_size = {
|
|
814
|
+
max: config.size * (2 - config.accuracy) * 1024,
|
|
815
|
+
accurate: config.size * 1024,
|
|
816
|
+
min: config.size * config.accuracy * 1024
|
|
817
|
+
};
|
|
818
|
+
const data_url = await file_to_data_url(file);
|
|
819
|
+
const original_mime = data_url.split(",")[0].match(/:(.*?);/)?.[1];
|
|
820
|
+
let mime = EImageType.JPEG;
|
|
821
|
+
if (check_image_type(config.type)) {
|
|
822
|
+
mime = config.type;
|
|
823
|
+
}
|
|
824
|
+
const image = await data_url_to_image(data_url);
|
|
825
|
+
const canvas = await image_to_canvas(image, config);
|
|
826
|
+
const proportion = 0.75;
|
|
827
|
+
let image_quality = 1;
|
|
828
|
+
let compress_data_url = "";
|
|
829
|
+
const temp_data_urls = [];
|
|
830
|
+
for (let x = 1; x <= 7; x++) {
|
|
831
|
+
compress_data_url = await canvas_to_data_url(canvas, image_quality, mime);
|
|
832
|
+
const calculation_size = compress_data_url.length * proportion;
|
|
833
|
+
if (x === 7) {
|
|
834
|
+
if (result_size.max < calculation_size || result_size.min > calculation_size) {
|
|
835
|
+
compress_data_url = [compress_data_url, ...temp_data_urls].filter(Boolean).sort(
|
|
836
|
+
(a, b) => Math.abs(a.length * proportion - result_size.accurate) - Math.abs(b.length * proportion - result_size.accurate)
|
|
837
|
+
)[0];
|
|
838
|
+
}
|
|
839
|
+
break;
|
|
840
|
+
}
|
|
841
|
+
if (result_size.max < calculation_size) {
|
|
842
|
+
temp_data_urls[1] = compress_data_url;
|
|
843
|
+
image_quality -= 0.5 ** (x + 1);
|
|
844
|
+
} else if (result_size.min > calculation_size) {
|
|
845
|
+
temp_data_urls[0] = compress_data_url;
|
|
846
|
+
image_quality += 0.5 ** (x + 1);
|
|
847
|
+
} else {
|
|
848
|
+
break;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
const compress_file = await data_url_to_file(
|
|
852
|
+
compress_data_url,
|
|
853
|
+
original_mime || mime
|
|
854
|
+
);
|
|
855
|
+
if (compress_file.size > file.size) {
|
|
856
|
+
return file;
|
|
857
|
+
}
|
|
858
|
+
return compress_file;
|
|
859
|
+
}
|
|
860
|
+
async function compress_image$1(file, max_width = 800, max_height = 800, quality = 0.8) {
|
|
861
|
+
return compress_image(file, max_width, max_height, quality);
|
|
862
|
+
}
|
|
863
|
+
return {
|
|
864
|
+
// 压缩功能
|
|
865
|
+
compress,
|
|
866
|
+
compress_accurately,
|
|
867
|
+
compress_image: compress_image$1,
|
|
868
|
+
// 转换功能
|
|
869
|
+
canvas_to_data_url,
|
|
870
|
+
canvas_to_file,
|
|
871
|
+
data_url_to_file,
|
|
872
|
+
data_url_to_image,
|
|
873
|
+
file_to_data_url,
|
|
874
|
+
image_to_canvas,
|
|
875
|
+
url_to_blob,
|
|
876
|
+
url_to_image,
|
|
877
|
+
// 工具功能
|
|
878
|
+
download_file: download_file$1
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
function use_fullscreen(selector) {
|
|
882
|
+
const full = ref(false);
|
|
883
|
+
const container = shallowRef(null);
|
|
884
|
+
function fullscreen_change() {
|
|
885
|
+
full.value = document.fullscreenElement === container.value;
|
|
886
|
+
}
|
|
887
|
+
onMounted(() => {
|
|
888
|
+
container.value = get_element(selector);
|
|
889
|
+
if (container.value) {
|
|
890
|
+
on(container.value, "fullscreenchange", fullscreen_change);
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
onUnmounted(() => {
|
|
894
|
+
if (container.value) {
|
|
895
|
+
off(container.value, "fullscreenchange", fullscreen_change);
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
function enter_fullscreen() {
|
|
899
|
+
if (!full.value && container.value?.requestFullscreen) {
|
|
900
|
+
container.value.requestFullscreen().catch((error) => {
|
|
901
|
+
console.error("进入全屏失败:", error);
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
function exit_fullscreen() {
|
|
906
|
+
if (document.exitFullscreen) {
|
|
907
|
+
document.exitFullscreen().catch((error) => {
|
|
908
|
+
console.error("退出全屏失败:", error);
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
function toggle_fullscreen() {
|
|
913
|
+
if (full.value) {
|
|
914
|
+
exit_fullscreen();
|
|
915
|
+
} else {
|
|
916
|
+
enter_fullscreen();
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return {
|
|
920
|
+
container,
|
|
921
|
+
full,
|
|
922
|
+
toggle_fullscreen,
|
|
923
|
+
enter_fullscreen,
|
|
924
|
+
exit_fullscreen
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
export {
|
|
928
|
+
EImageType,
|
|
929
|
+
use_emit,
|
|
930
|
+
use_fullscreen,
|
|
931
|
+
use_image,
|
|
932
|
+
use_print,
|
|
933
|
+
use_water_mark,
|
|
934
|
+
use_web_socket
|
|
935
|
+
};
|