@mptw/skylens-ui 1.4.80 → 1.4.82
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/components/SLDownloadButton.vue +3 -122
- package/components/SLIOSSwitch.vue +20 -7
- package/components/SLSidebarNav.vue +10 -11
- package/package.json +1 -1
- package/utils/index.ts +128 -0
|
@@ -46,21 +46,7 @@ function safeSheetName(name: string) {
|
|
|
46
46
|
return name.replace(/[\\/*?:[\]]/g, "").substring(0, 31);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
return new Promise<HTMLImageElement>((resolve) => {
|
|
51
|
-
const img = new Image();
|
|
52
|
-
img.onload = function () {
|
|
53
|
-
resolve(img);
|
|
54
|
-
};
|
|
55
|
-
img.src = url;
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const DEFAULT_STYLE = {
|
|
60
|
-
marginTop: 0,
|
|
61
|
-
marginBottom: 9,
|
|
62
|
-
border: 0,
|
|
63
|
-
};
|
|
49
|
+
const WatermarkImage = '/assets/images/commons/watermark-2.svg';
|
|
64
50
|
|
|
65
51
|
export default defineComponent({
|
|
66
52
|
name: "SLDownloadButton",
|
|
@@ -137,22 +123,6 @@ export default defineComponent({
|
|
|
137
123
|
let clickSubscriber: Subscription | null = null;
|
|
138
124
|
let popperInstance: PopperInstance | null = null;
|
|
139
125
|
|
|
140
|
-
const watermarkImg = computed(() => '/assets/images/commons/watermark-2.svg')
|
|
141
|
-
const styleOption = computed(() => {
|
|
142
|
-
return Object.assign({}, DEFAULT_STYLE, customStyle.value);
|
|
143
|
-
});
|
|
144
|
-
const filterOption = computed(() => {
|
|
145
|
-
return (node: HTMLElement) => {
|
|
146
|
-
return (
|
|
147
|
-
!(
|
|
148
|
-
node?.getAttribute && node.getAttribute("data-skip-download") === ""
|
|
149
|
-
) &&
|
|
150
|
-
(typeof customFilter.value !== "function" ||
|
|
151
|
-
(customFilter.value && customFilter.value(node)))
|
|
152
|
-
);
|
|
153
|
-
};
|
|
154
|
-
});
|
|
155
|
-
|
|
156
126
|
function subscribeClick() {
|
|
157
127
|
clickSubscriber = fromEvent(window, "click").subscribe((e) => {
|
|
158
128
|
const { target } = e;
|
|
@@ -252,10 +222,8 @@ export default defineComponent({
|
|
|
252
222
|
return;
|
|
253
223
|
}
|
|
254
224
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
225
|
+
const dataURL = await domToPngDataUrl(imageTarget.value, customFilter.value, customStyle.value, padding.value, dateRanges.value, watermark.value, WatermarkImage);
|
|
226
|
+
downloadURL(dataURL, `${fileName.value}.png`);
|
|
259
227
|
|
|
260
228
|
if (evCategory.value !== "") {
|
|
261
229
|
gaev({
|
|
@@ -269,93 +237,6 @@ export default defineComponent({
|
|
|
269
237
|
label: "下載圖片",
|
|
270
238
|
});
|
|
271
239
|
}
|
|
272
|
-
|
|
273
|
-
const options = {
|
|
274
|
-
pixelRatio: 1,
|
|
275
|
-
filter: filterOption.value,
|
|
276
|
-
style: styleOption.value,
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
let dataURL = await htmlToImage.toPng(imageTarget.value, options);
|
|
280
|
-
if (padding.value) dataURL = await addPadding(dataURL, padding.value);
|
|
281
|
-
if (dateRanges.value?.length > 0)
|
|
282
|
-
dataURL = await addDateRanges(dataURL, [...dateRanges.value].reverse());
|
|
283
|
-
if (watermark.value) dataURL = await addWatermark(dataURL, watermarkImg.value);
|
|
284
|
-
downloadURL(dataURL, `${fileName.value}.png`);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async function addPadding(dataURL: string, padding: number) {
|
|
288
|
-
const canvas = document.createElement("canvas");
|
|
289
|
-
const ctx = canvas.getContext("2d");
|
|
290
|
-
if (!ctx) return "";
|
|
291
|
-
|
|
292
|
-
const img = await loadImage(dataURL);
|
|
293
|
-
const { width, height } = img;
|
|
294
|
-
canvas.width = width + padding * 2;
|
|
295
|
-
canvas.height = height + padding * 2;
|
|
296
|
-
ctx.fillStyle = "#FFFFFF";
|
|
297
|
-
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
298
|
-
ctx.drawImage(img, padding, padding);
|
|
299
|
-
|
|
300
|
-
return canvas.toDataURL();
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
async function addDateRanges(
|
|
304
|
-
dataURL: string,
|
|
305
|
-
dateRanges: (IDateRange | null)[]
|
|
306
|
-
) {
|
|
307
|
-
const fontSize = 16;
|
|
308
|
-
const canvas = document.createElement("canvas");
|
|
309
|
-
const ctx = canvas.getContext("2d");
|
|
310
|
-
if (!ctx) return "";
|
|
311
|
-
|
|
312
|
-
const img = await loadImage(dataURL);
|
|
313
|
-
const { width, height } = img;
|
|
314
|
-
canvas.width = width;
|
|
315
|
-
canvas.height = height + fontSize * 3;
|
|
316
|
-
ctx.fillStyle = "#FFFFFF";
|
|
317
|
-
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
318
|
-
ctx.drawImage(img, 0, 0);
|
|
319
|
-
|
|
320
|
-
const text = `時間區間:${dateRanges
|
|
321
|
-
.filter((dateRange) => !!dateRange)
|
|
322
|
-
.map((dateRange) => formatDateRange(dateRange))
|
|
323
|
-
.join(" v.s. ")}`;
|
|
324
|
-
ctx.fillStyle = "#333333";
|
|
325
|
-
ctx.font = `${fontSize}px "Microsoft JhengHei"`;
|
|
326
|
-
ctx.fillText(text, fontSize, canvas.height - fontSize);
|
|
327
|
-
|
|
328
|
-
return canvas.toDataURL();
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
async function addWatermark(dataURL: string, watermarkImg: any) {
|
|
332
|
-
const canvas = document.createElement("canvas");
|
|
333
|
-
const ctx = canvas.getContext("2d");
|
|
334
|
-
if (!ctx) return "";
|
|
335
|
-
|
|
336
|
-
const img = await loadImage(dataURL);
|
|
337
|
-
const watermark = await loadImage(watermarkImg);
|
|
338
|
-
|
|
339
|
-
const { width, height } = img;
|
|
340
|
-
canvas.width = width;
|
|
341
|
-
canvas.height = height;
|
|
342
|
-
|
|
343
|
-
ctx.fillStyle = "#FFFFFF";
|
|
344
|
-
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
345
|
-
ctx.drawImage(img, 0, 0);
|
|
346
|
-
|
|
347
|
-
// const matrix = new DOMMatrix();
|
|
348
|
-
// const pattern = ctx.createPattern(watermark, "repeat");
|
|
349
|
-
// if (!pattern) return "";
|
|
350
|
-
|
|
351
|
-
// pattern.setTransform(matrix.rotate(-20).scale(0.5));
|
|
352
|
-
// ctx.globalAlpha = 0.05;
|
|
353
|
-
// ctx.fillStyle = pattern;
|
|
354
|
-
// ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
355
|
-
|
|
356
|
-
ctx.drawImage(watermark, canvas.width - watermark.width - 12, canvas.height - watermark.height - 12);
|
|
357
|
-
|
|
358
|
-
return canvas.toDataURL();
|
|
359
240
|
}
|
|
360
241
|
|
|
361
242
|
onMounted(() => {
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
ref="el"
|
|
5
5
|
)
|
|
6
6
|
input(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
type="checkbox"
|
|
8
|
+
:id="uid"
|
|
9
|
+
v-model="modelProxy"
|
|
10
|
+
:true-value="trueValue",
|
|
11
|
+
:false-value="falseValue",
|
|
12
|
+
)
|
|
13
13
|
label(:for="uid" :style="`width: ${labelWidth}px;`" ref="labelEl")
|
|
14
14
|
span(ref="trueLabelEl").true-label {{ size === '' ? trueLabel : minTrueLabel }}
|
|
15
15
|
span(ref="falseLabelEl").false-label {{ size === '' ? falseLabel : minFalseLabel }}
|
|
@@ -52,7 +52,7 @@ export default defineComponent({
|
|
|
52
52
|
size: {
|
|
53
53
|
type: String,
|
|
54
54
|
default: "",
|
|
55
|
-
validator: (val: string) => ["", "min", "sm"].includes(val),
|
|
55
|
+
validator: (val: string) => ["", "min", "sm", "xs"].includes(val),
|
|
56
56
|
},
|
|
57
57
|
color: {
|
|
58
58
|
type: String,
|
|
@@ -198,6 +198,19 @@ export default defineComponent({
|
|
|
198
198
|
.false-label
|
|
199
199
|
@apply s('pl-[31px]')
|
|
200
200
|
|
|
201
|
+
&.ios-switch-xs
|
|
202
|
+
label
|
|
203
|
+
@apply s('min-w-[60px]') s('h-[24px]') text-xs
|
|
204
|
+
|
|
205
|
+
&:before
|
|
206
|
+
@apply s('inset-y-0.75') s('left-0.75') s('w-[16px]') border border-primary-100 rounded-full shadow-none
|
|
207
|
+
|
|
208
|
+
.true-label
|
|
209
|
+
@apply s('pr-[27px]')
|
|
210
|
+
|
|
211
|
+
.false-label
|
|
212
|
+
@apply s('pl-[27px]')
|
|
213
|
+
|
|
201
214
|
&.ios-switch-primary-600
|
|
202
215
|
input:checked + label
|
|
203
216
|
@apply bg-primary-600 border-primary
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
v-else
|
|
36
36
|
)
|
|
37
37
|
.sidebar-mode-setting
|
|
38
|
-
.sidebar-mode-setting-title
|
|
38
|
+
.sidebar-mode-setting-title 選單模式
|
|
39
39
|
SLIOSSwitch(
|
|
40
40
|
:modelValue="dark"
|
|
41
41
|
trueLabel="深色"
|
|
42
42
|
minTrueLabel="深"
|
|
43
43
|
falseLabel="淺色"
|
|
44
44
|
minFalseLabel="淺"
|
|
45
|
-
:size="closuredProxy ? 'min' : ''"
|
|
45
|
+
:size="closuredProxy ? 'min' : 'xs'"
|
|
46
46
|
:color='switchColor'
|
|
47
47
|
@update:modelValue="onUpdatedMode"
|
|
48
48
|
)
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
SLIcon(icon="chevron-right")
|
|
52
52
|
.sidebar-nav-intro(ref='introEl')
|
|
53
53
|
.sidebar-nav-intro-header {{ navLinkIntro ? navLinkIntro.title : '' }}
|
|
54
|
-
.sidebar-nav-intro-body {{ navLinkIntro ? navLinkIntro.content : ''}}
|
|
54
|
+
.sidebar-nav-intro-body {{ navLinkIntro ? navLinkIntro.content : '' }}
|
|
55
55
|
.sidebar-nav-intro-footer
|
|
56
56
|
SLButton(
|
|
57
57
|
title='我知道了',
|
|
@@ -81,7 +81,7 @@ export default defineComponent({
|
|
|
81
81
|
},
|
|
82
82
|
config: {
|
|
83
83
|
type: Array as PropType<NavConfigType>,
|
|
84
|
-
|
|
84
|
+
default: () => [],
|
|
85
85
|
},
|
|
86
86
|
navIntros: {
|
|
87
87
|
type: Array as PropType<{
|
|
@@ -116,7 +116,7 @@ export default defineComponent({
|
|
|
116
116
|
|
|
117
117
|
function onClickedToggle() {
|
|
118
118
|
if (introing.value) return;
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
closuredProxy.value = !closuredProxy.value;
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -287,7 +287,7 @@ export default defineComponent({
|
|
|
287
287
|
@apply bg-nav-color-switch-bg border-primary-600
|
|
288
288
|
|
|
289
289
|
&:before
|
|
290
|
-
@apply border-
|
|
290
|
+
@apply border-primary-600 bg-primary-300
|
|
291
291
|
|
|
292
292
|
.sidebar-toggle
|
|
293
293
|
@apply bg-primary-900 text-white
|
|
@@ -373,19 +373,19 @@ export default defineComponent({
|
|
|
373
373
|
@apply w-full h-px my-2 bg-primary-200
|
|
374
374
|
|
|
375
375
|
.sidebar-mode-setting
|
|
376
|
-
@apply flex-grow-0 flex-shrink-0 flex items-center justify-
|
|
376
|
+
@apply flex-grow-0 flex-shrink-0 flex items-center justify-start my-4 pl-4 space-x-2 text-xs text-primary
|
|
377
377
|
|
|
378
378
|
&-title
|
|
379
379
|
@apply flex-grow-0 flex-shrink-0
|
|
380
380
|
|
|
381
381
|
.ios-switch
|
|
382
|
-
@apply flex-grow
|
|
382
|
+
@apply flex-grow-0 flex-shrink-0
|
|
383
383
|
|
|
384
384
|
label
|
|
385
385
|
@apply border-primary-100 bg-primary-25
|
|
386
386
|
|
|
387
387
|
&:before
|
|
388
|
-
@apply border-
|
|
388
|
+
@apply border-primary-100
|
|
389
389
|
|
|
390
390
|
&-toggle
|
|
391
391
|
@apply absolute top-0 right-0 flex justify-center items-center w-5 h-10 s('mt-2.5') bg-primary-50 rounded-l-xl text-sm text-primary-500 leading-none transition-all
|
|
@@ -424,5 +424,4 @@ export default defineComponent({
|
|
|
424
424
|
&:before
|
|
425
425
|
content ''
|
|
426
426
|
@apply visible
|
|
427
|
-
</style>
|
|
428
|
-
|
|
427
|
+
</style>
|
package/package.json
CHANGED
package/utils/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type IDateRange from '~ui/interface/IDateRange';
|
|
|
21
21
|
import { ScreenSize } from '~ui/interface/commons';
|
|
22
22
|
import ExcelJS from 'exceljs';
|
|
23
23
|
import { useGtm } from '@gtm-support/vue-gtm';
|
|
24
|
+
import * as htmlToImage from "html-to-image";
|
|
24
25
|
|
|
25
26
|
export const getUID = () => {
|
|
26
27
|
return `uid-${Math.random().toString(36).substring(2)}`;
|
|
@@ -574,4 +575,131 @@ export const waitFor = async (condition: () => boolean, pollInterval = 50, timeo
|
|
|
574
575
|
|
|
575
576
|
await new Promise(r => setTimeout(r, pollInterval));
|
|
576
577
|
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function loadImage(url: string) {
|
|
581
|
+
return new Promise<HTMLImageElement>((resolve) => {
|
|
582
|
+
const img = new Image();
|
|
583
|
+
img.onload = function () {
|
|
584
|
+
resolve(img);
|
|
585
|
+
};
|
|
586
|
+
img.src = url;
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
async function addPadding(dataURL: string, padding: number) {
|
|
591
|
+
const canvas = document.createElement("canvas");
|
|
592
|
+
const ctx = canvas.getContext("2d");
|
|
593
|
+
if (!ctx) return "";
|
|
594
|
+
|
|
595
|
+
const img = await loadImage(dataURL);
|
|
596
|
+
const { width, height } = img;
|
|
597
|
+
canvas.width = width + padding * 2;
|
|
598
|
+
canvas.height = height + padding * 2;
|
|
599
|
+
ctx.fillStyle = "#FFFFFF";
|
|
600
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
601
|
+
ctx.drawImage(img, padding, padding);
|
|
602
|
+
|
|
603
|
+
return canvas.toDataURL();
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
async function addDateRanges(
|
|
607
|
+
dataURL: string,
|
|
608
|
+
dateRanges: (IDateRange | null)[]
|
|
609
|
+
) {
|
|
610
|
+
const fontSize = 16;
|
|
611
|
+
const canvas = document.createElement("canvas");
|
|
612
|
+
const ctx = canvas.getContext("2d");
|
|
613
|
+
if (!ctx) return "";
|
|
614
|
+
|
|
615
|
+
const img = await loadImage(dataURL);
|
|
616
|
+
const { width, height } = img;
|
|
617
|
+
canvas.width = width;
|
|
618
|
+
canvas.height = height + fontSize * 3;
|
|
619
|
+
ctx.fillStyle = "#FFFFFF";
|
|
620
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
621
|
+
ctx.drawImage(img, 0, 0);
|
|
622
|
+
|
|
623
|
+
const text = `時間區間:${dateRanges
|
|
624
|
+
.filter((dateRange) => !!dateRange)
|
|
625
|
+
.map((dateRange) => formatDateRange(dateRange))
|
|
626
|
+
.join(" v.s. ")}`;
|
|
627
|
+
ctx.fillStyle = "#333333";
|
|
628
|
+
ctx.font = `${fontSize}px "Microsoft JhengHei"`;
|
|
629
|
+
ctx.fillText(text, fontSize, canvas.height - fontSize);
|
|
630
|
+
|
|
631
|
+
return canvas.toDataURL();
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
async function addWatermark(dataURL: string, watermarkImg: string) {
|
|
635
|
+
const canvas = document.createElement("canvas");
|
|
636
|
+
const ctx = canvas.getContext("2d");
|
|
637
|
+
if (!ctx) return "";
|
|
638
|
+
|
|
639
|
+
const img = await loadImage(dataURL);
|
|
640
|
+
const watermark = await loadImage(watermarkImg);
|
|
641
|
+
|
|
642
|
+
const { width, height } = img;
|
|
643
|
+
canvas.width = width;
|
|
644
|
+
canvas.height = height;
|
|
645
|
+
|
|
646
|
+
ctx.fillStyle = "#FFFFFF";
|
|
647
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
648
|
+
ctx.drawImage(img, 0, 0);
|
|
649
|
+
|
|
650
|
+
// const matrix = new DOMMatrix();
|
|
651
|
+
// const pattern = ctx.createPattern(watermark, "repeat");
|
|
652
|
+
// if (!pattern) return "";
|
|
653
|
+
|
|
654
|
+
// pattern.setTransform(matrix.rotate(-20).scale(0.5));
|
|
655
|
+
// ctx.globalAlpha = 0.05;
|
|
656
|
+
// ctx.fillStyle = pattern;
|
|
657
|
+
// ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
658
|
+
|
|
659
|
+
ctx.drawImage(watermark, canvas.width - watermark.width - 12, canvas.height - watermark.height - 12);
|
|
660
|
+
|
|
661
|
+
return canvas.toDataURL();
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
export const domToPngDataUrl = async (
|
|
665
|
+
imageTarget: HTMLElement | null,
|
|
666
|
+
customFilter: Function | null,
|
|
667
|
+
customStyle: CSSStyleDeclaration | null,
|
|
668
|
+
padding: number = 0,
|
|
669
|
+
dateRanges: (IDateRange | null)[] = [],
|
|
670
|
+
watermark: boolean = true,
|
|
671
|
+
watermarkImg: string = '/assets/images/commons/watermark-2.svg',
|
|
672
|
+
) => {
|
|
673
|
+
if (!imageTarget) {
|
|
674
|
+
console.log("Can not find target of htmlToImage!");
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const filterOption = (node: HTMLElement) => {
|
|
679
|
+
return (
|
|
680
|
+
!(
|
|
681
|
+
node?.getAttribute && node.getAttribute("data-skip-download") === ""
|
|
682
|
+
) &&
|
|
683
|
+
(typeof customFilter !== "function" ||
|
|
684
|
+
(customFilter && customFilter(node)))
|
|
685
|
+
);
|
|
686
|
+
};
|
|
687
|
+
const styleOption = Object.assign({}, {
|
|
688
|
+
marginTop: '0px',
|
|
689
|
+
marginBottom: '9px',
|
|
690
|
+
border: 'none',
|
|
691
|
+
}, customStyle || {});
|
|
692
|
+
|
|
693
|
+
const options = {
|
|
694
|
+
pixelRatio: 1,
|
|
695
|
+
filter: filterOption,
|
|
696
|
+
style: styleOption,
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
let dataURL = await htmlToImage.toPng(imageTarget, options);
|
|
700
|
+
if (padding) dataURL = await addPadding(dataURL, padding);
|
|
701
|
+
if (dateRanges?.length)
|
|
702
|
+
dataURL = await addDateRanges(dataURL, [...dateRanges].reverse());
|
|
703
|
+
if (watermark) dataURL = await addWatermark(dataURL, watermarkImg);
|
|
704
|
+
return dataURL;
|
|
577
705
|
}
|